ChatGPT、Codex工程方法:复杂任务开始前,为什么先定义“不变量”,比多写Prompt更重要?
最近用 ChatGPT、Codex 跑复杂开发任务时,我越来越少把主要精力放在:
“Prompt还能不能再写详细一点?”
因为任务越复杂,真正决定Agent会不会跑偏的,往往不是Prompt长度。
而是:
有没有几条始终不能被破坏的规则。
比如你让Agent优化支付流程。
Prompt可以写得很详细:
分析代码。
检查调用链。
补测试。
优化异常处理。
验证并发场景。
这些都没问题。
但如果没有明确告诉它:
同一个支付事件只能产生一次业务副作用
旧API必须保持兼容
权限范围不能扩大
历史订单必须继续可读
那Agent在处理某个局部问题时,很可能为了“把当前问题解决掉”,顺手破坏这些更重要的边界。
所以复杂任务开始前,我现在更关注一个概念:
Invariant——不变量。
也就是:
无论实现怎么改、计划怎么变、Agent执行多少步,这些条件始终必须成立。
一、Requirement和Invariant不是一回事
很多人会把两者混在一起。
比如Requirement是:
增加订单部分退款能力
这是:
要做到什么。
但Invariant可能是:
退款总金额不能超过原订单金额
旧版客户端仍然能够读取订单
同一退款事件不能重复产生副作用
它回答的是:
做的过程中,什么绝对不能被破坏。
所以两者的区别可以简单理解成:
Requirement
告诉Agent往哪里走
Invariant
告诉Agent哪些边界永远不能跨
复杂任务里,这两个都需要。
但很多Prompt只写了前者。
二、为什么任务越长,不变量越重要?
因为长任务里:
方案可能改变。
计划可能重建。
文件范围可能扩大。
中间假设也可能被推翻。
例如一开始准备:
直接调用RefundService
后来发现更适合:
Event Driven
实现方式已经完全变化。
但无论哪种方案:
同一退款只能成功一次
这个规则都不能变。
所以相比:
“必须按照方案A执行”
更稳定的表达其实是:
“方案可以变化,但这些条件始终必须成立。”
这会给Agent更多实现自由,
同时保留真正重要的工程边界。
三、Prompt写得再长,也可能被后续上下文稀释
复杂任务跑几十轮以后,
上下文里会不断加入:
日志。
错误。
测试结果。
代码修改。
新发现的问题。
这些信息会越来越多。
最初Prompt里的某些要求,
可能慢慢变成上下文中的一小部分。
尤其当Agent正在处理一个很具体的失败时,
注意力很容易集中到:
怎么把眼前这个错误解决。
而不是重新回看:
几十轮之前写的所有要求。
所以真正关键的约束最好不要埋在几千字Prompt里。
而应该单独提炼成:
一小组高优先级Invariant。
四、一个好的不变量,应该足够具体
比如:
保证系统稳定。
这不算好的Invariant。
因为Agent根本不知道:
什么叫稳定。
更好的写法是:
任何重试都不能产生重复订单
或者:
现有API字段不能删除或改变语义
再比如:
普通用户不能获得管理员权限
这些条件有一个共同特点:
可以验证。
如果一个Invariant无法验证,
它最终很容易退化成一句口号。
五、哪些东西最适合定义成Invariant?
我一般会从几个方向找。
第一类:数据不变量
例如:
账户余额不能小于0
退款金额不能超过支付金额
同一业务ID必须唯一
第二类:兼容性不变量
例如:
旧客户端仍然能够调用现有API
历史数据必须继续可读
旧事件格式在迁移期内必须兼容
第三类:权限不变量
例如:
普通用户不能读取管理员数据
Agent新增功能不能扩大现有权限范围
第四类:副作用不变量
例如:
同一支付事件最多产生一次扣款
数据库回滚不能被误认为外部操作也已回滚
第五类:架构不变量
例如:
领域层不能直接依赖基础设施实现
外部调用不能放进核心数据库事务
这些规则比具体文件结构更稳定。
六、为什么Agent特别容易破坏不变量?
因为Agent通常优先处理:
当前错误。
比如测试失败。
它可能发现:
把一个校验删掉以后,
测试就能通过。
从局部看:
问题解决了。
但那个校验可能恰好保护着:
退款金额不能超过订单金额
这时候:
Local Fix破坏了Global Invariant。
Agent不一定会自动意识到这一点。
尤其是当Invariant没有被明确写出来时。
七、所以测试也应该围绕Invariant设计
很多测试只验证:
某个函数输出。
某个接口状态码。
某个类有没有正常运行。
但对于复杂Agent任务,
还应该增加一类:
Invariant Test。
例如:
不管重试几次:
最终只能产生一条退款记录
不管新旧版本怎么混跑:
旧客户端始终能读订单
不管Agent如何重构:
普通用户始终无法访问管理员接口
这些测试不关心:
内部怎么实现。
只关心:
边界有没有被破坏。
这对Agent特别重要。
八、不变量应该比实现更稳定
例如当前实现是:
Redis分布式锁
用来保证:
同一个任务只能执行一次。
后来可能改成:
数据库唯一约束。
或者:
幂等Key。
实现可以换。
但真正需要保持的是:
同一业务操作只能生效一次
所以不要把:
“必须继续使用Redis锁”
当成Invariant。
真正的Invariant应该是:
业务或系统必须始终成立的事实。
实现只是实现。
九、复杂任务开始前,我会先让Agent提炼Invariant
比如一个需求:
重构订单退款流程。
不要马上让Codex开始改代码。
可以先让它回答:
请先识别这次任务中必须始终保持成立的关键不变量。
包括:
- 数据一致性
- API兼容
- 权限
- 副作用
- 历史数据
不要开始修改代码。
最后可能得到:
INV-01
同一退款请求最多成功一次
INV-02
累计退款金额不能超过订单支付金额
INV-03
旧客户端API行为保持兼容
INV-04
历史订单无需迁移即可读取
确认以后,
再开始执行。
这比一开始直接加一句:
“请谨慎修改。”
有效得多。
十、给Invariant编号,会更适合长任务
比如:
INV-01:支付幂等
INV-02:API兼容
INV-03:权限不扩大
INV-04:历史数据可读
后面的每个重要修改都可以问:
是否影响INV-01?
是否影响INV-02?
测试结果也可以绑定:
INV-01 PASS
INV-02 PASS
INV-03 PASS
INV-04 PASS
这样长任务跑到后面,
不变量仍然非常清晰。
十一、多Agent场景里,不变量更加重要
假设:
Agent A改数据库。
Agent B改API。
Agent C补测试。
如果三个人只知道自己的子任务,
就很容易局部最优。
但如果所有Agent共享同一组:
INV-01
INV-02
INV-03
那至少它们有共同边界。
比如Agent A想改变数据结构,
必须检查:
是否破坏历史兼容。
Agent B改接口,
必须检查:
是否破坏旧客户端。
Agent C测试时,
也知道:
哪些规则是必须覆盖的。
所以多Agent协作里,
Invariant其实是一种:
Shared Constraint。
十二、需求变化时,不变量也应该重新检查
不变量并不是永远不变。
业务规则本身如果变化,
Invariant也可能更新。
但重点是:
必须显式变化。
例如原来:
API必须100%兼容旧客户端
后来产品明确决定:
新版本允许Breaking Change。
那就应该:
INV-02 DEPRECATED
再建立新的边界。
而不是让Agent在执行过程中,
默默把旧Invariant破坏掉。
这和Plan版本管理其实很像。
十三、不要定义太多Invariant
这里也不能走极端。
如果一次任务定义:
30条Invariant。
Agent每改一步都要检查30条,
工作流会变得非常重。
真正关键的是:
少而关键。
通常复杂任务保留:
3—7条核心Invariant
已经非常有价值。
重点覆盖:
最严重的业务风险。
最难回滚的变化。
最容易被局部修改破坏的边界。
十四、什么时候说明你的Invariant写得不好?
有几个信号。
第一:
太抽象。
例如:
保持高质量
无法验证。
第二:
太像实现。
例如:
必须使用Redis
它限制的是方案,不是边界。
第三:
太多。
几十条全部都是最高优先级。
最后等于没有优先级。
第四:
互相冲突。
比如:
不能改变API
同时又要求:
必须新增一个强制字段
这种任务应该先解决约束冲突,
而不是直接执行。
十五、我更推荐这样的复杂任务启动模板
例如:
目标:
重构退款流程,提高幂等可靠性。
关键不变量:
INV-01:
同一退款事件最多产生一次业务副作用。
INV-02:
累计退款金额不能超过原支付金额。
INV-03:
现有API保持兼容。
INV-04:
历史订单数据必须继续可读。
要求:
每完成一个关键修改,都检查是否影响以上Invariant。
如果某个方案会破坏Invariant,先暂停并说明原因,不要直接执行。
这段并不长。
但它比继续增加:
背景介绍。
解释文字。
大量实现细节
更能稳定长任务。
十六、给自己看一个指标:不变量保持率
这篇我建议只看一个指标:
Invariant Preservation Rate——不变量保持率
可以简单理解为:
任务执行过程中始终保持成立的关键Invariant数量 ÷ 全部关键Invariant数量
例如一次任务定义:
5条Invariant。
最后有4条一直保持。
1条在中途被破坏,
直到Review才发现。
那么:
不变量保持率 = 80%。
对于真正关键的不变量来说,
理想状态当然应该尽量接近:
100%。
因为这些本来就是:
不能轻易被破坏的工程边界。
十七、不变量保持率低,先检查什么?
我一般会看:
第一:
Invariant是不是太模糊。
第二:
有没有给每条规则明确编号。
第三:
执行过程中有没有定期重新验证。
第四:
测试是不是覆盖Invariant,而不是只覆盖实现。
第五:
中间方案是不是未经确认就继续被后续继承。
第六:
多Agent之间有没有共享同一套Invariant。
如果这些没建立起来,
再长的Prompt也很难稳定住复杂任务。
十八、Plus和Pro怎么判断?
如果你的复杂Agent任务现在经常出现:
代码能跑。
测试能过。
任务也完成了。
但最后才发现:
兼容性破坏了。
权限变了。
历史数据读不了了。
副作用重复了。
那当前真正限制效率的,
通常不是 ChatGPT、Codex 容量。
而是:
关键工程边界没有被显式定义。
这种阶段Plus通常已经够用。
更值得先把:
Invariant定义。
Invariant测试。
Checkpoint验证。
共享约束
建立起来。
如果这些机制已经很成熟:
复杂任务开始前有清晰Invariant。
Agent每次关键修改都会重新检查。
测试能自动验证。
多Agent也共享同一组边界。
不变量保持率长期稳定。
同时还有大量复杂任务等待执行,
这时候Pro才更容易放大效率。
因为更多AI容量是在:
明确边界内自由执行。
而不是:
跑得更快以后,才发现把系统底线也一起改掉了。
最后
复杂任务开始前,
很多人第一反应是:
Prompt是不是还应该再详细一点?
但Prompt越长,
并不自动意味着任务越稳。
真正稳定复杂Agent Workflow的,
往往是几条非常明确的问题:
什么一定要做到?
以及:
什么无论如何都不能被破坏?
前者是Goal。
后者就是Invariant。
实现可以变。
计划可以变。
文件可以变。
甚至架构方案都可以变。
但只要关键Invariant始终成立,
Agent就拥有足够大的执行自由,
又不会轻易跨过系统真正不能跨的边界。
所以复杂任务开始前,
与其继续给 ChatGPT、Codex 加几百字Prompt,
不如先花几分钟写清楚:
这次任务里,哪几件事必须永远保持成立?
很多长任务的稳定性,
真正就是从这里开始的。
持续分享 Codex、大模型开发与 AI 编程实战内容。
长期深度使用各类代码大模型,也整理了稳定的Plus/Pro会员订阅渠道,有需要可自取。
更多推荐




所有评论(0)