AI‐5단계‐데이터·컨텍스트 보강 설계 - 100-hours-a-week/KTB4-5th-wiki GitHub Wiki

단계 5: 데이터·컨텍스트 보강 설계

1. 모델의 기억보다 우리 서비스의 기준 자료가 필요합니다

영수증의 줄임말을 표준 재료명으로 바꾸거나 보유 재료로 레시피를 찾는 일은 모델의 일반 지식만으로 해결하기 어렵습니다. 백엔드 MySQL은 사용자·냉장고·재료 원본을 관리하고, AI는 API로 전달받은 재고만 검색 입력으로 사용합니다. 검색·검증한 레시피 원본은 AI PostgreSQL에서 관리하고 백엔드 MySQL에는 주기적으로 사본을 동기화합니다.

PostgreSQL 상품 카탈로그·별칭·보관기간 조회와 키워드 규칙을 연결합니다. 레시피 검색·저장·캐시·MySQL 동기화도 같은 데이터 경계 안에서 처리합니다. 바코드 조회는 보류합니다. 상품·보관기간 자료의 품질은 계속 검증해야 하며 모델 추론 항목은 사용자 확인 대상으로 유지합니다.

2. 두 가지 검색 흐름

flowchart TD
    A[OCR 상품명·포장 문자] --> B[상품·별칭·비식품 검색]
    B --> C{미확정 행이 있는가}
    C -- 있음 --> D[미확정 행만 Gemma 선택 보완]
    C -- 없음 --> E[이름·카테고리 정규화]
    D --> E
    E --> F[표시 날짜 검증]
    F --> G[날짜가 없고 조건이 맞으면 보관기간 조회]
    G --> H[출처를 붙인 임시 결과·사용자 확인]
    I[백엔드가 전달한 확정 재고·제외 재료] --> J[원본 구성 보존·재료명 정규화]
    J --> C2{추천 캐시 hit?}
    C2 -- 예 --> L[추천 결과 반환]
    C2 -- 아니오 --> D2{AI PostgreSQL에<br/>사용 가능한 레시피가 있는가}
    D2 -- 예 --> R[레시피 추출]
    D2 -- 아니오 --> FS[식품안전나라]
    FS --> FQ{후보 있음?}
    FQ -- 없음 --> TM[TheMealDB]
    TM --> TQ{후보 있음?}
    TQ -- 없음 --> TV[Tavily 최종 검색]
    FQ -- 있음 --> K[검증·정규화]
    TQ -- 있음 --> K
    TV --> VQ{저장·재사용 가능한 후보?}
    VQ -- 있음 --> K
    VQ -- 없음 --> L
    K --> P[(AI PostgreSQL 레시피 원본)]
    P --> R
    R --> L2[규칙 순위·추천 캐시 저장]
    L2 --> L
    P --> S[커서 기반 변경 조회]
    S --> M[(백엔드 MySQL 레시피 사본)]
Loading

3. 사용할 자료와 관리 책임

자료 필수 내용 확보·갱신 기준
상품·별칭 보조 자료 원문 별칭, 표준 이름, ERD 카테고리, 출처, 버전 AI PostgreSQL receipt_ai.product_knowledge·product_aliases. 식품안전나라 가공식품 DB 약 23.8만 건(카테고리 충돌 763건 비활성화)과 흔한 생과일·채소 큐레이션, 반복 관찰된 OCR 오인식 별칭
비식품 사전 비식품명, 식품과 혼동되는 표현, 판정 근거 담배·전자제품 등 관측 오류를 반영하되 상호만으로 단정하지 않음
보관기간 보조 자료 식재료, 냉장·냉동 등 조건, 개봉 여부, 기간 범위, 출처·확인일 AI PostgreSQL receipt_ai.shelf_life_rules. 가공식품은 식약처 식품유형별 소비기한 참고값, 신선식품은 USDA FoodKeeper. 신선식품 규칙은 채소·과일·육류·수산 항목에만 적용
레시피 원본 출처 안정 ID·revision, 제목, 원천 재료명·측정량, 조리 단계, 이용 조건 AI PostgreSQL이 소유. 식품안전나라 우선, TheMealDB 보조, Tavily 최종 검색. 물리 스키마는 미정
추천 결과 캐시 냉장고 ID, 원본·정규화 재료 구성의 fingerprint, 추천 모드, 결과 FastAPI 메모리에 저장하며 재시작 시 유실을 허용
레시피 사본 백엔드 ERD의 recipes, recipe_ingredients, recipe_steps 백엔드가 동기화 API를 읽어 MySQL에 반영
사용자 피드백 원본 예측, 수정값, 동의 여부, 검수 상태 현재 ERD에 영속 테이블이 없으며 별도 스키마 승인 전까지 저장을 보장하지 않음

검색 결과에는 단계 1과 같은 evidenceIds와 함께 sourceVersion, retrievedAt을 붙이는 내부 계약을 제안합니다. 원본 URL은 출처 관리에 보관하되, 접근 권한이 없는 원문이나 개인정보를 모델 입력에 포함하지 않습니다. 인터넷에서 찾았다는 이유만으로 학습·재배포 권한이 있다고 보지 않습니다.

4. 보조 자료 조회와 모델 입력

상품·별칭·보관기간 테이블은 AI 레포 fastapi/database/migrations로 확정했습니다. 조회 순서는 키워드 규칙과 별칭·표준명 정확 일치를 먼저 보고, 없으면 pg_trgm 유사도(임계값 0.4, 질의 4자 이상·카탈로그 이름 3자 이상)로 근접 후보를 찾습니다. 유사도 매칭은 CATALOG_FUZZY로 표시하고 확인 필요로 둡니다. 레시피 테이블의 최소 논리 계약은 레시피의 source + sourceRecipeId, 불투명 sourceRevision, 재료·단계 순서, 활성 상태와 동기화 변경 순서를 제공해야 합니다. 임베딩 모델이나 벡터 인덱스는 초기 구성에 포함하지 않습니다.

  1. 현재는 키워드 규칙과 별칭·표준명 정확 일치를 먼저 확인합니다.
  2. 정확 일치가 없으면 pg_trgm 유사도 후보를 만들고 NEEDS_REVIEW로 유지합니다.
  3. 사전이 해결하지 못한 행은 민감정보를 제거한 OCR 문맥과 함께 Gemma에 전달합니다.
  4. 모델이 선택한 행과 원문이 실제 입력에 존재하는지 검증합니다.
  5. 바코드 해독·상품 DB 조회는 보류하며 포장 문자와 시각 후보로 판별합니다.

모델 입력 예시는 다음과 같습니다. 실제 API 응답 스키마를 변경하는 내용이 아니라 내부 보강 계약 초안입니다.

{
  "allowedRowIds": [12, 18],
  "ocrDocumentRef": "restricted/document-001",
  "catalogEvidence": [
    {"rowId": 12, "candidateName": "우유", "category": "DAIRY", "sourceVersion": "catalog-v1", "confirmed": false}
  ],
  "instruction": "허용된 원문 행에서 식품만 선택하고 새 상품명이나 날짜를 생성하지 않는다."
}

사전 후보가 있다고 무조건 정답으로 유도하지 않습니다. 원문과 맞지 않으면 후보를 거절할 수 있어야 합니다.

5. 소비기한과 레시피에 적용하는 규칙

표시된 날짜가 있으면 날짜 종류와 원문을 확인합니다. 제조일·구매일을 소비기한으로 바꾸지 않습니다. 날짜 일부가 잘렸다면 확인 가능한 원문을 보존하고 확인 필요로 처리하며, 검색으로 얻은 날짜를 표시 날짜처럼 채우지 않습니다.

날짜가 아예 없는 경우에만 보관기간 자료를 검토합니다. 보관 방식·개봉 상태·기준일이 불명확하면 분석 후보의 날짜를 비워 두고 사용자에게 확인합니다. 다만 ERD의 ingredients.expiration_date는 NOT NULL이므로 날짜가 빈 분석 후보를 재고에 저장하지 않습니다. 기준일은 영수증에서 확인한 구매일을 우선합니다. 구매일이 없거나 미래이거나 365일보다 오래됐으면, 또는 실물 사진처럼 구매일을 알 수 없으면 요청 시간대 기준 분석일을 임시 기준일로 사용합니다. 이때 신뢰도를 0.35로 낮추고 "분석일 기준의 참고 범위이며 확정 소비기한이 아님"을 가정과 확인 사유에 명시하며, date는 비우고 estimatedRange만 제공합니다. 보관기간 규칙이 없는 상품은 추정하지 않습니다. 조건이 확인된 경우에만 기준일에 기간을 더한 관리용 예상 기간을 제안하며, 실제 소비기한이나 섭취 안전을 보장하지 않습니다. AI 예상 표시와 계산 기준·가정·출처를 함께 제공합니다.

AI는 전달받은 ingredients.name과 AI PostgreSQL 레시피 재료명을 정규화해 후보를 찾습니다. 백엔드 ERD에는 표준 식재료 FK가 없으므로 이름 매칭의 한계와 정규화 버전을 기록합니다. excludedIngredientNames는 순위를 매기기 전에 제외하지만 빈 목록을 알레르기 없음으로 해석하지 않습니다. 소비기한 임박 재료 활용을 우선하고 호환되는 측정 방식일 때만 정량 충족 여부를 비교합니다.

추천은 원본 재료 구성을 보존한 채 정규화 결과로 inventory_fingerprint를 만들고, 정규화·중복 제거·정렬한 제외 재료로 exclusion_fingerprint를 만듭니다. 두 fingerprint와 추천 모드로 캐시를 먼저 확인합니다. 캐시 miss이면 AI PostgreSQL을 조회하고, 여기에도 후보가 없을 때만 식품안전나라, TheMealDB, Tavily 순서로 검색합니다. Tavily는 앞의 두 API에 사용 가능한 후보가 없을 때만 호출합니다.

검색된 레시피는 출처·저장·재배포가 허용된 경우에만 AI PostgreSQL에 저장합니다. 공급자별 최종 이용 조건은 아직 확인이 필요합니다. 추천 응답은 동기화 전에도 표시할 수 있도록 안정 원천 식별자와 허용된 레시피 스냅샷을 반환합니다. 백엔드 MySQL의 recipe_id는 동기화 과정에서 백엔드가 부여합니다.

6. 지금 이미지 기반 검색을 도입하지 않는 이유

현재 영수증의 핵심 문제는 비슷한 사진 검색보다 상품행 누락과 식품·비식품 판정입니다. 별도 검수된 상품 이미지와 정답이 부족한 상태에서 시각 검색을 추가하면 비슷한 포장을 다른 상품으로 연결할 위험이 있습니다. 실물 모델 평가에서 바코드·OCR가 없는 식품의 오류가 주요 원인으로 확인될 때 이미지·텍스트 검색을 별도 비교합니다.

검색 보강은 추가 학습을 대체하지 않습니다. 동의받고 검수한 데이터로 입력 유형 분류와 식품명·카테고리 모델을 발전시키되, 학습에 쓴 영수증과 같은 상품의 유사 이미지는 최종 평가와 분리합니다.

7. 검증 계획과 승인 기준

비교 측정할 값 기대 효과와 실패 시 조치
OCR+Gemma / 사전 보강 추가 식품행 정밀도·재현율, 표준명 일치율, 보완 호출률 이름 일관성과 호출 감소 기대. 잘못된 별칭 연결이 늘면 승인 범위를 축소
조건 없는 날짜 추정 / 조건·출처 필수 정책 출처 누락, 조건 불일치, 잘못 확정한 날짜 수 근거 없는 확정을 차단. 날짜 미입력 비율 증가는 함께 보고
단순 재료 겹침 / 임박도·제외 재료 필터 순위 임박 재료 활용률, 재료 충족률, 제외 재료 위반, 사용자 평가 만들 수 있는 메뉴를 우선. 위반 사례는 출시 전 해결
캐시·DB·외부 검색 순서 캐시 적중률, AI DB 적중률, 공급자별 호출률, Tavily fallback률 기존 결과를 재사용하고 Tavily가 최후에만 호출되는지 확인
⚠️ **GitHub.com Fallback** ⚠️