Repository navigation
[Feat] OCR 약 추출 LLM 하이브리드 — 처방전=파서, 약봉투/영수증=gpt-4o #100
Copy link
Copy link
Closed
Labels
✨ Feature기능 개발기능 개발
Description
📌 Description
OCR로 rawText/좌표는 잘 가져오는데, 이를 {약이름, 1회량, 1일횟수, 총일수} 구조로 바꾸는
파싱이 서식마다 깨진다. OcrParser(정규식+좌표)는 알려진 서식만 커버해 새 서식마다 코드를
추가해야 하고, 뭉개진 약 이름(지스로먹스장 → 지스로맥스정)은 교정할 수 없다.
파싱 단계를 LLM 추출로 검증하고, 측정 결과에 맞는 도입 방식을 정한다.
전제: #96(회귀 하네스), #99(파서 고도화) 머지. fixture 9종이 "정답"을 정의.
🎯 목표
- fixture 9종으로 파서 vs LLM(gpt-4o) 약 단위 정확도 측정
- 측정 결과에 맞는 도입 방식 결정 및 구현
- 응답 스키마(
OcrResultResponse.parsedDrugs) 유지 → 프론트 영향 0 - 키 미설정 시 LLM 자동 비활성 → 기존(파서 전용)과 동일 동작
🧪 측정 결과 (gpt-4o + 좌표 + 프롬프트 개선)
프롬프트 1차 버그: "제형어 빼라"→아모잘탄정을 아모잘탄으로 잘림.
→ "제형 접미사는 이름의 일부, 절대 떼지 마라"로 수정.
| 서식 | 파서 | gpt-4o |
|---|---|---|
| 병원 처방전 (표 서식) | 4/4 | 0/4 (좌표 텍스트로 표 숫자 매핑 실패) |
| 약봉투 (별표/압축) | 4/4 · 9/9 | 3~4/4 · 9/9 |
| grid 영수증 | 4/5 | 5/5 (파서가 놓친 약을 잡음) |
| scattered 영수증 | 3/8 | 이름 8/8 |
→ 처방전은 파서(좌표 알고리즘), 약봉투·영수증은 LLM이 유리. 하이브리드로 결정.
🛠️ 작업 범위
하이브리드 라우팅
OCR → 병원 처방전인가? (isPrescription)
├─ YES → 파서 (좌표 알고리즘)
└─ NO → 약봉투/영수증/그 외 → gpt-4o
↓ LLM 실패·약 0개
파서 폴백
isPrescription — 서식별 고유 문구로 구분 (발행처가 약국/병원/약국으로 달라 겹치지 않음):
| 서식 | 고유 문구 |
|---|---|
| 처방전 | "처방전" 제목 / "처방 의약품" / "교부번호"·"교부일" / 보험코드 줄(\d{8,10}\s+약이름) |
| 약봉투 | "복약안내" / *약이름 별표 / "N정씩N회N일분" |
| 영수증 | "약제비" / "계산서" / "본인부담금" |
isPrescription = 처방전 신호 있음 AND 약봉투 신호 없음 AND 영수증 신호 없음
보험코드 줄만 보면 코드 미인쇄 EMR에서 처방전을 놓쳐서, 여러 신호를 OR로 묶고 약봉투·영수증 신호로 배제.
1. DrugExtractor + LlmDrugExtractor
List<ParsedOcrData> extract(fields)— 실패 시 예외 대신 빈 리스트- 키 미설정·API 오류 → 빈 리스트 (폴백 유도)
- sanity check:
timesPerDay16,90 벗어나면 nulltotalDays1
2. OpenAiClient
- fields →
"텍스트 @(x,y)"목록(y→x) →gpt-4otemperature 0json_object25s - 프롬프트: 행 해석, 제형 접미사 유지, 용량 표기·성분명·제형어 제외, "교부일로부터 N일" 제외
- 새 의존성 없음 (
WebClient)
3. OcrParser.isPrescription(fields) public
4. OcrCommandService 라우팅 + method 로깅
5. application.yml — openai.api-key: ${OPENAI_API_KEY:} 등
6. 테스트
OcrCommandServiceTest(키 불필요) — 처방전→파서(LLM 미호출), 약봉투→LLM, LLM 실패→파서 폴백LlmDrugExtractorComparisonTest—@Tag("integration")+OPENAI_API_KEY있을 때만. 파서 vs LLM vs 하이브리드 리포트
✅ To-do
- fixture 9종 측정 (gpt-4o-mini rawText/좌표 → gpt-4o 좌표+프롬프트 개선)
-
DrugExtractor+LlmDrugExtractor(sanity check) -
OpenAiClient(좌표 텍스트, 프롬프트, gpt-4o) -
OcrParser.isPrescription(서식별 고유 문구) -
OcrCommandService하이브리드 라우팅 -
application.ymlopenai 설정 -
OcrCommandServiceTest,LlmDrugExtractorComparisonTest - 설계 문서
docs/kangcheolung/issue-100-ocr-llm-hybrid.md
📏 완료 조건 (DoD)
- 키 있는 로컬: 약봉투 → LLM 경로, 처방전 → 파서 경로로
parsedDrugs채워짐 - 키 없음/LLM 실패 → 파서로 기존과 동일 동작
OcrResultResponse필드 변화 없음./gradlew test통과
📒 기타 · 알려진 한계
- LLM 비결정성:
temperature 0이어도 gpt-4o가 run마다 이름 형식 미세하게 다름. 사용자 확인 UI가 최종 방어선. - 비용: gpt-4o 호출당
1520원. 볼륨 커지면 gpt-4o-mini + 프롬프트 강화 또는 캐싱. - 이름 정규화(
지스로먹스장→지스로맥스정)는 gpt-4o도 못 함 → ES 대조 = 다음 이슈 (druginfo4,745건). - 이미지→비전 LLM은 별도 검토.
- 로컬 테스트용 컨트롤러
userId하드코딩은 커밋 금지.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
✨ Feature기능 개발기능 개발
Activity
[-]feat: OCR 파싱을 LLM 추출로 전환 (파서는 폴백)[/-][+][Feat] OCR 약 추출 LLM 하이브리드 — 처방전=파서, 약봉투/영수증=gpt-4o[/+]