언제 쓰나
"담도암 환자에게 쓸 수 있는 요법이 뭔가"를 답하는 DB를 만들 때. ctrial-collect + cancer-type-classify가 임상시험 쪽을 맡는다면, 이쪽은 이미 승인·급여되는 치료를 맡는다.
넓게는 PDF 표를 Vision LLM으로 구조화 DB에 넣는 방법의 정본이다.
정본 코드
~/Documents/IMOK/허초프로젝트/
extraction/ 추출 산출물 + 캐시 + 교정 파일 (JSON)
regbook_vision/, geupyeo_vision/ Vision 추출
regbook_pages/ 페이지별 결과
*_cache.json API 응답 캐시
*_corrections.json, review_state.json, verify_findings.json 검토·교정
scripts/ crop_*, extract_*, enrich_*, apply_*, build_*, export_*
db_design/
데이터_출처_정리.md ★ 테이블·컬럼별 출처를 전부 기록한 문서
schema_v2.sql, d1_import.sql, d1_overlay.sql
항암제_DB설계서_v2.xlsx, verify_report.md
web/, wrangler.toml Cloudflare D1 + Workers 배포
~/Documents/IMOK/oncoalert/ 같은 DB를 쓰는 웹 서비스바로 쓰는 코드
python3 scripts/corrections.py --records out.json --corrections fix.json --key id --out final.jsonscripts/corrections.py — apply_*.py 들이 공유하던 패턴을 하나로. apply_corrections(멱등, before/after 기록, 교정 표시) · split_review(확인완료/필요/미검토) · orphan 경고. 교정 대상이 사라진 키를 반드시 보고한다 — 재추출로 레코드가 없어지면 사람 검토가 소리 없이 증발한다.
소스가 7개다 — 출처 문서를 먼저 써라
| 코드 | 소스 | 형태 | 무엇을 줌 |
|---|---|---|---|
| S1 | 약제급여목록·급여상한금액표 (HIRA) | 엑셀 | 제품목록·EDI·주성분코드·약가 |
| S2 | 사전신청요법·불승인요법 (HIRA 허초 심의결과) | 엑셀 | 허가초과 요법(인정/검토중/불승인) |
| S3 | 항암제보험급여 공고책자 (HIRA, 연 1회) | 급여 요법·고시번호·투여대상·투여단계 | |
| S4 | Onco Regimen book (아산병원 종양내과) | 표준요법·용법용량·투여일·참고논문 | |
| S5 | 식약처 제품허가정보 API | API | 영문명·약효분류·전문일반·EDI |
| S6 | 식약처 허가상세 API | API | ATC코드·효능효과·용법용량·허가변경이력 |
| S7 | 식약처 대조약 API | API | 오리지널 여부 |
db_design/데이터_출처_정리.md가 테이블·컬럼별로 어느 소스에서 왔는지 전부 적어둔다. 소스가 여럿이면 이 문서 없이는 몇 달 뒤 아무도 값의 근거를 못 찾는다.
두 가지가 특히 중요하다:
- AI 수기 입력 항목을 따로 표시한다.
cancer_aliases(NSCLC 같은 영문 약어)는 "⚠️ 자료 아님 — 약제부 검토 예정"으로 명시돼 있다. 근거 자료가 있는 값과 AI가 채운 값을 섞으면 신뢰도가 통째로 무너진다. - 제거한 소스도 이유와 함께 남긴다. "약가기준정보 API(심평원)는 제거됨 — 약가는 S1 엑셀에서 받음."
PDF → 구조화: crop 후 Vision
crop_regbook_cards.py / crop_geupyeo_criteria.py 페이지에서 카드·표 영역을 잘라냄
→ regbook_vision/, geupyeo_vision/ Vision LLM으로 추출
→ regbook_pages/ 페이지별 JSON
→ regbook_schema_v3_samples.json 스키마 정착페이지 전체를 통째로 Vision에 넣지 마라. 요법 카드 단위로 잘라 넣어야 정확하다. crop 결과는 사람이 검토하고(crop_review_decisions.json), apply_crop_review.py로 반영한다.
암종 단위로 나눠 산출한다(regbook_esophageal.json, regbook_gastric_TEST.json, regbook_page005_braintumor.json). 한 암종부터 스키마를 확정하고 나머지로 넓힌다.
API 응답은 캐시한다
drug_detail_cache.json, drug_permit_cache.json, recollect_cache.json. 식약처 API는 느리고 호출 제한이 있으며, enrich 스크립트를 여러 번 돌리게 되기 때문에 캐시가 없으면 작업이 불가능하다. enrich_from_cache.py가 캐시만으로 재구성한다.
사람 교정을 데이터로 관리한다
이 프로젝트에서 가장 배울 점이다. 교정을 코드에 넣지 않고 JSON 파일 + apply 스크립트로 분리했다:
| 파일 | apply 스크립트 |
|---|---|
regimenbook_dose_corrections.json | apply_dose_corrections.py |
crop_review_decisions.json | apply_crop_review.py |
needs_confirm.json | apply_needs_confirm.py |
_review_flags_cache.json | apply_review_flags.py / apply_flag_dispositions.py |
verify_corrections.json, orphan_corrections.json | apply_review.py |
verified_ok.json, verify_findings.json, review_state.json | 검토 상태 추적 |
추출을 다시 돌려도 사람이 고친 내용이 살아남는다. 추출 → 검토 → 교정 JSON → 재적용이 반복 가능한 파이프라인이 된다. 추출 결과를 직접 수정하면 다음 재추출에서 전부 날아간다.
needs_confirm.json(확인 필요)과 verified_ok.json(확인 완료)을 나눠 검토 진행 상황을 추적한다.
빌드·배포
build_db.py / build_sqlite.py / build_drugcost.py SQLite 생성
enrich_db.py / enrich_detail.py / enrich_permit.py API로 보강
export_d1.py → d1_import.sql + d1_overlay.sql Cloudflare D1
export_web.py → web/ 정적 웹d1_overlay.sql을 d1_import.sql과 분리한 게 요령이다 — 전체 재적재 없이 변경분만 덮어쓴다.
프로젝트 문서를 Obsidian으로 관리한다
CLAUDE.md가 Obsidian 볼트(~/Documents/obsidian/dnlife/허초프로젝트/)의 6개 문서를 규정한다: 00_Overview(고정 정보), 01_Plan, 02_Progress(날짜별), 03_Decisions(기술 결정), 04_Issues(미해결), 05_Completed(검증 완료만).
규칙이 좋다 — 덮어쓰지 않고 날짜별로 추가, 실제 완료한 것만 기록, 확인되지 않은 내용은 추측하지 않음. 긴 프로젝트에는 이 방식을 그대로 쓸 만하다. obsidian-mcp-connector MCP로 접근한다.
함정
oncoalert.db-shm/-wal이 있다. SQLite WAL — 복사 시 세 파일을 함께.- 공고책자는 연 1회 갱신된다. 고시번호가 바뀌면 요법 매칭이 깨진다.
relink_report.json이 재연결 결과를 남긴다. _experiments/,_archive/에 시도한 것들이 남아 있다.
연관 skill
ctrial-collect·cancer-type-classify(담도암 치료옵션 조합), excel-case-validator