Files
agcore/docs/note-context-switch-design.md
T
徐涛 be595a6771 docs(agent-runtime): 补充 SessionMemory 设计与多 context 切换备忘
- 在方案文档中新增 `SessionMemory` 作为会话级记忆桥接组件
- 将 AgentSession 的 `agent_name: String` 改为 `agent: Arc<dyn Agent>`
- RuntimeBundle 新增 `session_memory_backend` 可选字段
- 新增 `docs/note-context-switch-design.md` 记录多 context 切换方案
- 更新 Roadmap 状态并补充 Phase 4 范围界定
- Phase 4 仅实现 SessionMemory 数据结构与 API,ContextManager 延后至 v0.2+
2026-06-10 22:22:18 +08:00

223 lines
8.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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-reminder>` 提醒
- 同时**完全重新计算** system prompt
这个方法的问题是:**长上下文中频繁切换 agent 容易给 LLM 造成身份困惑**。同一个消息列表里有 `system: build` 的 identity,又出现 `system: plan` 的 identityLLM 容易"串味"。
### 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 委派子任务给 subagentsubagent 独立运作 |
| 长 session 上下文压缩 | ✅ 附带收益 | 拆分 context 后,每个 context 独立累积消息,不会互相拖长 |
| 并行 context 执行 | ⚠️ 拓展场景 | context_a 和 context_b 可各自独立推进 |
---
## 2. 三个候选方案
### 方案 AOpenCode 式(system prompt 重算 + synthetic 追加)
**做法**
- 单一消息列表
- 切换时重算 system prompt
- user message 末尾追加 `<system-reminder>` 标签
**优点**
- 实现简单
- 消息历史完整可见
**缺点**
- 长上下文身份困惑
- context 互相污染(每个 context 都要看全部历史)
**结论**:❌ 否决。不解决身份困惑问题。
### 方案 B:信息池 + 切换不重置(借鉴 OpenCode + 增强)
**做法**
- 切换时保留历史
- 使用 `<system-reminder>` 标签
- 靠 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()
→ "<session-context>
design_decision: 用 PostgreSQL
files_changed: src/db.rs
</session-context>"
→ 对话 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 格式 | `<session-context>` XML 风格 | 专为注入 system prompt 设计 |
### 3.2 谁写 SessionMemory 的三种选项
| 选项 | 描述 | 评估 |
|------|------|------|
| **选项 1AgentSession 自动写** | 每轮对话后自动摘录关键信息 | ❌ 摘录什么?容易变成精简版对话历史,失去"关键信息"的定位 |
| **选项 2LLM 通过 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<dyn Agent>` 替代 `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<String, AgentSession>,
active_context: String,
session_memory: SessionMemory,
}
impl ContextManager {
/// 创建一个新的 context,绑定指定 agent
pub fn create_context(&mut self, id: &str, agent: Arc<dyn Agent>) -> 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 只铺"水管接口",不装"水循环系统"。**