AI-Native 之路 —— 从 AI 友好型网站改造开始

背景
一次有趣的发现
我在日常工作和学习中使用 Claude Code 比较多,然后近半年在团队里也做了一些 AI 相关的探索和建设。所以我不时会去访问 Claude Code 文档,了解下这个非常成熟的 Harness 背后的工作逻辑,并时常因为了解到某个机制和原理感到充满激情。
在一次阅读文档的过程中,一个按钮引起了我的注意:以 Markdown 格式查看。

点击后在新标签页打开了一个全新的页面,里面同样放的是一篇文档,但没有任何样式,是一篇用 Markdown 格式写的纯文字文档。URL 是原本文档页面后面加一个后缀 .md。
当下我就觉得很有意思,这和我在许多年前看过的一种做法有些相似 —— 有些以文字为主的网站会提供纯文字版本或省流量版本,如下图:

我曾经也想在我的个人网站里实现这种省流版本的功能,但当时还没有 Agent 帮忙干活,而且我的网站本来也不怎么消耗流量,就没有花时间去研究了。
多年以后我再次看到类似的东西,我对以前的我产生了共鸣,感触良多。
搜索范式的改变
我的个人网站 LRD.IM 积累了我多年来的工作作品集、设计博客等。当时做这个网站目的是让自己当下的认知、经验能以某种形式长期存下来,也是我对外展示自己的窗口,还有一点很重要 —— 这是我做网页开发技术实践的游乐场。
当时网站做完后,希望自己的名字和内容能轻易被搜到,于是做了一些简单的 SEO:meta 标签、RSS Feed、文章同步发到 Medium(该网站在谷歌权重极高)等等。
但现在 AI 已经取代了一部分搜索引擎的工作。当 Agent 判断已有的知识不足以回答用户的问题时,就会主动进行网络搜索、抓取并阅读网页。

此时问题来了:AI 是怎么进行联网搜索、阅读网页的?我的个人网站 LRD.IM 在 AI 的视角下是怎么样的?足够友好吗?
我心里没有底,觉得这是一个值得探讨的课题。于是我决定做一次深入调研,然后对网站进行 AI 友好型改造。
工具调研
先说 AI 怎么搜索。调研下来发现,Claude Code 和 Codex 的联网搜索底层都是调用搜索引擎,本质上跟传统 SEO 没太大区别。常规的 Sitemap、robots.txt、JSON-LD 该补的补上就行,这块我没花太多精力。
真正值得深挖的是第二个:AI 怎么阅读网页,抓到的内容质量如何。这直接决定了我的网站在 AI 眼里的样子。
以我最常用的 Claude Code 为例,主要通过三种方式抓取网页内容:
- CURL:最基础的抓取工具,通过 Bash 命令执行;
- WebFetch 工具:Claude Code 官方的网页内容提取工具;
- 第三方 MCP 工具:Tavily、Brave 和 Firecrawl 等。
为了不停留在表层,我决定先调研下这三种工具,看看它们是怎么运作的,行为是怎么样的,边界又在哪里。
调研 CURL

CURL 是最原始、最直接抓取网页内容的工具,工作流程如下:

- 大模型写出
CURL指令,在 shell 里执行; - 通过 HTTP 请求拿到网页的 HTML 完整内容(就像是我们在网页里右键 → 查看源代码那种);
- 把原文返回到主会话的上下文窗口。
以我的网站 LRD.IM 为例,CURL 后拿到的内容开头是这样的:
// curl -s https://lrd.im
<!DOCTYPE html><html><head><meta charSet="utf-8" data-next-head=""/><meta name="viewport" content="width=device-width" data-next-head=""/><meta name="author" content="李瑞东" data-next-head=""/><meta name="description" content="李瑞东的个人网站,记录我在设计路上的作品、经验和观点。" data-next-head=""/><meta property="og:image" ...CURL 确实能抓到网页内容,但返回的结果对 AI 很不友好。原因是会把完整的 HTML 原文塞回给主会话的上下文,这会导致:
- 噪音多:包含很多无效内容,比如:各种 HTML 标签、脚本代码、样式/SVG 图标代码等,动辄数万个字符;
- 成本高:主模型处理大段内容时 Token 消耗大,推理时间久;
- 上下文压力大:大体积 HTML 会快速占满上下文窗口,挤压了其他任务的空间;
- 影响准确率:AI 处理得费劲,就会尝试偷懒。噪音越多、文件越大,就越有可能不准确。
同时这个工具对客户端渲染(CSR)的页面束手无策,拿到的可能只是一堆 <script> 标签,没有正文(其实后文提到的另外两个工具也会这样)。不过我在日常使用中也触发过这个 tool_use,绝大多数情况都是能抓到内容的。
但其实 CURL 并非没有优点。CURL 支持自定义标头,让站点优先返回特定格式的内容。比如 text/json 等,可以让网站优先返回 JSON 格式的内容,用于调试 API 接口或抓取结构化数据时非常方便,省去了解析 HTML 的麻烦。
调研 WebFetch

这是 Claude Code 官方的网页内容获取工具,每次我在问一些网上的资料时,经常能看到这个被调用了。这让我更好奇这个工具到底是怎么运作的。调研后了解到 WebFetch 的工作流程:

1. HTTP 请求获取页面
Agent 确定要访问一个链接后,会用 HTTP 来访问指定的URL,其中:
- 会以
Claude-User开头的User-Agent标头来访问页面; - 优先用 Markdown 的
Accept标头来访问。
其中第一点会让网站知道「谁要来访问」了,这样做有利也有弊。好处是网站的管理员能知道有多少流量是通过 Claude 访问的,同时做法也更加透明和合规,没有伪装正常流量。
但是也有坏处:容易被服务器直接屏蔽。通过 Vercel 建设的站点,默认会在防火墙中屏蔽掉机器人的流量。以我的站点 LRD.IM 为例:

我的个人网站就是用 Vercel 部署的,也试过让 Claude 使用 WebFetch 工具访问。但正如上文提到的,被服务器拦截了。
如果想在 Vercel 中解除拦截,需要订阅 Pro 套餐后在防火墙设置系统级的 Bypass 规则。但订阅费不便宜,每月 20 刀,个人觉得不太划算。
值得一提的是,第二点「优先用 Markdown 头来访问」是我认为最明智的设计。如果能成功访问站点,WebFetch 会尝试去寻找这个网页有没有 Markdown 版本的替代,如果有则直接拿这份 Markdown 文档的内容,噪音最少,质量最高。
2. 转换页面
就像上一步所说的,成功访问页面后分两步走:
- 如果是访问到 HTML 页面,会将 HTML 转为 Markdown,转换规则没有公开。
- 如果成功匹配到 Markdown 版本的页面,则跳过转换的步骤,直接拿到 Markdown 内容。
3. 小模型提取内容
用一个小型且快速的模型,基于主模型给的 Prompt(不一定是直接给用户的 query) 对 Markdown 进行理解、信息提取或摘要。
4. 返回结果给主模型
最终将这个小模型的回答返回给主模型,回到上下文窗口中。主模型拿到的内容不一定是网页的原文,而是经过小模型理解和总结后的。
WebFetch 工具总结
简单来说 WebFetch 就是在访问网站的时候,优先找是否有 Markdown 版本的内容,如果有就取用,没有则通过 HTML 转为 Markdown。然后基于一个小模型将内容进行理解、取摘要,把回答返回给主模型。主模型收到这个回答后,再做下一步处理。
所以主模型拿到的信息是有损的,是经过 LLM 理解后的内容/摘要。个人理解这种做法有好有不好:
- 好处是更省 Token、更快,因为主模型不用阅读大段原文了,而且官方文档称这是一个「小型快速模型」;
- 坏处则是幻觉发生概率变大了(但是应该也没有很大),因为只要涉及 AI 理解的任务,就有一定概率会出错或者幻觉。小模型理解页面可能出错,主模型理解小模型的回答又可能出错,误差会累积。
调研 Tavily

前文提到如果目标站点进行了防火墙拦截,WebFetch 工具有一定概率抓取失败。由于这是 Claude 远端的工具,如果用非官方的大模型 API(比如 DeepSeek)接入 Claude Code,WebFetch 也一定会失败。

我用过 WebFetch 的其他替代品,比如比较出名的 Tavily 和 Brave 等。Tavily 可以执行网络搜索、页面内容抓取、站内页面爬取和爬取站点地图。其中 Tavily Extract 同样能实现页面内容的抓取,我日常也有在用。
实际体验下来 Tavily Extract 的效果挺好,我好奇它和 WebFetch 有什么不同,于是做了调研。先说工具流程:

1. HTTP 请求获取页面
Tavily 会发起 HTTP 请求,拿到 HTML 完整内容。
值得注意的是,Tavily 抓取网站的成功率颇高,至少我的个人网站也能被正常抓取(使用 WebFetch 会被 Vercel 拦截)。看起来是做了些处理来避免被识别为机器人而被拦截。
2. 清洗内容
Tavily 会对完整 HTML 文件做一些清理程序,会尝试把导航栏、广告、样式、底部 Footer 链接等多余噪音去掉。只保留有价值的内容部分。
我看过输出日志,大部分噪音都被去掉了,但仍有部分残留,比如导航栏/Footer 的内容、服务端渲染时的代码碎片和一些设置 sr-only 的隐藏文本。
这是我用 Tavily 提取我其中一篇博客文章返回的内容,在 raw_content 可以看到仍然保留了一些导航栏和 Footer 的内容:
{
"results": [
{
"url": "https://lrd.im/blog/2026-08-07",
"title": "DESIGN.md 企业内落地实践与踩坑复盘分享 - 李瑞东 LRD.IM",
"raw_content": "[作品](/ \"作品\")[博客](/blog \"博客\")[关于我](/about \"关于我\")\n\n> ## 文章索引\n>\n> 在这里获取全部文章的索引:</blog/llms.txt>\n>\n> 如需阅读本文的纯文本版本,可访问当前 URL 末尾加 .md 的地址。\n\n# DESIGN.md 企业内落地实践与踩坑复盘分享\n\n李瑞东发布于\n\n## [项目背景](#项目背景)\n\n我当前所处的团队属于设计中台,负责制定和维护集团内部 B 端业务的组件库、规范等,同时也为一些业务线做设计。我们有一套 B 端设计系统
...
🎉 你觉得这篇文章怎样?\n\n是否引起了灵魂深处的共鸣?如果有其他看法,我在这里留下了联系方式。觉得这篇文章还不错,欢迎分享给朋友!我在这里留下了联系方式。如果觉得这篇文章还不错,欢迎分享给朋友!\n\n© 李瑞东 2017-2026\n\n[What’s New](/updates \"更新日志\") · [RSS 订阅](https://lrd.im/feed.xml \"订阅李瑞东的设计博客\") ·",
"images": []
}
],
"failed_results": [],
"response_time": 0.02,
"request_id": "17e9a846-0706-4eff-9118-980a525889b1",
"auth_mode": "keyed"
}具体的清洗过程、规则是黑盒,无法得知。不确定是用 LLM 来清洗还是用脚本,或者两者混合也有可能。
3. 返回结果给主模型
将清洗后的 HTML 转为 Markdown 格式,然后将全文返回给主模型。
直到这一步,WebFetch 与 Tavily Extract 的关键区别是:前者返回的是小模型理解后的内容,而后者返回的是完整的 Markdown。
除非做大量实验,否则很难直接说哪个更好。返回原文可能会增加上下文压力,但速度快;而小模型的介入则能让返回的内容更轻量,但需要处理时间,而且可能会加重幻觉。
以我目前的经验来看,两者在日常工作学习都是好用的。
值得一提的是,Tavily Extract 还支持传入一个参数:query。
4. 可选参数:query 相关性排序

传入 query 参数后会对整篇 Markdown 文章切分 Chunks,然后根据用户的提示词进行相关性排序。最后再把其他不相关的内容都清理掉,将更短,更精准的内容返回给主模型。
下面举一个例子。我分两次让 Tavily 提取我的博客文章 DESIGN.md 企业内落地实践与踩坑复盘分享:
- 第一次是没有指明特定模式,只写上我的意图;
- 第二次则是在写上我的意图的同时,明确指明需要使用 query 参数。
工具返回的结果截然不同。在默认模式下,返回了完整的 HTML 转 Markdown 内容;而带有 query 的结果更精准,把无关的内容舍弃掉,只剩下几段相关的 Markdown。以下是对比:
// Prompt: 阅读 http://lrd.im/blog/2026-08-07,我想知道作者认为 DESIGN.md 有哪些短板
// 输出:
{
"response_time": 2.3,
// 默认模式:全文提取,无截断。共返回 12,285 字符
"results": [{
"raw_content":
"## 文章索引\n\n在这里获取全部文章的索引:</blog/llms.txt>...\n\n# DESIGN.md 企业内落地实践与踩坑复盘分享\n\n\n\n## 项目背景\n\n我当前所处的团队属于设计中台..."
}]
}// Prompt: 使用 query 参数阅读 http://lrd.im/blog/2026-08-07,我想知道作者认为 DESIGN.md 有哪些短板
// 输出:
{
"response_time": 0.21,
// query 模式:按相关性截取,返回多段内容。共返回 2,273 字符
"results": [{
"raw_content":
"但我在测试阶段体验的过程中,也发现了 DESIGN.md 有明显的短板。\n\n#### 组件库场景\n\n对于已经引用了组件库的项目,DESIGN.md 对此束手无策...
[...]
> 我在社区上找了一份与我们公司设计风格相似的 DESIGN.md,以此为骨架,再逐步调整、补充属于自己的内容...
[...]
至于存量应用,则是在编辑页面,点击按钮来唤起一个选择设计系统的浮层。点选任意一个并确定后,直接调用技能的更换设计系统能力..."
}]
}工具调研总结
CURL 的方式最朴素,就是最简单的把网页的 HTML 内容返回给主模型,不做任何加工处理。而 Tavily 和 WebFetch 则是经过改良设计的,都会把 HTML 文档转为 Markdown 格式,然后分别用原文和小模型理解后的内容返回给主模型。
虽然 WebFetch 和 Tavily 的行为有明显区别,但目的都是一样的:把原网页里的噪音过滤掉,返回更精简、更高质量的内容给主模型。
以下是我对比表格,对这三个工具的特点进行对比:
| CURL | WebFetch | Tavily | |
|---|---|---|---|
| 返回内容 | HTML 原文 | 小模型摘要(有损) | 清洗后的 Markdown 全文 |
| 过滤噪音 | 否 | 是 | 是 |
| 抓取成功率 | 大多数情况可用 | 可能被服务器防火墙拦截;非官方 API 不可用 | 较高,做了反机器人处理 |
| 特点 | 可自定义标头,如 text/json | 优先匹配 Markdown 替身 | 可选 query 参数,按相关性排序后仅返回相关片段 |
网页调研

了解了 Claude Code 是怎么「阅读网页」之后,我想继续调研看看网页里做些什么建设,才能让 AI 更好地获取到内容。
我觉得 AI 公司最了解 Agent 是怎么运作的。于是我以 Claude Code 文档为目标进行调研,以下是我的发现。
Markdown Twin
前文提到我在一次访问文档的时候看到有「以 Markdown 格式查看」的功能,点击打开了一个纯文本页面,里面用 Markdown 格式把网页的内容展示出来。

了解到这是一种叫「Markdown Twin」的做法。Markdown 格式的内容对 LLM 比较友好,所以网站的维护者可以提供一份已经清洗过噪音,最干净的 Markdown 文档给 AI 消费。
我再顺藤摸瓜,在 Claude Code 网站的 HTML 里还发现了这一句:

<link rel="alternate" type="text/markdown" href="/docs/en/overview.md">这是一个 <link rel="alternate"> 标签,表示「当前文档的另一个替代版本」,可以是不同格式、不同语言、或面向不同设备的版本。这里的例子中,指向了这篇文档的 Markdown 版本。
前半部分在对 WebFetch 调研时,提到了一句 —— WebFetch 优先用 Markdown 的 Accept 标头来访问网页。此时正好联动起来了!
alternate 使得每一次 Claude Code 使用 WebFetch 在访问该网站,都能直接获取到 Markdown 版本,完全不需要接触到有大量噪音的 HTML 了。
我用 CURL + Markdown 标头的方式尝试访问 Claude Code Docs。结果如下,实测后确实拿到了 Markdown 版本的内容:
<!-- curl -sSL -H "Accept: text/markdown" https://code.claude.com/docs/en/overview -->
> ## Documentation Index
> Fetch the complete documentation index at: https://code.claude.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.
# Overview
> Claude Code is an agentic coding tool that reads your codebase, edits files, runs commands, and integrates with your development tools. Available in your terminal, IDE, desktop app, and browser.
Claude Code is an AI-powered coding assistant that helps you build features, fix bugs, and automate development tasks. It understands your entire codebase and can work across multiple files and tools to get things done.
...这一轮调研下来,我在此断言:如果我们想自己的网站内容被 Agent 更高效率、更高质量地消费,Markdown Twins + alternate 声明是必须做的事情。
这种做法普遍吗?

不止 Claude Code,其他很多文档网站都有做 Markdown Twins,比如 Shopify Dev Docs 和 OpenAI 等。
我还对 Markdown Twin 内容的细节感兴趣,比如是不是会有一些额外的内容、图片视频等媒体元素怎么处理,以及如何表达可交互控件等。于是我打开了这几家产品文档的 .md 版本页面,看看里面的细节。
细节 1:顶部补充信息
我在这三个站点都看到 Markdown 文档的顶部被添加了一些补充信息。其中 Claude Code 文档和 OpenAI 文档放的是 llms.txt 的地址(下文会具体介绍这个);而 Shopify 文档放的则是 Frontmatter 元信息,包括标题、文档简介、文档链接。

细节 2:补齐图片 ALT 文本
在 Markdown 语法里,图片只是一个 URL,没有任何语义,机器也不知道图片里描绘的是什么。为了让 AI 能理解图片内容,就像 Shopify 文档那样,会为每张图都添加 ALT 文本。
![Shopify 文档 Markdown 源码截图,高亮行 ![Screenshot showing a simple app...] 展示了为图片添加详细 Alt 文本描述的做法。](https://lrdim.oss-accelerate.aliyuncs.com/blogimg/2026-08-21/18.png)
注:发现 Shopify 会把视频素材整个清洗掉,不出现在 Markdown Twin 里。
细节 3:自定义组件
因为我的个人网站里的博客就是用增强版 Markdown 来写的,所以我知道自定义组件是可以通过 JSX 格式嵌入在 Markdown 文档内。
除了以完整保留 JSX 代码的方式,我们在输出 Markdown Twin 时,可以写一些规则来改变自定义组件的输出格式,比如把不关键的参数部分清洗掉,或甚至在转换时直接把组件内容转为真实的 Markdown 格式。
比如 Claude Code 文档内不时会出现一些补充信息、提示等,这些内容在 Markdown 里被不同语义的标签包裹:<Tip> <Note> 和 <Warning> 等:

而 Shopify 文档的做法则完全不同,把代码清洗为扁平的 Markdown 语法格式。以 Info 和 Tabs 组件为例,Info 组件用了加粗文本 ** 来实现,而 Tabs 则是用四级标题 ####。

反观 OpenAI 文档,组件代码不做任何处理,文档里也充斥着大量的 <div>、className 这种无实质意义的内容,实在是有些粗糙。

我更倾向 Claude 的做法:既能完整保留组件语义,文档也不会增加过多噪音。但缺点是复制到外部 Markdown 预览器时会显示异常,因为那些预览器没有组件相关代码;Shopify 的做法则相反,所有预览器都能正常展示,代价是丢失组件本身的语义。
细节 4:底部补充信息
最后看到文档末尾,Claude Code 文档和 Shopify 文档都以列表的方式展示了相关文档的链接。

而 OpenAI 把整段原始代码暴露出来,格式也没处理好,可读性很差。

llms.txt
在 Claude Code 文档和 OpenAI 文档的原始 HTML 里,有一段被设置为不可见的 DOM 里面提到了 llms.txt 的路径。里面有所有文档的索引,提示 AI 在继续深入探索之前可以先读一下这份索引。
打开这份 llms.txt,出现的同样是用 Markdown 格式写的纯文本。里面的内容很简单,列出了其他文档、资源的索引。

llms.txt 扮演了目录的角色。让 AI 在查阅文档时能够有一份高质量的 Markdown 文档目录索引。
但是谷歌的技术大牛提到,目前没有任何 AI 工具声称他们在使用 llms.txt。
我也试过几次在 Claude Code 里询问一些文档里的内容,并没有发现 llms.txt 被消费的记录。实际上 Agent 自身就有爬取相关网页的能力。但我觉得做这件事没有什么成本,可以加入这次个人网站 AI 友好型改造的任务中。
robots.txt
我在让 Agent 了解 Claude Code 文档时,发现他们在 robots.txt 也做了些处理:
User-agent: *
Content-Signal: ai-train=yes, search=yes, ai-input=yes
Disallow: /cdn-cgi/
Allow: /_next/image
Disallow: /_next/
Sitemap: https://code.claude.com/docs/sitemap.xml除了像普通的 robots.txt 文件一样声明了禁止爬虫抓取的范围以外,还多写了一个字段 Content-Signal。
这是 Cloudflare 提出的非标准扩展字段。
- ai-train — 是否允许用于训练/微调 AI 模型;
- ai-input — 是否允许作为 AI 推理时的输入(如 RAG、AI 摘要);
- search — 是否允许用于搜索索引和搜索结果展示。
不过目前这几个字段不属于 robots.txt 的正式标准,所以各家 AI/爬虫未必会遵守这个约定。
调研总结
基于对 CURL、WebFetch、Tavily 工具的认知,以及 Claude Code、Shopify、OpenAI 三家 AI 友好型文档建设的调研,总结三个待办:
- 为每个页面做 Markdown Twin。并且:
- 处理好图片的 ALT 文本
- 处理好自定义组件转为 Markdown 格式的转换规则;
- 补充
alternate标签指向 Twin 文件。
- 做一份 llms.txt 文件,隐秘地嵌入到网站的 HTML 里的恰当位置。
- 补充网站的 robots.txt。
开始改造
Markdown Twin 改造
1. 处理 ALT 文本
得益于 3 年前在对个人网站进行改版时的高瞻远瞩,目前我的博客文章就是以 MDX 的格式编写,后续的可扩展性非常强。
对于这次 Markdown Twin 改造,我需要先把所有文章里的图片 ALT 文本进行补全。具体方案:

- 给 LLM 提供文章上下文:博客标题、描述、图片所在章节名(H2 和 H3),以及图片前后各 300 字的上下文;
- 固定提示词模板,按图片类型(UI 截图/示意图/数据图表/对比图)侧重描述内容与结论;
- 使用支持视觉识别的模型(这里我用的 Sonnet 4.6),生成后人工进行验收。
脚本输出结果后会帮我回填到每一篇博客文章里,我要做的就是看看 ALT 文本有没有要修改的。少量修改我人工调整即可,大量的修改我会直接让 AI 重新生成。
2. 自定义组件的清洗逻辑

前文提到,Claude Code 文档和 Shopify Dev Docs 在处理自定义组件转为 Markdown 格式时的做法截然不同。前者是保留了很轻量的原始 JSX 标签,后者则是将内容完全转为 Markdown 格式。
我个人是略微倾向于前者。一方面是因为 JSX 标签也能承载一定的语义含义,比如 Info 信息区块、Tabs 和视频组件这些都是有意义的。这种精简过的标签并不会带来上下文压力和噪音,反而让内容更丰富。
另一方面则是更长远的打算——如果后续我的网站要做 Chat 对话助手。保留原始的标签则让我保留了输出可交互控件的空间。
这里举一个简单的例子,我的博客里有时候会插入视频。因为 Markdown 原生不支持视频素材,所以我插入的也是 JSX 标签。原始代码:
<figure>
<video
controls
width="100%"
muted
alt="翻译应用操作录屏。在打开选择语言的下拉面板后拖拽浏览器,下拉面板会跟随浏览器边缘移动而不超出视窗范围。体现了完美的适配设计效果。" >
<source
type="video/mp4"
src="https://..."
/>
</video>
</figure>这里有大量的噪音,比如 HTML 标签、视频播放器属性等等,让 AI 写一个清洗规则把多余的剥离掉,最终只剩下最核心的内容:
<video
alt="翻译应用操作录屏。在打开选择语言的下拉面板后拖拽浏览器,下拉面板会跟随浏览器边缘移动而不超出视窗范围。体现了完美的适配设计效果。"
src="https://...">3. 制作 Markdown Twin 脚本
把博客原文 Markdown 的清洗规则设计好之后,就可以让 AI 帮我做一个静态生成脚本,将 JSX/MDX 源文件转换成 Markdown,然后存在 /public 里,最终在网站链接后加上后缀 .md 就能访问。
除了博客文章,其他页面的 Markdown 都是按我提供的规则从原始 JSX 结构化提取各段文本生成的,所以后续只要我还是在当前结构框架内做修改,每次 build 时都会同步更新。
4. 补充 alternate 标签
最后,我也在所有有 Markdown Twin 的页面里加上 alternate 标签,指向当前页面的 .md 链接。
<link rel="alternate" type="text/markdown" href={`/blog/${slug}.md`} />5. 验证效果
验证时有一个小插曲。我用 curl -H "Accept: text/markdown" https://lrd.im/blog/2026-08-07 来验证在添加 text/markdown 标头后是否能获取到 Markdown Twin 时,发现返回的仍然是原始的 HTML。
解决方法是参考了 Vercel 的官方文章,在 next.config.js 里将对应的页面进行重路由即可。

llms.txt 改造
对于 llms.txt,前文提到其实现在没有什么 AI 工具是明确消费这份文件的,所以我也没有做得太复杂。
我目前的做法是纯博客文章索引:最上方是博客标题 + 简介,然后用无序列表展示所有文章的标题、链接(指向 Markdown Twin)以及文章描述。
# 李瑞东的设计博客
> 李瑞东的个人设计博客,记录设计与技术的实践与思考。内容涵盖六个方向:产品与交互设计经验、网页字体与排版知识、设计工具与软件技巧、优质译文、分享会文稿,以及设计之外的个人随笔。
## 文章
- [AI-Native 之路 —— 从 AI 友好型网站改造开始](https://lrd.im/blog/2026-08-21.md): AI 改变了搜索范式,我从探索 Agent 阅读网页的方式开始,逐步让自己网站里的内容对 AI 更加开放、友好。
- [DESIGN.md 企业内落地实践与踩坑复盘分享](https://lrd.im/blog/2026-08-07.md): 随着 Agent 时代来临,公司内涌现了新的合作模式,也产生了新的痛点。本文将分享如何使用 DESIGN.md 和 SKILL 让设计系统在 AI 时代发挥影响力。
...robots + Sitemap 改造
最后,我也让 AI 帮我做了 robots.txt 和 sitemap.xml。
其中 robots.txt 我是声明禁止 AI 训练我的内容。因为我还是想 AI 是通过阅读我网页内容来获取知识,而不是将其放进语料库内。
而 Sitemap.xml,也是很简单一个脚本就能生成。有趣的是:最开始 AI 给我出方案时提到要放 loc, lastmod, changefreq, priority 这几个字段。然后我追问一句:在谷歌 SEO 里,这几个字段都是有用的吗?
然后 AI 立刻回了一句:只有 loc 是有用的,lastmod 只在准确时有用,而另外两个 Google 已经明确表示已经忽略了。
在参考了 Claude Code 文档的 Sitemap 做法之后,综合了一下最终决定精简做法,只放 loc 和 lastmod。
总结
经过这次 AI 友好型改造,我的个人网站也有 Markdown Twin 和 llms.txt 了,同时也把以前一些 SEO 待办事项也都一一完结,比如 Sitemap, robots.txt 和 JSON-LD。
这里把关键结果的 URL 列一下:
- 首页.md
- 博客目录 llms.txt
- 博客文章.md(任意文章页 URL 后加
.md即可访问) - 简历.md
- Sitemap.xml
有意思的是,整个过程收获最大的不是在为自己的网站做优化的阶段——这些 AI 都能做 —— 反而是在调研 WebFetch 和 Tavily 这两个工具,以及调研 Claude、Shopify、OpenAI 文档的 AI 友好建设的时候。
在这之前 Agent 做很多事情是黑盒,我很难预测他的下一步是什么,也不清楚触发这一步的原因是什么。但是经过这次的探索,我觉得学习 Agent 运行机制、工具行为是一件挺有趣的事情,我爱上了从成熟的 Agent 中了解 AI 的能力边界以及学习软件设计哲学。
参考资料
- How to serve documentation for agents - Vercel Knowledge Base
- Making My Portfolio Agent-Readable: From Files to an Interface Agents Can Act On Cameron Rye