关闭

Tryaivo™技术

llms.txt v2 实测:平台随手挂 rel,手工站全漏

2026-11-15 03:29:53 浏览:

一句话结论:llms.txt v2(2026-08-10 发布)把"代理怎么找到页面的 Markdown 版"从猜地址改成 rel 关系;实测一圈发现,平台托管的文档站自动就挂好了,而手工维护的站连规范自己点名的参考实现都还没挂。

背景:代理不是不想读,是读不到、也找不到

文档站的 HTML 页面是给人看的:导航、样式表、脚本、脚注弹层全都塞在里面。同一篇教程,实测 HTML 191,663 字节,它的 Markdown 兄弟文件只有 54,867 字节——体积压到 28.6%,也就是能省下 71.4% 的上下文。代理把 19 万字节的 HTML 整个读进去,大半 token 花在了 <script> 和 CSS 类名上。

v1 只解决了"入口在哪"(根目录一个 llms.txt)。v2 解决的是剩下两件事:

  1. 每个页面有没有一个干净的 Markdown 副本;

  2. 代理怎么不用猜就知道这个副本的地址,以及描述这个页面的 llms.txt 在哪。

v2 给出的答案是两个标准 link relation:

  • rel="alternate" type="text/markdown":指向本页的 Markdown 版;

  • rel="describedby":指向覆盖本页的 llms.txt(/docs/llms.txt 覆盖 /docs/ 下全部内容)。

两者都可以写成 HTML <link> 元素,也可以走 HTTP Link: 响应头——后者能在服务器或 CDN 层加,不用改任何页面代码。

落地清单:可直接抄的四件事

1. 文件位置:根目录还是子路径

v2 明确允许放在任意子路径,文件覆盖其路径下的所有 URL;多个文件同时命中时,代理应取最具体的那一个。

实测规范里点名的参考实现 FastHTML:

路径状态Content-Type体积行数
/docs/llms.txt200text/plain; charset=utf-84,796 字节46 行
/llms.txt(站点根)404text/plain——
/docs/llms-full.txt404text/plain——

根目录 404 不是漏配,而是有意只覆盖 /docs/ 子树:站点首页是营销页,对代理没有价值,塞进 llms.txt 只会稀释信号。同理,llms-full.txt 也不是必选项——只有当你的文档确实需要"一次给全文"时才建,否则目录 + 按需取 .md 更省 token。

2. llms.txt 原文片段(真实文件,节选脱敏)

# FastHTML

> FastHTML is a python library which brings together Starlette, Uvicorn, HTMX, and fastcore's `FT` "FastTags" into a library for creating server-rendered hypermedia applications.

Things to remember when writing FastHTML apps:

- Although parts of its API are inspired by FastAPI, it is *not* compatible with FastAPI syntax
- Use `serve()` for running uvicorn
- When a title is needed with a response, use `Titled`

## Docs

- [FastHTML concise guide](https://www.fastht.ml/docs/ref/concise_guide.html.md): A brief overview of idiomatic FastHTML apps
- [HTMX reference](https://raw.githubusercontent.com/bigskysoftware/htmx/master/www/content/reference.md): Brief description of all HTMX attributes

看得出三个要点:H1 是站名(唯一必填段)、blockquote 写清"这是什么",H2 下的链接直接指向 .md 而不是 .html——这就是 v2 说的"链接应该指向 LLM 友好内容",让代理少一次跳转、少一次 HTML 解析。

3. 让代理不用猜:两种挂法,实测都有人用

HTML 元素挂法(平台自动注入,实测 gitbook.com/docs 的 <head> 里真有):

<link rel="alternate" type="text/markdown" href="https://gitbook.com/docs/readme.md"/>

同一次抓取里还能看到三条 rel="alternate" hrefLang= 的多语言互指(ja / zh / fr),和 Markdown 副本的 link 混在一个 <head> 里——两种 alternate 语义并存时,靠 type="text/markdown" 区分,客户端不实现这个判断就会读错。

HTTP 头挂法(CDN / 反代层加,不动页面代码)。Nginx 示例:

# 只对 /docs/ 下的 HTML 文档页下发两个关系
location ~ ^/docs/(.+)\.html$ {
    add_header Link "</docs/$1.html.md>; rel=\"alternate\"; type=\"text/markdown\", </docs/llms.txt>; rel=\"describedby\"";
}

注意 add_header 默认只在 200/201/204/206/301/302/303/304/307/308 上生效;一旦加 always,404、500 也会带上这个头——这就是下面第 5 条踩坑的来源。

Cloudflare 侧同理,用 Transform Rule 对 http.response.headers 加 Link,匹配条件限定 starts_with "/docs/" 且 http.response.code eq 200。

4. robots 与爬虫:两件事,不要混

robots.txt 管的是"允不允许来拿",llms.txt 管的是"来了之后读什么",二者不互相替代。最典型的翻车是把 llms.txt 当爬虫许可:写了文件、却没在 robots.txt 里放行对应 UA,结果代理连门都进不来。需要放行的常见 UA(按用途分,不是全放):

  • 推理期按需取内容的:ClaudeBot、OAI-SearchBot、PerplexityBot

  • 训练用的:GPTBot、Google-Extended、CCBot(llms.txt 的定位是推理而非训练,按业务取舍)

结果:一圈实测,谁真挂了、谁只是写了文件

全部为单台 Windows 终端、Chrome UA 直连 GET 实测,时间 2026-11-15:

站点llms.txt体积 / 行数head 里 rel=alternate type=text/markdown.md 兄弟文件
gitbook.com/docs200—有(自动注入)有
mintlify.com/docs20026,389 字节无(走 HTTP Link 头)平台生成
fastht.ml/docs2004,796 字节 / 46 行无(0 条)200,text/markdown,54,867 字节
docs.anthropic.com200102,075 字节 / 995 行无(0 条)假 .md(见踩坑 1)
vercel.com/docs2005,540 字节无—
developers.cloudflare.com20016,609 字节无—
developers.openai.com403———

三条观察,逐条标口径:

  1. 平台型站点已经默认合规:GitBook 自动给页面挂 markdown alternate,Mintlify 在平台层统一下发 HTTP Link 头。托管在这些平台上的站,这一项不需要自己动手。

  2. 规范自己的参考实现没挂 rel:FastHTML 的 /docs/ 按 v2 放了 llms.txt、也确实提供了 .html.md 兄弟文件(200、text/markdown),但页面 <head> 里 rel="alternate" 与 rel="describedby" 计数为 0,HTTP Link 头也为空。也就是说,代理仍要靠"猜文件名后缀"才能摸到那份 Markdown。规范写了的推荐项,参考实现未必做了——这是本轮最值得记的一条。

  3. OpenAI 那条 403 不能算到 llms.txt 头上:developers.openai.com 的 /llms.txt 返回 403,但同域 /robots.txt 同样 403、响应体为空,说明是整站对本网络环境不可达(Vercel 边缘拦截),不能据此说"OpenAI 拦了 llms.txt"。这也是请求日志类结论必须标口径的原因:单点不可达 ≠ 站点拒绝代理。

关于"到底有没有代理在用",公开可查的请求日志研究给出的量级是:Ahrefs 分析 2026 年 5 月 137,210 个域名的 /llms.txt 请求,约 38,000 个域名返回了有效文件,但其中只有约 3% 的文件在该月收到过任何请求;被识别的 AI 客户端只占测量请求的 19.5%,其中代理类占 10.5%,Claude-Code 的请求量高于任何检索、助手或训练爬虫(来源:llmtxt.info 汇总 Ahrefs 数据,非本机复现)。结论不是"没人用",而是自动化发现还没普及,主动去取的多数是开发者工作流——这恰好是 rel 关系能改善的地方。

复盘:3 条照着抄,2 条别再踩

三条配置动作:

  1. 双文件分工:/docs/llms.txt 只做目录 + 关键约束,控制在 10KB 以内;细节全部放在链接后面的 .md。目录里链接一律指向 .md,不要指向 .html。

  2. rel 用 HTTP 头挂:CDN/反代层下发,匹配条件必须限定 2xx 且路径前缀,, 不要用 always。这样即使站点有几千页也不用逐页改模板。

  3. 验证方式固定成两条 curl:curl -sI <页面> 看有没有 Link:;curl -sI <页面>.md 看 Content-Type 是不是 text/markdown。两条都过,才算真落地。

两条踩坑提醒:

  1. 假 .md:服务器 fallback 兜出 200。实测把 /docs/.../overview.md 拼到 Anthropic 文档页后面,返回 200,但 Content-Type 是 text/html、体积 301,107 字节——不是 Markdown,是整页 HTML。代理如果只看状态码,会把 30 万字节 HTML 当成 Markdown 吞掉。判断标准必须是 Content-Type,不是 200。

  2. 平台层无条件下发 Link 头会污染错误页。实测 mintlify.com/docs/llms 本身是 404 页面,但其响应头里照样带着 </docs/llms.md>; rel="alternate"; type="text/markdown",指向一个不存在的 Markdown(该 .md 也是 404)。代理会反复重试死链,浪费的是它自己的预算,也是你的抓取配额。

标注

  • llms.txt 版本:v2(规范页 llmstxt.org 于 2026-08-10 发布 v2;v1 文件仍然完全有效)

  • 规范状态:作者主张的提案,未被任何 LLM 官方确认为强制标准;rel="alternate" / rel="describedby" 需客户端自行实现才生效

  • 实测时间:2026-11-15,单机单次直连采样,非全量统计

  • 来源:llmstxt.org v2 规范页;gitbook.com/docs、mintlify.com/docs、www.fastht.ml/docs、docs.anthropic.com、vercel.com/docs、developers.cloudflare.com 的直连响应;llmtxt.info 汇总的 Ahrefs 2026-05 请求日志研究

  • 标签:llms.txt、AI可见度、AIWO、结构化暴露、技术文档站

标签:markdown html文件