写给 AI 的文档,模糊就是坑

同一份 PRD,交给人类和交给编码 agent,是两种写法。人类会靠常识补全,AI 会照字面实现。

一份产品需求文档写完,3000 多行,章节齐、表格密、流程图不少。看着挺像回事。

然后交付形态变了一句话:这份文档不给人看,直接给 Claude Code 拿去实现。

我做的第一件事不是庆祝可以省掉评审,而是把整份文档的写法推翻重来。

人类读者会替你补全,AI 不会

文档里有这么一行字段定义:

出库原因 | 枚举 | 必填 | 进货 / 退货 / 销售 / 赠送 / 试用

给人看,没问题。任何一个工程师读到这里,会自然而然地在脑子里生成 SALEGIFTTRIAL 这样的常量,甚至会顺手问一句「代码值你们有约定吗」。

给编码 agent 看,它不会问。它会自己编一套英文常量,编得还挺合理。问题是——下一份文档、下一个接口、下一次它自己重新读这段的时候,可能编出另一套。等到两套常量在系统里相遇,就是返工。

所以我把它改成了:

PURCHASE_IN(进货入库) RETURN_IN(退货入库) SALE_OUT(销售出库) GIFT_OUT(赠送出库) TRIAL_OUT(试用出库)

并且在附录单开一张枚举总表:枚举名、适用字段、代码值、中文名、说明。一处查全,不用满文档翻。

这不是「写得更细」,这是把本来在人脑里隐式完成的那一步,显式写下来

四条改写规则

同一份文档,从「给人复核」切到「给 AI 实现」,我总结出这几条必须改的地方:

1. 字段类型要工程化,不要自然语言

「文本」「数字」「金额」——这些词给人看足够,给 AI 看等于让它猜。改成 string(50)decimal(18,4)。长度和精度是业务决策,不是实现细节,本来就该出现在需求文档里。

2. 校验规则要写成可判定条件

❌ 「需对客户信息做合理校验」 ✅ 「出库原因 = SALE_OUT 时,customer_id 必填,不允许留空」

「合理」是个甩锅词。人类看到会来问你什么叫合理,AI 看到会自己定义合理。

3. 计算公式要给表达式 + 数值演算

写「采用移动加权平均法计算成本」是不够的。得给出公式,再给一组具体数字演算一遍:进货多少、单价多少、出库多少、结存单价变成多少。

AI 能把演算过程直接翻译成代码,而文字描述它只能理解。 理解和翻译之间,隔着一次可能出错的转换。

4. 状态机要写全,包括不可逆节点

有哪些状态、哪个状态允许哪些操作、流转条件是什么、哪些操作过了就回不去了。缺一样,AI 就会自己补一个看起来对的。

最贵的那个坑:范围边界

这份文档里我抓到的最严重问题,不是格式,是范围

需求方拍板「提醒推送这一期先不做,优先做电脑端」。文档里却留了一句:

推送走 Web Push / 短信降级

人类读者读到这句,会结合上下文判断「哦这是以后的方案」。编码 agent 读到这句,会真的去实现 Web Push

这就是模糊描述在 AI 协作里的杀伤力:它不会怀疑,只会执行。

订正的时候我没有简单删掉,而是全文搜「推送」「短信」「模板消息」,凡是写成「一期能力」的表述,一律改成明确的否定句:一期不做任何主动推送,待办仅站内展示。

否定句比留白安全得多。留白会被填充,否定句不会。

收敛范围 ≠ 砍功能

顺带说一个我认为更重要的判断。

「推送一期不做」这句话,最偷懒的执行方式是把整个提醒模块划掉。但那是错的——真正该做的是画边界

  • 提醒规则的配置、待办的生成逻辑、待办列表和状态流转,一期完整实现
  • 主动推送的通道本身,标注二期
  • 推送相关的字段(push_channelpushed_atpush_status保留在数据模型里,标注 reserved · 二期启用

理由很直接:生成是生成,推送是消费。 一期只做生成加站内展示,二期挂一个推送消费者就行,核心逻辑一行不用改,表结构也不用动。

如果一期图省事把字段一起砍了,二期就得改表——而改表在一个已经跑起来的库存系统里,是要停机和数据迁移的。

这条解耦设计我专门写进了文档的「给开发的实现提示」章节,并标出二期的接入点在哪。

一句话

给人写文档,你在传递意图,读者会用常识把缝隙填上。

给 AI 写文档,你在定义行为,缝隙会被它自己的想象填上——而它的想象,和你的常识不是一回事。

所以别嫌啰嗦。你少写的每一句,它都会替你补一句。

马启航Marvis 🐉