
【AI 주도 개발】 100페이지가 넘는 PDF를 “틀리지 않고” Markdown으로 변환하는 방법【v3·검증 강화판】
요약
대규모 PDF 문서를 Markdown으로 변환할 때 발생하는 AI의 환각과 검증 오류를 해결하기 위한 v3 워크플로우를 소개합니다. AI의 자가 검토 대신 스크립트를 활용한 기계 검산과 독립적인 정본(Original Source) 대조 방식을 도입하여 정확도를 극대화합니다.
핵심 포인트
- AI가 생성한 결과물을 동일한 AI로 검증할 때 발생하는 오류 위험성 지적
- 기계 검산을 통해 표의 열 개수, 합계 일치 여부 등을 스크립트로 확인
- Born-digital PDF의 임베디드 텍스트를 정본으로 활용한 기계적 대조
- 스캔본의 경우 별도 세션에서 재작성 후 Diff 대조를 통한 검증 강화
수백 페이지에 달하는 PDF를 Markdown으로 변환하여 AI에게 읽힌다. 이전에 작성했던 그 방법은 지금도 제대로 작동하고 있다. 적어도, 그렇게 생각했다.
아래는 당시의 기사입니다.
【AI 주도 개발】 100페이지가 넘는 PDF를 Markdown으로 변환하여 AI에게 읽히는 방법
그런데 수백 페이지를 계속 흘려보내던 어느 날 깨달았다. 변환의 “검증(Verification)”을 작성한 본인에게 시키고 있었다는 것을. AI가 쓰고, 똑같은 AI가 검토하며, “맞습니다”라고 말한다. 인간의 교정이라면 결코 통과시키지 못할 구도를 한동안 의심조차 하지 않았다. 이번 v3는 바로 이 한 점을 없애기 위해 다시 만든 버전입니다. 인간의 일은 여전히 커피를 마시며 진행 상황을 바라보는 것 정도다. ☕
먼저, 이전의 방식을 복습해 두자. PDF를 1페이지당 1장의 이미지로 만들고, AI가 이미지를 정독하여 Markdown으로 옮기게 한 뒤, 5~10페이지씩 처리하여 쌓아 올린다. 골격으로서는 지금도 옳으며, v3의 토대이기도 하다. 무너져 있었던 것은 마지막 검증이었다.
예를 들어, 상품명인 『우롱차』를 『고려차』라고 써버렸다고 가정하자. 똑같은 AI에게 검토를 시키면 어떻게 될까? 일본어로서 자연스럽게 읽히기 때문에 그대로 통과되어 버린다. 작성한 본인은 자신이 쓴 것을 의심할 이유가 없기 때문이다. 이전 프로젝트에서 실제로 겪었던 사고들을 나열하면 그 성질이 명확해진다.
- 금액의 근사치 오독:
50과68,0과8. 형태가 비슷할 뿐 숫자는 별개다. - 로마 숫자 착오:
売変Ⅱ-2가売変Ⅲ-2로 변한다. Ⅱ↔Ⅲ은 정말 자주 발생한다. - 기호 혼동: 권한표의
○ × ★와 공란. 공란과 ×를 착각하면 가부(可否)가 반전된다. - 의미의 반전: “표준 판매가보다 높은 판매가는 설정할 수 없습니다”가 “낮은”으로.
마지막 것이 가장 무섭다. 금액이나 코드는 틀리더라도 “뭔가 이상하다”라고 걸릴 여지가 있다. 하지만 반전된 문장은 문장으로서 완전히 자연스럽게 읽힌다. 걸릴 리가 없다. 네 가지의 공통점은, 형식이 잘 갖춰져 있을수록 알아차릴 수 없다는 한 점이다. 그렇다면 “주의해서 다시 본다”는 원리적으로 막을 수 없다. 막을 수 있는 것은 작성자와는 다른 근거와의 대조, 그리고 세어보면 알 수 있는 것을 세는 기계뿐이다.
v3에서 가장 바꾼 것은 바로 그 부분이다. 검증의 근거를 작성자의 신고에서, **정본(正本, Original Source)**과의 대조로 옮겼다. 정본이란 AI와 독립적으로 정확성을 담보할 수 있는 정보원을 말한다. 여기서는 PDF가 내부에 가진 문자 데이터나, 별도의 계통인 OCR 판독 결과가 이에 해당한다. 그리고 대조하는 것은 사람이 아니라 스크립트, 말하자면 기계 감사역이다. 말로 하면 한 줄이지만, 효과는 전혀 다르다.
구체적으로는 세 가지 장치를 추가했다. 각각 “무엇을” “어떻게” 담보할지를 애매하게 하지 않기로 했다.
- ① 기계 검산: 표의 열 수가 헤더와 일치하는가. 합계 행과 내역의 합이 일치하는가. AI가 전기(Transcription) 시에 붙이는 미확정 마커(
[판독불능]/⚠)가 남아있지 않은가. 모두 “세어보면 알 수 있는” 종류의 일로, 사람도 AI도 세는 실수를 하지만 스크립트는 실수하지 않는다. 그래서 육안 확인을 그만두고 스크립트에 맡긴다. - ② 독립적인 정본 대조: Born-digital PDF(내부에 문자 데이터를 가진 디지털 생성 PDF)는 그 임베디드 텍스트를 정본으로서 추출하여 AI의 출력물과 기계적으로 맞추어 본다. Vision을 수치나 고유명사의 정본으로 삼지 않는 것이 핵심이다. 스캔본이라 정본이 없을 때는, 출력을 보고 있지 않은 별도의 세션에 똑같은 표를 다시 작성하게 하여 두 개를 차분(Diff) 대조한다. 두 사람이 같은 실수를 같은 장소에서 저지를 확률은 낮다.
- ③ 예산 트리거 & 인수인계 장부: 세션에서 생성한 Markdown의 누적량이 1만 자에 달하거나, AI가 세션의 무게로 인해 “슬슬”이라고 판단하면 구분을 제안한다. 상태는
task.md에 남겨, 다음 세션이 정밀도와 커버리지(Coverage)를 떨어뜨리지 않고 이어서 진행할 수 있도록 했다. 구분의 실제 사례는 후반부에서 그대로 보여준다.
이 세 가지가 어떻게 맞물리는지는 전체상을 한 장의 그림으로 보는 편이 빠르다.
한 가지만 미리 짚고 넘어가고 싶은 핵심이 있다. PDF를 이미지화하는 것“만”으로 끝내지 말 것. Born-digital PDF는 내부에 정확한 문자열을 가지고 있는데, 이미지로 변환하는 순간 그 정보를 버리게 된다. 단순히 아깝다는 차원의 이야기가 아니다. 버린 문자야말로 나중에 오독을 잡아낼 정본이 된다. 그래서 v3는 이미지와 텍스트 레이어(Layer) 양쪽을 모두 준비하여 역할을 나눈다. 이미지는 레이아웃과 표의 구조를 이해시키기 위해, 텍스트 레이어는 수치와 고유명사의 정확성을 대조하기 위해 사용한다. 한쪽의 약점을 다른 한쪽이 메우는 것이다.
이전 버전에서 독자들이 가장 막혔던 부분은 (제 생각에는) poppler의 설치였습니다. 환경 구축 단계에서 의욕이 꺾이는 것은 변환 이전의 문제이며, 가장 피하고 싶은 상황입니다.
그래서 v3 스크립트는 자기 완결형(self-contained)으로 만들었습니다. 변환 엔진 (PyMuPDF)이 없으면 자동으로 pip를 통해 도입하며, poppler의 시스템 설치는 필요하지 않습니다. 솔깃한 이야기처럼 들릴 수도 있겠지만, 실제로 poppler가 도입되지 않은 환경에서 시도하면 pdf2image는 정확히 에러를 내며 중단됩니다. 중단된 후, 스크립트가 스스로 PyMuPDF로 전환하여 끝까지 실행됩니다. 아래는 그때의 실제 로그입니다.
이미지화와 같은 방식으로 텍스트 레이어(text layer)도 추출해 둡니다. 여기서 각 페이지는 자동으로 두 가지로 분류됩니다. 『TEXT (정본으로 사용 가능)』인지, 아니면 『IMAGE-ONLY (스캔 등으로 인해 문자가 없음 = 독립적인 교차 검증이 필수)』인지. 해당 페이지를 어떻게 검증해야 할지가 정독을 시작하기 전에 결정됩니다.
명령어는 이 두 가지뿐입니다 (p.1-5를 배치(batch) 처리하는 예시).
# ① 이미지화 (400dpi)
python3 scripts/pdf_to_page_images.py 107.pdf 107_pages -r 400 -f 1 -l 5 --force
# ② 텍스트 레이어 추출 (정본 확보)
...
해상도에서 타협하면 이후의 모든 과정이 무너집니다. 150dpi는 표가 뭉개져 오독의 온상이 되며, 300dpi가 표준입니다. 작은 글자나 색상 구분 매트릭스(matrix)를 포함한다면 400dpi를, 600dpi는 무겁기만 할 뿐 보통은 필요하지 않습니다. 거친 이미지는 그대로 오독의 양으로 이어집니다. 고민된다면 '400dpi'로 정해두면 망설임을 줄일 수 있습니다.
방금 언급한 TEXT / IMAGE-ONLY는 단순한 분류가 아닙니다. 해당 페이지의 "정본(original text)"을 어디서 가져올 것인가의 분기점입니다. 그리고 그 기준은 "스캔 여부"가 아니라, 텍스트 레이어(text layer)가 존재하는가 존재하지 않는가라는 단 한 가지 지점입니다. 페이지 단위로 읽는 방식이 전환됩니다.
텍스트 레이어 있음 (born-digital PDF)… 문자가 PDF 내에 확정적으로 존재합니다. 텍스트 레이어가 그대로 정본이 됩니다. OCR의 역할은 없습니다.
텍스트 레이어 없음 (IMAGE-ONLY)… 스캔에 국한되지 않습니다. 사진, 스크린샷, 내보낸 이미지, 이미지만으로 구성된 PDF 등이 해당합니다. 원본이 "이미지"라면 모두 문자 데이터를 가지고 있지 않습니다. 여기서 처음으로 OCR이 필요합니다. 다만 주역이 바뀌지는 않습니다. AI가 구조와 의미로 전사(transcription)하고, 그 곁에서 RapidOCR(로컬에서 동작하며 토큰 비용이나 API 과금이 없는 OCR)이 문자만을 글자 그대로 읽어 독립적인 정본이 됩니다. AI는 의미를 통해 "그럴듯하게" 보완하는 데 능숙하며, 바로 그 지점에서 숫자를 잘못 옮기곤 합니다. RapidOCR는 의미를 이해하지 못하는 대신 픽셀 그대로를 읽습니다. 장단점이 정반대, 즉 오류가 발생하는 방식이 독립적이기 때문에 한쪽의 환각(hallucination)을 다른 한쪽이 잡아낼 수 있습니다. born-digital에서 텍스트 레이어가 담당하던 정본 역할을, 이미지에서는 OCR이 대신하는 구도입니다. (선명한 스크린샷이라면 AI 단독으로도 잘 읽을 수 있지만, 수치나 코드의 검증을 위해 OCR을 곁들이는 것은 손해 볼 것이 없습니다. 무료니까요.)
수기(handwriting)·흐릿함… AI도 OCR도, 동일하게 모호한 스트로크(stroke)를 각자 별개로 추측할 뿐입니다. 독립된 정본이 존재하지 않습니다. 따라서 확정 짓지 않고, ⚠️ 표시를 붙여 사람에게 넘깁니다.
성질을 하나 적어두고 싶습니다. 모든 페이지가 TEXT인 자료라면, OCR은 단 한 번도 구동되지 않습니다. 이번 555페이지의 경우도 그러했습니다. 전체가 born-digital이었기에 모든 분기가 텍스트 레이어 쪽으로 기울었고, OCR의 도움을 받을 필요가 없었습니다. 뒤집어 말하면, 이미지 페이지가 섞여 있는 바로 그 순간에만 다른 경로가 조용히 가동됩니다. born-digital의 속도와 확실성은 무엇 하나 희생하지 않습니다.
이 단계는 이전과 마찬가지로, 이미지를 한 장씩 정독시켜 Markdown으로 옮깁니다. 같다고 말하면서도, 세 가지 규칙을 추가했습니다. 같은 사고를 두 번 반복하지 않기 위해서입니다.
첫 번째는, "표"와 "스크린샷"을 『그림』이라는 카테고리로 묶지 않는 것입니다. 종별에 따라 취급을 달리합니다.
| 종별 | 예시 | 취급 |
|---|---|---|
| A 개념도·플로우 차트 | 업무 플로우, 화살표 도표, 개념도(ponchi-e) | 내용을 설명문으로 재현해도 좋음. 단, 도표 내의 라벨 문자는 생략 없이 전사 |
| B 데이터 표·권한 표 | ○×★ 권한 표, 구분값 매트릭스, 명세 | 축어적 전사(verbatim transcription). 열 수, 행 수, 모든 셀을 이미지와 동일하게. 요약·생략 금지 |
| C 장부·화면 내 수치 | 영수증, 금액, 등록 번호, 코드 | 축어적 전사. 숫자·기호를 단 한 글자도 바꾸지 말 것 |
B와 C를 "대략 이런 느낌"이라며 설명문으로 뭉뚱그리는 순간, AI는 존재하지 않는 열 이름이나 수치를 창작하기 시작한다. 실제로 열 이름은 『매장/바코드/카테고리명』, 값은 『64.17』, 『¥10,234』와 같이 통째로 그럴싸한 별개의 것으로 변해버린 사고가 있었다. 번거롭더라도 한 글자씩 옮겨 적는 수밖에 없다.
두 번째는, 고위험 토큰(Token)——로마 숫자, 코드, 금액, 기호, 반전어(Inversion word)——를 전기(Transcription)한 "그 즉시" 기계와 정본(Original document)에 대조하는 것이다. 나중에 모아서 하는 것이 아니다. 위험한 것일수록 작성 직후에 처리한다.
세 번째는, 모르는 것을 아는 척하며 채우지 않는 것이다. 전기하는 도중 확신이 서지 않는 셀이나 문자가 나오면, 그 즉시 ⚠️(기계가 세는 대상으로는 ⚠ 또는 [판독불능])를 붙이고 다음으로 넘어간다. 상상으로 채운 오류는 검증을 빠져나가지만, 표시(Mark)라면 셀 수 있다. 표시가 남아 있는 한, 다음 검증은 "아직 완료되지 않았다"라고 계속 말한다. 따라서 표시를 붙여둔 채로 완성할 수는 없다. ⚠️가 어떻게 붙고 어떻게 제거되는지는 후반부의 실물을 통해 보여주겠다.
배치(Batch)마다 3단계의 검증을 거치고, 자료 전체에 마무리 2단계를 적용한다. 3단계의 순서에는 의미가 있다. 가장 신뢰할 수 없는 자기 재독(Self-reading)을 맨 처음에 배치하면, 거기서 안심해 버려 기계 대조를 생략하게 된다. 본말전도다. 그래서 신뢰할 수 있는 순서대로 나열하고, 자기 재독은 마지막 그물로 돌렸다.
3-A 기계 검산: 생성한 Markdown을 스크립트에 돌려 열 수 정합성·합계 산술·[판독불능] / ⚠의 잔여 개수를 검출한다. 잔여 개수가 0이 아닌 배치는 아직 완료된 것이 아니다.
3-B 독립 대조: 독립된 정본과 기계 대조한다. Born-digital은 텍스트 레이어(Text layer), 텍스트 레이어가 없는 이미지(스캔, 사진, 스크린샷, 이미지 PDF)는 RapidOCR이 정본이다. AI의 출력에는 있으나 정본에는 없는 수치·코드는 환각(Hallucination)의 후보이다. 단, OCR은 누락이나 오독이 발생하므로, 불일치는 "AI가 틀렸다"가 아니라 "그 부분을 크롭(Crop)하여 재독하여 결판을 내라"는 신호이다 (대조할 수 있는 것은 문자뿐이다. 표의 구조 오류는 3-A·3-C의 담당이며, 수기(Handwriting)는 정본이 없으므로 사람의 몫이다).
3-C 자기 재독: 여기서 비로소 인간적인 읽기를 사용한다. 반전어와 같이 "기계로는 포착할 수 없는 의미의 붕괴"만을 이미지와 비교한다.
여기까지의 3단계는 배치가 끝날 때마다 매번 수행한다. 남은 2단계는 모든 배치가 끝난 후, 자료 전체를 마무리하는 단계다.
횡단 일치: 동일한 표가 여러 페이지에 재게재되는 자료는 페이지 간에 대조하여 명칭·수치·행수의 차이를 찾는다.
⚠️/[판독불능] 제거: 미확정 상태로 붙인 표시는 붙여둔 채로 두지 않는다. 마지막에 정본(다른 페이지의 장 표지, 목차의 재게재, 상호 참조 대상 등)과 대조하여 하나도 남김없이 지운다. 정본을 동일 자료 내에서 쉽게 얻을 수 있다면 미루지 않는다.
3-A는 명령어 한 줄로 끝난다. 구조·산술·마커에 더해 텍스트 레이어 대조와 세션 예산(Session budget)까지 한 번에 출력한다.
python3 scripts/check_md_output.py 107.md --textlayer 107_textlayer --budget --task task.md
이론은 여기까지다. 실물로 시험해 본다. 대상은 『헤이세이 21년도 산업기술조사사업(기술에 관한 시책 조사) 최종 보고서』(Arthur D. Little 작성, 총 555페이지)이다. 우선 앞부분 5페이지——표지, 목차, 배경과 목적, MFT(시장·기능·기술의 관계를 한 장에 정리하는 이 자료의 분석 프레임워크)의 개념도, 그 특징표——를 1배치만 돌려보았다.
목차의 1페이지에서 곧바로 Vision은 분야명을 몇 개 잘못 읽었다. 모두 일본어로서 자연스러운 오류였으며, 자기 재독만 했다면 거의 그냥 지나쳤을 종류다. 잡아낸 것은 텍스트 레이어와의 대조였다.
예를 들어 7번째 항목. 이미지를 본 AI는 『제품 분야』라고 읽었다. 그럴싸하다. 하지만 정본은 다르다. 여기서 추출한 텍스트 레이어(page_002.txt)를 정형(Formatting)이나 생략 없이 전문 그대로 붙인다. 추출한 것의 일부만 보여주면 불리한 부분을 누락했다고 의심받을 수 있다. 그래서 추출 순서가 뒤섞여 있는 것이 눈에 보이더라도 전부 실는다. 이 뒤섞임 자체가 나중에 효과를 발휘한다.
1
Contents
2
...
나머지는 길어서 접기
1
Contents
2
...
원본에서는 『7 → 제철 분야』라고 되어 있다. 『제품』이 아니라 『제철』이다. 같은 식으로, 『가업』이 아니라 『요업』, 『과제』가 아니라 『비축』, 『산업 기반』이 아니라 『전력 기반』, 페이지도 『365』가 아니라 『372』이다. 모두 한 글자나 한 자릿수의 차이지만, 의미는 완전히 달라진다. 이것이 사람의 손을 거치지 않고 도출되는 것이, v3에서 얻고자 하는 가장 큰 안심이다.
왜 효과가 있는가. Vision은 레이아웃과 표 구조를 파악하는 데 강한 반면, 세밀한 한자나 숫자는 잘못 읽을 수 있다. 텍스트 레이어 (Text Layer)는 문자열 자체는 정확하지만, 레이아웃을 가지고 있지 않다. 그래서 역할을 나누었다. 구조는 이미지에, 문자의 정확성은 텍스트 레이어에 묻는다. 서로의 빈틈을 서로가 메우는 것이다.
물론, 텍스트 레이어도 만능은 아니다. 방금 전의 전문을 다시 살펴보면, 번호 「30」, 「32」가 명칭과 떨어져 끝부분에 떨어져 있다. 추출 순서가 흐트러진 것이다. 그 덕분에 오른쪽 열의 #30~35는 분야명의 철자와 페이지 번호의 정확도는 높았지만, 어떤 번호가 어떤 분야인지를 목차 텍스트만으로는 확정 지을 수 없었다.
이때, 수중에 있는 Markdown은 다음과 같았다. Step 3의 마지막에 붙여둔 로그의 [3] 미확정 마커: ⚠=7 (오른쪽 열 #30-35 확인 필요 플래그)
가 가리키고 있었던 것이 바로 이 상태다 (표 자체는 당시의 중간 출력을 남겨두지 않았기 때문에, 동일한 상태를 재현한 발췌본이다).
| # | 분야 | 페이지 |
|---|---|---|
| 30 | ⚠️ 기술진흥분야 (이 번호와의 대응을 목차에서는 확정할 수 없음) | 438 |
...
Step 2의 세 번째 규칙대로, 채우지 않고 표시를 남긴 채 앞으로 진행한다. 동시에, task.md의 미결 레지스터 (Unresolved Register)에 이 보류 사항을 쌓아둔다.
## 4. 미결 레지스터
- [ ] p.2 목차·오른쪽 열 #30~35: 번호와 분야명의 대응이 미확정 (텍스트 레이어의 추출 순서 흐트러짐)
→ 확인 대상 후보: 각 장의 표지 페이지 (p.438, p.450, p.467 …)
하지만, ⚠️를 붙이고 끝내는 것이 아니다. ⚠️는 답을 포기했다는 표시가 아니라, 판단을 일단 보류했다는 임시 배치일 뿐이다. 마지막에 원본과 대조하여 하나도 남김없이 없앤다. 게다가 원본이 같은 PDF 안에 저렴하게(쉽게) 있다면 미루지도 않는다. 목차 텍스트가 부실하더라도 각 분야의 본문 페이지가 있다. 그래서 해당 장의 실제 페이지(p.438, p.467…)를 열었다.
그러자 장의 표지가 목차를 통째로 재게재하고 있었고, 해당 장의 행만 회색 배경으로 반전시켜 놓았다. p.438에서는 『30 기술진흥분야』가, p.467에서는 『32 화학물질관리분야 2』가 명확하게 드러나 있었다. 번호와 분야명의 대응은 이것으로 확정되었다.
임시 배치되었던 행은 다음과 같이 바뀐다.
- | 30 | ⚠️ 기술진흥분야 (이 번호와의 대응을 목차에서는 확정할 수 없음) | 438 |
+ | 30 | 기술진흥분야 | 438 |
미결 레지스터의 해당 행에도 확인 완료 표시가 붙으며 마침내 해결된다. 모르는 부분을 아는 척하며 채우지 않는 것과, ⚠️를 붙여둔 채 방치하지 않는 것은 같은 규율의 양면이다.
변환 후에는 목차를 그대로 검색 및 참조할 수 있는 Markdown 테이블이 된다 (오독은 수정 완료). 여기서도 생략하지 않는다. 전체 37개 분야를 전문으로 싣는다.
### 조사 결과 분야 목록 (37개 분야)
| # | 분야 | 페이지 | # | 분야 | 페이지 |
|---|---|---|---|---|---|
...
표도 마찬가지다. MFT의 특징과 효용에 관한 2열 표를 요약하지 않고 축자적으로 옮긴다. 기계 검산으로 「3개 표 모두 열 수 일치」도 확인 완료했다. 생략 기호는 사용하지 않고 모든 셀을 기재한다.
| MFT의 특징 | 조사에서의 효용 |
|---|---|
| 각 레이어에서의 선택지 폭을 볼 수 있음 | 각 사업이 상정하고 있는 선택지와 선택하지 않은 선택지가 명확해지기 때문에, 각 사업의 계획의 타당성(충분조건성)을 평가하는 토대가 된다 (일반적인 사업 계획에서는 선택한 결과만 표현되므로, 필요조건밖에 확인할 수 없기 때문) |
...
방금 전의 #30~35는 이와 같은 불안(⚠️)을 원본으로 해소한 이야기였다. 해결 방법은 하나 더 있다. 기계가 "이것은 수상하다"라고 지목하는 경우다. 다른 페이지——p.80, 정보 보안 기본 계획의 목표를 정리한 표——를 전사하여, 3-A의 기계 검산에 돌렸다. 구조도 산술도 문제없었다. 다만, 텍스트 레이어 대조에서 한 곳이 걸렸다.
[4] 텍스트 레이어 대조: 확인 필요 1건
환각 후보 (텍스트 레이어에 없는 수치/코드): 1건
- 2009
출처의 각주에 적힌 『2009』가 원본(正本)에서 발견되지 않는다는 것이다. 같은 페이지에서 사용된 『2012』는 그냥 지나쳤는데 말이다. 여기서 실수하기 쉽다. 기계가 의심했으니 고쳐야지 하며 다른 숫자로 바꿔 쓰고 싶어진다. 하지만 플래그(Flag)는 판결이 아니다. 우선 원본을 본다. 각주를 크롭(Crop)하여 확대했다.
『제2차 정보 보안 기본 계획(2009년 2월 3일 정보 보안 정책 회의)』. 2009가 맞다. 역사적 사실과도 일치한다. 그렇다면 왜 대조 과정에서 걸러졌을까. 원본을 들여다보니 답이 있었다. 텍스트 레이어(Text Layer)가 이 연호를 **전각(Full-width)**인 『2009』으로 가지고 있었다. 나는 **반각(Half-width)**인 『2009』로 적었다. 같은 숫자지만, 문자로서는 별개의 것이다. 기계는 그저 솔직하게 "그런 배열은 없다"라고 말했을 뿐이었다. 앞서 『2012』가 그냥 지나갔던 이유는 원본 안에 반각 『2012』와 전각 『2012』가 모두 있었고, 우연히 반각 쪽과 일치했기 때문이다.
결론은 이렇다. 내용은 맞다. 플래그의 정체는 전각과 반각의 차이일 뿐, 환각(Hallucination)이 아니다. 따라서 내용은 그대로 확정하되, 출력은 AI 가독성을 우선하여 반각으로 통일한다(원본이 전각이었다는 사실은 기록으로 남긴다). 플래그를 없앤다는 것은 기계의 지적을 맹목적으로 믿거나 묵살하지 않고, 원본과 대조하여 흑백을 가리는 것이다. 때로는 "기계가 과민했다"가 결론이 되기도 한다. 그럼에도 직접 확인해 보기 전까지는 알 수 없다.
자, 5페이지는 이것으로 끝났다. 남은 분량은 550페이지다. 하나의 세션(Session)에서 끝까지 가는 것은 불가능하다. 컨텍스트(Context)가 채워질수록 세부적인 정밀도는 조용히 떨어진다. 그렇다고 1배치(Batch, 이번에는 5페이지)마다 끊어서 다시 시작하는 것은, 어렵게 확보한 넓은 문맥 창(Context Window)을 버리는 것과 같다.
타협점으로, v3는 다음과 같은 "빠른 시점"에 구분을 제안한다. 마음대로 멈추지 않는다.
① 기계 트리거(Machine Trigger): 누적 출력이 1만 자에 도달했을 때. 스크립트가 계산하는, 움직이지 않는 하한선이다.
② 판단 트리거(Judgment Trigger): 1만 자에 도달하지 않더라도, 왕복 횟수나 읽어들인 이미지·대용량 파일의 양, 문맥 압축(Context Compression)이 실행되었는지 여부에 따라 AI가 "슬슬 위험하다"고 느끼면 선제적으로 제안한다. 이는 추정치로 충분하다. 틀리더라도 ①이 하한선을 지켜줄 것이다.
구분할 때는 기억에 의존하지 않는다. task.md라는 인수인계 장부에 상태를 전부 기록한다. 다음 세션은 이것을 읽는 것만으로 망설임 없이, 빠짐없이 이어서 진행할 수 있다. 장부는 이 5개 블록으로 구성된다.
## 0. 재개 절차 (새 세션이 가장 먼저 읽는 순서)
## 1. 계획 (작업 경로 / 검증 강도 / IMAGE-ONLY 페이지 등)
## 2. 배치 장부 (✅ 완료 · 👉 다음 배치 · 📊 예산 누적 글자 수)
...
가장 효과적인 것은 『4. 미결 레지스터(Unresolved Register)』라고 생각한다. 확인이 필요한 셀을 여기에 올려두면, 세션을 넘어가더라도 "미결 상태인 채로 완료 처리"되는 가장 조용한 사고를 방지할 수 있다. 방금 실물을 보여준 것처럼, #30~35의 ⚠️ 도 여기에 머물러 있었다. 실제 페이지를 읽어 원본을 확인하고 대응을 확정한 뒤에야 비로소 삭제한다. 미결 사항은 채우거나 원본으로 해결하거나 둘 중 하나를 선택하여, 공중에 붕 뜬 상태로 두지 않는다.
마지막으로, 화려하지는 않지만 실무에서 유효한 한 수. 이전에는 생성된 이미지나 Markdown(md) 파일이 툴과 같은 장소에 어지럽게 흩어지기 일쑤였다. 정리는 나중에 할수록 번거로워진다.
v3는 착수 전에 반드시 출력 위치를 묻는다. "어디에 폴더를 만들까요?"라고 질문하며, 권장안 2개와 자유 입력을 제공한다. 이미지, 텍스트 레이어, md, 장부 모두 작업 경로 측으로 모은다. 툴은 코드 전용 상태로 유지한다. 이번에도 변환 작업 일체는 107_변환/에 집약했다.
이전 방식의 골격——PDF → 이미지 → AI 정독 → Markdown——은 v3에서도 그대로 살아있다. 바뀐 점은 그 위에 "AI의 거짓말을 어떻게 없앨 것인가"를 정면으로 쌓아 올렸다는 것이다.
| 관점 | 내용 |
|---|---|
| 과제 | 대량 PDF + AI의 "그럴듯한 거짓말" (형식이 잘 갖춰져 있을수록 알아채기 어려움) |
| ... |
도입부의 "그 'OK입니다'를 어디까지 믿을 수 있는가"로 돌아가자. 답은, 작성한 본인의 "OK"는 믿지 않는 것이다. 다른 근거를 가지고 기계에게 말하게 하라. "틀리면 곤란한" 수치나 고유명사가 많은 자료일수록 이 독립 감사(Independent Audit)는 효과적이다. 인수인계나 신규 참여 시 "자료가 PDF뿐이라 힘들다"라고 말하는 분들은 꼭 한번 시도해 보길 바란다. 이제 남은 것은 커피를 내리는 방법 정도뿐이다.
이용하는 Skill이나 스크립트는 여기에서 설치할 수 있습니다!
끝까지 읽어주셔서 감사합니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기