언제 쓰나
건강검진·영상 검사의 결론(conclusion) 텍스트에서 질병코드나 수치 판정을 뽑을 때. 코드 체계가 정해져 있고(유효 코드 집합이 있음) 오답을 내면 안 되는 상황.
pathology-llm-extract가 자유 필드를 뽑는다면, 이쪽은 닫힌 코드 집합으로 분류한다 — 그래서 규칙 기반 폴백이 성립한다.
정본 코드
~/Documents/medical_code_extractor 2/
keyword_extractor.py 규칙 기반 추출 (문장 단위 정밀 분석)
llm_extractor.py LLM + 힌트 주입 + 폴백
coronary_extractor.py 관상동맥 전용 (Keyword/LLM 쌍 + 팩토리)
pwv_abi_extractor.py PWV·ABI 전용
keywords/ carotid_keywords.py, coronary_keywords.py, pwv_abi_keywords.py
prompts/ 검사별 프롬프트 빌더
ollama_client.py 온프렘 LLM
excel_handler.py 입출력
api_server.py FastAPI
~/Documents/건강의학과/ 검증 케이스 (120/140/199/206건 xlsx)건강의학과/medical_code_extractor에도 사본이 있다. ~/Documents/medical_code_extractor 2/가 정본이다.
핵심: 규칙이 힌트를 만들고 LLM이 판단한다
1) KeywordExtractor.extract(text) → KeywordHints
2) format_hints_for_llm(hints) → 힌트 텍스트
3) prompt_builder(text, hints_text) → system/user 프롬프트
4) LLM 호출 → JSON 파싱
5) _postprocess_codes(codes, hints) → 유효 코드 검증
6) JSON 파싱 3회 실패 → KeywordExtractor 결과로 폴백규칙 결과를 LLM에 넘기되 최종 판단은 LLM이 한다. 규칙만 쓰면 표현 변형에 약하고, LLM만 쓰면 근거 없이 코드를 만든다. 그리고 규칙 결과가 살아 있으므로 LLM이 무너져도 답을 낼 수 있다 — 이게 이 구조의 진짜 가치다.
규칙 추출기가 잡아야 하는 것
VesselFinding이 담는 필드가 곧 규칙 추출기의 요구사항이다:
vessel 어느 혈관/부위
severity 중증도 표현
severity_level 비교 가능한 숫자 등급
stenosis_percent 협착률 수치
context 근거 문장
negated ★ 부정문 여부negated가 가장 중요하다. "no significant stenosis"를 협착으로 잡으면 전부 틀린다. 의료 판독문은 정상 소견을 부정문으로 쓴다. 그래서 문서 전체가 아니라 문장 단위로 쪼개 분석한다.
vessel_max_severity: Dict[str, VesselFinding]로 혈관별 최고 중증도만 남긴다. 같은 혈관이 여러 문장에 나오면 가장 심한 것이 판정 기준이다.
폴백 정책
max_json_retries = 3 # JSON 파싱 실패 재시도3회 실패하면 KeywordExtractor.get_disease_codes(hints)로 폴백하고, reasoning에 "KeywordExtractor fallback (JSON 파싱 3회 실패)"를 남긴다. 폴백 코드도 없으면 success=False.
폴백으로 나온 결과임을 반드시 표시해라. 나중에 정확도를 볼 때 LLM 결과와 폴백 결과를 섞어 계산하면 안 된다.
json_mode=False로 호출하고 직접 파싱한다(_parse_response_strict). 온프렘 모델은 json_mode를 제대로 지원하지 않는 경우가 많다. max_tokens=8192 — 4096에서 올렸다. reasoning 모델은 thinking에 토큰을 많이 쓴다.
검사 종류 추가하는 법
각 검사가 동일한 3종 세트를 가진다:
keywords/<검사>_keywords.py 위치·중증도 키워드 사전
prompts/<검사>_prompt.py 프롬프트 빌더
<검사>_extractor.py KeywordExtractor + LLMExtractor + create_<검사>_extractor() 팩토리create_carotid_extractor(ollama_client) 같은 팩토리로 조립한다. 키워드 사전과 프롬프트를 데이터로 분리했기 때문에 새 검사 추가가 파일 3개 추가로 끝난다.
함정
valid_codes: Set[int]로 유효 코드 집합을 강제한다. LLM이 존재하지 않는 코드를 만들어내면_postprocess_codes에서 걸린다. 닫힌 코드 체계면 반드시 사후 검증해라.- 결론 텍스트만 넣는다. 판독문 전체를 넣으면 소견 부분의 언급이 결론과 충돌한다.
thinking필드를 따로 보관한다 — 온프렘 reasoning 모델 대응은llm-provider-switch참조.- 검증 케이스가
건강의학과/에 xlsx로 있다(경동맥 120·170건, 관상동맥 140건, 199·206건). 규칙을 고치면 여기로 회귀 검증해라.
연관 skill
pathology-llm-extract, llm-provider-switch, excel-case-validator