diff --git a/src/lib/ai/coach/memory-snapshot.ts b/src/lib/ai/coach/memory-snapshot.ts index ff7371003..36c0a1560 100644 --- a/src/lib/ai/coach/memory-snapshot.ts +++ b/src/lib/ai/coach/memory-snapshot.ts @@ -38,6 +38,7 @@ import { type BandTransition, } from "@/lib/insights/narrative/period-narrative"; import type { BaselineProfile } from "@/lib/insights/derived"; +import { buildCoachFactsBlock } from "./facts"; /** The period the rolling profile recalls — month is the high-signal beat. */ const MEMORY_PERIOD: NarrativePeriod = "month"; @@ -72,6 +73,11 @@ export interface PriorNarrativeRecall { export interface CoachMemoryBlock { priorNarrative?: PriorNarrativeRecall; trendMemory: Record; + /** + * v1.11.1 — durable personal facts the Coach has learned (top-N, ranked by + * confidence then recency). Descriptive, never diagnostic. Absent when none. + */ + facts?: Array<{ category: string; text: string }>; } /** Pull the headline + driver recall off the latest period narrative. */ @@ -147,11 +153,29 @@ export async function buildCoachMemoryBlock( // A context failure leaves trendMemory empty — never sinks the turn. } - if (!priorNarrative && Object.keys(trendMemory).length === 0) { + // Sub-source 3 (v1.11.1): durable personal facts the Coach has extracted. + // Fault-isolated like the others — a read/decrypt failure drops the facts + // sub-block and never sinks the turn. + let facts: Array<{ category: string; text: string }> | undefined; + try { + const factsBlock = await buildCoachFactsBlock(userId); + if (factsBlock && factsBlock.facts.length > 0) { + facts = factsBlock.facts; + } + } catch { + facts = undefined; + } + + if ( + !priorNarrative && + Object.keys(trendMemory).length === 0 && + !facts + ) { return null; } const block: CoachMemoryBlock = { trendMemory }; if (priorNarrative) block.priorNarrative = priorNarrative; + if (facts) block.facts = facts; return block; } diff --git a/src/lib/ai/coach/system-prompt.ts b/src/lib/ai/coach/system-prompt.ts index ac2a384a4..9ae0d0542 100644 --- a/src/lib/ai/coach/system-prompt.ts +++ b/src/lib/ai/coach/system-prompt.ts @@ -200,6 +200,14 @@ ISO-week means. not clinical assessments or diagnoses — never frame a band as a medical finding, and lean on lower-confidence / few-day entries cautiously. Never recompute or second-guess the number. +- The SNAPSHOT's "memory" block MAY carry a "facts" list — durable + things you have learned about this user across conversations (stable + preferences, conditions they have told you about, goals, constraints, + life context). Use them to personalise your reply and to avoid + re-asking what you already know. Treat each as the user's OWN stated + context, DESCRIPTIVE not diagnostic — never restate a "condition" fact + as a medical finding, and never invent a fact the block does not + carry. If a fact seems outdated, gently check it rather than assume. EVIDENCE BLOCK @@ -471,6 +479,16 @@ ISO-Wochenmittel zusammen. das der SNAPSHOT nicht enthält. Der "workouts"-Block führt die jüngsten Einheiten plus eine Zusammenfassung je Sportart, nicht jede Einheit. +- Der "memory"-Block des SNAPSHOT kann eine "facts"-Liste tragen — + dauerhafte Dinge, die du über diesen Nutzer gelernt hast (stabile + Vorlieben, vom Nutzer selbst genannte gesundheitliche Umstände, + Ziele, Einschränkungen, Lebenskontext). Nutze sie, um deine Antwort + zu personalisieren und nicht erneut zu fragen, was du schon weißt. + Behandle jeden Eintrag als die EIGENE Aussage des Nutzers, + BESCHREIBEND, nicht diagnostisch — formuliere einen "condition"-Fakt + nie als medizinischen Befund um und erfinde nie einen Fakt, den der + Block nicht enthält. Wirkt ein Fakt veraltet, frage behutsam nach, + statt es anzunehmen. EVIDENZ-BLOCK