From b4f985f3dc60e7166186e0d01a7633e33b6a1059 Mon Sep 17 00:00:00 2001 From: chengyongru <61816729+chengyongru@users.noreply.github.com> Date: Tue, 7 Apr 2026 15:41:54 +0800 Subject: [PATCH] feat(memory):dream enhancement (#2887) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(dream): enhance memory cleanup with staleness detection - Phase 1: add [FILE-REMOVE] directive and staleness patterns (14-day threshold, completed tasks, superseded info, resolved tracking) - Phase 2: add explicit cleanup rules, file paths section, and deletion guidance to prevent LLM path confusion - Inject current date and file sizes into Phase 1 context for age-aware analysis - Add _dream_debug() helper for observability (dream-debug.log in workspace) - Log Phase 1 analysis output and Phase 2 tool events for debugging Tested with glm-5-turbo: MEMORY.md reduced from 149 to 108-129 lines across two rounds, correctly identifying and removing weather data, detailed incident info, completed research, and stale discussions. * refactor(dream): replace _dream_debug file logger with loguru Remove the custom _dream_debug() helper that wrote to dream-debug.log and use the existing loguru logger instead. Phase 1 analysis is logged at debug level, tool events at info level — consistent with the rest of the codebase and no extra log file to manage. * fix(dream): make stale scan independent of conversation history Reframe Phase 1 from a single comparison task to two independent tasks: history diff AND proactive stale scan. The LLM was skipping stale content that wasn't referenced in conversation history (e.g. old triage snapshots). Now explicitly requires scanning memory files for staleness patterns on every run. * fix(dream): correct old_text param name and truncate debug log - Phase 2 prompt: old_string -> old_text to match EditFileTool interface - Phase 1 debug log: truncate analysis to 500 chars to avoid oversized lines * refactor(dream): streamline prompts by separating concerns Phase 1 owns all staleness judgment logic; Phase 2 is pure execution guidance. Remove duplicated cleanup rules from Phase 2 since Phase 1 already determines what to add/remove. Fix remaining old_string -> old_text. Total prompt size reduced ~45% (870 -> 480 tokens). * fix(dream): add FILE-REMOVE execution guidance to Phase 2 prompt Phase 2 was only processing [FILE] additions and ignoring [FILE-REMOVE] deletions after the cleanup rules were removed. Add explicit mapping: [FILE] → add content, [FILE-REMOVE] → delete content. --- nanobot/agent/memory.py | 12 ++++++++---- nanobot/templates/agent/dream_phase1.md | 26 +++++++++++++++++-------- nanobot/templates/agent/dream_phase2.md | 25 +++++++++++++++++------- 3 files changed, 44 insertions(+), 19 deletions(-) diff --git a/nanobot/agent/memory.py b/nanobot/agent/memory.py index 73010b13f..fc72c573f 100644 --- a/nanobot/agent/memory.py +++ b/nanobot/agent/memory.py @@ -575,13 +575,15 @@ class Dream: ) # Current file contents + current_date = datetime.now().strftime("%Y-%m-%d") current_memory = self.store.read_memory() or "(empty)" current_soul = self.store.read_soul() or "(empty)" current_user = self.store.read_user() or "(empty)" file_context = ( - f"## Current MEMORY.md\n{current_memory}\n\n" - f"## Current SOUL.md\n{current_soul}\n\n" - f"## Current USER.md\n{current_user}" + f"## Current Date\n{current_date}\n\n" + f"## Current MEMORY.md ({len(current_memory)} chars)\n{current_memory}\n\n" + f"## Current SOUL.md ({len(current_soul)} chars)\n{current_soul}\n\n" + f"## Current USER.md ({len(current_user)} chars)\n{current_user}" ) # Phase 1: Analyze @@ -603,7 +605,7 @@ class Dream: tool_choice=None, ) analysis = phase1_response.content or "" - logger.debug("Dream Phase 1 complete ({} chars)", len(analysis)) + logger.debug("Dream Phase 1 analysis ({} chars): {}", len(analysis), analysis[:500]) except Exception: logger.exception("Dream Phase 1 failed") return False @@ -633,6 +635,8 @@ class Dream: "Dream Phase 2 complete: stop_reason={}, tool_events={}", result.stop_reason, len(result.tool_events), ) + for ev in (result.tool_events or []): + logger.info("Dream tool_event: name={}, status={}, detail={}", ev.get("name"), ev.get("status"), ev.get("detail", "")[:200]) except Exception: logger.exception("Dream Phase 2 failed") result = None diff --git a/nanobot/templates/agent/dream_phase1.md b/nanobot/templates/agent/dream_phase1.md index 2476468c8..596958e36 100644 --- a/nanobot/templates/agent/dream_phase1.md +++ b/nanobot/templates/agent/dream_phase1.md @@ -1,13 +1,23 @@ -Compare conversation history against current memory files. -Output one line per finding: -[FILE] atomic fact or change description +Compare conversation history against current memory files. Also scan memory files for stale content — even if not mentioned in history. -Files: USER (identity, preferences, habits), SOUL (bot behavior, tone), MEMORY (knowledge, project context, tool patterns) +Output one line per finding: +[FILE] atomic fact (not already in memory) +[FILE-REMOVE] reason for removal + +Files: USER (identity, preferences), SOUL (bot behavior, tone), MEMORY (knowledge, project context) Rules: -- Only new or conflicting information — skip duplicates and ephemera -- Prefer atomic facts: "has a cat named Luna" not "discussed pet care" +- Atomic facts: "has a cat named Luna" not "discussed pet care" - Corrections: [USER] location is Tokyo, not Osaka -- Also capture confirmed approaches: if the user validated a non-obvious choice, note it +- Capture confirmed approaches the user validated -If nothing needs updating: [SKIP] no new information +Staleness — flag for [FILE-REMOVE]: +- Time-sensitive data older than 14 days: weather, daily status, one-time meetings, passed events +- Completed one-time tasks: triage, one-time reviews, finished research, resolved incidents +- Resolved tracking: merged/closed PRs, fixed issues, completed migrations +- Detailed incident info after 14 days — reduce to one-line summary +- Superseded: approaches replaced by newer solutions, deprecated dependencies + +Do not add: current weather, transient status, temporary errors, conversational filler. + +[SKIP] if nothing needs updating. diff --git a/nanobot/templates/agent/dream_phase2.md b/nanobot/templates/agent/dream_phase2.md index 4547e8fa2..49c8020da 100644 --- a/nanobot/templates/agent/dream_phase2.md +++ b/nanobot/templates/agent/dream_phase2.md @@ -1,13 +1,24 @@ Update memory files based on the analysis below. +- [FILE] entries: add the described content to the appropriate file +- [FILE-REMOVE] entries: delete the corresponding content from memory files -## Quality standards -- Every line must carry standalone value — no filler -- Concise bullet points under clear headers -- Remove outdated or contradicted information +## File paths (relative to workspace root) +- SOUL.md +- USER.md +- memory/MEMORY.md -## Editing -- File contents provided below — edit directly, no read_file needed +Do NOT guess paths. + +## Editing rules +- Edit directly — file contents provided below, no read_file needed +- Use exact text as old_text, include surrounding blank lines for unique match - Batch changes to the same file into one edit_file call +- For deletions: section header + all bullets as old_text, new_text empty - Surgical edits only — never rewrite entire files -- Do NOT overwrite correct entries — only add, update, or remove - If nothing to update, stop without calling tools + +## Quality +- Every line must carry standalone value +- Concise bullets under clear headers +- When reducing (not deleting): keep essential facts, drop verbose details +- If uncertain whether to delete, keep but add "(verify currency)"