File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change 33/**
44 * @file app/llms.txt/route.ts
55 * @description
6- * `/llms.txt` 路由(llmstxt.org 约定)。AI 引擎抓站时先读它拿全站索引。
6+ * `/llms.txt` 路由(llmstxt.org 约定)。
77 *
8- * 为什么是 route handler 而不是 public/llms.txt:文档是 MDX,会增删改,
9- * 手维护一份静态索引必然过期。这里从 `source` 现枚举,和 sitemap 同源。
10- *
11- * force-static:内容全部来自构建期的 MDX,没有请求相关的东西,
12- * 构建时产出一次即可,别让它变成每次请求现算的 dynamic 路由
13- * (i18n/routing.ts 文件头记着上次全站 dynamic 把 Vercel CPU 打爆的事)。
8+ * force-static 是必需的,不是优化:内容只依赖构建期的 MDX,漏了它这条路由
9+ * 会退化成每次请求现枚举全站文档的 dynamic 路由。
1410 *
1511 * @see https://llmstxt.org
1612 */
Original file line number Diff line number Diff line change 1919 * sitemap 指向 app/sitemap.ts 产出的 /sitemap.xml,hostname 复用同一份
2020 * NEXT_PUBLIC_SITE_URL。
2121 *
22- * AI 爬虫策略(2026-08):按用途分开,不是一刀切。
23- *
24- * - **引用型**(OAI-SearchBot、ChatGPT-User、Claude-User、PerplexityBot…):
25- * 用户实时提问时才来抓,抓完在回答里附出处链接。本站要的就是被引用,
26- * 所以放行 —— 走上面的 `*` 组即可,**不单独开组**。robots.txt 里 UA 专属
27- * 组会整体覆盖 `*` 而不是叠加,给它们开组就得把 admin/editor 那串 disallow
28- * 再抄一遍,抄漏一条就等于把后台开放给它们。少写一组反而更安全。
29- *
30- * - **训练型**(见 AI_TRAINING_CRAWLERS):把正文收进训练语料,不产生任何
31- * 回链。内容是 CC BY-NC-SA,整站 disallow。
32- *
33- * 这份策略取代了 public/robots.txt —— 那份手写文件把两类一起屏蔽,而且
34- * 早就不生效了:App Router 下 app/robots.ts 才是线上实际服务的 /robots.txt,
35- * 那份文件被静默遮蔽,读代码的人却会以为 AI 爬虫已经被挡住了。已删除。
22+ * AI 爬虫按用途分:引用型(实时取用并附出处链接)放行,训练型(收进语料、
23+ * 不给回链)整站 disallow —— 内容是 CC BY-NC-SA。
3624 *
3725 * @see https://nextjs.org/docs/app/api-reference/file-conventions/robots
3826 */
@@ -55,10 +43,12 @@ const PRIVATE_PATHS = [
5543] ;
5644
5745/**
58- * 训练语料型爬虫:只取内容不给回链 ,整站 disallow。
46+ * 训练语料型爬虫,整站 disallow。
5947 *
60- * 刻意不含 ChatGPT-User / Claude-User / OAI-SearchBot / PerplexityBot ——
61- * 那几个是用户提问时实时取用并产生引用的,属于放行的一侧(见文件头)。
48+ * 刻意不含 OAI-SearchBot / ChatGPT-User / Claude-User / PerplexityBot:
49+ * 它们靠 `*` 组放行就够了。**不要**为它们再开一个 Allow 组 —— robots.txt 里
50+ * UA 专属组整体覆盖 `*` 而不是叠加,开了就得把 PRIVATE_PATHS 再抄一遍,
51+ * 抄漏一条等于把后台放给它们。
6252 */
6353const AI_TRAINING_CRAWLERS = [
6454 "GPTBot" , // OpenAI 训练语料
Original file line number Diff line number Diff line change 33/**
44 * @file lib/doc-entry.ts
55 * @description
6- * 文档页 → URL / 可见性的共享判定。
7- *
8- * 原来这两个函数是 `app/sitemap.ts` 的私有函数。`app/llms.txt/route.ts`
9- * 要枚举同一批文档,如果各写一份,早晚会出现「sitemap 过滤了 draft、
10- * llms.txt 没过滤」这种单边漂移 —— 草稿泄漏给 AI 引擎和泄漏给搜索引擎
11- * 一样糟。抽到这里,两边共用一份,理由同 `lib/site-url.ts` 文件头。
6+ * sitemap 与 llms.txt 共用的文档过滤 / URL 编码,两边必须同进同退:
7+ * 只有一边过滤 draft 的话,草稿会静默泄漏给另一边的抓取方。
128 *
139 * 刻意不 import `@/lib/source`:那条链会把整个 fumadocs-mdx 管线拖进来,
14- * vitest 没配 MDX 插件会直接 parse 失败。入参用结构化的宽类型,
15- * 让本文件保持纯函数、可单测。
10+ * vitest 没配 MDX 插件会直接 parse 失败。入参因此用结构化宽类型。
1611 */
1712
1813import type { PageData } from "@/app/types/doc" ;
Original file line number Diff line number Diff line change 33/**
44 * @file lib/llms-txt.ts
55 * @description
6- * `/llms.txt` 正文生成器(llmstxt.org 约定)。
6+ * `/llms.txt` 正文生成器(llmstxt.org 约定)。只出索引不出全文 —— 三百多篇
7+ * 正文拼进去是几 MB,会挤爆它本来要省的上下文。
78 *
8- * 干什么用:AI 引擎(ChatGPT / Perplexity / Claude 等)抓站时先看这个文件,
9- * 拿到一份无导航噪音的全站索引,再决定深入读哪几篇。sitemap.xml 只有 URL,
10- * 模型得逐个抓才知道讲什么;llms.txt 一次给全 标题 + 描述 + 链接。
11- *
12- * 只做索引不做全文:站内 152 篇中文 + 152 篇英文,全文拼进去是几 MB,
13- * 反而挤爆上下文。约定本身也是「索引 + 链接」。
14- *
15- * 纯函数,不 import `@/lib/source`(那条链会拖进 fumadocs-mdx 管线,
16- * vitest 起不来)。枚举文档的活儿在 `app/llms.txt/route.ts` 里干。
9+ * 不 import `@/lib/source`:那条链会拖进 fumadocs-mdx 管线,vitest 起不来。
10+ * 枚举文档在 `app/llms.txt/route.ts`,这里保持纯函数。
1711 */
1812
1913export interface LlmsTxtEntry {
Original file line number Diff line number Diff line change @@ -24,13 +24,22 @@ describe("robots", () => {
2424 expect ( wildcard . disallow ) . toContain ( "/*/editor/" ) ;
2525 } ) ;
2626
27+ // 列全当前策略里的每一个训练型 UA:少列几个的话,从 app/robots.ts 删掉
28+ // 没列到的那些测试照样绿,等于白守。新增不用同步(放宽是安全方向)。
2729 it ( "训练型爬虫整站 disallow" , ( ) => {
2830 for ( const ua of [
2931 "GPTBot" ,
3032 "ClaudeBot" ,
33+ "anthropic-ai" ,
3134 "Google-Extended" ,
32- "CCBot" ,
35+ "Applebot-Extended" ,
36+ "meta-externalagent" ,
37+ "FacebookBot" ,
3338 "Bytespider" ,
39+ "Amazonbot" ,
40+ "CCBot" ,
41+ "Omgilibot" ,
42+ "DataForSeoBot" ,
3443 ] ) {
3544 expect ( blockedAgents ) . toContain ( ua ) ;
3645 }
You can’t perform that action at this time.
0 commit comments