Skip to content

geo: 加 /llms.txt,AI 爬虫按用途分开放行 - #395

Merged
longsizhuo merged 2 commits into
mainfrom
geo/llms-txt-and-ai-robots
Aug 3, 2026
Merged

geo: 加 /llms.txt,AI 爬虫按用途分开放行#395
longsizhuo merged 2 commits into
mainfrom
geo/llms-txt-and-ai-robots

Conversation

@longsizhuo

Copy link
Copy Markdown
Member

背景

线上实测(2026-08)发现两件事:

  1. /llms.txt 不存在(404)。AI 引擎抓站时先看这个文件拿全站索引,没有就只能靠 sitemap 逐个页面抓,才知道每篇讲什么。
  2. public/robots.txt 早就不生效,而且和线上行为完全相反。 App Router 下 app/robots.ts 才是实际服务 /robots.txt 的那份,public/robots.txt 被静默遮蔽。前者对所有 AI 爬虫全放行,后者写着 "Block AI scrapers" 并屏蔽 GPTBot / ClaudeBot / Google-Extended / CCBot。读代码的人会以为 AI 爬虫已经挡住了,实际一个都没挡。

改动

robots:按用途分开,不再一刀切

类型 举例 处理
引用型 OAI-SearchBot、ChatGPT-User、Claude-User、PerplexityBot 放行(走 * 组)
训练型 GPTBot、ClaudeBot、Google-Extended、CCBot、Bytespider、Applebot-Extended、meta-externalagent 等 12 个 整站 Disallow: /

引用型实时取用并在回答里附出处链接,训练型只收语料不给回链。内容是 CC BY-NC-SA,这个分法既拿得到 AI 引用,又守住不进训练集的原意。

刻意不给引用型开 UA 专属组:robots.txt 里专属组是整体覆盖 * 而不是叠加,开了就得把 admin / editor / settings 那串 disallow 再抄一遍,抄漏一条等于把后台放给它们。少写一组反而更安全,这点在 app/robots.ts 的注释里写清楚了。

顺手删掉失效的 public/robots.txt

/llms.txt

app/llms.txt/route.tsforce-static 构建期产出一次(○ (Static),和 /robots.txt/sitemap.xml 同级)。从 source 现枚举,和 sitemap 同源,不会过期。

309 条(中英各半),只做索引不做全文 —— 全站正文拼进去是几 MB,反而挤爆它本来要省的上下文。头部声明了 CC BY-NC-SA,向引用方讲清署名要求。

顺带:抽出 lib/doc-entry.ts

draft 过滤和 slug 编码原本是 app/sitemap.ts 的私有函数,llms.txt 要枚举同一批文档。各写一份的话早晚出现「sitemap 过滤了草稿、llms.txt 没过滤」,草稿泄漏给 AI 引擎和泄漏给搜索引擎一样糟。理由同 lib/site-url.ts 文件头。

测试

tests/robots.test.ts 守的是策略不变量而不是实现:训练型必须被挡、引用型必须不在名单里。两边错任何一边都不会报错 —— 要么内容白送进训练集,要么再也拿不到 AI 引用,只能靠测试拦。

tests/llms-txt.test.ts 覆盖分组、绝对链接、超长描述截断、标题方括号(会破坏 markdown 链接)、以及 docPathname 的中文 slug 编码。

  • 84/84 测试通过,tsc --noEmit 干净
  • pnpm build 通过,/llms.txt 预渲染为静态,绝对 URL 是 https://involutionhell.com

验证方式

pnpm build && head -20 .next/server/app/llms.txt.body
curl -s http://localhost:3000/robots.txt

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings August 2, 2026 20:06
@vercel

vercel Bot commented Aug 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
involutionhell-github-io Ready Ready Preview Aug 2, 2026 8:44pm
website-preview Ready Ready Preview Aug 2, 2026 8:44pm

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a statically generated /llms.txt index for AI engines and updates /robots.txt generation to distinguish “training” crawlers (blocked site-wide) from “citation” crawlers (allowed via the * group). It also factors shared doc filtering + slug encoding into a reusable helper to keep sitemap and llms enumeration consistent.

Changes:

  • Add app/llms.txt/route.ts and lib/llms-txt.ts to generate a sectioned, absolute-link llms.txt from the existing doc source.
  • Update app/robots.ts to block a curated list of training crawlers while keeping citation crawlers allowed via the wildcard rule set; remove the ineffective public/robots.txt.
  • Extract shared isDraftOrHidden and docPathname into lib/doc-entry.ts and update sitemap to use it; add Vitest coverage for robots + llms behaviors.

Reviewed changes

Copilot reviewed 8 out of 9 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tests/robots.test.ts Adds policy-level tests for robots rules (training blocked, citation not blocked).
tests/llms-txt.test.ts Adds tests for llms.txt grouping/rendering and shared doc-entry helpers.
public/robots.txt Removes shadowed/ineffective static robots file.
lib/llms-txt.ts Implements pure llms.txt renderer (grouping, sanitization, truncation, absolute URLs).
lib/doc-entry.ts Centralizes shared draft/hidden filtering and slug→pathname encoding.
generated/doc-contributors.json Regenerates metadata timestamp.
app/sitemap.ts Switches sitemap doc entry generation to shared doc-entry helpers.
app/robots.ts Implements updated crawler policy and consolidates disallow paths/constants.
app/llms.txt/route.ts Adds force-static route handler to enumerate docs and return llms.txt content.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tests/robots.test.ts
Comment thread app/robots.ts Outdated
Comment on lines +33 to +36
* 这份策略取代了 public/robots.txt —— 那份手写文件把两类一起屏蔽,而且
* 早就不生效了:App Router 下 app/robots.ts 才是线上实际服务的 /robots.txt,
* 那份文件被静默遮蔽,读代码的人却会以为 AI 爬虫已经被挡住了。已删除。
*
Comment thread app/llms.txt/route.ts Outdated
Comment on lines +11 to +13
* force-static:内容全部来自构建期的 MDX,没有请求相关的东西,
* 构建时产出一次即可,别让它变成每次请求现算的 dynamic 路由
* (i18n/routing.ts 文件头记着上次全站 dynamic 把 Vercel CPU 打爆的事)。
Comment thread lib/doc-entry.ts Outdated
Comment on lines +8 to +15
* 原来这两个函数是 `app/sitemap.ts` 的私有函数。`app/llms.txt/route.ts`
* 要枚举同一批文档,如果各写一份,早晚会出现「sitemap 过滤了 draft、
* llms.txt 没过滤」这种单边漂移 —— 草稿泄漏给 AI 引擎和泄漏给搜索引擎
* 一样糟。抽到这里,两边共用一份,理由同 `lib/site-url.ts` 文件头。
*
* 刻意不 import `@/lib/source`:那条链会把整个 fumadocs-mdx 管线拖进来,
* vitest 没配 MDX 插件会直接 parse 失败。入参用结构化的宽类型,
* 让本文件保持纯函数、可单测。
llms.txt(llmstxt.org 约定)给 AI 引擎一份无导航噪音的全站索引:
标题 + 描述 + 绝对链接,构建期从 source 枚举,和 sitemap 同源。
309 条(中英各半),只做索引不做全文 —— 全站正文拼进去几 MB,
反而挤爆它本来要省的上下文。

robots 按用途分开,不再一刀切:
- 引用型(OAI-SearchBot / ChatGPT-User / Claude-User / PerplexityBot)
  用户提问时实时取用并附出处链接,走 * 组放行。刻意不给它们开 UA 专属组
  —— robots.txt 里专属组整体覆盖 * 而不是叠加,开了就得把 admin/editor
  那串 disallow 再抄一遍,抄漏一条等于把后台放给它们。
- 训练型(GPTBot / ClaudeBot / Google-Extended / CCBot / Bytespider 等)
  只收语料不给回链,内容是 CC BY-NC-SA,整站 disallow。

顺手删掉 public/robots.txt:那份手写文件把两类一起屏蔽,而且早就不生效
—— App Router 下 app/robots.ts 才是线上实际服务的 /robots.txt,它被静默
遮蔽,读代码的人却会以为 AI 爬虫已经挡住了。线上实测放行所有爬虫,
和文件里写的意图正好相反。

draft 过滤和 slug 编码从 app/sitemap.ts 抽到 lib/doc-entry.ts 共用:
两边各写一份的话,早晚出现 sitemap 过滤了草稿、llms.txt 没过滤,
草稿泄漏给 AI 引擎和泄漏给搜索引擎一样糟。

测试守的是策略不变量而不是实现:训练型必须被挡、引用型必须不在名单里。
两边错任何一边都不报错 —— 要么内容白送进训练集,要么再也拿不到 AI 引用。
tests/robots.test.ts 原来只断言 5 个训练型 UA,而策略里有 12 个:
从 AI_TRAINING_CRAWLERS 删掉没被断言的那几个,测试照样绿,等于白守。
补全成 12 个(新增不用同步,放宽是安全方向)。变异验证过:删掉
Applebot-Extended 会红,还原后绿。

注释违反 CLAUDE.md §3「严禁历史叙事 / 大段 docstring 描述功能」:
删掉 public/robots.txt 的来龙去脉、doc-entry 的抽取过程、force-static
里那段 Vercel CPU 往事,只留维护时会踩的不变量 —— UA 专属组整体覆盖
`*` 不叠加(所以不给引用型开 Allow 组)、不要 import @/lib/source
(会拖进 MDX 管线让 vitest 挂)、sitemap 与 llms.txt 的过滤必须同进同退。

Co-authored-by: copilot-pull-request-reviewer[bot] <198982749+copilot-pull-request-reviewer[bot]@users.noreply.github.com>
Copilot AI review requested due to automatic review settings August 2, 2026 20:17
@longsizhuo
longsizhuo force-pushed the geo/llms-txt-and-ai-robots branch from 07fbb22 to 2ec1ea4 Compare August 2, 2026 20:17

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated no new comments.

Suppressed comments (1)

app/robots.ts:34

  • PRIVATE_PATHS 的注释说“* 组和下面每个 UA 专属组都要带上”,但当前下方的训练爬虫组是 Disallow: /,实际上不需要再带 PRIVATE_PATHS(而且现在也没带)。这会让后续维护者误以为这里漏配或需要复制粘贴。建议把注释改成:只有当某个 UA 组不是整站 Disallow 时,才需要显式带上 PRIVATE_PATHS(因为 UA 专属组覆盖 * 且不继承)。
/**
 * 登录态 / 接口路径,任何爬虫都不该进。
 * `*` 组和下面每个 UA 专属组都要带上(专属组覆盖 `*`,不继承)。
 */

@longsizhuo
longsizhuo force-pushed the geo/llms-txt-and-ai-robots branch from 2ec1ea4 to 410b8de Compare August 2, 2026 20:28
@longsizhuo
longsizhuo merged commit ba0cdc7 into main Aug 3, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants