장문 문서를 읽고 구조화하고 근거를 대조하는 일을 LLM 에이전트에 맡길 때, 틀린 결과가 조용히 통과하지 않도록 짠 작업 구조다.
프롬프트 모음이 아니다. 판정 루프다.
여기 규칙은 하나도 미리 설계한 게 없고 전부 뭔가를 틀린 다음에 생겼다.
4절이 그 목록이다.
코딩 에이전트가 잘 도는 건 판정자가 있어서다. 틀리면 빨간 줄이 뜨고 테스트가 깨진다. 그 신호가 다음 수정을 어디로 해야 할지까지 알려준다.
문서 작업엔 그게 없다.
대부분의 문서 AI는 그럴듯한 걸 빠르게 뽑고 멈춘다. 다 돼서 멈추는 게 아니라 더 갈 신호가 없어서 멈추는 건데, 겉으로는 구분이 안 됐다.
남는 건 문서 더미다. 매끄럽고 아무도 검증하지 않은.
판정자를 만들어 넣었다. 그게 전부다.
┌──────────────────────────────┐
│ │
▼ │
산출물 수정 ────→ 판정자가 검사 ─────┘
│
불일치 · 근거 없음 · 반론
대화가 길어지면 초반 지시가 희석되길래 매번 새 창을 열기로 했다.
대신 창을 닫아도 아무것도 안 날아가도록, 지금 무엇을 하고 있고 무엇이 정해졌고 무엇이 미결인지를 전부 폴더에 적어둔다.
| 폴더 | 무엇 | 컨텍스트에 넣는 법 |
|---|---|---|
00_고정/ |
판정 기준 · 금지 목록 · 제약 | 매 호출마다 전부 |
10_원자료/ |
원본 문서 (읽기 전용) | 관련될 때 원문 그대로 |
20_산출물/ |
실제 결과물 | 그 호출에 관련된 것만 |
30_역할/ |
판정자 프롬프트 | 하나씩. 절대 섞지 않는다 |
06_결정사항/ |
DECISIONS.md (append-only) · OPEN_ISSUES.md |
판단이 갈릴 때 |
루트에는 CLAUDE.md(또는 동등한 프로젝트 지침 파일)를 둔다. 서식은 templates/CLAUDE.md에 있다.
한 바퀴는 이렇게 돈다. 00_고정 전부와 관련 산출물, 그리고 역할 하나를 넣어 호출한다. 나온 지적을 산출물에 반영한다. 버린 것과 그 이유는 금지 목록에 쌓는다.
그리고 다시 처음으로.
"장단점을 균형 있게 평가해줘"라고 하면 무난한 평균이 나오므로, 날을 세우려면 한쪽만 시켜야 한다.
균형은 사람이 잡는다. 모델한테 균형까지 맡기면 아무것도 안 걸러진다.
| 역할 | 언제 | 무엇을 잡나 |
|---|---|---|
| 질문 생성 | 시작할 때, 막혔을 때 | 모르는 것. 답보다 질문이 값질 때가 있다 |
| 회의론자 | 방향을 정하기 전 | 반대 논거만. 장점은 한 줄도 쓰지 않게 한다 |
| 일관성 검사 | 산출물 2개 이상 고칠 때마다 | 문서 사이의 모순 |
| 근거 대조 검증자 | 근거·수치가 많이 들어간 직후 | 인용이 원문을 실제로 뒷받침하는가 |
넷 중에 효율이 가장 높은 건 일관성 검사라고 봤다. 다른 역할은 판단이 섞이는데 이건 기계적 대조에 가까워서 결과가 안정적이다. 사람이 지쳐서 놓치는 자리를 대신 봐준다.
문서를 하나씩 고치다 보면 어느 순간 A를 전제로 쓴 문서와 B를 전제로 쓴 문서가 나란히 남게 되는데 각각을 따로 펼쳐 보면 둘 다 앞뒤가 맞아서 매일 그 문서를 만지는 사람은 끝까지 못 느낀다. 밖에서 보는 사람은 5분이면 찾는다.
근거 대조만은 새 세션에서, 원본만 들고 시작한다. 정리 노트는 주지 않는다.
같은 대화 안에서 하는 검증은 약했다. 앞에서 자기가 세운 전제를 그대로 물려받은 채로 검사하니, 전제가 틀렸으면 그 위에서 아주 꼼꼼하게 검사하고 통과시킨다.
사실을 말하기 전에 사다리를 탄다. 맨 위가 10_원자료/의 원본 문서고 그다음이 조직 공식 자료, 용어집, 일반 지식 노트 순이다. 외부 검색은 다섯 번째라 위 넷에 없을 때만 쓰고 쓸 때는 (외부 검색)을 붙인다.
1·2를 확인하지 않고 5로 내려가지 않는다. 내부 용어를 외부 검색으로 정의했더니 그럴듯한 오답이 나왔다.
아예 틀린 답이면 누가 봐도 이상해서 걸린다. 그럴듯한 오답은 문서 사이를 옮겨 다니며 계속 인용되다가 한참 뒤에야 드러난다.
모르면 검색하지 말고 물어본다.
확신의 정도는 표기로 남긴다. 원문이나 공식 자료로 확인한 것은 아무 표시 없이 쓴다. 원문 대조를 안 했으면 [확인필요]를 붙여 단정하지 않는다. 외부 검색에서 가져온 것은 (외부 검색), 내 판단은 [추정]이다. 표시가 붙은 문장은 나중에 누가 봐도 근거의 층이 다르다는 걸 안다.
수치나 조항을 인용할 때 링크만으로는 부족한데, 멀쩡히 존재하는 문서를 링크해놓고 그 문서가 뒷받침하지 않는 내용을 쓸 수 있고 파일명 기반 검사는 이걸 100% 통과시키기 때문이다.
구절을 옮겨 적는다.
나쁨: 이 조문은 1년까지 운전을 금지할 수 있게 한다 (원문 링크)
좋음: 제97조① — "1년을 넘지 아니하는 범위에서 국제운전면허증에 의한
자동차등의 운전을 금지할 수 있다"
예시는 extraction-benchmark에서 쓴 공개 법령 문서다.
같은 숫자가 두 파일에 있으면 이미 사고 예비 상태로 봤다. 원천 한 곳, 나머지는 링크.
아래 사고 1번의 수정 비용이 컸던 게 정확히 이것 때문이다. 틀린 정의가 이미 아홉 군데 복사돼 있었다.
각 줄은 실제로 무언가를 틀린 사건이다. 오른쪽이 그래서 생긴 규칙이다.
| # | 무슨 일이 있었나 | 그래서 생긴 규칙 |
|---|---|---|
| 1 | 내부 고유 용어를 원본도 안 보고 외부 검색으로 정의했다. 그럴듯한 답이 나왔고, 확신 표시도 안 붙였다. 그 정의가 9개 파일로 복사됐고 발표까지 나갔다. 실제 정의는 공식 자료 2쪽 각주 한 줄에 있었다 | 근거 사다리(3절). 지식이 모자란 게 아니라 순서가 틀렸다 — 밖을 먼저 봤다 |
| 2 | 문서 변환기가 표 셀 안의 이스케이프 문자에서 셀을 잘랐다. 한 항목이 5,270자에서 84자로, 전체로 약 38,000자가 사라졌다. 그런데 그 파일은 머리에 "원문 전문"이라고 적혀 있었다. 링크도 ID도 멀쩡했고, 분량이 맞는지는 아무도 안 봤다 | 기계 추출물을 "전문"이라 부르기 전에 원문에서 구절 몇 개를 옮겨 대조한다. 표·규격이 든 항목은 반드시 |
| 3 | 항목 본문(□)만 읽고 단서(※)를 흘렸다. 단서가 본문을 정면으로 부정하고 있었고, 결론이 정반대로 나갔다. 세어보니 단서 136줄 중 24개가 본문을 뒤집거나 한정했다 |
인용·요약할 때 본문·하위·단서의 개수를 세어 맞춘다. 단서가 본문을 뒤집으면 단서 쪽이 진짜 요구다 |
| 4 | 연계정보로 검색해 0건이 나오자 "원문에 없음"으로 단정했다. 실제 표기는 연계 정보, 띄어쓰기가 있었고 3건 나왔다. 다른 용어는 도면 이미지 안에 있어 텍스트 검색이 닿지 않았다 |
"검색 0건"은 "없다"가 아니다. 띄어쓰기 변형·단어경계·이미지·원본 포맷까지 네 단계를 다 거친 뒤에만 "없음"이라 쓴다 |
| 5 | 추출 결과의 빈 표를 보고 "그림이 소실됐다"고 단정해 노트 세 군데에 적었다. 그림은 파일 안에 온전히 있었다. 이틀간 없는 것으로 취급했고, 사람이 "혹시 못 보고 있는 거냐"고 묻고서야 확인했다 | 추출 결과가 비면 "없다"가 아니라 "내 도구가 못 꺼냈다"를 먼저 의심한다. 빈 셀과 0바이트는 결론이 아니라 확인 시작점이다 |
| 6 | 문서 수를 세는데 작업 사본 디렉터리가 같이 잡혀 실제의 4배로 부풀었다. 그 숫자가 지침 문서에 박혔다. 그 문서가 스스로 "사본을 제외하라"고 경고하고 있었는데도 | 집계 명령에 제외 경로가 들어갔는지 눈으로 확인한다. 규칙을 쓰는 것과 지키는 건 다르다 |
| 7 | 정규식으로 항목 ID를 세니 문서 제목보다 3개 많았다. 데이터 오류로 의심했는데, 다른 문서의 ID를 참조한 3건이었다. 원본에는 총계표가 있었다 | 정규식은 문맥을 모른다. 원본에 합계가 있으면 그게 정답이다. 도구 출력과 원본이 다르면 도구를 먼저 의심한다 |
| 8 | 파일 동기화 훅이 의존하는 CLI 도구가 설치돼 있지 않아 매번 실패하고 있었다. 게다가 경로 판정이 문자열 포함 검사라 관계없는 편집에도 발동했다 | 조용히 실패하는 자동화가 가장 위험하다. 실패는 소리 나게 만들고, 판정은 넓은 매칭이 아니라 정확한 대상으로 한다 |
여덟 건을 한 표에 늘어놓고 나서야 보였는데 각각은 전혀 다른 사고처럼 보이던 것들이 다섯(1·2·4·5·7)이나 같은 뿌리였다. 도구가 준 결과를 원본과 대조하지 않고 사실로 받았다는 것. 원본에 접근할 수 있는데 파생물을 근거로 삼는 것이 이 작업에서 가장 자주 나는 사고다. 나머지 셋은 자기가 쓴 규칙을 자기가 안 지킨 것이고.
핵심은 프롬프트가 아니다. 원본을 언제 다시 보게 만드느냐다.
위 여덟 개는 정리된 뒤에 적은 거라 깔끔해 보인다. 실제로는 이렇게 안 굴러갔다.
바로 직전에 겪은 걸 정리 안 하고 그대로 적어둔다.
공개 전에 금지어 점검 스크립트(scripts/leak_check.py)를 만들었더니 첫 실행에서 39건이 검출됐는데 전부 금지어 목록 파일 자신이었다.
목록이 곧 보호 대상 이름들이니 당연한 건데, 만들 때는 생각을 못 했다. 목록을 .gitignore로 빼고 다시 돌렸다.
이번엔 40건. per_run, per_item_wall, counter 같은 평범한 코드 식별자였다. 대문자 약어를 대소문자 구분 없이 찾고 있었다.
여기서 검사 기준을 느슨하게 할까 잠깐 생각했다. 그러면 ABC_항목목록.md 같은 진짜 파일명을 놓친다고 봐서, 대소문자만 구분하도록 고쳤다. 그때 회귀 시험 아홉 건을 붙였다고 여기 적어뒀었는데 이 저장소에는 시험이 하나도 없다 — 커밋 이력에도 들어온 적이 없다. 적어둔 것과 올린 것이 달랐다.
그러고 나서 진짜 유출 두 건이 나왔다.
하나는 실제 항목 ID와 원문 구절이 README 예시로 들어가 있던 것으로, "링크가 아니라 구절을 옮겨 적는다"는 규칙을 설명하려다 진짜 구절을 옮겨 적은 것이었다.
규칙을 지키다가 그 규칙이 막으려던 걸 했다.
다른 하나는 조직명이 하나도 없는데 문서 종류 이름만으로 무슨 일을 하는지 드러나던 문장이었다.
여기까지 하고 다 됐다고 생각했다. 아니었다.
며칠 뒤에야 git 이력 검사가 대상 저장소가 아니라 스크립트가 놓인 폴더를 보고 있다는 걸 발견했는데 다른 저장소를 검사해도 자기 이력만 읽으니 커밋 메시지 점검은 처음부터 한 번도 돌지 않았다. 그런데도 화면에는 계속 통과 표시가 떴고 그 표시를 믿고 나는 이미 두 번이나 "이제 됐다"고 결론을 냈던 것이다. 고쳤다. 금지어를 일부러 심은 임시 저장소로 확인하고서야 믿었다.
여기서 배운 건 규칙이 아닌 순서였다. 검사를 만들었으면 그 검사가 실제로 도는지를 따로 확인한다.
8번이랑 같은 얘긴데, 그걸 써놓고도 내가 만든 검사에서 똑같이 당했다.
DECISIONS.md는 append-only다. 결정은 지우지 않는다. 뒤집히면 새 항목을 추가하고 이전 항목에 취소선을 긋는다. 지워버리면 같은 판단을 몇 달 뒤에 또 하게 된다.
OPEN_ISSUES.md는 미결을 담는다. 해소되면 DECISIONS.md로 옮긴 뒤 여기엔 링크만 남긴다.
금지 목록에는 버린 아이디어와 그 이유를 쌓는데 안 쌓아두면 다음 창에서 같은 걸 또 제안받는다.
서식은 templates/에 있다.
이 방식으로 굴린 프로젝트에서 가장 자주 갱신된 두 파일이 결정 로그와 미결 목록이었다. 각각 16회, 13회.
산출물보다 판단의 기록을 더 자주 고쳤다. 그게 이 구조가 실제로 돌았다는 증거라고 봤다.
같은 작업을 사람과 파이프라인으로 나눠 실측한 기록이 extraction-benchmark에 따로 있다. 공개 법령 문서에서 항목을 뽑아 구조화 표로 만드는 작업, 표본 10건이다. 거기서 나온 것 중 셋이 이 구조에 들어왔다.
단발 실행은 그 자체로 못 믿는다. 같은 입력과 같은 프롬프트로 10건 중 2건이 실행마다 다른 답을 냈다. 그게 반복 실행을 기본으로 하는 이유다.
다만 반복이 주는 건 정확도가 아니었다. 다수결까지 적용해 다시 계산해봤더니 일치율은 한 건도 안 움직였다. 그러니까 반복이 사주는 것은 재현성이지 정답이 아니다.
일치율은 난이도를 못 본다. 사람이 가장 어려워한 항목이 채점에서는 만점이었는데 사람과 기계가 우연히 같은 답을 골랐기 때문이다.
그래서 골든셋에 확신도를 같이 받고 채점을 확신도별로 나눠 본다.
판정 기준의 결함은 불일치로 드러난다. 일관된 불일치 2건을 파고드니 어느 쪽의 오류도 아니었다. 내가 쓴 규칙이 그 경우를 못 가르고 있었다. 불일치는 오답 목록으로 읽지 않는다. 규칙 개정 목록으로 읽는다.
점수가 올라가는 걸 목표로 삼지 않기로 했다. 판정자에게 과적합된다.
전제부터 적는다. 여기 있는 프롬프트 넷은 텍스트 파일이라 아무 LLM에나 붙여 넣으면 되지만 검증은 Claude Code에서만 했다. 긴 문서 여러 개를 한 호출에 밀어 넣는 방식이라 맥락 창이 좁은 모델에서는 3절의 근거 규율이 먼저 무너질 것으로 본다.
재보지는 않았다.
scripts/leak_check.py는 파이썬 3.12에서 만들었고 표준 라이브러리만 써서 설치할 게 없다. 저장소가 git이면 커밋 메시지까지 훑는다.
폴더 다섯 개를 만든다. 00_고정, 10_원자료, 20_산출물, 30_역할, 06_결정사항.
templates/CLAUDE.md를 루트에 복사하고 판단 기준 한 줄을 채운다. "이 문장이 ○○에서 통하는가" 같은 것. 00_고정/에 금지 목록 파일을 만든다. 비어 있어도 된다.
프롬프트는 일관성 검사 하나만 먼저 가져간다. 효과가 가장 확실하고 가장 기계적이다.
마지막으로 DECISIONS.md에 첫 줄을 적는다. 왜 이렇게 시작했는지.
나머지는 틀리면서 늘었다. 4절의 여덟 개도 앉아서 설계한 게 아니라 하나씩 사고를 치고 나서 "다음엔 이러지 말자"고 적어둔 것들이다.
이 구조가 죽는 방식은 둘이다. 도구 세팅에 한 주 쓰는 것, 그리고 폴더를 예쁘게 만드는 것. 설치는 30분에 끝내고 실제 문서로 넘어간다. 판정자를 한 번도 안 돌린 폴더는 그냥 폴더다.
README.md 이 문서 — 방법
prompts/
01_question-generation.md 역할 · 질문 생성
02_skeptic.md 역할 · 회의론자
03_consistency.md 역할 · 일관성 검사
04_evidence-audit.md 역할 · 근거 대조 검증자
templates/
CLAUDE.md 프로젝트 지침 템플릿
DECISIONS.md 결정 로그 (append-only)
OPEN_ISSUES.md 미결 목록
scripts/
leak_check.py 공개 전 금지어 점검
개인 작업 방식을 정리한 것이다. 특정 조직의 문서·데이터·업무 내용은 들어 있지 않다. 구조와 규칙만 옮겼다. 4절의 사고 기록도 무슨 문서였는지 어느 조직 일이었는지는 전부 걷어내고 어떤 모양의 실수였는지만 남겼다.
구체적인 실행 예시가 필요하면 extraction-benchmark를 본다. 공개 법령 문서로 같은 워크플로를 처음부터 끝까지 돌린 기록이다.
프로젝트 두 건에서 실제로 돌리는 중이다. 4절의 규칙 여덟 개는 전부 그 두 건에서 사고가 나고 나서 적힌 것이다. 만들어놓고 안 쓰는 규칙은 없다.
검증되지 않은 자리는 셋이다.
n이 2다. 문서 작업 두 건에서 나온 방법이고 둘 다 내가 했다. 다른 사람이 이 폴더 구조로 일해본 적은 없다.
이 문서 자체를 아직 남에게 안 읽혔다. 공개 절차에 "한 문단을 남에게 읽히고 어느 조직 얘기 같으냐고 물어본다"를 넣어놓고 그 단계를 건너뛴 채 올렸다. 4절의 유출 두 건을 잡아준 건 스크립트다. 다만 스크립트는 목록에 적은 것만 본다.
스케줄러나 파이프라인 도구는 안 써봤다. 여기서 말하는 자동화는 전부 사람이 한 호출씩 돌리는 것이다.
MIT(LICENSE). 프롬프트와 서식은 가져다 고쳐 쓰라고 올린 것이다.