对比 OpenCode、Codex 和 Gemini-cli 三个项目的 context 管理方案
| 维度 | OpenCode | Codex | Gemini-cli | |
|---|---|---|---|---|
| 核心数据结构 | 线性 Message List | ContextManager (Vec) | Content[] (双版本) | |
| 消息历史版本 | 单一版本 | 单一版本 + GhostSnapshot | 精选版本 + 完整版本 | |
| 分层设计 | 无 | 无 | ✓ 三层: Global → Environment → JIT | |
| Plan 管理 | goal.json (计划中) + plan.md (参考) | SQLite + TodoListItem | 无 Plan 机制 | |
| 存储格式 | Storage Key-Value | JSONL + SQLite 混合 | JSON + 文本文件 | |
| 并发控制 | 未明确 | Arc + 文件锁 | Promise并发限制 |
| 项目 | 估算策略 | 精度 | 实现位置 |
|---|---|---|---|
| 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() |
关键差异:
| 项目 | 限制类型 | 阈值定义 |
|---|---|---|
| OpenCode | 删除阈值 | PRUNE_MINIMUM = 20,000, PRUNE_PROTECT = 40,000 |
| Codex | 模型限制 | 依赖模型配置,无固定值 |
| Gemini-cli | 压缩阈值 | 默认 50% 模型限制 (DEFAULT_COMPRESSION_TOKEN_THRESHOLD = 0.5) |
┌─────────────────┬──────────────────────────┬─────────────────────┐
│ OpenCode │ Codex │ Gemini-cli │
├─────────────────┼──────────────────────────┼─────────────────────┤
│ 删除旧工具输出 │ 前缀+后缀保留,中间截断 │ 反向token预算 │
│ 保护最近2轮turns │ 插入省略标记 │ 最近工具完整保留 │
│ 不删除"skill"工具│ 保证UTF-8边界完整性 │ 旧工具仅保留30行 │
└─────────────────┴──────────────────────────┴─────────────────────┘
Gemini-cli 的反向预算策略 (最独特):
// 从最新消息往回遍历,为每条工具输出分配token预算
// 优先保留最近的,旧的按需截断
COMPRESSION_FUNCTION_RESPONSE_TOKEN_BUDGET = 50,000;
COMPRESSION_TRUNCATE_LINES = 30;
| 项目 | 触发时机 | 方式 |
|---|---|---|
| OpenCode | 事后 (context满时) | 被动压缩 |
| Codex | 超过模型窗口时 | 自动压缩 |
| Gemini-cli | 主动 (达到50%阈值) + 手动 (/compress) | 混合方式 |
阶段1: Prune (清理旧工具输出)
├─ 从后向前遍历
├─ 跳过最后2轮
├─ 跳过已有summary的消息
└─ 删除量 > PRUNE_MINIMUM 时执行
阶段2: Full Compaction (上下文总结)
├─ 创建summary=true的assistant消息
├─ 调用"compaction"专用agent
└─ 提示词: "Provide a detailed prompt for continuing..."
触发: run_inline_auto_compact_task()
├─ 生成摘要前缀 (SUMMARY_PREFIX)
├─ 使用SUMMARIZATION_PROMPT
├─ 保留GhostSnapshot (幽灵快照)
└─ 替换历史记录为CompactedItem
GhostSnapshot (Codex独有):
Phase 1: 历史分割
├─ 保留最后30% (COMPRESSION_PRESERVE_THRESHOLD)
└─ 压缩前70%
Phase 2: 双重总结验证 ⭐ (独特)
├─ 第1次: 生成 <state_snapshot>
├─ 第2次: 自我批评 ("Did you omit any...")
└─ 生成改进版本或确认原版本
Phase 3: 输出验证
├─ 检查压缩后token数 < 原token数
└─ 失败则保持原历史
双重验证的价值:
// 第一次生成
"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."
| 项目 | 压缩失败处理 | 结果存储 |
|---|---|---|
| OpenCode | 标记为已compacted | 替换为summary message |
| Codex | 保留GhostSnapshot | CompactedItem + replacement_history |
| Gemini-cli | 回退原历史 ⭐ | 成功时替换,失败时保持原状 |
.trace/{trace_id}/
├── goal.json # Goal Tree (结构化plan)
├── messages.jsonl # 消息记录 (含 goal_id)
└── meta.json # Trace 元数据
~/.codex/
├── history.jsonl # 全局消息历史
├── sessions/
│ ├── rollout-{timestamp}-{uuid}.jsonl # 会话回滚文件
│ └── ...
└── state.db # SQLite状态数据库
关键特性:
O_APPEND 标志~/.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目录)
└─ 避免加载不相关上下文
| 项目 | 加载时机 | 策略 | 并发控制 | |
|---|---|---|---|---|
| OpenCode | 会话启动/恢复 | 按需加载 | 未明确 | |
| Codex | 启动时 | lookup(log_id, offset) | Arc + 文件锁 | |
| Gemini-cli | 分层+JIT ⭐ | 全局(启动) + 环境(会话) + JIT(访问) | Promise并发限制 (10/20) |
| 项目 | 关联方式 | 可编辑性 | 可视化 |
|---|---|---|---|
| OpenCode (参考) | 无结构化关联 | plan.md 可直接编辑 | 基础 |
| OpenCode (计划) | message.goal_id | 通过 goal 工具 | 增强 (树形+步骤) |
| Codex | TodoItem 在 ResponseItem 中 | 通过模型更新 | 基础 |
| Gemini-cli | - | - | - |
// Agent Mode
- primary: 主代理,执行工具
- subagent: 子代理,独立context
// 内置 Sub-Agents
- general: 通用代理,可并行执行
- explore: 代码探索,仅查询工具
- compaction: 上下文总结
执行流程:
1. 创建 SubtaskPart
2. 子代理独立处理 (独立 message list)
3. 结果汇总: "The following tool was executed by the user"
@tool
def explore(
question: str, # 探索要回答的问题
branches: List[str], # 探索方向 (2-4个)
) -> str:
"""并行探索多个方向,汇总结果"""
执行流程:
1. 为每个探索方向创建独立的 Sub-Trace(完整的 Trace 结构)
2. 并行执行所有 Sub-Traces(使用 asyncio.gather)
3. 收集每个 Sub-Trace 的结论
4. 返回汇总结果给主 Trace
无明确 Sub-Agent 机制,但有:
无 Sub-Agent 机制,但有:
| 特性 | OpenCode (参考) | OpenCode (计划) | Codex | Gemini-cli |
|---|---|---|---|---|
| 并行探索 | Sub-agent 手动管理 | explore 工具自动汇总 ⭐ | 无 | 工具级并发 |
| Context 隔离 | ✓ (独立 message list) | ✓ (独立 message list) | - | - |
| 结果汇总 | 手动 | 自动 (返回 markdown) | - | - |
| 适用场景 | 大任务隔离 | 多方案评估 | - | 工具执行 |
限制: 有限的回溯能力
- 无精确状态保存
- 依赖压缩后的摘要
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
优势:
// GhostSnapshot: 保留被压缩部分的引用
pub struct CompactedItem {
message: String,
replacement_history: Option<Vec<ResponseItem>>,
}
回溯能力:
enum CompressionStatus {
COMPRESSED,
COMPRESSION_FAILED_INFLATED_TOKEN_COUNT, // 压缩反而增加
COMPRESSION_FAILED_EMPTY_SUMMARY, // 摘要为空
NOOP,
}
回溯能力:
ConversationRecord 保存所有消息| 项目 | 回溯粒度 | 状态保存 | 失败处理 | 历史查看 |
|---|---|---|---|---|
| OpenCode (参考) | 粗粒度 | 摘要 | 有限 | 基础 |
| OpenCode (计划) | goal级别 ⭐ | goal.summary + abandon原因 | 精确压缩 | goal树 + 步骤 |
| Codex | 用户turn级别 | GhostSnapshot | GhostSnapshot引用 | 完整回滚文件 |
| Gemini-cli | 消息级别 | ConversationRecord | 自动回退 | 会话记录文件 |
| 项目 | 核心理念 | 优势场景 |
|---|---|---|
| OpenCode | 结构化计划驱动 | 复杂任务,需要回溯和探索 |
| Codex | 简单高效,成熟稳定 | 通用编码助手,快速响应 |
| Gemini-cli | 分层智能,高保真保留 | 多项目管理,长期会话 |
| 技术点 | OpenCode | Codex | Gemini-cli | |
|---|---|---|---|---|
| 语言 | TypeScript | Rust | TypeScript | |
| 存储 | Storage KV | JSONL + SQLite | JSON + 文本 | |
| 并发 | 未明确 | Arc + 文件锁 | Promise限制 | |
| token估算 | 未详述 | bytes/4 | 启发式 (区分类型) | |
| 压缩策略 | 两阶段 | 内联自动 | 三相智能 |
| 方面 | 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更简洁 |
可借鉴:
| 方面 | 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胜 ⭐ |
可借鉴:
结构化 Plan (goal.json)
执行与 Plan 的强关联 (goal_id)
增量压缩 (goal 完成/放弃时)
explore 工具 (并行探索自动汇总)
精确回溯 (abandon + 状态流转)
存储可靠性 (借鉴 Codex)
Context 分层 (借鉴 Gemini-cli)
压缩质量 (借鉴 Gemini-cli)
主动压缩 (借鉴 Gemini-cli)
工具输出管理 (借鉴 Gemini-cli)
Token 估算 (借鉴 Gemini-cli)
用户友好性 (借鉴 Codex)
| 能力维度 | 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 |
评分说明: ⭐ 基础, ⭐⭐ 良好, ⭐⭐⭐ 优秀
OpenCode 的核心优势 (保留并强化):
应借鉴的关键特性:
综合方案 (OpenCode + 借鉴):
OpenCode 计划方案
├─ 保留: goal.json, explore工具, goal-based回溯
├─ 增强压缩: Gemini-cli的双重验证 + 主动压缩
├─ 增强存储: Codex的原子写入 + 并发安全
├─ 增强Context: Gemini-cli的三层分层 + JIT加载
└─ 增强UX: Codex的GhostSnapshot
这将是一个结构化驱动 + 智能压缩 + 可靠存储 + 分层Context的综合方案,集三家之长! 🎯