① 한눈에 — 이 모듈이 무엇을 왜 하나
이 모듈은 아이가 며칠간 먹은 식재료 목록을 받아 '영양 신호등'을 켭니다. 부엌 저울로 그램(g)을 재는 게 아니라 "이 영양소를 채워주는 음식을 며칠에 한 번꼴로 먹었나"라는 빈도로 판단합니다. 단백질은 매일(주5일+) 먹어야 초록, 철분처럼 몸에 저장이 잘 되는 건 주2일만 닿아도 초록이 됩니다. 정량 측정이 불가능한 가정·기관 식단 기록의 한계를 '빈도'라는 현실적 잣대로 바꾼 것입니다. 색은 초록(충분)·노랑(조금 부족)·빨강(부족) 세 단계입니다.
이 모듈에는 세 갈래가 있습니다. (가) 식사 신호등(nutrition.ts): 36종 영양소(보건복지부 KDRI 2025)와 8개 식품군(곡물·콩류·유제품·고기계란·생선해산물·비타민A채소·기타채소·과일)을 각각 빈도로 평가하고, 가공식품·반복 메뉴 감점을 붙여 '다양성 점수'를 냅니다. (나) 간식 엔진(snack.ts): 끼니와 완전히 분리해 간식만 봅니다. 과자·사탕·단 음료 같은 초가공을 얼마나 자주 주는지, 좋은 통식품(과일·삶은 계란·치즈)으로 채우는지, 그리고 간식이 다음 끼니 입맛을 망쳤는지를 봅니다. (다) 체격·성장 트랙(growth-reference.ts + coachWeekly.ts): 키·몸무게를 질병관리청 성장도표에 빗대 또래 곡선을 잘 따라가는지 보고, 저체중·성장 더딤이면 결핍 다양성 대신 탄단지(고기·계란·콩·유제품) 보강으로 추천 방향을 틀고(macro 트랙), 과체중이면 간식 절제로 틉니다.
핵심 비유는 '신호등 + 안전장치'입니다. 데이터가 적으면(기록 3일 미만) 섣불리 빨강을 켜지 않고 노랑으로 보류하고, 비타민D처럼 음식보다 햇빛·보충제로 채우는 건 빨강을 끕니다. 짠 음식 많은 한국 식단 특성상 나트륨은 아예 집계에서 빼서 '괜한 빨강(가짜 경고)'을 막습니다. 결과물은 별도 화면이 아니라 매일 가는 코칭 편지 안에 자연스럽게 녹습니다.
결핍 판정은 '빈도 밴드'로 합니다. 영양소를 몸의 저장성·식품 분포·결핍 위험에 따라 매일군(green=주5일+)·자주군(주4일+)·가끔군(주2일+)으로 나눕니다. 한 번도 안 닿으면(0일) 빨강이지만, 비타민D는 빨강을 끄고(보충제 영양소), 기록 5일 미만이면 빨강을 노랑으로 보류합니다. 데이터가 3일 미만이면 모두 '참고(reference)'로 보류해 가짜 경고를 안 냅니다. 8식품군은 매일군(곡물·채소·과일·유제품 주5회)과 로테이션군(고기계란 주5회, 생선해산물 주2회, 콩류 주2회)으로 목표가 다르고, 로테이션군은 0회일 때만 빨강입니다.
편식 대응의 핵심은 '강요 금지 + 잘 먹는 것에 섞기'입니다. 부족 영양소가 나오면 그걸 채우는 음식을 추천하되(NUTRIENT_FOODS), 이 모듈은 신호만 만들고 실제 편지는 잘 먹는 음식에 결핍을 곁들이는 방향으로 씁니다. 집과 기관(점심 급식)을 분리해서, 부모가 못 바꾸는 기관 급식엔 가공·반복 감점을 안 매기고(applyMealPenalty=false) 과일·유제품 결핍도 빼줍니다(기관은 점심만이라 부당 감점 방지). 가공식품도 단백질은 주니 과잉 처벌하지 않게 감점 상한(가공 22·반복 12)을 둡니다.
체격은 '방향'으로만 말합니다 — 체중·살·다이어트 단어를 절대 안 쓰고, BMI 밴드·성장곡선은 '든든하게/유지/넉넉히 챙기기'로만 표현합니다. 간식의 작고 단단한 식품(방울토마토·생당근·포도·견과)은 추천에 항상 조리 표시('반으로 잘라'·'익혀·강판'·'잘게')를 붙여 질식을 막고, 같은 멘트가 같은 예시로만 보이지 않게 매일 회전(rotate)시킵니다.
구현된 내용 — 함수·타입·데이터흐름 (실측)
이 모듈은 세 파일에 걸쳐 산다 — 식품군·미량 신호등(nutrition.ts), BMI·성장 추종도(growth-reference.ts), macro 트랙 + 거울 스케줄(coachWeekly.ts 주간계획 모듈). cron(app/api/cron/coach/route.ts)이 셋을 조립해 손(coach.ts)에 넘긴다.
A. 식품군·미량 신호등 — lib/nutrition.ts
| 함수 · 타입 (file:line) | 역할 · 실측 동작 |
|---|---|
nutrientsOf(ing, catOf) :147 | 식재료 1개 → 커버 영양소 배열. ① 농진청 정밀맵 GEN_NUTRI(getNutrientMap) ∪ NUTRI_MAP(손큐레이션 200여종) 우선, ② 둘 다 없으면 CATEGORY_NUTRI 18종 카테고리 빗대기 폴백. '모름'으로 안 비움. |
computeSignals(byDay, catOf) :266 → NutrientSignal[] | KEY 15종 신호등. KEY_BAND(daily/frequent/occasional) × KDRI_BAND_NEED{5,4,2} 문턱. 기록<3일=yellow 보류, d===0이면 비타민D·기록<5일은 yellow 클램프 아니면 red. cron이 red만 뽑아 reds로 사용. |
computeKdriSignals(byDay, catOf, ageBand) :485 | 36종 KDRI 신호등(연령대 KDRI_VAL_BY_AGE 1-2/3-5/6-8세 기준치). mapped 없으면 'reference', 기록<3일 보류. 분모는 달력 7일 고정(누수 교정). 홈 모달용 데이터. |
computeGroupSignals(byDay, catOf) :200 | 8식품군 신호등(GROUP_TARGET: daily 5+ / rotation). daily는 weeklyEst≥5 green·≥2 yellow·else red. rotation은 0회만 red·≥green green·else yellow. proteinOk(고기계란·생선·콩 중 매일≥1군) 동반 반환. |
computeFoodGroups(allIng, catOf) :307 | 8군 covered/missing 분해(FOOD_GROUP 직접 → CATEGORY_GROUP 빗대기). cron이 missing/homeMissing으로 분리. |
groupOf(ing, catOf) :197 | 식재료→식품군 정본(FOOD_GROUP‖CATEGORY_GROUP[catOf]). K-01 — catOf(카테고리 18종)를 식품군(8종)과 직접 비교하던 네임스페이스 버그 수정용으로 export. refExposable(거부 결핍군 필터)이 공유. |
computeTimeseries(byDate, menuFreq, catOf, today, {assertNoVeg}) :335 | 추세 사실 문장('채소 N일째 없음'·반복 메뉴 3회+·흰쌀밥→잡곡 제안 전환). LLM 날짜 환각 차단(분석이 계산한 사실만). assertNoVeg는 catOf 신뢰 시만 '채소 없음' 단정(P4). |
computeDiversityScore(args) :551 | 점수 단일 진입점: 다양성 base(green100/yellow65/red0 평균) − 게이트캡(red 1개=90, 추가군당 −8, 하한66) − 가공패널티(상한22) − 반복패널티(상한12). applyMealPenalty=false 또는 끼니<3건이면 패널티 미적용. daycareMode면 과일·유제품 군 제외. |
processedPenalty :525 · repeatPenalty :539 · isProcessed :518 | ULTRA_RE(초가공 가중1.0)·CURED_RE(가공육 0.7) 정규식으로 메뉴명 매칭. 흰쌀밥·김치·국 등 REPEAT_SKIP 제외. snack 엔진도 isProcessed 재사용. |
상수: KDRI_NUTRIENTS:381 · KDRI_VAL_BY_AGE:451 · KDRI_EXCLUDED:374 · GROUP_TARGET:190 · NUTRIENT_FOODS:164 | KDRI 2025 연령대 기준치·제외 5종(나트륨·에너지·수분·불소·크롬)·8군 목표·부족 영양소별 추천 식재료. |
B. BMI · 성장 추종도 — lib/growth-reference.ts 라이브
| 함수 (file:line) | 역할 · 실측 동작 |
|---|---|
bmiOf:210 · bmiPercentile:158 · bmiBand(pct):172 → BmiBand('저체중'|'정상'|'과체중'|'비만') | 키·몸무게·성별·월령 → KDCA 2017 LMS로 BMI 백분위·밴드. cron bmiBandFor(route:240)가 최신 growth_logs 체위 우선, 없으면 children 스냅샷. |
growthTracking(baseline, current, sex, metric) :263 → GrowthTrack | null | 성장곡선 추종도: 첫 측정(baseline)으로 그 아이 백분위 채널을 잡고, 현재 측정의 z(SDS) 하향 이탈로 status 판정 — zDrift≤−1.34='경고', ≤−0.67='주의', else='양호'. 측정간격<1개월='정보부족'(노이즈 보류). z 둘 중 하나라도 null이면 전체 null. score=100+min(0,zDrift)×45. |
growthTrackToPhrase({band, height, weight}) :290 → string | null | BMI 밴드+추종도 → 비수치 P10 한 구절. 우선순위: 곡선 경고>주의 > BMI 비만·과체중·저체중. 정상·정보부족이면 null(거울 생략). 숫자·퍼센타일·BMI·체중·살 단어 0(P10 톤). 경고 키 구절: '키가 또래 흐름을 천천히…단백질·열량(고기·계란·콩·유제품)을 넉넉히…소아과 상의'. |
C. macro 트랙 + 거울 스케줄 — lib/coachWeekly.ts (주간계획 모듈) 라이브(코드)
| 함수 · 타입 (file:line) | 역할 · 실측 동작 |
|---|---|
MacroTrack 타입 :71 · buildMacroTrack(ctx) :485 | #7 macro 성장 nudge. EnrichContext.band/heightTrack/weightTrack 입력. lowWeight(band==='저체중') 또는 growthLag(height/weight track status가 '주의'|'경고')이면 active:true·boostGroups=['고기·계란','콩류','유제품']·boostDishes(각 군 대표의 popularDishesFor) 산출. overweight(과체중·비만)이면 snackRestraint:true. phrase=growthTrackToPhrase(...). cadenceWeek=(arcWeek%2===0) — 격주(2주 연속 금지·측정 저빈도 존중). |
enrichWeeklyPlan(synth, ctx) :520 → PlanDetail | 일요일 종합 위 7일치 결정론 후처리(LLM 0콜). macro를 먼저 계산해 저체중/성장더딤이면 boostGroups 식재료를 supplyPool 앞에 주입('고기류 타깃'화)(:526-529). macroTrack을 PlanDetail에 박제. |
buildMirrorSchedule(deficits, covered, macro, daycare, freqMap) :500 → MirrorSlot[7] | 7일 거울 스케줄. macro.active && cadenceWeek면 슬롯 i===3에 macro.phrase를 kind:'macro'로 박제(:504). 결핍 2군+면 라운드로빈, 단일 결핍은 격일 쿨다운(#5·K-04b — 짝수 슬롯만 결핍줄·홀수 칭찬/생략). 거울줄은 mirrorLineFor 3변형 회전 + deficitDishFor 구체 dish. |
pickPlanSlot(detail, {daySeed,cidHash,deficitNow,recentIngredients}) :576 → PlanSlotPick | 일간 소비(유연성 가드). macroPhrase = (macroTrack.active && cadenceWeek && mirrorSchedule[idx].kind==='macro') ? phrase : null(:582). 즉 그날 슬롯이 macro 슬롯일 때만 macroPhrase가 살아 나온다. |
D. 간식 엔진 — lib/snack.ts
| 함수 (file:line) | 역할 |
|---|---|
evaluateSnacks(args) :149 → SnackEval|null | SNACK_SLOTS만 추려 worst-of로 ultra/sugary/good 분류, 간식 직후 0~2.5h 내 덜 먹은 끼니(ate_well=false)로 간섭성, snackStance(band)→stance(encourage/maintain/reduce), reds로 결핍보완 간식. |
snackStance(band) :74 · goodSnackSuggestions(stance, reds, rotate) :106 · snackEvalToPrompt(ev, rotate) :229 | BMI 밴드→방향(체중 단어 금지). 좋은 간식 4종(질식 조리표시 보존·중복제거·예시 rotate). 편지용 한 블록. |
E. cron 조립 + 손(편지) 배선 — app/api/cron/coach/route.ts · lib/coach.ts
- BMI·성장 산출(route:384-405):
snackBand=bmiBandFor()·heightTrack/weightTrack=growthTracking(firstGrowth, latestGrowth)— baseline(가장 오래된) + current(최신) 2점으로 추종도 계산(firstGrowth:206·latestGrowth:205). 격주 게이트: 최근 13일 내 성장 거울 노출 있으면(recentGrowthDates) 생략, 없으면growthMirrorCtx=growthTrackToPhrase(...). - macro 슬롯(route:624-627):
enrichWeeklyPlan에band·heightTrack·weightTrack전달 → macroTrack 박제.pickPlanSlot(route:852)이 오늘 슬롯·macroPhrase픽. - 손 배선(route:916):
growthMirror: planSlotCtx?.macroPhrase ?? growthMirrorCtx— macro 슬롯 우선, 없으면 격주 BMI 거울. 같은 날 간식 멘트는 생략(route:834 — 성장 거울 있으면 snackText=null, 한 번에 하나). - 편지 주입(coach.ts:788-789·793):
growthBlock = b.growthMirror ? '[⚠️ 성장 신호 — 숫자·퍼센타일·BMI·체중·살 단어 금지…]\n'+growthMirror : ''→ 프롬프트 본문에${growthBlock}인서트. 작문기 재생성(coach.ts:1031)이 성장 구절 누락 시 fixNotes로 강제 포함.
growthTracking은 firstGrowth+latestGrowth 2점으로 호출되고, growthMirror/growthBlock/macro 슬롯이 손까지 닿습니다. 다만 ⑤에서 보듯 — growthTrackToPhrase가 '정상·정보부족'이면 null을 반환하므로, 측정이 1점뿐이거나(정보부족) 밴드가 정상이면 거울이 비어 실제 발행 편지에는 한 번도 안 들어갑니다(랄프위검 24통 0토큰). 배선은 살았으나 데이터 다리(2점 측정)가 비어 트랙이 실전에서 잠자는 상태입니다.설계 아키텍처 시각화 — 입력테이블 → 가공함수 → 출력컨텍스트키
데이터 파이프라인 전체 그림. 초록=손까지 닿음, 빨강 점선=코드는 있으나 실전에서 잠자는(DEAD) 경로.
(≥2점·≥1개월 간격 필요)
(정상·정보부족 → null)
┌──────────────────────────── 두 진단 축 (이 모듈의 골격) ────────────────────────────┐ │ │ │ 축1: 식품군 8분류 다양성 ──▶ reds·missing ──▶ [라이브] 거울줄·추천 타깃·점수 │ │ (computeGroupSignals) 매일 손에 닿음 │ │ │ │ 축2: 체격·성장 연료(macro) ──▶ band·track ──▶ [코드 라이브 / 실전 잠] phrase │ │ (bmiBand·growthTracking) growthTrack null 다발 → growthBlock 빈 채 발행 │ │ ↑ │ │ └── 데이터 다리 부재: growth_logs 2점(≥1개월) 자녀 거의 없음 → 정보부족/정상 │ │ → growthTrackToPhrase=null → macro 트랙·성장 nudge 실전 미점화 │ └──────────────────────────────────────────────────────────────────────────────────────┘ 결론: 칼로리·단백·지방 절대량(성장 연료) 차원이 진단에서 사실상 빠짐 — 축1만 굴러감
작동 예시 — 아린 실데이터 before / after
아린(5세) 최근 5일 집/기관 식재료 → 이 모듈 통과 → 손에 닿는 출력. 두 케이스로 비교한다.
// computeSignals — 아린(5세) 최근 5일 식재료(날짜별)
computeSignals([
["쌀","소고기","계란","감자","미역"],
["쌀","계란","김치","두부"],
["쌀","소고기","감자","양파"],
["쌀","계란","미역","당근"],
["쌀","닭고기","감자","김치"],
], catOf)
// evaluateSnacks — 같은 아이 meal_logs + 밴드 + reds
evaluateSnacks({
rows: [
{slot:"pm_snack",log_date:"2026-06-15",
menus:["초코우유","과자"],ingredients:[],
ate_well:true,meal_time:15.5},
{slot:"dinner",log_date:"2026-06-15",
menus:["밥","미역국"],ingredients:["쌀","미역"],
ate_well:false,meal_time:18.5},
],
band:"정상",
reds:["오메가3","비타민D","철"],
})
// macro 트랙 입력(EnrichContext 발췌)
band: "정상", heightTrack: null, weightTrack: null
// ↑ growth_logs 2점 없음 → growthTracking=null// computeSignals → NutrientSignal[] (발췌)
[{nutrient:"단백질",daysCovered:5,level:"green"},
{nutrient:"철", daysCovered:4,level:"green"},
{nutrient:"비타민A",daysCovered:1,level:"yellow"},
{nutrient:"오메가3",daysCovered:0,level:"yellow"}]
// computeGroupSignals: 콩류 weeklyEst≈1.4
// → rotation·0회 아님 → yellow (0회였다면 red)
// evaluateSnacks → SnackEval
{ultraCount:1, interference:[{date:"2026-06-15",
mealSlot:"dinner",gapH:3,kind:"ultra"}],
stance:"maintain", band:"정상",
goodSuggestions:["치즈","삶은 계란","두유","과일"]}
// buildMacroTrack(band:정상,tracks:null)
{active:false, reason:null, boostGroups:[],
phrase:null, cadenceWeek:false} // ← macro 잠
// → LetterInput.growthMirror = null (macroPhrase·격주거울 둘 다 null)
// → coach.ts growthBlock = '' (성장 구절 0)거울줄(손에 닿음·라이브):
buildMirrorSchedule이 콩류 결핍을 잡아 슬롯에 박고, 편지는 →"소고기·계란은 잘 챙기고 있어요. 집 끼니에 콩류(두부조림 같은 걸로)를 한 번씩 곁들이면 다양성이 좋아져요."
성장줄(DEAD):
growthMirror=null → growthBlock 빈 채 발행. 성장·BMI 토큰 0개. 이게 랄프위검 24/24의 실태다.입력에
band:"저체중"(또는 weightTrack.status:"주의")만 바뀌면:•
buildMacroTrack → active:true·reason:"lowWeight"·boostGroups:["고기·계란","콩류","유제품"]·phrase:"또래보다 가벼운 편이라 끼니 양과 단백질·열량(고기·계란·콩·유제품)을 평소보다 조금 더…"•
enrichWeeklyPlan이 supplyPool 앞에 고기·계란/콩류/유제품 식재료 주입 → 추천 타깃이 결핍 다양성에서 탄단지 보강으로 전환• 격주(
cadenceWeek)인 주에 한해 mirrorSchedule[3]에 macro 구절 박제 → 그날 편지 본문에 성장 nudge 한 구절그러나 이 분기는 실데이터에서 거의 점화되지 않는다 — band 산출에 필요한 growth_logs 측정이 비어 대부분 band=null/정상으로 떨어지기 때문(⑤ 천장).
남은 문제점 · 해야할 일 — 랄프위검 실측 천장 (연속성 47점, 2026-06-20)
EARNED(사실) — 식품군 8분류 신호등·결핍 거울줄·간식 stance·다양성 점수·집/기관 분리·질식 가드는 실측상 매일 손에 닿아 작동한다. macro/성장 코드(buildMacroTrack·growthTracking·growthBlock 배선)도 깔려 있다. 아래는 미화 없이 정직한 천장.
| 증상 | 근본원인 | 해야할일 | owner |
|---|---|---|---|
| [천장3] macro/성장 트랙 100% DEAD — 발행 24통에 BMI/키/몸무게/percentile 토큰 0. '단백질'은 안심 멘트로만 소비, 성장신호→고기류 타깃 전환 0회. #7 코드는 깔렸으나 본문 점화 0. | ① growthTrackToPhrase(growth-reference:290)가 정상·정보부족이면 null을 반환 → 측정 1점뿐이거나 정상 밴드면 growthBlock이 빈 문자열로 발행. ② 더 근본은 데이터 다리 부재: growthTracking은 firstGrowth+latestGrowth 2점(≥1개월 간격)을 요구하는데, 실제 growth_logs에 2점 쌓인 자녀가 거의 없어 track=null/정보부족으로 떨어짐. P0-D(child_daily_state)는 환경신호 전용이라 성장신호 다리가 아님. |
성장지표 데이터 다리 신설 — care 앱에서 키·몸무게 입력 유도(온보딩+주기 리마인드)로 growth_logs 2점 확보 경로를 명시 배선. 1점만 있는 자녀는 절대량(BMI 밴드)만이라도 살리도록 growthTrackToPhrase의 band 분기를 측정 1점에서도 쓰게 게이팅 분리(정보부족은 track만 보류·band는 살림). |
P0 · 코칭엔진(route+growth-reference) |
| 결핍 모델이 식품군 다양성 축에만 편향 — 칼로리·단백질·지방 절대량(성장 연료) 차원이 진단에서 통째로 빠짐. | computeGroupSignals/computeDiversityScore가 전부 '며칠 닿았나'(빈도·다양성) 축. 단백질도 proteinOk(매일≥1군 닿음)로만 보고 총량·열량은 안 봄. BMI/percentile이 진단(reds·missing) 산출로 흐르는 다리가 없음 — macro는 enrichWeeklyPlan 부가 트랙에만 있고 핵심 신호 산출에 안 섞임. |
저체중/성장더딤 분기 강화 — band='저체중' 또는 track.status∈{주의,경고}이면 reds/supplyPool 산출 단계에서 결핍 다양성군 대신 고기류(단백·지방) 타깃을 1차 후보로 승격(현재 enrichWeeklyPlan:526에 supplyPool 주입은 있으나, 두뇌 시나리오·reds까지는 안 닿음 → 시나리오 게이트에도 macro 분기 추가). | P1 · 코칭엔진(coachScenarios+route) |
| '끼니 30분 전 간식·우유·주스 멈춤' 무브가 BMI 무관 blind 4회 — 저체중 아이엔 총칼로리를 깎아 macro 목표와 정반대. | 간식 절제·식사 간섭 무브 셀렉터가 snackStance/macro의 snackRestraint를 안 봄. 저체중(encourage)인데도 칼로리-차단 무브가 발동. evaluateSnacks의 stance는 산출되나 무브 선택 게이트에 stance/band 가드가 없음. |
무브 셀렉터 BMI 가드 — 칼로리-차단 무브(끼니 전 간식 멈춤·우유 줄이기)는 저체중/성장더딤(macro.snackRestraint=false)이면 억제, 과체중/비만(snackRestraint=true)이면 강화. snackStance를 무브 셀렉터 입력으로 연결. | P1 · 코칭엔진(coach 무브 게이트) |
| 거울 앵무새 잔여(비대칭 해소됨·완전 종결 아님) | 단일 결핍 격일 쿨다운(#5·K-04b)·문장틀 3변형 회전·macro 슬롯 격주는 라이브. 다만 추천풀이 협소(콜드스타트 Tier2·두부5/달걀6)해 같은 식재료가 회전 풀로 재등장하는 잔여 반복. | 추천풀 확장(Tier3 youa 큐레이션·앵커 다양화)으로 거울 dish 다양성 확보. care 5단계 실데이터 누적 시 자연 완화. | P2 · 추천엔진(②)·food-mapping |
문헌 · 이론 근거 (실구현 적용)
| 이론 · 문헌 | 어떻게 적용하나 |
|---|---|
| 보건복지부 「2025 한국인 영양소 섭취기준(KDRI)」 | 36종 영양소의 연령대별(만1-2/3-5/6-8세) 권장 기준치를 KDRI_VAL_BY_AGE로 상수화해 모달 표시·신호등 밴드 근거로 사용 |
| 농촌진흥청 국가표준식품성분표 10.4 | 식재료별 100g당 1일 권장 15%↑를 '공급원'으로 보는 정밀 영양맵(GEN_NUTRI)을 빗대기 대신 1차 출처로 사용 |
| 식약처 영유아 식생활지침 / WHO 8식품군 | '매끼 곡류·단백질·채소, 매일 우유·과일'을 GROUP_TARGET(매일군 주5·로테이션군)으로 코드화 |
| 질병관리청 「2017 소아청소년 성장도표」 | 신장·체중·BMI for-age LMS를 kdca-growth-lms.json으로 박제(P3/P50/P97 골든·vitest 384). WHO 아님(만3세+ 국내값 상이·24mo 미만 WHO 폴백) |
| 성장곡선 추종 모델(z/SDS 채널) | 첫 측정으로 백분위 채널을 잡고 z 하향 이탈로 주의(−0.67)/경고(−1.34) — 절대 위치 아니라 '자기 곡선 유지'를 봄. 가정 측정 노이즈는 측정간격<1개월='정보부족'으로 보류 |
| Satter 식사 구조화(division of responsibility) | 식간 군것질·단 음료가 끼니 식욕을 떨어뜨린다는 원리로 간식 직후 끼니를 덜 먹은 '식사 간섭성' 판정 |
| 영양 신호등 자체 근거 보고서 | 체내 저장·식품 분포·결핍 위험 3축으로 매일/자주/가끔 밴드(green 문턱 5/4/2일)를 정함 |
가드 — 괴식·환각·안전 차단 (실구현)
- 빈도 평가 명시(정량 g 아님) — '며칠 커버' 기반, 과대 단정 금지
- 데이터 가드: 기록<3일이면 신호 보류(yellow/reference), 기록<5일이면 red 보류, 비타민D는 빈도 red 비활성(보충제 영양소)
- 빗대기 안전망: 미매핑 식재료를 '모름'으로 비우지 않고 카테고리 근사 / KDRI 미매핑은 reference(가짜 빨강 방지)
- KDRI 제외 5종(나트륨·에너지·수분·불소·크롬) — 결핍 걱정 없는 항목 신호화 안 함
- 환각 차단:
computeTimeseries가 날짜 사실만 계산해 LLM 추정 방지,assertNoVeg는 catOf 신뢰 시에만 '채소 없음' 단정(P4) - 질식 안전: 간식 추천에 (반으로 잘라)·(익혀·강판)·(잘게) 조리표시 보존 — 작문기가 통째 표현으로 못 바꾸게
- 체중 민감 가드:
snackStance·snackEvalToPrompt·growthTrackToPhrase모두 체중·살·다이어트·BMI·숫자·퍼센타일·등급 단어 금지, 방향(stance)·비수치 문구만 노출. growthBlock 지침도 동일 금지어 강제(coach.ts:789) - 가공/반복 패널티 과잉처벌 방지: 끼니 3건 미만 미적용, 상한(가공22·반복12), 흰쌀밥·김치·국 등 주식 제외
- daycareMode: 점심 급식만 평가하는 기관이 과일·유제품 결핍으로 부당 감점되지 않게 제외
- 성장 잔소리 저빈도: 성장 거울은 격주(13일 쿨다운), macro 슬롯은
cadenceWeek=arcWeek%2===0(2주 연속 금지) — 측정 저빈도 존중
CoachContext 전달 — 🧠 뇌 + ✍️ 손 양쪽
이 모듈이 채우는 컨텍스트 키: