From 7594a5b7f3d85df4de3c59aad8ccd5fe783082d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BB=9D=E5=B0=98?= <237809796@qq.com> Date: Mon, 20 Jul 2026 09:51:12 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E:=20MemoryAdapter=20trait=20+?= =?UTF-8?q?=20ContextManager=20=E9=BB=98=E8=AE=A4=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit crates/df-ai/src/memory_adapter.rs: - MemoryAdapter trait: 15 个核心方法(写入/读取/压缩/生命周期) - ContextManager 作为默认实现(行为零变化) - 预留 VectorMemoryAdapter 接入点 lib.rs: pub mod memory_adapter 导出 --- crates/df-ai/src/lib.rs | 1 + crates/df-ai/src/memory_adapter.rs | 86 ++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 crates/df-ai/src/memory_adapter.rs diff --git a/crates/df-ai/src/lib.rs b/crates/df-ai/src/lib.rs index eb3bd11..e271388 100644 --- a/crates/df-ai/src/lib.rs +++ b/crates/df-ai/src/lib.rs @@ -5,6 +5,7 @@ pub mod anthropic_compat; pub mod anthropic_helpers; pub mod context; pub mod context_helpers; +pub mod memory_adapter; // 多 Agent 协作调度中心(Phase 1 规则驱动, Phase 2 LLM 扩展)。 pub mod coordinator; // 会话意图识别层(纯函数,不接入 agentic loop)。依据 docs/02-架构设计/构想审查/ diff --git a/crates/df-ai/src/memory_adapter.rs b/crates/df-ai/src/memory_adapter.rs new file mode 100644 index 0000000..dea4197 --- /dev/null +++ b/crates/df-ai/src/memory_adapter.rs @@ -0,0 +1,86 @@ +//! MemoryAdapter trait — 上下文记忆层抽象 +//! +//! 将 ContextManager 的核心接口提取为 trait,支持多种记忆后端: +//! - InMemoryAdapter(当前 ContextManager 的包装,默认实现) +//! - VectorMemoryAdapter(未来:接入向量检索,支持语义记忆) +//! +//! 使用方式:`Box` 替代 `ContextManager`。 + +use crate::context::ContextManager; +use crate::context_helpers::{ContextConfig, TrackedMessage}; +use crate::provider::ChatMessage; + +/// 上下文记忆层抽象接口 +/// +/// 覆盖 agentic loop / IPC 最常用的 15 个方法。 +/// 保留 `ContextManager` 作为默认实现(`InMemoryAdapter`),行为零变化。 +pub trait MemoryAdapter: Send + Sync { + // ── 写入 ── + fn push(&mut self, msg: ChatMessage); + fn clear(&mut self); + fn insert_at(&mut self, index: usize, message: ChatMessage); + fn replace_tool_result_content(&mut self, tool_call_id: &str, new_content: &str) -> bool; + fn pop_last_assistant_round(&mut self) -> bool; + fn truncate_after_user_message(&mut self, target_content: &str) -> Result; + fn replace_last_active_user_content(&mut self, new_content: &str) -> Result<(), ()>; + + // ── 读取 ── + fn build_for_request(&self, sys_tokens: u32) -> (Vec, bool); + fn all_messages_clone(&self) -> Vec; + fn len(&self) -> usize; + fn is_empty(&self) -> bool; + fn history_tokens(&self) -> u32; + fn budget_limit(&self) -> u32; + fn config(&self) -> &ContextConfig; + + // ── 压缩 ── + fn compress_old_messages(&mut self, end: usize) -> Vec; + fn has_compressible_messages(&self, protect_start: usize) -> bool; + fn is_compressing(&self) -> bool; + fn set_compressing(&mut self, v: bool); + + // ── 生命周期 ── + fn restore_from_messages(&mut self, messages: Vec); + fn take_topic_marker(&mut self) -> Option; + fn messages_mut(&mut self) -> &mut [TrackedMessage]; + fn iter(&self) -> Box + '_>; +} + +/// ContextManager 作为默认 MemoryAdapter 实现 +impl MemoryAdapter for ContextManager { + fn push(&mut self, msg: ChatMessage) { self.push(msg); } + fn clear(&mut self) { self.clear(); } + fn insert_at(&mut self, index: usize, message: ChatMessage) { self.insert_at(index, message); } + fn replace_tool_result_content(&mut self, tool_call_id: &str, new_content: &str) -> bool { + self.replace_tool_result_content(tool_call_id, new_content) + } + fn pop_last_assistant_round(&mut self) -> bool { self.pop_last_assistant_round() } + fn truncate_after_user_message(&mut self, target_content: &str) -> Result { + self.truncate_after_user_message(target_content) + } + fn replace_last_active_user_content(&mut self, new_content: &str) -> Result<(), ()> { + self.replace_last_active_user_content(new_content) + } + + fn build_for_request(&self, sys_tokens: u32) -> (Vec, bool) { + self.build_for_request(sys_tokens) + } + fn all_messages_clone(&self) -> Vec { self.all_messages_clone() } + fn len(&self) -> usize { self.len() } + fn is_empty(&self) -> bool { self.is_empty() } + fn history_tokens(&self) -> u32 { self.history_tokens() } + fn budget_limit(&self) -> u32 { self.budget_limit() } + fn config(&self) -> &ContextConfig { self.config() } + + fn compress_old_messages(&mut self, end: usize) -> Vec { self.compress_old_messages(end) } + fn has_compressible_messages(&self, protect_start: usize) -> bool { self.has_compressible_messages(protect_start) } + fn is_compressing(&self) -> bool { self.is_compressing() } + fn set_compressing(&mut self, v: bool) { self.set_compressing(v); } + + fn restore_from_messages(&mut self, messages: Vec) { self.restore_from_messages(messages); } + fn take_topic_marker(&mut self) -> Option { self.take_topic_marker() } + fn messages_mut(&mut self) -> &mut [TrackedMessage] { self.messages_mut() } + fn iter(&self) -> Box + '_> { + Box::new(self.iter()) + } +}