拆解 Agent Skill 运行机制:从“语义路由”到“渐进式披露”
你以为 Skill 只是一个文件夹?其实它是 LLM 与外部世界之间的“惰性接口”。
一、Skill 究竟是什么?
在开始之前,我们先统一认知:Skill 不是函数,不是插件,它是一个磁盘上的文件夹,其标准结构如下:
my-skill/ ├── SKILL.md # 核心文件(必须) ├── references/ # 参考文档(可选) ├── scripts/ # 可执行脚本(可选) └── assets/ # 静态资源(可选)其中,SKILL.md是最关键的文件,它包含两部分:
- YAML 头(由
---包裹):存放name和description等元数据。 - Markdown 正文:存放详细的指令、工作流、引用说明等。
这种文件化的形态,决定了 Skill 的所有加载行为都是按需、惰性的 —— 这也是其核心优势所在。
二、核心设计哲学:渐进式披露(Progressive Disclosure)
Agent 面临的矛盾很现实:
- 我们希望 Agent 拥有成百上千个 Skill,以应对各种复杂任务。
- 但 LLM 的上下文窗口有限(即便 128K、1M 也经不起大量文本的堆砌)。
渐进式披露正是解决这一矛盾的关键思想:Skill 的内容不是一次性全部塞给 LLM,而是分层次、按需加载,仅在需要时才进入上下文。
具体来说,Skill 被划分为三个信息层级:
| 层级 | 内容 | 加载时机 | Token 成本 |
|---|---|---|---|
| L1 索引层 | name+description | Agent 启动时 | ~100 Token/个 |
| L2 指令层 | SKILL.md完整正文 | LLM 决定调用该 Skill 后 | 几千 Token |
| L3 资源层 | references/、scripts/等 | LLM 按指令执行具体操作时 | 按需读取,脚本代码本身不进上下文 |
下面我们通过一个具体案例,来看这四个阶段是如何串联起来的。
三、案例 Skill:pdf-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 这三层信息是如何在不同时机被加载的:
阶段 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 能否被正确触发。
阶段 2:Agent 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正文会一直留在对话上下文中,供后续推理使用。
阶段 3:LLM 按指令干活,按需碰附属文件
现在 LLM 有了完整指令,开始逐步执行:
- 执行脚本:调用 Bash 工具运行
scripts/extract_ratios.py --pdf quarterly_report.pdf,Runtime 负责拉起子进程,只将 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 的具体职责包括:
- 启动时扫描并构建索引
- 拦截工具调用并路由
- 从磁盘读取文件并注入上下文
- 执行子进程并捕获输出
- 管理会话状态和上下文生命周期
正是 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 可根据需要封装任意复杂度的工具链。
