## 한 줄 요약
기존 단어시험지 Skill Tree는 사용자가 직접 입력한 영단어 | 한글 뜻 목록만 처리했다. 실제로 사용하는 단어책의 페이지를 사진을 찍거나 스캔한 pdf를 처리하도록 하여 더욱 편리하게 만들었다.
앞단에 vocabulary-scan-ocr-extractor를 새로 추가해 스캔 PDF·사진을 렌더링하고, vision-assisted OCR 결과의 원본 페이지와 confidence를 보존하도록 확장했다. 불확실 항목이 하나라도 있으면 시험지를 만들지 않고 HOLD_OCR_REVIEW_REQUIRED로 멈추게 했다. 실제 4쪽 독립 재검수에서 불확실 항목 1개가 발견돼 전체 범위를 HOLD한 뒤, 확정된 앞쪽 20개로 범위를 좁혀 PASS 시험지를 만들었다. 기존 기능을 포함한 자동 테스트 10개와 active runtime 실행도 통과했다.
## 기존 흐름의 실제 병목
기존 Skill Tree는 다음 네 책임으로 구성돼 있었다.
Umbrella
├─ Validator
├─ Composer
├─ Verifier
└─ Teacher Human Gate시험지·정답지 생성과 검증은 자동화됐지만, 시작 입력은 사람이 직접 만들어야 했다.
word | meaning시간이 부족한 수업 준비 상황에서는 이 입력 타이핑이 가장 큰 병목이었다. 그래서 기존 Skill을 크게 만드는 대신, 실패 책임이 다른 OCR 단계를 별도 하위 Skill로 분리했다.
## 새 Skill 경계
새 하위 Skill의 이름은 다음과 같다.
vocabulary-scan-ocr-extractor### 이 Skill이 맡는 일
- 이미지형 PDF를 페이지별 PNG로 렌더링
- 원본 SHA-256·페이지 수·내장 텍스트 수 기록
- 대표 표제어와 인쇄된 한글 뜻을 vision-assisted OCR로 구조화
- PDF 페이지와 책 페이지 보존
- confidence 기록
- 확정 항목만 word | meaning으로 내보내기
### 이 Skill이 맡지 않는 일
- OCR에 없는 뜻 추측
- 예문·어원·파생어를 자동 시험 범위로 채택
- 손글씨 메모를 인쇄 뜻으로 간주
- 시험지 렌더링
- 인쇄·학생 배포
- 원본 스캔 공개
## 업데이트된 Tree
vocabulary-test-maker-umbrella
├─ vocabulary-scan-ocr-extractor
├─ vocabulary-list-validator
├─ vocabulary-test-composer
├─ vocabulary-test-verifier
└─ Human Gate: teacher approvaltree는 소속만 보여준다. 실제 전달과 실패 반환은 Circuit으로 분리했다.
## 업데이트된 Circuit
교사의 스캔 PDF·사진
→ OCR Extractor
흐림·반사·잘림·회전 문제 → 재스캔 요청
단어·뜻 불확실 → OCR Human Gate HOLD
확정 pair → Validator
→ Composer
→ Verifier
답 노출·순서·뜻 불일치 → Composer로 반환
PASS → Teacher Human Gate
→ 교사 승인 후에만 별도 인쇄·배포 가능## Interface Card
yaml
input:
source: scanned PDF or page images
scope: printed primary headword + printed Korean definition
intermediate:
extraction_json:
fields: pdf_page, book_page, word, meaning, confidence
output:
vocabulary_txt: "word | meaning"
receipt: ocr-receipt.json
status:
PASS: 확정 항목만 있고 미해결 OCR 이슈 없음
HOLD: 확정 항목과 확인 필요 항목이 함께 있음
BLOCKED: 확정 가능한 pair가 없음
failure_return:
image_quality: teacher rescan
uncertain_ocr: OCR Human Gate
duplicate_or_format: Validator
## 테스트를 먼저 실패시켰다.
구현 전에 네 가지 테스트 계약을 만들었다.
1. 확정 OCR 항목은 word | meaning으로 변환돼야 한다.
2. 불확실 항목은 출력에서 제외되고 HOLD가 돼야 한다.
3. 확정 항목만 있으면 OCR→시험지 전체 흐름이 PASS해야 한다.
4. 확인 필요 항목이 있으면 시험지 파일 자체가 생성되지 않아야 한다.
정규화 스크립트와 스캔 Umbrella가 없는 상태에서 테스트가 먼저 실패하는 RED를 확인했다.
OCR normalizer tests: 2 FAIL
scanned umbrella tests: 2 FAIL이후 최소 구현으로 GREEN으로 전환했다.
OCR/scan tests: 4 PASS
기존 Validator·Composer·Verifier·Umbrella 회귀 테스트: 6 PASS
전체 자동 테스트: 10 PASS## 통제된 OCR 실패가 실제로 멈추는지 확인했다
다음 fixture를 사용했다.
json
{
"entries": [
{
"word": "cache",
"meaning": "은닉처",
"confidence": "confirmed"
},
{
"word": "[불확실]",
"meaning": "불명",
"confidence": "needs_review"
}
]
}실행 결과는 다음과 같았다.
status: HOLD
confirmed: 1
needs_review: 1
human_gate: HOLD_OCR_REVIEW_REQUIRED
proof_verdict: NOT_RUN
test_generation: NOT_PERFORMED
external_release: NOT_PERFORMED확실한 단어 하나가 있어도 불확실 항목이 섞여 있으면 시험지 생성 단계로 넘어가지 않았다. OCR이 그럴듯한 오답을 만들고 조용히 통과하는 것을 막는 Gate다.
## 실제 스캔 PDF에서 독립 검수가 실패를 발견했다
실제 샘플은 4쪽의 이미지형 PDF였다.
내장 텍스트: 0자
대표 표제어 후보: 44개
seed: 23첫 판독에서는 44개를 모두 high로 분류했지만, 별도 판독자가 원본 페이지를 다시 확인하자 마지막 페이지의 dispense with는 인쇄 뜻 뒷부분이 선명하지 않다는 결과를 반환했다. 그럴듯한 뜻을 보충하지 않고 해당 항목을 needs_review로 낮췄다.
전체 4쪽을 다시 실행한 결과는 다음과 같았다.
confirmed: 43
needs_review: 1
status: HOLD
proof: NOT_RUN
test_generation: NOT_PERFORMED
human_gate: HOLD_OCR_REVIEW_REQUIRED불확실한 한 항목 때문에 전체 44개를 확정했다고 주장하지 않았다. 대신 시험 범위를 PDF 앞쪽 1~2쪽의 독립적으로 읽을 수 있는 첫 20개 표제어로 명시적으로 좁혔다. 확인 필요 항목은 전체 OCR receipt에 남기고 시험 범위에서만 제외했다.
안전 범위 재실행 결과는 다음과 같았다.
ocr_confirmed: 20
selected: 20
status: PASS
proof: PASS
reproducible: true
human_gate: PENDING_TEACHER_APPROVAL
external_release: NOT_PERFORMED학생용·교사용 Word 파일까지 만든 뒤 독립적으로 내용을 다시 읽었다.
학생용 정답 뜻 누출: 0
학생용 단어 누락: 0
교사용 단어 누락: 0
교사용 뜻 누락: 0
DOCX 구조: PASS## Hermes active profile에 실제 설치했다
working copy에서만 검증하고 끝내지 않았다. active Hermes profile을 백업한 뒤 다음 변경을 적용했다.
신규 설치:
- vocabulary-scan-ocr-extractor v0.1.0
업데이트:
- vocabulary-test-maker-umbrella v2.1.0PyMuPDF도 Hermes Python 환경에 설치해 다음 대화에서 이미지형 PDF를 페이지별로 렌더링할 수 있게 했다. 대용량 marker OCR 모델은 설치하지 않았다.
CLI Skill 목록에서 OCR Skill은 enabled로 발견됐다. active 설치본으로 원본 PDF를 다시 실행했을 때 전체 범위는 독립 검수에서 발견된 불확실 1건 때문에 HOLD됐고, 확정된 20개 범위만 별도로 실행해 PASS했다.
PDF render: PASS, pages=4, embedded_text_chars=0
전체 범위 OCR: HOLD, confirmed=43, needs_review=1
전체 범위 시험 생성: NOT_PERFORMED
안전 범위 OCR pipeline: PASS, confirmed=20, selected=20
Verifier: PASS
Reproducibility: true따라서 Skill 문서만 저장된 상태가 아니라 discovery, 실패 차단, 안전 범위 behavior를 모두 확인했다.
## 백업과 Rollback
working copy와 active profile을 수정하기 전에 각각 전체 백업을 만들었다.
- 파일별 SHA-256 manifest 생성
- source와 backup readback 일치 확인
- 별도 복원 경로로 restore rehearsal 실행
- restore rehearsal PASS
문제가 생길 경우 가장 최근 active profile 백업의 payload로 복원할 수 있다. 실제 rollback은 필요하지 않아 실행하지 않았다.
## 공개 경계
공개 사례글과 업로드 ZIP에는 다음을 넣지 않았다.
- 원본 단어책 스캔
- 책 제목과 전체 단어 목록
- 로컬 절대경로
- 학생 개인정보
- 비공개 URL
스캔 원본은 개인 수업용 검증에만 사용했다.
## 남은 Human Gate
자동 검사에서 PASS가 나왔지만 다음은 여전히 교사의 책임이다.
- OCR 뜻이 실제 수업에서 가르친 표현과 맞는지
- 시험 범위와 문항 수가 맞는지
- Word 문서의 실제 페이지 배치가 적절한지
- 인쇄·학생 배포를 승인할지
LibreOffice가 없어 Word의 시각적 렌더링은 not_checked로 남겼다. 따라서 실제 인쇄 전 Word 파일을 열어 표와 줄 간격을 눈으로 확인해야 한다.
## 배운 점
OCR를 기존 Skill 안에 단순히 추가하는 것보다 별도 책임으로 분리하는 편이 안전했다.
- 스캔 품질 실패는 OCR Extractor의 책임
- 입력 중복·형식은 Validator의 책임
- 시험지·정답지 렌더링은 Composer의 책임
- 답 노출·1:1 대응은 Verifier의 책임
- 실제 수업 사용은 교사 Human Gate의 책임
특히 OCR에서는 PASS를 늘리는 것보다 불확실할 때 시험 생성을 중단하는 HOLD 기준이 더 중요했다.