拆解 Agent Skill 运行机制:从“语义路由”到“渐进式披露”
你以为 Skill 只是一个文件夹其实它是 LLM 与外部世界之间的“惰性接口”。一、Skill 究竟是什么在开始之前我们先统一认知Skill 不是函数不是插件它是一个磁盘上的文件夹其标准结构如下my-skill/ ├── SKILL.md # 核心文件必须 ├── references/ # 参考文档可选 ├── scripts/ # 可执行脚本可选 └── assets/ # 静态资源可选其中SKILL.md是最关键的文件它包含两部分YAML 头由---包裹存放name和description等元数据。Markdown 正文存放详细的指令、工作流、引用说明等。这种文件化的形态决定了 Skill 的所有加载行为都是按需、惰性的 —— 这也是其核心优势所在。二、核心设计哲学渐进式披露Progressive DisclosureAgent 面临的矛盾很现实我们希望 Agent 拥有成百上千个 Skill以应对各种复杂任务。但 LLM 的上下文窗口有限即便 128K、1M 也经不起大量文本的堆砌。渐进式披露正是解决这一矛盾的关键思想Skill 的内容不是一次性全部塞给 LLM而是分层次、按需加载仅在需要时才进入上下文。具体来说Skill 被划分为三个信息层级层级内容加载时机Token 成本L1 索引层namedescriptionAgent 启动时~100 Token/个L2 指令层SKILL.md完整正文LLM 决定调用该 Skill 后几千 TokenL3 资源层references/、scripts/等LLM 按指令执行具体操作时按需读取脚本代码本身不进上下文下面我们通过一个具体案例来看这四个阶段是如何串联起来的。三、案例 Skillpdf-financial-analyzer我们有一个用于分析财报 PDF、提取关键财务指标并生成健康度评分的 Skill目录结构如下pdf-financial-analyzer/ ├── SKILL.md ├── references/ │ ├── gaap-standards.md # 美国通用会计准则参考 │ └── industry-benchmarks.md # 各行业财务基准值 ├── scripts/ │ └── extract_ratios.py # 提取并计算财务比率的脚本 └── assets/ └── report_template.json # 最终报告输出的 JSON 模板现在用户提问“分析这份财报 PDF提取关键财务比率并给出健康度评分。”接下来我们跟随 Agent 的视角走一遍完整调用链。四、完整调用链4 个阶段在看具体阶段前我们先通过一张全景流程图感受一下 L1、L2、L3 这三层信息是如何在不同时机被加载的阶段3按需执行L3加载阶段2指令注入L2加载阶段1语义路由description 匹配成功Read references/gaap-standards.mdBash scripts/extract_ratios.py阶段0启动索引L1加载Agent 启动Runtime 遍历磁盘 Skill 目录只读 SKILL.md 的 YAML 头name description拼接进系统提示词 L1 元数据层加载完成用户提问“分析这份财报PDF”LLM 语义匹配LLM 返回 tool_use 请求type: Skill, command: pdf-financial-analyzerAgent Runtime 拦截请求校验权限 读取磁盘完整 SKILL.md 正文追加为新的对话消息 L2 指令层加载完成LLM 按 SKILL.md 指令推理需要具体资源Runtime 读取文件内容Runtime 拉起子进程运行仅返回文本内容给 LLM仅返回执行日志/结果给 LLM L3 资源层加载完成LLM 整合结果并回答用户阶段 0启动扫描 —— 只读“身份证”Agent 启动时Runtime运行时会遍历所有 Skill 目录例如~/.agent/skills/*只解析每个SKILL.md的 YAML 头提取name和description。对于我们的案例YAML 头可能是这样的---name:pdf-financial-analyzerdescription:解析财报PDF自动提取三大报表数据计算流动比率、速动比率、毛利率等关键指标并与行业基准对比生成企业健康度评分。当用户提及“分析财报”、“PDF财务数据”、“财务比率”、“企业健康度”时使用。---随后Runtime 将所有 Skill 的name description拼接成系统提示词例如你有以下 Skill 可用 - pdf-financial-analyzer解析财报PDF自动提取三大报表数据... - xlsx处理 Excel 电子表格 - pptx创建 PowerPoint 演示文稿 ...关键点这一步成本极低每个 Skill 仅消耗 ~100 Token几十个 Skill 也不过几千 Token。Markdown 正文、scripts/、references/ 等全部按兵不动。阶段 1用户提问LLM 语义路由用户说“分析这份财报 PDF提取关键财务比率并给出健康度评分。”此时LLM 接收到的上下文 系统提示含 Skill 列表 当前对话。LLM 会在 Transformer 的前向传播中将用户的问题与各个 Skill 的description进行语义匹配。因为pdf-financial-analyzer的 description 明确提到了“财报PDF”、“财务比率”、“健康度”LLM 判断该 Skill 适合当前任务于是返回一个标准工具调用{type:tool_use,name:Skill,input:{command:pdf-financial-analyzer,args:分析这份财报 PDF提取关键财务比率并给出健康度评分}}重要澄清决定使用哪个 Skill不是关键词匹配或规则引擎而是LLM 自己基于语义理解做出的判断。因此description写得好不好直接决定 Skill 能否被正确触发。阶段 2Agent Runtime 拦截注入完整指令LLM 返回tool_use后Agent Runtime接管控制权执行以下操作校验检查pdf-financial-analyzer是否真实存在于磁盘。权限检查确认调用方配置中允许使用Skill工具。读取文件从磁盘读取SKILL.md的完整 Markdown 正文。注入上下文将完整的 Markdown 正文作为一条新的对话消息追加到 LLM 的上下文中注意不是修改系统提示词。此时LLM 手里拿到了类似这样的指令节选## Workflow 1. 调用 scripts/extract_ratios.py --pdf path 解析 PDF 中的三张表。 2. 按 references/gaap-standards.md 校验科目名称的合规性。 3. 计算核心指标 - 流动比率 流动资产 / 流动负债 - 速动比率 (流动资产 - 存货) / 流动负债 - 毛利率 (营收 - 成本) / 营收 4. 去 references/industry-benchmarks.md 查询同行业基准值。 5. 按 assets/report_template.json 格式输出结果。 ...关键点这一步是L2 指令层的加载只在 Skill 被真正调用时才发生。注入后的SKILL.md正文会一直留在对话上下文中供后续推理使用。阶段 3LLM 按指令干活按需碰附属文件现在 LLM 有了完整指令开始逐步执行执行脚本调用 Bash 工具运行scripts/extract_ratios.py --pdf quarterly_report.pdfRuntime 负责拉起子进程只将 stdout/stderr 回传给 LLM脚本源代码本身不进上下文。查阅标准当遇到不常见的财报科目时LLM 会Read references/gaap-standards.md进行核对。对比基准计算完比率后Read references/industry-benchmarks.md获取同行业平均水平。加载模板将assets/report_template.json作为输出结构模板确保输出格式统一。关键点这是L3 资源层的按需加载只有被SKILL.md正文引用到的文件才会被读取。scripts/ 下的代码完全不会进入 LLM 上下文这彻底绕过了 Token 限制也保护了代码隐私。阶段 4结果回传Skill 指令留在上下文脚本输出的财务比率、基准对比结论、健康度评分等都通过 Runtime 回传给 LLM。LLM 整合这些信息最终给用户一个完整的分析报告。而注入的SKILL.md正文依然留在上下文中如果用户追问“把存货周转率也加上”LLM 仍然记得指令中的扩展点可以无缝继续。如果对话过长触发截断策略或者用户明确切换任务Skill 的正文才可能被压缩或移除。五、核心角色Agent Runtime从上文可以看出在整个调用链中LLM 只负责“想”和“说”语义判断 返回 tool_use而Agent Runtime 负责“做”。为了更清晰地展示两者之间的交互边界我们通过时序图来看一次完整的调用过程 磁盘文件系统 Agent Runtime躯体 LLM大脑 用户 磁盘文件系统 Agent Runtime躯体 LLM大脑 用户阶段0启动时极低成本阶段1提问与决策阶段2拦截与注入L2阶段3按需执行L3遍历 skills/解析 YAML 头返回 name description注入系统提示仅 Skill 列表“分析这份财报PDF”语义比对 description返回 tool_use (Skill, commandpdf-financial-analyzer)读取完整 SKILL.md 正文返回 Markdown 指令追加为新的对话消息注入操作手册按手册要求执行 scripts/extract_ratios.py拉起子进程运行代码不进上下文返回 stdout/stderr 日志回传执行结果仅文本输出最终财务分析报告与健康度结论Agent Runtime 的具体职责包括启动时扫描并构建索引拦截工具调用并路由从磁盘读取文件并注入上下文执行子进程并捕获输出管理会话状态和上下文生命周期正是 Runtime 的存在使得 Skill 能像“插件”一样挂载又不会像传统函数那样提前占用上下文。六、为什么 YAML 头如此重要YAML 头是 Skill 的“机器可读摘要”它的作用不可替代极低成本建索引启动时只读这几十个字节不读正文省 Token。标准化解析YAML 格式让 Runtime 可以轻松提取字段无需 NLP。语义路由依据LLM 完全依靠description来判断何时调用该 Skill。写得好触发精准写得差形同虚设。因此写好description是一门学问建议包含触发场景、适用任务类型、关键词等。七、总结与思考核心机制一句话概括Skill 的运行机制 启动时扫索引L1 调用时注指令L2 执行时取资源L3 代码永不进上下文。这一设计带来的优势优势说明海量技能可拥有数百个 Skill启动成本仅线性增长每个 ~100 Token。上下文干净只有被调用的 Skill 指令才会进入上下文避免无关信息干扰。代码隐私脚本源码留在磁盘不给 LLM 看保护知识产权。灵活扩展新增 Skill 只需放一个文件夹无需修改 Agent 核心代码。一点延伸思考这种“目录即接口”的设计其实与微服务架构中的“服务发现”有异曲同工之妙。未来Skill 或许会成为 Agent 生态中的“标准化容器”让跨 Agent 的技能共享变得更加容易。本文案例代码均为示意实际 Skill 可根据需要封装任意复杂度的工具链。