diff --git a/apps/web/app/_components/ChatPanel.tsx b/apps/web/app/_components/ChatPanel.tsx index fb97be0..a834152 100644 --- a/apps/web/app/_components/ChatPanel.tsx +++ b/apps/web/app/_components/ChatPanel.tsx @@ -156,6 +156,15 @@ export function ChatPanel({ // Fetched once; `getPatientProfile()` itself returns null on a 401, so an // anonymous visitor never even attempts an authenticated call more than once. const patientProfileRef = useRef(null); + // Which sessionId has already had the patient-context clause sent. Prepending + // on every single turn (the original design) adds "Bệnh nhân X tuổi, Y kg." + // noise to queries that have nothing to do with dosing/patient-specific + // context — measured live: it pushed a plain ADR-listing question + // (unrelated to age/weight) from a correct answerable answer into + // evidence_insufficient. `rag/understanding.py`'s multi-turn merge already + // carries a stated fact forward, so sending it once per conversation is + // enough — this only resets when `sessionId` itself changes. + const patientContextSentForSessionRef = useRef(null); useEffect(() => { getPatientProfile() @@ -202,16 +211,22 @@ export function ChatPanel({ }, 1000); try { + patientContextSentForSessionRef.current = sessionId; const res = await fetch("/api/chat", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ - // Prepends saved patient context (age/weight/renal/hepatic/allergy) - // to what's actually sent, never to what's shown in the chat - // bubble above — the LLM understanding step already extracts these - // fields from free text every turn (see rag/understanding.py), so - // this reuses that exact path instead of adding a second one. - content: withPatientContext(userText, patientProfileRef.current), + // Prepends saved patient context on the first turn of this session + // only — never to what's shown in the chat bubble above. The LLM + // understanding step extracts these fields from free text and + // carries them across the conversation (rag/understanding.py), so + // repeating them on every later turn only adds noise to queries + // that aren't patient-specific (measured: it broke a plain ADR + // lookup). See patientContextSentForSessionRef above. + content: + patientContextSentForSessionRef.current === sessionId + ? userText + : withPatientContext(userText, patientProfileRef.current), conversationId: sessionId, responseMode, }),