1. 先搞清楚 Codex Harness 到底在管什么
Codex Harness 这个名字听起来像是个测试框架,但它本质上是一套执行策略编排层。你可以把它理解成一个“交通指挥中心”:代码生成模型是路上的车,而 Harness 决定哪辆车能上路、走哪条道、在哪个路口必须停下来接受检查。它要同时管三件事——审批(Approval)、沙箱(Sandbox)、AGENTS.md 配置。这三者不是独立开关,而是互相咬合的齿轮,组合起来能产生 12 种有效状态。
很多人第一次接触时容易犯一个错:把审批和沙箱当成一回事。审批管的是“这个操作要不要人点头”,沙箱管的是“这个操作能在多大范围内折腾”。一个管权限,一个管边界。AGENTS.md 则是告诉 Harness“当前项目里有哪些角色、各自能干什么、默认策略是什么”。三者叠加,才决定了 Codex 在某个具体任务里到底是“乖乖听话”还是“放开手脚干”。
我见过不少团队在 CI 里直接开全自动,结果模型把测试数据库的 schema 给改了;也见过有人把审批拉满,导致每次改一个变量名都要人工确认,效率直接归零。所以选组合不是拍脑袋,得先理解每个维度的取值逻辑。
1.1 审批维度的三个档位
审批在 Codex Harness 里通常分三档:全自动(auto)、关键操作审批(critical-only)、全量审批(always)。全自动意味着模型可以连续执行多步操作,中间不打断;关键操作审批只对写文件、执行 shell、调用外部 API 这类有副作用的动作弹确认;全量审批则是每一步都要人点一下。
选哪一档,取决于你对“错误成本”的容忍度。比如在一个只读的分析任务里,全自动完全没问题,因为模型就算乱来也改不了东西。但如果是生产环境的迁移脚本,哪怕只是生成 SQL,也建议至少用 critical-only,因为模型可能会“好心”帮你加上 DROP TABLE 的清理逻辑。
注意:审批档位不是越高越安全。全量审批在长任务里会让人产生“确认疲劳”,反而容易无脑点通过。关键操作审批是大多数场景下的甜点区。
1.2 沙箱维度的四种边界
沙箱这边常见的有四种:无沙箱(none)、只读沙箱(read-only)、工作区沙箱(workspace-write)、完全隔离沙箱(full-isolation)。无沙箱就是模型直接在你当前环境里跑,权限跟你本人一样大;只读沙箱允许读文件、跑只读命令,但写操作会被拦截;工作区沙箱允许在项目目录内写,但出了目录就受限;完全隔离沙箱则是给模型一个独立的容器或虚拟环境,跟宿主机彻底隔开。
这里有个容易踩的坑:工作区沙箱听起来很安全,但如果你的项目目录里包含了.env或者密钥文件,模型在沙箱内依然能读到。所以沙箱的边界不等于敏感信息的边界,敏感文件得靠.gitignore或者额外的挂载策略来排除。
1.3 AGENTS.md 的角色定义作用
AGENTS.md 不是可有可无的说明文档,它在 Harness 里是策略声明文件。你可以在里面定义多个 agent 角色,比如builder、reviewer、tester,每个角色可以绑定不同的审批和沙箱策略。Harness 在调度时会根据当前任务类型自动匹配角色。
举个例子,你可以在 AGENTS.md 里写:builder角色使用 workspace-write 沙箱 + critical-only 审批,reviewer角色使用 read-only 沙箱 + auto 审批。这样当 Codex 在做代码审查时,它自动进入只读模式,不需要人工干预;而当它要改代码时,才会触发审批。
这种角色化配置的好处是,你不需要在每次调用时手动传一堆参数,Harness 会根据 AGENTS.md 里的声明自动注入。坏处是,如果 AGENTS.md 写得不清晰,Harness 可能会匹配到错误的角色,导致策略比预期宽松或严格。
2. 12 种组合是怎么算出来的
3 种审批 × 4 种沙箱 = 12 种基础组合。但实际使用中,AGENTS.md 的角色定义会进一步影响这 12 种组合的生效方式。比如同一个“critical-only + workspace-write”组合,在builder角色下可能允许自动执行测试命令,但在deployer角色下可能连读文件都要二次确认。
所以真正要选的不是 12 选 1,而是先定角色,再定审批和沙箱。下面我把 12 种组合按风险从低到高排个序,然后挑几个典型场景展开说。
| 组合编号 | 审批档位 | 沙箱档位 | 适用场景 | 风险等级 |
|---|---|---|---|---|
| C1 | auto | none | 本地临时脚本、一次性实验 | 极高 |
| C2 | auto | read-only | 代码分析、文档生成 | 低 |
| C3 | auto | workspace-write | 个人项目快速迭代 | 中 |
| C4 | auto | full-isolation | CI 中的自动化测试 | 低 |
| C5 | critical-only | none | 需要人工兜底的本地操作 | 高 |
| C6 | critical-only | read-only | 生产环境只读诊断 | 低 |
| C7 | critical-only | workspace-write | 团队协作中的日常开发 | 中 |
| C8 | critical-only | full-isolation | 多租户 CI 流水线 | 低 |
| C9 | always | none | 高风险迁移脚本 | 中 |
| C10 | always | read-only | 合规审计场景 | 低 |
| C11 | always | workspace-write | 受监管的代码修改 | 中 |
| C12 | always | full-isolation | 安全敏感型任务 | 低 |
这张表不是绝对的。比如 C1 在本地临时脚本场景下风险极高,但如果你只是让模型生成一个echo hello的脚本,那风险其实可以忽略。所以风险等级是相对“典型任务”而言的。
2.1 为什么 auto + none 是危险组合
C1 组合意味着模型可以在你的真实环境里无限制执行任何操作,而且不需要你确认。这相当于把 root 权限交给一个刚入职的实习生,还告诉他“随便干”。我实测过一次,让模型“清理一下项目里的临时文件”,它直接跑了一个find . -name "*.tmp" -delete,结果把我一个还没提交的临时配置文件也删了。
这个组合唯一合理的用法是:在一个完全隔离的容器里,且容器内没有任何有价值的数据。否则,哪怕只是让模型“看看当前目录有什么”,它也可能顺手执行一些你没想到的命令。
2.2 read-only 沙箱为什么是安全底线
C2、C6、C10 这三个组合都用了 read-only 沙箱,区别只在审批档位。read-only 沙箱的核心价值是:模型可以看,但不能改。它允许模型读取文件内容、执行ls、cat、grep这类只读命令,但任何写操作都会被 Harness 拦截。
我个人的习惯是,只要任务不涉及代码修改,一律用 read-only 沙箱 + auto 审批。比如让 Codex 分析一个日志文件、生成一份 API 文档、或者审查一段代码的逻辑漏洞。这种场景下,模型不需要写权限,全自动也不会造成任何破坏。
提示:read-only 沙箱并不能阻止模型通过只读命令泄露敏感信息。如果项目里有密钥文件,建议在 AGENTS.md 里显式声明排除路径。
2.3 workspace-write 的边界在哪里
C3、C7、C11 用的是 workspace-write 沙箱。这个沙箱的边界是“项目根目录”,模型可以在目录内自由读写,但出了目录就会被拦截。听起来很合理,但实际使用中有两个坑:
第一个坑是符号链接。如果项目目录里有一个指向/etc的软链接,模型在沙箱内依然可以通过这个链接访问到外部文件。Harness 通常会对符号链接做解析,但不同版本的行为可能不一致,建议在 AGENTS.md 里显式禁止跟随符号链接。
第二个坑是子进程逃逸。如果模型执行了一个脚本,脚本里又启动了另一个进程去写外部目录,沙箱是否拦截取决于 Harness 的实现深度。我实测下来,大多数 Harness 实现只能拦截直接的文件操作,对子进程的间接写入拦截能力有限。
所以 workspace-write 适合“信任模型不会主动作恶,但需要防止意外越界”的场景。如果你对模型的行为完全没有信任,应该用 full-isolation。
2.4 full-isolation 的代价与收益
C4、C8、C12 用的是 full-isolation 沙箱。这个沙箱会给模型一个独立的容器或虚拟机,跟宿主机彻底隔开。模型在里面可以随便折腾,哪怕把整个文件系统删了,也不会影响你的真实环境。
代价是性能开销和环境准备成本。每次启动隔离环境都需要时间,而且你需要把项目依赖、工具链都装进去。对于 CI 流水线来说,这个成本可以接受,因为 CI 本身就是一次性的。但对于本地开发来说,每次让模型改个变量名都要等容器启动,体验就很差。
我的建议是:本地开发用 workspace-write,CI 用 full-isolation。本地开发时你就在旁边看着,出了问题随时可以中断;CI 里没人盯着,必须用最强隔离。
3. AGENTS.md 怎么写才能让 Harness 不犯迷糊
AGENTS.md 的写法直接决定了 Harness 能不能正确匹配角色。我见过太多人把 AGENTS.md 写成“项目说明书”,里面全是“本项目使用 React 框架”这种对 Harness 毫无意义的信息。Harness 需要的是策略声明,不是项目介绍。
一个有效的 AGENTS.md 应该包含三部分:角色定义、策略绑定、排除规则。角色定义告诉 Harness 有哪些角色可用;策略绑定告诉 Harness 每个角色用什么审批和沙箱;排除规则告诉 Harness 哪些文件或目录永远不能被访问。
3.1 角色定义的最小可用模板
下面是我常用的一个模板,你可以直接抄:
# AGENTS.md ## Roles ### builder - description: 负责代码修改和功能实现 - approval: critical-only - sandbox: workspace-write - allowed_paths: - src/ - tests/ - denied_paths: - .env - secrets/ - node_modules/ ### reviewer - description: 负责代码审查和逻辑分析 - approval: auto - sandbox: read-only - allowed_paths: - src/ - docs/ - denied_paths: - .env - secrets/ ### deployer - description: 负责部署脚本生成和执行 - approval: always - sandbox: full-isolation - allowed_paths: - deploy/ - denied_paths: - "*"这个模板的关键在于denied_paths的优先级高于allowed_paths。也就是说,即使builder角色允许访问src/,但如果src/下面有一个.env文件,它依然会被拒绝。Harness 在匹配路径时,通常会先检查拒绝列表,再检查允许列表。
3.2 策略绑定的常见错误
最常见的错误是角色名和任务类型不匹配。比如你把角色命名为coder,但 Harness 在调度时用的是builder这个关键词,结果就是 Harness 找不到匹配的角色,回退到默认策略。默认策略通常是“最宽松”的,这就很危险。
另一个错误是审批和沙箱的档位写错。比如你想写critical-only,但写成了critical_only,Harness 解析失败后可能会回退到auto。这种拼写错误在 YAML 或 Markdown 里很常见,建议写完用 Harness 的校验命令跑一遍。
注意:不同版本的 Codex Harness 对 AGENTS.md 的解析规则可能不同。建议在升级 Harness 后,先用一个只读任务测试一下角色匹配是否正常。
3.3 排除规则怎么写才彻底
排除规则不能只写文件名,要写路径模式。比如你想排除所有.env文件,不能只写.env,因为src/config/.env可能匹配不到。应该写**/.env或者*.env,具体语法取决于 Harness 使用的 glob 实现。
我通常会在 AGENTS.md 里加一条“全局拒绝”规则,把所有敏感路径都列进去:
## Global Deny - "**/.env" - "**/.env.*" - "**/secrets/**" - "**/*.pem" - "**/*.key" - "**/id_rsa*" - "**/.aws/**" - "**/.ssh/**"这条规则会应用到所有角色,不管角色自己的denied_paths写了什么。这样即使某个角色的配置漏了,全局拒绝也能兜底。
4. 实操:从零搭一套可复现的 Harness 配置
光说理论没用,下面我带你走一遍完整流程。假设你有一个 Node.js 项目,想让 Codex 帮你做三件事:分析代码质量、修改 bug、生成部署脚本。我们分别用reviewer、builder、deployer三个角色来对应。
4.1 环境准备与 Harness 初始化
首先确认你的 Codex Harness 版本。不同版本的配置格式可能有差异,我用的版本是0.9.x,配置文件放在项目根目录的.codex/下面。初始化命令通常是:
codex harness init --project-root .这个命令会生成一个.codex/harness.yaml和一个空的AGENTS.md。harness.yaml里定义了全局的默认策略,比如默认审批档位、默认沙箱类型、日志级别等。我一般会把默认策略设成最严格的:
# .codex/harness.yaml default: approval: always sandbox: read-only log_level: info timeout_seconds: 300这样即使 AGENTS.md 里某个角色配置错了,Harness 也会回退到最严格的默认策略,而不是最宽松的。
4.2 编写 AGENTS.md 并验证角色匹配
把前面那个模板复制到AGENTS.md里,然后跑验证命令:
codex harness validate --agents-file AGENTS.md如果输出里显示Roles matched: builder, reviewer, deployer,说明角色定义没问题。如果显示Roles matched: none,说明 Harness 没识别到你的角色,可能是格式问题。
验证通过后,用一个小任务测试角色匹配:
codex harness run --task "分析 src/index.js 的代码质量" --role reviewer如果 Harness 正确匹配到reviewer角色,它应该用 read-only 沙箱 + auto 审批执行。你可以在日志里看到sandbox=read-only, approval=auto这样的输出。
4.3 用 builder 角色修改代码的完整流程
假设 reviewer 分析后发现src/utils.js里有一个空指针 bug,现在让 builder 去修。命令是:
codex harness run --task "修复 src/utils.js 中的空指针问题" --role builderHarness 会做以下几件事:
- 根据 AGENTS.md 匹配到
builder角色,加载critical-only审批和workspace-write沙箱。 - 检查任务描述里提到的文件路径
src/utils.js是否在allowed_paths里。如果在,继续;如果不在,直接拒绝。 - 启动沙箱环境,把项目目录挂载进去,但排除
denied_paths里的文件。 - 模型开始分析代码,生成修改方案。当它准备写文件时,Harness 会弹出审批确认。
- 你确认后,模型执行写入。写入完成后,Harness 会检查写入路径是否在允许范围内。
这里有个细节:审批确认的粒度。有些 Harness 实现是“每次写文件都确认”,有些是“整个任务确认一次”。我用的版本是每次写操作都确认,这样更安全,但如果你要改十个文件,就得点十次。可以在 AGENTS.md 里加一个approval_batch: true来合并确认,但我不建议,因为批量确认容易让人忽略细节。
4.4 用 deployer 角色生成部署脚本的注意事项
deployer 角色用的是always审批 +full-isolation沙箱。这意味着模型在隔离环境里生成脚本,每一步操作都要你确认。生成完成后,脚本文件不会直接写到你的项目目录里,而是留在隔离环境中,你需要手动导出。
导出命令通常是:
codex harness export --role deployer --output ./deploy/generated.sh这个命令会把隔离环境里的文件复制到指定路径。注意,导出操作本身也需要审批,因为它是从隔离环境往真实环境写文件。
提示:deployer 角色的
allowed_paths我建议只写deploy/,不要写src/。部署脚本不应该修改源代码,这是职责分离的基本原则。
5. 常见问题与排查技巧实录
5.1 审批弹窗不出现,模型直接执行了写操作
这是最危险的情况。原因通常是 AGENTS.md 里的角色匹配失败,Harness 回退到了默认策略,而默认策略的审批档位是auto。排查步骤:
- 检查
codex harness validate的输出,确认角色是否匹配成功。 - 检查
harness.yaml里的default.approval是否被改成了auto。 - 检查任务描述里是否包含了角色关键词。有些 Harness 实现要求任务描述里必须显式提到角色名,比如“作为 builder,修复...”。
如果以上都没问题,可能是 Harness 的 bug。我遇到过一次,是因为 AGENTS.md 里的角色名用了大写字母,而 Harness 匹配时区分大小写。改成小写后正常。
5.2 沙箱内无法访问项目依赖
workspace-write 沙箱默认只挂载项目目录,但node_modules通常在项目目录里,所以一般没问题。如果你用的是 monorepo,依赖可能在上层目录,沙箱就访问不到了。解决方法是在 AGENTS.md 里把依赖目录也加到allowed_paths里:
### builder - allowed_paths: - src/ - tests/ - ../shared/node_modules/但这样会扩大沙箱边界,降低隔离性。更好的做法是在沙箱启动前把依赖复制到项目目录内,或者用 full-isolation 沙箱并在容器里重新安装依赖。
5.3 模型在 read-only 沙箱里依然尝试写文件
read-only 沙箱会拦截写操作,但模型可能不知道自己在只读模式,依然会尝试写。这时 Harness 会返回一个错误给模型,模型可能会重试或者报错退出。你可以在 AGENTS.md 里加一条提示:
### reviewer - description: 负责代码审查和逻辑分析。注意:当前角色为只读模式,不要尝试修改任何文件。这条描述会被注入到模型的上下文里,让它知道自己没有写权限。实测下来,加上这条提示后,模型尝试写文件的概率会降低很多。
5.4 审批确认后模型执行了预期之外的操作
这种情况通常是因为模型在审批通过后,连续执行了多个操作,而 Harness 只对第一个操作做了审批。解决方法是在 AGENTS.md 里设置approval_scope: per-operation,强制每个操作都单独审批。代价是确认次数变多,但安全性更高。
另一个原因是模型误解了任务描述。比如你说“清理临时文件”,模型可能把“临时”理解成了“所有未提交的文件”。这种问题只能通过更精确的任务描述来避免,比如“删除 /tmp 目录下的 .tmp 文件”。
5.5 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 审批弹窗不出现 | 角色匹配失败,回退到 auto | 跑 validate 命令 | 检查角色名拼写和大小写 |
| 沙箱内依赖缺失 | 依赖目录不在 allowed_paths | 查看沙箱挂载日志 | 添加依赖路径或改用 full-isolation |
| 模型尝试写只读文件 | 模型不知道当前是只读模式 | 查看模型输出日志 | 在角色描述里加只读提示 |
| 审批后执行了额外操作 | approval_scope 设置过宽 | 查看操作日志 | 改为 per-operation |
| 导出文件失败 | 导出路径不在 allowed_paths | 查看导出日志 | 添加导出路径到 allowed_paths |
6. 我个人的组合选择建议
如果你不想看那么多理论,只想快速选一个组合,下面是我的经验法则:
本地开发、个人项目:用 C7(critical-only + workspace-write)。审批只在写文件时弹,沙箱限制在项目目录内。效率和安全平衡得最好。
团队协作、日常开发:用 C8(critical-only + full-isolation)。CI 里没人盯着,必须用最强隔离。审批只在关键操作时弹,不会太影响流水线速度。
代码审查、文档生成:用 C2(auto + read-only)。全自动,只读,零风险。模型爱怎么分析就怎么分析,改不了任何东西。
部署脚本、迁移脚本:用 C12(always + full-isolation)。每一步都确认,而且隔离环境保证脚本不会误伤真实环境。慢是慢了点,但安全第一。
临时实验、一次性脚本:用 C1(auto + none)。但前提是你在一个完全隔离的容器里,且容器内没有任何有价值的数据。否则别用。
最后再分享一个小技巧:不管你选哪个组合,都先在 AGENTS.md 里把denied_paths写全。这是最后一道防线,比审批和沙箱都更直接。我见过太多人因为忘了排除.env文件,导致模型在分析代码时把密钥读出来写进了日志里。这种事故一旦发生,后果比代码被改严重得多。