mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-11-06 17:27:31 +00:00
fix(memory): 场景召回按块向量余弦排序 + 召回 query 可见性 + 对话回执放宽
① 场景召回是每轮常驻注入,此前按「场景权重→时间倒序」排,与 query 无关: 老图库的 rt/rtchild/inputch 等历史事实常年霸榜,实测 1833 字符里九成是它们, 刚聊的群聊内容一句没有 —— 这就是"忘事"的机制。 块里本来就存着向量,相关性就是一次余弦比较:RecallByScene 带出端点/原句块 向量,Indexer 按 max(cos) 排序并设 0.20 下限,取前 8。 无稠密空间时保持原顺序(不能用字面匹配冒充语义 —— 既有判据要求 「措辞与记忆零字面重合时场景路必须兜住」)。 ② recall query 可见性:cleaner 缺失/生效/回空三种情况都打日志。此前静默回退 原文,"召回 query 其实是工具原始回执"这件事在日志里完全看不到。 实测 qq_get_message: 199 -> 27 bytes(cleaner 生效)。 ③ 对话正文类工具(get_message/get_history/list_chats)时间线渲染放宽到 400 字 (160 字会把群聊上下文切成碎片)。 判据:internal/memory/scene_relevance_test.go
This commit is contained in:
@ -75,11 +75,22 @@ func (a *Agent) toolOutputForQuery(toolName, raw string) string {
|
||||
}
|
||||
cleaner := a.stageHost.ToolDefCleaner(toolName)
|
||||
if cleaner == nil {
|
||||
// ★ 可见性(2026-10-27):cleaner 缺失时必须**说出来**。
|
||||
// 此前这里静默回退原文,于是"召回 query 是 tool 原始 map[...] 字符串"
|
||||
// 这件事在日志里完全看不到 —— 只能靠读代码猜。
|
||||
if len(raw) > 0 {
|
||||
log.Printf("[agent] recall query for %s has NO cleaner, using raw output (%d bytes, head=%q)",
|
||||
toolName, len(raw), headRunes(raw, 40))
|
||||
}
|
||||
return raw
|
||||
}
|
||||
if cleaned := cleaner(raw); cleaned != "" {
|
||||
cleaned := cleaner(raw)
|
||||
if cleaned != "" {
|
||||
log.Printf("[agent] recall query for %s cleaned: %d -> %d bytes (head=%q)",
|
||||
toolName, len(raw), len(cleaned), headRunes(cleaned, 40))
|
||||
return cleaned
|
||||
}
|
||||
log.Printf("[agent] recall query for %s cleaner returned empty, falling back to raw (%d bytes)", toolName, len(raw))
|
||||
return raw
|
||||
}
|
||||
|
||||
@ -588,10 +599,27 @@ const (
|
||||
// 时间线是"给下一轮看的历史",不是当轮工作区 —— 够让模型认出
|
||||
// "我查到了什么",细节仍可用 coldcontext_query / 工具重取。
|
||||
toolResultTimelineChars = 160
|
||||
// toolResultTimelineCharsChat 是**对话正文类**工具(qq_get_message /
|
||||
// qq_get_history)的放宽值(2026-10-27):群聊上下文被切到 160 字后只剩碎片,
|
||||
// 模型看得到"有人说过平板",看不到"平板→promax→机型→随机发货"这条链。
|
||||
toolResultTimelineCharsChat = 400
|
||||
// toolResultTimelineMax 单条事件最多渲染几条工具回执。
|
||||
toolResultTimelineMax = 3
|
||||
)
|
||||
|
||||
// isChatCorpusTool 报告该工具的输出是否属于"对话正文"(需要更长的渲染)。
|
||||
//
|
||||
// 判定用后缀而不是全名:工具名带插件前缀(qq_get_message / telegram_get_message…),
|
||||
// 而语义是"取聊天正文"这一族。
|
||||
func isChatCorpusTool(name string) bool {
|
||||
for _, suffix := range []string{"_get_message", "_get_history", "_list_chats", "_search_messages"} {
|
||||
if strings.HasSuffix(name, suffix) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// oneLine 压成单行(时间线是一行一条事件,换行会破坏结构)。
|
||||
func oneLine(s string) string {
|
||||
s = strings.ReplaceAll(s, "\r", " ")
|
||||
@ -625,7 +653,11 @@ func (a *Agent) renderedToolResultsOf(e *ContextEvent) []string {
|
||||
skipped++
|
||||
continue
|
||||
}
|
||||
out = append(out, fmt.Sprintf("%s: %s", tr.Name, truncateStr(txt, toolResultTimelineChars)))
|
||||
limit := toolResultTimelineChars
|
||||
if isChatCorpusTool(tr.Name) {
|
||||
limit = toolResultTimelineCharsChat
|
||||
}
|
||||
out = append(out, fmt.Sprintf("%s: %s", tr.Name, truncateStr(txt, limit)))
|
||||
}
|
||||
if skipped > 0 {
|
||||
out = append(out, fmt.Sprintf("+%d more tool results", skipped))
|
||||
@ -643,3 +675,12 @@ func (a *Agent) buildMessages(sysPrompt, input string, ctxTokens int) []agentAPI
|
||||
msgs = append(msgs, agentAPI.Message{Role: "user", Content: input})
|
||||
return msgs
|
||||
}
|
||||
|
||||
// headRunes 取前 n 个 rune 用于日志(避免把整段回执打进 journal)。
|
||||
func headRunes(s string, n int) string {
|
||||
r := []rune(strings.TrimSpace(s))
|
||||
if len(r) <= n {
|
||||
return string(r)
|
||||
}
|
||||
return string(r[:n]) + "…"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user