a captured fact is confirmed in his words, not the model's (V-592)
"я выпил воды" came back as "Проверила, что ты выпел стакан воды". The verb is not a Russian word, the glass was never mentioned, and nothing had been checked. The store was right throughout: DefaultFactParser files this as key=water value="drank", and no row anywhere held "стакан". Every Russian word in that sentence was generated. replyContext hands the model "записала факт: water \"drank\"", so the model had nothing to phrase FROM and reached for the nearest plausible sentence — the example in ReplySystemPrompt, which was literally "Записала, что ты выпил стакан воды." So the fact path stops generating, the way the note payload did in V-576. The confirmation is a fixed deck frame with his own sentence in it, in both repliers, and the prompt example is contentless now. The stub also read the parser's KEY back at him, which is machine vocabulary he never said. The clarify half of this — a fact clarified out of "запиши" answers with "запиши" and nothing else — lands with V-593, which touches the same lines. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -15,6 +15,7 @@ import (
|
||||
_ "embed"
|
||||
"log"
|
||||
"math/rand"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/kami/maven/internal/say"
|
||||
@@ -33,6 +34,7 @@ const (
|
||||
AckFact = "ack_fact"
|
||||
AckFactKey = "ack_fact_key"
|
||||
AckFactValue = "ack_fact_kv"
|
||||
AckFactEcho = "ack_fact_echo"
|
||||
AckNote = "ack_note"
|
||||
AckReminder = "ack_reminder"
|
||||
AckAct = "ack_act"
|
||||
@@ -58,7 +60,7 @@ const (
|
||||
|
||||
// ackKeys — every key the code requires the file to define.
|
||||
var ackKeys = []string{
|
||||
AckFact, AckFactKey, AckFactValue, AckNote, AckReminder, AckAct,
|
||||
AckFact, AckFactKey, AckFactValue, AckFactEcho, AckNote, AckReminder, AckAct,
|
||||
AckTask, AckTaskUrgent, AckTaskDuplicate, AckNudge, AckSnooze, AckGeneric,
|
||||
AckQuietOn, AckQuietOff,
|
||||
FailFact, FailFactUnparsed, FailNote, FailReminder, FailReminderTime,
|
||||
@@ -71,6 +73,7 @@ var ackFloor = map[string]string{
|
||||
AckFact: "записала факт.",
|
||||
AckFactKey: "отметила: {key}",
|
||||
AckFactValue: "отметила: {key} = {value}",
|
||||
AckFactEcho: "записала: {text}",
|
||||
AckNote: "сохранила заметку.",
|
||||
AckReminder: "напомню.",
|
||||
AckAct: "ок, записала действие: {fn}",
|
||||
@@ -109,6 +112,7 @@ func LoadAcks(src rand.Source) (*Acks, error) {
|
||||
// captured, which reads as a successful save of nothing.
|
||||
for _, req := range []struct{ key, ph string }{
|
||||
{AckFactKey, "{key}"}, {AckFactValue, "{key}"}, {AckFactValue, "{value}"},
|
||||
{AckFactEcho, "{text}"},
|
||||
{AckAct, "{fn}"}, {AckTask, "{text}"}, {AckTaskUrgent, "{text}"},
|
||||
} {
|
||||
if err := d.RequirePlaceholder(req.key, req.ph); err != nil {
|
||||
@@ -157,6 +161,26 @@ func DefaultAcks() *Acks {
|
||||
// Ack — one acknowledgement line, the way every caller says it.
|
||||
func Ack(key string, vars map[string]string) string { return DefaultAcks().Say(key, vars) }
|
||||
|
||||
// FactAck — the confirmation for a captured fact, in the words he used (V-592).
|
||||
//
|
||||
// It is a deck line with his sentence dropped into it, and there is no
|
||||
// generation anywhere on this path. Asking a 1.7B to say his sentence back
|
||||
// produced "Проверила, что ты выпел стакан воды" for "я выпил воды": a non-word
|
||||
// for the verb, a glass he never mentioned — lifted straight out of the example
|
||||
// in ReplySystemPrompt — and a claim to have checked something. The fact store
|
||||
// held key=water value="drank" throughout, so nothing was mis-captured and
|
||||
// everything after the capture was invented.
|
||||
//
|
||||
// An empty utterance falls back to the contentless line rather than confirming
|
||||
// a capture of nothing.
|
||||
func FactAck(utterance string) string {
|
||||
utterance = strings.TrimSpace(utterance)
|
||||
if utterance == "" {
|
||||
return Ack(AckFact, nil)
|
||||
}
|
||||
return Ack(AckFactEcho, map[string]string{"text": utterance})
|
||||
}
|
||||
|
||||
// IsAck reports whether text is a line key could have produced. For the daemon
|
||||
// tests, which can no longer compare against one literal.
|
||||
func IsAck(key string, vars map[string]string, text string) bool {
|
||||
|
||||
Reference in New Issue
Block a user