智能体漏掉业务规则,问题可能在需求
作者回看 78 次 Claude Code 实验:有规格说明的运行中,28/30 通过全部隐藏检查,无规格说明时为 12/48。进一步分析优惠券案例,区分需求已提及与未写明的业务规则,追查规格说明的作用。
AI 深度解读
AI 编程助手把工单明确写出的要求全部做对了,功能仍然可能不符合业务预期。作者复盘了一个 Node 结账服务的优惠券任务:失败没有发生在「无效优惠码不能影响支付」这条明示要求上,而是发生在重复核销、支付时复查等没人写下来的规则上。
这篇最有价值的结论是:规格说明的作用,是让业务决定变得明确、实现变得可预测。它也展示了另一面:规格漏掉的规则,模型可能一致地漏掉。
工单写出的要求都过了,为什么功能还会错
作者此前公开了 78 次 Claude Code 运行记录:提供规格说明的 30 次运行中,28 次通过全部隐藏检查;没有规格说明的 48 次运行中,只有 12 次全部通过。这个结果支持「规格有帮助」,但无法解释究竟哪些内容起了作用。
于是,作者重新分析优惠券案例,把检查按「工单有没有提到这条规则」分类。原始需求只有两句话:
Add coupon codes to checkout. Make sure invalid codes do not break payment.
要留意第二句:它要求无效优惠码不能破坏支付,却没有规定优惠券能用几次、何时算使用成功,以及失败时接口应该返回什么。
实验仓库是一个小型 Node 结账服务。作者在运行前冻结了 11 项隐藏检查,模型看不到,其中 5 项检查对应工单没有提及的规则。
在 Opus 5.5 的三次模糊需求运行和三次规格运行中,工单明示的要求全部通过,所有失败都落在未写明的规则上。Haiku 4.5 的差异更明显:三次模糊需求运行都未通过两项重复使用检查;加入规格后,三次运行都通过全部 11 项。
模型完成了明示要求,也可能交付了错误的业务规则。
自己写的测试全绿,为什么仍然靠不住
其中一次失败运行没有崩溃,自带测试也全部通过。模型发现优惠券数据里没有使用次数限制,于是决定不限制,并在最终说明中明确交代了这个选择。
另外两次运行读取同样的数据,却决定优惠券只能使用一次。三份实现都能通过自己编写的测试,因为测试验证的是各自选定的政策。
这里的问题很具体:如果业务规则由模型补齐,再让同一个模型围绕实现写测试,测试通过只能说明二者相互一致。
还有两处行为出现了分歧,却没有触发隐藏检查失败。
第一处是优惠券在应用后、付款前过期。一次运行直接恢复原价,实际扣款为 $54.00,而用户此前看到的是 $43.20;另外两次则拒绝首次付款尝试。原文没有提供哪种处理符合真实业务的依据,这本来就需要产品决策。
第二处是错误契约。所有模糊需求运行都在优惠码无效时抛异常,规格运行则返回 { ok: false, reason }。对前端来说,这会决定调用方究竟使用 try/catch,还是检查返回值。两种接口各自都能成立,混用就会破坏对接。
真正起作用的,是次数、时点和返回值
作者提供的规则和验收标准并不长:
## Rules
- A coupon is either percentOff (of the subtotal) or amountOffCents.
- A coupon can be redeemed once per user. It counts as redeemed only
when an order is paid, not when it is applied.
- Applying a second coupon replaces the first.
## Acceptance criteria
- AC-1 Valid coupon: getSummary shows the discount and the discounted
total; pay charges exactly that total.
- AC-2 Unknown, disabled or expired code: applyCoupon returns
{ ok: false, reason } with reason unknown, disabled or expired.
It does not throw, and the checkout keeps its previous total
and stays payable.
- AC-3 Code already redeemed by this user: returns
{ ok: false, reason: 'already_redeemed' }.
- AC-4 Redemption is re-checked inside pay: if the user redeemed the
coupon on another order after it was applied here, pay drops the
coupon and charges the undiscounted total.
- AC-5 An amount-off coupon larger than the subtotal brings the pre-tax
amount to 0, never below.
- AC-6 A successful apply returns { ok: true, code, discountCents }.
最该看的是核销时点和 AC-4:应用优惠券不算核销,支付成功才算;同一用户同时打开两个结账流程时,pay 必须重新检查是否已经在另一张订单使用。
这把「一个用户只能用一次」从静态限制补成了跨步骤规则。第一次检查通过后,数据仍然可能变化,付款前需要再次确认。
不过,规格仍然漏了过期复查。它要求 pay 复查核销状态,没有要求复查有效期,所以三次规格运行都保留了应用后过期的优惠券,按折后价扣款。其中一次还主动指出了这个缺口。
作者认为,这种一致且被说明的行为更容易评审。规格没有消除遗漏,但让遗漏的后果更容易被发现。
作者还建议检查每条验收标准是否会在当前代码上失败。已经满足的条件,可以保留为回归约束,却不能证明这次新增功能完成了。
我的判断
我认同作者把注意力放在「谁来决定业务规则」上。对接口开发尤其有用:明确返回值、错误码和状态变化时点,通常比一句「处理好异常」更方便实现,也更方便前后端验收。
但「人类同事不会这样决定」说得偏满。不同团队对优惠券过期、重复使用和价格承诺也可能有不同约定。人类同事同样需要确认规则,区别在于他们可能拥有更多组织上下文。
实验结论也要收住。每个变体只有三次运行,只能证明某种行为会发生,不能估计发生概率;仓库刻意保持小而整洁,规格和隐藏测试又出自同一作者。原文也没有给出完整的控制变量对照,无法据此认定提升具体来自哪一句规则。
与单纯增加测试数量相比,先确定业务政策更能解决这里暴露的问题;与要求规格一次写全相比,把未决问题列出来更现实。让模型起草验收标准时,作者要求它先提问,只为已确定的内容编写 AC-1..n,并标记当前代码已经满足的条目,最后由人回答业务问题。这是一套可采用的经验做法,原文没有提供它与其他流程的对照数据。
能拿来做什么
- 给 Node 接口下任务时,把失败行为写成可核对的契约,明确函数名、输入、返回结构、错误码,以及是否抛异常,让前端可以据此实现调用逻辑。
- 遇到先应用、后支付这类多步骤流程,逐项追问两步之间哪些状态会变,并明确最终操作前需要复查什么、失效后如何处理。
- 让 AI 起草验收标准时,先要求它列出未决问题,再为已确定的规则编号;评审生成代码时,同时核对它自行选择的业务政策,不只看测试是否通过。