给 AI 文档生成加上仓库证据校验
方案将文档陈述分为可从仓库验证的事实与需要负责人签署的承诺,分别采用脚本校验和人工确认。生成流程依赖制品索引,并在 CI 中拦截无法解析的参数、配置键及退出码等事实。
AI 深度解读
AI 写文档,最难发现的错误往往只有几个字符:一个不存在的 --dry-run、一个改过名的配置键,或者一个已经失效的退出码。段落读起来仍然流畅,读者照着操作却会出错。
原文作者提出了一条文档流水线:仓库能证明的事实交给脚本核验,产品承诺交给具体的人负责,模型只能在这条边界内起草。这个思路适合接口和配置经常变化的项目,但它能防住标识符漂移,距离证明整篇文档正确还有一段距离。
先区分事实和承诺,再决定让谁检查
作者把文档句子分成两类,分类依据是句子声称了什么,而不是谁写了它。
第一类叫 derived,可以理解为「从仓库里查得到的事实」。例如某个命令是否有 --dry-run,代码里是否定义了 MAX_RETRIES。这些内容适合由程序从代码、命令输出和配置 schema 中提取证据。
第二类叫 owned,指需要有人负责的承诺,包括兼容保证、弃用安排、支持周期、安全声明、价格和 SLA。仓库里存在某段代码,不能自动证明团队会持续支持它。
标识符是否存在,可以交给脚本;团队承诺什么,必须有人负责。
这也解释了为什么只让模型「仔细检查」不够。人工审稿通常关注意思是否通顺,容易漏掉 retry.budget_ms 这样的细节;而「我们永远保持兼容」即使写得很谨慎,也需要组织层面的授权。
作者进一步规定:模型起草的区域不能包含 owned 声明。这样,承诺混入草稿就成为流水线可以拦截的违规,而不只是审稿人的判断题。
让模型从仓库生成的清单里取材
流水线先生成 docs_claims_index.json,作为文档允许引用的名称清单。作者要求提交生成器,而不是手工维护索引文件。
示例生成器有三个来源:
- 用 Python AST 收集函数、异步函数、类,以及部分大写赋值名称,并跳过
.venv、build、dist、node_modules。 - 执行 CLI 的
--help,提取双短横线参数。 - 遍历 JSON schema 的
properties,收集配置键。
下面这段代码解决的是「从程序自己输出的帮助信息中提取参数」:
def cli_flags(entry: str) -> set[str]:
out = subprocess.run([sys.executable, entry, "--help"], capture_output=True, text=True, check=True).stdout
return set(re.findall(r"(?<![\w-])(--[a-z0-9][a-z0-9-]*)", out))
要留意 subprocess.run 那一行:它会实际执行入口程序,而且只读取标准输出。入口必须能在 CI 环境运行;没有出现在帮助信息里的隐藏参数,也不会自动进入清单。
起草时,模型拿到的产品功能上下文只有这份索引。它可以组织叙述、改写已验证文本,却不能自行补全产品能力。
这能缩小模型猜测的空间,但清单本身也有信息损失。示例中的 schema 提取器保存的是属性名,没有保留完整路径和层级关系;不同位置的同名字段可能被合并。作者用 retry.budget_ms 说明问题,展示的代码却还不足以核验这种完整路径。
所以,这是一套实现方向,还不是拿来就能覆盖全部文档事实的工具。
CI 拦截未知名称,也拦截没人负责的承诺
拿到草稿后,流水线从 Markdown 行内代码和围栏代码块中抽取待核验内容。作者坚持让标识符使用反引号,避免把普通叙述中的词也送进检查器。
这个约束有实际价值:写作格式同时成为机器识别的边界。
不过,原文展示的 atoms() 只提取 CLI 参数和至少三个字符的全大写名称。开头提到的 exit code 3、点分配置键,以及索引中的函数名,都没有在这段提取逻辑里得到完整覆盖。原文的围栏正则展示也存在换行异常,直接复制前需要修正并验证。
承诺检查则依靠两组 HTML 注释:
<!-- draft:model -->和<!-- /draft:model -->标记模型起草区域。<!-- owner: @name -->标记负责的人。
检测词包括 guarantee、we will、never、always、deprecat、support window、secure、compliant 和 SLA。模型区域命中这些词会违规;其他段落命中却没有 owner 标记,也会违规。
解析器最终区分已解析名称、未知名称和策略违规。只要未知名称或违规列表非空,就以非零状态退出。报告尽量只打印名称与行号,方便定位。
作者用三个 Markdown 样例检查这道关卡:
def test_clean_page_passes():
assert run("tests/fixtures/clean.md").returncode == 0
def test_unknown_flag_fails():
result = run("tests/fixtures/unknown_flag.md")
assert result.returncode == 1 and "--fast-mode" in result.stdout
def test_owned_claim_in_model_block_fails():
result = run("tests/fixtures/owned_claim.md")
assert result.returncode == 1 and "guarantee" in result.stdout.lower()
这里要看失败测试的断言:它们同时检查返回码 1 和错误内容,避免程序因无关异常退出,测试却误以为成功拦截。
测试前运行 python -m tools.docs_claims.index_build > docs_claims_index.json,并可在任务之间缓存索引。但缓存需要对应当前仓库内容,否则旧清单会削弱检查价值。
作者把起草放在 MonkeyCode 的免费服务器和免费模型访问上,索引生成与验证仍留在自己的 CI。除起草外,整条流程可以离线运行。原文也披露,这篇文章属于 MonkeyCode 产品推广合作。
我的判断
我认同把审稿目标缩小到具体名称。相比只要求模型自查,再让人通读全文,仓库索引提供了一条可重复执行的检查路径,尤其适合参数和配置频繁变化的项目。
但原文没有给出误报率、漏报率、审稿耗时或对照实验。三个样例只能证明三个预设场景按预期工作,「足以信任整个关卡」仍是经验判断。
名称存在,不等于描述它的句子正确。
例如,检查器找到了 --dry-run,仍无法证明「这个参数不会产生任何写入」。默认值、参数组合、跨字段约束和运行效果,都需要额外证据。作者承认了解析器只做语法层面的核验,读者也应保留这条边界。
owned 检测同样比较粗糙。英文关键词无法直接覆盖中文承诺,普通技术描述中的 always 也可能被误判。owner 注释能表示归属,却不能证明这个人实际审阅或批准过内容。
和执行文档示例、接口契约测试相比,这套方案更容易从名称检查起步,覆盖的正确性也更窄。单文件脚本、长期不更新的文档未必值得维护生成器;描述未发布功能的文档,还需要第三类声明或大量人工归属标记。没有必要为了使用模型,给所有仓库都加同一套流程。
能拿来做什么
- 如果你维护 Node CLI,可以先从
--help建立参数清单,在 CI 中检查文档代码区域里的未知参数。先解决参数改名后示例没更新的问题,再考虑扩大范围。 - 如果前端工具库提供配置 schema,可以尝试生成包含完整路径的字段索引。需要单独明确默认值、取值范围和字段组合由谁验证,避免把「字段存在」当成全部验收条件。
- 使用 AI 更新 README 时,可以限制它只依据当前索引改写,并把兼容保证、弃用时间和安全承诺留给负责人审阅。中文文档需要补充对应的承诺识别规则。
- 落地前先保留原文的三个基础样例,再补充项目实际使用的配置路径和代码块格式。只有检查器能稳定识别这些内容,CI 通过才有明确含义。