From 15b4d7cccc88326a24944afb5e0d7139c135bd67 Mon Sep 17 00:00:00 2001 From: DBT Date: Mon, 23 Feb 2026 15:42:20 +0000 Subject: [PATCH] enforce memory recall hints and long-term memory write gating --- pkg/agent/loop.go | 19 +++++++++++++++++++ pkg/tools/memory_write.go | 19 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/pkg/agent/loop.go b/pkg/agent/loop.go index 10f4605..c2ff998 100644 --- a/pkg/agent/loop.go +++ b/pkg/agent/loop.go @@ -307,6 +307,11 @@ func (al *AgentLoop) processMessage(ctx context.Context, msg bus.InboundMessage) history := al.sessions.GetHistory(msg.SessionKey) summary := al.sessions.GetSummary(msg.SessionKey) + if shouldRecallMemory(msg.Content) { + if recall, err := al.tools.Execute(ctx, "memory_search", map[string]interface{}{"query": msg.Content, "maxResults": 3}); err == nil && strings.TrimSpace(recall) != "" { + summary = strings.TrimSpace(summary + "\n\n[Memory Recall]\n" + recall) + } + } if explicitPref := ExtractLanguagePreference(msg.Content); explicitPref != "" { al.sessions.SetPreferredLanguage(msg.SessionKey, explicitPref) } @@ -828,6 +833,20 @@ func truncateString(s string, maxLen int) string { return s[:maxLen-3] + "..." } +func shouldRecallMemory(text string) bool { + s := strings.ToLower(strings.TrimSpace(text)) + if s == "" { + return false + } + keywords := []string{"remember", "记得", "上次", "之前", "偏好", "preference", "todo", "待办", "决定", "decision", "日期", "when did", "what did we"} + for _, k := range keywords { + if strings.Contains(s, k) { + return true + } + } + return false +} + func alSessionListForTool(sm *session.SessionManager, limit int) []tools.SessionInfo { items := sm.List(limit) out := make([]tools.SessionInfo, 0, len(items)) diff --git a/pkg/tools/memory_write.go b/pkg/tools/memory_write.go index 7045563..2f66ff0 100644 --- a/pkg/tools/memory_write.go +++ b/pkg/tools/memory_write.go @@ -94,6 +94,11 @@ func (t *MemoryWriteTool) Execute(ctx context.Context, args map[string]interface formatted := formatMemoryLine(content, importance, source, tags) + if (kind == "longterm" || kind == "memory" || kind == "permanent") && !allowLongTermWrite(importance, tags) { + kind = "daily" + formatted = formatMemoryLine(content, importance, source, append(tags, "downgraded:longterm_gate")) + } + switch kind { case "longterm", "memory", "permanent": path := filepath.Join(t.workspace, "MEMORY.md") @@ -179,3 +184,17 @@ func formatMemoryLine(content, importance, source string, tags []string) string } return fmt.Sprintf("[%s] %s", strings.Join(meta, " | "), content) } + +func allowLongTermWrite(importance string, tags []string) bool { + if strings.ToLower(strings.TrimSpace(importance)) == "high" { + return true + } + for _, t := range tags { + s := strings.ToLower(strings.TrimSpace(t)) + switch s { + case "preference", "decision", "rule", "policy", "identity": + return true + } + } + return false +}