From ea2ff7c53bc0530db7fb2859dd4cbb8d0afd867c Mon Sep 17 00:00:00 2001 From: Edvard Date: Thu, 19 Feb 2026 15:24:06 -0500 Subject: [PATCH] fix(memory): add minimum-length filter for auto-save messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every user message was auto-saved to memory regardless of length, flooding the store with trivial entries like "ok", "thanks", "hi". These noise entries competed with real memories during recall, degrading relevance — especially with keyword-only search. Skip auto-saving messages shorter than 20 characters. Applied to both the channel path (channels/mod.rs) and CLI agent path (agent/loop_.rs). Co-Authored-By: Claude Opus 4.6 --- src/agent/loop_.rs | 14 ++++++++++---- src/channels/mod.rs | 6 +++++- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/agent/loop_.rs b/src/agent/loop_.rs index a59b055..e191aff 100644 --- a/src/agent/loop_.rs +++ b/src/agent/loop_.rs @@ -26,6 +26,10 @@ const STREAM_CHUNK_MIN_CHARS: usize = 80; /// Used as a safe fallback when `max_tool_iterations` is unset or configured as zero. const DEFAULT_MAX_TOOL_ITERATIONS: usize = 10; +/// Minimum user-message length (in chars) for auto-save to memory. +/// Matches the channel-side constant in `channels/mod.rs`. +const AUTOSAVE_MIN_MESSAGE_CHARS: usize = 20; + static SENSITIVE_KEY_PATTERNS: LazyLock = LazyLock::new(|| { RegexSet::new([ r"(?i)token", @@ -1475,8 +1479,8 @@ pub async fn run( let mut final_output = String::new(); if let Some(msg) = message { - // Auto-save user message to memory - if config.memory.auto_save { + // Auto-save user message to memory (skip short/trivial messages) + if config.memory.auto_save && msg.chars().count() >= AUTOSAVE_MIN_MESSAGE_CHARS { let user_key = autosave_memory_key("user_msg"); let _ = mem .store(&user_key, &msg, MemoryCategory::Conversation, None) @@ -1597,8 +1601,10 @@ pub async fn run( _ => {} } - // Auto-save conversation turns - if config.memory.auto_save { + // Auto-save conversation turns (skip short/trivial messages) + if config.memory.auto_save + && user_input.chars().count() >= AUTOSAVE_MIN_MESSAGE_CHARS + { let user_key = autosave_memory_key("user_msg"); let _ = mem .store(&user_key, &user_input, MemoryCategory::Conversation, None) diff --git a/src/channels/mod.rs b/src/channels/mod.rs index c01e47c..537fd0c 100644 --- a/src/channels/mod.rs +++ b/src/channels/mod.rs @@ -81,6 +81,10 @@ use tokio_util::sync::CancellationToken; type ConversationHistoryMap = Arc>>>; /// Maximum history messages to keep per sender. const MAX_CHANNEL_HISTORY: usize = 50; +/// Minimum user-message length (in chars) for auto-save to memory. +/// Messages shorter than this (e.g. "ok", "thanks") are not stored, +/// reducing noise in memory recall. +const AUTOSAVE_MIN_MESSAGE_CHARS: usize = 20; /// Maximum characters per injected workspace file (matches `OpenClaw` default). const BOOTSTRAP_MAX_CHARS: usize = 20_000; @@ -808,7 +812,7 @@ async fn process_channel_message( let memory_context = build_memory_context(ctx.memory.as_ref(), &msg.content, ctx.min_relevance_score).await; - if ctx.auto_save_memory { + if ctx.auto_save_memory && msg.content.chars().count() >= AUTOSAVE_MIN_MESSAGE_CHARS { let autosave_key = conversation_memory_key(&msg); let _ = ctx .memory