Headless Agent CLI 独立评审:代码审查与配置检查的工程实践
1. 为什么独立评审才是 Headless Agent CLI 的正确打开方式很多人第一次接触 Headless 模式的 Agent CLI脑子里冒出来的第一个念头都是自动化——让它自己跑任务、自己改代码、自己提交。我一开始也是这么想的折腾了小半个月结果发现这条路又累又不稳任务越复杂Agent 越容易在中间某一步跑偏而且你根本不知道它是在哪一步开始错的。后来我换了个思路把 Headless Agent CLI 当成一个独立的评审者来用整个体验完全不一样了。所谓 Headless指的是 Agent CLI 不依赖交互式终端界面可以直接在脚本、CI 流水线、Git hook 里被调用输入一段内容、输出一段结果就退出。它没有对话窗口这个概念你给它什么它处理什么。而独立评审的意思是让一个和主开发流程隔离的 Agent 实例专门负责对产出物做审查、挑刺、给意见而不是让它去写代码。这个用法为什么合适因为 Agent 在生成这件事上天然有幻觉风险但在评审这件事上它的价值反而被放大了。评审不需要它百分之百正确只需要它把可疑的地方标出来人来拍板。它像一个不知疲倦、不会顾及情面的初级审阅者你不需要它多聪明你需要它稳定、可重复、不遗漏。这篇文章适合三类人看一是已经在用 Codex 这类 Agent CLI 但总觉得自动化不靠谱的开发者二是想把代码评审、文档校对、配置检查这类重复劳动外包出去的团队三是单纯想搞明白 Headless 模式到底该怎么落地、而不是被各种教程带着瞎跑的人。下面我会从架构、实操、踩坑、进阶四个层面把独立评审这套用法讲透。2. 独立评审的架构主 Agent 与评审子代理如何分工2.1 为什么不能让同一个 Agent 既写又审这是最核心的一条经验。我早期图省事让同一个会话里的 Agent 写完代码后自己 review 一遍结果它几乎永远说看起来没问题。原因很简单同一个上下文里的 Agent 对自己的产出有确认偏误它刚刚生成的内容还在上下文窗口里它会倾向于认为自己的推理是连贯的、正确的。正确的做法是把写和审拆成两个独立的进程调用。写的那次调用结束后进程退出上下文清空评审时重新起一个 Headless 进程只把产出物本身diff、文件内容、配置文本喂进去不带任何这是我刚写的这类元信息。这样评审 Agent 面对的就是一份陌生材料它的挑刺意愿会明显上升。2.2 子代理Sub-agent模式的三种落地形态子代理这个词听起来玄乎落地其实就三种形态我按复杂度从低到高列一下形态实现方式适用场景隔离程度进程级隔离每次评审起一个新的 CLI 进程个人项目、本地 pre-commit高目录级隔离评审 Agent 在独立工作目录运行只读挂载待审文件团队 CI、多仓库高容器级隔离评审跑在一次性容器里网络与文件系统受限对外部贡献代码的评审最高我个人最常用的是进程级隔离因为它最简单一条命令就能起。目录级隔离适合 CI 场景因为 CI 本身就是干净环境。容器级隔离一般只在评审第三方 PR 时才值得上成本偏高。2.3 评审 Agent 的输入应该包含什么这一点很多人做错。评审 Agent 的输入不是帮我看看这段代码而是结构化的材料包。我通常会给它这几样东西变更的 diff用git diff或git diff --staged生成相关的上下文文件被改动文件的完整内容而不是只有 diff 片段一份评审清单checklist明确告诉它关注哪些维度项目约定比如命名规范、目录结构约定提示diff 单独喂给 Agent 时它经常因为看不到上下文而误报。把改动文件的完整内容一起给它误报率能降一大截。评审清单是灵魂。没有清单Agent 会自由发挥一会儿聊性能一会儿聊风格输出没法用。有了清单它的输出就变成可解析的结构化结果。我常用的清单维度包括逻辑正确性、边界条件、错误处理、命名一致性、潜在的性能陷阱、安全相关写法。3. 把评审跑起来从命令行到 CI 的完整链路3.1 最小可用的本地评审命令先给一个能直接抄的最小例子。假设你已经装好了 Codex CLI并且配置好了模型接入那么一次本地评审大概长这样# 生成待评审的 diff 和上下文 git diff --staged /tmp/review.diff git diff --staged --name-only | xargs -I{} cat {} /tmp/review.context # 组装评审材料 cat /tmp/review.diff /tmp/review.context /tmp/review.input # 起一个 Headless 评审进程 codex exec --headless \ --prompt-file ./review-checklist.md \ --input /tmp/review.input \ --output /tmp/review.result这里几个参数值得说明。--headless表示不进入交互模式处理完就退出。--prompt-file指定评审清单把清单和材料分开管理方便复用。--input是待审内容--output是结果落盘方便后续解析。不同版本的 CLI 参数名可能略有差异比如有的用-p代替--prompt-file有的把 headless 模式叫exec或run。参数名以你本地--help输出为准别照搬网上的教程我见过太多人因为参数名对不上卡半天。3.2 评审清单怎么写才有效清单不是越长越好。我试过写二十条的清单结果 Agent 每条都蜻蜓点水输出质量反而下降。后来精简到六到八条每条都要求它给出具体行号 问题描述 修改建议输出立刻变得可用。一份我常用的清单长这样你是独立代码评审者。请针对输入内容逐项检查每项给出 - 是否存在问题是/否 - 若存在指出具体位置文件:行号 - 问题描述与修改建议 检查项 1. 逻辑正确性是否存在明显的逻辑错误或死代码 2. 边界条件空值、越界、并发等边界是否处理 3. 错误处理异常是否被吞掉或未处理 4. 命名一致性命名是否符合项目约定 5. 性能陷阱是否存在明显的低效写法 6. 安全写法是否存在注入、越权等风险模式注意最后一句每项给出是否存在问题这个格式约束非常关键。它逼着 Agent 对每一项都表态而不是挑几个它感兴趣的说说。没有这个约束它经常只报两三条就收工。3.3 把评审接进 CI 流水线本地跑通之后接 CI 是水到渠成的事。核心思路是在 CI 的某个阶段把 diff 和上下文准备好起一个评审进程把结果作为构建产物或评论输出。# 伪配置字段名按你所用 CI 平台调整 review_job: steps: - run: git fetch origin main - run: git diff origin/main...HEAD review.diff - run: | codex exec --headless \ --prompt-file .ci/review-checklist.md \ --input review.diff \ --output review.result - run: cat review.result这里有个坑要提前说CI 环境里的 Agent 调用通常需要凭证而凭证管理是安全红线。我的做法是把凭证放在 CI 平台的加密变量里绝不写进仓库也绝不在日志里打印。评审结果里如果包含敏感路径信息输出前要做一次过滤。注意评审 Agent 的输出不要直接当成通过/不通过的硬门禁。它会有误报硬门禁会误伤正常提交。我的做法是把它作为提示性评论人来决定是否阻塞合并。4. 踩过的坑评审 Agent 的误报、漏报与上下文陷阱4.1 误报的三种典型来源误报是评审 Agent 最让人头疼的问题。我统计过自己项目里一个月的评审输出误报大概占三成。归因下来主要是三类第一类是上下文缺失导致的误报。Agent 只看到 diff看不到被调用函数的定义就会说这个函数不存在。解决办法前面提过把完整文件内容一起喂进去。第二类是清单过于宽泛导致的误报。比如清单里写检查性能问题Agent 会把任何循环都标成性能隐患。把清单细化成检查是否存在嵌套循环导致的 O(n²) 复杂度误报立刻减少。第三类是模型对项目约定不了解导致的误报。比如项目里snake_case和camelCase混用是有意为之历史原因Agent 不知道就会一直报命名不一致。解决办法是在清单里附上一段项目约定说明。4.2 漏报往往比误报更危险误报烦人但无害漏报才是真问题。我遇到过最典型的一次漏报Agent 评审一个并发修改没发现两个 goroutine 同时写同一个 map结果上线后偶发崩溃。复盘时发现清单里根本没有并发安全这一项。所以清单要定期根据线上事故反推补充。每次出了 bug问一句如果评审清单里有这一项能不能提前发现能就加进去。这样清单会随着项目演进越来越贴合实际风险。4.3 上下文窗口的边界处理大项目里一次 diff 可能涉及几十个文件全塞进去会超上下文窗口。我的处理策略是分层核心改动文件完整内容喂入相关但未改动的依赖文件只喂函数签名和注释无关文件不喂这个分层逻辑可以写成一个脚本自动完成根据 diff 涉及的文件路径去仓库里捞对应的上下文。别偷懒全塞超窗口后 Agent 会截断截断位置之后的内容它完全看不到漏报就是这么来的。4.4 一个真实的排查链路有段时间评审结果里频繁出现无法解析输入的报错。我按这个顺序排查先看输入文件大小发现某个 diff 有 8MB怀疑超限手动截断到 1MB 重跑正常了确认是大小问题进一步查发现是某次提交误把构建产物也 diff 进去了在生成 diff 的命令里加上路径过滤排除dist/、build/等目录重跑问题消失这个链路说明一件事评审 Agent 的很多故障其实是输入准备阶段的故障别一上来就怀疑模型先检查你喂给它的东西干不干净。5. 让评审更准清单迭代、结果解析与人工兜底5.1 评审结果的结构化解析Agent 的输出是自然语言直接看很累。我一般要求它输出成固定格式然后用脚本解析成表格。比如要求每条问题以[严重度] 文件:行号 - 描述开头解析脚本就能按严重度排序、按文件聚合。import re pattern re.compile(r\[(高|中|低)\]\s(\S):(\d)\s-\s(.)) issues [] with open(review.result) as f: for line in f: m pattern.match(line.strip()) if m: issues.append({ severity: m.group(1), file: m.group(2), line: int(m.group(3)), desc: m.group(4), }) # 按严重度排序输出 order {高: 0, 中: 1, 低: 2} issues.sort(keylambda x: order[x[severity]]) for i in issues: print(f{i[severity]} {i[file]}:{i[line]} {i[desc]})有了结构化结果就能做很多事只把高严重度的推到人面前低的攒起来定期看统计哪类问题出现频率最高反过来优化清单。5.2 清单迭代的节奏清单不是写完就完事的。我的节奏是每两周回顾一次把这两周的评审结果翻一遍看哪些条目从来没报过问题可能太宽泛或没用哪些条目误报特别多需要细化哪些线上问题清单没覆盖需要新增。这个迭代过程本身就是价值。它逼着团队把我们到底在意什么想清楚而不是笼统地说代码质量要好。5.3 人工兜底不可省无论评审 Agent 多好用最终拍板必须是人。我见过有团队把评审结果直接当门禁结果因为一次误报阻塞了紧急修复得不偿失。正确的定位是Agent 负责广撒网人负责精判断。它把可疑点都列出来人快速扫一遍该忽略的忽略该改的改。提示把评审 Agent 的输出当成同事的初稿意见而不是权威结论。这个心态摆正了用起来会顺很多。6. 进阶玩法多评审子代理交叉验证与场景延展6.1 两个评审子代理交叉验证单个评审 Agent 会有盲区。我的进阶做法是起两个评审进程用不同的清单或不同的模型然后对比结果。两个都报的问题基本可以确认只有一个报的人工重点看。# 评审者 A侧重逻辑与边界 codex exec --headless --prompt-file checklist-logic.md \ --input review.input --output review.a # 评审者 B侧重安全与性能 codex exec --headless --prompt-file checklist-security.md \ --input review.input --output review.b # 取交集与差集 comm -12 (sort review.a) (sort review.b) confirmed.issues comm -3 (sort review.a) (sort review.b) needs.human.review这个模式成本翻倍但换来的是更高的召回率。对于核心模块的改动我觉得值。对于边角改动单评审就够了。6.2 评审场景不止于代码独立评审这套思路可以延展到很多场景我试过并且有效的有配置评审把配置文件 diff 喂给 Agent检查是否有拼写错误、是否有未识别的字段。热词里提到的unrecognized configuration setting这类问题用评审 Agent 提前抓非常合适。文档评审检查文档里的链接是否失效、术语是否一致、示例代码是否能跑通。SQL 迁移评审检查迁移脚本是否有锁表风险、是否有回滚方案。依赖变更评审检查新增依赖的许可证、体积、维护状态。这些场景的共同点是产出物是文本评审标准可以写成清单且人工评审很枯燥。只要满足这三点就适合交给 Headless Agent。6.3 与主开发流程的隔离原则最后强调一条架构原则评审 Agent 永远不要有写权限。它只读材料、只输出意见绝不碰仓库。我见过有人图省事让评审 Agent 直接改代码结果它把建议当指令执行了改出一堆问题。隔离的具体做法评审进程的工作目录是只读挂载的或者干脆在一个临时目录里跑材料是拷贝进去的。这样即使 Agent 想写也写不了安全边界清晰。7. 我在实际使用中总结的几条硬经验第一评审 Agent 的价值不在聪明在稳定。它不需要比人强它只需要每次都按同样的标准检查一遍不遗漏、不偷懒。人做不到每次都全神贯注它能。第二输入质量决定输出质量。花在准备评审材料上的时间远比花在调 prompt 上的时间值得。diff 干净、上下文完整、清单明确输出自然可用。第三别追求零误报。误报是评审 Agent 的固有特性接受它然后用结构化解析和人工兜底来消化它。追求零误报只会让你不断加约束最后把有用的信号也约束没了。第四清单要跟着事故走。每次线上出问题都问一句评审清单能不能提前发现能就加。这样清单会越来越贴合你的真实风险而不是网上抄来的通用模板。第五评审结果要留档。我习惯把每次评审的输入和输出都存下来一是方便回溯这个问题当时为什么没报二是积累一段时间后可以用来分析团队的高频问题类型反过来指导开发规范。这套用法我用了大半年最大的感受是Headless Agent CLI 最舒服的姿势真的不是让它替你干活而是让它替你盯着。写代码的活人自己干更靠谱但盯代码的活交给一个不知疲倦、不讲情面的独立评审者刚刚好。