
周五下午我花了一个多小时把站点的 llms.txt 写得漂漂亮亮站点定位、核心栏目、每个重要页面的三行摘要、常用接口入口甚至把 FAQ 都整理进去了。合上编辑器的时候特别满足觉得只要是稍微智能一点的 LLMs扫一眼这个文件就能彻底搞懂我这个站。结果呢我在自己做的 AI 问答助手里问“你们这个站主要解决什么问题”“支持哪几种接入方式”它回答得含含糊糊跟我 llms.txt 里写的完全是两码事。后来我才反应过来一个反直觉的事实llms.txt 是写给 LLM 看的但 LLM 默认根本不会主动去读它。它不像 robots.txt 那样有主流爬虫默认遵守的协议地位更像是一封你写好放在门口的信——问题是送信员搜索引擎爬虫、RAG 检索器、Agent 工具链并不知道这封信存在就算知道也不一定把它当优先事项。所以“如何让 LLMs 读你的 llms.txt”这个问题的核心不是“怎么写这个文件”而是“怎么把它主动塞进 LLM 的内容获取链路里”。这篇文章我会围绕这件事展开把原理、RAG 抓取方案、Agent 工具注入方案、验证方法以及我踩过的几个坑完整过一遍。适合正在做 RAG 应用、Agent 工具或品牌站 AI 问答的开发者参考。1. 先搞清楚一件事llms.txt 默认不会被 LLM 主动读取1.1 llms.txt 的设计初衷llms.txt 是 fast.ai 的 Jeremy Howard 在 2024 年提出的一个想法核心理念是在网站的域名根目录放一个 Markdown 格式的文件比如https://example.com/llms.txt用人类可读的方式描述这个站点的核心信息和内容索引帮助 LLM 快速理解“这个网站是什么、有什么、该重点看什么”。这个设计的出发点很实在LLM 的训练语料不可能覆盖每一个站点的最新内容尤其是个人的博客、文档站、中小型产品的官网。就算 GPT 或 Claude 知道你的域名它对站点内容的了解也大概率停留在几年前甚至完全空白。llms.txt 就是想给这些空白补一张“导览图”。它和 robots.txt 有本质区别。robots.txt 是“限制型”的告诉爬虫哪些路径不能碰llms.txt 是“推荐型”的告诉模型“我先建议你看这个文件”。问题是robots.txt 是经过长期沉淀的事实标准主流搜索引擎都会遵守而 llms.txt 目前更多是一个社区倡议没有任何一家主流模型厂商承诺“我们一定会读取每个站点的 llms.txt”。1.2 一个关键认知文档在那里不等于模型能读到很多刚接触这个概念的人包括我一开始都会想当然地认为只要我把 llms.txt 放到根目录LLM 自然就能读到。这个认知是错的。一个 LLM 要“读到”你的 llms.txt内容必须进入下面三条通道中的至少一条预训练 / 后训练语料OpenAI、Anthropic、Google 在训练模型时可能会把互联网上已经存在的 llms.txt 文件当作语料的一部分。但这条路完全不可控时效性差而且模型参数一旦固定文件后续更新它就感知不到了。RAG 检索你在应用里搭一条检索管道用抓虫去抓取站点内容分块、向量化、存进向量库。用户提问时检索器根据相似度召回相关片段放入上下文。llms.txt 只有在你的抓虫主动去抓、检索器恰好召回它的分块时才算“被读到”。Agent 工具调用模型通过 function calling / tool use在对话过程中主动调用一个“读取 llms.txt”的工具。这是可控性最高的一条路但需要你在工具层显式声明并让模型知道这个工具的存在。我见过太多人把 llms.txt 写完之后就不管了然后在 AI 问答里得不到理想效果回头怀疑模型“不聪明”。其实不是模型不聪明是它从头到尾就没见过这个文件。这里我建议所有做 RAG 应用的团队统一一个认知llms.txt 是一条需要主动投喂的内容管道不是一个被动等待的内容陈列。你只有把它的内容接进 RAG 索引或者把它的读取动作暴露成 Agent 工具它才算真正生效。通道可控性时效性适用场景预训练语料低差不可主动干预纯看运气RAG 检索中好最常见的落地方式适合文档问答Agent 工具调用高好需要模型“按需查阅”站点结构时2. 最常见也最稳妥的做法在 RAG 索引里主动抓取并解析 llms.txt2.1 一个核心设计把 llms.txt 当“入口”而不是“终点”我在自己的 RAG 管道里对 llms.txt 的定位是入口文件 路由表。它本身提供站点概览但它链接指向的那些页面才是真正的正文内容。很多教程只教你“把 llms.txt 抓下来、切块、存向量库”然后就没有然后了。这样做的效果其实很有限因为 llms.txt 只有一屏内容用户问的问题往往需要结合具体页面的细节才能回答完整。所以我建议的流程是抓取https://你的域名/llms.txt。解析 Markdown提取标题、段落和所有链接。把 llms.txt 本身按小节切块作为“站点级摘要”存入向量库。把链接指向的页面也抓下来作为“页面级详情”存入向量库。两者通过元数据关联比如parent_sourcellms.txt、sectionxxx。这样做的好处是当用户问“你们站主要做什么”时检索器能直接召回 llms.txt 的摘要块当用户问“某个具体功能怎么接入”时又能召回对应页面的详情块。两者互补不会出现“只有地图没有街道”或者“只有街道没有地图”的尴尬。2.2 一份可直接抄走的抓取解析代码下面这个脚本是我在项目里用的一个简化版本依赖就三个requests、markdownify、re。它的作用是抓取 llms.txt解析出里面的链接再对每个链接页面做简单的正文提取和分块。import requests import hashlib import re from urllib.parse import urljoin HEADERS { User-Agent: MyRAGBot/1.0 (https://example.com/botpolicy) } def fetch_llms_txt(site_root: str) - str: 拉取站点根目录的 llms.txt url urljoin(site_root.rstrip(/) /, llms.txt) resp requests.get(url, headersHEADERS, timeout10) resp.raise_for_status() # 注意有的服务器把 .md 当 application/octet-stream 返回 # 但内容实际是文本所以这里要用 content 解码而不是 text。 return resp.content.decode(utf-8, errorsreplace) def parse_links_from_markdown(md_text: str, base_url: str) - list[str]: 从 Markdown 中提取链接并转为绝对地址 links re.findall(r\[.*?\]\((.*?)\), md_text) absolute_links [] for link in links: if link.startswith((http://, https://)): absolute_links.append(link) elif link.startswith(/): absolute_links.append(urljoin(base_url, link)) elif link.startswith(#): continue else: absolute_links.append(urljoin(base_url, link)) return list(dict.fromkeys(absolute_links)) # 使用示例 site https://example.com try: llms_txt fetch_llms_txt(site) print(llms.txt 内容长度:, len(llms_txt)) links parse_links_from_markdown(llms_txt, site) print(llms.txt 中解析出的链接数量:, len(links)) for link in links[:20]: print( -, link) except requests.HTTPError as e: print(抓取失败请确认 llms.txt 是否放在域名根目录:, e)这段代码解决了一个很容易被忽略的问题llms.txt 里的链接有可能是相对路径。比如写[关于我们](/about)而不是[关于我们](https://example.com/about)解析时必须用urljoin拼全否则后续抓取就断了。2.3 为什么用 llms.txt 比直接抓首页 HTML 划算我做过多组对比一个典型的企业站首页 HTML 解析成文本后大概在 5000 到 8000 token 之间其中一半是导航栏、弹窗、页脚、无意义空白和广告脚本。而同一个站的 llms.txt 通常在 200 到 500 token 左右信息密度高得多因为它不需要迎合浏览器渲染只需要把“站点是什么、有哪些关键页面、每页解决什么问题”写清楚。如果你的 RAG 系统每次更新要抓几百个页面用 llms.txt 当入口能帮你快速确定哪些页面值得抓、哪些可以放一放抓虫成本会明显下降。我做抓虫调度时先读 llms.txt 再决定抓哪些链接比传统“遍历 sitemap 抓全部”的方案节省了接近一半的无效抓取。提示llms.txt 是一个相对低频变更的文件。对大多数站点抓虫一天抓一次就够了。但如果你的 llms.txt 是动态生成的比如推送发布时自动更新那抓取频率可以缩短到小时级。我建议抓取时带If-None-Match或者If-Modified-Since头用 304 状态判断要不要重新解析能省不少请求。2.4 分块时千万别把 llms.txt 整个揉成一个向量这是一个非常隐蔽的坑。llms.txt 内容虽然短但内部可能包含好多个主题站点介绍、产品列表、文档链接、团队信息、联系方式。如果你把整个文件作为一个 chunk 存进向量库检索时问题相关性会非常难过——用户的真实问题往往只匹配文件里的某一个小节而整个块的向量是所有这些小节语义的平均值结果就是“每一个问题都能沾到一点边但每一个问题匹配得都不够精准”。我的做法是按 Markdown 标题切块。#是站点整体导览##或###是具体主题。切完之后每个 chunk 只有 100 到 300 个 token召回效果会好很多。3. 更可控的进阶路线把 llms.txt 变成 Agent 的工具和上下文3.1 为什么仅靠 RAG 还不够RAG 方案看着靠谱但它有一个天然的短板检索器的召回排序是“相似度优先”的。用户问一个非常具体的技术问题时向量数据库很可能返回 5 个内容高度相关的正文页面把 llms.txt 这种“站点导览性质”的 chunk 挤到 topk 之外。也就是说即使你的 RAG 索引里存了 llms.txt它也不保证每次都能被召回到。另外还有一层RAG 的上下文窗口是有限的。塞进去一堆和问题不相关的正文之后模型很难再腾出注意力去主动发现“哦原来这是应该有站点地图的”。所以为了让 LLM 稳定地“知道有 llms.txt 这回事”我倾向于在 Agent 层面做兜底。3.2 方案 A把读取 llms.txt 抽象成一个 function calling 工具这是我最推荐的方式。给模型一个工具叫read_llms_txt让它在需要了解站点全貌时主动调用。工具描述写得越明确模型调用它的概率越高。{ name: read_llms_txt, description: Read the /llms.txt guide of a website root to get a structured overview of the site, its main sections, key pages and content summaries. Use this tool when the answer requires understanding what the website is about or which pages may contain the requested information., parameters: { type: object, properties: { site_url: { type: string, description: The root URL of the website, e.g., https://example.com } }, required: [site_url] } }调用逻辑不复杂用户问题进来后如果模型认为这道题需要了解站点结构或信息分布它就会主动调用这个工具工具函数里做一次 HTTP 请求把 llms.txt 的文本拉下来返回给模型。模型拿到之后再基于这个“站点地图”决定下一步是直接回答还是继续调用其他页面读取工具。这个方案的好处是llms.txt 不占常驻上下文只在模型觉得需要时才进入窗口。尤其适合 Agent 场景因为 Agent 本身就是在做“多步决策”给它一个“读地图”的能力比硬塞地图更自然。3.3 方案 B把精简版 llms.txt 直接放进 System Prompt如果你的系统没有接入 function calling或者你用的模型工具调用不稳定退而求其次的做法是把 llms.txt 的精简版放进 System Prompt。注意是精简版。System Prompt 的预算很宝贵你不可能把几百条链接全塞进去。只放这几样站点一句话定位对应 llms.txt 的 H1 或首段核心栏目或功能清单内容更新日期或版本号一句引导语“有关本站的更详细信息请参考 https://你的域名/llms.txt”这样做的意义在于模型在一开始就知道“这个站是谁、大概有什么”当用户问到某个具体页面时模型会根据引导语建议用户查看详情或者触发后续检索。虽然不如工具调用灵活但在轻量级 ChatBot 里已经够用了。我在实际项目中通常是 A B 结合System Prompt 里放一行“本站有 llms.txt 站点导览文件”工具层提供读取工具。两者配合一方面模型不会完全瞎猜另一方面它也有能力拿到完整版本。3.4 方案 C通过 MCP/插件协议暴露读取能力如果你的 Agent 框架支持 MCPModel Context Protocol或者类似插件机制可以考虑把read_llms_txt实现成一个标准工具让多个客户端复用。这样你在一个站点上写好的 llms.txt 读取逻辑可以一键连接到任何支持 MCP 的客户端不用每个项目重新写一遍 tool definition。目前社区里已经开始有人做通用的 “site memory” 类 MCP server核心动作就是“读取站点根目录的 llms.txt 并缓存”。这类工具还很年轻但方向是对的——它把“站点导览”从一次性脚本变成了 Agent 的标准能力。我的建议是先拿 function calling 方案跑通等 MCP 生态再稳定一点再迁移也不迟。3.5 我的统一建议RAG 负责检索Agent 工具负责兜底我自己现在跑项目的标准配置是这样的RAG 管道把 llms.txt 和其他页面一起抓取、分块、入库负责常规问答。Agent 工具层提供read_llms_txt在模型觉得“现有上下文不足以回答”时可以主动读取。System Prompt 中只放 llms.txt 的一句话定位和一句话引导。这套组合在大多数场景下比单一方案稳定得多。RAG 保证速度和准确性Agent 工具保证不遗漏站点全貌System Prompt 负责给模型一个基本预期。三层各管各的事互不干扰。4. 怎么验证 LLM 真的“读”到了你的 llms.txt4.1 设计一套“只有 llms.txt 才答得对”的测试题很多人做完 llms.txt 接入后不知道怎么验证随手在对话框里问一句“你知道 llms.txt 里写了什么吗”模型回了一大段看起来像模像样其实全是脑补。这种验证没有意义。正确的做法是设计一组答案只能从 llms.txt 中得出的封闭式问题。比如我的站点 llms.txt 里写了站点定位面向独立开发者的 AI 工具评测与教程站三个核心栏目工具评测、实战教程、踩坑记录更新频率每周三更新联系方式仅通过 GitHub Issues那我就会设计这么一组测试问题这个站点主要解决什么问题站点划分为哪几个核心栏目内容更新频率是多少通过什么渠道可以联系到站长这个站点和普通 AI 资讯站有什么区别这些问题有一个共同特点它们不太可能在站内正文页面里出现一模一样的答案只有 llms.txt 会系统性地把这些信息写全。模型答对了说明 llms.txt 确实被读到了答不对说明接入链路有问题。4.2 用脚本批量跑评测逐条手动问太慢。我通常会写一个简单的评测脚本把所有测试问题喂给模型接口然后人工或规则判断答案是否命中关键字段。import json from openai import OpenAI client OpenAI() TEST_CASES [ { question: 这个站点主要解决什么问题, answer_should_contain: [独立开发者, AI 工具] }, { question: 站点划分为哪几个核心栏目, answer_should_contain: [工具评测, 实战教程] }, ] def ask_with_rag(question: str) - str: # 这里替换成你自己的 RAG 管道调用逻辑 ... for case in TEST_CASES: answer ask_with_rag(case[question]) hit all(kw in answer for kw in case[answer_should_contain]) print(f问题: {case[question]}) print(f命中: {hit}) print(f答案片段: {answer[:100]}) print(---)注意现在大模型接口的 SDK 更新频率很快具体客户端初始化和调用方式以你当前的 SDK 版本为准。我这里想表达的重点是评测思路每条测试用例包含“问题”和“答案必须包含的关键词”用关键词命中率来衡量 llms.txt 是否真正对最终回答产生了影响。4.3 做一组有对照的对比实验光测一次通过还不够建议做一个简单的对照组实验组设置预期效果A 组不接 llms.txt只抓正文页面对站点定位类问题回答模糊B 组接 llms.txt但不加 Agent 工具大部分时候能答对偶尔检索不到C 组RAG Agent 工具全部接入稳定答对且回答结构清晰我实际跑下来A 组对“站点主要解决什么问题”这种问题的通过率大概只有三成模型经常会答成通用套话B 组能到七成左右C 组基本能到九成以上。剩下的一成基本都是 llms.txt 本身写得不够明确导致的——比如站点定位那句写得太模糊模型虽然读到了但没法精准理解。这个对比实验值得保留下来以后你改 llms.txt 内容或者调整抓取策略时重跑一遍就能看出改动是正向还是负向。4.4 别忘了在 trace 里看链路如果你的 RAG 或 Agent 框架接入了 LangSmith、Langfuse 这类可观测性工具验证会变得非常直观。直接看一次完整请求的 trace用户问题进来后检索器有没有把sourcellms.txt的 chunk 放进上下文模型有没有调用read_llms_txt工具工具返回的内容有没有真正出现在后续的 LLM 调用里只要 trace 里看到 llms.txt 的内容进过上下文并且答案质量确实变好了那这个验证就闭环了。我之前踩过的一个坑是模型调用工具了但因为我工具返回内容太长被截断了导致模型只看到了前半段站点介绍没看到后面的核心栏目列表。这种问题离线测试很难发现只有在 trace 里才能看到。5. 排查链路为什么你的 LLM 就是不读 llms.txt5.1 症状描述假设你已经把 llms.txt 放到了根目录RAG 也配置了抓取但测试时模型还是答非所问。这时不要慌按下面的链路一步步排。5.2 第一步确认 llms.txt 文件本身可达且类型正确先手动请求一下看返回头curl -sI https://example.com/llms.txt期望的结果是HTTP/1.1 200 OK Content-Type: text/markdown有一个很常见的坑如果你的服务器把.txt文件按application/octet-stream返回部分抓虫工具会直接放弃解析。多数现代 Web 服务器对.txt都能正确返回text/plain但如果你把文件命名成llms.md或者走了一些自定义路由就可能会出现 MIME 类型错误的问题。可以尝试在服务器配置里显式加一条.txt文件返回text/plain; charsetutf-8。还有一个隐蔽问题llms.txt 不能放在子路径下。它必须在你站点的域名根目录。比如你站点是https://blog.example.com那文件就是https://blog.example.com/llms.txt如果你把文件放在了https://blog.example.com/static/llms.txt那几乎所有按标准实现的抓虫都找不到它。5.3 第二步确认 RAG 管道真的存入了 llms.txt 的 chunk排除了文件可达问题后去向量数据库里查一下用 llms.txt 的来源标记筛选看看有没有对应记录。很多时候问题出在抓虫配置上比如你配置了max_depth1而 llms.txt 是在入口页成功 fetch 了但解析出来的链接没有进入入库队列。我遇到过最离谱的一次是抓虫代码里做了去重去重键用的是 URL 的 MD5但 llms.txt 解析出来的链接是相对路径用urljoin之后变成了https://example.com/https://example.com/about这种脏数据结果所有内部链接都被判定成外部地址给过滤了。这种问题不看日志根本发现不了。5.4 第三步确认检索器真的召回了 llms.txt 相关 chunk向量库存了不代表会被召回。直接拿你的测试问题去查询向量数据库看 topk 结果里有没有sourcellms.txt的记录。如果没有有两个常见原因一是向量化之后的 chunk 语义和用户问题匹配度不够高。上面我说过不要整个 llms.txt 揉成一个向量按标题切块能有效缓解。二是你是用ttl或者无监督聚类做过滤的把一些“看起来像导览”的文本给过滤掉了。建议检查一下抓虫入库的预处理规则别把站点摘要类文本当成导航噪音清掉。5.5 第四步确认 Prompt 或工具描述真的传给了模型如果你走的是 Agent 工具方案去 trace 里看模型收到的 tool schema 是否包含read_llms_txt的描述。我遇到过一个问题我定义工具时把函数装饰器名字写错了导致运行时工具列表里一直没有这个工具模型根本不知道可以调用它。这种问题表面上是“模型不读 llms.txt”实际上是“模型根本不知道有这个选项存在”。还要注意工具描述的长度。描述写得太长会被截断描述写得太短模型可能不理解什么时候该用。我建议把“使用场景”和“什么时候不要用”写清楚模型在决策时会更准确。5.6 第五步确认模型“读到了”但没有“答出来”最后一种情况最让人崩溃trace 里显示 llms.txt 的内容确实在上下文里工具也调了但答案仍然不对。这通常是 llms.txt 内容结构的问题。比如你的文件里只写了一堆链接没有任何说明性文字模型读了等于白读或者你的站点定位写得太含糊比如“本站提供优质的解决方案”LLM 读完还是不知道你到底解决什么问题。我用一个真实案例说明。有个朋友的博客站把 llms.txt 放在了/blog/llms.txt但 RAG 抓虫配置的站点根域名是https://example.com导致每次抓取都是 404。他排查了很久最后发现是当初部署博客框架时llms.txt 被生成的静态文件路径带上了/blog前缀。这个例子很典型问题往往不在模型而在文件位置和抓取配置不一致。注意如果实在没法保证 llms.txt 放根目录可以做一个兜底方案——把 llms.txt 的内容同时生成一份llms-full.txt把所有正文核心内容都写在里面。这类“全文导览文件”即使链接页面不全也能让 LLM 获得足够信息容错率更高。6. 让 llms.txt“活”起来动态生成与垂直场景适配6.1 静态文件的宿命内容更新了导览没跟上llms.txt 最大的敌人不是格式而是过期。站点栏目改了、页面删了、新增了方向但 llms.txt 还是三个月前的那一版。尤其在做 RAG 抓取时如果抓虫只认 llms.txt 里的链接你的站点信息源就会慢慢落后于实际内容。所以我现在在项目里的做法是把 llms.txt 当作构建产物不手动维护。站点内容发布时CI/CD 流程里自动生成一份 llms.txt生成逻辑非常直接——从内容管理系统或数据库里抽出标题、描述、路径、更新时间套一个模板推到根目录。这样 llms.txt 和站点内容始终是同一个版本不会出现“内容更新了两星期导览还在上一版”的尴尬。6.2 一种可落地的动态生成模板这是我的一个通用模板结构你可以在自己的生成脚本里参考# 站点名称 一句话站点定位说明这个站点为谁、解决什么问题。 ## 核心栏目列表 - [栏目 A](链接)一句话说明该栏目包含哪些内容 - [栏目 B](链接)一句话说明 ## 高频问题 Quick FAQ - Q: 你们支持哪种接入方式 - A: 支持 REST API 和 Webhook 两种方式详见[接入文档](链接) ## 最近更新 - 2025-XX-XX更新了 XX 功能 - 2025-XX-XX发布 XX 报告 本文件由 CI 自动生成更新时间为 UTC 2025-XX-XX。注意这里 一句话站点定位是全文件里最重要的一句。我在测试中发现很多模型在 Agent 场景下只读文件开头几行就决定要不要继续深入如果开头这一句写不明白后面的内容被注意到的概率会大幅下降。所以哪怕其他部分仓促一些首段定位句一定要仔细打磨。6.3 垂直场景适配从 time-llama 这类模型项目看 llms.txt 怎么写最近刷到一些关于 time-llama 的热度它做的事情是通过动态低秩自适应把 LLM 迁移到时序预测任务上。像 time-llama 这种“面向特定任务的大模型项目主页”其实非常适合用 llms.txt 来给模型提供上下文因为它解决的问题可以非常精准地结构化。如果你的站点或者文档站主题是这类垂直模型项目llms.txt 里应该突出的字段和普通内容站不一样。普通站写栏目和简介就够了模型项目主页还需要写清楚这个模型适用于什么场景比如“适用于长序列时间序列预测特别是存在分布漂移的序列”输入输出格式接收什么形状的时序数据输出是预测值还是预测分布核心机制说明比如动态低秩自适应dynamic low-rank adaptation做了什么事基准评测结果在哪些公开数据集上跑过效果如何代码和论文入口README、ArXiv、GitHub 链接部署和调用方式API 还是本地推理这样的 llms.txt 相当于给任何基于 LLM 的技术助手一份“模型说明书”用户问“time-llama 能不能用于我的电力负荷预测”时模型可以从 llms.txt 里快速定位到适用场景和输入格式给出比“根据我的训练知识猜一下”准确得多的回答。6.4 动态生成时别忘了版本和变更记录给 llms.txt 加上updated字段和版本号看起来是个小事但能省很多排查成本。我在验证阶段吃过亏改动 llms.txt 之后RAG 管道因为缓存没有及时刷新一直用旧版本入库测试结果自然没有变化我还以为代码有问题最后才发现是缓存。加了版本号之后这类问题一眼就能看出来。检索结果里如果显示llms.txt v3而站点线上已经发布到v4那就不用怀疑模型直接去查抓虫刷新逻辑就行。发布时间也可以考虑在 CI 里自动写入 UTC 时间避免手动改错。最后分享一个我自己沉淀下来的统一流程如果你不确定从哪里下手可以直接照这个顺序做RAG 索引优先抓 llms.txt把文件按标题切块入库同时抓取链接指向的正文页面Agent 工具层暴露一个read_llms_txt工具描述里写清楚适用场景和调用时机System Prompt 只放一句站点定位和一句“更多信息见 /llms.txt”的引导每次内容发布时由 CI 自动重新生成 llms.txt并写入时间戳和版本号评测集固定保留每次改动后重跑一遍封闭式问题关注通过率变化。这套流程不一定适合所有人但它基本覆盖了“写文件—让 LLM 读到—验证读到了—内容更新后仍然有效”的完整闭环。我踩了挺多坑才把这套链路理顺尤其那句“别指望模型主动来找你”值得每个做 RAG 的开发者记住把你在根目录写好的 llms.txt 当一封重要信件寄信这件事本身永远要比写信更花心思。