Merge: nothing the model writes reaches a note (#221)
V-576. The diagnosis in the task was wrong and the agent said so. The stored body was never generated: actions_note.go writes dec.Utterance and always has. The invention was one layer up, in the two places the owner hears. llmrouter.go set a note's Slots.Text to firstNonEmpty(a.Text, utterance), where a.Text is the router model's own free-text field. replier.go renders that slot into the confirmation. So a fragment with no content let the router write anything into the payload slot and then had it read back. Two runs, two different inventions, which is what was measured. A note's Slots.Text is now the utterance, unconditionally. A correction fragment also writes nothing at all. correctionFragment uses no new Russian stem patterns: the first token is a one-word refusal from the confirm_no lexicon, the sentence negates and then contrasts, and no token is a verb form per morph.IsVerbForm. The verb test is what spares a real note, so "нет, я не поеду, а останусь" is still stored. The repair path is left to V-573. This fragment carries no intent word, so parseRepair correctly declines it, and widening repair to claim fragments it cannot redo would be the wrong fix.
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user