Files
Maven/cmd/mavend/replier_llm.go
T
claude afac8fb670 mavend: run the persona checks before she speaks (V-399)
The checks stay in the eval package and the daemon calls three of them:
feminine, address, and a new leaked-reasoning test. No retry — it doubles
the latency on the turn that is already going badly, and on the nudge path
the moment has passed. A failure falls back to the deterministic floor and
is logged with the whole rejected text and counted by check name.

hisgender is deliberately not run: the simulator showed it rejecting
"записала, что ты выпил воды", which is her own correct self-reference.
2026-08-04 04:35:42 +04:00

40 lines
1.2 KiB
Go

package main
import (
"context"
"github.com/kami/maven/internal/phraser"
"github.com/kami/maven/internal/router"
"github.com/kami/maven/internal/voice"
)
// llmReplier is the daemon-side wiring around phraser.Replier: it owns the
// deterministic floor, and nothing else. The phrasing itself, the prompt and the
// output parsing live in internal/phraser so the eval can score them (#396).
type llmReplier struct {
p *phraser.Replier
stub *voice.StubReplier
}
func newLLMReplier(c phraser.Completer, block func() string) *llmReplier {
return &llmReplier{p: phraser.NewReplier(c, block), stub: voice.NewStubReplier()}
}
// Reply never fails: a clarify, a model error and an unusable generation all
// answer from the stub, which is what keeps a turn from breaking on the model.
func (r *llmReplier) Reply(d router.Decision) string {
if d.Clarify {
return r.stub.Reply(d)
}
out, err := r.p.PhraseReply(context.Background(), d)
if err != nil || out == "" {
return r.stub.Reply(d)
}
// The persona checks, on the live path (personaguard.go). A reply that
// leaks reasoning or calls him "вы" is worse than a flat one.
if _, ok := guardSpoken("reply", out); !ok {
return r.stub.Reply(d)
}
return out
}