智能体可读的网站:llms.txt、Markdown 镜像,以及真正会坏在哪里
一个智能体可读的网站,会为每个页面提供一份干净的机器副本,在页面头部声明它,并且不屏蔽任何需要抓取它的东西。难的不是决定要这么做,而是在第四次部署之后它依然成立,因为这里每一种失败在浏览器里都看不见,在你的日志里也不出声。
你正在读的这个网站就在跑这套东西:每个页面的 Markdown 镜像、每种语言一份 llms.txt、公开发布的 agent skills,以及一个只要其中任何一处坏掉就让生产构建失败的校验器。这篇文章列的是这个校验器实际抓到过的东西,比照着规范读出来的清单更有用。
想在投入修复之前先知道智能体能不能读懂你的网站?
运行免费检查器智能体真正会用到的四个接口
智能体可读性经常被当成一个话题来谈,但它其实是四个彼此独立的接口,而一个网站可以过了三个,仍然是隐形的。
| 接口 | 它回答的问题 | 失败时的样子 |
|---|---|---|
| robots.txt | 我到底允不允许抓这个? | 在答案里悄无声息地缺席 |
| 服务端输出的 HTML | JavaScript 运行之前正文在不在? | 一个带导航的空壳 |
| Markdown 镜像 | 有没有一份便宜且无歧义的副本? | 对渲染出来的外壳做又贵又嘈杂的解析 |
| JSON-LD | 这是谁发布的,它又是什么? | 靠正文猜出来的事实,或者什么都没有 |
顺序很重要。给一个检索抓取器根本无权读取的页面修结构化数据,是白干,而这正是这类项目最常见的倒着做的方式。
诚实地看 llms.txt
llms.txt 是一种约定,不是标准。没有引擎有义务读它,谁向你保证一定会被读取,那就是在夸大。它同时也很便宜,而且能给智能体一张干净的地图,而不是你渲染出来的导航,所以我们自己发布了一份,通常也会建议这么做。
如果你要发布,有三件事决定它有没有用:
- 绝对 URL。这是我们见得最多的错误,我们自己也犯过。llms.txt 会被抓走并四处传递,脱离它原本所在的页面,于是没有基准 URL 可以用来解析相对链接。一份满是
/services/…路径的文件,就是一份满是死路的文件。 - H1 下面那段引用块。那一句话,是智能体在介绍你时最有可能原样复用的内容。缺了它,智能体就自己写这句话,用的是它推断出来的东西。
- 每条链接都带说明。每个条目里
: 说明那一半,决定了预算有限的智能体先打开哪条链接。一份光秃秃的标题列表只能让它猜。
Markdown 镜像,以及怎么声明它
镜像就是与页面相同的内容,去掉外壳,以 Markdown 形式放在一个可预测的路径上,并从页面头部指向它:
<link rel="alternate" type="text/markdown" href="/services/ai-visibility.md">生成镜像很简单。让它保持忠实才是需要机器的部分,因为镜像可以以数月都没人察觉的方式出现细微错误。生成器在站点构建之后运行,遍历渲染后的 HTML,随后由校验器比对两者。
值得知道的失败模式
这些是我们自己构建里的真实发现,不是假设。在这道关卡存在之前,每一种都至少上线过一次。
| 失败 | 为什么会发生 | 智能体看到什么 |
|---|---|---|
| 未解析的模板值 | 生成器输出了它的空值占位符,或者一对模板分隔符没被渲染就留了下来,而没人看输出 | 一个把你的模板语言原样念回给你的页面 |
| 未闭合的代码围栏 | 有开始的围栏,没有结束的 | 它之后的每个标题都不再是标题,文档因此失去结构 |
| 未解码的 HTML 实体 | 转换过程中没有解码实体 | 一个未解码的与号或撇号实体,被当成它的字面字符读取,而不是当成那个标点 |
| 相邻链接被粘连 | 转换时丢掉了两个 anchor 之间的空白 | 两个链接文字粘成了一个词组 |
| 署名粘在链接上 | 作者链接前少了一个空格 | 一个作者字段,其中链接前面的那个词和姓名黏在了一起 |
| 表格列数对不上 | 表头行和分隔行对列数的说法不一致 | 这个块不再被解析为表格,于是每个数字都失去了它的列 |
| 不止一个 H1 | 外壳里的标题漏进了正文 | 连这个页面到底讲什么都变得含糊 |
| 正文为空 | 内容是客户端注入的,镜像没有东西可镜像 | 只有 front matter 和沉默 |
我们最喜欢的一例比上面任何一种都更细微。给某个页面加了一个装饰性图形,结果把一个字面的双引号放进了 SVG 文本节点,这让 HTML 压缩器在外来内容里停了下来。那个页面剩下的部分未经压缩就上线了,它的 Markdown 镜像在三分之二处悄悄截断,把整个 FAQ 一起带走了。在浏览器里看不出任何异常。是这道关卡让构建失败、指名了那条路由,而修复只是一个字符。
robots.txt 的陷阱:检索不等于训练
这是整个话题里代价最高的误解,而它只是一行配置。
有些爬虫存在的目的是收集训练数据。另一些是为了回答一个问题并当场引用而去抓取页面。屏蔽第一类是一个你很可能确实想做的授权决定。屏蔽第二类会把你彻底从答案里删掉,而这几乎总是无意的:
User-agent: GPTBot
Disallow: /
User-agent: *
Disallow: /第一段是有意的训练退出。第二段把所有答案引擎抓取器一起带走了,因为没有自己分组的爬虫会继承通配符规则。写下这份配置的团队以为自己退出了训练。他们同时也退出了被引用。
如果你想要的是“可以引用我,但不要拿我训练”,这个立场是自洽且可配置的:明确列出检索抓取器并放它们进来,再按名字屏蔽训练爬虫。
为什么要用关卡,而不是清单
上面每一项都很容易修一次,也不可能靠意愿一直修着。内容每周在变,模板每月在变,而这些失败都不产生可见症状。季度审计只会晚一个季度才发现它们。
所以这些检查属于构建流程,和测试放在一起。我们的检查在站点生成之后运行,并会让部署失败,于是坏掉的镜像是一条红色流水线,而不是一处慢慢渗漏。这就是全部的诀窍,也是我们把规则目录交出去而不是交一份报告的原因:智能体可读性检查器在你的浏览器里跑的就是同一套镜像规则,也正是决定这个网站能不能部署的同一段代码。
相邻的问题另有去处:我们的Open Knowledge Format 指南讲如何把内部知识打包成可移植的 Markdown,AI 就绪的公司 wiki 指南讲如何把这些知识提供给你自己的智能体。这篇文章严格只谈公开接口:别人的智能体能读到什么。
常见问题
什么让一个网站对智能体可读?
llms.txt 是标准吗?
llms.txt 里能用相对 URL 吗?
我们该屏蔽 GPTBot 吗?
Markdown 镜像会不会造成搜索引擎的重复内容问题?
上线之后怎么防止它退化?
最终思考
智能体可读性不是一个内容项目。它是四个机械性的接口、一份不长的转换错误清单,以及一行决定其余一切有没有意义的配置。
先从 robots.txt 开始,因为它检查成本最低,出错代价最高。然后提供一份干净的副本、把它声明出来、校验你的结构化数据,并把这一切放到关卡后面,让下一次部署必须继续让它成立。
