ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

llms.txt与Skill.md:大模型内容索引与智能体技能定义全解析

llms.txt与Skill.md:大模型内容索引与智能体技能定义全解析 很多开发者在第一次接触大模型应用时都会被llms.txt和Skill.md这两个文件名搞混。它们听起来很像都像是给“AI 看”的文件甚至连后缀都是 Markdown。但真实用途差别很大llms.txt解决的是“大模型来到你的网站时如何快速读懂这个站点”Skill.md解决的是“智能体在干活时如何知道某个技能该怎么执行”。一个是内容入口一个是能力定义。本文会把两份文件分别讲透再放到同一个技术场景里对照最后回答一个很容易踩坑的问题skill.md里#后面的内容到底会不会被执行。读完你会清楚什么场景只需要一份文件什么场景必须两份一起维护。1. 先认清这两份文件解决的是完全不同的问题1.1 大模型访问网站时看到的并不是你浏览器里的页面传统网站为浏览器服务有导航菜单、轮播图、Cookie 弹窗、侧边栏推荐和大量装饰性 HTML。大模型或智能体访问网站时一般不会真的打开浏览器逐个点击而是直接对 URL 发起请求拿到页面内容再把纯文本交给模型处理。问题在于模型端拿到的内容往往包含大量噪声。导航、广告、版权声明、登录框、动态脚本渲染出来的空壳都会让模型难以定位“这个页面真正提供了什么”。更麻烦的是搜索引擎有sitemap.xml和robots.txt但这两个协议面向的是爬虫和收录不面向“如何用一句话描述页面用途、哪些链接最重要、哪部分内容是核心”。于是社区出现了一种更轻量的约定llms.txt。它放在站点根目录或.well-known目录下用 Markdown 格式写清楚这个站点的说明、核心入口和推荐给大模型阅读的链接。这样大模型只要抓到一个文件就能建立起对站点的整体认知而不必漫无目的地遍历整个页面。1.2 智能体需要的不只是“知道页面”还要“知道怎么做”Skill.md处在另一条技术路线上。智能体执行任务时不可能把每个操作细节都写进系统提示词。系统提示词的上下文窗口有限而且每次会话都要传递成本高、容易冲突。更合理的做法是把“一类技能”的描述拆成独立文件比如“如何查询订单状态”“如何从 MySQL 导出报表”“如何调用内部接口完成退款”。在这些技能仓库里最常见的元文件就是SKILL.md或者写作skill.md。它描述这个技能的名称、作用、使用前提、必要的执行步骤和可调用脚本。智能体在需要处理用户请求时会先从技能列表中匹配描述匹配成功后再读取对应的 Markdown 内容把技能说明注入当前上下文再决定如何执行。到这里可以看出两份文件不是在抢同一个位置。llms.txt更像网络资源的“索引卡片”服务于内容获取Skill.md更像工具使用手册服务于动作执行。1.3 为什么都是 Markdown而不是 JSON 或 YAMLMarkdown 有三个好处人类可以直接阅读沟通成本低模型的预训练语料里包含大量 Markdown模型更擅长理解它的结构解析成本低大多数语言都有现成解析器。不过 Markdown 也有缺点它不像 JSON 那样有强类型约束同一语义可以写成不同结构解析时容易出现偏差。因此使用这两份文件时不能只依赖格式还要在约定层保持命名和内容顺序稳定。2. llms.txt 做什么一个站点如何靠它被大模型快速理解2.1 llms.txt 是一种带约束的目录文件llms.txt的定位是“让大模型在获取网页时优先读取该站点的相关链接和摘要”。它的设计约束很明确文件不能太大避免大模型还没有读到正文就浪费了上下文链接必须选择性地给出优质内容不能把全站 URL 都塞进去内容不能依赖 JavaScript 渲染必须能通过一次 HTTP GET 直接拿到文本。它和robots.txt的区别在于robots.txt是给爬虫的访问许可声明llms.txt是给 LLM 的内容导航。它和sitemap.xml的区别在于sitemap.xml收录的是全部 URLllms.txt只给出“模型最该读的少数链接”并且每条链接都要有一句人话描述。社区常见的约定是把文件放在两个位置https://example.com/llms.txt和https://example.com/.well-known/llms.txt。如果你的站点项目支持静态文件托管可以直接在发布目录放一个llms.txt。2.2 一个完整的最小示例下面以文档站为例。这个站点的核心内容只有三块快速开始、配置参考、API 说明。对应内容如下# Example 文档中心 提供 Example 产品的安装、配置和 API 使用说明。 适合需要接入 Example SDK 的开发者阅读。 ## 重要链接 - https://example.com/docs/quickstart: 从零开始安装并运行 10 分钟示例 - https://example.com/docs/configuration: 环境变量、启动参数和常见配置说明 - https://example.com/api/reference: 完整的 API 字段、请求示例和错误码 ## 可选链接 - https://example.com/blog/roadmap: 产品路线图和里程碑介绍大模型读取这份文件时会把标题当成站点名称把引用块内容当成站点摘要把“重要链接”列表当成推荐入口。它可以先调用https://example.com/docs/quickstart获取具体页面也可以在回答用户问题时引用这些链接。注意几个容易被忽略的细节第一行#后面是页面标题不要省略。摘要放在引用块里模型更容易识别为说明性文字。“重要链接”分组用##开头链接格式为URL: 描述。部分实现约定“重要链接”必须放在“可选链接”之前。2.3 如何使用这份文件验证效果本地验证时先确认文件能被正常访问curl -s https://example.com/llms.txt | head -20再确认返回的是 Markdown 文本而不是登录页或 JSONcurl -sI https://example.com/llms.txt预期响应头应包含200 OK和Content-Type: text/markdown或text/plain。如果返回text/html说明静态服务器没有把.txt或.md当作纯文本返回需要检查 MIME 配置。用大模型验证时可以把 URL 直接放入提示词让模型先抓取llms.txt再回答。比如请先访问 https://example.com/llms.txt 了解文档结构 再回答这个产品支持哪些环境变量如果文件配置正确模型会优先从“重要链接”里找到配置说明页再读取具体内容。如果文件写得不好模型可能会陷入整站抓取既慢又容易跑题。2.4 llms.txt 的边界和常见坑这份文件不适合放什么不适合放所有页面链接不适合放需要登录才能访问的内部地址不适合放大量带状态参数的 API 地址也不适合放图片、视频、文件下载这类非文本资源。大模型拿到这些链接也无法直接产生答案。常见坑是相对链接。llms.txt文档规范推荐使用完整 URL因为模型不一定知道当前站点二级路径。另一个坑是文件内容过大。模型上下文有限一份 10 万字的llms.txt会挤占真正需要理解的内容。实际项目里建议把重要链接控制在 20 条以内每条描述尽量精简。3. Skill.md 是什么智能体如何靠它完成具体任务3.1 技能文件解决的问题是“大模型不知道怎么调用工具”假设你在做一个人力资源问答机器人用户问“帮我查一下员工张三的入职日期”。你不可能每次都在提示词里写一大段“先用 SQL 查数据库再调用查询接口再对结果做格式化”。这些操作可以抽象成一个技能查员工信息。技能文件里写清楚输入是什么、输出是什么、中间要调用哪个脚本、返回结果有哪些字段。此时Skill.md的作用就出现了。它通常不直接放业务逻辑而是描述业务逻辑的入口和步骤。一个完整的技能目录可以长这样skills/ ├── employee-query/ │ ├── SKILL.md │ └── scripts/ │ └── query_employee.py ├── expense-export/ │ ├── SKILL.md │ └── scripts/ │ └── export_excel.py每个技能目录都有一个SKILL.md脚本放在同级或子目录。智能体在运行时扫描所有技能目录读取每个SKILL.md的元信息决定什么场景调用什么技能。3.2 SKILL.md 的基本结构一个常见的SKILL.md结构可以分为三块文件头元信息、说明正文、执行步骤。--- name: employee-query description: 查询员工基础信息包括入职日期、部门、职级。适合招聘、HR、内部问答场景使用。 --- # 员工信息查询 在 user 需要查询员工信息时使用。查询前必须获得员工姓名或工号。 ## 输入 - employee_name: 字符串员工姓名 - employee_id: 可选员工工号 ## 步骤 1. 如果只有员工姓名先调用 scripts/query_employee.py --name 张三 --id-only 获取 employee_id。 2. 调用 scripts/query_employee.py --id 10001 查询详细信息。 3. 返回 JSON 字段employee_id, name, department, hire_date, level。 ## 输出示例 json { employee_id: 10001, name: 张三, department: 研发部, hire_date: 2022-03-15, level: P6 }智能体读取 description 后会判断“查一下张三的入职日期”是否可以匹配这个技能。如果匹配系统把 Markdown 内容注入上下文模型就能根据“步骤”决定执行顺序。这里有一个关键点SKILL.md 的主要读者是模型不是直接执行文件的 Shell。脚本能不能跑通取决于脚本自身和运行环境不取决于 Markdown 的标题层级。 ### 3.3 智能体是怎么“执行” SKILL.md 的 SKILL.md 本身不会被执行。它是一份说明文档。智能体拿到说明后要做的是 第一步扫描技能仓库里的所有 SKILL.md读取 YAML 文件头中的 name 和 description构建一个“技能名 技能描述”的索引。第二步根据用户请求和当前上下文从索引里找匹配度最高的技能。第三步把该文件的正文插入到模型上下文让模型知道技能的详细调用方式。第四步模型按正文中的指令调用脚本或工具并把结果反馈给用户。 所以真正被“执行”的是脚本而不是 Markdown。这也是为什么技能文件中要尽量把“可执行命令”放在代码块里而不是放在普通段落里。模型在解析时更容易识别代码块也就更不容易把自然语言当成命令执行。 ### 3.4 为什么不直接写 JSON 对比一下 JSON 形式 json { name: employee-query, description: 查询员工基础信息, steps: [获取 employee_id, 查询详细信息] }JSON 优点是不会出现歧义缺点是阅读性差写长步骤时要处理大量转义也不方便放示例。Markdown 能够把说明、命令、结果集放在同一份文件里既能给模型看也能给人做代码评审。实际项目里直接写 Markdown 更友好但要注意结构稳定不要频繁改动章节顺序否则模型每次读取时都可能产生不同理解。4. 核心对照内容索引 vs. 能力定义4.1 两文件速查对照表下面这个表可以直接用于方案选型对比维度llms.txtSkill.md / SKILL.md主要目的让大模型快速理解一个网站的内容结构让智能体知道如何执行一类技能典型位置站点根目录或.well-known目录技能仓库目录内服务对象LLM、RAG 工具、网页摘要工具Agent、Copilot、自动化工具体系文件内容站点标题、摘要、重要链接技能名称、描述、调用步骤、脚本说明是否可执行否只提供导航信息文件本身不执行但文件内的脚本可执行是否依赖服务端依赖 HTTP 服务器返回内容不依赖服务器依赖技能加载器更新频率网站结构调整时更新技能逻辑变化时更新失败表现模型抓不到核心页面回答变宽泛智能体匹配不到技能或错误调用脚本可以简单记成llms.txt告诉模型“这个网站有什么”Skill.md告诉智能体“这件事能怎么做”。4.2 什么时候只需要 llms.txt如果你的产品是文档站、开放 API 站点、公司官网需要让搜索引擎大模型或 RAG 工具更准确地抓取内容那只需要维护llms.txt。它能让模型在回答“这个 API 怎么用”时直接跳到正确页面。这里的核心目标是信息获取不需要执行脚本也不需要复杂的工具调用。4.3 什么时候只需要 Skill.md如果你构建的是内部机器人它的数据来源并不依赖公开网页而来自数据库、内部 API 或命令行工具那llms.txt基本派不上用场。你应该把精力放在技能仓库上把每个业务动作拆成技能并保证技能目录可被智能体稳定加载。4.4 什么场景两份文件要一起使用典型场景是“智能体既要浏览公开文档又要执行内部操作”。比如一个开发者助手它既需要查文档站里的配置说明又需要根据用户意图执行命令。此时文档站提供llms.txt让助手快速找到配置说明页面技能仓库提供Skill.md让助手知道如何执行构建命令、如何解析测试报告。两条技术路线各管一段并不重复。5. 重点解答skill.md 里面 # 后面的内容是不是不执行5.1 这个问题容易从哪里产生有开发者在写skill.md时看到类似下面的内容# 初始化数据库 mysql -u root -p init.sql # 启动服务 python app.py然后会产生疑惑# 初始化数据库和# 启动服务带了一个#号是不是不会执行如果把这段内容直接复制进 Shell答案确实是“不执行”因为#在 Shell 脚本里表示注释。mysql和python app.py前面的# 初始化数据库会被 Shell 当成注释忽略不会报错也不会产生任何效果。但这里面要分清楚两层意思。第一层如果整个skill.md被当成 Markdown 文档那么#是标题语法代表一级标题它决定文档结构不参与命令执行。第二层如果模型从skill.md中提取了 Markdown 代码块例如mysql -u root -p init.sql python app.py那么 Shell 的注释规则依然生效。只有真正以#开头的行会被当作注释mysql和python会被执行。5.2 Markdown 标题和 Shell 注释是两套语法需要区分两套完全不同的语法Markdown 中# 初始化数据库表示这是一个一级标题展示时字号变大、加粗。它表示文档层级不表示“忽略这行内容”。Bash 中# 初始化数据库表示注释Shell 不会执行这行内容。注释的作用是给阅读代码的人看。YAML 中# 初始化数据库表示注释解析 YAML 时会忽略。所以只看“执行不执行”判断依据是你把这段文本交给了谁。交给渲染器它是标题交给 Shell它是注释交给 YAML 解析器它也是注释。同一段文本在不同解释器里语义完全不同。skill.md本身是 Markdown所以它的正常身份是标题但如果模型把代码块抽出来给 Shell 执行里面的#行就被当成注释。5.3 正确写法命令放代码块说明放普通段落为了避免歧义在skill.md中要把“人类阅读的说明”和“模型需要执行的命令”分开。推荐写成## 初始化数据库 执行下面的命令用 init.sql 初始化本地数据库 bash mysql -u root -p init.sql启动服务初始化完成后使用下面的命令启动服务python app.py这种结构对模型最友好。模型读到“初始化数据库”的标题时理解这是步骤名称不会尝试把标题作为命令。它真正准备执行时会从 bash 代码块里提取命令。如果一段文字混在普通段落里模型可能无法判断该不该执行。 ### 5.4 另一个容易踩的坑# 后面的空格并不影响注释 Shell 注释语法允许 # 后直接跟内容也允许 # 后加一个空格再跟内容。两种写法的效果一样 bash # 这是注释 #这是注释 echo hello # 这行会执行 echo后面的内容是注释在skill.md的指令里如果命令行本身包含#要格外小心。例如curl http://example.com/api/report #v2这个命令在 Shell 中#v2会被当成注释实际请求的 URL 可能是http://example.com/api/report而不是带#v2的片段。模型从skill.md提取命令时也可能犯同样错误。正确做法是用引号包住 URLcurl http://example.com/api/report#v2这样#不会被 Shell 解释为注释。5.5 给智能体加载器的建议如果你在做自己的技能加载器不要直接对skill.md执行bash或者source。正确流程是先解析 Markdown 结构把代码块提取出来再根据代码块的语言标识决定执行方式。代码块语言标识为bash或shell的内容可以交给 Shell其他代码块按脚本逻辑处理。解析时不建议使用“正则提取所有行”的方式否则很容易把 Markdown 标题、表格、列表里的文本也当成命令。import subprocess import re # 这里只做示例说明代码块提取比直接执行整个 Markdown 更安全 code_blocks re.findall(r(?:bash|shell)\n(.*?), skill_content, re.S) for block in code_blocks: result subprocess.run(block, shellTrue, capture_outputTrue, textTrue) print(result.stdout)这段代码只提取 bash 代码块不会把# 标题当命令执行。当然生产环境不能这么简陋还需要处理超时、失败重试、日志记录和命令白名单。6. 在项目里落地和验证6.1 为网站添加 llms.txt 的步骤在网站根目录新增一个llms.txt直接提交到静态资源目录。内容控制在以下范围内站点标题、一段摘要、最重要的 5 到 20 个链接。如果站点有多个语言版本可以写多个llms.txt通过子路径区分。比如https://example.com/en/llms.txt和https://example.com/zh/llms.txt。不建议在同一个文件里混入所有语言的所有链接那会让模型很难判断优先级。验证时使用下面的命令curl -s https://example.com/llms.txt再检查响应头curl -sI https://example.com/llms.txt如果 Content-Type 不是text/markdown或text/plain要检查 Web 服务器的 MIME 配置。Nginx 下可以添加location /llms.txt { default_type text/markdown; }这样能避免服务器把.txt文件按application/octet-stream返回。6.2 建立技能仓库和 SKILL.md 的步骤技能仓库可以放在项目目录下但要注意路径大小写。有的加载器按SKILL.md全大写识别有的按skill.md全小写识别还有的会同时扫描两种情况。最低成本的做法是在一个技能目录里只放一个SKILL.md不要同时放skill.md和SKILL.md否则会让加载器产生歧义。技能文件的 description 字段很重要。它决定了智能体在候选技能中如何匹配所以要写成“用户请求中可能出现的关键词 技能动作 适用场景”的格式。比如description: 根据员工姓名或工号查询 HR 系统里的入职日期、部门、职级。适合查询组织架构、员工信息和内部信息变更场景。不建议写成description: 员工查询功能。因为描述太短时模型很难判断“张三的入职日期是什么”该不该匹配到这个技能。技能描述要写完整但也不需要写成一篇论文两到三句话足够。6.3 验证清单下表可以用作发布前检查检查项命令或方法预期结果llms.txt 可访问curl -I /llms.txtHTTP 200llms.txt 类型正确响应头text/markdown 或 text/plainllms.txt 链接有效随机抽取 3 个 URL 访问全部返回 200SKILL.md 能被扫描查看智能体日志能列出技能名称SKILL.md 描述匹配用典型问题触发能命中正确技能脚本参数正确运行脚本带--help参数说明正常特殊字符不误伤#出现在命令中命令按预期执行完成这份检查后基本可以确保文件和加载器之间的集成是通畅的。7. 常见问题与排查7.1 llms.txt 无法被大模型读取现象提示词里给出了llms.txt地址模型仍没有使用文件里的链接而是自己猜测 URL。可能原因文件地址写到了需要登录的域名下文件里使用了相对链接页面被重定向到了其他页面HTTP 头类型返回不是文本。排查先用curl -L跟随重定向查看最终地址再查看响应头 Content-Type再检查文件里的链接是否为完整 URL。修复后刷新浏览器缓存同时在提示词里明确要求“先读取 llms.txt再从文件给出的链接中获取内容”。7.2 技能文件没有进入候选列表现象智能体始终不调用某个技能即使任务描述完全匹配。可能原因description不够具体技能文件路径扫描规则不匹配文件头---没有正确闭合文件名大小写不对。排查先看智能体日志中扫描到的技能列表再确认SKILL.md是否在预期目录最后检查 YAML 文件头的name和description是否被正确解析。--- name: employee-query description: 查询员工基础信息 ---如果---前多了一个空格或文件名不是预期的SKILL.md都可能无法加载。7.3#注释导致命令执行异常现象模型从skill.md中提取命令时URL 里的#被截断或标题被当成命令执行。可能原因Markdown 代码块提取不严谨把自然语言段落当成命令行Shell 注释规则被忽略URL 没有加引号。排查在技能文件中检查所有命令行看是否包含裸#。如果命令中需要保留#必须用引号包裹。同时在加载器中确认只提取带bash或shell标识的代码块。7.4 文件更新后模型还在用旧内容现象llms.txt或SKILL.md已经修改模型依然按旧内容执行。可能原因HTTP 缓存智能体端对技能文件做了本地缓存文件内容更新时间没有变化。排查curl -s -H Cache-Control: no-cache https://example.com/llms.txt检查智能体日志中技能加载时间确认是否每次会话都重新扫描。技能仓库如果非常大建议在目录结构中加一个version字段并在更新文件时同步修改。7.5 两份文件同时使用时互相干扰现象智能体既读取了llms.txt又加载了技能文件结果在回答时把站点导航链接当成可执行命令。原因技能加载器把网页获取结果也当成了技能说明的一部分没有区分“内容上下文”和“工具技能”。解决在提示词工程里明确两类信息的分工。llms.txt的结果进入知识上下文Skill.md的结果进入能力上下文。不要让模型从普通的 Markdown 链接列表直接推断出可执行命令。8. 最佳实践与扩展方向8.1 写 llms.txt 时保持“少而精”llms.txt不是站点地图不要追求全量。真正值得进去的链接是那些“用户问题命中率最高”的页面。如果你的产品文档有搜索页、登录页、个人中心页这些都不应该出现在llms.txt里。维护时定期用访问日志看一下模型实际抓了哪些页面把高频页面提升到“重要链接”把低频页面移出文件。8.2 写 Skill.md 时严格区分“说明”和“执行”在技能文件里把所有可执行命令集中放到bash代码块中命令前后尽量不要用表格描述命令参数。测试时不要手动复制 Markdown 文本到终端执行应该用加载器跑一遍确保只有代码块内容被提取。8.3 生产环境必须考虑缓存和版本llms.txt和Skill.md都可能被模型服务端缓存。发布时不能只更新文件还要更新站点缓存或加载器缓存。技能仓库里建议添加变更记录## Changelog - 2025-03-01: 新增 employee-id 参数 - 2025-02-10: 修复脚本输出字段 department 缺失问题模型在匹配技能时如果看到明确的变更记录也可以减少歧义。8.4 两个方向值得继续关注第一个方向是内容导航的标准化。llms.txt只是其中一种尝试未来可能出现更多变体比如按模型能力拆分文件规模、按用户意图生成动态llms.txt。第二个方向是技能文件的自动发现和校验。现在技能加载器通常只做简单扫描未来可以引入 schema 校验、脚本单元测试和技能依赖管理把Skill.md当成一种轻量级“可插拔模块”来管理。回到最开始的问题skill.md里#后面的内容到底会不会执行答案取决于解释器。在 Markdown 里#是标题在 Shell 里#是注释在技能加载器里它取决于你如何提取和执行代码块。理解这份区别比记住一个简单结论重要得多。你可以在自己的项目里先分别跑通llms.txt和Skill.md再决定是否要同时维护两套文件。大多数文档型项目只需要llms.txt大多数工具型智能体只需要Skill.md只有把它们连成一个完整系统时才需要同时规划两者的内容边界。
返回列表