d850f1f5fd
The subjectless-reminder gate has been dead since V-383. It tested `d.Slots.Text == ""`, and that slot is never empty: fillSlots hands it the utterance when the model names nothing narrower. Measured on the box on 05-08-2026 — "напомни" alone routed to IntentReminder with Text:напомни, reached actionReminder, and answered "не получилось разобрать время напоминания." A parse error for a request he never finished asking about. "ну напомни же" did the same. The test is now what the slot CONTAINS. reminderHasSubject discounts the reminder verb by lemma and the filler particles, and asks whether anything is left. A day or an hour counts as a subject, which is why this does not reuse cmd/mavend/reminderbody.go — that one strips the time words too. filler_particles is the lexicon's 16th set. Not a stopword list: every word in it is one that cannot BE a reminder's subject. Measured against the 87-case fixture with and without the change: 65/87 both ways, identical clarify counts, because no case exercised the shape. So amb-007 "напомни" and amb-008 "ну напомни же" were added, both want_clarify. At 89 cases the cascade scores 67/89 (75.3% full, 79.8% intent-only), 3 false clarifies / 1 missed, p50 1.199s — the two new cases clarify, and nothing else moved. The classifier path still guesses both (62/89, 8 missed clarify); the gate is on the LLM arm only. The box needs a rebuild for this to take effect. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SoL7EBdYC5Mhz3DJd49GJy
63 lines
2.7 KiB
Go
63 lines
2.7 KiB
Go
package router
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/kami/maven/internal/lexicon"
|
|
"github.com/kami/maven/internal/morph"
|
|
)
|
|
|
|
// A reminder needs something to say at the hour, and the gate that checks for
|
|
// one was reading a slot that is never empty.
|
|
//
|
|
// gateLLMDecision has asked about a subjectless reminder since V-383, on the
|
|
// test `d.Slots.Text == ""`. Measured on the box on 05-08-2026: "напомни" alone
|
|
// routes to IntentReminder with `Text:напомни`, because fillSlots hands the text
|
|
// slot the utterance when the model names nothing narrower. So the slot was
|
|
// never empty, the gate never fired, and the turn reached actionReminder and
|
|
// answered "не получилось разобрать время напоминания." — a parse error for a
|
|
// request she never finished asking about. "ну напомни же" did the same.
|
|
//
|
|
// The fix is to ask what the text slot CONTAINS rather than whether it is set.
|
|
// Two closed classes answer that and no third mechanism is needed: the reminder
|
|
// verbs are her own vocabulary (lexicon.ReminderVerbs), and the particles and
|
|
// politeness words cannot be the subject of anything (lexicon.IsFillerParticle).
|
|
// A verb is matched by lemma through morph.SameWord, so "напоминай" and
|
|
// "напомнить" need no entry of their own.
|
|
//
|
|
// Deliberately NOT reusing cmd/mavend/reminderbody.go, which strips the same
|
|
// marker: that function also strips the time words, so "напомни завтра" would
|
|
// read as subjectless there. Asking is right when he named no subject, and wrong
|
|
// when he named a day — the reminder for tomorrow is the one whose subject she
|
|
// should ask about, not one she should treat as noise.
|
|
|
|
// reminderHasSubject reports whether a reminder's text names anything to say at
|
|
// the hour. False for "напомни", "напомни мне", "ну напомни же"; true for
|
|
// "напомни позвонить маме" and for "напомни завтра", where the day is a subject
|
|
// she can ask nothing better about.
|
|
func reminderHasSubject(text string) bool {
|
|
for _, f := range strings.Fields(strings.ToLower(text)) {
|
|
w := strings.Trim(f, " ,.;:!?—-«»\"'()")
|
|
if w == "" || lexicon.IsFillerParticle(w) {
|
|
continue
|
|
}
|
|
if isReminderVerb(w) {
|
|
continue
|
|
}
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// isReminderVerb matches one of her reminder imperatives by lemma. Lemma and not
|
|
// prefix: "напоминание" is a noun he can perfectly well ask to be reminded
|
|
// about, and a stem test would eat it.
|
|
func isReminderVerb(word string) bool {
|
|
for _, v := range lexicon.ReminderVerbs() {
|
|
if word == v || morph.SameWord(word, v) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|