diff --git a/docs/7-agent-runtime.md b/docs/7-agent-runtime.md index beeb33d..9c41c6b 100644 --- a/docs/7-agent-runtime.md +++ b/docs/7-agent-runtime.md @@ -27,6 +27,7 @@ Phase 4 目标是提供一个**薄胶水层 + 一组 trait 抽象**,让上层 - **`AgentSession` struct** — 智能体的"会话"实例(绑定 session_id + 状态) - **`TaskAgent` trait** — 任务型智能体的"规划/执行"抽象 - **`RuntimeBundle`** — 显式依赖注入容器,集中管理 provider/registry/hook/memory 等依赖 +- **`SessionMemory`** — 会话级记忆,用于 context 间的信息桥接(基于 `MemoryStore` 后端) - **`AgentBuilder`** — 链式构造入口 - **`AgentError`** — 统一错误类型,聚合 LlmError / ToolError / MemoryError @@ -42,6 +43,7 @@ Phase 4 严格遵循以下原则,所有范围决策都基于这些原则推导 | **实体/会话分离** | 同一角色可被多 session 复用 | `Agent` + `AgentSession` 两层模型 | | **记忆弱引用** | 记忆是"被动能力",不内嵌循环 | `memory_store: Option>` 弱引用 | | **业务可注入** | Plan 拆解是业务能力,不在 core 库实现 | 暴露 `PlanParser` trait,上层注入 | +| **会话级记忆** | session 内共享、context 间桥接,不是持久层也不是对话历史 | `SessionMemory` 基于 `MemoryStore`,按 session_id 命名空间隔离 | | **借鉴不照搬** | 4 个参考项目均非 Rust 实现 | 只取架构模式,不抄实现细节 | ### 1.4 与已完成的 Phase 关系 @@ -190,6 +192,7 @@ pub struct RuntimeBundle { pub hook_executor: Arc, pub memory_store: Option>, // 弱引用 pub retriever: Option>, // 弱引用 + pub session_memory_backend: Option>, // SessionMemory 后端(选填) pub config: AgentConfig, } ``` @@ -198,6 +201,7 @@ pub struct RuntimeBundle { - 所有运行时依赖**显式打包**(OpenHarness 风格) - `memory_store` / `retriever` 均为 `Option`——上层应用**不传也能跑**(无记忆模式) - 当 `retriever` 存在时,`RuntimeBundle::new()` 内部自动注册一个名为 `"retrieve"` 的 tool(具体实现:在 `ToolRegistry` 里加一个 `RetrieveTool` 包装),让 LLM 在对话中**主动**调用检索能力 +- `session_memory_backend` 是 `SessionMemory` 的持久后端。传入时 `SessionMemory` 使用该后端(支持跨进程共享);不传时 `AgentSession` 内部自动创建 `InMemoryStore` 作为进程级隔离的后端 - `config` 集中管理所有可调参数(max_turns、max_tool_turns、session_ttl、compact_config) #### 3.2.3 `AgentSession` 与最小 reference impl @@ -205,10 +209,11 @@ pub struct RuntimeBundle { ```rust pub struct AgentSession { pub session_id: String, - pub agent_name: String, + pub agent: Arc, bundle: Arc, turn_index: u32, cost_so_far: CostTracker, + session_memory: SessionMemory, } impl AgentSession { @@ -229,6 +234,8 @@ impl AgentSession { ``` **设计意图**: +- `agent: Arc` 而非 `agent_name: String`——`submit_turn` 从 agent 获取 `system_prompt()` 和 `tool_definitions()`,同时为 v0.2+ 的"热切换 agent"预留:替换 `self.agent` 即可切换角色 +- `session_memory` 是进程内共享的会话级记忆,context 间通过它桥接信息(详见 §3.2.8) - "最小 reference impl" 只演示**最常见**的对话场景 - 业务循环(多轮策略、错误重试、记忆回写时机)由上层应用或具体的 `TaskAgent` 实现决定 - `submit_turn` 不持有 `ConversationMemory`——上层应用可独立 new 一个 `ConversationMemory`,在合适的时机(如 OnTurnEnd hook)调 `add_message` @@ -324,6 +331,7 @@ impl AgentBuilder { pub fn hook_executor(self, h: Arc) -> Self; pub fn memory_store(self, m: Arc) -> Self; // 选填 pub fn retriever(self, r: Arc) -> Self; // 选填 + pub fn session_memory_backend(self, s: Arc) -> Self; // 选填 pub fn config(self, c: AgentConfig) -> Self; pub fn build(self) -> Result; } @@ -332,7 +340,74 @@ impl AgentBuilder { **设计意图**: - `AgentBuilder` 是**唯一**的 `RuntimeBundle` 构造入口 - 必填字段在 `build()` 时校验(`provider` / `tool_registry` / `hook_executor` 不可缺) -- `memory_store` / `retriever` 选填,对应 §3.2.2 的"无记忆模式" +- `memory_store` / `retriever` / `session_memory_backend` 选填 +- `session_memory_backend` 不传时,`AgentSession` 内部用 `InMemoryStore` 兜底(进程级隔离) + +#### 3.2.8 `SessionMemory` — 会话级记忆 + +```rust +pub struct SessionMemory { + store: Arc, + namespace: String, +} + +impl SessionMemory { + /// 创建新的 session 级记忆实例。 + /// store:后端存储(可跨进程共享的 MemoryStore 实现)。 + /// namespace:按 session_id 隔离,防止跨 session 泄漏。 + pub fn new(store: Arc, namespace: &str) -> Self; + + /// 写入一条 key-value 条目。 + pub async fn set(&self, key: &str, value: &str) -> Result<(), AgentError>; + + /// 读取指定 key 的值。 + pub async fn get(&self, key: &str) -> Result, AgentError>; + + /// 返回所有条目的格式化快照,适合注入 system prompt。 + /// 格式: + /// + /// key1: value1 + /// key2: value2 + /// + pub async fn snapshot(&self) -> Result; + + /// 删除指定 key。 + pub async fn remove(&self, key: &str) -> Result<(), AgentError>; + + /// 清空当前 namespace 下所有条目。 + pub async fn clear(&self) -> Result<(), AgentError>; +} +``` + +**设计意图**: +- `SessionMemory` 是**会话级**记忆,不是持久层(`MemoryStore`)也不是对话历史(`ConversationMemory`)——它的定位是 session 内各 context 之间的信息桥接 +- **复用 Phase 3 `MemoryStore` trait**:不引入新的存储后端机制。单进程场景用 `InMemoryStore`(零序列化开销),跨进程场景换 Redis / SQLite 等实现即可 +- **按 `namespace` 隔离**:每个 session 一个独立命名空间(`"_session_{session_id}"`),避免跨 session 意外泄漏 +- **`snapshot()` 格式化为标记文本**:专为注入 system prompt 设计,LLM 可以自然理解 `` 标签中的内容 +- **所有方法为 `async`**:因为后端可能是跨进程的(Redis / DB),虽然 `InMemoryStore` 本身是同步操作 +- **不引入自己的错误类型**:错误通过 `AgentError::Memory` 传播(复用已有变体) + +**三层记忆体系关系**: + +``` +持久层(Phase 3) MemoryStore / KnowledgeStore ── 跨 session 持久,长期知识 +会话层(新增) SessionMemory ── 单 session 内共享,context 桥接 +对话层(Phase 3) ConversationMemory ── 单 context 内消息历史 +``` + +**典型使用模式**(v0.2+ context 切换场景): + +``` +context_a (build agent) + → 在对话中决定某个关键结论值得记下来 + → 调用 session_memory.set("design_decision", "用 PostgreSQL") + → 继续对话 + +创建 context_b (plan agent) + → system_prompt 末尾追加 session_memory.snapshot() + → LLM 看到 "\ndesign_decision: 用 PostgreSQL\n" + → 无需看 context_a 的 50 轮完整历史,但知道关键上下文 +``` ### 3.3 状态机 @@ -440,24 +515,28 @@ pub struct HookContext { | `JsonPlanParser::parse` | `serde_json::from_str` | task.rs | | `AgentError::from` | `LlmError` / `ToolError` / `MemoryError` | error.rs | | `HookContext` 扩展 | `HookEvent::OnTurnStart/End/OnPlanStepComplete` | llm/hooks.rs | +| `SessionMemory::set/get/snapshot` | `MemoryStore::save/load/search` | session_memory.rs | **不调用的下层 API**(明确边界): - ❌ `ConversationMemory`(由上层独立 task 管理) - ❌ `KnowledgeStore`(由上层独立 task 管理) - ❌ `McpClient`(已由 `ToolRegistry` 包装) - ❌ `StreamEvents::submit_stream`(v1 暂不暴露流式 `submit_turn`,v0.2 再说) +- ❌ 多 context 切换管理(v0.2+ 实现,Phase 4 只预留 `SessionMemory` 桥接通道) +- ❌ `"session_memory_set"` 等 session memory tool 自动注册(v0.2+ 可选) ## 4. 实施计划 ### 4.1 文件清单 -#### 新增文件(7 个) +#### 新增文件(8 个) ``` src/agent.rs # 模块根 + pub use 重导出 src/agent/agent.rs # Agent trait src/agent/runtime.rs # RuntimeBundle + AgentConfig src/agent/session.rs # AgentSession(含 submit_turn reference impl) +src/agent/session_memory.rs # SessionMemory(会话级记忆,基于 MemoryStore) src/agent/task.rs # TaskAgent trait + Plan/Step + PlanParser + JsonPlanParser src/agent/builder.rs # AgentBuilder src/agent/error.rs # AgentError @@ -486,15 +565,16 @@ docs/7-agent-runtime.md # ✅ 本文件 | 1 | 修改 `llm/hooks.rs` 追加 3 个事件 + 2 个字段 | `src/llm/hooks.rs` | `cargo build` 通过;现有测试不挂 | | 2 | 新建 `agent/error.rs` 定义 `AgentError` | `src/agent/error.rs` | `cargo build` 通过 | | 3 | 新建 `agent/agent.rs` 定义 `Agent` trait | `src/agent/agent.rs` | `cargo build` 通过 | -| 4 | 新建 `agent/runtime.rs` 定义 `RuntimeBundle` + `AgentConfig` | `src/agent/runtime.rs` | `cargo build` 通过 | -| 5 | 新建 `agent/builder.rs` 定义 `AgentBuilder` | `src/agent/builder.rs` | `cargo build` 通过 | -| 6 | 新建 `agent/session.rs` 定义 `AgentSession` + `submit_turn` | `src/agent/session.rs` | `cargo build` 通过 | -| 7 | 新建 `agent/task.rs` 定义 `TaskAgent` + `Plan` / `Step` / `PlanParser` / `JsonPlanParser` | `src/agent/task.rs` | `cargo build` 通过 | -| 8 | 新建 `src/agent.rs` 模块根 + `pub use` 重导出 | `src/agent.rs` | `cargo build` 通过 | -| 9 | 修改 `lib.rs` 导出 `pub mod agent;` | `src/lib.rs` | `cargo build` 通过 | -| 10 | 编写 2-3 个烟雾测试 | `src/agent/*.rs` 内联 | `cargo test` 通过 | -| 11 | 更新 `roadmap.md` 状态翻转 | `docs/roadmap.md` | 文档 review | -| 12 | 完整 `cargo test` 跑全量回归 | — | 所有已有测试不挂 | +| 4 | 新建 `agent/runtime.rs` 定义 `RuntimeBundle` + `AgentConfig`(含 `session_memory_backend` 字段) | `src/agent/runtime.rs` | `cargo build` 通过 | +| 5 | 新建 `agent/session_memory.rs` 定义 `SessionMemory` | `src/agent/session_memory.rs` | `cargo build` 通过 | +| 6 | 新建 `agent/builder.rs` 定义 `AgentBuilder`(含 `.session_memory_backend()` 方法) | `src/agent/builder.rs` | `cargo build` 通过 | +| 7 | 新建 `agent/session.rs` 定义 `AgentSession` + `submit_turn`(持 `Arc` + `SessionMemory`) | `src/agent/session.rs` | `cargo build` 通过 | +| 8 | 新建 `agent/task.rs` 定义 `TaskAgent` + `Plan` / `Step` / `PlanParser` / `JsonPlanParser` | `src/agent/task.rs` | `cargo build` 通过 | +| 9 | 新建 `src/agent.rs` 模块根 + `pub use` 重导出 | `src/agent.rs` | `cargo build` 通过 | +| 10 | 修改 `lib.rs` 导出 `pub mod agent;` | `src/lib.rs` | `cargo build` 通过 | +| 11 | 编写 3-4 个烟雾测试(含 SessionMemory) | `src/agent/*.rs` 内联 | `cargo test` 通过 | +| 12 | 更新 `roadmap.md` 状态翻转 | `docs/roadmap.md` | 文档 review | +| 13 | 完整 `cargo test` 跑全量回归 | — | 所有已有测试不挂 | ### 4.3 依赖关系 @@ -504,30 +584,33 @@ hooks.rs (1) ──┐ │ │ │ ▼ │ agent/runtime.rs (4) + │ │ │ + │ │ ▼ + │ │ agent/session_memory.rs (5) + │ │ │ + │ ▼ │ + │ agent/builder.rs (6) │ │ │ ▼ - │ agent/builder.rs (5) + │ agent/session.rs (7) │ │ │ ▼ - │ agent/session.rs (6) - │ │ - │ ▼ - └─────────────────► agent/task.rs (7) + └─────────────────► agent/task.rs (8) │ ▼ - src/agent.rs (8) + src/agent.rs (9) │ ▼ - src/lib.rs (9) + src/lib.rs (10) │ ▼ - cargo test (10) + cargo test (11) │ ▼ - roadmap.md (11) + roadmap.md (12) │ ▼ - 回归 (12) + 回归 (13) ``` ### 4.4 预估工作量 @@ -535,11 +618,11 @@ hooks.rs (1) ──┐ | 阶段 | 行数 | 说明 | |------|------|------| | 1(hooks 扩展) | ~15 | 3 个变体 + 2 个字段 + 文档 | -| 2-7(7 个 agent 文件) | ~600 | 含 import + trait + struct + impl + 文档 | -| 8-9(lib.rs + agent.rs 模块根) | ~20 | 主要是 pub use 重导出 | -| 10(烟雾测试) | ~100 | 2-3 个测试 | -| 11(roadmap 同步) | ~5 | 状态翻转一行 | -| **合计** | **~740** | 与 `note-agent-runtime-design.md` §6 预估一致 | +| 2-8(8 个 agent 文件) | ~640 | 含 import + trait + struct + impl + 文档(+ session_memory.rs ~40 行) | +| 9-10(lib.rs + agent.rs 模块根) | ~20 | 主要是 pub use 重导出 | +| 11(烟雾测试) | ~120 | 3-4 个测试(含 SessionMemory) | +| 12(roadmap 同步) | ~5 | 状态翻转一行 | +| **合计** | **~800** | 从 ~740 增加 ~60 行(SessionMemory ~40 + 测试 ~20) | ## 5. 风险评估 @@ -595,30 +678,34 @@ hooks.rs (1) ──┐ - [ ] `cargo build --release` 0 错误 0 警告(clippy) - [ ] `cargo test` 所有 Phase 0-3 已有测试 + Phase 4 新增测试全部通过 - [ ] `cargo doc --no-deps` 所有公开 API 有 `///` 文档注释 -- [ ] 新增代码 700-750 行(含测试 + 文档注释),与 §4.4 预估一致 +- [ ] 新增代码 750-850 行(含测试 + 文档注释),与 §4.4 预估一致 - [ ] `src/lib.rs` 新增一行 `pub mod agent;` - [ ] `src/llm/hooks.rs` 仅追加(不修改现有变体或字段) ### 6.2 接口验收 -- [ ] 7 个新文件全部存在(§4.1) +- [ ] 8 个新文件全部存在(§4.1) - [ ] `Agent` trait 包含 `name` / `system_prompt` / `tool_definitions` 三个方法 -- [ ] `RuntimeBundle` 包含 6 个字段(provider / tool_registry / hook_executor / memory_store? / retriever? / config) +- [ ] `RuntimeBundle` 包含 7 个字段(provider / tool_registry / hook_executor / memory_store? / retriever? / session_memory_backend? / config) +- [ ] `AgentSession` 持 `Arc` 而非 `agent_name: String`,含 `session_memory: SessionMemory` 字段 - [ ] `AgentSession::submit_turn` 实现约 30 行,含 OnTurnStart/End hook 触发 +- [ ] `SessionMemory` 包含 5 个方法(set / get / snapshot / remove / clear),基于 `MemoryStore` 实现 +- [ ] `SessionMemory::snapshot` 返回 `` 标签包裹的格式化文本 - [ ] `TaskAgent` 提供双入口 `run` + `execute_plan` - [ ] `JsonPlanParser` 实现约 20 行,基于 `serde_json` - [ ] `AgentError` 聚合 8 个变体,含 `is_recoverable()` -- [ ] `AgentBuilder` 提供 6 个 setter + `build()` 校验 +- [ ] `AgentBuilder` 提供 7 个 setter(含 `.session_memory_backend()`)+ `build()` 校验 - [ ] `HookEvent` 新增 3 个变体:`OnTurnStart` / `OnTurnEnd` / `OnPlanStepComplete` - [ ] `HookContext` 新增 2 个 `Option` 字段:`turn_index` / `plan_step_index` ### 6.3 测试验收 -至少 2-3 个烟雾测试通过: +至少 3-4 个烟雾测试通过: - [ ] **测试 1**:`Agent` trait 可实现 + `RuntimeBundle` 可构造(builder 链式调用) - [ ] **测试 2**:`AgentSession::submit_turn` 跑通 mock provider(Phase 0 `MockProvider` 模式) -- [ ] **测试 3(可选)**:`JsonPlanParser::parse` 能解析合法 JSON,失败时返回 `AgentError::PlanParse` +- [ ] **测试 3**:`SessionMemory` set / get / snapshot 基本读写(基于 `InMemoryStore`) +- [ ] **测试 4(可选)**:`JsonPlanParser::parse` 能解析合法 JSON,失败时返回 `AgentError::PlanParse` ### 6.4 文档验收 @@ -630,9 +717,11 @@ hooks.rs (1) ──┐ ### 6.5 行为验收(人工 review) - [ ] `AgentSession::submit_turn` 不持有 `ConversationMemory`(grep 验证无 `use crate::memory::ConversationMemory`) +- [ ] `AgentSession` 持 `Arc`,可从 agent 获取 `system_prompt()` / `tool_definitions()` - [ ] `RuntimeBundle::new` 当 `retriever` 为 `Some` 时自动注册 `"retrieve"` tool - [ ] `AgentBuilder::build` 在必填字段缺失时返回 `AgentError::Config`(而非 panic) - [ ] `AgentError::is_recoverable()` 对各变体返回正确分类 +- [ ] `SessionMemory` 在 `session_memory_backend` 未传入时自动使用 `InMemoryStore` 兜底 ### 6.6 风险验收 @@ -644,4 +733,4 @@ hooks.rs (1) ──┐ ## 7. 一句话总结 -> **Phase 4 = 1 个 trait(Agent)+ 1 个容器(RuntimeBundle)+ 1 个会话(AgentSession)+ 1 个任务抽象(TaskAgent)+ 4 个辅助组件(Builder / Error / PlanParser / Hook 扩展),约 740 行代码,把 Phase 0-3 已有能力"装配"成"智能体"的概念。** +> **Phase 4 = 1 个 trait(Agent)+ 1 个容器(RuntimeBundle)+ 1 个会话(AgentSession, 持 Arc\)+ 1 个会话级记忆(SessionMemory)+ 1 个任务抽象(TaskAgent)+ 4 个辅助组件(Builder / Error / PlanParser / Hook 扩展),约 800 行代码,把 Phase 0-3 已有能力"装配"成"智能体"的概念。** diff --git a/docs/note-context-switch-design.md b/docs/note-context-switch-design.md new file mode 100644 index 0000000..4f9d9de --- /dev/null +++ b/docs/note-context-switch-design.md @@ -0,0 +1,222 @@ +# Context 切换方案设计备忘 + +> 创建日期:2026-06-10 +> 状态:备忘(Phase 4 不实现) +> 关联文档: +> - `docs/7-agent-runtime.md` — Phase 4 方案(含 SessionMemory 设计) +> - `docs/note-opencode-agent-switching.md` — OpenCode 切换机制调研 +> - `docs/roadmap.md` — 项目总 Roadmap + +--- + +## 1. 背景 + +### 1.1 问题 + +在调研 OpenCode 的 Agent 切换机制后(详见 `docs/note-opencode-agent-switching.md`),发现其做法是: + +- 切换 agent 时**不动消息历史** +- 在 user message 末尾追加 `synthetic: true` 的 `` 提醒 +- 同时**完全重新计算** system prompt + +这个方法的问题是:**长上下文中频繁切换 agent 容易给 LLM 造成身份困惑**。同一个消息列表里有 `system: build` 的 identity,又出现 `system: plan` 的 identity,LLM 容易"串味"。 + +### 1.2 核心思路 + +以一个 session 里存在**多个独立的 context** 来解决,每个 context 有自己独立的 system prompt + 消息列表: + +``` +OpenCode 模式(一条流): + [system: build, user: A, ass: A', user: <切plan>, system: plan, user: B] + ↑ 身份困惑 + +建议的多 context 模式: + session { + context_a: [system: build, user: A, ass: A'] ← 只有 build 的 identity + context_b: [system: plan, user: B, ass: B'] ← 只有 plan 的 identity + } +``` + +### 1.3 适用范围 + +| 场景 | 适用性 | 说明 | +|------|--------|------| +| Agent 切换(build ↔ plan) | ✅ 核心场景 | 同一 session 内更换角色 | +| 主从 Agent 协作 | ✅ 核心场景 | primary 委派子任务给 subagent,subagent 独立运作 | +| 长 session 上下文压缩 | ✅ 附带收益 | 拆分 context 后,每个 context 独立累积消息,不会互相拖长 | +| 并行 context 执行 | ⚠️ 拓展场景 | context_a 和 context_b 可各自独立推进 | + +--- + +## 2. 三个候选方案 + +### 方案 A:OpenCode 式(system prompt 重算 + synthetic 追加) + +**做法**: +- 单一消息列表 +- 切换时重算 system prompt +- user message 末尾追加 `` 标签 + +**优点**: +- 实现简单 +- 消息历史完整可见 + +**缺点**: +- 长上下文身份困惑 +- context 互相污染(每个 context 都要看全部历史) + +**结论**:❌ 否决。不解决身份困惑问题。 + +### 方案 B:信息池 + 切换不重置(借鉴 OpenCode + 增强) + +**做法**: +- 切换时保留历史 +- 使用 `` 标签 +- 靠 prompt 工程让 LLM 理解身份变更 + +**优点**: +- 历史连贯 +- 改动最小 + +**缺点**: +- 仍然有身份困惑风险 +- 上下文不受控增长 + +**结论**:❌ 否决。治标不治本。 + +### 方案 C:多 context 隔离 + SessionMemory 桥接(推荐) + +**做法**: +- 每个 agent 切换创建一个新的 context(独立消息列表 + 独立 system prompt) +- context 之间通过 `SessionMemory` 桥接关键信息 +- 切换时新 context 的 system prompt 末尾注入 `SessionMemory::snapshot()` + +``` +context_a (build) + → 对话 50 轮 + → 写入 SessionMemory: {"design_decision": "用 PostgreSQL", + "files_changed": "src/db.rs"} + → 销毁(或沉睡) + +创建 context_b (plan) + → system_prompt += snapshot() + → " + design_decision: 用 PostgreSQL + files_changed: src/db.rs + " + → 对话 10 轮(不需要看 context_a 的 50 轮历史) + → 读 SessionMemory: get("design_decision") → "用 PostgreSQL" +``` + +**优点**: +- ✅ 身份稳定:每个 context 只有一套 system prompt +- ✅ 上下文隔离:context_b 不受 context_a 的消息量影响 +- ✅ 信息桥接:关键结论通过 SessionMemory 显式传递 +- ✅ 并行潜力:两个 context 可各自运行 + +**缺点**: +- ❌ 实现复杂度:从"一个消息列表"到"多个消息列表 + 桥接" +- ❌ 信息完整性:LLM 自主决定"什么值得记",可能遗漏细节 +- ❌ 上层理解成本:应用层需要理解 context 概念 + +**结论**:✅ 推荐。架构上最干净,但 Phase 4 不做全部实现。 + +--- + +## 3. SessionMemory 桥接机制(方案 C 的核心) + +### 3.1 设计决策 + +| 决策 | 结论 | 理由 | +|------|------|------| +| 复用 Phase 3 `MemoryStore` | ✅ 是 | 不引入新存储机制 | +| 跨进程支持 | ✅ 是 | 换后端即可(Redis / SQLite),`InMemoryStore` 兜底 | +| namespace 隔离 | ✅ 是 | `_session_{session_id}` 命名空间 | +| 谁写 SessionMemory | LLM 通过 tool 显式写(v0.2+)或上层应用 API 写 | 不支持自动写——避免 "写太多 = 噪音,写太少 = 遗漏" | +| snapshot 格式 | `` XML 风格 | 专为注入 system prompt 设计 | + +### 3.2 谁写 SessionMemory 的三种选项 + +| 选项 | 描述 | 评估 | +|------|------|------| +| **选项 1:AgentSession 自动写** | 每轮对话后自动摘录关键信息 | ❌ 摘录什么?容易变成精简版对话历史,失去"关键信息"的定位 | +| **选项 2:LLM 通过 tool 显式写** | 把 `SessionMemory::set` 暴露为 Tool 供 LLM 调用 | ✅ LLM 自主决定什么值得记;v0.2+ 实现自动注册 | +| **选项 3:上层应用 API 写** | `agent_session.session_memory.set("k", "v")` | ✅ Phase 4 即可用,最透明 | + +**Phase 4 实现选项 3**,v0.2+ 补充选项 2(tool 自动注册)。 + +### 3.3 三层记忆体系 + +``` +持久层(Phase 3) MemoryStore / KnowledgeStore ── 跨 session 持久,长期知识 +会话层(Phase 4) SessionMemory ── 单 session 内共享,context 桥接 +对话层(Phase 3) ConversationMemory ── 单 context 内消息历史 +``` + +--- + +## 4. Phase 4 范围 vs v0.2+ 范围 + +### ✅ Phase 4 做 + +| 组件 | 状态 | 行数 | +|------|------|------| +| `SessionMemory` struct | ✅ 做 | ~40 行 | +| `AgentSession` + `session_memory` 字段 | ✅ 做 | ~3 行 | +| `AgentSession` 持 `Arc` 替代 `agent_name: String` | ✅ 做 | ~3 行 | +| `RuntimeBundle` + `session_memory_backend` 字段 | ✅ 做 | ~1 行 | +| `AgentBuilder` + `.session_memory_backend()` | ✅ 做 | ~3 行 | + +### ❌ 延后到 v0.2+ + +| 组件 | 状态 | 说明 | +|------|------|------| +| Context 切换管理(`switch_context` / `create_context`) | ❌ 延后 | 需要 `ContextManager` 包装 | +| 多 context 生命周期管理 | ❌ 延后 | context 的创建/销毁/切换策略 | +| `"session_memory_set"` tool 自动注册 | ❌ 延后 | 在 `ToolRegistry` 里注册特殊 tool | +| Context 级别的 `ConversationMemory` 自动管理 | ❌ 延后 | 每个 context 独立消息历史 | + +### 延后的理由 + +1. **最小范围原则**:Phase 4 定位是"薄胶水层 + trait 抽象",多 context 管理属于业务编排的范畴 +2. **稳定 API 优先**:先把 `AgentSession` / `RuntimeBundle` / `SessionMemory` 的 API 定稳,v0.2+ 在上面搭建 context 切换 +3. **降低实施风险**:Phase 4 已有 13 个交付任务,加 context 切换会增加 2-3 倍复杂度 + +--- + +## 5. v0.2+ Context 切换的设想接口 + +> 以下为未来实现的草案,非承诺。记录在这里避免 v0.2+ 重新设计时丢失上下文。 + +```rust +pub struct ContextManager { + contexts: HashMap, + active_context: String, + session_memory: SessionMemory, +} + +impl ContextManager { + /// 创建一个新的 context,绑定指定 agent + pub fn create_context(&mut self, id: &str, agent: Arc) -> Result<(), AgentError>; + + /// 切换到已有 context + pub fn switch_context(&mut self, id: &str) -> Result<&mut AgentSession, AgentError>; + + /// 销毁 context + pub fn destroy_context(&mut self, id: &str) -> Result<(), AgentError>; + + /// 从 context_a 桥接关键信息到 context_b 的 system prompt + pub fn bridge(&mut self, from: &str, to: &str) -> Result<(), AgentError>; +} +``` + +切换流程: +1. `context_manager.create_context("plan", plan_agent)` — 新 context 的 system prompt 自动附加 `session_memory.snapshot()` +2. `context_manager.switch_context("plan")` — 返回 context 的 `AgentSession`,应用层调 `submit_turn` +3. context 销毁时,关键信息经由 LLM 或上层应用写入 `SessionMemory` + +--- + +## 6. 一句话总结 + +> **多 context 切换方案 = `SessionMemory`(Phase 4 做信息桥接基础) + `ContextManager`(v0.2+ 做切换管理)。Phase 4 只铺"水管接口",不装"水循环系统"。** diff --git a/docs/roadmap.md b/docs/roadmap.md index d4717f4..87c571d 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -1,13 +1,13 @@ # AG Core Roadmap > 定稿日期:2026-05-11 -> 最后更新:2026-06-09(Phase 4 设计讨论收尾;扩展计划补充 v0.2+ 候选项) +> 最后更新:2026-06-10(Phase 4 方案定稿 + SessionMemory 扩展 + Context 切换备忘) ## 愿景 AG Core 定位为构建 AI 智能体的底层工具箱,通过模块化、可插拔的架构,提供大模型调用、提示词工程、工具系统、记忆检索四大核心能力,支持快速组合出符合业务需求的智能体应用。 -**当前状态**:Phase 0 基础设施已全部完成,Phase 1 提示词工程已全部完成,Phase 2 工具系统已全部完成,Phase 3 记忆系统已全部完成,等待 Phase 4 启动。 +**当前状态**:Phase 0 基础设施已全部完成,Phase 1 提示词工程已全部完成,Phase 2 工具系统已全部完成,Phase 3 记忆系统已全部完成,Phase 4 方案文档已完成(含 SessionMemory 扩展),待编码实施。 --- @@ -19,7 +19,7 @@ AG Core 定位为构建 AI 智能体的底层工具箱,通过模块化、可 | 提示词工程 | ✅ 完整 | `docs/4-prompt-engineering.md` | P1 | | 工具系统 + 权限 | ✅ 完整 | `docs/5-tool-system.md` | P1 | | 记忆检索 | ✅ 完整 | `docs/6-memory-system.md` | P2 | -| Agent 运行时 | ❌ 缺失 | — | P2 | +| Agent 运行时 | ✅ 方案已完成 | `docs/7-agent-runtime.md` | P2 | | 生命周期钩子 | ✅ 完整 | `docs/3-phase0-remaining.md` | P0(LLM Cycle 扩展) | | Provider 注册发现 | ✅ 完整 | `docs/3-phase0-remaining.md` | P0(Provider 接口扩展) | | 流式事件系统 | ✅ 完整 | `docs/3-phase0-remaining.md` | P0(流式接口前置) | @@ -123,20 +123,25 @@ AG Core 定位为构建 AI 智能体的底层工具箱,通过模块化、可 ### Phase 4 — Agent Runtime(智能体运行时) -**目标**:实现多轮对话编排与任务规划。 +**目标**:实现多轮对话编排、任务规划与会话级记忆桥接。 **交付物**: -1. `agent.rs` + `agent/` 模块 -2. `Agent` trait — 智能体接口定义 -3. `ConversationAgent` — 对话型智能体实现 -4. `TaskAgent` — 任务型智能体(规划 → 执行 → 反馈) -5. `specs/agent-runtime.md` — 方案文档 +1. `agent.rs` + `agent/` 模块(8 个文件) +2. `Agent` trait — 智能体角色定义(name / system_prompt / tool_definitions) +3. `AgentSession` — 会话实例(绑定 `Arc` + `RuntimeBundle` + `SessionMemory`) +4. `RuntimeBundle` — 显式依赖注入容器(7 个字段,含 `session_memory_backend`) +5. `SessionMemory` — 会话级记忆(基于 `MemoryStore`,context 间信息桥接) +6. `TaskAgent` — 任务型智能体(规划 → 执行 → 反馈) +7. `AgentBuilder` — 链式构造入口 +8. `AgentError` — 统一错误类型(聚合 LlmError / ToolError / MemoryError) +9. Hook 事件扩展(OnTurnStart / OnTurnEnd / OnPlanStepComplete) +10. `docs/7-agent-runtime.md` — 方案设计文档 **依赖**:Phase 0, 1, 2, 3(整合所有模块) **优先级**:Could Have -**预估规模**:约 600 行代码 +**预估规模**:约 800 行代码 --- @@ -232,6 +237,7 @@ graph BT |-------|---------|------|--------|------| | Agent 身份切换(角色轮换) | `agent` | 借鉴 OpenCode Tab 键切换 build/plan:同一 `AgentSession` 持有可热替换的 `Agent` 引用,切换时不重置消息历史,在末尾追加 `synthetic: true` 的状态变更消息。详见 `docs/note-opencode-agent-switching.md` §4 | P2 | v0.2 待评估 | | System Prompt 多层动态拼接 | `agent/session` | 借鉴 OpenCode `request.ts:58-66`:拆分 `base_prompt + agent_prompt + env_context` 三层,`AgentSession::submit_turn` 每轮重算(不缓存),便于按 agent 类型动态切换 | P2 | v0.2 待评估 | +| **多 Context 切换** | `agent` | **Phase 4 的 SessionMemory 数据结构已预留信息桥接通道,v0.2+ 在其上包装 `ContextManager` 实现完整的多 context 切换:创建/销毁/切换 context、通过 SessionMemory 桥接关键信息。详见 `docs/note-context-switch-design.md`** | P2 | v0.2 待评估 | --- @@ -241,15 +247,15 @@ graph BT 2. **并行可能性**:Phase 0 和 Phase 1 可并行开展(无相互依赖),可加速早期交付 3. **MCP 协议复杂性**:MCP 涉及协议握手、session 管理、长期连接,建议预留充足时间调研协议细节 4. **Scope 蔓延风险**:当前 specs 只有 1 份文档,建议每个模块上线前都产出对应 spec,避免边实现边设计 -5. **Phase 4 抽象化边界**:AG Core 定位为"支持库"而非"Agent 产品",Phase 4 需严格控制范围——只暴露 trait + 最小 reference impl,业务循环(多轮 turn 编排、记忆自动回写、Task 拆解策略)留给上层应用,避免与 OpenHarness / Hermes / OpenHuman 等已有 Agent 产品竞争实现细节。详细设计决策见 Phase 4 设计讨论记录(待 `docs/7-agent-runtime.md` 落盘) +5. **Phase 4 抽象化边界**:AG Core 定位为"支持库"而非"Agent 产品",Phase 4 需严格控制范围——只暴露 trait + 最小 reference impl,业务循环(多轮 turn 编排、对话记忆自动回写、Task 拆解策略)留给上层应用。`SessionMemory`(会话级记忆)已在范围内——它提供信息桥接通道但不实现 context 切换逻辑。多 context 切换管理延后至 v0.2+。详细设计决策见 `docs/7-agent-runtime.md` 6. **参考项目语言差异**:OpenClaw / Hermes / OpenHarness 均为 Python/TypeScript 实现,OpenHuman 虽是 Rust + Tauri 但定位是桌面应用。借鉴时**只取架构模式**,不照搬具体实现(如 Pydantic 工具校验、SQLite Memory Tree、Node+Python 双进程等) --- ## 下一步行动 -1. **Phase 4 设计讨论收尾**:Phase 4 范围已收窄为「`Agent` trait + `RuntimeBundle` 依赖注入容器 + `AgentSession` 实体/会话分离 + `TaskAgent` 双入口 + 记忆弱引用 + Hook 事件扩展 3 个」。决策记录已固化,待写 `docs/7-agent-runtime.md` 方案文档后启动编码实现 -2. **Phase 4 方案文档**:将 Phase 4 设计决策沉淀为方案文档,沿用 `docs/4-prompt-engineering.md` / `5-tool-system.md` / `6-memory-system.md` 的 6 段式结构,文件名 `docs/7-agent-runtime.md` +1. **Phase 4 实施方案**:`docs/7-agent-runtime.md` 方案文档已完成(含 SessionMemory 扩展),下一步启动编码实现,按依赖顺序完成 13 个交付任务后翻转 Roadmap 状态 +2. **Context 切换备忘**:`docs/note-context-switch-design.md` 记录了多 context 切换方案讨论,作为 v0.2+ 扩展项的输入 3. **参考项目调研沉淀**:已完成 OpenClaw / Hermes / OpenHuman / OpenHarness 横向调研,结果沉淀至 `docs/note-agent-harness-references.md`,作为 v0.2+ 扩展项的输入 4. **Phase 3 备用设计就绪**:`docs/note-knowledge-graph-design.md` 记录了 KnowledgeGraph、高级评分、RecallBased 淘汰等设计,v0.2+ 记忆扩展可直接参考