AI 写的 README 和接口文档看着挺像样,一核对全是错的

2026-07-29

数据截至 2026-07,各产品的额度与报错口径以官方最新说明为准。

AI 文档写不准,九成不是模型能力问题,而是你让它同时干了两件事:一件是”整理已知信息”,一件是”决定这个系统应该是什么样”。前者它做得比你快,后者它只能猜。 猜出来的东西语气跟真的一模一样,所以你看不出来。你读到一份措辞妥帖、结构齐整、字段解释头头是道的接口文档,然后调用方按它接进去,线上炸了——这才发现里面有三个参数代码里根本不存在。

这篇按工程环节切:文档这件事上 AI 能帮到哪一步、哪一步必须人来定,怎么按现象分因、给什么输入、拿什么动作验收,以及什么时候该停手。

站内已有两篇相邻的文章,分工是这样的:注释与代码不一致讲的是代码内部那一层——注释写了什么、和函数实际行为怎么对上;CLAUDE.md 怎么写讲的是给工具看的规则文件怎么组织。本篇管的是给人看的外部文档:README、接口文档、运维手册,它们的读者不在你的代码库里,出错的代价是别人按错的说明操作。

一、先判断:是”喂得不够”还是”边界没定”

拿到一份烂文档先别急着重写,两类病因的治法完全相反。

喂得不够:模型手上没有足够材料,就用同类项目的常见写法补全。表现是内容通顺、结构标准、但具体到你这个仓库的部分全是泛泛之谈,或者干脆凭空生成不存在的配置项。这类病加材料就能治。

边界没定:材料够,但文档里有些内容本来就不是”整理”能得出来的——比如这个服务的 SLA 承诺是多少、这个字段废弃后调用方要在哪个版本前迁完、故障时谁有权限重启。这些是人的决策,模型只能编一个听起来合理的。这类病加多少材料都没用,只能你先定下来,它再落成文字。

判别方法很朴素:随便挑文档里三句最”实在”的话,问自己一句”这句话的依据在仓库的哪个文件里”。答得上来的是第一类,答不上来的是第二类。第二类的比例超过三成,说明你不该让它写这份文档,你该先开个会。

模型在信息不足时倾向于补全而不是留白,这个特性在大模型幻觉那篇里讲得更细,这里只用它的结论:留白需要你显式要求,否则默认得到的是编造。

二、现象到成因的判别表

现象大概率成因怎么验证处置动作
字段名拼写全对,但含义解释是错的只喂了类型定义,没喂业务约束抽 3 个字段,在代码里搜它的判空与分支处理,比对文档里的解释补一份字段语义清单,只重写受影响的小节
接口文档里有代码中不存在的参数输入不足时的补全式编造grep -rn "参数名" src/ 搜,搜不到即编造改成以路由定义或 schema 文件为唯一输入
README 的安装步骤在干净机器上跑不通按同类项目套路写,而非按本仓库脚本写在空容器里从零执行一遍只允许它从 package.json、Makefile、CI 配置里摘步骤
运维手册每条命令都对,但按顺序执行会出事缺前置条件和依赖关系让另一个人在预发环境照着走一遍每步加前置条件与失败回退,顺序由人定
每次重生成文档结构都不一样没给固定骨架对比两次输出的标题层级骨架写死,它只填内容不动结构
文档描述的是旧版本行为读到的是历史文件或过期注释git log --oneline -20 -- 该模块/ 看最近改动把最近的 diff 一并作为输入

这张表的用法是:先定位到行,再执行”处置动作”那一列,不要一上来就整篇重写。整篇重写会把上一轮已经核对过的正确内容一起冲掉,然后你得重新核对一遍。

三、三类文档各要喂什么

三类文档的”真相源”不同,喂错了材料就白干。

README 要喂的是入口和实际可跑的东西。 具体是四样:目录树的前两层、依赖清单文件(package.json / pyproject.toml / go.mod 之类)、构建与启动脚本、CI 配置。CI 配置尤其值钱,因为它是唯一被机器持续验证过的”怎么把这东西跑起来”的说明,人写的步骤会过期,CI 不会——它过期了流水线就红了。

README 里 AI 能干的:把这四样材料翻译成人话、补齐命令的参数解释、把目录结构讲清楚、生成一个能跑通的最小示例。AI 不能干的:写”这个项目解决什么问题”和”什么时候不该用它”。这两段是定位,定位是你的判断,它写出来永远是”一个高性能的、易用的、模块化的……”这种谁看了都没印象的话。

接口文档要喂的是结构化定义,不是实现代码。 优先级排序:OpenAPI/JSON Schema 之类的接口描述文件 > 路由注册处的类型声明 > controller 层代码 > 业务实现代码。越往后越容易让它跑偏,因为实现代码里混着分支、兼容逻辑和历史包袱,它会把某个只在灰度开关打开时才生效的字段当成正式契约写进去。

接口文档里 AI 能干的:把类型定义展开成字段表、生成各语言的调用示例、把错误码列全、按 REST 惯例补齐请求头说明。AI 不能干的:定错误语义。返回 401 和 403 在你的系统里到底怎么分(是”没登录”和”登录了但没权限”,还是别的划法),返回 429 时调用方该退避多久再重试——这些是你的契约,你不说它就按最常见的写法猜,猜得还挺像。

运维手册是三类里最危险的,因为读者会在深夜、在压力下、逐字照做。这类文档要喂的是环境拓扑、服务依赖关系、真实执行过的操作记录(比如上次发布的操作日志),以及最重要的一样:你希望它写成什么粒度。粒度不给,它要么写成一句”重启服务”,要么写成一篇 Linux 教程。

运维手册里 AI 能干的:把你口述的操作过程整理成编号步骤、给每步补上”预期看到什么输出”、把散落在群聊里的应急处置整理成条目。AI 不能干的:决定操作顺序、决定回滚点在哪、决定哪一步需要二次确认。这三样定错了会直接造成事故。

四、验收动作:怎么证明它写对了

看着对不算对。三类文档各有一个成本很低的验收动作。

README 的验收是在干净环境里执行一遍。开个容器,只装基础运行时,从 clone 开始照文档走,走到跑起来为止。中途需要你凭经验补的任何一步,都是文档的缺口。这一步不能省,因为”我本机能跑”的机器上装着一堆你早忘了的东西。

接口文档的验收是字段级对拍。把文档里的字段名抽成列表,和 schema 文件里的字段名做集合比较:

# 从文档里抽出反引号包裹的字段名,排序去重
grep -o '`[a-zA-Z_][a-zA-Z0-9_]*`' docs/api.md | tr -d '`' | LC_ALL=C sort -u > /tmp/doc_fields.txt
# 从接口定义文件里抽字段名(按你的 schema 结构调整取值路径)
python -c "
import json
d = json.load(open('openapi.json', encoding='utf-8'))
names = set()
def walk(o):
    if isinstance(o, dict):
        for k, v in o.items():
            if k == 'properties' and isinstance(v, dict):
                names.update(v.keys())
            walk(v)
    elif isinstance(o, list):
        for v in o:
            walk(v)
walk(d)
for n in sorted(names):
    print(n)
" | tr -d '\r' | LC_ALL=C sort -u > /tmp/schema_fields.txt
# 只在文档里出现、schema 里没有的,就是编造出来的
LC_ALL=C comm -23 /tmp/doc_fields.txt /tmp/schema_fields.txt

两个细节别省,否则这段会骗你。一是两侧必须用同一套排序规则comm 只做单趟归并比较,两个文件的排序规则不一致时它不会报错,只会把大量正确字段也当成”文档独有”吐出来,你就得到一份全红的假警报。上面统一加 LC_ALL=C 就是为了这个。二是统一行尾:Python 在 Windows 上默认按平台行尾写出,多一个回车符就会让每一行都对不上,所以补了 tr -d '\r'。跑完先看两个中间文件的行数是否都合理,再看差集。

还有一类可预期的噪声:反引号在文档里不只包字段名,也会包函数名、类型名、示例路径,所以差集里会混进一些本来就不是字段的词。第一次跑先人工扫一眼差集,把这类词加进忽略列表,之后每次改文档重跑就是干净的。

关键不在具体命令,在于这一步必须是机器做的。人眼扫字段表扫到第四十个就开始跳行,而编造出来的字段往往就藏在第四十个之后。

运维手册的验收是换个人走一遍。找一个没参与写这份手册的同事,在预发环境照做,你在旁边只看不说话。他卡住的每一处、犹豫的每一处,都记下来。这个动作比任何自动检查都管用,因为手册的失效模式是”作者觉得显然、读者不知道”,而作者自己永远测不出这个。

核对本身也有方法,如何核查 AI 的答案那篇讲了通用做法,文档场景可以直接套:凡是能在仓库里搜到出处的句子,标出处;搜不到出处的句子,要么删,要么加一句”以实际配置为准”

五、什么时候别再折腾

三个止损点,到了就停。

第一个:同一小节改到第三轮还是错,就自己写。 反复要它改的过程里,你其实已经把正确答案说了三遍。第三遍的时候,你打字把正确内容直接敲进文档,比再描述一遍要什么更快。多轮修改还有个副作用是上下文里堆满了历次错误版本,它容易把已经改掉的错误再改回来。

第二个:发现文档里有编造内容,且你无法快速确认编造范围,整篇丢弃重来。 这条和第二节”不要一上来就整篇重写”不冲突,分界线是编造范围能不能圈死:第二节说的是你已经按表定位到具体成因、错的就是某一小节,那就只动那一节;这里说的是你连编造了几处都数不清——比如上面那个字段对拍跑出来差集有十几行、且分布在各个小节。前者是定点修,后者是补够材料重来一遍,逐句核对的成本反而更高。判断顺序是先跑一遍机器验收拿到范围,再决定用哪种。

第三个:需要写的内容依赖你还没做的决定,停下来去做决定。 典型是接口文档写到”废弃字段的迁移期”、运维手册写到”什么情况下允许直接改数据库”。这时候继续折腾输入是浪费时间,你缺的不是材料是结论。

回滚点怎么留:文档改动单独提交,一次提交对应一份文档或一个大节,提交信息里写清”哪些内容是生成的、哪些是人工核对过的”。这样发现问题时可以精确回退到上一个可信版本:

git log --oneline -- docs/api.md
git checkout <commit> -- docs/api.md

别把文档改动和代码改动混在一个提交里。混了之后回滚文档就会带走代码,或者反过来。

顺带说一句工具选择:文档任务对上下文的消耗比写代码大(要塞进去大量原始材料),仓库一大就容易触发上下文不够的问题,处理办法参见大仓库上下文不足。另外,部分海外 AI 工具官方对中国大陆有区域限制、不支持直连,虽然存在第三方中转的做法,但可靠性和数据合规都由你自己承担,把公司内部文档材料喂过去之前先想清楚这一点。

六、避坑清单

坑一:让它”参考现有文档风格”来写新文档。 为什么会踩——这个要求听起来很合理,还能保持一致性。实际后果是它把旧文档里的过期内容当成事实继承下来,一份错的传染成两份。怎么避:风格参考和内容来源分开说,明确”格式照旧文档,内容只能来自我给的这几个文件”。

坑二:一次性让它写完整份手册。 为什么会踩——省事,而且它真的能一口气吐出很长一篇。实际后果是越往后编造越多,前面材料充足的部分质量还行,后面就开始自由发挥,而你读到后面已经累了。怎么避:按小节切,每节单独给材料、单独验收,节与节之间你来保证衔接。

坑三:把”你不确定的地方标出来”当成安全网。 为什么会踩——这个要求写起来很自然,看起来能兜住幻觉。实际后果是它标出来的往往是它确实不确定的那些无关紧要的地方,真正编造的部分它标得很自信。怎么避:不要依赖它的自我标注,用第四节那种机器对拍的方式验。

坑四:用生成好的文档反过来指导代码修改。 为什么会踩——文档看着比代码清爽,讨论时大家自然拿文档说事。实际后果是文档里那个编造出来的字段被当成需求,有人真的去把它实现了。怎么避:明确一条规矩——未经核对的文档不进评审、不进需求讨论,草稿目录和正式目录物理分开。

坑五:接口文档只喂 controller 代码。 为什么会踩——controller 看起来正是”接口在哪”。实际后果是中间件里做的参数校验、统一响应包装、鉴权头要求全都丢了,文档里的响应结构和真实返回差一层。怎么避:把中间件和统一响应封装的代码一起给,或者干脆以网关处抓到的真实响应样本为准。

坑六:运维手册里保留它写的时间估计。 为什么会踩——“预计耗时 X 分钟”这种句子读起来很专业。实际后果是这些数字全是猜的,故障时有人拿它当承诺,等超时了就开始慌。怎么避:所有时长、阈值、容量数字要么删掉,要么换成你实测过的值,并注明测量环境。

收尾

把这件事拆开看其实很清楚:AI 在文档上的价值是把已有信息重新组织成人能读的形态,这一步它比你快得多也耐心得多;而决定这个系统对外承诺什么,从来不是组织信息能得出的答案。你把这两件事混在一次请求里,得到的就是一份混着事实和猜测、且两者语气完全相同的文档。

落地前过一遍这几条:

  • 这份文档里的每个具体断言,我能说出它来自哪个文件吗?
  • README 在干净容器里从头跑通了吗?
  • 接口文档的字段名和 schema 做过机器比对吗?
  • 运维手册有人照着走过一遍吗?走的人卡在哪?
  • 里面还有没有 AI 生成的数字(时长、阈值、容量)没被替换掉?
  • 文档改动是不是单独一个提交,能一键回退?

六条里有一条答不上来,就先别合入。文档错了不会有测试变红,只会有人半夜按着它操作。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。