# Context 管理与执行计划 > 本文档描述 Agent 的 Context 管理、执行计划和探索机制。 --- ## 设计目标 1. **自主长程执行**:Agent 能独立执行复杂任务,无需人工频繁干预 2. **有效的 Context 管理**:长任务中保持关键信息,压缩次要细节 3. **支持探索和回溯**:能尝试多种方案,失败时能有效回溯 4. **简单的工具接口**:LLM 只需理解少量简单工具,复杂逻辑由系统处理 --- ## 参考方案:OpenCode 的 Context 管理 ### 核心架构 ``` ┌─────────────────┐ │ plan.md │ ← 文本格式的计划(TODO 列表) └─────────────────┘ ↓ ┌─────────────────┐ │ 线性 Message │ ← 对话历史 │ List │ └─────────────────┘ ↓ ┌─────────────────┐ │ Prune + Full │ ← 两阶段压缩 │ Compaction │ └─────────────────┘ ↓ ┌─────────────────┐ │ Sub-Agent │ ← 隔离大任务 └─────────────────┘ ``` ### 1. Message 管理 **数据结构**: - User Message: 用户输入,包含 TextPart, FilePart, CompactionPart, SubtaskPart 等 - Assistant Message: LLM 输出,包含 TextPart, ToolPart, ReasoningPart 等 - 每个 Message 包含多个 Part,支持流式处理 **存储**: ``` Storage Key: ["message", sessionID, messageID] -> MessageV2.Info ["part", messageID, partID] -> MessageV2.Part ``` ### 2. Context 压缩机制 **两阶段压缩**: **阶段 1: Prune(清理旧工具输出)** ``` 参数: - PRUNE_MINIMUM = 20,000 tokens(最少删除量) - PRUNE_PROTECT = 40,000 tokens(保护阈值) - PRUNE_PROTECTED_TOOLS = ["skill"](不删除的工具) 流程: 1. 从后向前遍历 messages 2. 跳过最后 2 轮 turns(保护最近交互) 3. 跳过已有 summary 标记的 assistant 消息 4. 收集已完成工具调用的输出 5. 当累计 > PRUNE_PROTECT 时,标记为已 compacted 6. 当删除量 > PRUNE_MINIMUM 时,执行删除 ``` **阶段 2: Full Compaction(上下文总结)** ``` 流程: 1. 创建新的 assistant 消息(summary=true) 2. 调用 "compaction" 专用 agent 3. 提示词: "Provide a detailed prompt for continuing our conversation..." 4. 返回 "continue" 时自动创建新的 user 消息继续 ``` ### 3. Plan/Todo 机制 **数据结构**: ```typescript Todo.Info = { id: string content: string // 任务描述 status: string // pending | in_progress | completed | cancelled priority: string // high | medium | low } ``` **存储**:文件系统(.opencode/plans/xxx.md)或 Storage ### 4. Sub-Agent 机制 **Agent Mode**: - `primary`: 主代理,执行工具 - `subagent`: 子代理,独立 context,结果汇总回主会话 **内置 Sub-Agents**: - `general`: 通用代理,可并行执行多个任务 - `explore`: 代码探索专用,仅允许查询工具 - `compaction`: 上下文总结专用 **Subtask 执行**: 1. 创建 SubtaskPart 2. 子代理独立处理(独立 message list) 3. 结果通过 "The following tool was executed by the user" 汇总 ### 5. 优缺点分析 **优点**: - 简单成熟,经过大量验证 - Plan 和执行分离,用户可直接编辑 plan.md - Sub-agent 有效隔离大任务的 context **局限**: - Plan 是纯文本,与执行记录无结构化关联 - 压缩是"事后"的,等满了再压缩 - 回溯能力有限,无法精确回到某个状态 - 不支持并行探索-合并的模式 --- ## 我们的方案 ### 核心思路 ``` 基于 OpenCode 方案,增强三个能力: 1. 结构化 Plan(goal 工具) 2. 并行探索-合并(explore 工具) 3. 精确回溯(abandon + context 压缩) ``` ### 架构 ``` ┌─────────────────────────────────────────────┐ │ GoalTree (嵌套 JSON) │ │ 层级目标,LLM 通过 goal 工具维护 │ │ 注入 LLM 时过滤废弃目标,重新生成连续显示序号 │ └─────────────────────────────────────────────┘ │ ┌────────────┴────────────┐ ↓ ↓ ┌─────────────────┐ ┌─────────────────┐ │ Messages │ │ 并行探索 │ │ (扁平列表, │ │ (explore 工具) │ │ goal_id 关联) │ │ 多个独立分支 │ └─────────────────┘ └─────────────────┘ │ │ ↓ ↓ ┌─────────────────┐ ┌─────────────────┐ │ 完成/回溯 │ │ 合并评估 │ │ done/abandon │ │ 返回主会话 │ │ 触发 context │ └─────────────────┘ │ 压缩 │ └─────────────────┘ │ ↓ ┌─────────────────────────────────────────────┐ │ DAG 可视化(派生视图) │ │ 从 GoalTree + Messages 生成 │ │ 节点 = 结果/里程碑,边 = 动作/执行过程 │ │ 边可展开/折叠,对应目标的层级展开 │ └─────────────────────────────────────────────┘ ``` ### 数据结构 #### 两层数据 后端存储两类数据,可视化的 DAG 是派生视图: 1. **GoalTree**(嵌套 JSON):层级目标,注入 LLM 2. **Messages**(扁平列表):执行记录,通过 `goal_id` 关联 Goal 不存在独立的"边"数据结构,边在可视化时从 Messages 聚合生成。 #### Goal ```python @dataclass class GoalStats: message_count: int = 0 # 消息数量 total_tokens: int = 0 # Token 总数 total_cost: float = 0.0 # 总成本 preview: Optional[str] = None # 工具调用摘要,如 "read_file → edit_file → bash" GoalStatus = Literal["pending", "in_progress", "completed", "abandoned"] GoalType = Literal["normal", "explore_start", "explore_merge"] @dataclass class Goal: id: str # 内部唯一 ID,纯自增("1", "2", "3"...) parent_id: Optional[str] = None # 父 Goal ID(层级关系) branch_id: Optional[str] = None # 所属分支 ID(分支关系,null=主线) type: GoalType = "normal" # Goal 类型 description: str # 目标描述(做什么) reason: str # 创建理由(为什么做) status: GoalStatus # pending | in_progress | completed | abandoned summary: Optional[str] = None # 完成/放弃时的总结 # explore_start 特有 branch_ids: Optional[List[str]] = None # 关联的分支 ID 列表 # explore_merge 特有 explore_start_id: Optional[str] = None # 关联的 explore_start Goal merge_summary: Optional[str] = None # 各分支汇总结果 selected_branch: Optional[str] = None # 选中的分支(可选) # 统计(后端维护,用于可视化边的数据) self_stats: GoalStats # 自身统计(仅直接关联的 messages) cumulative_stats: GoalStats # 累计统计(自身 + 所有后代) ``` **实现**:`agent/goal/models.py:Goal` **ID 设计**: - **内部 ID**:纯自增数字("1", "2", "3", "4"...),不管层级、分支、废弃 - **层级关系**:通过 `parent_id` 字段维护 - **分支关系**:通过 `branch_id` 字段维护(null 表示主线,"A"/"B" 表示分支) - **显示序号**:`to_prompt()` 时动态生成连续有意义的编号("1", "2", "2.1", "2.2"...) **统计更新逻辑**: - 每次添加 Message 时,更新对应 Goal 的 `self_stats`,并沿祖先链向上更新所有祖先的 `cumulative_stats` - 可视化中,折叠边使用 target Goal 的 `cumulative_stats`,展开边使用 `self_stats` ```python @dataclass class GoalTree: mission: str # 总任务描述 current_id: Optional[str] = None # 当前焦点(内部 ID) goals: List[Goal] # 顶层目标(扁平列表,通过 parent_id 构建层级) ``` **实现**:`agent/goal/models.py:GoalTree` #### Message Message 对应 LLM API 的消息,加上元数据。每条 Message 通过 `goal_id` 和 `branch_id` 关联所属 Goal。 ```python @dataclass class Message: message_id: str trace_id: str branch_id: Optional[str] = None # 所属分支(null=主线, "A"/"B"=分支) role: Literal["assistant", "tool"] # 和 LLM API 一致 sequence: int # 全局顺序 goal_id: str # 关联的 Goal 内部 ID tool_call_id: Optional[str] # tool 消息关联对应的 tool_call content: Any # 消息内容(和 LLM API 格式一致) description: str # 消息描述(系统自动生成) # 元数据 tokens: Optional[int] = None cost: Optional[float] = None created_at: datetime ``` **description 字段**(系统自动生成): - `assistant` 消息:优先取 content 中的 text,若无 text 则生成 "tool call: XX, XX" - `tool` 消息:使用 tool name **实现**:`agent/execution/models.py:Message` **Message 类型说明**: - `role="assistant"`:模型的一次返回,可能同时包含文本和多个 tool_calls - `role="tool"`:一个工具的执行结果,通过 `tool_call_id` 关联对应的 tool_call ### 工具设计 #### goal 工具:计划管理 ```python @tool def goal( add: Optional[str] = None, # 添加目标(逗号分隔多个) done: Optional[str] = None, # 完成当前目标,值为 summary abandon: Optional[str] = None, # 放弃当前目标,值为原因 focus: Optional[str] = None, # 切换焦点到指定显示序号 ) -> str: """管理执行计划。""" ``` **实现**:`agent/goal/tool.py:goal_tool` **层级支持**:`add` 添加到当前 focus 的 goal 下作为子目标。 ```python # 没有 focus 时,添加到顶层 goal(add="分析代码, 实现功能, 测试") # 结果: # [ ] 1. 分析代码 # [ ] 2. 实现功能 # [ ] 3. 测试 # focus 到某个 goal 后,add 添加为其子目标 goal(focus="2") goal(add="设计接口, 实现代码") # 结果: # [ ] 1. 分析代码 # [→] 2. 实现功能 # [ ] 2.1 设计接口 # [ ] 2.2 实现代码 # [ ] 3. 测试 ``` **状态流转**: ``` pending ──focus──→ in_progress ──done──→ completed │ ↓ │ (压缩 context) │ (级联:若所有兄弟都 completed,父 goal 自动 completed) │ abandon ↓ abandoned ↓ (压缩 context) ``` #### explore 工具:并行探索 基于 sub-agent 机制实现。 ```python @tool def explore( branches: List[str], # 探索方向列表 background: Optional[str] = None, # 背景概括(可选) ) -> str: """ 并行探索多个方向,汇总结果。 - background 有值:用它初始化各分支的 context - background 为空:继承主 message list """ ``` ### Context 管理 #### 1. Plan 注入 每次 LLM 调用时,在 system prompt 末尾注入当前计划状态。注入时过滤掉 abandoned 目标,使用连续的显示序号: ```markdown ## Current Plan **Mission**: 实现用户认证功能 **Current**: 2.2 实现登录接口 **Progress**: [✓] 1. 分析代码 → 用户模型在 models/user.py,使用 bcrypt 加密 [→] 2. 实现功能 [✓] 2.1 设计接口 [→] 2.2 实现登录接口 ← current [ ] 2.3 实现注册接口 [ ] 3. 测试 ``` **实现**:`agent/goal/models.py:GoalTree.to_prompt` #### 2. 完成时压缩 当调用 `goal(done="...")` 时: 1. 找到该 goal 关联的所有 messages(通过 goal_id) 2. 将详细 messages 替换为一条 summary message 3. 更新 goal 状态为 completed #### 3. 回溯(Abandon) 两种模式: **模式 1:需要修改的计划还没有执行** 直接修改计划并继续执行。Goal 状态为 pending 时,可以直接修改 description 或删除。 **模式 2:需要修改的计划已经执行** 1. 将原 Goal 标记为 `abandoned`(保留在 GoalTree 数据中,但 `to_prompt()` 不展示) 2. 将废弃分支关联的 messages 做 summary 3. 将 summary 累积到新分支的第一条消息中(供 LLM 参考历史失败原因) 4. 创建新的 Goal 继续执行 **Before 回溯**: ``` GoalTree 数据: [✓] 1. 分析代码 (内部ID: 1) [→] 2. 实现方案 A (内部ID: 2) [ ] 3. 测试 (内部ID: 3) Messages: [分析代码的 20 条 message...] [实现方案 A 的 30 条 message...] [测试失败的 message...] ``` **After 回溯**: ``` GoalTree 数据(含废弃): [✓] 1. 分析代码 (内部ID: 1) [✗] 2. 实现方案 A (内部ID: 2, abandoned) [→] 3. 实现方案 B (内部ID: 4, 新建) [ ] 4. 测试 (内部ID: 3) to_prompt() 输出(给 LLM,连续编号): [✓] 1. 分析代码 [→] 2. 实现方案 B ← current [ ] 3. 测试 Messages: [分析代码的 20 条 message...] [Summary: "尝试方案 A,因依赖问题失败"] ← 原 messages 压缩为 1 条 [方案 B 第一条消息,包含废弃分支的 summary] ← 供 LLM 参考 [方案 B 的后续 message...] ``` **实现**:`agent/goal/compaction.py` ### 可视化 #### DAG 模型 可视化展示为 DAG(有向无环图),不是树。 **核心概念**: - **节点** = Goal 完成后的结果/里程碑 - **边** = 从一个结果到下一个结果的执行过程(动作/策略) - 每个节点对应一条入边,入边的数据从该 Goal 关联的 Messages 聚合 **展开/折叠**:对边操作,对应目标的层级展开。 ``` 折叠视图(只看顶层 Goals): [START] ──→ [1:分析完成] ──→ [2:实现完成] ──→ [3:测试完成] 逻辑边 展开 [1]→[2] 的边(显示 Goal 2 的子目标): [START] ──→ [1:分析完成] ──→ [2.1:设计完成] ──→ [2.2:代码完成] ──→ [3:测试完成] 执行边 执行边 ``` 展开时,父节点 [2] 被子节点 [2.1], [2.2] **替代**。 折叠时,子节点合并回父节点 [2]。 嵌套展开:如果 2.1 也有子目标,可以继续展开 [1]→[2.1] 的边。 **废弃分支**:在可视化中以灰色样式展示废弃分支。 ``` [1:分析完成] ──→ [2:方案A(废弃)] ──→ ... ← 灰色 ──→ [4:方案B] ──→ [3:测试] ← 正常 ``` #### API 后端提供 GoalTree 数据,前端负责生成 DAG 视图。 **REST 端点**: ``` GET /api/traces/{trace_id} # 获取 Trace + GoalTree GET /api/traces/{trace_id}/messages?goal_id=2.1 # 获取 Messages(边详情) ``` **响应**(GoalTree 部分): ```json { "goal_tree": { "mission": "实现用户认证功能", "current_id": "4", "goals": [ { "id": "1", "parent_id": null, "branch_id": null, "type": "normal", "description": "分析代码", "reason": "了解现有结构", "status": "completed", "summary": "用户模型在 models/user.py", "self_stats": {"message_count": 5, "total_tokens": 2300, "total_cost": 0.03, "preview": "glob → read × 2"}, "cumulative_stats": {"message_count": 5, "total_tokens": 2300, "total_cost": 0.03, "preview": "glob → read × 2"} }, { "id": "2", "parent_id": null, "branch_id": null, "type": "normal", "description": "实现功能", "reason": "核心任务", "status": "in_progress", "self_stats": {"message_count": 0, ...}, "cumulative_stats": {"message_count": 11, "total_tokens": 5700, ...} }, { "id": "3", "parent_id": "2", "branch_id": null, "type": "normal", "description": "设计接口", "status": "completed", "self_stats": {...} }, { "id": "4", "parent_id": "2", "branch_id": null, "type": "normal", "description": "实现代码", "status": "in_progress", "self_stats": {...} }, { "id": "5", "parent_id": null, "branch_id": null, "type": "normal", "description": "测试", "status": "pending", ... } ] } } ``` **DAG 生成逻辑**(前端实现): 1. 根据用户展开状态,确定可见 Goal 序列 2. 相邻 Goal 之间形成边 3. 边的统计数据从 target Goal 的 stats 获取(折叠用 `cumulative_stats`,展开用 `self_stats`) 4. 边的详细内容通过 Messages API 查询 **实现**:见 [frontend/API.md](../frontend/API.md) #### WebSocket 实时推送 ``` ws://localhost:8000/api/traces/{trace_id}/watch?since_event_id=0 ``` **事件类型**: | 事件 | 触发时机 | payload | |------|---------|---------| | `connected` | WebSocket 连接成功 | trace_id, current_event_id, goal_tree(完整 GoalTree) | | `goal_added` | 新增 Goal | goal 完整数据(含 self_stats, cumulative_stats) | | `goal_updated` | Goal 状态变化(含级联完成) | goal_id, updates(含 cumulative_stats),affected_goals | | `message_added` | 新 Message | message 数据(含 goal_id),affected_goals | | `trace_completed` | 执行完成 | 统计信息 | **事件详情**: **`connected`** - 连接时推送完整 GoalTree,前端据此初始化 DAG: ```json { "event": "connected", "trace_id": "xxx", "current_event_id": 42, "goal_tree": { "mission": "...", "goals": [...] } } ``` **`message_added`** - 新 Message 时,后端更新统计并推送受影响的 Goals: ```json { "event": "message_added", "message": { "message_id": "...", "role": "assistant", "goal_id": "2.1", "..." : "..." }, "affected_goals": [ { "goal_id": "2.1", "self_stats": {"message_count": 4, "total_tokens": 2000, "total_cost": 0.03, "preview": "read → edit × 2"}, "cumulative_stats": {"message_count": 4, "total_tokens": 2000, "total_cost": 0.03, "preview": "read → edit × 2"} }, { "goal_id": "2", "cumulative_stats": {"message_count": 12, "total_tokens": 6800, "total_cost": 0.08, "preview": "glob → read → edit × 5 → bash"} } ] } ``` `affected_goals` 包含该 Message 直接关联的 Goal(更新 self_stats + cumulative_stats)以及所有祖先 Goal(仅更新 cumulative_stats)。前端根据当前展开状态选择使用哪个 stats 渲染边。 **`goal_updated`** - Goal 状态变化时推送,包含级联完成场景: ```json { "event": "goal_updated", "goal_id": "2.1", "updates": { "status": "completed", "summary": "接口设计完成" }, "affected_goals": [ { "goal_id": "2.1", "cumulative_stats": {"message_count": 4, "total_tokens": 2000, "total_cost": 0.03, "preview": "read → edit × 2"} }, { "goal_id": "2", "status": "completed", "summary": "功能实现完成", "cumulative_stats": {"message_count": 12, "total_tokens": 6800, "total_cost": 0.08, "preview": "..."} } ] } ``` 当所有子 Goal 完成时,后端自动级联完成父 Goal,并在 `affected_goals` 中包含所有状态变更的祖先。前端收到后直接更新对应节点,无需自行计算。 **`goal_added`** - 新增 Goal,携带完整 Goal 数据: ```json { "event": "goal_added", "goal": { "id": "2.1", "description": "设计接口", "reason": "需要先定义 API", "status": "pending", "self_stats": {}, "cumulative_stats": {} }, "parent_id": "2" } ``` **实现**:`agent/execution/websocket.py` ### 存储结构 ``` .trace/{trace_id}/ ├── goal.json # GoalTree(嵌套 JSON,含 abandoned 目标) ├── messages/ # Messages(每条独立文件) │ ├── {message_id}.json │ └── ... ├── events.jsonl # 事件流(WebSocket 断线续传) └── meta.json # Trace 元数据 ``` --- ## 分支-合并设计(explore 工具) ### 场景 ``` 主线 Agent: [1] 分析问题 [2] explore_start: 启动并行探索 (type=explore_start) │ ├── 分支A (sub-agent): [1]设计 → [2]实现 → 完成 └── 分支B (sub-agent): [1]设计 → [2]实现 → [3]测试 → 完成 │ ↓ (工具返回汇总结果) [3] explore_merge: 评估选择JWT (type=explore_merge) [4] 完善实现 ``` **核心原则**: - 每个分支是独立的 sub-agent,有自己的 GoalTree 和 Message List - 模型在分支内看到的是简单的连续编号 "1", "2", "3"(独立于主线) - `explore_start` 和 `explore_merge` 是主线 GoalTree 中的特殊 Goal 类型 - 分支数据独立存储,不直接嵌入主线 GoalTree - explore 工具返回时自动汇总各分支 summary ### 数据结构 **主线 GoalTree**(不含分支内部 Goals): ```python # Goal 类型在前面已定义,这里展示主线 GoalTree 示例 goals = [ Goal(id="1", type="normal", description="分析问题", ...), Goal(id="2", type="explore_start", description="探索认证方案", branch_ids=["A", "B"], ...), Goal(id="3", type="explore_merge", description="选择JWT方案", explore_start_id="2", merge_summary="...", selected_branch="A", ...), Goal(id="4", type="normal", description="完善实现", ...), ] ``` **分支上下文**(独立存储): ```python @dataclass class BranchContext: """分支执行上下文(独立的 sub-agent 环境)""" id: str # 分支 ID,如 "A", "B" explore_start_id: str # 关联的 explore_start Goal ID description: str # 探索方向描述 status: BranchStatus # exploring | completed | abandoned # 独立的执行环境 goal_tree: GoalTree # 分支自己的 GoalTree(简单编号 1, 2, 3...) summary: Optional[str] # 完成时的总结 cumulative_stats: GoalStats # 累计统计 last_message: Optional[Message] # 最新消息(用于可视化预览) ``` ### 存储结构 ``` .trace/{trace_id}/ ├── meta.json # Trace 元数据 ├── goal.json # 主线 GoalTree ├── messages/ # 主线 Messages │ └── ... ├── branches/ # 分支数据(独立存储) │ ├── A/ │ │ ├── meta.json # BranchContext 元数据 │ │ ├── goal.json # 分支 A 的 GoalTree │ │ └── messages/ # 分支 A 的 Messages │ └── B/ │ └── ... └── events.jsonl # 事件流 ``` ### DAG 可视化 **折叠视图**(explore 区域显示为 start → merge): ``` [1:分析] ──→ [2:explore_start] ──→ [3:explore_merge] ──→ [4:完善] │ │ (启动2分支) (汇总评估) ``` **展开分支视图**(显示并行路径): ``` ┌──→ [A:JWT方案] ────┐ [1:分析] ──→ [2] ─┤ ├──→ [3:合并] ──→ [4:完善] └──→ [B:Session方案] ┘ ``` **继续展开分支 A 内部**: ``` ┌──→ [A.1:设计] → [A.2:实现] ──┐ [1:分析] ──→ [2] ─┤ ├──→ [3:合并] ──→ [4:完善] └──→ [B:Session方案] ──────────┘ ``` 注意:`[A.1]`, `[A.2]` 是**前端显示格式**,后端存储的是 `(branch_id="A", goal_id="1")`。 ### 前端 API **REST**:返回主线 GoalTree + 分支元数据(不含分支内部 Goals),按需加载分支详情。 ```http GET /api/traces/{trace_id} ``` 响应: ```json { "goal_tree": { "goals": [ {"id": "1", "type": "normal", "description": "分析问题", ...}, {"id": "2", "type": "explore_start", "branch_ids": ["A", "B"], ...}, {"id": "3", "type": "explore_merge", "explore_start_id": "2", ...}, {"id": "4", "type": "normal", ...} ] }, "branches": { "A": { "id": "A", "explore_start_id": "2", "description": "JWT方案", "status": "completed", "summary": "JWT方案实现完成,无状态但token较大", "cumulative_stats": {"message_count": 8, "total_tokens": 4000, ...}, "goal_count": 2, "last_message": {"role": "assistant", "content": "JWT实现完成...", ...} }, "B": {...} } } ``` **按需加载分支详情**: ```http GET /api/traces/{trace_id}/branches/{branch_id} ``` 响应: ```json { "id": "A", "description": "JWT方案", "status": "completed", "summary": "...", "goal_tree": { "goals": [ {"id": "1", "description": "JWT设计", ...}, {"id": "2", "description": "JWT实现", ...} ] }, "cumulative_stats": {...} } ``` **WebSocket 事件**: | 事件 | 触发时机 | payload | |------|---------|---------| | `branch_started` | 分支开始探索 | explore_start_id, branch 元数据 | | `branch_goal_added` | 分支内新增 Goal | branch_id, goal | | `branch_message_added` | 分支内新 Message | branch_id, message, affected_goals | | `branch_completed` | 分支完成 | branch_id, summary, cumulative_stats, last_message | | `explore_completed` | 所有分支完成 | explore_start_id, merge_summary | ### explore 工具流程 ```python @tool def explore(branches: List[str]) -> str: """并行探索多个方向""" # 1. 创建 explore_start Goal start_goal = Goal( id=next_id(), type="explore_start", description=f"探索 {len(branches)} 个方案", branch_ids=[chr(ord('A') + i) for i in range(len(branches))], ) # 2. 为每个方向创建 sub-agent for i, desc in enumerate(branches): branch_id = chr(ord('A') + i) create_branch_context( branch_id=branch_id, explore_start_id=start_goal.id, description=desc, ) spawn_sub_agent(branch_id) # 3. 等待所有分支完成 results = await gather_branch_results() # 4. 创建 explore_merge Goal merge_summary = format_exploration_results(results) merge_goal = Goal( id=next_id(), type="explore_merge", description="评估探索结果", explore_start_id=start_goal.id, merge_summary=merge_summary, ) # 5. 返回汇总给主线 Agent return merge_summary ``` **汇总结果示例**(作为 explore 工具的返回值): ```markdown ## 探索结果 ### 分支 A: JWT 方案 实现完成。优点:无状态,易扩展。缺点:token 较大,无法主动失效。 ### 分支 B: Session 方案 实现完成。优点:token 小,可主动失效。缺点:需要 Redis 存储。 --- 两种方案都已实现,请选择一种继续。 ``` ### Context 压缩 分支完成后的压缩策略: 1. **分支完成时**:分支的详细 context 压缩为 summary,存储在 BranchContext.summary 2. **explore 完成后**:所有分支的 summary 汇总为 merge_summary 3. **主线 context**:explore 工具调用被压缩为一条包含 merge_summary 的消息 --- ## 与 OpenCode 方案的对比 | 方面 | OpenCode | 我们的方案 | |------|----------|-----------| | Plan 格式 | 纯文本 (plan.md) | 结构化 (GoalTree 嵌套 JSON) | | Plan 与执行关联 | 无 | 通过 goal_id 关联 | | 执行记录 | Message List | Message List(加 goal_id 元数据) | | 压缩时机 | 事后(context 满时) | 增量(goal 完成/放弃时) | | 并行探索 | Sub-agent(手动管理) | explore 工具(自动汇总) | | 回溯能力 | 有限 | 精确(基于 goal 压缩 + 废弃分支 summary) | | 可视化 | 无 | DAG(边可展开/折叠) | | 工具复杂度 | todoread/todowrite | goal/explore | --- ## 实现位置 | 功能 | 文件路径 | 状态 | |------|---------|------| | Goal 数据模型 | `agent/goal/models.py` | 待调整(ID 映射) | | goal 工具 | `agent/goal/tool.py` | 待调整 | | explore 工具 | `agent/goal/explore.py` | 待实现 | | Context 压缩 | `agent/goal/compaction.py` | 待调整 | | Message 数据模型 | `agent/execution/models.py` | 待调整(Step→Message) | | TraceStore 协议 | `agent/execution/protocols.py` | 待调整 | | DAG 可视化 API | `agent/execution/api.py` | 待调整(tree→DAG) | | WebSocket 推送 | `agent/execution/websocket.py` | 待调整(新增 goal 事件) | | Plan 注入 | `agent/core/runner.py` | 待调整 | --- ## 渐进式实现计划 ### Phase 1: 基础 goal 工具 - GoalTree 数据结构(含 ID 映射) - goal 工具(add, done, focus) - Plan 注入到 system prompt(含显示序号生成) - Message 模型(替代 Step) ### Phase 2: 回溯支持 - abandon 操作(两种模式) - 废弃分支 summary 累积到新分支 - 基于 goal 的 context 压缩 ### Phase 3: 可视化 - DAG 视图 API - WebSocket goal/message 事件 - 展开/折叠逻辑 ### Phase 4: 并行探索 - explore 工具 - 独立 message list 管理 - 结果汇总机制