跳至主内容
博客

我们不再手写文档

一个没有专职产品经理的小团队,靠一个 AI agent 找出数百个文档缺口、补齐内容并提交 PR。解决办法不是反复提醒工程师写文档,而是让漂移及时暴露出来。

快猫技术

我们不再手写文档

一个没有专职产品经理的小团队,怎么保证文档不掉队

Flashduty 的值班管理有个功能叫「公平轮换」。开启后,系统会自动调整排班顺序,避免同一个人总是轮到周末值班。前端代码长这样:

<div className='flex items-center'>
  {t('Fair Rotation')}
  <Tooltip>
    The system automatically adjusts rotation order to ensure
    each member gets on-call shifts across different time periods.
  </Tooltip>
</div>
<Form.Item name='fair_rotation' valuePropName='checked'>
  <Switch size='small' />
</Form.Item>

功能做得很用心,前后端都实现了,上线了,跑在生产环境里,用户也在用。

但文档里一个字都没有。

除非你恰好在编辑轮换规则时注意到一个不起眼的开关,否则你根本不知道这个功能存在。这就是文档漂移的样子:不是灾难性事故,只是一个接一个的小缺口,最后让用户不再相信文档。

我们没有继续催工程师补文档,而是让 agent 每周把漂移变成可审核的 PR。

为什么文档总是跟不上

我们在做 AI 客服。这个 AI 靠读文档来回答用户问题。文档有缺漏,AI 就答不上来;文档有错误,AI 就答错。以前文档质量主要影响人类读者:凌晨三点被告警吵醒的工程师翻文档查问题,找不到就骂两句。现在它直接决定 AI 客服的回答质量。垃圾文档进去,垃圾回答出来。

问题是,没人写文档。

Flashduty 没有专职产品经理。工程师就是产品经理:分析需求、写 PRD、开发、测试、部署、处理客户反馈,全是同一拨人。没有人的岗位职责里写着「确保文档同步更新」。花了两周做出公平轮换的人,不想再花一天写文档说明;他想做的是下一个功能。于是文档就不写了。答案散落在飞书、企微、钉钉的聊天记录里,始终没进文档。

我们的规模不算小:12 个产品模块、20 多个微服务、中英双语文档、几百个页面。每一个合入的 PR 都可能悄悄让其中某条描述变成错误的。手动审查理论上可以做,实际上从来没人做。

局面很清楚:工程师不想写文档,用户需要文档,AI 客服的质量完全取决于文档。靠「请大家注意更新文档」解决不了问题。既然人靠不住,那就不靠人了。

我们造了什么

我们做了一个 Claude Code skill,一个可复用的 AI agent。工程师敲一条命令就能跑,也可以挂定时任务自动跑。

核心设计思路:一个 YAML 映射文件,把每个文档页面和背后的源码路径对应起来。agent 之所以知道去哪里找,是因为我们告诉了它哪段代码对应哪篇文档。没有这个映射,它要么搜索范围太大(淹没在无关的内部代码里),要么漏掉关键变更。

flowchart TD
    T1["代码推送 / 定时任务"] --> SCAN["扫描源码仓库"]
    T2["手动触发"] --> SCAN

    SCAN --> DIFF{"模式?"}
    DIFF -- "diff" --> GD["读取 git diff\n识别新路由、开关、默认值"]
    DIFF -- "audit" --> WF["遍历前端代码\n盘点所有功能"]

    GD --> XREF["与文档交叉比对"]
    WF --> XREF

    XREF --> FIND["findings.yaml"]

    FIND --> AUTO{"自动模式?"}
    AUTO -- 否 --> HUMAN["工程师审核\n去除误报"]
    HUMAN --> FIX
    AUTO -- 是 --> FIX["读取源码\n生成文档(中 + 英)"]

    FIX --> PR["提交 PR"]
    PR --> REVIEW["工程师审核内容"]
    REVIEW --> MERGE["合并"]

两种模式:

Diff 模式问的是「最近两周改了什么?」agent 读取所有关联仓库的 git diff,找文档相关的信号:

  • 新增的 API 路由或接口
  • 新增的 UI 开关或表单字段
  • 变更的默认值或校验规则
  • 删除或重命名的功能

每个信号都会和现有文档交叉比对。已经有文档覆盖的跳过,没有的就记录为一个发现。适合日常维护:在漂移堆积之前把它抓住。

Audit 模式问的是「所有功能都有文档吗?」这个更彻底。它先遍历前端代码,盘点每一个用户可见的功能:页面、表单字段、开关、下拉选项。然后逐一检查文档覆盖情况。

为什么从前端入手?因为前端是产品能力的诚实呈现。后端有内部接口、预留字段、客户定制逻辑、UI 上根本不会展示的保留枚举值。前端展示的,才是用户能看到和操作的。

Audit 模式还会做准确性检查:从现有文档里提取可机械验证的事实,比如字段名、默认值、导航路径和校验规则,再逐一和代码比对。文档里说轮换周期支持「天、周、自定义」,代码里支持的是小时、天、周、月。这就是一个发现。

两种模式都会输出一份结构化的发现文件。然后进入修复阶段:agent 读取每个发现对应的源码,生成中英双语的完整文档,包括段落、表格和配置步骤,然后提交 PR。没有占位符,也没有 TODO。工程师审核内容后合并。

我们发现了什么

先说全貌:跑完 diff + audit 两种模式后,总共产出 20 个 PR,超过 9000 行新文档,覆盖 370 多个文件,数百个发现。

第一次跑的是 diff 模式:/doc-review --mode diff --since "1 month"。它只看最近一个月的代码变更,得到了这个 PR。光这一次就揪出 21 个缺口,33 个文件变更,两种语言共新增超过一千行文档。以下是其中几个典型的:

发现具体情况
公平轮换文章开头的故事。功能完整,文档为零。
权限体系从扁平权限改成了基于作用域的权限,文档还是老版本。
Nagios 集成整个集成已在生产环境运行,文档没有。
团队管理团队详情页里添加成员、移除成员、退出团队这些操作完全没有文档。
RUM Source MapsAndroid ProGuard 和 iOS dSYM 的上传流程,上线好几个月了,从没写过。
协作空间导航UI 从 Tab 页切换成了侧边栏,文档还在描述旧布局。

这些都不是冷门功能。它们是真实用户在文档里找不到的真实功能。

21 个缺口已经让我们吃惊了。然后跑了 audit 模式,对全量前端代码做功能盘点。结果是 21 的十几倍。这让我们下定决心,把它从一次性清理变成每周例行运行。

自己动手做一个

不用从零开始写。Claude Code 有一个 skill-creator,可以根据描述生成 skill。下面是一个能用的 prompt 模板:

/skill-creator Create a doc-review skill that cross-references
source code against documentation to find documentation drift.

Context:
- Docs repo: company-docs (Docusaurus, English only)
- Source repos: billing-service (Go), web-app (React), api-gateway (Go)
- Docs structure: docs/ with subdirectories per product area
- Key concern: new features ship without doc updates

The skill should have two modes plus a fix phase:
1. Diff mode: scan recent git changes for doc-relevant signals
2. Audit mode: walk frontend code to inventory features, check coverage

After either mode, a fix phase generates complete documentation and opens a PR.

skill-creator 会帮你生成映射配置、prompt 和整体串联逻辑。搭出框架很快,大概一个小时。但调到好用需要迭代,我们大约调了四轮才把信噪比控制到满意的程度。关键在于你写进 prompt 的设计原则。以下是我们踩坑后总结出来的:

  1. 模块到仓库的映射。 一个 YAML 文件,把每个文档页面和对应的源码路径关联起来。没有这个,agent 要么被不相关的代码淹没,要么会遗漏关键变更。这是整套系统的骨架。

  2. 从前端入手盘点。 前端定义了用户可见的功能边界。后端有内部接口、预留字段、保留类型。从前端组件出发:页面、表单、开关、下拉框。后端代码只用来补充前端已经暴露出来的功能细节。

  3. 用产品经理的视角判断。 在 prompt 里写明:「像产品经理一样判断什么该写进公开文档。」只记录用户在生产环境中实际能操作的功能。跳过测试工具、调试面板、测试框架、内部管理功能、客户定制逻辑。

  4. 只检查可机械验证的事实。 检查字段名、默认值、API 路由、校验规则。不要试图验证行为描述类的文字;那需要人类判断,而且误报率很高。

  5. 前端是约束条件的权威来源。 后端说上限 100,但前端表单只允许填 50,用户看到的就是 50。文档该写 50。只有文档和前端矛盾时才记录为发现。

  6. 生成完整内容,不要占位符。 修复阶段必须读源码、写出完整文档:段落、表格、配置详情都要有。审核者的工作是润色文字,不是填空。这是「有用的 PR」和「一堆 TODO」的区别。

  7. 两阶段流程加人工关卡。 分析 → 发现文件 → 人工去除误报 → 修复。发现文件是两个阶段之间的检查点。信噪比调到满意后,可以用 --auto 跳过发现审核环节,但最终的 PR 仍然由人审核后才合并。

第一次跑出来肯定比较乱。先用 --dry-run 看看发现列表,调整 prompt。用 skill-creator 来迭代:把出了什么问题喂给它,让它优化 prompt 和映射配置。

调好之后就可以自动化了。我们用 cron 定时任务每周跑一次 diff 模式,有发现就自动提 PR。工程师只需要审核 PR。检测、撰写、提交都由系统完成。

我们学到了什么

文档漂移不能靠要求工程师变成更好的写手解决。有效的办法是把遗漏暴露出来,再给工程师一个内容完整的 PR。审核这样的 PR,五分钟的事。从零写一篇文档,半天起步,优先级永远排不上去。

我们本以为自己的文档还过得去,跑完才发现差得远。你的文档大概率也有类似规模的缺口,只是还没人去翻。

AI 擅长处理繁琐的部分:读几百个文件、交叉比对字段名、检查默认值是否和代码一致。它不擅长判断一个功能值不值得写进文档,也不擅长判断一句话会不会让用户困惑。PR 仍然需要工程师审核。我们去掉的是工程师最讨厌的那部分苦活。

一个实操建议:如果你的产品有 Web 界面,从前端代码开始扫,别从后端开始。我们前两轮迭代是后端优先,结果被一堆谁都不该写文档的内部工具淹没了。前端是天然的过滤器。如果你的产品是 API 优先、CLI 工具或 SDK,没有 UI,那就换个入口;API 路由定义或 CLI 命令注册是不错的起点。

下一步

公平轮换那个开关现在有文档了。数百个发现也都修好了。但这只是起点。

我们正在做两件事:一是把 diff 模式接入 CI,让代码合入时自动检测文档漂移,像跑测试一样跑文档检查,在缺口产生时就拦住它,而不是等每周定时扫描才发现。二是扩展检测范围到 API 文档。目前系统主要从前端入手,但我们的 Open API 也需要同样的覆盖率保障。

我们的文档在 docs.flashduty.com。现在工程师审核文档 PR,不再从零写文档。对我们有效的路径很简单:一个小时搭框架,四轮迭代调好,之后每周花五分钟审一次 PR。

相关阅读