4.1 routing quality + 4.4 persona prompt
- VoiceConfig: add QueryMinScore (default 0.55) + Persona config fields - voice.go: remove queryMinScore const, wire from cfg.Voice.QueryMinScore as reactiveHandler field - llmphraser.go: add Persona to Config, prepend to system prompts in chat and query paths (systemPrompt/querySystemPrompt methods) - main.go: pass personaFromCfg into both phraser config blocks - Makefile: add download-embedder target (Xenova/paraphrase-multilingual- MiniLM-L12-v2, ~90MB ONNX) - AGENTS.md: document embedder model download + libonnxruntime setup - server.go: fix pre-existing wg.Add vs wg.Wait data race using accept mutex. make test green, zero races across all 29 packages.
This commit is contained in:
@@ -213,6 +213,7 @@ func run(args []string) error {
|
||||
NGpuLayers: cfg.Phraser.NGpuLayers,
|
||||
NCtx: cfg.Phraser.NCtx,
|
||||
Timeout: time.Duration(cfg.Phraser.Timeout),
|
||||
Persona: personaFromCfg(cfg),
|
||||
}
|
||||
if pc.BinPath == "" {
|
||||
pc.BinPath = "llama-server"
|
||||
@@ -364,6 +365,7 @@ func run(args []string) error {
|
||||
NGpuLayers: cfg.Phraser.NGpuLayers,
|
||||
NCtx: cfg.Phraser.NCtx,
|
||||
Timeout: time.Duration(cfg.Phraser.Timeout),
|
||||
Persona: personaFromCfg(cfg),
|
||||
}
|
||||
if pc.BinPath == "" {
|
||||
pc.BinPath = "llama-server"
|
||||
@@ -494,3 +496,13 @@ func run(args []string) error {
|
||||
log.Printf("mavend: bye")
|
||||
return nil
|
||||
}
|
||||
|
||||
// personaFromCfg extracts the voice persona from the config, or returns ""
|
||||
// when voice isn't configured. Used to pass a character prompt into the
|
||||
// LLM phraser without requiring voice to be enabled.
|
||||
func personaFromCfg(cfg *config.Config) string {
|
||||
if cfg.Voice != nil {
|
||||
return cfg.Voice.Persona
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
+9
-7
@@ -228,6 +228,7 @@ func wireVoice(cfg *config.Config, coreAPI ipc.CoreAPI, phr phraser.Phraser, mem
|
||||
weatherLocation: weatherLocation,
|
||||
memStore: memStore,
|
||||
dialogueSessions: dialogueSessions,
|
||||
queryMinScore: cfg.Voice.QueryMinScore,
|
||||
}
|
||||
|
||||
// ----- the server (TCP listener) -----
|
||||
@@ -261,6 +262,12 @@ type reactiveHandler struct {
|
||||
|
||||
memStore memory.Store
|
||||
|
||||
// queryMinScore — the note-recall confidence gate. Top cosine below this ⇒
|
||||
// "I don't know" instead of a guess. Tuned for the ONNX embedder; a knob, not
|
||||
// load-bearing math (same posture as the presence thresholds). Set by
|
||||
// wireVoice from VoiceConfig; default 0.55.
|
||||
queryMinScore float64
|
||||
|
||||
// dialogueSessions carries slots across turns for follow-ups (single-user
|
||||
// box → one session slot, keyed voiceDialogueID). nil ⇒ no carry-over.
|
||||
dialogueSessions *dialogue.SessionStore
|
||||
@@ -376,11 +383,6 @@ func (h *reactiveHandler) HandlePushToTalk(ctx context.Context, req voice.PushTo
|
||||
// returns a non-empty string when the action path wants to OVERRIDE the
|
||||
// reply text (e.g. an action error the user should hear SPECIFICALLY, not
|
||||
// a generic "ok"). Errors surface as a short reply text the user hears.
|
||||
// queryMinScore — the note-recall confidence gate. Top cosine below this ⇒
|
||||
// "no note" instead of a guess. Hand-tuned for the ONNX embedder; a knob, not
|
||||
// load-bearing math (same posture as the presence thresholds).
|
||||
const queryMinScore = 0.55
|
||||
|
||||
func (h *reactiveHandler) applyAction(ctx context.Context, dec router.Decision) string {
|
||||
if dec.Clarify {
|
||||
return "" // the Replier phrases clarify
|
||||
@@ -541,13 +543,13 @@ func (h *reactiveHandler) applyAction(ctx context.Context, dec router.Decision)
|
||||
// a gap (spec's "not a guesser-of-truth"). Same instinct as the loop's
|
||||
// since(key)==null → don't fire. Tuned for the ONNX embedder; the Hash
|
||||
// floor scores lexically and may rarely clear it.
|
||||
if len(notes) == 0 || notes[0].Score < queryMinScore {
|
||||
if len(notes) == 0 || notes[0].Score < h.queryMinScore {
|
||||
// Long-term memory recall (notes + facts) before general knowledge:
|
||||
// the notes table can't answer fact questions, but the memory store
|
||||
// indexes both. Only runs when notes-RAG already gave up → additive.
|
||||
if h.memStore != nil {
|
||||
if hits, herr := h.memStore.Search(ctx, vec, 3); herr == nil {
|
||||
if text, ok := bestRecall(hits, queryMinScore); ok {
|
||||
if text, ok := bestRecall(hits, h.queryMinScore); ok {
|
||||
return text
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user