누비랩
인턴·신입근거: Product Engineer는 특정 기술 영역이 아니라 제품 문제를 End-to-End로 책임지는 역할입니다
※ 실제 산출물이 아닌 AI 예측 시안입니다.
누비랩은 식수·식사·잔반 데이터로 ‘건강한 행동 변화’ 를 만드는 회사입니다. ‘무엇을 바꿔야 하는가’ 를 데이터로 가려내고 그 제안이 실제로 효과가 있었는지 확인하는 이 도구는, 급식 잔반이라는 누비랩 고유의 데이터가 있어야 의미가 있습니다. 잔반을 줄이는 메뉴 제안에서 식자재 비용 절감 발주까지 이어지는 사슬을 다른 회사 데이터로는 그대로 만들 수 없습니다.
‘인사이트로 건강한 행동 변화’ 가 미션. 데이터 → 변화 가능한 KPI → 차주 결과 검증까지 닫혀야 ‘행동 변화’ 가 측정됨.
기술 영역이 아니라 ‘제품 문제’ E2E. 2주 = 데이터 → 인사이트 → 제안 → 검증 한 루프가 끝나는 최소 단위.
LLM 이 ‘무엇을 바꿔야 하는가’ 까지 답하는 단계. 영양사 검토자와 답이 얼마나 일치하는지, 그리고 제안이 실제로 맞았는지로 신뢰도를 높인다.
급식 메뉴·잔반량·만족도 기록에 단가·날씨·학사일정을 붙여 한 화면에서 봅니다. LLM 이 잔반이 늘어난 이유 Top3 와 다음 주 메뉴 3건을 제안하고, 매일 아침 슬랙으로 리포트를 보냅니다. 1주차만으로 완성품이고, 제안이 실제로 맞았는지 확인(2주차)·식자재 비용 절감 제안(3주차)으로 단계적으로 키웁니다. (근거는 채용공고 정렬이다.)
누비랩 PE. ‘무엇을 바꿔야 하는가’ 한 줄을 매번 손으로 만들어주는 상황을 끝내고, 제안이 실제로 효과가 있었는지 확인하고 싶은 사람입니다.
메뉴·잔반량·만족도를 한 스키마로 묶고 단가·날씨·학사일정을 붙이면, LLM 이 (a) 잔반이 늘어난 이유 Top3 와 (b) 다음 주 메뉴 제안 3건을 몇 초 안에 답하고, (c) 다음 주 실제 잔반량으로 그 제안이 맞았는지 확인합니다.
메뉴·잔반·만족도를 한곳에 모아, 잔반이 늘은 이유 Top3 와 다음 주 메뉴 제안을 LLM 이 만들고 매일 아침 슬랙으로 보내는 동작하는 도구입니다.
지난주 제안한 메뉴의 예상 잔반율을 다음 주 실제값과 맞춰보고, 영양사 검토자 2명이 인사이트가 타당한지 라벨링해 신뢰도를 보여주는 대시보드입니다.
확인된 제안을 바탕으로 단가를 고려한 식자재 발주량을 제안해, 잔반과 비용을 함께 줄이는 단계입니다.
잔반 급증 시나리오 10개를 넣으면, 원인 Top3 와 다음 주 메뉴 제안 3건이 몇 초 안에 나오는 걸 시연합니다.
일부러 만든 악성·결측 입력 30종을 넣어도 출력 형식(스키마)이 한 번도 안 깨집니다. 형식 검증(Zod) 자동 통과합니다.
매일 아침 슬랙으로 리포트가 자동 발송되는 걸 14일치 시연으로 보여줍니다.
내가 만든 정답 30개 중 원인 Top3 가 일치한 게 24개(80%). 단, 정답과 데이터를 모두 내가 만든 합성 기준이므로 ‘성능 검증’ 이 아니라 ‘데모 조건에서의 동작’ 임을 명시합니다.
위 수치는 전부 작성자가 만든 더미데이터 자가채점이며, 실제 적중률·일치도는 누비랩 실 급식 데이터 + 실 영양사 검수가 있어야 측정 가능합니다. 이 도구는 ‘그 검증을 바로 돌릴 수 있는 상태’ 까지 만듭니다.
이건 고객·실데이터 없이 혼자 만든 포트폴리오입니다. 모든 수치는 합성 데이터 자가채점이라 ‘성능 증명’ 이 아닙니다. 면접에서 솔직히 ‘데모 조건 동작 + 실검증 준비 상태’ 로 설명합니다.
정답을 작성자가 만들었으니 잘 맞는 것이 당연합니다. 일부러 헷갈리는 결측·악성·경계 케이스를 더미에 섞어, 쉬운 문제만 푸는 게 아님을 보여줘야 합니다.
‘무엇 때문에 잔반이 늘었다’ 는 상관일 뿐 인과가 아닐 수 있습니다. 단정 표현을 강제로 누그러뜨리고 별도 LLM 검사로 우회 표현을 거릅니다.
급식 메뉴 이름 표기가 제각각입니다. 이름 유사도 + 분류·온도 필터로 같은 메뉴로 묶고, 못 묶은 건 그냥 추측하지 말고 ‘미매핑’ 으로 알립니다.
실제 도입 시 달라지는 것 명시합니다. 실 급식 분포는 더미와 다르므로 기준폭·보정값을 다시 맞춰야 하고, 영양사 검수도 실제 인력으로 교체해야 한다(도입 단계 과제로 분리)
① 인라인 샘플 · ② seed.json 다운로드 · ③ 증강 프롬프트. 세 층 모두 동일한 schema 에서 파생.
메뉴·잔반량·만족도 일일 로그. 인사이트 LLM 이 가장 먼저 질의하는 테이블. 운영 현실대로 결측(설문 미실시·계측 누락)·미등록 신메뉴·별칭 표기·경계값을 일부러 섞어, 쉬운 데이터만 푸는 게 아님을 보여준다.
| date | site | menu_raw | menu_id | served_count | tray_loss_pct | satisfaction_1to5 | staple_type |
|---|---|---|---|---|---|---|---|
| 2026-03-04 | 강남센터 | 제육볶음 | m_jeyuk | 420 | 12.3 | 4.2 | rice |
| 2026-03-04 | 강남센터 | 콩나물 맑은국 | m_kongnamul_soup | 420 | 18.5 | 3.4 | rice |
| 2026-03-04 | 판교센터 | 치킨가스 | m_chicken_katsu | 310 | 8.2 | 4.6 | rice |
| 2026-03-05 | 강남센터 | 매콤제육 | m_jeyuk | 418 | 13 | 4.1 | rice |
| 2026-03-05 | 판교센터 | 잔치국수 | m_janchi_noodle | 305 | 14.1 | — | noodle |
아래 Zod 스키마에 맞는 한국 급식 운영 로그 14일치(2주) JSON 배열로 생성. 사이트 2곳(강남/판교), 요일·시즌·이벤트(중간고사·체육행사) 분포 포함.
{"type":"object","properties":{"date":{"type":"string"},"site":{"type":"string"},"menu_raw":{"type":"string"},"menu_id":{"type":["string","null"]},"served_count":{"type":"integer"},"tray_loss_pct":{"type":["number","null"]},"satisfaction_1to5":{"type":["number","null"]},"staple_type":{"type":"string","enum":["rice","noodle","bread"]}},"required":["date","site","menu_raw","menu_id","served_count","tray_loss_pct","satisfaction_1to5","staple_type"]}
쉬운 데이터만 만들지 말 것. 다음 하드 케이스를 의도적으로 섞어라(쉬운 문제만 푸는 함정 회피): (1) 같은 메뉴의 별칭 표기 혼입(menu_raw '매콤제육'→menu_id m_jeyuk 처럼 menu_master 로 정규화돼야 함), (2) 미등록 신메뉴(menu_id=null) 2~3건, (3) 결측. satisfaction_1to5/tray_loss_pct 가 null 인 행 각 1~2건(설문 미실시·계측 누락), (4) 휴무일(served_count=0, 잔반·만족도 null), (5) 경계값 tray_loss_pct 0.0 과 95~100 각 1건 + 폭증 이상치(>55%) 2건. JSON 배열만.{"type":"object","properties":{"date":{"type":"string"},"site":{"type":"string"},"menu_raw":{"type":"string"},"menu_id":{"type":["string","null"]},"served_count":{"type":"integer"},"tray_loss_pct":{"type":["number","null"]},"satisfaction_1to5":{"type":["number","null"]},"staple_type":{"type":"string","enum":["rice","noodle","bread"]}},"required":["date","site","menu_raw","menu_id","served_count","tray_loss_pct","satisfaction_1to5","staple_type"]}메뉴 표준 ID + 별칭(같은 메뉴가 ‘제육볶음/돼지불고기/매콤제육’ 으로 들어옴). 인사이트가 별칭 차이로 분산되는 문제 해소.
| menu_id | canonical_name | aliases | category | root_vegetable | temp |
|---|---|---|---|---|---|
| m_jeyuk | 제육볶음 | ["돼지불고기","매콤제육"] | protein | false | warm |
| m_kongnamul_soup | 콩나물국 | ["콩나물맑은국"] | soup | false | warm |
| m_gaji_stirfry | 가지볶음 | ["가지나물볶음"] | vegetable | false | warm |
| m_woong_braised | 우엉조림 | ["우엉간장조림"] | side | true | ambient |
| m_chicken_katsu | 치킨까스 | ["치킨가스","닭까스"] | protein | false | warm |
아래 Zod 스키마에 맞는 한국 급식 menu_master 50행 JSON 배열로 생성. 같은 메뉴가 별칭 1~3개를 가지도록.
{"type":"object","properties":{"menu_id":{"type":"string"},"canonical_name":{"type":"string"},"aliases":{"type":"array","items":{"type":"string"}},"category":{"type":"string","enum":["protein","vegetable","soup","side","staple","dessert"]},"root_vegetable":{"type":"boolean"},"temp":{"type":"string","enum":["warm","cold","ambient"]}},"required":["menu_id","canonical_name","aliases","category","root_vegetable","temp"]}
조건: category 분포 자연스럽게, JSON 배열만.{"type":"object","properties":{"menu_id":{"type":"string"},"canonical_name":{"type":"string"},"aliases":{"type":"array","items":{"type":"string"}},"category":{"type":"string","enum":["protein","vegetable","soup","side","staple","dessert"]},"root_vegetable":{"type":"boolean"},"temp":{"type":"string","enum":["warm","cold","ambient"]}},"required":["menu_id","canonical_name","aliases","category","root_vegetable","temp"]}메뉴별 식자재 단가. ‘식자재 절감 후보’ 인사이트의 분모. 단가 미입력(결측)·menu_master 에 없는 orphan menu_id 를 일부러 섞어, 비용 인사이트가 결측·미매핑을 만났을 때도 안 깨지는지 보여준다.
| menu_id | date | cost_krw | ingredient_cost_share |
|---|---|---|---|
| m_jeyuk | 2026-03-04 | 2,100 | 0.62 |
| m_kongnamul_soup | 2026-03-04 | 850 | 0.48 |
| m_chicken_katsu | 2026-03-04 | 2,350 | 0.65 |
| m_squid_stirfry | 2026-03-05 | 2,480 | 0.71 |
| m_janchi_noodle | 2026-03-05 | 1,320 | 0.52 |
아래 Zod 스키마에 맞는 cost_per_meal 30행 JSON 배열을 menu_master 의 menu_id 와 매칭되도록 생성.
{"type":"object","properties":{"menu_id":{"type":"string"},"date":{"type":"string"},"cost_krw":{"type":["integer","null"]},"ingredient_cost_share":{"type":["number","null"]}},"required":["menu_id","date","cost_krw","ingredient_cost_share"]}
쉬운 데이터만 만들지 말 것. 하드 케이스 의도적 혼입: (1) cost_krw/ingredient_cost_share 가 null 인 단가 미입력 행 1~2건, (2) menu_master 에 없는 orphan menu_id 1건(미매핑 신메뉴 단가). 그 외 cost_krw 1200~2800, ingredient_cost_share 0.45~0.78. JSON 배열만.{"type":"object","properties":{"menu_id":{"type":"string"},"date":{"type":"string"},"cost_krw":{"type":["integer","null"]},"ingredient_cost_share":{"type":["number","null"]}},"required":["menu_id","date","cost_krw","ingredient_cost_share"]}날씨·교내 이벤트(중간고사/체육행사) 컨텍스트. 변수통제용. 기상 데이터 누락(weather=null) 행을 섞어, 컨텍스트 결측 시에도 인사이트가 안 깨지는지 보여준다.
| date | site | weather | event |
|---|---|---|---|
| 2026-03-04 | 강남센터 | cool | none |
| 2026-03-04 | 판교센터 | cool | none |
| 2026-03-05 | 강남센터 | cold | none |
| 2026-03-05 | 판교센터 | cold | none |
| 2026-03-06 | 강남센터 | cool | none |
아래 Zod 스키마에 맞는 14일 × 2 사이트 context_events JSON 배열로 생성.
{"type":"object","properties":{"date":{"type":"string"},"site":{"type":"string"},"weather":{"type":["string","null"],"enum":["hot","warm","cool","cold"]},"event":{"type":"string","enum":["none","midterm","sports_day","holiday_eve"]}},"required":["date","site","weather","event"]}
쉬운 데이터만 만들지 말 것. weather=null(기상 데이터 누락) 행 1~2건 의도적 혼입. 그 외 weather 분포 자연스럽게, event 평일 7할 none. JSON 배열만.{"type":"object","properties":{"date":{"type":"string"},"site":{"type":"string"},"weather":{"type":["string","null"],"enum":["hot","warm","cool","cold"]},"event":{"type":"string","enum":["none","midterm","sports_day","holiday_eve"]}},"required":["date","site","weather","event"]}영양사 페르소나(LLM 시뮬 외부 평가자) 2명이 LLM 인사이트에 부여한 라벨. Cohen’s κ 의 입력.
| insight_id | evaluator | agree | domain_valid | causal_overclaim | note |
|---|---|---|---|---|---|
| ins_2026-03-10_01 | nutritionist_p1 | true | true | false | — |
| ins_2026-03-10_01 | nutritionist_p2 | true | true | false | 근거 메트릭 명확 |
| ins_2026-03-10_01 | self | true | true | false | — |
| ins_2026-03-10_02 | nutritionist_p1 | true | true | false | — |
| ins_2026-03-10_02 | nutritionist_p2 | false | true | false | 이벤트 표본 부족. 일반화 어려움 |
아래 Zod 스키마에 맞는 external_labels 40 인사이트 × 평가자 3(p1/p2/self) = 120 행 JSON 배열로 생성.
{"type":"object","properties":{"insight_id":{"type":"string"},"evaluator":{"type":"string","enum":["nutritionist_p1","nutritionist_p2","self"]},"agree":{"type":"boolean"},"domain_valid":{"type":"boolean"},"causal_overclaim":{"type":"boolean"},"note":{"type":["string","null"]}},"required":["insight_id","evaluator","agree","domain_valid","causal_overclaim","note"]}
조건: 평가자 간 agree 70% 내외, 불일치 시 note 에 도메인 사유, causal_overclaim=true 인 케이스 10% 내외, JSON 배열만.{"type":"object","properties":{"insight_id":{"type":"string"},"evaluator":{"type":"string","enum":["nutritionist_p1","nutritionist_p2","self"]},"agree":{"type":"boolean"},"domain_valid":{"type":"boolean"},"causal_overclaim":{"type":"boolean"},"note":{"type":["string","null"]}},"required":["insight_id","evaluator","agree","domain_valid","causal_overclaim","note"]}| Python | ingest 파이프라인(CSV/JSON 로그 → PostgreSQL upsert), LLM eval 스크립트(Cohen’s κ / Brier score 계산) |
| Kotlin | Spring Boot 기반 일일 리포트 봇 워커 (cron 스케줄러 + Slack Webhook) |
| Spring | 리포트 봇 + 인사이트 API (`POST /insights`. period 입력 → output_schema 반환) |
| PostgreSQL | tray_logs / menu_master / cost_per_meal / context_events / external_labels 단일 진실원 + 뷰(`v_daily_kpi`) |
| Node.js | Next.js 대시보드 (인사이트 카드 + verification 적중률 차트) |
| JavaScript | 프론트 인사이트 카드 렌더링 + Zod 런타임 검증 |
✓ pass → 그대로 멈춰도 완결입니다. 더 키우고 싶으면 2주차(제안이 맞았는지 확인)로 진행합니다.
✓ pass → 여기서 멈춰도 완결입니다. 더 키우면 3주차(식자재 비용 절감 발주 제안)로 진행합니다.
1) 일/주 인사이트 카드(해석·근거 지표·완곡 표현), 2) 다음 주 메뉴 제안 3건 + 예상 잔반율 + 확신도, 3) 다음 주 실측치로 적중 여부 검증. 제안→확인 한 주기를 한 스키마에 담음.
{"type":"object","properties":{"period":{"type":"string"},"insights":{"type":"array","items":{"type":"object","properties":{"insight_id":{"type":"string"},"headline":{"type":"string","minLength":10},"interpretation":{"type":"string","minLength":20},"evidence_metrics":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"value":{"anyOf":[{"type":"number"},{"type":"string"}]}},"required":["name","value"]},"minItems":1},"hedge":{"type":["string","null"],"minLength":8}},"required":["insight_id","headline","interpretation","evidence_metrics","hedge"]},"minItems":3,"maxItems":5},"proposals":{"type":"array","items":{"type":"object","properties":{"menu_id":{"type":"string"},"reason":{"type":"string","minLength":20},"predicted_tray_loss_pct":{"type":"number"},"confidence":{"type":"string","enum":["low","mid","high"]},"confidence_prob":{"type":"number"}},"required":["menu_id","reason","predicted_tray_loss_pct","confidence","confidence_prob"]},"minItems":3,"maxItems":3},"verification":{"type":["object","null"],"properties":{"prior_period":{"type":"string"},"hit_rate_within_1sigma":{"type":"number"},"brier_score":{"type":"number"},"hits":{"type":"array","items":{"type":"object","properties":{"menu_id":{"type":"string"},"predicted":{"type":"number"},"actual":{"type":"number"},"within_1sigma":{"type":"boolean"},"confidence_prob":{"type":"number"},"actual_hit_binary":{"type":"number"}},"required":["menu_id","predicted","actual","within_1sigma","confidence_prob","actual_hit_binary"]}}},"required":["prior_period","hit_rate_within_1sigma","brier_score","hits"]}},"required":["period","insights","proposals","verification"]}[
{
"input_ref": "2026-03-10 강남센터 (2주차 시작일. verification 은 1주차 제안 결과)",
"output": {
"period": "2026-03-10",
"insights": [
{
"insight_id": "ins_2026-03-10_01",
"headline": "잔반 폭증 메뉴 Top1: 가지볶음",
"interpretation": "잔반율 64%. 평균 22% 대비 3배. 학생 만족도 점수 1.8/5 동행. 뿌리채소·차가운 메뉴군과 패턴 유사.",
"evidence_metrics": [
{
"name": "leftover_rate_pct",
"value": 64
},
{
"name": "satisfaction_1to5",
"value": 1.8
},
{
"name": "category",
"value": "vegetable"
}
],
"hedge": "표본 14일. 시즌 변수 미통제, 채소군 일반화는 후속 검증 필요."
},
{
"insight_id": "ins_2026-03-10_02",
"headline": "신규 관점: 중간고사 기간 만족도 -0.6 동행",
"interpretation": "중간고사 기간 만족도 평균 3.4(평소 4.0). 메뉴 차이가 아닌 이벤트 효과 가능성.",
"evidence_metrics": [
{
"name": "midterm_satisfaction_avg",
"value": 3.4
},
{
"name": "baseline_satisfaction_avg",
"value": 4
}
],
"hedge": "이벤트 표본 3일. 학기당 1회로 추세 단정 어려움."
},
{
"insight_id": "ins_2026-03-10_03",
"headline": "식자재 절감 후보: 가지 → 양배추 (상관 관찰)",
"interpretation": "양배추 메뉴의 잔반율 12%, 단가 -22%, 만족도 +0.3 동행 관찰. 가지 대체군 우선 검토 가치.",
"evidence_metrics": [
{
"name": "cabbage_avg_leftover_pct",
"value": 12
},
{
"name": "cost_delta_pct",
"value": -22
}
],
"hedge": "관찰 표본 4건. 인과로 단정 어려움, A/B 메뉴 교차 1주 검증 권유."
}
],
"proposals": [
{
"menu_id": "m_cabbage_stirfry",
"reason": "가지군 잔반 패턴 회피 + 단가 절감. 과거 잔반 12%. 1σ 임계 ±6%p.",
"predicted_tray_loss_pct": 14,
"confidence": "mid",
"confidence_prob": 0.6
},
{
"menu_id": "m_curry_rice",
"reason": "‘따뜻 + rice staple’ 군 만족도 4.5/5, 잔반 6.4%. 시험기간 만족도 회복용.",
"predicted_tray_loss_pct": 8,
"confidence": "high",
"confidence_prob": 0.85
},
{
"menu_id": "m_bibimbap",
"reason": "채소 비중 높지만 단백질 동행으로 채소 잔반 회피. 과거 잔반 9.8%.",
"predicted_tray_loss_pct": 11,
"confidence": "mid",
"confidence_prob": 0.6
}
],
"verification": {
"prior_period": "2026-03-03 ~ 2026-03-09 (1주차 제안)",
"hit_rate_within_1sigma": 0.67,
"brier_score": 0.18,
"hits": [
{
"menu_id": "m_jeyuk",
"predicted": 13,
"actual": 12.3,
"within_1sigma": true,
"confidence_prob": 0.85,
"actual_hit_binary": 1
},
{
"menu_id": "m_kongnamul_soup",
"predicted": 14,
"actual": 18.5,
"within_1sigma": true,
"confidence_prob": 0.6,
"actual_hit_binary": 1
},
{
"menu_id": "m_squid_stirfry",
"predicted": 17,
"actual": 24.7,
"within_1sigma": false,
"confidence_prob": 0.6,
"actual_hit_binary": 0
}
]
}
}
},
{
"input_ref": "2026-03-12 강남센터",
"output": {
"period": "2026-03-12",
"insights": [
{
"insight_id": "ins_2026-03-12_01",
"headline": "잔반 폭증 메뉴 Top1: 우엉조림",
"interpretation": "잔반율 58%. 어제 가지볶음과 유사. 뿌리채소 category=side 군 패턴.",
"evidence_metrics": [
{
"name": "leftover_rate_pct",
"value": 58
},
{
"name": "root_vegetable",
"value": "true"
}
],
"hedge": "뿌리채소 표본 4건. 일반화는 후속 검증."
},
{
"insight_id": "ins_2026-03-12_02",
"headline": "따뜻한 메뉴 vs 차가운 메뉴 잔반 차이",
"interpretation": "cold 날씨 14일 중 따뜻 메뉴 잔반 18% / 차가운 메뉴 32%. 시즌 메뉴 가중치 시그널.",
"evidence_metrics": [
{
"name": "warm_menu_leftover_pct",
"value": 18
},
{
"name": "cold_menu_leftover_pct",
"value": 32
}
],
"hedge": "weather 컨텍스트 결측일 2일 제외."
},
{
"insight_id": "ins_2026-03-12_03",
"headline": "요일별 만족도 편차 관찰",
"interpretation": "수요일 3.9 / 금요일 4.4 동행 관찰. 주말 직전 + 금요일 메뉴 가중치 영향 가능성.",
"evidence_metrics": [
{
"name": "wed_avg_satisfaction",
"value": 3.9
},
{
"name": "fri_avg_satisfaction",
"value": 4.4
}
],
"hedge": "요일 × 2주 = 표본 2. 단정 어려움."
}
],
"proposals": [
{
"menu_id": "m_tteokgalbi",
"reason": "‘따뜻 + protein’ 군 만족도 4.7, 잔반 8%. 수요일 만족도 회복용.",
"predicted_tray_loss_pct": 9,
"confidence": "high",
"confidence_prob": 0.85
},
{
"menu_id": "m_donkatsu",
"reason": "과거 잔반 7.5%, 만족도 4.7. 시즌·이벤트 무관 안정.",
"predicted_tray_loss_pct": 10,
"confidence": "high",
"confidence_prob": 0.85
},
{
"menu_id": "m_spaghetti",
"reason": "noodle staple 다양화 + 따뜻 메뉴군. 과거 잔반 11.3%.",
"predicted_tray_loss_pct": 13,
"confidence": "mid",
"confidence_prob": 0.6
}
],
"verification": {
"prior_period": "2026-03-10 ~ 2026-03-11 제안",
"hit_rate_within_1sigma": 0.67,
"brier_score": 0.19,
"hits": [
{
"menu_id": "m_cabbage_stirfry",
"predicted": 14,
"actual": 11,
"within_1sigma": true,
"confidence_prob": 0.6,
"actual_hit_binary": 1
},
{
"menu_id": "m_curry_rice",
"predicted": 8,
"actual": 6.4,
"within_1sigma": true,
"confidence_prob": 0.85,
"actual_hit_binary": 1
},
{
"menu_id": "m_bibimbap",
"predicted": 11,
"actual": 18.2,
"within_1sigma": false,
"confidence_prob": 0.6,
"actual_hit_binary": 0
}
]
}
}
},
{
"input_ref": "2026-03-16 강남센터",
"output": {
"period": "2026-03-16",
"insights": [
{
"insight_id": "ins_2026-03-16_01",
"headline": "메뉴를 특정 못 한 배식 기록이 늘었습니다",
"interpretation": "메뉴 식별자가 비어 있는 기록이 늘어 어느 메뉴 잔반인지 묶지 못했습니다. 표기를 맞추기 전에는 순위를 내지 않습니다.",
"evidence_metrics": [
{
"name": "unmapped_rows",
"value": 7
}
],
"hedge": "미매핑 행을 뺀 집계라 순위가 바뀔 수 있습니다."
}
],
"proposals": [
{
"target": "배식 단말 메뉴 표기 통일",
"reason": "메뉴 식별자 결측이 반복됩니다",
"expected_effect": "잔반 집계 복구",
"confidence": "high"
}
],
"verification": {
"prior_period": "2026-03-12 제안",
"hit_rate": 0.5,
"hits": [
{
"insight_id": "ins_2026-03-12_01",
"predicted": "뿌리채소 잔반 지속",
"actual": "지속",
"hit": true
},
{
"insight_id": "ins_2026-03-12_02",
"predicted": "따뜻한 메뉴 선호",
"actual": "차이 없음",
"hit": false
}
]
}
}
}
]자동: (1) Zod parse, (2) evidence_metrics.name 이 입력 스키마 실제 메트릭 이름, (3) 단정 표현 토큰 검출 + LLM-as-judge 의미 단위 평가, (4) Brier score 자동 계산(사이클 1 reporting / 사이클 2+ KPI). 수동: 외부 평가자 2명 비동기 라벨 → bootstrap Cohen’s κ CI 하한 ≥ 0.6, judge 인간 spot check 5%. verification 은 차주 실측치 자동 조인.