🧭 통합 정본 · 2026-06-21

음식·식재료 추천 데이터 엔진

식단 데이터를 모아 식재료 지식 그래프로 만들고, 아이가 잘 먹는 것에서 닮은 식재료·새 조리법으로 다리를 놓아 코치 편지가 괴식 없이 믿고 추천하게 하는 엔진. (옛 6개 문서를 이 정본 1개로 통합)

← 문서 허브 · 통합: 음식추천네트워크 · 코퍼스/도감 · 식단파이프라인 · 푸드체이닝 · 추천SQL · menu-ingest
목적

이 엔진은 무엇을 위한 것인가

한 줄: 2~6세 아이가 거부감 없이 먹는 음식의 '가짓수'를 한 발씩 넓히도록 — 양이 아니라 다양성을 키운다. 그러려면 ① 추천이 "지어낸 조합"이 아니라 실측 데이터로 검증된 것이어야 하고, ② 아이의 실제 선호·결핍에 맞아야 하며, ③ 영유아 안전(질식·알레르겐·저염·괴식)을 절대 어기지 않아야 한다.
  • ① 데이터 인입 — 전국 어린이집·유치원 식단표(3경로)를 모아 "급식에 뭐가 자주 나오나"의 모집단 기준을 만든다.
  • ② 식재료 지식 그래프 — 식재료를 곁들임(pair)·사촌(bridge)으로 잇고, 도감 등급(2축)·영양을 붙인다.
  • ③ 메뉴→식재료 분해 — 식단표의 '음식 이름'을 표준 '식재료'로 결정론 분해(환각·괴식 0).
  • ④ 추천·푸드체이닝 — 잘 먹는 것 → 곁들임 → 사촌 → 새 조리법으로 다리를 놓아 편지에 안전한 후보만 넘긴다.
  • ⑤ 저장·자가학습 — SQL을 단일 진실로, 데이터가 쌓일수록 추천이 현실에 맞게 야간 자가보정(복리).
핵심 설계 원칙: 판단(괴식이냐·등재냐)은 빌드타임 1곳에서 사람·증거가 정하고, 런타임/편지는 검증된 것을 믿고 결정론 필터(연령·알레르기)만 건다. "수치가 LLM을 이기는 게 아니라, 수치가 LLM을 증거로 무장시킨다."
1

전체 흐름 — 식단 1장이 추천이 되기까지

3경로로 들어온 식단표가 식재료 지식이 되고, 그게 추천·편지로 나가는 e2e. 외부 OCR/LLM·우리 모듈·DB를 색으로 구분.

범례 외부 OCR외부 LLMDB·SQL 우리 모듈 code
입력식단표 3경로 → ocr_logs 큐(멱등)
zip 관리자(센터 식단 묶음) · ② daycare-eval 업로드 · ③ 부모 기록탭. 모두 (기관,월) 멱등키로 중복 제거 — 같은 기관-월 재입력해도 한 번만.
S1OCR 전사 (사진/PDF → 글자)Naver CLOVA OCR V2Storage
app/api/ocr — 표 인식(실패 시 일반 OCR 폴백). PDF는 pdftoppm 래스터화.
📦 ocr_logs(감사): ocr_text·model·tokens
S2메뉴 추출·이름 교정Claude Sonnet 4.6 · Vision
OCR 텍스트+이미지 → items[{date,slot,menu}]. 오인식 교정(돈까스→돈가스)·알레르기기호 제거.
S3메뉴 → 식재료 분해 (결정론 6단계)DeepSeek/Haiku 미해결만
0 learned 캐시1 dict 20621b 부분2 스캔+boostmeatFix → 미스만 3 LLM. 표준명(lexicon)·CANON_VOCAB 검증(양념 제외·환각 차단).
📦 learned_menus 캐시(멱등·다음부터 무료)
S4(기관,월) dedup 집계 → 지식SQL
기관-월 1벌 단위로: 등장률(youa-freq baseline) + 공출현 쌍(같은 끼니) + 도감 2축 등급.
📦 institution_menus·ingredient_edges(pair/bridge)·ingredients(도감)
S5추천·푸드체이닝 → 편지DeepSeek 작문만
graphSource(SQL) → strongPairsOf·verifiedCousinsOf·safeGarnishOf → 5전략(liked·pair·chain·dish·plain) → 안전 필터 → 편지(재료=결정론, 문장=LLM).
▼  새벽 크론(자가학습 복리)  ▼
🌙야간 통합 배치
ocr_logs 큐 정제→learned_menus · 식재료 backfill · 신규 식재료→enrich_queue(영양) · 공출현→ingredient_edges 갱신 · warm(SQL→메모리). 데이터 쌓일수록 추천 자가보정.
2

5개 축 (옛 문서들의 통합)

옛 6개 문서가 다루던 내용을 5축으로 합쳤다. 각 축 제목 옆 = 현재 상태(라이브/부분/계획).

A. 데이터 인입 부분 ← menu-ingest
전국 식단표 3경로 → (기관,월) 멱등 → 모집단 빈도(youa-freq) baseline
  • 3경로: zip(센터)·daycare-eval(institution_menus UNIQUE upsert)·부모 기록탭(children.institution_id+place=daycare로 환원)
  • 빈도 2층 분리: baseline(기관-월 등장률 youa-freq.json = 도감 등급·코칭 우선순위 — 2026-06-21 실측 OCR 식단 MAX 병합: 단호박 1.4→65.7%·애호박·배추김치 게이트 통과) / observed(ingredient-freq.json = 랭킹·풀 순서 — 7종 degraded → 162종 실측 식단 재구축 build-ingredient-freq.py --src sikdan)
  • 경로1 zip = 사진/PDF면 OCR 경로(parse_and_aggregate는 xlsx 전용). 폐지된 식약처 영유아 API를 이 OCR 적재가 대체.
B. 식재료 지식 그래프 라이브 ← 음식추천네트워크
식재료 노드 + pair(곁들임/궁합)·bridge(사촌/대체)·도감 2축 등급
  • pair 940 · bridge 175(무방향). lift=c·N/(na·nb)로 우연 보정 → grade strong/medium/weak. tray(NEIS 식판 공출현) 축 병합.
  • strongPairsOf(약신호 차단)·verifiedCousinsOf(검증 사촌)·garnishPairsOf(tray 제외 곁들임).
  • 도감 2축: 영유아 등장률 ⊕ 초등 elem_count → 결합등급 MAX → 코칭 노출 우선순위. 계획 TIER2 자동 bridge(영양 코사인·감각 7축).
C. 메뉴→식재료 분해·유지 라이브 ← 코퍼스/도감 스킬
음식 이름을 표준 식재료로 결정론 분해 + 사전/도감 유지보수
  • 결정론 6단계(0 dict→1 룰→1b 부분→2 스캔+boost→meatFix) → 미스만 LLM. 대부분 메뉴 무료·즉시.
  • 크론=적용 / 스킬=개선: 새벽 크론은 사전대로 분해·백필, /mealfred-food-mapping 스킬은 사전·도감 자체를 개선(DB 쓰기 0·정적 JSON+코드 커밋).
  • 안전 스크린 우회 금지: 도감 자동 등재해도 영유아 안전(질식·보툴리누스·고나트륨 한방·비타민A 과잉)은 절대 통과 못 함. 한방 재료는 매핑에서 무시(인삼닭곰탕→[닭고기,무,대파]).
D. 추천 — 이 데이터를 *쓰는* 쪽 → 별도 모듈(B)
위 지식그래프·도감·등장률을 런타임에 소비해 "오늘 무엇을 권할지" 고르는 건 추천 모듈 소관. 이 데이터 엔진은 그 재료만 공급.
  • 요약: 잘 먹는 것 → 곁들임(pair) → 사촌(bridge) → 새 조리법으로 한 발씩(다양성↑). 5전략·괴식게이트·콜드스타트·안심톤.
  • 상세 구현·로드맵(2축 확장·온도축·neophobia 곡선)은 → 추천 모듈 ② (정본)로 분리. (책임 분리: 데이터=여기 / 런타임 추천=거기)
E. 저장·전환·자가학습 부분 ← 추천 SQL 핸드오프
SQL 단일 진실 + graphSource 격리 + 야간 복리 학습
  • 7테이블(ingredients 허브 ⊃ ingredient_edges·dish_ingredient_stats·dogam·meal_logs·learned_menus·nong_foods). 3층 캐시(L1 SQL→L2 스냅샷 JSON→L3 인메모리+폴백).
  • graphSource.ts 한 겹으로 JSON↔SQL 격리 → 소비 함수 byte 무변경. 라이브 크론이 항상 SQL warm(플래그 졸업).
  • 미구현 야간 SQL→JSON export job(스냅샷 자동 갱신) · 야간 공출현 학습 write(증분 강화) — 로직 검증 완료(아래 예시).
3

현재 개발현황 (2026-06-21)

1115
ingredient_edges(SQL=JSON)
175
검증 사촌(bridge)
2062
menu-dict 메뉴
679
도감 식재료
13.4k
learned_menus 캐시
항목상태메모
식재료 그래프 pair/bridge·lift·tray·garnish 분리라이브strongPairsOf/garnishPairsOf 등 적용
SQL ingredient_edges ↔ JSON 동기화완료782→1115(strong 276→517, tray 복구)·값 100% 일치
COACH_GRAPH_SQL 플래그 졸업(항상 warm)코드 완료·배포 대기route.ts 타 작업과 얽혀 합류 대기(prod는 JSON 폴백으로 안전)
메뉴 분해(결정론 6단+LLM)·도감 스킬라이브커버리지 ~83%(실측), 미스만 DeepSeek
추천 5전략·괴식 게이트·안심 톤라이브tray/매운/단↔짠 교차괴식 차단
데이터 인입 3경로 통합·(기관,월) 멱등부분OCR 배치 가동 중·institution_menu_items 아직 0행
야간 공출현 학습 write·SQL→JSON export job미구현집계 로직은 검증(아래 예시)·크론 배선 남음
푸드체이닝 2축 확장·neophobia·학습루프계획5전략·진척5단계는 라이브, 조리법/온도축은 설계
TIER2 자동 bridge(영양 코사인·감각 7축)계획현재 bridge 175 수기 시드
4

예시 데이터 사례

① 실데이터 — 광진구 어린이집 10월 식단표 (OCR→분해→집계)

S1·S2 (CLOVA+Sonnet): 1(수) 오전간식: 떠먹는요구르트, 파프리카스틱 / 점심: 닭살채소볶음밥, 연근조림, 자른토마토, 배추김치 / 오후간식: 단호박샐러드
S3 분해: 닭살채소볶음밥→[닭고기·당근·양파·쌀] · 연근조림→[연근] · 배추김치→[배추·김치]
S4 집계(143개 기관-월·멱등): 등장률 TOP = 양파·소고기·두부·미역 100% · 단호박 65.7%(표준식단 1.4%였던 것 복권) · 공출현(같은 끼니) = 멥쌀+파 ×140 · 김치+배추 ×133 · 멸치+파 ×129ingredient-freq.json 162종·youa-freq.json 병합으로 라이브
멱등 검증: 같은 데이터 재투입 → 신규 0·전량 중복 스킵·누적 불변(중복 카운팅 0).

※ 데이터 쌓일수록(현재 OCR 배치 가동 중·155개 식단표) 등장률이 차등화 → 폐지된 식약처 영유아 API 대체. 분해 미스(돈가스·오므라이스 등)는 야간 DeepSeek.

② 판정 엑셀 — 사람이 증거 보고 결정(빌드타임 1곳)

a ↔ b종류lift증거안전판정
소고기 ↔ 시금치pair1.8시금치무침+불고기 식판 3건O승인
떡 ↔ 달걀pair0.72같은 끼니 거의 없음(우연 이하)음성lift거부
견과 ↔ 요거트bridgeflavor 유사질식·알레르겐조건부(만3+·잘게)
두부 ↔ 근대bridge부드러운 식감·철분 결핍 동급O승인

안전플래그(질식·8대알레르겐·음성lift·꿀만1세)는 코드 자동 계산 = 판단 대상 아님. 나머지만 사람 판정.

③ 분해 4단 폴백
  • '계란찜' → learned 캐시 → menu-dict 일치 → substring 최장일치 → LLM(Haiku)
  • '인삼닭곰탕' → [닭고기·무·대파] (인삼=한방 안전스크린 차단)
  • '함초삼겹' → [돼지고기] (함초=고나트륨 차단)
④ 추천·괴식 차단
  • 감자 잘 먹음 → 단호박(사촌·비타민A 갭) + 찐 감자(조리법 전이)
  • 미역국+당근 = 괴식 자동 제외(scoreCombo<임계)
  • 두부 타깃 + 김치 → "김치 곁들임" 차단(매운/tray 제외)
5

안전·불변선 (절대 어기지 않는 것)

하드 차단(코드 자동) — 질식위험·8대 알레르겐·음성 lift·꿀(만1세). 판단 대상 아님, 무조건 제외.
괴식 0 — 검증 테이블 안의 조합만 추천. 공출현 0 조합은 메뉴로 존재 안 함. 단↔짠(과일·우유 ↔ 생선·콩)·매운/김치류 곁들임 차단.
도감 안전스크린 우회 금지 — 자동 등재해도 보툴리누스·고나트륨 한방·비타민A 과잉은 통과 못 함. 위험군 보류·보고.
날조·과장 금지 — "5→20.5" 영구 금지. 서열·등수·% 부모 노출 금지(안심 톤만). 근거 없으면 안 넣음.
불변 계약 — 소비 함수 시그니처·RawEdge 필드명·PAIR_MIN_STRENGTH=2·괴식 가드·골든 테스트는 불변. graphSource 한 겹으로만 JSON↔SQL 전환.
저작권·경계 — Satter sDOR 무수정만, SOS표 복제 금지. 점검 신호 ≠ 진단(위험 신호 시 소아과 게이트). 푸드브릿지 ≠ 푸드체이닝.
← 문서 허브로 · 음식·식재료 추천 데이터 엔진 (통합 정본) · 옛 6문서 통합본