
一、一个“只改一处”的需求为什么改了二十六处还是那个shop订单服务。需求很小POST /orders/{id}/cancel只允许PENDING待处理状态的订单取消取消时写一条审计事件。你把任务交给 Agent还特意写了三条要求只改订单模块、不要动依赖、不要改测试。它花了二十分钟回你一句“已完成”。你打开改动清单发现它改了二十六处。订单模块里的两个文件是主要改动这部分没问题剩下二十四处的来源是它顺手升级了pyproject.toml里的一个依赖版本理由是“新版本更稳定”它跑了格式化工具把二十个不相关文件的行距重排了一遍它觉得tests/orders/test_cancel.py里有两个用例“重复”把它们合并成了一个。三句叮嘱一条也没生效。这时候你很容易得出一个结论模型不听话。但如果你换一个角度看会发现这三句话从来没有被设计成会生效的东西。它们只是文本躺在对话里和几十页代码、日志、文档挤在一起。更具体一点那三句叮嘱分别属于三个不同的意图。“只改订单模块”是在限制范围“不要动依赖”是在限制权限“不要改测试”是在保护检查。三种意图需要三种不同的手段来落实而你把它们写成了同一种形式这是它们一起失效的原因之一。你真正缺的不是更严厉的措辞而是一圈围在模型外面的装置什么东西必须先给它它被允许碰哪些文件什么结果才算做完出错了怎么把信息送回去。这圈装置有个名字叫 harness。这个词本来指马身上的挽具套在马身上不是为了让它变聪明而是为了让它的力气朝一个方向走。这篇不讨论抽象的架构。我们只跟着上面这一次修改走完整条路把控制面拆成四个节点看清每个节点上具体放什么、缺了会发生什么。二、先把词讲明白Harness控制面包在模型外面的一圈装置用来决定它拿到什么材料、能碰什么、交什么、错了怎么办。它不提升模型的智力只限制它的作用范围就像给车装安全带并不会让车更会开。判断一个 harness 好不好标准是“出错的时候有没有东西拦住”不是“功能有多少”。节点checkpoint流程里一个明确的停顿点在这里有一件事被决定。比如开工前把任务单递过去就是一个节点跑完测试读出退出码也是一个节点。节点的特征是有输入、有判断、有输出三者缺一就不算节点。执行前before run模型还没动手的那段时间。这时候能控制的只有两样东西它看到什么它被要求交出什么。任务单、仓库规则文件、允许阅读的目录都放在这里。执行中during run模型动手的那段时间。这时候控制的是权限能写哪些目录、能不能联网、哪些命令需要先问人。权限之外的东西在这时候基本无效因为措辞只能影响概率不能阻止动作。执行后after run改完了判定能不能算完成。判定必须是命令的结果不是描述。测试退出码、静态检查、接口契约检查都可以放在这里。失败后on failure检查没过决定接下来怎么办。是把结构化的失败信息送回下一轮还是停下来交给人。这个节点决定了一次任务会变成一次对话还是一条可控的循环。沙箱sandbox技术上划出的边界规定程序能碰哪些目录、能不能联网。它作用在被启动的每一个命令上git、包管理器、测试命令都继承同一条边界。注意它管的是“能不能”不是“该不该”。审批approval什么时候必须停下来问人。它管的是“该不该”比如一条会重写历史的命令、一次会影响外部系统的调用。沙箱和审批是一对搭档一个画范围一个设关卡缺一个都会漏。验收条件acceptance condition把“做完了”翻译成可以判断真假的一句话最好是能跑出退出码的一句话。比如“pytest -q tests/orders/test_cancel.py退出码为 0”而不是“功能正确”。三、为什么这四个节点是必须的四个节点对应四个不同的问题给它什么、允许它做什么、怎么算完成、错了怎么办。这四个问题没办法互相替代这也是为什么只补其中一个问题还是会出现。先看“给它什么”。执行前的材料决定它会不会猜。上面那次修改里如果你的任务单只写着“加上取消限制”没有说状态有哪些、接口要返回什么、审计事件叫什么名字它就必须自己补全这些空白。补全的结果可能合理但和你想要的不是一回事。这一类错误不该被归到“模型不听话”它属于材料不全。再看“允许它做什么”。执行中的权限决定它会不会越界。这里最需要分清的是提示词约束和权限约束的区别。提示词说“不要改依赖”它是一句建议模型可能在某个时刻认为“升级依赖有助于完成任务”然后就改了而权限设置如果只允许写src/orders/和tests/orders/那它在改pyproject.toml的时候会直接被挡下来附带一次它无法忽略的信号。然后是“怎么算完成”。执行后的判定决定它能不能自己宣布完成。这一步如果缺失判定权就落在了被判定的人手里也就是模型自己的总结。前面那篇讲过的内容在这里会重复出现说法不是证据命令才有退出码。最后是“错了怎么办”。失败后的处理决定一次尝试会变成什么。如果失败之后没有把信息送回去模型只能凭猜测再试一次如果送回去的是几千行日志关键信息会淹没在中间这一点在长上下文里尤其明显Liu 等2024Lost in the MiddleTACL 12:157–173DOI: 10.1162/tacl_a_00638。结构化的失败信息能同时解决这两个问题。有一个问题值得单独回答既然仓库里已经有规则文件为什么还要节点因为规则文件是不带时间的。它躺在那里从第一次被读到任务结束都是同一段文字。而四个节点各有自己的时间执行前要递材料执行中要限权限执行后要判定失败后要反馈。规则文件的内容会在这四个时间点分别被用上但它本身不构成任何一个节点。顺便说清一件事AGENTS.md这类规则文件是怎么被读的因为这直接影响你该把规则写在哪一层。以 Codex 为例它会在开始工作时构建一条指令链全局层先读~/.codex/AGENTS.override.md没有就读~/.codex/AGENTS.md项目层从项目根目录逐层读到当前目录每层最多取一个文件优先AGENTS.override.md其次AGENTS.md再其次你在配置里列出的备用文件名。合并顺序是从根目录到当前目录越靠近当前目录的内容排在越后面优先级也越高空文件会被跳过合并后的内容默认有 32 KiB 的上限由project_doc_max_bytes控制。这条链每次运行都会重建不需要你手工清理缓存。这三条性质带来的实践建议很直接跟某个目录相关的规则放在那个目录下而不是继续加长根文件规则接近上限的时候要拆分而不是压缩措辞。那为什么不能只靠规则文件把四个节点都写成文字因为规则文件解决的是“知道”节点解决的是“发生”。知道和发生之间的距离在工程里从来不小。你可以把“不要改依赖”写进三个地方但只有权限能在它伸手去改的那一刻把动作停下来。换一个说法规则文件帮助模型做出更好的选择节点保证错误的选择也有后果。两者都需要但不能互相替代。四个节点之间也不是随便排序的它们之间存在交付物执行前交出的是任务单和材料执行中交出的是被限制住的改动执行后交出的是退出码和证据失败后交出的是六字段反馈。每个节点的输出正好是下一个节点的输入这条链断开在任何一处后面的节点都会失去依据。比如执行后拿不到退出码失败后节点就只能收到一句“失败了”反馈质量立刻下降。四、用一次取消订单的修改走完四个节点下面这个shop项目是虚构示例你可以照着搭一遍。这次我们不再关注“怎么写出好代码”只关注四个节点上各放了什么。4.1 执行前把材料递进去执行前这个节点只做两件事给材料、给标准。材料决定模型会不会瞎猜标准决定它能不能自己宣布完成。任务单可以短但要把四件事写清改什么、参考哪些文件、不要做什么、怎么算完成。任务让 POST /orders/{id}/cancel 只允许 PENDING 订单取消并写一条审计事件 参考文件先只读这三份 src/orders/services/cancel_order.py src/orders/domain/order.py tests/orders/test_cancel.py 可改范围src/orders/**、tests/orders/** 不要做改依赖清单、改 CI 配置、改其它模块、删除或弱化已有断言 验收条件 1) pytest -q tests/orders/test_cancel.py 退出码为 0 2) 新增一条用例PAID 订单取消时返回 409 交付物 代码改动 一条命令记录命令 / 退出码 / 输出摘要 / 版本号这张任务单里有三处细节值得留意。第一参考文件只列了三份而不是整个仓库。材料给多了会稀释关键信息前面的位置效应结论同样适用。第二“不要做”列的是可检查的行为不是态度比如“不要改依赖清单”可以直接用git diff --stat核对。第三验收条件里包含一条现在还不存在的测试这意味着任务从第一天起就带着一个会失败的检查。项目级的长期规则放在AGENTS.md里只回答四类问题项目做什么、代码放哪、怎么验证、什么不能做。# AGENTS.md项目根目录 ## 项目做什么 shop 是订单服务Python 3.12 FastAPI PostgreSQL。 核心用例创建订单、支付订单、取消订单。 ## 代码放哪 - 接口层src/orders/api - 领域模型与规则src/orders/domain - 用例编排src/orders/services - 持久化src/orders/repositories - 测试tests/orders ## 怎么验证 - 提交前bash scripts/verify.sh - 只跑订单模块pytest -q tests/orders ## 什么不能做 - 不改 pyproject.toml 与 CI 配置 - 不删除或弱化 tests/ 下已有断言 - 不让 domain 层直接依赖数据库驱动如果src/orders/services/下还有更细的约定比如“这个目录下每个用例函数都要显式接收 repo 和 audit不要使用全局单例”那就把这些约定放在src/orders/services/AGENTS.md里。规则离它管辖的目录越近被用上的机会越大也不会挤占根文件那 32 KiB 的额度。4.2 执行中把权限收窄执行中这个节点只做一件事限制能做什么。提示词在这时候已经不起作用了因为模型的动作正在发生唯一能拦住它的是权限。权限可以按“四问”来设能写哪些目录、能不能联网、哪些命令必须先问人、剩下的默认行为是什么。上面对应的答案大概是只能写src/orders/和tests/orders/不需要联网因为测试用本地数据库删除类命令和数据库迁移命令需要人工确认其余按默认的保守值处理。下面这段只是表达意图的示意字段名不要照抄具体写法以你当前版本的官方文档为准# 权限意图示意不是可直接使用的配置文件 可写目录src/orders、tests/orders 网络关闭 文件系统规则**/*.env 设为 deny禁止读写 需要审批数据库迁移、删除文件、发布相关命令官方文档里给出的实际能力是这样的权限配置文件标注为 beta提供内置的:read-only、:workspace、:danger-full-access三种预设自定义配置可以设置可写目录、文件系统规则read / write / deny例如把**/*.env设为 deny以及网络域名规则。如果你的任务只是让 Agent 读代码、给出建议、不要改文件用read-only模式更省心在 Codex 里权限预设可以用/permissions切换。沙箱的行为也有几条通用性质需要记住。它作用在被启动的命令上所以git、包管理器、测试命令都继承同一条边界本地默认不联网写权限通常限制在当前工作区不同平台的实现不同macOS 用 SeatbeltLinux/WSL2 用 bubblewrap原生 Windows 在 PowerShell 中使用 Windows 沙箱具体能力以你当前的环境为准。命令级的规则可以单独写。官方文档里把这部分标注为实验特性写法是放在rules/目录下的.rules文件里例如~/.codex/rules/default.rulesprefix_rule(pattern[git, push, --force], decisionforbidden, justification禁止改写远端历史) prefix_rule(pattern[alembic, downgrade], decisionprompt, justification迁移降级需要人确认)两条规则的含义分别是“直接禁止”和“必须先问人”。多条规则同时匹配时取最严格的一条forbidden 比 prompt 严格prompt 比 allow 严格。想确认某条命令会得到什么结果可以在本地先跑一次codex execpolicy check --pretty --rules 文件 -- 命令不用等到真正执行时才发现规则写错了。沙箱和审批要分开理解因为它们回答两个不同问题。沙箱回答“技术上能不能”比如能不能写工作区之外的目录、能不能发起网络请求审批回答“要不要停下来问人”比如一条破坏性很强的命令。只设置沙箱你会遇到“它能做但本不该做”的情况只设置审批你会遇到“每一步都要问人流程没法自动化”的情况。4.3 执行后用命令判定完成执行后这个节点只做一件事拿到一个机器给出的结论。pytest-qtests/orders/test_cancel.pyechoexit$?假设这次输出是FAILED tests/orders/test_cancel.py::test_paid_order_cannot_be_cancelled 1 failed, 3 passed in 0.58s exit1按照任务单的验收条件这次不算完成。这里有一个容易忽略的细节任务单里那条“新增一条用例PAID 订单取消时返回 409”现在还没写出来所以哪怕pytest退出码是 0验收条件也只满足了一半。验收条件写成清单的好处就在这里它让你能一眼看出“跑到哪一步了”。证据要和结论一起留下版本: 3f9a2c1 命令: pytest -q tests/orders/test_cancel.py 退出码: 1 摘要: 1 failed, 3 passed 日志: artifacts/orders-cancel-3f9a2c1.log如果你们把 Agent 放进了自动化流程验证步骤最好独立于 Agent 步骤。codex exec这个入口可以让你在脚本和 CI 里非交互地跑任务它默认在只读沙箱里运行需要写文件时要显式加--sandbox workspace-write--output-schema ./schema.json用来要求最终输出符合某个 JSON Schema-o 路径把最终消息写到文件--json输出 JSONL 事件流方便事后核对它跑了哪些命令。只有在受控环境里才考虑--sandbox danger-full-access。这里还有一条值得抄进团队约定的提醒不要把 API key 设成整个 CI job 的环境变量。同一个 job 里运行的仓库代码、依赖安装脚本和测试都可能读到它。相对稳妥的做法是把凭据限制在确实需要它的那一步里。相似的设计也出现在云端环境里依赖安装放在 setup 阶段agent 阶段默认离线配置的密钥只在 setup 阶段可用并在 agent 阶段开始前移除。4.4 失败后把信息送回去失败后这个节点做两件事把失败压成可用的输入以及决定还要不要继续。命令: pytest -q tests/orders/test_cancel.py 退出码: 1 失败用例: tests/orders/test_cancel.py::test_paid_order_cannot_be_cancelled 关键断言: assert audit_log.events [] - 实际 [order.cancelled] 变更版本: 3f9a2c1 停止条件: 同一用例连续失败 2 次或两次修改方向相反 - 交给人六个字段每个都有用。命令和退出码说明发生了什么失败用例名把人直接带到现场关键断言说明期望和实际差在哪版本避免了“你改的不是我看到的那一版”停止条件防止循环失控。如果失败信息里只有“命令失败”四个字下一轮只能靠猜。如果失败信息里有几百行日志关键行会被淹在中间。六个字段是一个折中信息足够定位长度足够读得完。这里还有一个容易漏掉的动作确认这次失败是真的失败。区别很实际——如果失败来自环境数据库没起来、端口被占用那么把“修代码”作为下一轮的输入就是错的正确做法是先把环境问题变成一条明确的错误信息再决定要不要重试。判断方法很简单同一条命令在同一个版本上重跑一次如果结果不一样先怀疑环境。4.5 四个节点的对照把四个节点放在一起对照每一格都填具体值不要填形容词节点它回答的问题这次的落点缺了会怎样执行前材料给全了吗任务单写清状态限制、接口响应、可改范围、必须跑的测试模型靠自己补全空白改到你不想要的范围执行中允许做什么只能写src/orders/、tests/orders/网络关闭迁移命令需审批顺手改依赖、重排无关文件、删测试断言执行后怎么判定完成pytest -q tests/orders/test_cancel.py退出码为 0且新增用例存在“看起来对”被当成完成失败后信息怎么回去失败命令、退出码、用例名、关键断言、版本、停止条件重复试错上下文里堆满过期结论这张表可以直接贴进你的任务模板。它的作用不是记录流程而是让每一次任务都有同样的四个空白要填。4.6 把四个节点连起来跑一遍[执行前] $ cat docs/tasks/cancel-order.md # 任务单范围、验收条件、交付物 $ git switch -c fix/cancel-status-check [执行中] $ codex exec --sandbox workspace-write 按 docs/tasks/cancel-order.md 修改 # 可写目录被限制在订单模块内 [执行后] $ pytest -q tests/orders/test_cancel.py 1 failed, 3 passed in 0.58s # 退出码 1 $ git rev-parse --short HEAD 3f9a2c1 [失败后] $ cat artifacts/cancel-failure-3f9a2c1.txt # 六个字段的失败报告 $ codex exec --sandbox workspace-write 按 artifacts/cancel-failure-3f9a2c1.txt 修复 $ pytest -q tests/orders/test_cancel.py 4 passed in 0.51s # 退出码 0可以交付四段之间没有一处需要额外解释。这就是控制面的价值它不要求每一步都很聪明只要求每一步都有明确的输入和输出让下一个人或者下一轮接得上。4.7 把开头那二十六处改动回放一遍现在回到这篇开头的那次任务三句叮嘱没生效改动清单上多了二十四处在计划之外。把它按四个节点回放一遍你能看清每一类问题该由哪个节点负责。第一类是升级依赖。这个动作属于“越界修改”责任人应该是执行中的权限配置可写目录里没有pyproject.toml它就会被挡下来并且在那一刻留下一条清晰的信号。如果权限还没配那么执行前的任务单里“不要改依赖清单”能降低发生概率执行后的差异审查能发现它但两者都比权限慢一步。第二类是格式化重排了二十个无关文件。这一类最容易被当成“无害”因为逻辑没有变。它的代价在评审环节真正需要看的两个文件被二十个文件的变化稀释评审人会开始只看摘要。对应的控制点在执行前说清可改范围和执行中可写目录限制必要时可以在执行后加一条“改动文件数量超过预期范围就提示”的检查。第三类是合并测试用例。这一类最危险因为它让检查本身变弱了。执行后的验证要能识别这种变化如果被测代码没改多少、测试却在减少或断言强度下降就应该停下来看一眼。一个简单的做法是把测试文件的变化单独列出来尤其是删除和断言修改这两类让它们必须被人确认。把这三次回放放在一起会看到一个规律越靠前的节点拦下的问题你付出的代价越小。权限拦下的越界修改代价是一次拒绝执行后拦下的越界修改代价是一次完整的评审和回退。4.8 执行前到底该给什么材料执行前这个节点最常见的两种浪费一种是给太少一种是给太多。给太少模型开始猜给太多关键信息被稀释这一点在长上下文里的表现已经被反复验证过。下面这张表按“什么时候给”把材料分开材料什么时候给不给会怎样任务单一屏每次任务范围漂移改到计划之外的文件层级规则文件每次任务按目录自动生效违反分层约定或漏掉必须跑的验证相关代码文件几个到十几个每次任务按需猜接口名、猜字段、猜错误类型失败报告六字段只在重试时重复试错反复改同一个地方全量日志一般不关键行被淹没成本上升整个仓库的粘贴内容不上下文被占满重要规则被挤到中段这张表里的判断标准是相关性不是数量。一个文件如果和这次改动没有因果关系它的价值接近零但它的成本一直在。还有一个细节容易被忽略材料的顺序也会影响效果。把验收条件和“不要做”的部分放在任务单开头比放在末尾更可靠把失败报告里的用例名和断言放在开头比放在日志之后更可靠。4.9 证据放在哪里怎么命名证据如果随手放在聊天记录里一周之后就没了。给它一个固定的落点比如项目的artifacts/目录并约定命名规则artifacts/orders-cancel-3f9a2c1.log # 任务-命令-版本.log artifacts/orders-cancel-3f9a2c1.txt # 同一次的失败报告或摘要命名里带上任务名和版本号有两个直接好处你不用打开文件就知道它对应哪次改动别人拿到一个提交号就能猜到对应的日志文件名。落点最好放在不进入版本库的目录或者由 CI 作为构建产物保存避免把日志混进代码差异里。检查方式很简单随便挑一个上周的提交看能不能在三十秒内找到它对应的命令和退出码。找不到说明你的证据还是散的。4.10 验收条件的三种写法同样一条验收条件写法不同可判断的程度完全不同写法例子能不能自动判断形容词取消功能正确、体验良好不能行为描述已支付订单取消时返回错误半能需要人读代码或手动试命令pytest -q tests/orders/test_cancel.py::test_paid_order_cannot_be_cancelled退出码为 0能三种写法对应三种不同的成本。形容词的成本最高因为它把判断推给了下一个人行为描述好一些至少说清了预期但每次判断都要有人在场命令最省因为结论由机器给出。实际写任务单的时候可以先写行为描述再为它配一条命令。行为描述帮助你确认自己想要的到底是什么命令负责把它变成可执行的检查。两者都要不能只留一个。五、反例与代价反例一用措辞代替权限。在提示里写“不要改配置”“不要装依赖”看起来是最省事的做法因为它不需要配置任何东西。它的代价有两个一是拦不住措辞只改变概率某个时刻模型认为升级依赖有助于完成任务它就会改二是难以追溯越界之后你只能在 diff 里发现而且分不清是它有意的还是理解偏了。权限约束的好处恰恰是它会在动作发生的那一刻给出信号而不是等到事后。反例二只做最后一个节点。有些团队觉得验证最重要于是只在最后加了 CI 检查前面的材料、权限、反馈都不管。这样做确实拦住了坏代码但代价全部转移到返工上每次出错都要等改完、跑完、红了才发现方向从一开始就偏了。四个节点的顺序本身就是效率顺序越靠前的节点越便宜。反例三节点之间互相矛盾。任务单里写“只有PENDING能取消”测试里却断言“已支付订单取消返回 200”模型在这种矛盾里只能选一个。它选哪个都不算错但你会收到一个和预期不符的结果。检查方法很简单把任务单的验收条件和测试文件放在一起读一遍看它们说的是不是同一件事。反例四把控制面理解成“工具装得越多越好”。工具本身不产生控制只有被执行的检查才产生控制。装了一个静态检查但没人看结果和一个只写在文档里的约定效果是一样的区别只在于多了一份维护成本。选择顺序应该是先看哪类错误重复出现、影响有多大、能不能被自动检测出来再决定要不要为它加一个工具。反例五规则文件变成堆放场。一开始只有十条规则半年后变成八十条每条都“很重要”。结果是越靠后的规则越难被用上而接近上限的那部分还有被截断的风险。处理方式是分层跨模块的少量规则留在根文件目录相关的规则放到子目录过期的规则直接删掉。规则不是资产清单它更像作业指导书写得准比写得多有用。反例六把“审批”当成主要防线。每个关键动作都弹一次确认看起来最安全实际上最容易失效人会在第十次确认时开始无脑点同意。更好的组合是把大部分越界变成不可能权限只在少数真正需要判断的地方设关卡审批并且让人有足够上下文做判断。五种反例的代价可以归到一句话上控制面缺一个节点错误就会在那个位置漏过去控制面加得太多太杂维护成本会从另一边漏回来。四个节点是下限不是上限超出的部分要看它是否真的解决了一类反复出现的问题。还有一个判断标准可以帮你取舍这个控制项有没有可能被绕过。可以被绕过的检查要么补上后果要么降级成提醒。比如“提交前请运行脚本”是一句可以被忽略的话而“CI 里这条命令不通过就不能合并”是一条不能被忽略的规则。两者说的是同一件事力度差很多。六、落地步骤第一步为这次任务写一页任务单。做什么写清改什么、参考哪些文件、不要做什么、怎么算完成、交付什么。为什么执行前的材料决定模型会不会自行补全空白而自行补全的部分最容易偏离你的意图。怎么检查把任务单交给一个不了解背景的同事读一遍如果他能说出“改完之后我该跑哪条命令”说明写清楚了。第二步确认长期规则文件只回答四类问题。做什么检查AGENTS.md里是不是只有项目做什么、代码放哪、怎么验证、什么不能做这四类内容多出来的部分挪到子目录。为什么指令链有 32 KiB 的默认上限越接近上限越靠后的规则越容易被稀释。怎么检查数一下根文件里的规则条数。如果超过二十条或者里面有大量和具体目录绑定的细节就是该拆分的时候了。第三步写下这次任务的权限边界。做什么明确可写目录、网络开关、需要审批的命令以及默认行为。为什么权限是执行中唯一真正起作用的限制提示词的约束力在这个阶段大幅下降。怎么检查自己试着改一个范围外的文件看是否被挡住。挡不住就说明你的边界还没生效。第四步把验收条件写成可以出退出码的命令。做什么为每条验收条件配一条命令并写清通过判定。为什么只有命令能给出机器结论形容词不能。怎么检查在改动之前先跑一遍确认这条命令现在是失败的。一条从不失败的检查等于没有检查。第五步把证据的落点定下来。做什么约定证据保存在哪里包含哪些字段命令、退出码、输出摘要、版本号、日志路径。为什么证据让不同时间、不同人对同一个结论有共同的事实基础。怎么检查随机挑一次改动看看能不能从提交记录追到一条明确的命令和它的退出码。第六步定义失败反馈模板。做什么把上面那六个字段固定成一个模板任何人或任何 Agent都能照着填。为什么结构化反馈减少下一轮的猜测空间也让“事实”和“建议”分开摆放。怎么检查拿最近一次失败按模板重写一遍。如果重写时缺字段说明采集环节要补。第七步给循环加上停止条件。做什么约定最多重试几次、出现哪些信号就停下来交给人。为什么没有停止条件的循环会消耗时间也会把过期结论堆进上下文。怎么检查把停止条件写进任务模板并抽查一次真实任务看它是否被遵守。把七个步骤合成一份可以直接复制的四节点清单[ ] 执行前 · 材料规则文件路径 任务单路径 本次验收条件 [ ] 执行中 · 权限可写目录 网络开关 需审批命令 默认行为 [ ] 执行后 · 验证命令 通过判定 证据保存位置 版本记录方式 [ ] 失败后 · 反馈回传字段 最大重试次数 接管条件清单填完之后你会发现最有用的不是那几行字而是每格后面那个尖括号。它逼你把“我们要很小心”翻译成“可以写src/orders/这两个目录”。四个节点各自失效的时候现象很不一样可以按下面这张表对号入座节点失效时的现象怎么确认先改什么执行前改动范围和你说得不一样方向偏了对比任务单和git diff --stat任务单里补“不要做”和“参考文件”执行中出现计划外的文件改动、依赖变动看差异里有没有范围外的路径收窄可写目录必要时关掉网络执行后有人说“测试通过”但没人能复现追问命令、退出码、版本三样把验收写成一条能出退出码的命令失败后同一个错反复出现改动方向来回摆比对两次失败报告的字段换成六字段模板并加停止条件这张表的用法是反过来的先观察现象再定位节点最后改配置。直接猜“是不是模型不行”通常是最贵的一条路。如果这七个步骤一次做完太多可以按这个顺序分批上线第一周只做执行后的验证让每次改动都有一条命令的结论第二周补失败后的反馈模板让循环变得可读第三周补执行中的权限把可写目录收到最小第四周再回头精简执行前的材料把长期规则和任务单分开。每一步都能独立带来收益也不需要等前面全部就绪。七、FAQ问harness 和提示词是什么关系提示词是 harness 里的一部分属于执行前节点里的材料。它们的区别在于作用方式提示词改变模型做某件事的概率harness 的其他部分改变结果权限决定它能做什么验收决定什么算完成反馈决定错了以后怎么走。只优化提示词等于只优化了四个节点中的一个。问四个节点能不能只做一部分能但要知道自己放弃了什么。如果只能先做一个选“执行后”的验证因为它能立刻拦住坏代码如果只能做两个再加“执行中”的权限因为它能防止越界修改第三个补“失败后”的反馈它决定返工效率最后一个补“执行前”的材料它减少的是方向性错误这类错误发现得最晚也最贵。问规则写在AGENTS.md和写在任务单里有什么区别时间尺度不同。AGENTS.md里的规则会在这个项目的每一次任务里被读到适合放长期不变的约定比如代码分层和验证命令任务单只影响这一次适合放本次的范围、参考文件和验收条件。常见错误是把一次性要求写进长期规则半年后没人记得它为什么在那里也没人敢删。问权限收得太紧Agent 干不了活怎么办先分清是“缺权限”还是“缺信息”。缺权限的表现是动作被挡下来日志里能看到具体是哪个路径或哪条命令缺信息的表现是它反复试不同的改法。前者按需放开一个具体的目录或命令并且带上审批后者应该补材料而不是补权限。放开权限的时候尽量写具体值比如某个目录而不是整块工作区之外的范围。问沙箱和审批到底怎么配合沙箱画的是范围审批设的是关卡。一个实用的组合是范围尽量小关卡尽量少。范围小越界就变成不可能而不是需要讨论的问题关卡少人就不至于被频繁打断。比如把网络关掉、把可写目录限制在订单模块同时对数据库迁移和删除操作保留审批。这样既不会天天问人也不会出现“它能做但不该做”的窗口。问为什么我的规则文件越来越长效果却越来越差因为规则也要占用被真正使用的机会。合并内容有 32 KiB 的默认上限而且信息在长上下文中的位置会影响它被利用的效果。规则一多靠后的条目和细节条款就容易被稀释。处理办法是分层根文件只放跨目录的少量规则细化规则放到对应子目录的文件里删掉那些已经由门禁自动保证的条目它们不需要再占用说明文件的篇幅。问多个 Agent 同时改一个仓库时控制面要怎么调整主要变化在执行中和执行后两个节点。执行中要给每个任务划出不重叠的目录避免两个任务同时改同一个文件如果做不到隔离就用独立的临时环境让每个任务在各自的副本上工作。执行后要有统一的合并门禁任何分支都要过同一条命令。失败后节点的停止条件也要更严格因为多个循环同时跑的时候互相冲突的改动会更难排查。问怎么知道我的控制面真的有效做一次假失败实验故意把一条断言改错然后走一遍完整流程看它会停在哪个节点。理想情况下执行后的验证会拦住它如果你期待它更早被拦住就去补前面两个节点。比如你希望它根本不该改测试断言那检查一下权限是不是允许写tests/以及规则里有没有写清“不要弱化断言”。问这套东西在很小的项目上值得做吗值得但可以直接用最小版本一页任务单、一条验收命令、一份失败报告模板。三样东西加起来不到一个文件却能把四个节点都覆盖住。等到项目变大你再把权限配置、规则分层和自动化流程逐步补上。问四个节点里哪一个最容易被忽略执行中。因为它不像写文档那样有产出也不像跑测试那样有结果它只是在开工前调一下设置。很多人第一次做控制面时会把三个节点都补上唯独不设权限理由是“我信任它”。等到某次改动顺手动了一个配置目录才会回头补。这个节点的特点是平时看不出价值出事时唯一有用。问任务单写多长合适一屏能读完。它的作用是消除歧义不是写设计文档。判断标准是任何一个接手的人人或 Agent读完都能说出“改哪里、不能碰什么、跑哪条命令算通过”。如果任务单长到需要目录说明你需要的是一份设计说明而不是任务单。问如果模型能力提升这些控制还需要吗需要但重心会移动。能力提升会让执行前的材料更省、失败后的重试次数更少但“谁来判定完成”这个问题不会消失因为任何判断都需要一个外部信号。反过来说能力越强越值得把权限边界画清楚能力越强越界造成的影响越大。控制面解决的是流程的可判定性不是模型的聪明程度。问这套东西和代码评审是什么关系两者解决不同的问题。评审解决“这个设计是否合理、命名是否合适、有没有隐藏的业务风险”这些是机器判断不了的控制面解决“它有没有跑过我要求的检查、有没有改到范围之外”这些是不该由人来盯的。把这两件事混在一起评审会变成逐行核对命令输出而设计问题会被漏掉。理想的分工是机器先过一遍可判定的部分人只在通过之后看需要判断的部分。问如果团队里没人愿意填任务单怎么办从模板开始不要从制度开始。给大家一个可以直接复制的五段式任务单前几次由愿意填的人填把填好的任务单和对应的提交放在一起。等到出现一次“因为任务单里写了不要改依赖所以 Agent 没有越界”的实际例子其他人会自己开始用。控制面的推广靠的是它省过的时间不是它的正确性。八、动手练习与小结这次的动手练习产出两样东西。第一样是四节点清单。打开你手头的一个真实项目按下面四行填写具体值每一格都要能对应到文件、目录或命令[ ] 执行前 · 材料____________ [ ] 执行中 · 权限____________ [ ] 执行后 · 验证____________ [ ] 失败后 · 反馈____________填完之后做一次自检。执行前那一格如果写的是“让它理解需求”说明还是形容词执行中那一格如果写的是“不要乱改”说明还没有权限执行后那一格如果写的是“测试通过”说明还没有命令失败后那一格如果写的是“重新试一次”说明还没有反馈格式。第二样是假失败实验的记录。任选一个真实任务故意制造一次失败记录三件事它在哪个节点被拦住、拦截信号是什么退出码、拒绝写入、审批请求、你会不会调整节点配置。这份记录的价值在于它把“我觉得应该有保障”变成了“我见过它拦住东西”。做完之后可以用五个问题复查一遍每个问题都应该能用一句话回答1. 这次任务的材料在哪个文件里 2. 它被允许写哪些路径 3. 哪条命令的退出码决定这次任务算不算完成 4. 失败时它会收到哪些字段 5. 循环最多跑几次之后交给谁五个问题里任何一个答不上来对应的节点就还是空的。这时候先别急着加工具把那一格填上具体值收益会更大。回到开头那个改了二十六处的需求。如果四个节点都在那次任务的样子会完全不同任务单限制了可改范围权限挡下了pyproject.toml的改动验证命令认出了被弱化的断言失败反馈让下一轮知道该修什么。四个节点没有一个需要模型变得更聪明。这一篇讲的是控制面上的四个节点。往后几篇会分别把其中两处展开一处是“前馈”和“反馈”这两种不同的控制方向另一处是当循环开始失控时该怎么设停止条件。弄清楚节点的位置之后下一个问题就是每个节点上放什么内容最有效。如果你只带走一句话可以是这句控制面不是给模型加规矩而是给流程加判断。规矩写在纸上判断发生在节点上。四个节点各自回答一个问题四个问题都有人回答一次任务才算被真正管住。