a note stores his words, not the model's (V-576)

The note body was already the utterance. Two other holes were not.

The LLM router filled Slots.Text for a note from the model's own text
field, and that slot is what the replier reads out. So the confirmation
he heard named things he never said, twice over, differently each time.
The note payload is now the utterance and the model cannot touch it.

A correction with no referent is also not a note. 'нет, не маме, а папе'
names no intent, so parseRepair declines it and it routed as a fresh
note. actionNote now declines it and asks instead of filing it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-06 01:39:38 +04:00
parent 70b32af8a7
commit b705a786ef
5 changed files with 199 additions and 3 deletions
+5 -1
View File
@@ -232,7 +232,11 @@ func (lr *LLMRouter) Route(ctx context.Context, utterance string, now time.Time)
d.Slots.Text = a.Text
case IntentNote:
d.Intent = IntentNote
d.Slots.Text = firstNonEmpty(a.Text, utterance)
// The utterance, never the model's text field (V-576). A note is his
// own words, and the daemon phrases the confirmation from this slot.
// The model is free to write anything here, and on the box it did: one
// fragment came back twice as two different sentences he never said.
d.Slots.Text = utterance
case IntentQuery:
d.Intent = IntentQuery
d.Slots.Text = firstNonEmpty(a.Text, utterance)
+4 -2
View File
@@ -78,13 +78,15 @@ func TestLLMRouterFactMapping(t *testing.T) {
}
}
// A note keeps the utterance, whatever the model wrote in its text field
// (V-576). The note is durable and it is his own words.
func TestLLMRouterNoteMapping(t *testing.T) {
lr := NewLLMRouter(mockLLM{out: `{"intent":"note","text":"кофе закончился"}`})
lr := NewLLMRouter(mockLLM{out: `{"intent":"note","text":"ты поедешь на дачу"}`})
d, ok, err := lr.Route(context.Background(), "запомни что кофе закончился", time.Now())
if err != nil || !ok {
t.Fatalf("ok=%v err=%v", ok, err)
}
if d.Intent != IntentNote || d.Slots.Text != "кофе закончился" {
if d.Intent != IntentNote || d.Slots.Text != "запомни что кофе закончился" {
t.Fatalf("bad decision %+v", d)
}
}