# 横向对比 · Codex 与 Grok Build、OpenCode

> 把 Codex 和站里另外两本源码解读的对象——Rust 写的 Grok Build、TypeScript 加 Effect 写的 OpenCode——并排比较。三家都让界面只当客户端，但协议各选各的：自定的 app-server v2、开放的 ACP、HTTP；安全上一家给每条命令套沙箱，一家把整个进程关进沙箱，一家不设沙箱；Codex 只说 Responses API，另两家追求提供方中立。关于 Grok Build 与 OpenCode 的说法都出自站内专栏并附链接。

- 作者：David（道雾轩）
- 专栏：Codex 源码解读（https://daiw.net/manual/codex-source.md）
- 最后更新：2026-09-29
- 原文：https://daiw.net/manual/codex-source/codex-vs-others
- 转载与引用：请注明出处并附原文链接（https://daiw.net/about/copyright）

# 横向对比 · Codex 与 Grok Build、OpenCode

站里拆过的开源编码 agent 里，[《Grok Build 源码解读》](https://daiw.net/manual/grok-build)与[《OpenCode 源码解读》](https://daiw.net/manual/opencode)是和本专栏最适合对照的两本：一本同样用 Rust，一本用 TypeScript，三份代码的基准日期相差不到三周。这一篇里关于 Grok Build 与 OpenCode 的每一条说法都出自那两本专栏，并链接到出处；它们的基准分别是 2026-09-09 的提交 `37949780` 与同日的 `v1.18.30`，那两本之后若有更新，以它们为准。Codex 这边的说法出自本专栏各篇。

## 基本面

| | Codex | [Grok Build](https://daiw.net/manual/grok-build) | [OpenCode](https://daiw.net/manual/opencode) |
| --- | --- | --- | --- |
| 语言 | Rust | Rust | TypeScript 加 Effect，跑在 Bun 上 |
| 组织 | Cargo workspace，显式成员 153 个 | Cargo workspace，`crates/` 下 96 个 crate | monorepo，36 个 workspace 包 |
| 代码量（不含测试） | 约 82 万行 | 约 85 万行 | 近 50 万行，其中 agent 相关约 17 万行 |
| 许可 | Apache-2.0 | Apache-2.0（[读完之后](https://daiw.net/manual/grok-build/whats-next)） | MIT（[OpenCode 是什么](https://daiw.net/manual/opencode/what-is-opencode)） |
| 分发 | npm 包里的 `codex.js` 挑出本平台的原生二进制（[Codex CLI 是什么](https://daiw.net/manual/codex-source/what-is-codex)） | 二进制 `xai-grok-pager`，官方安装包装成 `grok`（[专栏首页](https://daiw.net/manual/grok-build)） | npm 包里的小脚本挑出 Bun 编译的独立可执行文件（[OpenCode 是什么](https://daiw.net/manual/opencode/what-is-opencode)） |

三家体量相当，Codex 的成员数最多，很大一部分来自内核周边刻意拆出的小 crate（见 [workspace 全景](https://daiw.net/manual/codex-source/workspace-map)）。

## 界面只是客户端：三种协议

```mermaid
flowchart LR
  subgraph CX[Codex]
    C1[TUI] -->|Unix socket 连守护进程| C4[app-server<br/>v2 JSON-RPC]
    C2[codex exec] -->|进程内| C4
    C3[IDE 扩展、Python SDK] -->|stdio| C4
    C4 --> C5[codex-core<br/>ThreadManager 与 run_turn]
  end
  subgraph GB[Grok Build]
    G1[TUI] -->|ACP 事件| G4[SessionActor<br/>process_conversation_turn]
    G2[headless] -->|进程内 ACP 通道| G4
    G3[编辑器等 ACP 客户端] -->|stdio、WebSocket、中继、leader| G5[MvpAgent] --> G4
  end
  subgraph OC[OpenCode]
    O1[默认 TUI] -->|SDK 请求经 RPC 隧道进 Worker| O4[Effect HttpApi 服务端]
    O2[Web、桌面端、attach] -->|HTTP 与 SSE| O4
    O4 --> O5[SessionPrompt<br/>runLoop]
  end
```

**Codex** 自定协议，并把它做成产品的中心。TUI、`codex exec`、IDE 扩展、桌面端与 Python SDK 都经 app-server 的 v2 JSON-RPC 访问内核，TUI 的依赖里没有 `codex-core`；协议是双向的，审批、提问是服务端反过来发给客户端的请求；交互式 TUI 默认连本机的守护进程，几个客户端共享同一批线程（[app-server](https://daiw.net/manual/codex-source/app-server-architecture)、[守护进程与传输](https://daiw.net/manual/codex-source/daemon-and-transport)）。

**Grok Build** 用开放的 ACP，再以 `x.ai/*` 方法扩展。stdio、WebSocket 服务、经 grok.com 的中继、leader 四种传输汇入同一个 `MvpAgent`，编辑器集成走的是 stdio；leader 模式让多个客户端共享一个后端进程，崩溃后还能重放恢复（[ACP](https://daiw.net/manual/grok-build/acp)）；headless 在进程内拉起 agent、开一条 ACP 通道（[Headless 模式](https://daiw.net/manual/grok-build/headless)）；TUI 收的也是来自 `SessionActor` 的 ACP 事件（[TUI 架构](https://daiw.net/manual/grok-build/tui-architecture)）。

**OpenCode** 用 HTTP。默认的 `opencode` 把服务端放进一个 Bun Worker 线程，TUI 只发 SDK 请求、订阅事件；加上 `--port` 或改用 `serve`、`web` 就真正对外监听，Web 与桌面端走同一套 API（[专栏首页](https://daiw.net/manual/opencode)、[启动链路](https://daiw.net/manual/opencode/startup)）。它的 `session.prompt` 要等整轮对话结束才返回，另有立即返回的 `prompt_async`（[一条消息的生命周期](https://daiw.net/manual/opencode/message-lifecycle)）；Codex 的 `turn/start` 只有“立即返回”一种形态。

三家殊途同归：界面只是客户端，进度全靠事件。区别在协议归谁。Codex 自定协议，审批往返、线程管理、配置读写都能按自己的需要设计，代价是每个编辑器都得专门接入；Grok Build 借开放协议，编辑器现成就能接；OpenCode 选 HTTP，任何能发请求的程序都能驱动它。把后端托管成共享的后台进程，三家也都做了：Codex 的 app-server 守护进程、Grok Build 的 leader 模式、OpenCode 新 CLI 在后台拉起并复用的 V2 服务（[新 CLI 与守护进程](https://daiw.net/manual/opencode/cli-daemon)）。

## 逐项对比

### 内核

| | Codex | Grok Build | OpenCode |
| --- | --- | --- | --- |
| 一轮怎么转 | `RegularTask` 反复调用 `run_turn`，模型还要工具或来了插话就再采样；可重试的流错误在采样函数里就地退避（[run_turn](https://daiw.net/manual/codex-source/turn-loop)） | 单线程 actor `SessionActor` 跑 `process_conversation_turn`，外加 TodoGate、动作停滞检测与瞬时故障重投（[主循环](https://daiw.net/manual/grok-build/agent-loop)） | 每个会话一个 `Runner` 跑 `runLoop`，每一步调一次 AI SDK 的 `streamText`，工具结果靠下一步回灌（[一条消息的生命周期](https://daiw.net/manual/opencode/message-lifecycle)） |
| 模型协议 | 只说 Responses API，经 HTTP 的 SSE 或 WebSocket（[模型客户端](https://daiw.net/manual/codex-source/model-client)） | Chat Completions、Messages、Responses 三种流归一成 `SamplingEvent`（[三种 API 流](https://daiw.net/manual/grok-build/api-streams)） | models.dev 目录加 Vercel AI SDK，实验开关下改走自研的 `@opencode-ai/llm`（[模型调用层](https://daiw.net/manual/opencode/llm-bridge)） |
| 上下文压缩 | 默认在窗口的 90% 触发；OpenAI 等由服务端返回不透明的压缩条目，其余本地写交接摘要；新历史基本只剩用户消息与摘要（[上下文压缩](https://daiw.net/manual/codex-source/compaction)） | 默认 85% 触发；把整盘对话总结成九段式摘要再重建历史，不留尾巴（[上下文压缩](https://daiw.net/manual/grok-build/compaction)） | 隐藏的 compaction agent 写结构化摘要，按预算原样保留最近几轮；修剪旧工具输出默认关闭（[上下文压缩](https://daiw.net/manual/opencode/compaction)） |
| Plan 模式 | 协作模式，“不改文件”只写在提示词里，代码只拦下 `update_plan`（[任务类型与 Plan 模式](https://daiw.net/manual/codex-source/tasks-and-plan-mode)） | 工具层硬拦：除 `plan.md` 外的编辑一律拒绝，放行一切的 yolo 下也成立（[Plan mode](https://daiw.net/manual/grok-build/plan-mode)） | `plan` agent 的 `edit` 权限一律 `deny`，只放行计划文件（[OpenCode 是什么](https://daiw.net/manual/opencode/what-is-opencode)） |

### 工具与安全

| | Codex | Grok Build | OpenCode |
| --- | --- | --- | --- |
| 工具契约 | `ToolExecutor` 把 spec 与处理器绑在一起；工具表每次采样前重建；一把读写锁管并行（[工具系统总览](https://daiw.net/manual/codex-source/tool-architecture)） | 一个类型实现 `Tool` 与 `ToolMetadata` 两个 trait，schema 从参数类型生成，执行结果是流（[工具契约](https://daiw.net/manual/grok-build/tool-contract)） | `Tool.define` 给出说明、Effect Schema 参数与执行体，每轮由 `SessionTools.resolve` 包上插件钩子与权限询问（[工具契约与注册表](https://daiw.net/manual/opencode/tool-contract)） |
| 改文件 | 只有 `apply_patch`：按文件分段、不带行号的精简 diff（[apply_patch](https://daiw.net/manual/codex-source/apply-patch)） | search/replace、concise、hashline 三种格式（[三种编辑格式](https://daiw.net/manual/grok-build/edit-formats)），另有逐字照搬 Codex 规范的 `apply_patch`（[与别家 agent 互通](https://daiw.net/manual/grok-build/agent-interop)） | `edit` 的九级匹配级联；GPT 系列模型改用同一种补丁格式的 `apply_patch`（[文件工具](https://daiw.net/manual/opencode/file-tools)） |
| 执行命令 | unified exec：`exec_command` 起进程，`write_stdin` 续写或轮询，进程可以跨轮复用（[执行命令](https://daiw.net/manual/codex-source/unified-exec)） | 非流式与流式两条路径，跨平台杀进程树，还有一个完整的 PTY 控制器（[Shell 执行与 PTY](https://daiw.net/manual/grok-build/shell-and-pty)） | 工具 ID 仍叫 `bash`；tree-sitter 拆出每条子命令，按命令前缀记“总是允许”（[Shell 工具](https://daiw.net/manual/opencode/shell-tool)） |
| 权限与审批 | 沙箱与审批是两个正交的量；Starlark 执行策略；审批请求依次交给 hook、自动审查、用户（[权限模型](https://daiw.net/manual/codex-source/permissions-model)、[审批流程](https://daiw.net/manual/codex-source/approvals-flow)） | 权限管理器按判定瀑布决定放行、询问或拒绝；打开 `auto_allow_bash`（默认关）后，沙箱激活时 bash 调用可自动放行（[workspace 抽象](https://daiw.net/manual/grok-build/workspace-abstraction)、[沙箱与权限](https://daiw.net/manual/grok-build/sandbox-permissions)） | 规则取最后一条匹配；默认全部放行，只对疑似死循环、工作区外的目录与 `.env` 询问（[权限系统](https://daiw.net/manual/opencode/permission)） |
| 沙箱 | 每条命令单独套：macOS 现拼 Seatbelt 策略，Linux 用 bubblewrap 加 seccomp，Windows 用写受限令牌、elevated 模式另建专门的沙箱用户；联网经本地代理（[权限模型](https://daiw.net/manual/codex-source/permissions-model)、[网络代理](https://daiw.net/manual/codex-source/network-proxy)） | 整个进程一次性关进去：Landlock 或 Seatbelt 在启动时施加、不可撤销，子进程网络另用 seccomp 封（[沙箱与权限](https://daiw.net/manual/grok-build/sandbox-permissions)） | 不设沙箱，Shell 从来不是沙箱（[收尾复盘](https://daiw.net/manual/opencode/recap)） |

### 扩展

| | Codex | Grok Build | OpenCode |
| --- | --- | --- | --- |
| Skills | 扫描多个根目录，清单按预算进上下文，`$名字` 显式注入；要不要自动选用由模型自己决定（[Skills](https://daiw.net/manual/codex-source/skills)） | `SKILL.md` 按作用域排优先级，带 glob 门控的技能碰到匹配文件才现身（[Skills](https://daiw.net/manual/grok-build/skills)） | 提示词里放一份 `<available_skills>` 清单，正文由 `skill` 工具取回（[Agent、命令与 Skills](https://daiw.net/manual/opencode/agents-and-commands)） |
| Hooks | `ClaudeHooksEngine`，12 个事件；处理器是命令或 MCP 工具；按内容哈希记账，审阅信任后才运行（[Hooks](https://daiw.net/manual/codex-source/hooks)） | 16 个事件名；处理器是命令或 HTTP 回调；钩子自身失败时放行；认 Claude 与 Cursor 的事件名（[Hooks](https://daiw.net/manual/grok-build/hooks)） | 由插件提供：进程内模块返回 `tool.execute.before` 这类钩子，按加载顺序串行调用（[插件系统](https://daiw.net/manual/opencode/plugins)） |
| 插件 | 打包 skills、MCP、应用连接器与 hooks，装进版本化缓存；装上不等于信任，带来的每条 hook 仍要单独审阅（[插件](https://daiw.net/manual/codex-source/plugins)） | 打包 skills、commands、agents、hooks、MCP、LSP；信任按作用域判定，未受信的插件只露出元数据（[插件](https://daiw.net/manual/grok-build/plugins-manifest)） | 进程内的 JS 与 TS 模块，来自内置、配置声明的 npm 包或目录发现，提供钩子、自定义工具与 TUI 插件（[插件系统](https://daiw.net/manual/opencode/plugins)） |
| MCP | stdio 与可流式 HTTP，带 OAuth 与 elicitation；工具进 `mcp__服务器` 命名空间，支持工具搜索的模型上默认延迟加载，由 `tool_search` 取回（[MCP 工具调用](https://daiw.net/manual/codex-source/mcp-tool-calls)） | 三种传输、单飞握手、OAuth；模型只看到 `search_tool` 与 `use_tool` 两个元工具（[MCP 客户端](https://daiw.net/manual/grok-build/mcp-client)、[MCP 工具进注册表](https://daiw.net/manual/grok-build/mcp-registry)） | 本地走 stdio，远程先试可流式 HTTP 再退回 SSE；工具名是“服务器名_工具名”；客户端能力只开了 `roots`（[MCP 集成](https://daiw.net/manual/opencode/mcp)） |
| 多 agent | 子代理是同一个 `ThreadManager` 里的另一条线程；V1、V2 两代工具，V2 经信箱交回结论（[多 agent](https://daiw.net/manual/codex-source/multi-agent)） | 协调器把每个子代理放到独立的 OS 线程上，默认后台跑；能力模式按 `ToolKind` 裁掉工具；可选 worktree 隔离（[Subagents](https://daiw.net/manual/grok-build/subagents)） | `task` 工具派生子会话，跑完整个主循环后交回最后一段文字；深度默认 1（[子代理、待办与提问](https://daiw.net/manual/opencode/task-and-todo)） |

### 会话与界面

| | Codex | Grok Build | OpenCode |
| --- | --- | --- | --- |
| 会话持久化 | 每个线程一个只追加的 rollout JSONL 为真相，SQLite 是可以重建的投影（[会话持久化](https://daiw.net/manual/codex-source/rollout-and-storage)） | `updates.jsonl` 是回放的真相源，`chat_history.jsonl` 存模型面的历史；SQLite 只做全文检索缓存（[Sessions](https://daiw.net/manual/grok-build/sessions)） | SQLite 加 Drizzle；事件、序号与投影在同一个事务里写（[存储](https://daiw.net/manual/opencode/storage)） |
| 终端界面 | 基于 ratatui，`codex-tui` 约 20.6 万行（不含测试）；可以把定稿的历史交给终端回滚，也可以全屏；最高 120 帧每秒（[TUI 架构](https://daiw.net/manual/codex-source/tui-architecture)） | 基于 ratatui，pager 与 render 合计约 26.5 万行（不含测试）；Elm 式的 Action、dispatch、Effect 三段（[TUI 架构](https://daiw.net/manual/grok-build/tui-architecture)） | 独立的 `@opencode-ai/tui` 包，OpenTUI 加 SolidJS；事件每 16 毫秒攒一批（[终端界面](https://daiw.net/manual/opencode/tui)） |
| 无界面与 SDK | `codex exec` 内嵌 app-server；TypeScript SDK 每轮起一次 `codex exec`，Python SDK 经 stdio 讲 v2（[codex exec 与 SDK](https://daiw.net/manual/codex-source/exec-and-sdk)） | `grok -p` 复用同一个 `SessionActor`，四种输出格式（[Headless 模式](https://daiw.net/manual/grok-build/headless)） | `opencode run` 与 `serve`；旧 SDK 由 OpenAPI 规格生成（[SDK、协议与客户端](https://daiw.net/manual/opencode/sdk-and-protocol)） |
| 与别家互通 | `/import` 把 Claude Code 与 Cursor 的配置一次性改写成 Codex 的格式，只补不覆盖（[从别的 agent 迁移](https://daiw.net/manual/codex-source/external-agent-migration)） | 运行时直接读 `CLAUDE.md`、`.cursor/rules` 与 Claude 的 hooks；有 Codex 命名空间的 `apply_patch`，`/resume` 能接手 Codex 的会话（[与别家 agent 互通](https://daiw.net/manual/grok-build/agent-interop)、[Sessions](https://daiw.net/manual/grok-build/sessions)） | 技能扫描包括 `~/.claude/`；内置的提供方插件里有 Codex（[Agent、命令与 Skills](https://daiw.net/manual/opencode/agents-and-commands)、[插件系统](https://daiw.net/manual/opencode/plugins)） |

## 几处值得细看的差别

**安全模型差得最远。** Codex 的沙箱是按命令套的：每条命令单独包进 macOS 的 `sandbox-exec`、Linux 的 bubblewrap 或 Windows 的受限令牌里，所以沙箱拒绝之后还能按审批策略请求提权，只把这一条命令重跑一次（[审批流程](https://daiw.net/manual/codex-source/approvals-flow)）。Grok Build 在启动时把整个进程关进笼子、之后不可撤销，恢复会话时沙箱档位必须与创建时一致；换来的是笼子焊死之后，权限层可以少问几次（[沙箱与权限](https://daiw.net/manual/grok-build/sandbox-permissions)、[Sessions](https://daiw.net/manual/grok-build/sessions)）。OpenCode 不设沙箱，边界全靠权限规则，而规则默认全部放行（[收尾复盘](https://daiw.net/manual/opencode/recap)）。三种选择背后是三种假设：每条命令都不可信、整个 agent 都不可信、由用户自己决定信任到哪。

**同一个“先规划”，落在三层。** Codex 的 Plan 模式落在提示词层，Grok Build 落在工具层，OpenCode 落在权限层。Grok Build 与 OpenCode 至少在编辑工具上由代码拦住，Codex 要做到硬性只读，得另外靠沙箱与审批设置。

**模型接入是深与广的取舍。** Codex 只说 Responses API，于是能放心依赖服务端：远程压缩返回不透明条目，WebSocket 只发增量输入，推理以密文往返，一轮之内带着粘性路由令牌（[模型客户端](https://daiw.net/manual/codex-source/model-client)、[上下文压缩](https://daiw.net/manual/codex-source/compaction)）；代价是别家后端也得说同一种协议，Ollama、LM Studio、Amazon Bedrock 都不例外（[模型目录与提供方](https://daiw.net/manual/codex-source/models-and-providers)）。Grok Build 把三种 API 流归一成一套中立的事件（[三种 API 流](https://daiw.net/manual/grok-build/api-streams)）；OpenCode 靠 models.dev 与 AI SDK 做到提供方中立，再用近两千行的 `provider/transform.ts` 抹平各家差异（[专栏首页](https://daiw.net/manual/opencode)）。

**三家都把日志当真相，但压缩时留下的东西不同。** Codex 的 rollout 与 Grok Build 的 `updates.jsonl` 都是只追加的回放真相，数据库只是派生物；OpenCode 把事件、序号与投影放进同一个 SQLite 事务，数据库里的旧消息一条不删，只是模型看不到（[收尾复盘](https://daiw.net/manual/opencode/recap)）。压缩时，Codex 基本只留用户消息原文，Grok Build 一条不留、全靠九段式摘要，OpenCode 按预算保留最近几轮原文。

**兼容别家：改写还是原地读。** Codex 选择一次性迁移，把 Claude Code 与 Cursor 的配置改写成自己的格式；Grok Build 在运行时直接读别家的约定，连 Codex 的补丁格式与会话都认；OpenCode 扫描 `~/.claude/` 下的技能。有意思的是 Codex 的 `apply_patch` 格式成了一种公共语言：Grok Build 逐字照搬了它的规范，OpenCode 给 GPT 系列模型提供的也是同一种补丁格式（[与别家 agent 互通](https://daiw.net/manual/grok-build/agent-interop)、[文件工具](https://daiw.net/manual/opencode/file-tools)）。

能力谱上，三家几乎重合：AGENTS.md 一类的项目指令、Skills、钩子、MCP、子代理、上下文压缩、Plan 模式、无界面模式都有。差别不在“有没有”，而在每一项落在哪一层、信任谁、交给谁决定。

## 同组的其它几本

同一组还有四本同类专栏：[《Kimi Code 源码解读》](https://daiw.net/manual/kimi-code)（TypeScript，DI 容器加作用域的引擎）、[《MiMo Code 源码解读》](https://daiw.net/manual/mimo-code)（从 OpenCode 分叉，为长程任务加了检查点与分层记忆）、[《MiniMax Code 源码解读》](https://daiw.net/manual/minimax-code)（TypeScript，以 pi-mono 的 Agent 循环为底座）、[《ZCode 源码解读》](https://daiw.net/manual/zcode)（同一个运行时驱动终端、桌面端与 Web 三种宿主）。想拿闭源产品的用法来对照，Grok Build 专栏的 [grok Build vs Claude Code](https://daiw.net/manual/grok-build/grok-vs-claude-code) 用《Claude Code 中文手册》做过一次。

---

上一篇：[复盘 · 对照《从 LLM 到 Coding Agent》](https://daiw.net/manual/codex-source/recap) · 下一篇：[读完之后](https://daiw.net/manual/codex-source/whats-next)
