博客

OpenCode vs Codex CLI:同一模型,无人值守实测

Codex 开 workspace-write 后,24 次 DeepSeek 编程运行全部通过;按各任务中位数比,Codex 输入更多、耗时更长。默认只读沙箱那次没改成文件。

本页目录 · 11 节
  1. OpenCode vs Codex 的测试配置
  2. 没人盯着跑时的默认行为
  3. 只回复 OK 的固定开销
  4. 4 个小编程任务
  5. 项目规则放进 AGENTS.md
  6. Codex 用自家模型:ChatGPT 登录的参照测试
  7. 订阅与价格
  8. 复现空操作
  9. Codex vs OpenCode:我会怎么选
  10. 测试范围
  11. 相关

我做了 OpenCode vs Codex 的无人值守实测:OpenCode 是支持多家模型提供商的开源终端编程 agent,Codex CLI 则是 OpenAI 开发、面向自家模型的开源终端编程 agent。这篇文章写给把它们放进脚本、CI 或定时任务、运行时没人盯着的开发者。下面的数字是同一模型下两边各发给模型多少 token、默认会做什么,文末附 DeepSeek 配置和一个能复现最简单那项测量的脚本。这里的无人值守运行,就是不开交互界面、从脚本里跑。

我是 Larry 🍀 Orbi,Orbi 的创始人。Orbi 用 Pi 这个开源编程 agent 命令行工具,把 GitHub Issue 做成改动并合并进仓库,每次改动都由另一个 Pi 会话评审。

我在 2026 年 11 月 11 日用自己的 Linux 机器做了测试,OpenCode 2.2.7 和 Codex CLI 0.167.1 都调用 DeepSeek V4.1 Flash(deepseek-flash)。受控编程运行中,我给 Codex 开了 workspace-write 权限,两个工具合计 24 次全部通过。按每个任务的中位数比较,Codex 的输入总量更多,耗时也更长。

OpenCode vs Codex 的测试配置

OpenCode 用内置的 DeepSeek provider,它从环境变量读 DEEPSEEK_API_KEY。--standalone 让这次运行自己起一个私有服务,不用 OpenCode 的后台服务:

opencode run --standalone --format json -m deepseek/deepseek-flash "<prompt>"

Codex 需要配置自定义 provider。它现在只支持 OpenAI 的 Responses API(Chat Completions 的后继接口),配置时填 wire_api = "chat" 会报错:

Error loading config.toml: `wire_api = "chat"` is no longer supported.
How to fix: set `wire_api = "responses"` in your provider config.

这条报错来自机器上原有的 0.167.0,后面所有计数的运行都用 0.167.1。DeepSeek 提供 Responses 接口,下面这 7 行 ~/.codex/config.toml 配置跑通了:

model = "deepseek-flash"
model_provider = "ds"
[model_providers.ds]
name = "DeepSeek"
base_url = "https://api.deepseek.com/v1"
env_key = "DEEPSEEK_API_KEY"
wire_api = "responses"

Codex 警告说,找不到 deepseek-flash 的模型元数据,已回退到默认值,可能导致表现变差。Codex CLI 主要为 OpenAI 的模型设计和调优,DeepSeek 并非它主要适配的模型。我也跑了 Codex 调用 OpenAI 模型的测试,结果放在后文供参考。两组环境不同,无法做受控性能比较。

每次受控运行都用 env -i、空 HOME、新建的 git 仓库,并把 stdin 指向 /dev/null。我在 api.deepseek.com 前放了一个小型日志代理,记录每次模型请求和 DeepSeek 返回的用量。受控对比里的 Codex 运行全部经过代理,共 29 次:3 次空操作(提示词是 Reply with exactly OK)、13 次编程任务、2 次规则文件检查、11 次权限探测。Codex 自己也会报输入 token,29 次都和代理记录一致。输入总量包含缓存 token,下文也把缓存用量单独列出,因为 DeepSeek 对两类输入的收费差别很大。

没人盯着跑时的默认行为

下表的探测每项只跑了 1 次,单独开启网络的那次也是;danger-full-access 只在被拦的探测里挑了两项跑:项目外 shell 写入和联网请求。任务 1 那一行不同:OpenCode 和 workspace-write 两格汇总的是后文任务一节的 3 次受控运行,不加参数那格是另外单跑的 1 次。所有运行的退出码都是 0,操作被拦也一样。“未写入”指文件最终没有写入。Codex 各列都用 codex exec,沙箱参数见表头。

我让它做什么 OpenCode 默认 Codex:不加沙箱参数 --sandbox workspace-write --sandbox danger-full-access
在项目中创建文件 已写入 未写入 已写入 未测试
用 shell 命令向项目外写入 已写入 被拦截 被拦截 已写入
curl https://example.com 200 Could not resolve host Could not resolve host;加 -c sandbox_workspace_write.network_access=true 后为 200 200
在上级目录的 notes.md 追加一行,不提供命令 已写入 未写入 未写入 未测试
任务 1:修复 duration bug 已修复 未修复 已修复 未测试

直接运行 codex exec "<task 1 prompt>" 时,Codex 读取了代码,随后 stderr 报出 patch rejected: writing is blocked by read-only sandbox; rejected by user approval settings。接着它在内存里加载一份改好的模块,对着这份副本跑测试,测试通过;然后在最终回复中描述了修复,以 0 退出。补丁没有落盘,文件没改,我事后重跑仓库里的测试仍然失败。这次用了 6 次请求、67,537 个输入 token,耗时 27.2 秒。这个任务的所有成功运行,输入和耗时都比这次少。

Codex 自动化文档写着:"By default, codex exec runs in a read-only sandbox." 文档给出的编辑参数是 --sandbox workspace-write。OpenCode 没有操作系统级沙箱,它的权限文档写着:"shell runs with the host user’s filesystem, process, and network authority." 其他模式可以看完整的 Codex 沙箱与审批矩阵。脚本只看退出码,就会把那次只读运行算作完成,还得检查测试或 diff。

处理无人值守的 GitHub Issue 时,Orbi Cloud 会为每张 Issue 运行 Pi。如果一次运行连一个文件都没改,就会被停下。PR 交给另一个 Pi 会话评审,评审和仓库的 CI 都通过才合并。

只回复 OK 的固定开销

我给每个工具各发了 3 次 Reply with exactly OK,同一工具每次的请求数、输入和缓存计数都相同,3 次都是缓存已经热了的运行。

工具 请求数 输入总量 缓存命中的输入 未命中缓存的输入
OpenCode 2 5,815 2,048 3,767
Codex CLI 1 8,560 7,808 752

OpenCode 的主请求带了 12 个工具,发送 5,370 个输入 token;单独的标题请求不带工具,又加了 445 个。Codex 的 1 次请求带了 9 个工具。

OpenCode 的工具是 edit、glob、grep、question、read、shell、skill、subagent、webfetch、websearch、write 和 execute。Codex 的工具是 exec_command、write_stdin、request_user_input、view_image、multi_agent_v1、get_goal、create_goal、update_goal 和 web_search。

从记录下来的编程请求中,能看到一部分开销的来源:OpenCode 有 1 条 system 消息,长 8,654 个字符,工具 JSON 为 14,043 字节。Codex 有 2 条 developer 消息,分别长 16,979 和 2,492 个字符,另有 1 条 environment-context 消息,工具 JSON 为 17,767 字节。

要命中缓存,近期必须发送过完全相同的 prompt 前缀。这些运行相隔只有几分钟。我预计首次运行的缓存命中会更少,但没有测过冷缓存时的命中率。用了 DeepSeek 的前缀缓存后,Codex 只有 752 个输入 token 按全价计费,OpenCode 则有 3,767 个。我没有测试其他 provider 的缓存。

4 个小编程任务

任务 1 修复 duration.py 的 bug,prompt 给了文件名。任务 2 实现 top_words。任务 3 在一个有 3 个文件的 shop 包里找问题,prompt 没给文件名。任务 4 用一个新建的 ledger 包,里面有 4 个文件、4 个 bug、9 个可见测试,还有 agent 从未见过的隐藏检查。

每个任务用每个工具各跑 3 次,共 24 次。Codex 加 --sandbox workspace-write,OpenCode 用默认策略。前面说过,Codex 这里跑的不是它调优过的模型。所有运行都通过了测试,包括任务 4 的隐藏检查,也都没改测试文件。单看这些任务的正确性,两个工具没有分出差别。

下表对每项指标分别取 3 次运行的中位数,一行里的数字不代表同一次运行,所以「未命中」不一定等于「输入」减「缓存命中」。输入包含缓存 token。

任务 / 工具 模型请求数 输入 缓存命中的输入 未命中缓存的输入 输出 耗时(秒)
1 / OpenCode 5 24,416 19,584 4,832 348 6.1
1 / Codex 5 52,561 49,792 2,747 2,049 11.4
2 / OpenCode 6 31,504 26,368 5,136 586 6.9
2 / Codex 4 39,735 37,632 2,068 1,488 9.2
3 / OpenCode 7 37,578 32,256 5,322 885 8.3
3 / Codex 4 41,333 38,912 2,421 1,869 10.1
4 / OpenCode 7 46,814 39,680 7,259 1,947 15.3
4 / Codex 6 79,305 74,240 5,065 4,378 25.3

按每个任务的中位数比较,Codex 的输入总量更多,未命中缓存的输入更少,输出 token 是 OpenCode 的 2.1–5.9 倍。每个任务的耗时中位数也都更长。

按 DeepSeek 的高峰时段标价,每百万 token 的价格是:缓存命中的输入 $0.006,未命中缓存的输入 $0.30,输出 $1.20。这批运行在周三 02:00 UTC 前后,落在 DeepSeek 工作日两个高峰时段之一(01:00 到 04:00 UTC,另一个是 06:00 到 10:00 UTC)。先按标价计算每次运行的费用,再取中位数,结果如下:

任务 OpenCode Codex CLI
1 $0.00198 $0.00359
2 $0.00236 $0.00258
3 $0.00280 $0.00320
4 $0.00471 $0.00722

这些运行每次都不到 1 美分。对每个任务,我把每个工具的 3 次运行按总费用排序,各取中间那次来比较。Codex 的输入费用更低,但输出更多,算下来总费用更高。这次测的是开销和默认行为,不构成 benchmark,也不对 agent 的能力排名。

项目规则放进 AGENTS.md

我在项目 AGENTS.md 里写了一条规则,要求回复以 ZEBRA-MARKER 结尾,两个工具都遵守了。把同一行改放进 CLAUDE.md、项目里不放 AGENTS.md,两个工具都没遵守。每种文件和工具的组合只跑了 1 次。

Codex 的结果符合它的指令文件文档:"Filenames not on this list are ignored for instruction discovery." 要让它读取 CLAUDE.md,可以把这个文件名加入 project_doc_fallback_filenames。OpenCode 的规则页面(页面标注为 V1 文档)列出了 "CLAUDE.md in your project directory (used if no AGENTS.md exists)"。我这次的 2.2.7 没有读取它,与上一篇对比里看到的一致。共用规则放进 AGENTS.md。

Codex 用自家模型:ChatGPT 登录的参照测试

我用自己的 ChatGPT 账号登录,选 gpt-6.1-sol、medium 推理强度,加上 --sandbox workspace-write,把同样的 4 个任务各跑了 1 次。4 个都通过了测试,包括任务 4 的隐藏检查。每个任务执行 3–5 条 shell 命令,耗时 17.3–44.9 秒。

这组运行用了我平常的 Codex HOME,里面有全局 AGENTS.md 和配置,无法和空 HOME 的运行做受控性能比较。token 计数也没法直接比较,所以没列。

我没跑成 OpenCode 调用 GPT 的测试。它的 provider 文档介绍了 "Sign in with ChatGPT",需要交互式登录,这条路径我没测。这台机器上的 OpenAI API key 被拒绝了。

订阅与价格

按 Codex 价格页,通过 ChatGPT 套餐使用 CLI,至少需要 Plus($20/月)。Pro 起价为 $100/月;Business 至少 2 位用户,按年付是 $20/用户/月,按月付是 $25/用户/月。Free($0)和 Go($8/月)列出的 Codex 权益只适用于桌面应用。以上是该页面 2026 年 11 月 11 日的价格。用 API key 就按 token 付费。Codex CLI 采用 Apache-2.0 许可证。

OpenCode 免费,采用 MIT 许可证,模型费用付给 provider。它也提供 OpenCode Go,$10/月。

复现空操作

本文里能照着复现的只有空操作这一项,4 个任务的仓库没有公开。把下面的内容保存为 Bash 脚本。脚本用到了 PIPESTATUS。我跑它用的是 OpenCode 2.2.7 和 Codex CLI 0.167.1,另外需要 jq,还要提前导出 DEEPSEEK_API_KEY。脚本里的 Codex 留在默认的只读沙箱,回一句话够用;要让它改文件,先加 --sandbox workspace-write。运行目录及其上级目录都不能有 AGENTS.md,否则项目规则也会加进 prompt。

脚本打印的 OpenCode 用量来自主请求,不含标题请求。

#!/usr/bin/env bash
# Tested with opencode 2.2.7 and codex 0.167.1. Needs jq and DEEPSEEK_API_KEY exported.
# OpenCode no-op: empty HOME, stdin closed
H=$(mktemp -d)
env -i HOME="$H" PATH="$PATH" DEEPSEEK_API_KEY="$DEEPSEEK_API_KEY" \
  opencode run --standalone --format json -m deepseek/deepseek-flash "Reply with exactly OK" < /dev/null \
  | jq -c 'select(.type=="step_finish") | .part.tokens'
echo "opencode exit: ${PIPESTATUS[0]}"

# Codex CLI no-op on the same model
H=$(mktemp -d); mkdir -p "$H/.codex"
cat > "$H/.codex/config.toml" <<'EOF'
model = "deepseek-flash"
model_provider = "ds"
[model_providers.ds]
name = "DeepSeek"
base_url = "https://api.deepseek.com/v1"
env_key = "DEEPSEEK_API_KEY"
wire_api = "responses"
EOF
env -i HOME="$H" PATH="$PATH" DEEPSEEK_API_KEY="$DEEPSEEK_API_KEY" \
  codex exec --json --skip-git-repo-check "Reply with exactly OK" < /dev/null \
  | jq -c 'select(.type=="turn.completed") | .usage'
echo "codex exit: ${PIPESTATUS[0]}"

我最近一次跑出的用量如下:

{"input":234,"output":2,"reasoning":0,"cache":{"read":5120,"write":0}}
{"input_tokens":8557,"cached_input_tokens":7808,"cache_write_input_tokens":0,"output_tokens":8,"reasoning_output_tokens":6}

OpenCode 只打印了主请求这一行,把 input 和 cache.read 相加是 5,354 个输入 token。同一个脚本更早一次跑出的是 3,562 和 1,792,两数相加同样是 5,354,只是缓存命中少。Codex 这次报告 8,557 个,第一次是 8,556 个。OpenCode 的 5,354 比上文主请求的 5,370 少 16,Codex 的 8,557 和 8,556 比表格里的 8,560 少 3 到 4。脚本换了运行目录,不在 git 仓库里,也不经过代理,差在哪我没有继续查。跑出来接近 5,354 和 8,557,就算复现了。

密钥错误或为空时,两个工具的退出码都是 1,也都没有打印用量行。前面那些操作被拦的运行,退出码却都是 0。

Codex vs OpenCode:我会怎么选

已经付费订阅 ChatGPT,又想让沙箱默认拦住项目外写入和网络访问,我会选 Codex。需要编辑文件时加 --sandbox workspace-write,检查结果时别只看退出码。

经常要换模型 provider 的话,我会选 OpenCode:它改一个 -m 参数就行,Codex 要写一段自定义 provider 配置,还会提示缺模型元数据。空操作测试里,OpenCode 的主请求也更小,5,370 个输入 token 对 8,560,不过它在 DeepSeek 上按全价计费的部分更多。它拥有当前用户的完整权限,deny 规则可以限制操作。需要操作系统级隔离,就得自己把它放进容器。无论选哪个,都要自己检查 diff 和测试。

测试范围

这次只用了一台机器,都在同一天测完,用一个 DeepSeek 模型跑 4 个小任务,每个任务用每个工具各跑 3 次。权限等探测每种设置只跑 1 次。受控对比中的 Codex 没有使用它主要适配的模型,缓存数据也只适用于 DeepSeek 和这批重复运行。没有 OpenCode 调用 GPT 的运行数据。用量计数只适用于这些版本。

相关

每周一个真实交付

每周一封,讲一次真实交付。随时退订。