16 KiB
16 KiB
实测记录(2026-10-25,VM,codex-cli 0.164.0-alpha.9)
环境
- 本机已有官方 codex:
/opt/hatch-image/bin/codex,codex-cli 0.164.0-alpha.9 - 无 cargo/rustc,2 核、可用内存约 0.5GB:从 Rust 源码现编不现实, 因此“照搬”走的是官方二进制自带的 app-server(即开源 core 编译产物), 而不是在本机重新编译一遍源码。协议 schema 同样由该二进制导出。
codex doctor:除“active model provider auth env var is missing”外 其余健康;MCP 有可选配置问题(缺 env var 的 server)。
协议导出
codex app-server generate-json-schema --out …成功,导出 2.6MB, 本仓库只收了两个合并文件:protocol/codex_app_server_protocol.schemas.json与protocol/codex_app_server_protocol.v2.schemas.json。- 从 v2 schema 确认的方法:initialize、initialized、model/list、 thread/start、thread/resume、thread/fork、thread/list、thread/archive、 thread/delete、turn/start、turn/steer、turn/interrupt、turn/completed。
- ThreadStartParams:cwd、model、modelProvider、sandbox、approvalPolicy、 approvalsReviewer、baseInstructions、developerInstructions 等。
- TurnStartParams:threadId、input(text)、model、effort、cwd、 parentTurnId、rootTurnId(委派链路用)、sandboxPolicy、approvalPolicy 等。
- features:
multi_agent为 stable/true,multi_agent_v2为 stable/false。
客户端链路实测
隔离 CODEX_HOME 跑 client.py(见 README):
- initialize:成功。
- thread/start:成功,thread id 如
01a17394-…,cwd 正确落到 work/job-a。 - turn/start:成功,收到 turn/started 与 userMessage 事件。
- 模型调用:失败,
401 Unauthorized: Missing bearer token, 重试 5 次后 turn/completed 状态 failed,耗时约 17 秒。 - 结论:协议、线程、回合、事件流、错误回推全部走通; 唯一缺的是模型登录,这一步在隔离目录里本就没有, 在已登录的机器上不需要改任何代码。
宿主编排实测(2026-10-25,host.py,中转站 gpt-6.1-sol,low 档位)
测试仓:work/host-repo,git 初始化,sample.py 只有 hello()。
隔离 CODEX_HOME(/tmp/codex-host-test,配了中转站 config),
配对二进制 bin/codex。
- 并行:
parallel一次派 host-a(加 add2)和 host-b(加 mul2), 单个 app-server 进程内两个 thread 并发,约 70 秒双双 completed, 各自 worktree 里的sample.py正确,互不污染。 首跑曾因隔离目录缺中转站 config 而 401 到 api.openai.com, 补 config 后重跑通过——这是测试配置问题,不是 host 代码问题。 - 评审:
review host-a给出M sample.py+ 完整 diff。 - 委派:
delegate --parent-task host-a子任务只读检查 add2, 结论列了 NaN/inf、字符串拼接、TypeError 等边界,未改文件;--report-to-parent后主 thread 汇总了该结论。 - fork / 续跑:
fork host-a -> host-a-v2拿到新 thread id;run host-a续跑一回合“一句话说 add2 是干嘛的”成功。 - 目标循环:
goal goal1目标是创建result.txt(内容 goal-ok), 第 1 轮验收test -f result.txt通过,状态 goal-met。 - 合并:
merge host-b先在 worktree 提交、再--no-ff合并, 主仓sample.py已含 mul2,worktree 已清理。 注意:merge 前必须先提交 worktree 改动,否则分支本身还是 旧 HEAD,合不到东西——这个 bug 在实测中发现并已修。 - 自动化:
automations --add daily-check+run-due, 到期任务自动派发并 completed,进评审队列。 - 状态:
host-state.json当前任务:host-a completed、 host-b merged、host-a-review completed、host-a-v2 forked、 goal1 goal-met、daily-check-* completed。
宿主编排第二批对齐试验(2026-10-25 晚,中转站 gpt-6.1-sol,low 档位)
上一批测的是“能跑起来”,这一批测的是“边界和控制对不对齐”, 全部通过,只有一个协议坑被挖出来并已修成 host.py 的命令:
- 沙箱边界:
--sandbox read-only的任务让 agent 建文件, agent 回答“当前工作目录是只读文件系统”,文件确实没建出来。 边界不是摆设,是真拦住了。 - 审批 deny:
--approval-policy untrusted --approval deny, agent 要跑echo approval-test-123,宿主 deny 后 agent 明确说 “自动审批拒绝了”,没拿到输出。注意 host.py 里审批回答的 payload 是按实测反推的{"decision": "deny"},这条路是通的。 - 模型内子代理:3 个子代理并行检查 sample.py,安全 / 质量 / 测试覆盖三份视角汇总回来,未改任何文件,结论还点名了主仓 已合并的 mul2 和 0% 测试覆盖——子代理不是走形式。
- 中断:长诗任务跑了 5 秒后发
turn/interrupt,turn/completed 状态直接是interrupted,不用等它写完。 - compact:直接对旧 thread 调
thread/compact/start会报thread not found,先thread/resume再 compact 就通了。 这是协议层面的先后顺序,host.py 已加compact命令, 内部就是 resume + compact,不让用户踩这个坑。 - 合并冲突:conflict-a(hello-a)和 conflict-b(hello-b)改同一行, 先合 a 成功,主仓变 hello-a;再合 b 时 host 打印“合并冲突, 已 abort”,主仓仍是 hello-a 且干净,b 的 worktree 留着等人工解。
- 自动化幂等:给 skip-test 设 interval 86400、last_run 为现在,
run-due没派发它。同一轮里到期的 daily-check 被派发了, 但那次调用我没传中转站 key,turn 401 失败,已清理掉那个 误建任务和 worktree——这是测试调用失误,不是 host 逻辑问题。
宿主编排第三批对齐试验(2026-10-25 晚,ChatGPT 宿主能力)
这一批测的是 ChatGPT 宿主最有区分度的几件:宿主给工具、 宿主换模型、运行中改向、多任务并行上限、评审的未追踪文件、 以及失败时宿主是否稳得住。
- 宿主提供 MCP 工具(最重要的一条,带一个真修):
新写了
examples/mcp_echo.py(零依赖 stdio MCP,两个工具), 在隔离 CODEX_HOME 里注册为[mcp_servers.echo]。 工具确实被 agent 发现了(工具名带 mcp__echo__ 前缀),但前两轮 都被审批拦住:第一轮 approval-policy=never 时直接报 “MCP tool call requires approval”;第二轮 untrusted + host 自动 放行时仍报 “user rejected MCP tool call”。 查 rollout 才找到根因:MCP 审批走的不是通用 approval, 而是mcpServer/elicitation/request(mode=form),必须按 MCP elicitation 协议回action=accept/decline + content, 回通用{"decision": ...}会被当成拒绝。 host.py 已按这个协议分流回答,第三轮mcp-test3成功: agent 拿到了固定事实和 20+22=42,且未改文件。 结论:宿主给工具这条路是通的,但审批 payload 不能一刀切。 - 模型切换:同一 thread host-a(原 gpt-6.1-sol)用
--model gpt-6-sol续跑一回合,turn 的 model 字段确实是 gpt-6-sol,回答正确——宿主可以在 thread 内换模型。 - 运行中改向(steer):长诗任务跑 5 秒后发
turn/steer(expectedTurnId 校验),turn 完成时已按新指令只剩 4 行短诗, 不是等旧任务跑完再开新回合。 - 3 并行:host-c/div2、host-d/pow2、host-e/mod2 三个 worktree 同时跑,全部 completed,文件各自正确、互不污染。
- 未追踪文件评审:
review goal1时 diff 为空,但 status 和 untracked 栏都列出了 result.txt——评审不能只看 diff, 未追踪文件要单独看,这一点 host.py 的 review 是对的。 - 失败不崩宿主:用不存在的模型派发,task 状态为 failed, 错误是中转站 404(模型不可用),host.py 没崩、没卡住, 该任务的 worktree 和分支已清理。
- 未完成项:另有一项仓库级上下文试验,其工具输出未通过 安全检查,本批报告不采用它的细节、不做结论;上面六项 不依赖它,结论独立成立。
生产级试验(2026-10-25 晚,真实项目仓,不是玩具 sample.py)
试验仓 work/prod-repo:core.py(add/sub)+ features/ 独立模块 +
tests/ 标准库 unittest,验收命令就是 python3 -m unittest discover -s tests。
起点 2 个测试,终点主仓 5 个功能、17 个测试全部通过。
- 并行生产:prod-pow 和 prod-mod 各自建功能文件 + 测试文件, 不碰共享文件,并行完成,各自 worktree 4 个测试通过,互不污染。
- 目标循环(真实测试验收):prod-safe-div 1 轮通过,验收是 整个测试套件,不是“文件存在”。
- 评审闭环:prod-parse 先实现了一个 naive 版本,委派只读评审 找出 5 条边界问题,汇总回主任务后,主任务修复为严格校验 (只接受 str、只接受 ASCII 十进制、空串抛错等),测试从 5 个 涨到 8 个,全部通过。评审任务本身没改任何文件。
- 合并链:pow、mod、parse、safe-div、clamp 依次
--no-ff合并进主仓,每次合并后主仓测试都跑过,最终 17 个测试 OK。 合并前评审时发现一个缺口并已修:新文件默认 untracked, git diff 里看不到内容,review 现在会把未追踪文本文件内容 也打印出来,否则评审等于没看新文件。 - 自动化:prod-nightly-tests(只读跑测试)run-due 派发完成, 紧接着再 run-due 一次全部跳过,幂等正确。
- MCP(生产仓):prod-mcp 在生产仓 worktree 里调用宿主 MCP, 拿到固定事实和 7+8=15,未改文件。
- 失败与恢复(生产级真正挖出来的东西):
- 委派评审第一轮因 shell 超时 300 秒被杀,turn_aborted, 且任务登记只在 turn 完成后才落盘,等于任务丢了。 已修:dispatch / delegate / goal / run-due 都先落盘再跑 turn, 中断后可用 run 续跑。
- fork 换 worktree 后沙箱被服务端重置为 read-only,fork 时传 sandbox 参数不认;同 cwd fork 则保留 workspace-write。 已修:turn 级 sandboxPolicy 会覆盖 thread 级设置,run 时按 任务沙箱传,fork 隔离 worktree + 写操作已验证通过。
- fork 拷工作区文件时把 .git 文件也拷过去了,新 worktree 的指针指回旧 worktree,git worktree 校验失败。 已修:拷文件时跳过 .git,已坏的 v2 清理、v3 指针已修复。
- 目标循环第一版只说“验收未通过”,不告诉 agent 错在哪, prod-retry 3 轮原地不动(测试数一直是 1)。 已修:目标循环把验收命令和失败输出喂给下一轮,且验收命令 本身要打印诊断信息(如 test_count=1, need >=3), prod-retry2 第 1 轮失败、第 2 轮补到 3 个测试、目标达成。
- 生产级结论:本地、无 GUI 的宿主编排,ChatGPT 宿主在 本地能做的那部分,基本对齐了——并行隔离、测试验收、评审门、 委派修复闭环、合并链、自动化、MCP、模型切换、改向/中断、 失败恢复,都在真实项目上跑通了。 没对齐的仍然是云端和界面那部分:云端派发、推送通知、 GitHub/Slack 触发、CI/PR 集成、GUI inline 评论、Computer Use, 以及一个已知局限:host-state.json 是单文件,多 host.py 进程同时写会有 read-modify-write 竞态,同一进程内的并行 是安全的,跨进程并行没有锁,不要两个 host.py 进程同时跑 同一个 state 文件。
生产加固(2026-10-26,state 锁 / 项目配置 / 定时自动化)
- state 锁:
host-state.json.lock+ fcntl 锁,锁只包住读/写 短临界区,不包整个模型 turn;保存时在锁里重读磁盘,按任务名 覆盖、按自动化名合并,tmp 文件带 pid。 实测:两个进程同时加载、睡 0.5 秒、各自加一个任务再保存, 最终两个任务都在,没有合并逻辑的话后保存的会把先保存的覆盖掉。 - 项目配置:
host.py init生成host-config.json(repo、 model、effort、沙箱、审批、codex_home、state,无密钥),host.py config打印有效配置,优先级 CLI > 配置 > 内置默认; dispatch/goal 不传 --repo 时用配置里的 repo,没配则明确报错。 实测:/tmp 测试配置 init、config、repo 缺省路径都通; 项目里默认没有 host-config.json,旧调用方式不受影响。 - 定时自动化:run-due 现在写
host-run.log(派发和跳过都记),automations --cron-line只打印 crontab 行,不装机;automations --install-cron才真正写 crontab 且幂等。 实测:未到期的自动化 run-due 输出为空,但日志里有一条 skip; cron 行已验证格式正确,本次没有实际装机——装机后模型任务 会自动跑、花中转站额度,所以装机这一步留给用户显式下令。 另注意:VM 会被平台回收,crontab 不一定活过回收,常驻机器 用 crontab,VM 上更稳的是 Muse cron 定时调 run-due。 - 未修 / 已知局限:锁只能防同一台机器上的多进程竞态, 不能防两台机器同时写同一个共享 state 文件;定时任务本身 仍需外部调度器(crontab 或 Muse cron)触发,host.py 自己 不常驻。
补充:原始协议探针(同一晚,隔离目录)
model/list成功:返回gpt-6.1-sol,推理档位 low/medium/high/ xhigh/max/ultra,multiAgentVersion为v2。thread/list在隔离目录下为空,符合预期(不污染真实线程)。- 附带警告(不影响核心链路):本机缺 bubblewrap 时用内置兜底;
Code Mode 因缺
codex-code-mode-host可执行文件而 fail closed, 需该功能时要另装该组件。
工具执行修复(2026-10-25,用户选“先修好工具执行问题”后)
根因是两层缺件,不是中转站问题,rollout 日志里有明文证据:
- 缺 code-mode host:系统里的 codex 是裸二进制,
/opt/hatch-image/bin是只读挂载,旁边没有codex-code-mode-host,app-server 报 “Code Mode is unavailable … host executable was not found”, 工具 fail closed,agent 称“写入工具不可用”。 修复:从官方 npm 平台包@openai/codex@0.164.0-alpha.9-linux-x64里取出同版本codex-code-mode-host(73MB),与 codex 放到同一目录。 注意:host 只认真实可执行文件旁边的路径,放 PATH 里没用; 系统目录只读,所以配对目录只能放 workspace,不能直接补系统目录。 - 缺 bwrap:配对后工具能启动了,但写文件仍 3 次 instant failed,
rollout 里 panic 明文是
“bubblewrap is unavailable: no system bwrap was found on PATH
and no bundled codex-resources/bwrap binary was found
next to the Codex executable”。
修复:把系统目录里的
codex-resources/bwrap也复制到配对目录。
修复后配对目录:bin/codex(293MB)+
bin/codex-code-mode-host(74MB)+
bin/codex-resources/bwrap(502KB),合计约 366MB。
这是本机 VM 的补丁,Mac 上 npm 正常安装自带这三件套,不需要它;
start.sh 已改成优先用 bin/codex,client.py 也支持
CODEX_BIN 环境变量指定配对二进制。
修复后实测(中转站,gpt-6.1-sol,effort low,workspace-write):
- 回合 10.3 秒完成,状态 completed,不是 failed。
commandExecution状态 completed,命令是printf '%s\n' 'codex-ok' > test-result.txt && cat test-result.txt。- 落盘验证:
work/xcm-test/test-result.txt内容确为codex-ok。 - 此前失败的两个版本(max 60 秒、配对后无 bwrap 113 秒)均未建文件, 对照说明修的是执行环境,不是 prompt 或模型问题。
未实测
- 真实模型出结果的一回合(缺登录,VM 里不放密钥)。——已过期: 上面中转站实测已覆盖,见“工具执行修复”一节。
- ws / unix 传输的多客户端并发(start.sh 已把参数透传给官方 app-server,传输层本身是官方实现的,未在本机多客户端压测)。
- 审批请求的完整回包字段:各版本间字段有差异,client.py 默认 用 never 绕开审批;on-request + auto/deny 的回包是尽力而为, 生产交互客户端应以本版 protocol schema 为准再对齐一次。