我们如何让 construct.computer 对 AI 代理可读

Construct Computer Blog 2026-11-02T15:54:59.018008

Ankush

Ankush · 作者

撰写关于 Construct、AI 代理和开发者工具的内容。

发布于 2026 年 10 月 30 日 · @ankushKun_

Construct 的代理以阅读网页为生,所以我们把自己的网站做成了它们能轻松读懂的样子。

以 $9 开始使用 Construct →

或免费试用 Pro 7 天,之后每月 $299

  1. 代理抓取页面时得到的是什么

  2. 每个页面的 Markdown 双胞胎

  3. 请求 Markdown 与 406 响应

  4. 用响应头标明哪份是哪个版本

  5. Cloudflare 的 Markdown for Agents,以及它丢掉了什么

  6. robots.txt、内容信号与具名爬虫

  7. llms.txt 和 llms-full.txt

  8. 代理可以免费调用的工具

  9. 每次部署都触发 IndexNow

  10. 这套方案做不到什么

  11. 如果你想照做

  12. 我们是怎么验证的

要让一个网站对 AI 代理可读,做法是:给每个页面准备一份干净的 Markdown 版本,放在可预测的 URL 上;在 HTML 中用 Link 响应头指向它;允许客户端通过 Accept: text/markdown 请求头索取;同时确保文字在任何 JavaScript 执行之前就已经存在于 HTML 中。这就是我们于 2026 年 10 月 26 日在 construct.computer 上线的方案,同时还提供了 llms.txt、写明 AI 爬虫名称的 robots.txt,以及无需密钥即可调用的 JSON API 免费工具。

效果体现在代理实际收到的内容上。我们的定价页 HTML 有 81,770 字节,Markdown 版本只有 4,733 字节。这一切不会改变我们在 Google 上的排名——Google 明确表示,AI Overviews 不需要特殊文件或 Markdown。它改变的是代理抓取我们页面时读到的内容,而这正是我们能掌控的部分。

我们打造了 Construct,一个 AI 智能体平台,其智能体每天大部分时间都在读取其他人的网站。这篇文章是工程层面的复盘:包括代码、生产环境返回的响应头、首次尝试未奏效的部分,以及证据支持与不支持的结论。

智能体抓取页面时得到的是什么

智能体读取网页有两条路径。浏览器型智能体渲染页面,读取屏幕内容或无障碍树。抓取型智能体(ChatGPT、Claude 或 Perplexity 替用户查询信息,或者爬虫建立索引)则下载 HTML,从文本中提取信息。Vercel 在 2024 年 12 月对主流 AI 爬虫做了调查,发现“目前主流 AI 爬虫均不渲染 JavaScript”,GPTBot、ClaudeBot 和 PerplexityBot 也不例外(Vercel)。

在我们采取任何巧妙措施之前,这个问题就已经给我们造成了麻烦。首页 FAQ 使用了 Radix 手风琴组件,只有展开时才会渲染答案。在预渲染的 HTML 中,六个问题都在,但六个答案都不在,于是每个跳过 JavaScript 的爬虫看到的都是有问无答。修复方案(commit 9a66a2c)是在服务端渲染时将所有答案以折叠状态挂载,页面的外观和行为保持不变。

第二个问题是体积。现代营销页面主要是标记、脚本和内联样式。以下是同一内容在三种形式下的对比,测量于 10 月 26 日:

四个 construct.computer 页面以 HTML、我们的 Markdown 孪生版本以及 Cloudflare 边缘转换形式呈现的未压缩体积,2026-10-26,可按列排序

页面 HTML 我们的 Markdown 孪生版本 孪生版本的 X-Markdown-Tokens Cloudflare 转换
/(首页) 269,870 字节 11,258 字节 2,812 (我们自行提供)
/pricing/ 81,770 字节 4,733 字节 1,183 12,518 字节
/use-cases/memory/ 35,092 字节 3,460 字节 865 5,710 字节
/tools/zapier-cost-calculator/ 60,073 字节 7,878 字节 1,970 13,781 字节

最后一列是智能体目前在除首页外的任何页面上发送 Accept: text/markdown 到页面 URL 时得到的内容。它比我们的孪生版本更大,而且正如我们在下文解释的,有时会缺失标题。

每个页面都有一份 Markdown 副本

sitemap 里的每个页面(10 月 26 日时共 92 个),现在都在原路径后加 .md 得到一份 Markdown 副本:/pricing/ 对应 /pricing.md,首页对应 /index.md。这个约定来自 llms.txt 提案,它建议网站「在原页面相同的 URL 上提供一份干净的 Markdown 版本」,只在末尾加上 .md(llmstxt.org);Dualmark 发布的 AEO Spec 也采用了同样的做法(Dualmark)。

副本在构建时从预渲染的 HTML 生成,脚本是 scripts/markdown-twins.mjs,因此副本不会和页面本身脱节。脚本读取 sitemap,用 rehype 解析每个页面,只保留 <main>,再用 remark 转换。哪些内容保留,判断标准是「桌面布局下屏幕阅读器会念出来的东西」:

  1. 脚本、样式、SVG、表单、对话框、视频和页面内的 <nav>,一律去掉。

  2. 带 aria-hidden、inert 的元素,以及没有 alt 文本的图片,去掉。

  3. Tailwind 在桌面端隐藏的元素(md:hidden 之类),去掉,这样同一块内容的移动端副本不会被重复收录。

  4. 默认折叠起来的内容保留,因为 FAQ 的答案仍然是页面正文的一部分。

  5. 图标链接替换成它的 aria-label,所有相对 URL 改成绝对 URL。

  6. 相邻之间没有空白的 flex 子元素要补一个空格,否则「Monthly」和「Annual」会连成「MonthlyAnnual」。

每份副本开头都有一小段 front matter,包含 title、description 和 url。品牌页是唯一的例外:它的副本 /brand.md 由代码里的品牌数据直接写出,因此每个文件和用法规则都在里面。

一开始有个地方漏了过去。首页副本里重复出现了一个只在手机上显示的 Product Hunt 徽章,因为它是靠一个普通 CSS class 隐藏的,而我们的规则只认 Tailwind 的写法。现在这个徽章也带上了 md:hidden,用转换器看得懂的方式表达了同样的意思。

请求 Markdown,以及 406 状态码

一个已知 URL 上的孪生副本,能帮到那些了解这一约定的智能体。内容协商则能帮到不了解的客户端:客户端发送 Accept 头,服务器据此挑选返回格式。在 Cloudflare Pages 上,这段逻辑运行在 functions/_middleware.ts 中。它会读取 Accept 里 text/markdown 和 text/html 的 q 值(权重值),当客户端更偏好 Markdown 时就返回 Markdown;当两者权重相同(即一个裸的 */*)时,只对六个“为答案引擎读页面”的用户代理返回 Markdown:GPTBot、ChatGPT-User、ClaudeBot、Claude-User、Perplexity-User 和 CCBot。搜索爬虫则被有意留在 HTML 上,因为它们的结果依赖页面里的标题、卡片和结构化数据。用户代理只做匹配,从不存储。

下面是生产环境的实测,只保留了关键的响应头:

$ curl -sI https://construct.computer/ -H 'Accept: text/markdown'
HTTP/2 200
content-type: text/markdown; charset=utf-8
vary: Accept, User-Agent
x-markdown-tokens: 2812
x-aeo-version: 1.0

$ curl -sI https://construct.computer/ -H 'Accept: */*' -A 'Mozilla/5.0 GPTBot/1.0'
HTTP/2 200
content-type: text/markdown; charset=utf-8

$ curl -sI https://construct.computer/ -H 'Accept: */*' \
    -A 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)'
HTTP/2 200
content-type: text/html; charset=utf-8
vary: Accept

$ curl -si https://construct.computer/ -H 'Accept: image/png'
HTTP/2 406
content-type: text/plain; charset=utf-8
link: </index.md>; rel="alternate"; type="text/markdown"

This page is available as text/html or text/markdown.

406 是后来才加的(提交 ab3b1e8)。HTTP 规范定义它用于这样一种请求:客户端在 Accept 里排除了服务器能提供的所有形式(RFC 9110),AEO 规范也要求这么做。中间件只对存在孪生副本的 URL 返回 406,所以同一中间件背后的 JSON API 仍能正常响应请求 JSON 的客户端;浏览器、通配符,或者不带 Accept 头的请求,照样能拿到页面。

大部分工作都在 public/_headers 里,而且大部分都是为了不让同一页面的两个版本互相竞争:

Cloudflare Pages 有几个习惯,我们是踩了坑才摸清的。两个 _headers 块匹配同一路径时,会互相替换,而不是合并。带 ! 的 Header 行只能删掉它上方规则设置的头。占位符可以用在头部的值里,所以 /:page/ 可以发送 </:page.md>。文件最多支持 100 条规则。另外,Function 里的 next(new Request(url)) 会跟随重定向,除非传入 redirect: "manual"。我们旧版 /brand/brand.md 的 301 就是这么坏的,直到加上这个参数才修好。

Cloudflare 的 Markdown for Agents,以及它会丢掉什么

只要客户端发送 Accept: text/markdown,Cloudflare 就会在边缘把 HTML 转成 Markdown。这个功能在 Pro、Business 和 Enterprise 套餐的域名上可用,我们的站点也开了(Cloudflare 文档,2026 年 10 月 9 日更新)。这就是为什么每个页面都能协商返回 Markdown,而不只是首页:只有 /、/api/* 和 .md 文件会走我们的 Function,其余都是静态文件,由 Cloudflare 在响应时转换。

文档说非内容元素“比如页头、页脚、导航、脚本和样式”会被删掉。问题在于,它连 <main> 里面的 <header> 也一起删,而我们的用例页和工具页把主视觉区放在 <header> 里。下面是我们改掉之前,/use-cases/memory/ 两种形式的开头几行:

Our twin, /use-cases/memory.md:

Memory

# Chat history is not memory you can audit

A long context window is not a memory system. It is a pile. ...

Cloudflare's conversion of /use-cases/memory/:

---
  description: Preferences, decisions, and project context stay with ...
  title: Chat history is not memory you can audit
  image: https://construct.computer/og/use-cases-memory.jpg
  ---

## The cost of leaving it unencoded

边缘版本里,H1、开头段落和行动号召(CTA)全都不见了。在 /pricing/ 页面上,它保留了 32 层没有 alt 文本的图片、团队插画里三个吉祥物被拆散的碎片,还把相邻的标签粘成了“MonthlyAnnual4 months free”。我们的孪生版本没有这两个问题,而且每个页面都链向它;但智能体如果在非首页的页面上做协商,拿到的仍是 Cloudflare 那份副本。让每个页面都走我们的 Function 能解决这个问题,代价是每次页面浏览都跑一次 Function;把 hero 区块移出 <header> 则能免费解决——我们先在 /brand/first 上这么改,之后又推广到用例页和工具页。在 Dualmark 的评分器上,首页拿到 125/125,其他页面是 110,因为只有首页会按 user agent 做协商。

robots.txt、内容信号与具名爬虫

我们的 robots.txt 由 app/lib/generated-content.ts 生成。除了投资人 deck(融资演示文稿),其余一律放行;同时给十个爬虫各开一个独立分组:GPTBot、OAI-SearchBot、ChatGPT-User、ClaudeBot、Claude-User、PerplexityBot、Google-Extended、Applebot-Extended、Bingbot 和 CCBot。爬虫一旦匹配到具名分组,就会完全忽略 * 分组,所以每个分组都得重复同样的行,包括内容信号:

User-agent: GPTBot
Content-Signal: search=yes, ai-input=yes, ai-train=yes
Disallow: /deck
Allow: /

内容信号是 Cloudflare 给 robots.txt 加的扩展,共三个:search、ai-input(用页面内容为依据来支撑回答)和 ai-train。我们对三项全部同意,并把上面的政策原文一字不差地照抄——因为改写过措辞的副本就等于另一份许可(Cloudflare 的说法)。省略某个信号并不等于授予,所以我们逐条写明。理由很简单:对我们这种规模的公司来说,更大的风险是模型压根没听说过我们。

另有两点需要注意。内容信号和 robots 规则都只是请求,爬虫完全可以无视。另外,OpenAI 的文档说明 ChatGPT-User 是代表用户行事,所以“robots.txt 规则可能并不适用”于它;而 GPTBot 负责收集训练数据,OAI-SearchBot 负责构建 ChatGPT 的搜索结果(OpenAI)。屏蔽三者中的一个,并不会顺带屏蔽另外两个。

llms.txt 和 llms-full.txt

/llms.txt 是索引:一段摘要、免费工具及其 API 端点、一个品牌区(含命名规则和一行价格说明),以及每个页面及其描述。文件大小 24,696 字节。/llms-full.txt 则把每篇博客、FAQ 和工具参考的全文都塞进一个 958,888 字节的文件,方便想要一次性抓取全部内容的模型使用。两者都由与网站相同的内容生成,因此不会过期。

价格那行是有意为之。我们查过的软件目录站把我们的年付价格(每月 39 美元和 199 美元)当成了月付价格展示,而模型会照搬目录里的说法。一行“每月 9 美元起;7.50、39 和 199 美元为年付折算月价”,就能让模型引用正确的数字。

对 llms.txt 的作用要心里有数。Google 的生成式 AI 搜索指南明确说它忽略这类文件:创建 llms.txt“既不会损害也不会帮助你的网站在 Google 搜索中的可见度或排名,因为 Google 搜索会忽略它们”(Google Search Central,2026 年 7 月 10 日更新)。SE Ranking 对 216,524 个页面的研究发现,llms.txt 与 ChatGPT 引用之间的关系微乎其微(Search Engine Journal)。我们保留它,是因为生成它零成本,而且已经来到站内的 Agent 可以拿它当地图用。

可供 Agent 调用的免费工具

我们发布了三个免费工具:Zapier 成本计算器、Gmail 过滤器生成器和 MCP 配置生成器。每个工具同时也是一个 JSON 端点,无需密钥、无需账号。GET https://construct.computer/api/tools 会列出所有工具,对单个工具发 GET 会返回其输入 schema 和示例,发 POST 则执行:

$ curl -s -X POST https://construct.computer/api/tools/zapier-cost-calculator \
    -H 'Content-Type: application/json' \
    -d '{"zaps":[{"name":"Lead to CRM","action_steps":3,"runs_per_month":400}]}'
{
  "monthly_tasks": 1200,
  "cheapest": { "plan": "Professional", "total_usd_per_month": 35.02, ... },
  "covering_plan": { "plan": "Professional", "total_usd_per_month": 39, ... },
  "prices_checked": "2026-10-11",
  "source": "https://zapier.com/pricing"
}

这些报错信息同样是写给 AI agent 看的。比如把 action_steps 写成 steps,返回的 400 会逐条点名每个写错的路径,并提示「Fetch the endpoint without input for the schema」,这通常足以让模型改对调用并重试。首页用 Link 头把这些入口都挂了出来:一份 API 目录(RFC 9727)、一份 OpenAPI 文档,还有 llms.txt。

在浏览器里,同一批工具改由 WebMCP 注册——这是让页面把工具暴露给 agent 的一项提案标准(来源:Chrome for Developers)。我们的代码会检查 document.modelContext 和更早的 navigator.modelContext,只在浏览器支持时才下载工具代码,所以普通访客不会多加载任何东西。它跑在 Chrome 的 origin trial 下,从 Chrome 149 开始。

每次部署都调用 IndexNow

先搜索再抓取的 agent,是通过某个索引找到页面的;新页面在那份索引爬取之前等于不存在。每次部署之后,scripts/indexnow.mjs 会对比线上 sitemap 和新 sitemap,把 lastmod 有变化的 URL 提交给 IndexNow;后者会让提交同步到 Bing、Naver、Seznam、Yandex 和 Yep(来源:IndexNow)。Google 不接受 IndexNow,它只读 sitemap。Bing 表示很多大语言模型(LLM)以 Bing 索引为依据来作答(来源:Bing Webmaster Blog),所以给 Bing 发个 ping 值这十行代码。我们没法用 Cloudflare 的 Crawler Hints 做这件事:它只在缓存未命中时才上报 URL,而 Pages 把我们的 HTML 当作动态内容返回,所以它永远看不到。

Try Construct→

这套方案做不到的事

以上任何一项都不会提升排名。Google 关于 AI Overviews 和 AI Mode 的文档明确说「你不需要为出现在这些功能里而创建新的机器可读文件、AI 文本文件或标记」,并且「没有额外要求」(来源:Google Search Central,2025 年 12 月 10 日更新)。它更新的指南把 Markdown 也加进了这个列表。这里每一个页面,最终还是要靠它讲的内容来赢得自己的位置。

双标记满分只说明我们的响应头符合规范,并不代表ChatGPT会引用我们。九月份我们向六个AI引擎提了60个买家问题,结果被引用最多的是厂商博客、榜单文、YouTube和Reddit。好读只是及格线,值得被引用是另一回事。

我们也说不清有多少智能体会去抓取这些双胞胎页面。按政策,分析数据和日志里都不存用户代理字符串,中间件匹配完就丢弃。

这项工作的实际收益更窄,但很实在。智能体抓取construct.computer的页面时,拿到的是文字、价格和FAQ答案,几千个token,而不是四分之一兆字节的标记;想用我们工具的智能体可以直接调用,不用去爬表单。

想照着做的话

  1. 用不带JavaScript的curl抓取你的页面。先把原始HTML里缺的东西修好;在这之前别的都不重要。

  2. 构建时从渲染后的HTML生成每个页面的Markdown版本,放在/path.md,标记为noindex,并加上指回原页面的canonical链接。

  3. 每个页面都发送Link: </path.md>; rel="alternate"; type="text/markdown"和Vary: Accept。

  4. 如果用的是Cloudflare,打开Markdown for Agents,然后看看它把你的页面处理成什么样。把<main>里<header>元素中的标题移出来。

  5. 在robots.txt里点名AI爬虫,每个分组里重复你的规则,认真决定内容信号。

  6. 把llms.txt当地图发布,别指望它能改变排名。

  7. 如果你有智能体能用的东西,给它一个端点,带schema和能说明如何修正调用的错误信息。

我们是怎么核对的

我们自己网站的所有内容都来自仓库里的代码(提交f67d8c7、ab3b1e8和9a66a2c),以及2026年10月26日对construct.computer发起的请求;大小是curl收到的未压缩字节数。评分来自同一天Dualmark的演练场。Google的说法读自2026年10月26日的Search Central,Cloudflare的读自其开发者文档(2026年10月9日更新),OpenAI的读自同日的爬虫文档。

关于产品本身背后的基础设施,可以看我们为什么在 Cloudflare 上运行 AI 代理,而不是用虚拟机。

常见问题

试用 Construct →

  1. 所有代理都分到了电脑,但我们几乎不花钱
    文章 · 2026年7月31日 · Ankush
    Construct 是一个 AI 员工,为每位用户配一台真实的 Linux 电脑。先别激动——并不是所有电脑都由我们付费。我们是怎么用 Cloudflare Durable Objects、沙箱和 R2 把这件事跑起来的。
    工程 · cloudflare · durable-objects · 基础设施

  2. 代理可靠性计算器:你的代理能跑完多少步?
    文章 · 2026年10月15日 · Ankush
    AI 代理可靠性计算器:一个可靠性 95% 的代理,单次跑完 48 步任务的概率只有 8.5%。看看重试、半衰期和检查点能解决什么。
    ai-agent · 可靠性 · 工作流自动化 · 工程

  3. AI 代理提示词注入:2026 年的攻击都做了什么,怎么阻止一封恶意邮件操控你的代理
    文章 · 2026年11月2日 · Ankush
    提示词注入,就是 AI 代理读到一段文字然后照着执行。2026 年 8 月到 10 月的攻击做了什么,为什么过滤器挡不住,以及 Construct 是怎么应对的。
    ai-agent · 邮件 · 治理 · 可靠性

  4. Construct Computer 安全吗?它存了什么、代理能碰到什么、防护在哪里止步
    文章 · 2026年10月28日 · Ankush
    只要你把范围划清楚,Construct Computer 对小团队来说足够安全:存储的密钥是加密的,陌生人的邮件不会触发任何操作,所有已知缺口都列在这里。
    治理 · 产品 · AI 员工

  5. 工程

  6. cloudflare
  7. durable-objects
  8. 基础设施
  9. ai-agent
  10. 可靠性
  11. 工作流自动化
  12. 工程
  13. ai-agent
  14. 邮件
  15. 治理
  16. 可靠性
  17. 治理
  18. 产品
  19. AI 员工

查看原文