Przeglądaj źródła

design: context management

Talegorithm 1 miesiąc temu
rodzic
commit
3c1b194ff0
5 zmienionych plików z 1147 dodań i 751 usunięć
  1. 11 15
      docs/README.md
  2. 659 0
      docs/context-comparison.md
  3. 439 0
      docs/context-management.md
  4. 38 87
      docs/decisions.md
  5. 0 649
      docs/step-tree.md

+ 11 - 15
docs/README.md

@@ -226,8 +226,6 @@ class Step:
 
 **实现**:`agent/execution/models.py:Step`
 
-**详细设计**:参考 [`docs/step-tree.md`](./step-tree.md)
-
 ---
 
 ## 模块详情
@@ -249,12 +247,12 @@ class Step:
 
 **使用示例**:`examples/subagent_example.py`
 
-### [Step 树与 Context 管理](./step-tree.md)
-- Step 类型:goal、action、result、evaluation
-- Step 状态:planned、in_progress、completed、failed、skipped
-- 树结构:统一表达计划和执行
-- step 工具:计划管理和进度更新
-- Context 压缩:基于树结构的历史消息压缩
+### [Context 管理](./context-management.md)
+- OpenCode 方案参考:Message 管理、两阶段压缩、Sub-Agent
+- goal 工具:线性计划管理
+- explore 工具:并行探索-合并
+- 回溯机制:abandon + context 压缩
+- 数据结构:Goal Tree + 线性 Message List
 
 ### [工具系统](./tools.md)
 - 工具定义和注册
@@ -539,9 +537,9 @@ agent/
    - 需要统计分析
    - 数量大,动态更新
 
-4. **不需要事件系统**
-   - 后台场景,不需要实时通知
-   - Trace/Step 已记录所有信息
+4. **Context 管理:Goal + Explore 方案**
+   - 简单工具接口,系统管理复杂性
+   - 详见 [`docs/context-management.md`](./context-management.md)
 
 ---
 
@@ -563,8 +561,6 @@ watch -n 0.5 cat .trace/tree.txt
 
 **实现**:`agent/execution/tree_dump.py`
 
-**详细说明**:参考 [`docs/step-tree.md`](./step-tree.md#debug-工具)
-
 ---
 
 ## 测试
@@ -595,8 +591,8 @@ GEMINI_API_KEY=xxx pytest tests/e2e/ -v -m e2e
 | 概念 | 定义 | 存储 | 实现 |
 |------|------|------|------|
 | **Trace** | 一次任务执行 | 文件系统(JSON) | `execution/models.py` |
-| **Step** | 执行步骤(树结构) | 文件系统(JSON) | `execution/models.py` |
-| **Goal Step** | 计划项/目标 | Step 的一种类型 | `execution/models.py` |
+| **Step** | 执行步骤 | 文件系统(JSON) | `execution/models.py` |
+| **Goal** | 计划目标 | goal.json | `goal/models.py`(待实现) |
 | **Sub-Agent** | 专门化的子代理 | 独立 Trace | `tools/builtin/task.py` |
 | **AgentDefinition** | Agent 类型定义 | 配置文件/代码 | `subagents/` |
 | **Skill** | 能力描述(Markdown) | 文件系统 | `memory/skill_loader.py` |

+ 659 - 0
docs/context-comparison.md

@@ -0,0 +1,659 @@
+# Context 管理方案对比分析
+
+> 对比 OpenCode、Codex 和 Gemini-cli 三个项目的 context 管理方案
+
+---
+
+## 一、整体架构对比
+
+| 维度 | **OpenCode** | **Codex** | **Gemini-cli** |
+|------|-------------|-----------|---------------|
+| **核心数据结构** | 线性 Message List | ContextManager (Vec<ResponseItem>) | Content[] (双版本) |
+| **消息历史版本** | 单一版本 | 单一版本 + GhostSnapshot | 精选版本 + 完整版本 |
+| **分层设计** | 无 | 无 | **✓ 三层**: Global → Environment → JIT |
+| **Plan 管理** | goal.json (计划中) + plan.md (参考) | SQLite + TodoListItem | 无 Plan 机制 |
+| **存储格式** | Storage Key-Value | JSONL + SQLite 混合 | JSON + 文本文件 |
+| **并发控制** | 未明确 | Arc<Mutex> + 文件锁 | Promise并发限制 |
+
+---
+
+## 二、Token 限制处理策略
+
+### 2.1 Token 估算方法
+
+| 项目 | 估算策略 | 精度 | 实现位置 |
+|------|---------|------|---------|
+| **OpenCode** | 未详细说明,引用 Prune 阈值 | 中 | - |
+| **Codex** | **字节估算**: `bytes / 4` (1 token ≈ 4 bytes) | 低 | `truncate.rs::approx_token_count()` |
+| **Gemini-cli** | **启发式**: ASCII (0.25), 非ASCII (1.3), 图片 (3000), PDF (25800) | 高 | `tokenCalculation.ts::estimateTokenCountSync()` |
+
+**关键差异**:
+- **Codex**: 简单但快速,适合实时估算
+- **Gemini-cli**: 更精确,区分字符类型和媒体,牺牲少量性能
+
+### 2.2 Token 限制阈值
+
+| 项目 | 限制类型 | 阈值定义 |
+|------|---------|---------|
+| **OpenCode** | 删除阈值 | `PRUNE_MINIMUM = 20,000`, `PRUNE_PROTECT = 40,000` |
+| **Codex** | 模型限制 | 依赖模型配置,无固定值 |
+| **Gemini-cli** | 压缩阈值 | 默认 **50%** 模型限制 (`DEFAULT_COMPRESSION_TOKEN_THRESHOLD = 0.5`) |
+
+### 2.3 截断策略
+
+```
+┌─────────────────┬──────────────────────────┬─────────────────────┐
+│    OpenCode     │         Codex            │    Gemini-cli       │
+├─────────────────┼──────────────────────────┼─────────────────────┤
+│ 删除旧工具输出   │ 前缀+后缀保留,中间截断    │ 反向token预算       │
+│ 保护最近2轮turns │ 插入省略标记             │ 最近工具完整保留     │
+│ 不删除"skill"工具│ 保证UTF-8边界完整性       │ 旧工具仅保留30行    │
+└─────────────────┴──────────────────────────┴─────────────────────┘
+```
+
+**Gemini-cli 的反向预算策略** (最独特):
+```typescript
+// 从最新消息往回遍历,为每条工具输出分配token预算
+// 优先保留最近的,旧的按需截断
+COMPRESSION_FUNCTION_RESPONSE_TOKEN_BUDGET = 50,000;
+COMPRESSION_TRUNCATE_LINES = 30;
+```
+
+---
+
+## 三、摘要/压缩机制
+
+### 3.1 压缩触发时机
+
+| 项目 | 触发时机 | 方式 |
+|------|---------|------|
+| **OpenCode** | 事后 (context满时) | 被动压缩 |
+| **Codex** | 超过模型窗口时 | 自动压缩 |
+| **Gemini-cli** | **主动** (达到50%阈值) + **手动** (/compress) | 混合方式 |
+
+### 3.2 压缩策略对比
+
+#### OpenCode: 两阶段压缩
+
+```
+阶段1: Prune (清理旧工具输出)
+  ├─ 从后向前遍历
+  ├─ 跳过最后2轮
+  ├─ 跳过已有summary的消息
+  └─ 删除量 > PRUNE_MINIMUM 时执行
+
+阶段2: Full Compaction (上下文总结)
+  ├─ 创建summary=true的assistant消息
+  ├─ 调用"compaction"专用agent
+  └─ 提示词: "Provide a detailed prompt for continuing..."
+```
+
+#### Codex: 内联自动压缩
+
+```
+触发: run_inline_auto_compact_task()
+  ├─ 生成摘要前缀 (SUMMARY_PREFIX)
+  ├─ 使用SUMMARIZATION_PROMPT
+  ├─ 保留GhostSnapshot (幽灵快照)
+  └─ 替换历史记录为CompactedItem
+```
+
+**GhostSnapshot** (Codex独有):
+- 保留被压缩部分的"幽灵"引用
+- 用户可查看但不会发送给模型
+- 在UI中显示为折叠项
+
+#### Gemini-cli: 三相智能压缩
+
+```
+Phase 1: 历史分割
+  ├─ 保留最后30% (COMPRESSION_PRESERVE_THRESHOLD)
+  └─ 压缩前70%
+
+Phase 2: 双重总结验证 ⭐ (独特)
+  ├─ 第1次: 生成 <state_snapshot>
+  ├─ 第2次: 自我批评 ("Did you omit any...")
+  └─ 生成改进版本或确认原版本
+
+Phase 3: 输出验证
+  ├─ 检查压缩后token数 < 原token数
+  └─ 失败则保持原历史
+```
+
+**双重验证的价值**:
+```typescript
+// 第一次生成
+"Generate a state snapshot of the conversation..."
+
+// 第二次自我批评
+"Did you omit any specific file content, code snippets,
+or context that might be needed later? If yes, provide
+an improved version. If no, confirm the original."
+```
+
+### 3.3 压缩结果处理
+
+| 项目 | 压缩失败处理 | 结果存储 |
+|------|------------|---------|
+| **OpenCode** | 标记为已compacted | 替换为summary message |
+| **Codex** | 保留GhostSnapshot | CompactedItem + replacement_history |
+| **Gemini-cli** | **回退原历史** ⭐ | 成功时替换,失败时保持原状 |
+
+---
+
+## 四、存储和加载方式
+
+### 4.1 存储架构
+
+#### OpenCode (计划中)
+```
+.trace/{trace_id}/
+├── goal.json          # Goal Tree (结构化plan)
+├── messages.jsonl     # 消息记录 (含 goal_id)
+└── meta.json          # Trace 元数据
+```
+
+#### Codex
+```
+~/.codex/
+├── history.jsonl                           # 全局消息历史
+├── sessions/
+│   ├── rollout-{timestamp}-{uuid}.jsonl    # 会话回滚文件
+│   └── ...
+└── state.db                                # SQLite状态数据库
+```
+
+**关键特性**:
+- **原子写入**: 使用 `O_APPEND` 标志
+- **并发安全**: Advisory文件锁
+- **自动清理**: 超过限制时删除旧条目 (软限制80%)
+
+#### Gemini-cli
+```
+~/.gemini/
+├── GEMINI.md                               # 全局内存
+├── tmp/{project_hash}/
+│   └── chats/{session-ID}.json             # 会话记录
+└── config.json                             # 配置
+
+项目根目录/
+├── GEMINI.md                               # 环境内存
+└── subdirs/
+    └── GEMINI.md                           # JIT内存 (按需加载)
+```
+
+**独特的三层内存系统**:
+```
+Tier 1: Global Memory
+  ├─ ~/.gemini/GEMINI.md
+  └─ 用户级别,所有会话共享
+
+Tier 2: Environment Memory
+  ├─ 项目根目录的GEMINI.md
+  ├─ 扩展提供的上下文文件
+  └─ MCP客户端指令
+
+Tier 3: JIT Subdirectory Memory ⭐
+  ├─ 访问路径时动态发现
+  ├─ 向上遍历到项目根
+  ├─ 向下BFS搜索 (最多200目录)
+  └─ 避免加载不相关上下文
+```
+
+### 4.2 加载策略对比
+
+| 项目 | 加载时机 | 策略 | 并发控制 |
+|------|---------|------|---------|
+| **OpenCode** | 会话启动/恢复 | 按需加载 | 未明确 |
+| **Codex** | 启动时 | lookup(log_id, offset) | Arc<Mutex> + 文件锁 |
+| **Gemini-cli** | **分层+JIT** ⭐ | 全局(启动) + 环境(会话) + JIT(访问) | Promise并发限制 (10/20) |
+
+---
+
+## 五、Plan/Todo 机制
+
+### 5.1 数据结构对比
+
+#### OpenCode (参考方案)
+```typescript
+// plan.md (文本)
+- [ ] 分析代码
+- [x] 实现功能
+- [ ] 测试
+
+// Todo.Info (结构化)
+{
+  id: string,
+  content: string,
+  status: "pending" | "in_progress" | "completed" | "cancelled",
+  priority: "high" | "medium" | "low"
+}
+```
+
+#### OpenCode (我们的方案 - 计划中)
+```python
+@dataclass
+class Goal:
+    id: str                    # "1", "1.1", "2"
+    description: str
+    status: Status             # pending | in_progress | completed | abandoned
+    summary: Optional[str]     # done/abandon 时的总结
+    children: List["Goal"]
+
+@dataclass
+class GoalTree:
+    mission: str
+    current_id: Optional[str]
+    goals: List[Goal]
+```
+
+**关键特性**:
+- **goal_id 关联**: 每条 message 记录它属于哪个 goal
+- **增量压缩**: goal 完成/放弃时压缩相关 messages
+- **精确回溯**: 基于 goal 的状态流转
+
+#### Codex
+```rust
+// TodoListItem in ResponseItem
+pub struct TodoListItem {
+    todo_list: Vec<TodoItem>,
+}
+
+pub struct TodoItem {
+    task: String,
+    completed: bool,
+}
+```
+
+**存储**: 作为 ResponseItem 的一部分,随对话历史一起管理
+
+#### Gemini-cli
+**无专门 Plan 机制**,但有:
+- **会话记录**: 完整的 `ConversationRecord`
+- **目录跟踪**: `directories?: string[]` (会话中添加的目录)
+
+### 5.2 执行与 Plan 的关联
+
+| 项目 | 关联方式 | 可编辑性 | 可视化 |
+|------|---------|---------|--------|
+| **OpenCode (参考)** | 无结构化关联 | plan.md 可直接编辑 | 基础 |
+| **OpenCode (计划)** | message.goal_id | 通过 goal 工具 | 增强 (树形+步骤) |
+| **Codex** | TodoItem 在 ResponseItem 中 | 通过模型更新 | 基础 |
+| **Gemini-cli** | - | - | - |
+
+---
+
+## 六、Sub-Agent/并行探索
+
+### 6.1 Sub-Agent 支持
+
+#### OpenCode (参考方案)
+```typescript
+// Agent Mode
+- primary: 主代理,执行工具
+- subagent: 子代理,独立context
+
+// 内置 Sub-Agents
+- general: 通用代理,可并行执行
+- explore: 代码探索,仅查询工具
+- compaction: 上下文总结
+```
+
+**执行流程**:
+```
+1. 创建 SubtaskPart
+2. 子代理独立处理 (独立 message list)
+3. 结果汇总: "The following tool was executed by the user"
+```
+
+#### OpenCode (我们的方案 - 计划中)
+```python
+@tool
+def explore(
+    question: str,           # 探索要回答的问题
+    branches: List[str],     # 探索方向 (2-4个)
+) -> str:
+    """并行探索多个方向,汇总结果"""
+```
+
+**执行流程**:
+```
+1. 为每个 branch 创建独立 message list
+2. 串行执行每个 branch (各自调用 LLM + 工具)
+3. 收集每个 branch 的结论
+4. 返回汇总结果给主会话
+```
+
+#### Codex
+**无明确 Sub-Agent 机制**,但有:
+- **SessionState**: 管理会话状态
+- **Turn Context**: 单轮对话的上下文
+
+#### Gemini-cli
+**无 Sub-Agent 机制**,但有:
+- **CoreToolScheduler**: 工具执行调度
+- **并发工具执行**: 多个工具可并行运行
+
+### 6.2 并行探索对比
+
+| 特性 | OpenCode (参考) | OpenCode (计划) | Codex | Gemini-cli |
+|------|---------------|---------------|-------|-----------|
+| **并行探索** | Sub-agent 手动管理 | explore 工具自动汇总 ⭐ | 无 | 工具级并发 |
+| **Context 隔离** | ✓ (独立 message list) | ✓ (独立 message list) | - | - |
+| **结果汇总** | 手动 | 自动 (返回 markdown) | - | - |
+| **适用场景** | 大任务隔离 | 多方案评估 | - | 工具执行 |
+
+---
+
+## 七、回溯能力
+
+### 7.1 回溯机制对比
+
+#### OpenCode (参考方案)
+```
+限制: 有限的回溯能力
+- 无精确状态保存
+- 依赖压缩后的摘要
+```
+
+#### OpenCode (我们的方案 - 计划中)
+```python
+goal(abandon="方案A需要Redis,环境没有", add="实现方案B")
+```
+
+**回溯流程**:
+```
+Before:
+  Messages:
+    [分析代码的 20 条 message...]
+    [实现方案 A 的 30 条 message...]  ← 这些要压缩
+    [测试失败的 message...]
+
+After:
+  Messages:
+    [分析代码的 20 条 message...]
+    [Summary: "尝试方案A,因依赖问题失败"]  ← 压缩为1条
+    [开始方案B的 message...]
+
+  Plan:
+    [✓] 1. 分析代码
+    [✗] 2. 实现方案A (abandoned: 依赖问题)
+    [→] 2'. 实现方案B
+```
+
+**优势**:
+- **精确回溯**: 基于 goal 的状态标记
+- **保留失败原因**: summary 包含 abandon 原因
+- **压缩旧路径**: 失败尝试不占用大量 context
+
+#### Codex
+```rust
+// GhostSnapshot: 保留被压缩部分的引用
+pub struct CompactedItem {
+    message: String,
+    replacement_history: Option<Vec<ResponseItem>>,
+}
+```
+
+**回溯能力**:
+- **GhostSnapshot**: 用户可查看历史,但不发送给模型
+- **Rollout 记录**: 完整的会话记录保存在 JSONL
+- **用户转换检测**: 可定位到特定用户消息
+
+#### Gemini-cli
+```typescript
+enum CompressionStatus {
+  COMPRESSED,
+  COMPRESSION_FAILED_INFLATED_TOKEN_COUNT,  // 压缩反而增加
+  COMPRESSION_FAILED_EMPTY_SUMMARY,          // 摘要为空
+  NOOP,
+}
+```
+
+**回溯能力**:
+- **会话记录**: 完整的 `ConversationRecord` 保存所有消息
+- **压缩失败回退**: 自动回退到原历史
+- **无结构化回溯**: 缺乏基于任务的回溯机制
+
+### 7.2 回溯对比表
+
+| 项目 | 回溯粒度 | 状态保存 | 失败处理 | 历史查看 |
+|------|---------|---------|---------|---------|
+| **OpenCode (参考)** | 粗粒度 | 摘要 | 有限 | 基础 |
+| **OpenCode (计划)** | **goal级别** ⭐ | goal.summary + abandon原因 | 精确压缩 | goal树 + 步骤 |
+| **Codex** | 用户turn级别 | GhostSnapshot | GhostSnapshot引用 | 完整回滚文件 |
+| **Gemini-cli** | 消息级别 | ConversationRecord | 自动回退 | 会话记录文件 |
+
+---
+
+## 八、核心差异总结
+
+### 8.1 设计哲学
+
+| 项目 | 核心理念 | 优势场景 |
+|------|---------|---------|
+| **OpenCode** | **结构化计划驱动** | 复杂任务,需要回溯和探索 |
+| **Codex** | **简单高效,成熟稳定** | 通用编码助手,快速响应 |
+| **Gemini-cli** | **分层智能,高保真保留** | 多项目管理,长期会话 |
+
+### 8.2 独特创新点
+
+#### OpenCode (我们的方案)
+1. **goal 工具**: 结构化 Plan + 执行关联
+2. **explore 工具**: 并行探索自动汇总
+3. **增量压缩**: goal 完成/放弃时压缩,而非事后被动
+4. **精确回溯**: abandon + 状态流转
+
+#### Codex
+1. **GhostSnapshot**: 压缩历史仍可查看
+2. **原子写入**: O_APPEND + 并发安全
+3. **字节估算**: 简单快速 (bytes/4)
+4. **历史规范化**: 确保调用-输出对完整性
+
+#### Gemini-cli
+1. **三层内存**: Global → Environment → JIT ⭐
+2. **双重验证**: 自我批评式压缩
+3. **反向token预算**: 优先保留最近工具输出
+4. **启发式token估算**: 区分字符类型和媒体
+5. **JIT Context发现**: 按需加载相关GEMINI.md
+
+### 8.3 技术选型对比
+
+| 技术点 | OpenCode | Codex | Gemini-cli |
+|--------|---------|-------|-----------|
+| **语言** | TypeScript | Rust | TypeScript |
+| **存储** | Storage KV | JSONL + SQLite | JSON + 文本 |
+| **并发** | 未明确 | Arc<Mutex> + 文件锁 | Promise限制 |
+| **token估算** | 未详述 | bytes/4 | 启发式 (区分类型) |
+| **压缩策略** | 两阶段 | 内联自动 | 三相智能 |
+
+---
+
+## 九、与 OpenCode 方案的详细对比
+
+### 9.1 OpenCode vs Codex
+
+| 方面 | OpenCode (参考) | Codex | 评估 |
+|------|---------------|-------|------|
+| **Plan格式** | 纯文本 (plan.md) | TodoItem (结构化) | Codex更结构化,但OpenCode计划中的goal.json更强 |
+| **Plan与执行关联** | 无 | TodoItem在ResponseItem中 | Codex有关联,但OpenCode计划中的goal_id更精确 |
+| **压缩时机** | 事后 (满时) | 事后 (超过窗口) | 相同 (被动) |
+| **并行探索** | Sub-agent (手动) | 无 | OpenCode参考方案更强,计划方案的explore更自动化 |
+| **回溯能力** | 有限 | GhostSnapshot | Codex的GhostSnapshot有价值,但OpenCode计划的goal-based更精确 |
+| **存储可靠性** | 未明确 | 原子写入+并发安全 | **Codex胜** ⭐ |
+| **工具复杂度** | todoread/todowrite | 无专门工具 | OpenCode参考方案更复杂,计划的goal/explore更简洁 |
+
+**可借鉴**:
+- ✓ Codex的原子写入和并发安全机制
+- ✓ GhostSnapshot的用户友好性 (可查看但不发送)
+- ✓ 历史规范化 (ensure_call_outputs_present)
+
+### 9.2 OpenCode vs Gemini-cli
+
+| 方面 | OpenCode (参考) | Gemini-cli | 评估 |
+|------|---------------|-----------|------|
+| **Plan格式** | plan.md + Todo.Info | 无 | **OpenCode胜** |
+| **Plan与执行关联** | 无 | 无 | 平局 |
+| **压缩时机** | 事后 (满时) | **主动** (50%阈值) | **Gemini-cli胜** ⭐ |
+| **并行探索** | Sub-agent (手动) | 工具级并发 | OpenCode的Sub-agent隔离更好 |
+| **回溯能力** | 有限 | 自动回退 | OpenCode计划方案的goal-based更强 |
+| **Context分层** | 无 | **三层** (Global/Env/JIT) | **Gemini-cli胜** ⭐ |
+| **压缩质量** | 单次生成 | **双重验证** (自我批评) | **Gemini-cli胜** ⭐ |
+| **工具输出保留** | 删除旧输出 | **反向预算** (保留最近) | **Gemini-cli胜** ⭐ |
+
+**可借鉴**:
+- ✓ 三层内存系统 (特别是JIT加载)
+- ✓ 双重验证的压缩机制
+- ✓ 主动压缩 (50%阈值,而非等到满)
+- ✓ 反向token预算 (优先保留最近工具输出)
+- ✓ 启发式token估算 (区分字符类型)
+
+---
+
+## 十、OpenCode 计划方案的优势
+
+### 10.1 创新点
+
+1. **结构化 Plan (goal.json)**
+   - 比纯文本更精确
+   - 支持树形结构 (goal.children)
+   - 状态流转清晰
+
+2. **执行与 Plan 的强关联 (goal_id)**
+   - 每条 message 知道它属于哪个 goal
+   - 支持基于 goal 的压缩和回溯
+   - 可视化时能展示 goal + 对应步骤
+
+3. **增量压缩 (goal 完成/放弃时)**
+   - 比事后被动压缩更主动
+   - 压缩粒度可控 (按 goal)
+   - 保留失败原因 (abandon summary)
+
+4. **explore 工具 (并行探索自动汇总)**
+   - 比手动管理 Sub-agent 更简单
+   - 适合多方案评估场景
+   - 自动生成汇总报告
+
+5. **精确回溯 (abandon + 状态流转)**
+   - 比 GhostSnapshot 更结构化
+   - 支持从失败尝试中学习
+   - Plan 树中保留失败路径
+
+### 10.2 待改进点 (可借鉴其他方案)
+
+1. **存储可靠性** (借鉴 Codex)
+   - 原子写入
+   - 并发安全 (文件锁)
+   - 自动清理旧数据
+
+2. **Context 分层** (借鉴 Gemini-cli)
+   - 全局 context (用户级别)
+   - 项目 context (项目级别)
+   - JIT context (按需加载)
+
+3. **压缩质量** (借鉴 Gemini-cli)
+   - 双重验证 (自我批评)
+   - 压缩失败自动回退
+   - 验证压缩后 token 数真的减少
+
+4. **主动压缩** (借鉴 Gemini-cli)
+   - 达到阈值 (如 50%) 时主动压缩
+   - 而非等到 context 满
+
+5. **工具输出管理** (借鉴 Gemini-cli)
+   - 反向 token 预算
+   - 优先保留最近工具输出的完整性
+   - 旧输出智能截断 (保留最后N行)
+
+6. **Token 估算** (借鉴 Gemini-cli)
+   - 启发式估算 (区分 ASCII/非ASCII/媒体)
+   - 提高估算精度
+
+7. **用户友好性** (借鉴 Codex)
+   - GhostSnapshot 机制 (可查看但不发送)
+   - 历史规范化 (保证调用-输出对完整性)
+
+---
+
+## 十一、实现建议
+
+### 11.1 Phase 1: 核心 goal 工具 (保留计划)
+- Goal 数据结构
+- goal 工具 (add, done, focus)
+- Plan 注入到 system prompt
+- 基础可视化
+
+### 11.2 Phase 2: 增强存储和压缩 (借鉴)
+- **存储增强** (借鉴 Codex):
+  - 原子写入和并发安全
+  - 自动清理机制
+- **压缩增强** (借鉴 Gemini-cli):
+  - 双重验证机制
+  - 主动压缩 (50%阈值)
+  - 反向token预算
+
+### 11.3 Phase 3: 回溯和 Context 分层
+- **回溯支持** (计划):
+  - abandon 操作
+  - Message 关联 goal_id
+  - 基于 goal 的 context 压缩
+- **Context 分层** (借鉴 Gemini-cli):
+  - 全局 context 文件
+  - 项目 context 文件
+  - JIT 子目录 context 发现
+
+### 11.4 Phase 4: 并行探索和优化
+- **explore 工具** (计划):
+  - 独立 message list 管理
+  - 结果汇总机制
+- **优化** (借鉴):
+  - GhostSnapshot (用户友好)
+  - 启发式 token 估算
+  - 历史规范化
+
+---
+
+## 十二、总结
+
+### 12.1 三方案对比矩阵
+
+| 能力维度 | OpenCode (计划) | Codex | Gemini-cli | 最佳方案 |
+|---------|---------------|-------|-----------|---------|
+| **结构化 Plan** | ⭐⭐⭐ goal.json | ⭐⭐ TodoItem | ⭐ 无 | OpenCode |
+| **执行关联** | ⭐⭐⭐ goal_id | ⭐⭐ 弱关联 | ⭐ 无 | OpenCode |
+| **压缩策略** | ⭐⭐ 增量 | ⭐⭐ 自动 | ⭐⭐⭐ 三相智能 | Gemini-cli |
+| **压缩时机** | ⭐⭐ goal完成时 | ⭐ 被动 | ⭐⭐⭐ 主动50% | Gemini-cli |
+| **并行探索** | ⭐⭐⭐ explore工具 | ⭐ 无 | ⭐⭐ 工具级 | OpenCode |
+| **回溯能力** | ⭐⭐⭐ goal-based | ⭐⭐ GhostSnapshot | ⭐⭐ 会话记录 | OpenCode |
+| **存储可靠性** | ⭐⭐ 未明确 | ⭐⭐⭐ 原子+锁 | ⭐⭐ Promise限制 | Codex |
+| **Context分层** | ⭐ 无 | ⭐ 无 | ⭐⭐⭐ 三层JIT | Gemini-cli |
+| **工具输出管理** | ⭐ 删除旧的 | ⭐⭐ 截断 | ⭐⭐⭐ 反向预算 | Gemini-cli |
+| **Token估算** | ⭐⭐ 基础 | ⭐ bytes/4 | ⭐⭐⭐ 启发式 | Gemini-cli |
+
+**评分说明**: ⭐ 基础, ⭐⭐ 良好, ⭐⭐⭐ 优秀
+
+### 12.2 最终建议
+
+**OpenCode 的核心优势** (保留并强化):
+- ✅ 结构化 goal.json
+- ✅ goal_id 关联
+- ✅ explore 工具
+- ✅ 精确回溯
+
+**应借鉴的关键特性**:
+1. **Gemini-cli 的压缩机制**: 双重验证 + 主动压缩 + 反向预算
+2. **Gemini-cli 的分层 Context**: 特别是 JIT 加载
+3. **Codex 的存储可靠性**: 原子写入 + 并发安全
+4. **Codex 的 GhostSnapshot**: 提升用户体验
+
+**综合方案** (OpenCode + 借鉴):
+```
+OpenCode 计划方案
+  ├─ 保留: goal.json, explore工具, goal-based回溯
+  ├─ 增强压缩: Gemini-cli的双重验证 + 主动压缩
+  ├─ 增强存储: Codex的原子写入 + 并发安全
+  ├─ 增强Context: Gemini-cli的三层分层 + JIT加载
+  └─ 增强UX: Codex的GhostSnapshot
+```
+
+这将是一个**结构化驱动 + 智能压缩 + 可靠存储 + 分层Context**的综合方案,集三家之长! 🎯

+ 439 - 0
docs/context-management.md

@@ -0,0 +1,439 @@
+# 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 压缩)
+```
+
+### 架构
+
+```
+┌─────────────────────────────────────────────┐
+│              Plan (goal.json)                │
+│  结构化的目标树,LLM 通过 goal 工具维护       │
+└─────────────────────────────────────────────┘
+                      │
+         ┌────────────┴────────────┐
+         ↓                         ↓
+┌─────────────────┐      ┌─────────────────┐
+│   线性执行       │      │  并行探索        │
+│   (主 message   │      │  (explore 工具)  │
+│    list)        │      │  多个独立分支    │
+└─────────────────┘      └─────────────────┘
+         │                         │
+         ↓                         ↓
+┌─────────────────┐      ┌─────────────────┐
+│  完成/回溯       │      │  合并评估        │
+│  done/abandon   │      │  返回主会话      │
+│  触发 context   │      └─────────────────┘
+│  压缩           │
+└─────────────────┘
+```
+
+### 工具设计
+
+#### 1. goal 工具:计划管理
+
+```python
+@tool
+def goal(
+    add: Optional[str] = None,       # 添加目标(逗号分隔多个)
+    done: Optional[str] = None,      # 完成当前目标,值为 summary
+    abandon: Optional[str] = None,   # 放弃当前目标,值为原因
+    focus: Optional[str] = None,     # 切换焦点到指定 id
+) -> str:
+    """管理执行计划。"""
+```
+
+**层级支持**:`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)
+                        │
+                     abandon
+                        ↓
+                   abandoned
+                        ↓
+                  (压缩 context)
+```
+
+#### 2. explore 工具:并行探索
+
+基于 sub-agent 机制实现。
+
+```python
+@tool
+def explore(
+    branches: List[str],                  # 探索方向列表
+    background: Optional[str] = None,     # 背景概括(可选)
+) -> str:
+    """
+    并行探索多个方向,汇总结果。
+
+    - background 有值:用它初始化各分支的 context
+    - background 为空:继承主 message list
+    """
+```
+
+**示例**:
+```python
+explore(
+    background="我们在实现用户认证。项目用 FastAPI,用户模型在 models/user.py。环境没有 Redis。",
+    branches=[
+        "调研 JWT 方案,考虑 token 刷新和撤销",
+        "调研 Session 方案,寻找 Redis 替代存储"
+    ]
+)
+```
+
+**执行流程**:
+```
+1. 为每个 branch 创建 sub-agent
+   - context = background(或继承主 msg list)
+   - prompt = branch 指令
+2. 串行执行各 sub-agent
+3. 收集结论,汇总返回主会话
+```
+
+**分支 context 初始化**:
+- 有 `background`:LLM 概括的背景信息作为初始 context
+- 无 `background`:继承全部主 message list(适用于 context 不长的情况)
+
+### 数据结构
+
+#### Goal
+
+```python
+@dataclass
+class Goal:
+    id: str                              # 自动生成: "1", "1.1", "2"
+    description: str                     # 目标描述
+    status: Status                       # pending | in_progress | completed | abandoned
+    summary: Optional[str] = None        # 完成/放弃时的总结
+    children: List["Goal"] = field(default_factory=list)
+
+Status = Literal["pending", "in_progress", "completed", "abandoned"]
+
+@dataclass
+class GoalTree:
+    mission: str                         # 总任务描述
+    current_id: Optional[str] = None     # 当前焦点
+    goals: List[Goal] = field(default_factory=list)
+```
+
+#### Message 关联
+
+```python
+# 每条 message 记录它属于哪个 goal
+message = {
+    "role": "assistant",
+    "content": "...",
+    "goal_id": "2.1"  # 关联到目标 2.1
+}
+```
+
+### Context 管理
+
+#### 1. Plan 注入
+
+每次 LLM 调用时,在 system prompt 末尾注入当前计划状态:
+
+```markdown
+## Current Plan
+
+**Mission**: 实现用户认证功能
+**Current**: 2.1 实现登录接口
+
+**Progress**:
+[✓] 1. 分析代码
+    → 用户模型在 models/user.py,使用 bcrypt 加密
+[→] 2. 实现功能
+    [✓] 2.1 设计接口
+    [→] 2.2 实现登录接口  ← current
+    [ ] 2.3 实现注册接口
+[ ] 3. 测试
+```
+
+#### 2. 完成时压缩
+
+当调用 `goal(done="...")` 时:
+1. 找到该 goal 关联的所有 messages
+2. 将详细 messages 替换为一条 summary message
+3. 更新 goal 状态为 completed
+
+#### 3. 回溯时压缩
+
+当调用 `goal(abandon="...")` 时:
+1. 找到该 goal 关联的所有 messages
+2. 生成 summary(包含失败原因,供后续参考)
+3. 将详细 messages 替换为 summary message
+4. 更新 goal 状态为 abandoned
+
+**Before 回溯**:
+```
+Messages:
+  [分析代码的 20 条 message...]
+  [实现方案 A 的 30 条 message...]  ← 这些要压缩
+  [测试失败的 message...]
+
+Plan:
+  [✓] 1. 分析代码
+  [✓] 2. 实现方案 A
+  [→] 3. 测试
+```
+
+**After 回溯**:
+```
+Messages:
+  [分析代码的 20 条 message...]
+  [Summary: "尝试方案 A,因依赖问题失败"]  ← 压缩为 1 条
+  [开始方案 B 的 message...]
+
+Plan:
+  [✓] 1. 分析代码
+  [✗] 2. 实现方案 A (abandoned: 依赖问题)
+  [→] 2'. 实现方案 B
+  [ ] 3. 测试
+```
+
+### 存储结构
+
+```
+.trace/{trace_id}/
+├── goal.json          # Goal Tree(LLM 通过工具维护)
+├── messages.jsonl     # 消息记录(系统自动,含 goal_id)
+└── meta.json          # Trace 元数据
+```
+
+### 可视化
+
+Goal Tree + Messages 合并展示:
+
+```
+Mission: 实现用户认证功能
+══════════════════════════════════════════
+
+[✓] 1. 分析代码 (5 steps, 1.2s)
+    → 用户模型在 models/user.py
+    ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
+    ├─ glob_files("**/user*.py")
+    ├─ read_file("models/user.py")
+    └─ [详细步骤已折叠]
+
+[✗] 2. 实现方案 A (abandoned)
+    → 需要 Redis,环境没有
+
+[→] 2'. 实现方案 B  ← current
+    ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
+    ├─ read_file("requirements.txt")
+    └─ edit_file("app.py")
+
+[ ] 3. 测试
+
+──────────────────────────────────────────
+Progress: 1/3 goals | Current: 2'
+```
+
+---
+
+## 与 OpenCode 方案的对比
+
+| 方面 | OpenCode | 我们的方案 |
+|------|----------|-----------|
+| Plan 格式 | 纯文本 (plan.md) | 结构化 (goal.json) |
+| Plan 与执行关联 | 无 | 通过 goal_id 关联 |
+| 压缩时机 | 事后(context 满时) | 增量(goal 完成/放弃时) |
+| 并行探索 | Sub-agent(手动管理) | explore 工具(自动汇总) |
+| 回溯能力 | 有限 | 精确(基于 goal 压缩) |
+| 工具复杂度 | todoread/todowrite | goal/explore(更简单) |
+
+---
+
+## 实现位置
+
+| 功能 | 文件路径 | 状态 |
+|------|---------|------|
+| Goal 数据模型 | `agent/goal/models.py` | 待实现 |
+| goal 工具 | `agent/goal/tool.py` | 待实现 |
+| explore 工具 | `agent/goal/explore.py` | 待实现 |
+| Context 压缩 | `agent/goal/compaction.py` | 待实现 |
+| Plan 注入 | `agent/core/runner.py` | 待实现 |
+| 可视化 | `agent/goal/visualize.py` | 待实现 |
+
+---
+
+## 渐进式实现计划
+
+### Phase 1: 基础 goal 工具
+- Goal 数据结构
+- goal 工具(add, done, focus)
+- Plan 注入到 system prompt
+- 基础可视化
+
+### Phase 2: 回溯支持
+- abandon 操作
+- Message 关联 goal_id
+- 基于 goal 的 context 压缩
+
+### Phase 3: 并行探索
+- explore 工具
+- 独立 message list 管理
+- 结果汇总机制
+
+### Phase 4: 优化
+- 更智能的压缩策略
+- 可视化增强
+- 性能优化

+ 38 - 87
docs/decisions.md

@@ -347,134 +347,85 @@ async def advanced_search(
 
 ---
 
-## 11. Step 树结构 vs DAG
+## 11. Context 管理方案选择
 
-### 问题
-Step 之间的关系应该是树(单父节点)还是 DAG(多父节点)?
-
-### 方案对比
+**日期**: 2026-02-04
 
-| 方案 | 优点 | 缺点 |
-|------|------|------|
-| **DAG(多父节点)** | 能精确表达并行汇合 | 复杂,难以折叠/展开 |
-| **树(单父节点)** | 简单,天然支持折叠 | 并行汇合需要其他方式表达 |
+### 问题
+自主长程 Agent(非交互式工具)如何有效管理 Context?
 
 ### 决策
-**选择:树结构(单父节点)**
+**选择:基于 OpenCode 方案,增强计划管理和回溯能力**
 
-**理由**:
-1. **可视化友好**:树结构天然支持折叠/展开
-2. **足够表达**:并行工具调用可以是同一父节点的多个子节点
-3. **简化实现**:不需要处理复杂的 DAG 遍历
+**核心设计**:
+- 简单的工具接口(goal, explore)
+- 复杂逻辑由系统处理(分支管理、context 压缩)
 
-**实现**:`Step.parent_id: Optional[str]`(单个值,不是列表)
+**工具**:
+- `goal`:线性计划管理(add, done, abandon, focus)
+- `explore`:并行探索-合并(系统管理分支 msg list 和结果汇总)
 
----
+**回溯机制**:
+- 未执行的步骤:直接修改 plan
+- 已执行的步骤:移除原始信息,替换为简短 Summary
 
-## 12. 计划管理:统一到 Step 树 vs 独立 TODO 列表
+**详细设计**:见 [`docs/context-management.md`](./context-management.md)
 
-### 问题
-Agent 的计划(TODO)应该如何管理?
+---
 
-### 方案对比
+## 12. 计划管理:独立 Goal Tree vs 统一到 Step
 
-| 方案 | 优点 | 缺点 |
-|------|------|------|
-| **独立 TODO 列表**(OpenCode 方式) | 简单,与执行分离 | 计划与执行无结构化关联 |
-| **统一到 Step 树** | 计划和执行在同一结构中,可追踪关联 | 稍复杂 |
+**日期**: 2026-02-04(更新)
 
 ### 决策
-**选择:统一到 Step 树**
+**选择:独立的 Goal Tree + 线性 Message List**
 
-**设计**:
-- `Step.status = "planned"` 表示计划中的步骤
-- `Step.step_type = "goal"` 表示计划项/目标
-- 模型通过 `step` 工具管理计划
+- **Goal Tree**:结构化的目标/计划(goal.json)
+- **Message List**:线性的执行记录
+- **关联**:每条 message 标记 goal_id
 
 **理由**:
-1. **统一模型**:不需要额外的 TODO 数据结构
-2. **可追踪**:执行步骤自动关联到计划项
-3. **可视化**:计划和执行在同一棵树中展示
-
-**参考**:OpenCode 的 `todowrite`/`todoread` 工具(`src/tool/todo.ts`)
+- 概念清晰:Plan 是"要做什么",Message 是"怎么做的"
+- 压缩精确:基于 goal 完成状态压缩对应的 messages
 
 ---
 
 ## 13. Summary 生成策略
 
-### 问题
-哪些 Step 需要生成 summary?
+**日期**: 2026-02-04(更新)
 
 ### 决策
-**选择:仅 evaluation 类型节点需要 summary**
+**选择:Goal 完成或放弃时生成 summary**
 
-**理由**:
-1. **避免浪费**:不是每个 step 都需要总结
-2. **有意义的总结**:evaluation 是对一组操作的评估,值得总结
-3. **节省资源**:减少 LLM 调用次数
-
-**实现**:
-- `Step.summary` 字段可选
-- 仅在 `step_type == "evaluation"` 时填充
-- `tool_call`/`tool_result` 不需要 summary,直接从 `data` 提取关键信息
+- `goal(done="summary")` - 正常完成
+- `goal(abandon="原因")` - 放弃(包含失败原因,避免重蹈覆辙)
 
 ---
 
 ## 14. Context 压缩策略
 
-### 问题
-当消息历史过长时,如何压缩?
+**日期**: 2026-02-04(更新)
 
 ### 决策
-**选择:基于树结构的分层压缩**
+**选择:基于 Goal 状态的增量压缩**
 
-**设计**:
-- **Todo 格式(简略)**:仅选择 `goal` 类型节点
-- **历史压缩格式(详细)**:选择 `goal` + `result` + `evaluation` 节点
-
-**触发时机**:
-- 正常情况:模型通过工具按需读取进度
-- 压缩时(context 超 70%):自动注入详细历史摘要
-
-**理由**:
-1. **信息分层**:不同用途需要不同详略程度
-2. **节点选择**:关键是选择哪些节点,而非每个节点展示什么
-3. **按需读取**:正常情况不浪费 context
+- Message 关联 goal_id
+- Goal 完成/放弃时,将详细 messages 替换为 summary message
 
 ---
 
-## 15. Step 元数据设置策略
+## 15. 并行探索机制
 
-### 问题
-Step 的元数据(step_type、description、parent_id 等)如何设置?
-
-### 方案对比
-
-| 方案 | 优点 | 缺点 |
-|------|------|------|
-| **LLM 显式输出** | 准确 | 需要 LLM 配合特定格式,增加复杂度 |
-| **系统自动推断** | 简单,不需要 LLM 额外输出 | 可能不够准确 |
-| **混合** | 平衡准确性和简洁性 | 需要明确划分 |
+**日期**: 2026-02-04
 
 ### 决策
-**选择:系统自动推断为主,显式工具调用为辅**
+**选择:explore 工具,基于 sub-agent 机制**
 
 **设计**:
-- **系统自动记录**:`step_id`、`parent_id`、`tokens`、`cost`、`duration_ms`、`created_at`
-- **系统推断**:`step_type`(基于输出内容)、`description`(从输出提取)
-- **显式声明**(通过 step 工具):`goal`、`evaluation`(summary)
+- `background`:LLM 概括的背景(可选,为空则继承全部历史)
+- `branches`:具体探索方向列表
 
-**step_type 推断规则**:
-1. 有工具调用 → `action`
-2. 调用 step 工具且 complete=True → `evaluation`
-3. 调用 step 工具且 plan 不为空 → `goal`
-4. 最终回复 → `response`
-5. 默认 → `thought`
-
-**理由**:
-1. **简化 LLM 负担**:不需要输出特定格式的元数据
-2. **step 工具是显式意图**:计划和评估通过工具明确声明
-3. **其他类型自动推断**:`thought`、`action`、`result`、`response` 可从输出内容判断
+**执行**:每个 branch 创建 sub-agent,串行执行,结果汇总返回。
 
 ---
 

+ 0 - 649
docs/step-tree.md

@@ -1,649 +0,0 @@
-# Step 树结构与 Context 管理
-
-> 本文档描述 Agent 执行过程的结构化记录、计划管理和 Context 压缩机制。
-
----
-
-## 设计目标
-
-1. **可视化**:支持执行路径的树状展示,可折叠/展开
-2. **计划管理**:统一表达"已执行"和"计划中"的步骤
-3. **Context 优化**:基于树结构压缩历史消息,节省 token
-
----
-
-## 核心设计:Step 树
-
-### Step 类型
-
-```python
-StepType = Literal[
-    # 计划相关
-    "goal",        # 目标/计划项(可以有子 steps)
-
-    # LLM 输出
-    "thought",     # 思考/分析(中间过程)
-    "evaluation",  # 评估总结(需要 summary)
-    "response",    # 最终回复
-
-    # 工具相关
-    "action",      # 工具调用(tool_call)
-    "result",      # 工具结果(tool_result)
-]
-```
-
-| 类型 | 来源 | 说明 |
-|------|------|------|
-| `goal` | LLM(通过 step 工具) | 设定目标/计划 |
-| `thought` | LLM | 中间思考,不产生工具调用 |
-| `evaluation` | LLM | 对一组操作的总结,需要 summary |
-| `response` | LLM | 最终给用户的回复 |
-| `action` | System | LLM 决定调用工具,系统记录 |
-| `result` | System | 工具执行结果 |
-
-### Step 状态
-
-```python
-Status = Literal[
-    "planned",      # 计划中(未执行)
-    "in_progress",  # 执行中
-    "completed",    # 已完成
-    "failed",       # 失败
-    "skipped",      # 跳过
-]
-```
-
-### Step 模型
-
-```python
-@dataclass
-class Step:
-    step_id: str
-    trace_id: str
-    step_type: StepType
-    status: Status
-    sequence: int
-
-    # 树结构(单父节点)
-    parent_id: Optional[str] = None
-
-    # 内容
-    description: str                      # 所有节点都有
-    data: Dict[str, Any] = field(default_factory=dict)
-
-    # 仅 evaluation 类型需要
-    summary: Optional[str] = None
-
-    # UI 优化字段
-    has_children: bool = False            # 是否有子节点
-    children_count: int = 0               # 子节点数量
-
-    # 执行指标
-    duration_ms: Optional[int] = None
-    cost: Optional[float] = None
-    tokens: Optional[int] = None
-
-    # 时间
-    created_at: datetime = field(default_factory=datetime.now)
-```
-
-**关键点**:
-- `parent_id` 是单个值(树结构),不是列表(DAG)
-- `summary` 仅在 `evaluation` 类型节点填充,不是每个节点都需要
-- `planned` 状态的 step 相当于 TODO item
-- `has_children` 和 `children_count` 用于前端 UI 优化(判断可展开、显示统计)
-
-**字段设计规则**:
-
-**顶层字段**(Step 类属性):
-- 所有(或大部分)step 都有的字段
-- 需要筛选/排序/索引的字段(如 tokens, cost, duration_ms)
-- 结构化、类型明确的字段
-
-**data 字段**(Dict):
-- step_type 特定的字段(不同类型有不同 schema)
-- 详细的业务数据(如 messages, content, arguments, output)
-- 可能很大的字段
-- 半结构化、动态的字段
-
-**data 字段 schema(按 step_type)**:
-- `thought` / `response`: model, messages, content, tool_calls
-- `action`: tool_name, arguments
-- `result`: tool_name, output, error
-- `memory_read`: experiences_count, skills_count
-- `goal`: 自定义(根据具体目标)
-
----
-
-## 树结构示例
-
-```
-Trace
-├── goal: "探索代码库" (completed)
-│   ├── thought: "需要先了解项目结构"
-│   ├── action: glob_files
-│   ├── result: [15 files...]
-│   ├── thought: "发现配置文件,需要查看内容"
-│   ├── action: read_file
-│   ├── result: [content...]
-│   └── evaluation: "主配置在 /src/config.yaml" ← summary
-│
-├── goal: "修改配置" (in_progress)
-│   ├── action: read_file
-│   └── result: [content...]
-│
-└── goal: "运行测试" (planned)
-```
-
-### Parent 关系规则
-
-| Step 类型 | parent 是谁 |
-|----------|------------|
-| `goal` | 上一个 `goal`(或 None) |
-| `thought` | 当前 `in_progress` 的 `goal` |
-| `action` | 当前 `in_progress` 的 `goal` |
-| `result` | 对应的 `action` |
-| `evaluation` | 所属的 `goal` |
-| `response` | 当前 `in_progress` 的 `goal`(或 None) |
-
----
-
-## 元数据设置
-
-### 系统自动记录
-
-以下字段由系统自动填充,不需要 LLM 参与:
-
-```python
-step_id: str          # 自动生成
-parent_id: str        # 根据当前 focus 的 goal 自动设置
-step_type: StepType   # 根据 LLM 输出推断(见下)
-sequence: int         # 递增序号
-tokens: int           # API 返回
-cost: float           # 计算得出
-duration_ms: int      # 计时
-created_at: datetime  # 当前时间
-```
-
-### Step 类型推断
-
-系统根据 LLM 输出内容自动推断类型,不需要显式声明:
-
-```python
-def infer_step_type(llm_response) -> StepType:
-    # 有工具调用 → action
-    if llm_response.tool_calls:
-        return "action"
-
-    # 调用了 step 工具且 complete=True → evaluation
-    if called_step_tool(llm_response, complete=True):
-        return "evaluation"
-
-    # 调用了 step 工具且 plan 不为空 → goal
-    if called_step_tool(llm_response, plan=True):
-        return "goal"
-
-    # 最终回复(无后续工具调用,对话结束)
-    if is_final_response(llm_response):
-        return "response"
-
-    # 默认:中间思考
-    return "thought"
-```
-
-### description 提取
-
-`description` 字段由系统从 LLM 输出中提取:
-
-| Step 类型 | description 来源 |
-|----------|-----------------|
-| `goal` | step 工具的 plan 参数 |
-| `thought` | LLM 输出的第一句话(或截断) |
-| `action` | 工具名 + 关键参数 |
-| `result` | 工具返回的 title 或简要输出 |
-| `evaluation` | step 工具的 summary 参数 |
-| `response` | LLM 输出的第一句话(或截断) |
-
----
-
-## 计划管理工具
-
-### step 工具
-
-模型通过 `step` 工具管理执行进度:
-
-```python
-@tool
-def step(
-    plan: Optional[List[str]] = None,     # 添加 planned goals
-    focus: Optional[str] = None,          # 切换焦点到哪个 goal
-    complete: bool = False,               # 完成当前 goal
-    summary: Optional[str] = None,        # 评估总结(配合 complete)
-):
-    """管理执行步骤"""
-```
-
-### 使用示例
-
-```python
-# 1. 创建计划
-step(plan=["探索代码库", "修改配置", "运行测试"])
-
-# 2. 开始执行第一个
-step(focus="探索代码库")
-
-# 3. [执行各种 tool_call...]
-
-# 4. 完成并切换到下一个
-step(complete=True, summary="主配置在 /src/config.yaml", focus="修改配置")
-
-# 5. 中途调整计划
-step(plan=["备份配置"])  # 追加新的 goal
-```
-
-### 状态变化
-
-```
-调用 step(plan=["A", "B", "C"]) 后:
-├── goal: "A" (planned)
-├── goal: "B" (planned)
-└── goal: "C" (planned)
-
-调用 step(focus="A") 后:
-├── goal: "A" (in_progress) ← 当前焦点
-├── goal: "B" (planned)
-└── goal: "C" (planned)
-
-调用 step(complete=True, summary="...", focus="B") 后:
-├── goal: "A" (completed)
-│   └── evaluation: "..." ← 自动创建
-├── goal: "B" (in_progress) ← 新焦点
-└── goal: "C" (planned)
-```
-
----
-
-## Context 管理
-
-### 信息分层
-
-不同用途需要不同的信息粒度:
-
-| 用途 | 选择哪些节点 | 详略程度 |
-|------|-------------|---------|
-| **Todo 列表** | 仅 `goal` 类型 | 简略:描述 + 状态 |
-| **历史压缩** | `goal` + `result` + `evaluation` | 详细:包含关键结果 |
-
-### Todo 格式(简略)
-
-```python
-def to_todo_string(tree: StepTree) -> str:
-    lines = []
-    for goal in tree.filter(step_type="goal"):
-        icon = {"completed": "✓", "in_progress": "→", "planned": " "}[goal.status]
-        lines.append(f"[{icon}] {goal.description}")
-    return "\n".join(lines)
-```
-
-输出:
-```
-[✓] 探索代码库
-[→] 修改配置
-[ ] 运行测试
-```
-
-### 历史压缩格式(详细)
-
-```python
-def to_history_string(tree: StepTree) -> str:
-    lines = []
-    for goal in tree.filter(step_type="goal"):
-        status_label = {"completed": "完成", "in_progress": "进行中", "planned": "待做"}
-        lines.append(f"[{status_label[goal.status]}] {goal.description}")
-
-        if goal.status == "completed":
-            # 选择关键结果节点
-            for step in goal.children():
-                if step.step_type == "result":
-                    lines.append(f"  → {extract_brief(step.data)}")
-                elif step.step_type == "evaluation":
-                    lines.append(f"  总结: {step.summary}")
-
-    return "\n".join(lines)
-```
-
-输出:
-```
-[完成] 探索代码库
-  → glob_files: 找到 15 个文件
-  → read_file(config.yaml): db_host=prod.db.com
-  总结: 主配置在 /src/config.yaml,包含数据库连接配置
-
-[进行中] 修改配置
-  → read_file(config.yaml): 已读取
-
-[待做] 运行测试
-```
-
-### 压缩触发
-
-```python
-def build_messages(messages: List, tree: StepTree) -> List:
-    # 正常情况:不压缩
-    if estimate_tokens(messages) < MAX_CONTEXT * 0.7:
-        return messages
-
-    # 超限时:用树摘要替代历史详情
-    history_summary = tree.to_history_string()
-    summary_msg = {"role": "assistant", "content": history_summary}
-
-    # 保留最近的详细消息
-    return [summary_msg] + recent_messages(messages)
-```
-
-### 按需读取
-
-模型可通过工具读取当前进度,而非每次都注入:
-
-```python
-@tool
-def read_progress() -> str:
-    """读取当前执行进度"""
-    return tree.to_todo_string()
-```
-
-**策略**:
-- 正常情况:模型通过 `read_progress` 按需读取(省 context)
-- 压缩时:自动注入详细历史摘要(保证不丢失)
-
----
-
-## 可视化支持
-
-树结构天然支持可视化:
-
-- **折叠**:折叠某个 `goal` 节点 → 隐藏其子节点
-- **展开**:展示子节点详情
-- **回溯**:`failed` 或 `skipped` 状态的分支
-- **并行**:同一 `goal` 下的多个 `action`(并行工具调用)
-
-### 边的信息
-
-可视化时,边(连接线)可展示:
-- 执行时间:`Step.duration_ms`
-- 成本:`Step.cost`
-- 简要描述:`Step.description`
-
----
-
-## 与 OpenCode 的对比
-
-| 方面 | OpenCode | 本设计 |
-|------|----------|--------|
-| 计划存储 | Markdown 文件 + Todo 列表 | Step 树(`planned` 状态) |
-| 计划与执行关联 | 无结构化关联 | 统一在树结构中 |
-| 进度读取 | `todoread` 工具 | `read_progress` 工具 |
-| 进度更新 | `todowrite` 工具 | `step` 工具 |
-| Context 压缩 | 无 | 基于树结构自动压缩 |
-
-**参考**:OpenCode 的实现见 `src/tool/todo.ts`、`src/session/prompt.ts`
-
----
-
-## Debug 工具
-
-### 实时查看 Step 树
-
-开发调试时,系统自动输出三种格式的 Step 树:
-
-```python
-from agent.debug import dump_tree, dump_markdown, dump_json
-
-# 1. 文本格式(简洁,带截断)
-dump_tree(trace, steps)  # 输出到 .trace/tree.txt
-
-# 2. Markdown 格式(完整,可折叠)
-dump_markdown(trace, steps)  # 输出到 .trace/tree.md
-
-# 3. JSON 格式(程序化分析)
-dump_json(trace, steps)  # 输出到 .trace/tree.json
-```
-
-**自动生成**:在 `AgentRunner` 的 debug 模式下,会自动生成 `tree.txt` 和 `tree.md` 两个文件。
-
-### 三种格式对比
-
-| 格式 | 文件大小 | 内容完整性 | 适用场景 |
-|-----|---------|----------|---------|
-| **tree.txt** | 小(1-2KB) | 截断长内容 | 快速预览、终端查看 |
-| **tree.md** | 中(5-10KB) | 完整内容 | 详细调试、编辑器查看 |
-| **tree.json** | 大(可能>10KB) | 完整结构化 | 程序化分析、工具处理 |
-
-### Markdown 格式特性
-
-**完整可折叠**:使用 HTML `<details>` 标签实现原生折叠
-
-```markdown
-<details>
-<summary><b>📨 Messages</b></summary>
-
-```json
-[完整的 messages 内容]
-```
-
-</details>
-```
-
-**智能截断**:
-- ✅ **文本内容**:完整显示,不截断
-- ✅ **工具调用**:完整显示 JSON schema
-- ✅ **图片 base64**:智能截断,显示大小和预览
-
-示例输出:
-```json
-{
-  "type": "image_url",
-  "image_url": {
-    "url": "<IMAGE_DATA: 2363.7KB, data:image/png;base64, preview: iVBORw0KGgo...>"
-  }
-}
-```
-
-### 查看方式
-
-```bash
-# 方式1:终端实时刷新(tree.txt)
-watch -n 0.5 cat .trace/tree.txt
-
-# 方式2:VS Code 打开(tree.md,支持折叠)
-code .trace/tree.md
-
-# 方式3:浏览器预览(tree.md)
-# 在 VS Code 中右键 → "Open Preview" 或使用 Markdown 预览插件
-```
-
-### tree.txt 输出示例
-
-```
-============================================================
- Step Tree Debug
- Generated: 2024-01-15 14:30:25
-============================================================
-
-## Trace
-  trace_id: abc123
-  task: 修改配置文件
-  status: running
-  total_steps: 5
-  total_tokens: 1234
-  total_cost: 0.0150
-
-## Steps
-
-├── [✓] goal: 探索代码库
-│   id: a1b2c3d4...
-│   duration: 1234ms
-│   tokens: 500
-│   cost: $0.0050
-│   data:
-│     description: 探索代码库
-│   time: 14:30:10
-│
-│   ├── [✓] thought: 需要先了解项目结构
-│   │   id: e5f6g7h8...
-│   │   data:
-│   │     content: 让我先看看项目的目录结构...
-│   │   time: 14:30:11
-│   │
-│   ├── [✓] action: glob_files
-│   │   id: i9j0k1l2...
-│   │   duration: 50ms
-│   │   data:
-│   │     tool_name: glob_files
-│   │     arguments: {"pattern": "**/*.py"}
-│   │   time: 14:30:12
-│   │
-│   └── [✓] result: 找到 15 个文件
-│       id: m3n4o5p6...
-│       data:
-│         output: ["src/main.py", "src/config.py", ...]
-│       time: 14:30:12
-│
-└── [→] goal: 修改配置
-    id: q7r8s9t0...
-    time: 14:30:15
-```
-
-**实现**:`agent/execution/tree_dump.py`
-
----
-
-## 实现位置
-
-- Step 模型:`agent/execution/models.py:Step`(已实现)
-- Trace 模型:`agent/execution/models.py:Trace`(已实现)
-- 存储接口:`agent/execution/protocols.py:TraceStore`(已实现)
-- 文件存储:`agent/execution/fs_store.py:FileSystemTraceStore`(已实现)
-- Debug 工具:`agent/execution/tree_dump.py`(已实现)
-- **Core Skill**:`agent/skills/core.md`(已实现)
-- step 工具:`agent/tools/builtin/step.py`(待实现)
-- read_progress 工具:`agent/tools/builtin/step.py`(待实现)
-- Context 压缩:`agent/context/compressor.py`(待实现)
-
----
-
-## 可视化 API
-
-### 设计目标
-
-为前端提供 Step 树的查询和实时推送接口,支持:
-1. 历史任务和进行中任务的查询
-2. 大型 Trace(上千 Step)的按需加载
-3. WebSocket 实时推送进行中任务的更新
-
-### 核心设计
-
-**简化原则**:消除"批次计算"和"同层完整性检查"的复杂逻辑,使用简单的层级懒加载
-
-**数据结构**:返回树形 JSON,前端无需自行构建
-
-**性能策略**:
-- 小型 Trace(<100 Steps):用 `/tree` 一次性返回完整树
-- 大型 Trace(>100 Steps):用 `/node/{step_id}` 按需懒加载
-- 进行中任务:WebSocket 推送增量更新
-
-### API 端点
-
-```
-GET  /api/traces                          # 列出 Traces(支持过滤)
-GET  /api/traces/{trace_id}               # 获取 Trace 元数据
-GET  /api/traces/{trace_id}/tree          # 获取完整树(小型 Trace)
-GET  /api/traces/{trace_id}/node/{step_id}  # 懒加载节点 + 子节点
-WS   /api/traces/{trace_id}/watch         # 监听进行中的更新
-```
-
-### 懒加载核心逻辑
-
-```python
-async def get_node_with_children(
-    store: TraceStore,
-    step_id: Optional[str],  # None = 根节点
-    trace_id: str,
-    expand: bool = False,
-    max_depth: int = 1
-) -> dict:
-    # 1. 获取当前层节点
-    if step_id is None:
-        steps = await store.get_trace_steps(trace_id)
-        current_nodes = [s for s in steps if s.parent_id is None]
-    else:
-        current_nodes = await store.get_step_children(step_id)
-
-    # 2. 构建响应
-    result = []
-    for step in current_nodes:
-        node = step.to_dict()
-        node["children"] = []
-
-        # 3. 递归加载子节点(可选)
-        if expand and current_depth < max_depth:
-            children = await store.get_step_children(step.step_id)
-            if children:
-                node["children"] = [...]  # 递归
-
-        result.append(node)
-
-    return result
-```
-
-**品味评分**:🟢 好品味(逻辑清晰,< 30 行,无特殊情况)
-
-### WebSocket 事件
-
-```json
-// 新增 Step
-{"event": "step_added", "step": {...}}
-
-// Step 更新
-{"event": "step_updated", "step_id": "...", "updates": {...}}
-
-// Trace 完成
-{"event": "trace_completed", "trace_id": "..."}
-```
-
-### 实现位置(待定)
-
-两种方案:
-
-**方案 1:独立 API 模块**(推荐,如果未来需要多种 API)
-```
-agent/api/
-├── server.py           # FastAPI 应用
-├── routes/
-│   ├── traces.py       # Step 树路由
-│   └── websocket.py    # WebSocket 推送
-└── schemas.py          # Pydantic 模型
-```
-
-**方案 2:Step 树专用模块**(推荐,如果只用于 Step 树可视化)
-```
-agent/step_tree/
-├── api.py              # FastAPI 路由
-├── websocket.py        # WebSocket 推送
-└── server.py           # 独立服务入口
-```
-
-决策依据:
-- 如果系统未来需要提供多种 API(Experience 管理、Agent 控制等)→ 方案 1
-- 如果 API 仅用于 Step 树可视化 → 方案 2
-
-**详细设计**:参见 `/Users/sunlit/.claude/plans/starry-yawning-zebra.md`
-
----
-
-## 未来扩展
-
-- 重试原因、重试次数、是否降级/兜底
-- 为什么选择某个动作、是否触发了 skills、系统 prompt 中的策略
-- 数据库持久化(PostgreSQL/Neo4j)
-- 递归查询优化(PostgreSQL CTE)