前阵子我给自己长期维护的一个基于 Rspress 搭建的组件库文档站做了一次 AI 可读性测试测试代号叫 Rspress-20。名字很直白我从用户真实提问中筛出了 20 个高频问题比如“某组件支持哪几种尺寸”“某个配置项的默认值是什么”“为什么某个属性改了没生效”然后把这些链接丢给几个主流的 AI 阅读工具让它们只根据文档内容作答。结果比我预想的还要难看真正答对且能在文档里找到依据的只有 3 个剩下 17 个里有直接把参数名搞混的有把废弃接口当正式接口推荐的还有 7 个属于“根本不在文档里但模型一本正经编了个答案”。我把问题换了几种问法也换了不同的 AI 产品结果大同小异。那时候我才意识到不是 AI 太笨而是我们的技术文档从来没有为机器读者准备好。这其实是一件很普遍的事很多人写文档时默认读者是“人眼”会用排版、颜色、折叠面板、交互示例来辅助阅读但 AI 拿到的是另一套东西——它看不到视觉重点只能读文本序列和标签结构。Rspress 作为一款体验很好的文档框架恰恰把这种“人眼友好但机器不友好”的反差放大了。这篇文章就围绕这个测试展开讲讲 AI 为什么会看不懂技术文档哪些写作习惯在拖后腿以及我后来实际落地的一套改造方案。产品手册、API 参考、团队 Wiki 的维护者应该都能在这篇里找到有用的东西。1. 一场代号 Rspress-20 的测试揭开了文档的“人眼滤镜”1.1 测试是怎么设计的测试对象是我参与维护的一个内部组件库文档站点技术栈上就是一套最典型的 Rspress 配置MDX 页面、代码高亮、自定义组件、交互式 Playground外加一个版本切换控件。整个站点有大概 60 个组件页面覆盖表单、布局、数据展示、反馈等几大类。说实话从人眼看这个站点不算差目录层级清楚组件页都有 API 表格示例代码也都可运行。团队之前甚至引以为豪觉得文档体验已经超过不少开源项目。但问题集是按照“人会怎么问”设计的。比如表格组件可以设置哪些列的固定方式日期选择器支持的最小粒度是什么某表单组件在 2.0 版本里改了什么为什么我在 onBlur 里拿不到旧的 value这 20 个问题我按来源分成三类客服对话记录、社区 issue、内部 IM 里高频提问。为了保证测试公平我没有给 AI 任何额外提示词只告诉它“请基于提供的文档回答如果文档里没有直接说不知道”。然后分别喂给它站点的根地址或者直接把某些页面的文本导出后粘给它。为什么要这么设计因为判断一份文档能否被 AI 读懂最直接的方法不是看它的排版多漂亮而是看 AI 能否用它回答真实的问题。换句话说问题集就是文档可用性的试金石。后来的事实证明这份试金石非常灵敏灵敏到整个测试变成了一场公开处刑。1.2 翻车现场AI 的三种典型“病”测试结果完全可以归纳成三类我分别起名答非所问、张冠李戴、一本正经地瞎编。先说答非所问。问“表格组件有哪几种固定列的方式”AI 回答“表格组件支持排序、筛选、分页”——其实这三个功能都有但固定列方式写在另一个 Tab 页里AI 只读到了当前 Tab 的文字信息没被抓全。用户拿到这个答案会以为自己问错了问题实际上文档明明有答案只是藏得太深。张冠李戴这个更常见。问“某旧组件最高支持到哪个版本”AI 把文档里“历史版本说明”中的用法当成当前推荐用法还推荐用户继续使用。其实该组件在新版文档中已经标注废弃。问题出在页面没有版本元数据AI 分不清哪段内容是当前版本哪段是历史记录。最恶劣的是第三种一本正经地瞎编。当某问题的答案在文档中根本找不到时AI 不是乖乖说“文档没写”而是根据自己训练数据里的相似知识生成一段看似合理的解释。比如它把某个配置项的默认值从medium说成large然后自信地给出了使用示例。这种错误对用户伤害最大因为看起来太专业了。三类问题同时出现在一份“人看很舒服”的文档上这就说明问题不在某个 AI 产品的识别能力而在于文档本身。故障类型典型表现我们后来定位的根因答非所问检索到的内容与问题不相关关键信息被折叠或放在交互组件中AI 没读到张冠李戴拿旧版本或无关页面回答新问题页面缺版本元数据上下文不完整一本正经地瞎编文档没有也强行编造内容切块后上下文不足模型用先验知识补全1.3 问题不在模型在文档传递链路要理解为什么 AI 会这样得先知道 AI 在回答文档问题时到底经历了什么。完整链路大概是爬取、解析、切块、向量化、检索、生成最后才是你看到的回答。爬取阶段大多数 AI 阅读器会先抓一遍页面。对于 Rspress 这类静态站点初始 HTML 里通常有正文但那些依赖 JS 渲染的组件内容可能拿不到。比如 Playground 的 props 表格原始页面里确实有数据但如果它被包在一个客户端组件里爬虫抓下来的只是一个占位符。解析阶段自定义标签像Tips、Tabs、Carousel可能被当成普通div原本的“提示”“警告”“示例”语义全部丢失。切块阶段更粗暴很多工具按固定字数把长文档切成一段段组件 API 表格和示例代码往往被劈到不同块里语义关联断裂。向量化之后检索阶段又因为用户问法和文档用词不一致召回到一堆无关文本。最后生成阶段如果检索结果给的信息不够AI 为了让回答“看起来完整”就会用训练时学到的相似知识填坑。这一整套流程下来任何一环出问题最终答案都会跑偏。所以别急着骂模型模型只是在尽力把一段支离破碎的信息拼成听起来正常的句子。真正的问题是文档从最开始就没有为这段链路设计过。2. 为什么 Rspress 这类工具反而放大了问题2.1 Rspress 的优势与隐蔽代价先说清楚Rspress 本身没有做错什么。它的定位是快速构建高性能文档站点基于 Rspack 打包MDX 语法、内置搜索、组件生态样样齐全。我甚至见过很多开发者用它搭博客、搭组件库、搭内部知识库体验比不少传统方案好太多。但恰恰因为这套工具太“好用”作者们很容易把文档当成“页面”来做而不是当成“知识文本”来做。一个页面里有侧边栏、面包屑、卡片、按钮、折叠框、Playground人的阅读体验确实上去了可对 AI 来说视觉排版带来的“重点提示”全都失灵了。HTML 里也不是没有语义标签但多数自定义组件根本没考虑 aria 语义和无障碍标记自然也不会为机器阅读留任何接口。注意我后面说的任何改造方案都不是在说 Rspress 不好而是提醒大家——文档框架解决的是“怎么展示”技术写作解决的是“怎么被理解”。两者之间缺口比想象中大。2.2 组件化写作让内容变成“信息孤岛”在 Rspress 里写组件文档很容易发展成一套“组件套组件”的写法。比如说一个日期选择器页面的内容会分散在index.mdx正文和说明api.ts参数类型定义demo.tsx演示代码若干封装的PropsTable、Playground组件动态生成参数表、实时示例从人类的视角看这没啥问题因为浏览器会把这些组装成完整页面。但 AI 看到的却是多个离散的代码文件。尤其是api.ts里如果定义了一个params数组AI 能看懂数组里的键值对却很难关联到“这个参数属于日期选择器、由哪个版本引入、默认值是什么”这些上下文。再比如很多文档喜欢用折叠面板。人的习惯是点开“高级用法”看里面藏的配置说明。但对于 AI折叠内容在 HTML 里可能直接没有加载或者被包裹在交互状态中。我看到的那次测试里有一道题失败的原因就是因为答案被放在一个默认折叠的details标签里AI 抓取时只看到 summary 那几个字。2.3 视觉排版是 AI 的噪音不是信息人眼识别信息靠什么靠层级、颜色、留白、图标。一个红色框里面写“不推荐在生产环境使用这个参数”人是不会看漏的。但 AI 只会读取文本节点和标签属性它不会因为某段文字旁边有个警告图标就觉得这段话重要。如果自定义组件渲染出来的是div classwarning.../divAI 连“这是一句警告”都无法判断。更麻烦的是Rspress 默认主题生成的页面里导航信息非常多顶部菜单、侧边栏、面包屑、页内锚点、上一篇/下一篇甚至还有返回顶部按钮。如果 AI 工具把所有可见文本一股脑推给模型这些导航文本会变成噪音把真正的正文挤到一边。人看页面时会自动忽略导航AI 不一定会。我还见过更极端的案例某页面在正文上方放了一个巨大的 hero 区域里面是产品名 一句 slogan。AI 爬虫优先抓到那段 slogan把它当成整个页面的核心内容导致后续所有检索都偏向宣传语而不是真实的 API 说明。这种设计对人有冲击力对机器就是灾难。2.4 表格、代码块、交互式示例的“认知负担”表格是 API 文档的重头戏但 AI 对表格的理解能力远没有到“看一眼就懂”的程度。Markdown 原始表格还好列名、行内容还算规整但很多项目为了提高表格体验用了自定义 Table 组件支持筛选、排序、固定列、虚拟滚动。这些组件渲染出的 HTML 结构复杂AI 清洗后很可能把所有单元格文字压成一行列和行的关系直接消失。测试里有一道“哪些组件支持排序”AI 给出的列表虽然看起来像模像样但把两个不同表格的数据混在了一起就是因为行列语义丢了。代码块也经常出问题。文档里我们既会写“推荐用法”也会写“不推荐用法”。正常人可以靠文字判断哪段是反例但 AI 分不清。它看到一段代码标注了tsx然后就当成最佳实践吸收。一旦用户按这个错误示例去实现就会踩坑。我后来在测试中发现AI 会把注释里带“不要”的代码块同样当作示例因为它不理解否定语气在代码示例中的含义。至于 Playground 这类实时交互示例它本质上是一段在浏览器里运行的 React 组件很多 props 都放在组件的 state 里正文 HTML 里根本没有这些文本。AI 能读到的只有一个 iframe 或者一个挂载点。人可以用鼠标去玩AI 连进去的门都找不到。3. 技术写作的遮羞布我们一直欠文档一笔语义债3.1 人眼友好不等于机器可读传统技术写作有一套自己的衡量标准准确性、清晰度、一致性、可扫描性。这些标准默认的读者是一个能看图、能跳读、能联想的人类。你在文档中写一句“配置项见表 2”人会自动找到表格AI 不会。你写“如上所述”AI 得回溯好长一段才能猜到你指的是什么人扫一眼就懂了。所以在 AI 时代文档的定义正在悄悄发生变化读者不只是人类还包括各种 AI 阅读器、检索增强生成系统、代码智能体。“写得清楚”这四个字的含义从“人看着清楚”扩展成了“人和机器都能提取出准确信息”。这个变化很多老规矩来不及跟上。我也见过不少文档作者抱怨“AI 连这种文档都看不懂也太蠢了”。但换个角度想如果一份文档只能依靠人的上下文联想才能读懂那它本质上是一份“半成品”。以前没有机器读者半成品也能凑合用现在 AI 成了文档的实际消费者之一这层窗户纸迟早被捅破。3.2 缺少单一事实来源AI 很容易人格分裂第二个问题是信息重复且不统一。同一个配置项经常在概览页、API 页、示例代码、更新日志里各出现一次。如果这几个地方相互印证人会觉得很正常但一旦写法不一致AI 就会混乱。我们测试中就有一道题AI 把示例代码里的sizemd、API 表里的size: medium | small | large混在一起回答说支持md、medium、small、large四种取值但实际上 API 只接受全称写法。解决方向只有一个单一事实来源。把关键定义固定在一个来源里其余页面引用它而不是再抄一份。写作上听起来容易实际操作里需要很强的纪律文档每次改动都要同步所有引用处否则 AI 稍一对比就发现矛盾。这种问题在多人协作的项目里尤其严重。A 同学在改 API 文档B 同学在写博客教程C 同学在更新示例代码三个人各写各的最后 AI 读到三个版本的解释只能随机挑一个或者把三个混淆在一起经常还都是错的。3.3 上下文缺失AI 不知道“这是哪个版本的哪个包”另一个常见问题是页面缺少“身份信息”。一个组件文档光看内容可能很完整但 AI 不知道它在哪个包、属于哪个版本、依赖什么环境、是否已经废弃。Rspress 的版本切换通常是一个下拉框AI 抓取时只会看到默认版本的内容。结果就是用户问“最新版怎么配代理”AI 拿旧文档的配置去答。这种问题的解法是给每个页面补充元数据包括版本号、包名、废弃标记、相关文档链接。让人不管打开哪个页面都知道自己身处何地也让 AI 每次读取都能获得同样的坐标。我在测试中还发现很多页面的标题只有一个组件名比如“Table”没有前缀、没有版本、没有说明。AI 抓下来之后完全不知道这个 Table 是哪个框架的 Table是 HTML 原生表格还是业务组件库里的表格。如果页面标题本身能带上“某某组件库 Table API 参考v2.3”检索准确率会立竿见影地提升。3.4 简洁≠含糊被误会的“简明”写作技术写作一直提倡简洁这是对的。但不少人把“缺少必要信息”也包装成了简洁。比如只写“该属性可配置”不写默认值、不写可选值、不写前置条件或者只在代码注释里提一句正文完全不带。代码注释的确方便直接但 AI 阅读文档时并不会自动去翻源代码仓库。就算它翻了注释里也经常缺少和正文的关联。我碰到的真实案例是某个组件的onError回调文档里只写“处理错误”没有说明如果回调本身抛异常会发生什么。AI 被问到这个问题时用通用编程经验回答“异常会被外部捕获”实际上组件内部会直接中断渲染。这种信息缺口对人是小瑕疵对 AI 就是断崖。所以现在我写文档时会提醒自己凡是涉及参数、默认值、边界情况的内容宁可多写三行也不要省成一句话。省下来的时间会在用户翻车之后加倍还回去。4. 怎样把文档写得“AI 能读懂”一套可以落地的改造方案4.1 写作时给文档建立“语义骨架”先改变写作习惯。标题不要只写“size 属性”要写成“size 属性支持三种尺寸默认值为 medium”。这是一种断言式标题AI 能直接从标题提取答案。段落的第一句话也尽量给出结论后面再放解释、示例、注意事项。对 AI 来说这种“核心结论前置”的结构比铺陈半天再给答案友好得多。语义标签也很关键。自定义组件在渲染时尽量带上标准 HTML 语义警告用rolealert提示用rolenote代码示例在标题里写明“推荐用法”还是“错误示范”。Rspress 的 MDX 生态允许你自定义组件渲染输出改起来并不难关键是写组件的人要有这个意识。在文档里增加显式的“前置条件”和“边界情况”列表。这不仅是写给 AI 的也是写给人类读者的。写清楚“本组件要求项目使用 React 18 以上”比让用户自己踩坑强一百倍。边界情况也应该明确写比如“当传入空数组时组件显示空态占位图”“当禁用状态下点击不会触发事件”。这些看起来琐碎却是 AI 问答中最容易翻车的区域。4.2 给 AI 提供“纯文本视角”的平行内容对现有站点的改造性价比最高的动作就是提供纯文本版本。我在项目中做了一个构建脚本把每个 MDX 页面渲染成一份规整的 Markdown 快照折叠面板全部展开、Tabs 内容全部拼在一起、组件 API 表格转成 Markdown 表格、Playground 里可控的参数列表以 JSON 形式输出。快照放在public/ai/目录下页面路径与原始文档一一对应最后在robots.txt里放行。目录结构大概长这样public/ ai/ ai-context.md components/ button.md form.md table.md robots.txt为什么这么做因为 AI 阅读器读取 HTML 页面时需要经过复杂的解析和清洗但直接读一份干净 Markdown 时信息损耗几乎为零。折叠内容、Tab 中的参数、被隐藏的高级用法在快照里都是可见的。这等于白送给 AI 一条无障碍通道。很多团队担心维护成本。其实快照完全可以在 CI 里自动生成每次文档变更后重新构建即可。唯一要注意的是快照不能只复制正文还必须在开头带上页面标题、版本号、所属包、摘要说明。否则 AI 拿到的是“一坨没头没尾的文本”照样不知道上下文。4.3 用元数据补齐上下文让单页文档自成体系在 frontmatter 里认真填这些字段title、description、version、package、deprecated、related。description尤其重要它不能是空泛的“该组件用于表单场景”而应该写成“本页介绍某某组件的全部 API、属性默认值与常见错误适用于 React 18 项目”。AI 在切块时会把这句 description 当作最重要的向量锚点。还可以用 JSON-LD 的TechArticle类型给页面加结构化数据。搜索引擎和 AI 工具对 JSON-LD 的识别度都不错写清楚 headline、datePublished、version、about等于给文档加了一层机器可读的身份证。Rspress 允许你在 HTML head 中注入自定义 meta 标签这个不难实现。再往前一步在仓库根目录放一个ai-context.md用自然语言描述这个文档库的整体背景这是什么产品、有哪些核心模块、模块之间什么关系、文档的版本策略是什么。这个文件应该成为 AI 工具的“总入口”并建议先读它再读具体页面。相当于给 AI 画了一张地图。社区里还有一种做法是llms.txt简单理解就是给大模型看的站点索引。它可以用三五行说明项目背景然后列出关键页面的路径和用途。我实测下来它主要解决“发现”问题不能替代每个页面的语义完整。所以别指望一个文件解决所有事它只能算锦上添花。4.4 把 AI 问答测试变成文档发布的常驻流程改造完结构怎么知道效果好不好我的经验是把 AI 问答测试固化下来。整理一份ai-test-questions.json里面存 20~50 个高频问题每个问题带上预期答案的关键词以及应该在文档哪个位置找到依据。[ { id: q01, question: 表格组件支持哪几种固定列方式, expectedKeywords: [left, right, scroll], expectedDoc: components/table.md } ]每次文档 PR 合并前在 CI 里跑一遍把最新构建结果交给一个 AI 阅读器让 AI 只基于这份文档回答问题然后按“答案是否包含预期关键词、是否引用正确文档位置”来打分。团队可以约定一个最低通过率比如 80%没过就不允许发布。这套流程的价值不在于自动化本身而在于它逼着你用“用户会怎么问”来反推“文档应该怎么写”。很多之前被忽略的语义缺口在测试里会原形毕露。而且这个测试跑得越频繁团队对“机器可读”的敏感度就越高写文档时自然会注意默认值、版本、上下文这些细节。5. 真实改造记录一个 Rspress 项目从“AI 看不懂”到“AI 能答上话”5.1 我实际做了什么回到文章开头说的那个 Rspress 组件库文档站。在跑完 Rspress-20 测试后我拉着团队花了大概两周时间做了一次集中改造。第一步先导出全部 MDX人工标记哪些内容必须被 AI 看到所有 API 表格、所有默认值、所有版本变更说明、所有“不推荐”提示。第二步给每个页面补 frontmatter把version、package、deprecated、related填全。第三步改掉自定义组件的渲染逻辑Tips输出rolealert加aria-labelTabs默认展开第一个 Tab并在纯文本快照里把全部 Tab 的内容按顺序拼接Playground旁边额外渲染一份参数 JSON。第四步新增ai-context.md和public/ai/快照目录构建脚本自动生成robots.txt 放行。第五步把 20 个测试问题整理成ai-test-questions.json接入 CI。两周后重新跑了一遍 Rspress-20。结果对比很明显瞎编类问题从 7 个降到了 1 个答非所问从 10 个降到 3 个20 道题里有 16 道已经能在文档中定位到明确依据。剩下 4 道主要是因为用户问法实在太口语化比如把“日期选择器能不能只选到月”说成“怎么按月份挑时间”文档里没有完全同义的说法。我们用同义词扩展的方式在相关页面补了几行说明问题也解决了。这里要特别说明这个改造不是一次性的而是变成了一种常态。之后任何新组件文档必须过 AI 问答测试才能在站点上线。刚开始团队会觉得烦但跑了两三次之后大家写文档时已经默认会写清楚默认值和前置条件。效果远比开写作培训要好。5.2 改造过程中踩过的坑有几个坑印象很深。第一个是纯文本快照容易做成“信息倒马桶”——把所有内容一股脑拼成一个几万字的大文件。AI 读到后面基本失去注意力。后来我在快照里采用了和页面一致的段落结构并且每个组件一个文件不搞大杂烩。第二个坑是只加role标签不够。有些 AI 阅读工具在预处理时会把 HTML 压缩、去属性role会被丢掉。我们的对策是语义信息不能只依赖标签正文里也得有显式文字。比如警告不仅要有rolealert正文本身要写“警告该参数会导致组件重新挂载”。双保险。第三个坑是版本元数据如果只写在 frontmatter 里AI 可能还是拿不到因为 frontmatter 在渲染后不显示在页面上。稳妥做法是让页面顶部渲染一行可见的文字“适用于版本 v2.3.0自 v1.8.0 引入其余版本请参阅文档站版本目录”。AI 在正文里也能看到版本信息。第四个坑也是我最想提醒大家的别让 AI 摘要代替招标。很多团队喜欢在页面顶部放一个“AI 生成的摘要”但摘要写得含糊等于没写甚至有可能误导。摘要要用断言句写清楚“这个页面回答了什么”而不是“本文介绍了……”。5.3 常见问题速查表现象可能原因排查方向AI 答非所问检索到的文本与问题无关确认纯文本快照是否包含全部折叠/Tab 内容补口语化同义词AI 引用过期版本页面缺版本元数据或默认版本过旧在 frontmatter 和正文顶部都写版本号更新默认版本内容AI 把错误示例当推荐代码块缺少正反例标注在标题或文本中显式标注“错误示范”/“推荐用法”AI 忽略警告信息警告块缺少语义标记改用标准 blockquote 并加 rolealert正文也写明“警告”二字AI 给出文档之外的内容上下文切块不足模型用先验知识补全增加结论前置、补全默认值和边界情况降低切块信息损耗AI 无法定位具体 APIAPI 表格结构复杂行列语义丢失提供 Markdown 版表格避免只用自定义表格组件如果你遇到这里没有的问题可以把 AI 的回答和文档原文对照一下问题通常出在语义连通性上某个关键信息在文档里存在但是 AI 找不到或者存在多个版本AI 选错了。这两种情况分别对应缺上下文和缺唯一性。6. 写在最后一点个人体会做了这次改造之后我对技术写作的理解变了很多。过去我评价一份文档好不好会看结构清不清楚、示例全不全、措辞顺不顺现在我还会问一句把整页纯文本丢给 AI它能回答几个问题这个问题听起来简单实际上非常狠因为 AI 没有视觉滤镜它只看信息本身是否完整、语义是否有依托。写不清楚的地方它一定答不对。我那 20 个问题里的 7 个瞎编答案就是技术写作遮羞布的具体样本。它们不是 AI 的错是我们长期不把机器当读者欠下的债。如今 AI 变成文档的实际消费者之一这层布被撕开其实是好事。至少现在写文档的人多了一个非常挑剔的读者你我都会因此把话说得更完整。最后分享一个几乎零成本的小技巧下次文档发布前自己用一个 AI 阅读器读一遍新页面问它三个你最关心的问题。如果答错了先别急着调整提示词回去看文档。大概率会发现那里藏着一个你一直没补齐的语义缺口。