跳转到内容
关系图谱

llms:让站点对 AI 友好的 llms.txt

1,886 字 7 分钟

属性 1
series
VaultKit 包指南

@vaultkit/vitepress-plugin-llms 是一个在 VitePress buildEnd 钩子里调用的 llms.txt / llms-full.txt 生成器,遵循 llmstxt.org 约定:前者是给 LLM 看的站点索引(标题 + 摘要 + 链接),后者是全文汇编,AI 助手一次抓取就能把整站内容装进上下文。

它与 @vaultkit/vitepress-plugin-feed 采用同构的职责切分:站点元数据与写盘由插件负责(缺省从 site 配置与 sitemap.hostname 推导,零重复配置),「条目从哪来、正文如何净化」完全委托宿主。这是零依赖的纯构建期包,本文所有产物示例都以代码围栏呈现。

为什么需要它

越来越多的读者不再直接访问网页,而是让 AI 助手代为阅读与总结。HTML 对 LLM 并不友好:导航、侧栏、脚本占掉大量 token,正文反而被稀释。llmstxt.org 提出的约定很简单——在站点根部放一个 /llms.txt Markdown 文件,用固定结构告诉 LLM「这个站是什么、有哪些内容、去哪里读」:

  • 一个 H1(站点/项目名,唯一必需元素)
  • 一个引用块(一句话摘要)
  • 若干 H2 分节,每节是链接列表,行格式 - [标题](链接): 可选说明

社区还衍生出 llms-full.txt——直接把全文内容平铺进一个文件,省去 LLM 逐链接抓取。本包两个产物一次生成。

对 Obsidian vault 这类内容源,还有一层隐私考量:md 源文件里可能有 ``、未发布草稿。直接读盘导出的社区插件会把这些原样泄漏。本包的做法是隐私策略前置——插件只写入宿主传进来的字段,正文净化在传入前完成,插件不碰源文件。

安装

sh
pnpm add -D @vaultkit/vitepress-plugin-llms

使用 @vaultkit/vitepress-theme-vault 主题则开箱即带:features.llms 缺省为 true,主题复用与 RSS/页面一致的笔记口径(publish 门控、注释剥离、日期倒序),并把 wikilink 语法平文本化后再喂给本包。关闭方式:

ts
export default defineConfigWithTheme({
  extends: createVaultTheme({
    site: { /* ... */ },
    features: { llms: false },
  }),
})

快速上手

.vitepress/config.ts
ts
import { generateLlms } from '@vaultkit/vitepress-plugin-llms'
import { defineConfig } from 'vitepress'

export default defineConfig({
  sitemap: { hostname: 'https://example.com' }, // hostname 从这里自动读取
  async buildEnd(config) {
    await generateLlms(config, {
      sectionHeading: '## 笔记',
      items: notes.map(note => ({
        title: note.title,
        url: note.url,
        description: note.excerpt,
        date: note.date,
        tags: note.tags,
        body: note.sanitizedBody, // 宿主负责隐私净化
      })),
    })
  },
})

构建结束打印 [llms] llms.txt / llms-full.txt 已生成(N 条)

公开导出一览

导出形态说明
generateLlms异步函数(具名导出)生成入口,(config, options) => Promise<void>
默认导出同上import generateLlms from '...' 亦可
LlmsOptions类型选项接口
LlmsItem类型条目结构
LlmsSiteConfig类型SiteConfig 的结构化子集(只消费 outDirsiteuserConfig.sitemap.hostname

LlmsOptions 逐项详解

items(必填)

  • 类型:LlmsItem[] | (() => LlmsItem[] | Promise<LlmsItem[]>)
  • 语义:条目集合,或按需计算的函数(构建期调用一次,支持异步)。与 feed 包同款契约,两者常常从同一份笔记集合映射:
ts
const notes = await loadNotes()
await generateFeed(config, { items: notes.map(toFeedItem) })
await generateLlms(config, { items: notes.map(toLlmsItem) })

hostname

  • 类型:string,默认取 VitePress sitemap.hostname
  • 语义:站点源,尾部 / 自动剥离;索引行与全文头部的 URL 均由 hostname + item.url 拼出。两处都没配时直接抛错——llms.txt 的价值就在于可跟随的绝对链接。

title / description

  • 类型:均为 string,默认分别取 site.titlesite.description
  • 语义:llms.txt 的 H1 与引用块。llmstxt.org 规定 H1 是唯一必需元素,引用块摘要紧随其后——这两项直接决定 LLM 对站点的第一印象,站点描述写得越信息密集越好。

sectionHeading

  • 类型:string,默认 '## 内容'
  • 语义:索引分节标题(含 ## 前缀的完整行)。llmstxt.org 用 H2 分节组织链接列表,博客站点常用 '## 笔记' / '## 文章',文档站可用 '## 指南'

full

  • 类型:boolean,默认 true
  • 语义:是否输出 llms-full.txt 全文版。站点内容体量大、或不希望全文被一次性抓取时置 false,只保留索引。

indexPath / fullPath

  • 类型:均为 string,默认 'llms.txt' / 'llms-full.txt'
  • 语义:两个产物的输出文件名,相对 outDir。按 llmstxt.org 约定应放在站点根部,一般不需要改。

LlmsItem 逐字段详解

字段类型必填去向
titlestring索引行链接文字;全文条目的 # 标题
urlstring站内路径(如 '/my-post'),与 hostname 拼出条目链接
descriptionstring索引行冒号后的一句话摘要(llmstxt.org 的 optional details 位)
datestringllms-full.txt 条目头部的 日期: 元数据行
tagsstring[]llms-full.txt 条目头部的 标签: 元数据行(顿号连接)
bodystringllms-full.txt 的纯文本正文;缺省跳过正文段,宿主必须先完成隐私净化

description 只进索引、date / tags / body 只进全文——两个产物各取所需,条目结构一份即可。

产物解析

llms.txt(索引)

llms.txt
txt
# 数字花园

> 基于 Obsidian vault 的个人博客

## 笔记

- [llms:让站点对 AI 友好的 llms.txt](https://example.com/llms-txt): 介绍 llms.txt 生成插件
- [feed:给博客配上 RSS 与 JSON Feed](https://example.com/feed-rss-json-feed): 介绍 RSS 插件
- [Minimal Theme](https://example.com/minimal-theme)

结构逐行对应 llmstxt.org 约定:H1 站点名 → 引用块摘要 → H2 分节 → - [标题](链接): 摘要 列表;无摘要的条目省略冒号后半段。

llms-full.txt(全文)

llms-full.txt
txt
# llms:让站点对 AI 友好的 llms.txt
日期:2026-07-10
标签:VaultKit、AI
URL:https://example.com/llms-txt

llms.txt 是给 LLM 看的站点索引……(净化后的纯文本正文)

---

# feed:给博客配上 RSS 与 JSON Feed
日期:2026-07-09
标签:VaultKit、RSS
URL:https://example.com/feed-rss-json-feed

RSS 是博客与读者之间最长寿的契约……

每个条目 = # 标题 + 日期/标签/URL 元数据行(缺失的行直接省略)+ 空行 + 正文,条目之间以 --- 分隔线隔开。没传 body 的条目只留头部——LLM 至少还能拿到元数据与 URL。

设计机制

llmstxt.org 约定为什么选 Markdown

llms.txt 标准刻意选择 Markdown 而非 JSON/XML:LLM 本身就以 Markdown 为最舒适的输入格式,人类也能直接读。固定的「H1 + 引用块 + H2 链接列表」结构又保留了可编程解析的余地——既是给模型的散文,也是给解析器的 schema。本包的输出严格贴合这一结构,不添加私货。

隐私净化为什么前置到宿主

「读 md 源文件直接导出」的方案,等于把发布管线里所有净化步骤(注释剥离、草稿门控、定时发布)全部旁路。本包反过来:body 传什么写什么,不传就不写。主题适配层的净化示例——把 Obsidian 语法平文本化:

ts
function plainifyWikiSyntax(body: string): string {
  return body
    .replace(/!\[\[[^\]]*\]\]/g, '') // 媒体/嵌入剔除
    .replace(/\[\[([^\]|]+)\|([^\]]+)\]\]/g, '$2') // 别名 wikilink → 别名
    .replace(/\[\[([^\]|]+)\]\]/g, '$1') // wikilink → 纯文本
}

与 feed 的同构性

两包的 options 形状、hostname 推导、items 委托、抛错语义完全一致——学会一个等于学会两个。区别只在产物:feed 面向订阅器(结构化时间线),llms 面向模型(结构化知识库)。

常见问题

Q:构建报 [llms] 缺少 hostname 错? 与 feed 包同款约束:配 VitePress sitemap.hostname 或传 options.hostname,推荐前者一处配置全站共享。

Q:llms-full.txt 会泄漏我的私人注释吗? 只要你不把未净化的正文传进 body 就不会——插件本身从不读源文件。用主题则净化已内置(与页面渲染同一条 publish/剥离管线)。

Q:想只要索引、不要全文?full: false,llms-full.txt 不再生成,控制台日志也只报 llms.txt。

Q:如何验证 AI 真的在用? 观察访问日志中 /llms.txt 的抓取记录(GPTBot、ClaudeBot 等 UA)。如果反过来想屏蔽 AI 抓取,见同系列的 robots:一分钟配好 robots.txt

相关链接