유피테르
유피테르
🧙 AI 위자드
🏅 베스트오브베스트

정답은 37/78인데, 보고서 안에는 36/76이 남아 있었다-주석 정렬 프로젝트에서 NULL 정책 큐를 교정하고, 재현성과 독립 검토까지 통과시킨 사례

📝 한줄 요약

성경 주석 정렬 데이터에서 아직 절 단위로 안전하게 귀속하지 못한 항목을 분류하는 NULL 정책 큐를 다시 검증하던 중, 최상위 집계와 상세 목록은 37 files / 78 chunks를 가리키는데 보고서 안쪽의 audit_residual 집계만 예전 값인 36 files / 76 chunks로 남아 있는 불일치를 발견했다. 원문 데이터나 데이터베이스를 임의로 고치지 않고, 재산출 스크립트의 단일 진실 공급원을 바로잡은 뒤 회귀 테스트와 독립 적대 검토까지 통과시켰다.

바쁘시면 이것만 읽어도 됩니다.

  • 문제는 데이터 자체가 아니라, 같은 사실을 서로 다른 고정 숫자가 말하고 있었다는 점이었다.

  • 37/78이라는 정답을 한 군데 더 복사하는 대신, nested 집계를 파생 summary에서 직접 읽도록 바꿨다.

  • 같은 큐를 두 번 재산출해 저장본과 바이트 단위로 비교했다.

  • 같은 문제가 다시 생기지 않도록 회귀 테스트를 추가했다.

  • 첫 독립 검토에서 놓친 기준값이 하나 더 있었다. 새 테스트가 추가됐는데 검토 요청에는 예전 테스트 수 371이 남아 있었다.

  • 검토 범위를 현행 기준 372로 고친 뒤, 큐·DB·원문·해시·테스트를 모두 다시 확인해 최종 PASS했다.

  • 결론은 “더 고칠 것이 없다”가 아니라, 현재 정책에서 안전하게 실행할 다음 변경 후보가 0개라는 것도 검증 결과라는 것이다.

🎯 이런 분들께 도움됩니다

  • AI로 문서·주석·자료를 정렬한 뒤 결과 보고서를 믿어도 되는지 고민하는 분

  • 같은 숫자가 여러 표와 요약에 반복되는 데이터 파이프라인을 운영하는 분

  • “테스트가 통과했다”와 “결과물이 재현된다”를 구분하고 싶은 분

  • 사람이 검토해야 하는 HOLD·NULL·미분류 항목을 안전하게 관리하고 싶은 분

  • AI 에이전트의 작업 결과에서 실패와 수리 과정을 숨기지 않고 남기고 싶은 분

문제 상황: 숫자는 맞는 것 같았지만, 문서가 서로 다른 말을 하고 있었다

박윤선주석 정렬 프로젝트에서는 주석 원문을 성경별·절별로 연결하면서, 아직 안전하게 귀속할 수 없는 조각을 무리하게 승격하지 않기 위해 NULL 정책 큐를 운영한다. 이 큐는 “버릴 데이터 목록”이 아니다. 어떤 조각이 왜 보류됐는지, 어느 감사 기록이 있는지, 다음에 어떤 조건이 충족돼야 다시 검토할 수 있는지를 보존하는 안전장치다.

v119에서는 Romans DATA 일부를 다시 판정했다. 그 결과 로마서 1:1–17에 해당하는 안전한 조각 하나는 accepted로 분리했고, 경계를 확정하기 어려운 두 조각은 residual로 남겼다. 이 변화에 따라 전체 NULL 집계는 다음처럼 바뀌었다.

전체 NULL: 197 chunks / 156 files
감사 잔여(audit_residual): 37 files / 78 chunks
valid-commentary HOLD: 91 files / 91 chunks

그런데 독립 검토 과정에서 보고서의 서로 다른 층을 대조하자 이상한 점이 드러났다.

최상위 summary       37 files / 78 chunks
상세 source 목록      37 sources / 78 items
nested audit_residual 36 files / 76 chunks  ← 이전 값

각각의 숫자만 따로 읽으면 그럴듯했다. 그러나 한 보고서 안에서 같은 집계가 세 가지 값으로 존재하면, 독자는 어느 층을 정본으로 삼아야 할지 알 수 없다. 이 상태는 데이터가 맞더라도 보고서가 검증 가능한 산출물이라는 조건을 만족하지 못한다.

처음에는 왜 발견하기 어려웠나

이 문제는 대형 알고리즘 오류처럼 나타나지 않았다. 전체 합계 197/156은 맞았고, 상세 목록도 37/78이었다. 테스트도 기존 기준에서는 통과했다. 불일치는 보고서 생성 코드 안의 nested 출력부에 남은 고정 숫자에서 발생했다.

즉, 다음과 같은 구조였다.

새로 계산한 summary ─────── 37/78
상세 목록에서 계산한 값 ── 37/78
예전 코드에 남은 표시값 ── 36/76

이런 오류가 위험한 이유는 숫자가 틀렸다는 사실보다, 어떤 숫자가 계산된 값이고 어떤 숫자가 과거 실행에서 복사된 값인지 표면만 보고는 알기 어렵다는 점이다. 특히 문서가 길어지고 표·요약·부록이 늘어날수록 복사된 숫자는 실제 계산 결과처럼 보인다.

수리 원칙: 원문과 DB는 건드리지 않고, 산출 경로만 고친다

이번 작업에서 가장 먼저 지킨 원칙은 원문과 데이터베이스를 수정하지 않는 것이었다.

원문·DB·기존 입력
        │  immutable
        ▼
재산출 스크립트 교정
        ▼
JSON·Markdown 보고서 재생성
        ▼
회귀 테스트 + 독립 검토

수정한 내용은 단순하지만 중요했다. nested audit_residual의 files와 null_chunks를 직접 입력한 36, 76에서 가져오지 않고, 같은 실행에서 계산된 summary['audit_residual']을 참조하도록 바꿨다.

# 이전 방식: 과거 실행의 숫자를 고정해서 출력
"files": 36,
"null_chunks": 76,

# 교정 방식: 현재 실행에서 계산된 summary를 사용
"files": summary["audit_residual"]["files"],
"null_chunks": summary["audit_residual"]["null_chunks"],

이렇게 하면 다음에 원문이나 분류 결과가 변할 때 nested 표만 과거 값에 머무는 일이 줄어든다. 더 중요한 것은 보고서의 여러 층이 같은 실행 결과를 공유하게 된다는 점이다.

한 번 실행해서 맞는 것으로 끝내지 않았다

스크립트를 고친 뒤 한 번 실행한 결과만 믿지 않았다. 같은 입력에서 두 번 재산출하고, 두 실행 결과와 저장된 정식 보고서를 모두 바이트 단위로 비교했다.

재산출 1 ─┐
          ├─ JSON byte-identical
재산출 2 ─┘

재산출 1 ─┐
          ├─ Markdown byte-identical
저장본   ─┘

최종 해시는 다음과 같다.

대상

SHA-256

commentary_v119.db

c044c90477eeeece03a6d852106750556e7194c80709e4b1295b0392ca5c0385

NULL 큐 JSON

ab76914b667f9e96a47f1e8ccdf1306d8101ce0895dd0e916a78e5be61080d66

NULL 큐 Markdown

924a41139975ace5ef92928053872cb0262006bb539228d35326c5315b9ce667

해시는 장식이 아니다. “이 파일을 읽었다”는 말과 “이 입력으로 다시 만들어도 같은 파일이 나온다”는 말을 구분해 주는 증거다.

회귀 테스트를 추가하자 테스트 수가 달라졌다

같은 문제의 재발을 막기 위해 전용 회귀 테스트를 추가했다. 테스트의 핵심은 다음 세 층이 항상 일치하는지 확인하는 것이다.

summary audit_residual
= nested audit_residual
= detail cardinality

그 결과 전체 테스트 수가 기존 371개에서 372개로 늘었다. 여기서 또 하나의 교훈이 생겼다. 첫 번째 독립 재검토 요청에는 예전 기준인 371 tests가 그대로 남아 있었다. 실제로는 모든 테스트가 통과했지만, 검토자는 “요구된 371건과 현재 372건이 다르다”는 이유로 절차적 FAIL을 반환했다.

이 FAIL은 코드 결함이 아니었지만 무시하지 않았다. 검토 기준 자체가 현재 산출물과 맞는지 다시 확인하고, 신규 회귀 테스트가 정확히 1건을 추가한 원인임을 확인한 뒤 현행 기준 372/372로 재검토를 요청했다.

최종 결과는 다음과 같다.

전체 discovery: 372
신규 회귀 테스트 제외: 371
신규 파일이 추가한 테스트: 1
최종 실행: Ran 372 tests — OK

테스트 수를 맞추기 위해 회귀 테스트를 삭제하지 않은 것이 중요하다. 기준값이 오래됐다는 이유로 안전장치를 제거하는 대신, 검토 기준을 현재 상태에 맞게 갱신했다.

독립 검토에서 무엇을 다시 확인했나

최종 독립 검토는 단순히 테스트 명령 하나를 다시 실행하는 방식이 아니었다. 읽기 전용 검토자가 다음 항목을 별도로 대조했다.

검증 항목

결과

NULL 전체 분할

197 chunks / 156 files

상호배타·완전포괄

PASS

중복·누락·미분류

0

audit residual summary/nested/detail

37 files / 78 chunks

provenance

77 exact + 1 non-ratifying + 0 source-only

valid-commentary HOLD

91 files / 91 chunks

residual-merge screening basis

17

basis 입력 SHA 대조

17/17 일치

SQLite integrity/quick check

ok / ok

FTS alignment

13,442 / 13,442

보호 대상 snapshot

26개 불변

전체 테스트

372/372 PASS

warnings fail-closed

PASS

여기서 non-ratifying 한 건은 실패나 누락으로 숨기지 않았다. N-ROMANS__DATA2__ROMAN.md의 chunk 12592는 좌표 승인을 받은 항목이 아니라 historical replay와 typed residual을 보존하는 기록이다. 따라서 exact provenance와 같은 의미로 세지 않고 별도 상태로 남겼다.

이 사례에서 가장 중요한 실패는 두 번 있었다

첫 번째 FAIL은 실제 산출물의 불일치였다.

nested audit_residual: 36/76
현재 summary·상세:       37/78

이것은 코드와 보고서를 교정해야 하는 FAIL이었다.

두 번째 FAIL은 검토 기준의 stale 상태였다.

실제 현행 테스트: 372
검토 요청에 남은 기준: 371

이것은 회귀 테스트를 제거할 문제가 아니라, 검토 요청과 실제 산출물의 버전을 맞춰야 하는 FAIL이었다.

두 실패를 같은 방식으로 처리하지 않은 것이 핵심이다. 하나는 산출물을 수리했고, 다른 하나는 검토 기준을 갱신한 뒤 다시 검토했다. 실패 원인이 다르면 Repair Owner와 재검증 범위도 달라져야 한다.

왜 HOLD 91개를 더 처리하지 않았나

검증이 끝났다고 해서 보류 항목을 계속 자동 승격하지는 않았다. 현재 큐의 자동 allow-list는 비어 있고, fixed-manifest 축도 닫혀 있다. 남은 항목은 “무조건 처리해야 할 미완료 목록”이 아니라, 새로운 근거와 정책 승인이 생겼을 때 다시 볼 수 있는 보존 상태다.

특히 다음 두 조치는 별도 승인 없이 수행하지 않았다.

  • 원문 근거가 부족한 항목을 임의로 commentary에서 제외하는 것

  • 좌표가 불명확한 항목을 permanent hold로 의미 변경하는 것

따라서 이번 사례의 최종 결론은 “91개를 모두 해결했다”가 아니다. 현재 증거와 정책으로 안전하게 실행할 변경 후보가 0개임을 확인하고, 남은 불확실성을 보존했다는 것이다.

재사용할 수 있는 운영 패턴

이번 작업을 다른 문서 정렬·RAG·지식베이스 파이프라인에 적용한다면 다음 순서를 권한다.

1. 입력 원문과 DB를 불변으로 고정한다.
2. 같은 사실을 반복 출력하는 지점을 찾는다.
3. 복사된 숫자 대신 파생 계산값을 단일 공급원으로 만든다.
4. 보고서의 summary·detail·nested 층을 서로 대조한다.
5. 같은 입력으로 두 번 재산출한다.
6. 저장본과 byte-level 비교를 한다.
7. 발견된 결함을 고정하는 회귀 테스트를 추가한다.
8. 테스트 cardinality와 검토 기준을 함께 갱신한다.
9. 작성자와 다른 검토자가 read-only로 다시 확인한다.
10. 후보가 없으면 억지로 다음 변경을 만들지 않고 0개로 기록한다.

이 사례가 신학 자료 관리에 주는 의미

이 작업은 특정 교리나 본문의 신학적 해석을 새로 결정한 사례가 아니다. 핵심은 해석에 들어가기 전 단계에서 어떤 주석 조각이 어느 성경 구간에 귀속됐는지, 어떤 조각이 아직 보류됐는지, 그 판단이 다시 재현되는지를 관리한 사례다.

성경 본문과 주석을 함께 다루는 연구에서는 작은 경계 오류가 뒤의 해석을 조용히 바꿀 수 있다. 반대로 불확실한 조각을 안전하게 보류하면, 당장 모든 것을 완성하지 못하더라도 무엇이 확인됐고 무엇이 아직 확인되지 않았는지를 독자에게 정직하게 보여 줄 수 있다.

좋은 정렬은 모든 빈칸을 채우는 작업이 아니다. 때로는 빈칸을 빈칸으로 남겨 두되, 그 이유와 근거와 다음 검토 조건을 잃지 않는 작업이다.

마무리

이번 사례에서 고친 것은 숫자 두 개였다. 하지만 실제로 회복한 것은 숫자보다 큰 것이었다.

  • 보고서의 각 층이 같은 사실을 말하게 됐다.

  • 같은 입력으로 같은 결과를 다시 만들 수 있게 됐다.

  • 실패를 숨기지 않고 수리·회귀·독립 검토의 순서로 남겼다.

  • 테스트가 늘었다는 이유로 안전장치를 되돌리지 않았다.

  • 해결되지 않은 항목을 억지로 처리하지 않고 보존했다.

AI가 문서를 정리할 때 가장 위험한 말은 “대충 맞습니다”일 수 있다. 이번 작업에서 더 유용했던 말은 다음과 같았다.

어느 층의 숫자가 정본인가요? 같은 입력으로 다시 만들 수 있나요? 이 항목을 지금 승격해도 되는 근거가 있나요?

이 세 질문에 답할 수 있을 때, 정렬 결과는 단순한 자동화 출력이 아니라 다음 연구자가 다시 사용할 수 있는 지식 기반이 된다.

1
2개의 답글
지피터스 뉴스레터

이번 주 AI, 딱 쓸 것만

매주 월요일 오후,
읽고 바로 써먹을 AI 소식만