返回课程首页

16

为什么 Agent 需要 Skill

区分 Skill、Tool、System Prompt 与 Extension。

源码基线:Pi v0.82.0 · commit 518855d

假设桌面 Agent 已经拥有 readbashedit 等工具。现在希望它学会“代码审查”:

每次审查都先理解变更目标,再检查正确性、安全、测试和兼容性,最后按严重度给出带源码位置的结论。

工具能让 Agent 读取和修改代码,却不会自动教会它怎样做一次高质量审查。把完整流程塞进 System Prompt 又会让所有请求长期携带一大段无关规则。

Skill 用来填补这个空白:它把某类任务的方法、流程和专业知识包装成可按需加载的能力包。

1. 从一个失败的设计开始

最直接的做法是把规则写进 System Prompt:

你是桌面 Agent。
做代码审查时必须:
1. 阅读变更目标……
2. 检查……
3. 运行……
(还有 200 行)

这会产生三个问题:

  1. 用户只是问一句普通问题,也要发送全部审查规则;
  2. 多种专业能力会让 Prompt 越来越大;
  3. 更新审查流程必须修改 Agent 的全局身份配置。
flowchart LR
    P["固定 System Prompt"] --> C1["普通对话也携带审查规则"]
    P --> C2["能力越多,Context 越大"]
    P --> C3["难以独立版本化和分发"]

Skill 把“始终知道有这项能力”和“真正使用完整流程”分开。

2. Pi 对 Skill 的定义

Pi 文档把 Skill 定义为按需加载的、自包含的能力包。它可以包含:

  • 专业工作流;
  • 环境准备说明;
  • 辅助脚本;
  • 参考文档;
  • 模板和静态资源。

一个典型目录:

code-review/
├── SKILL.md
├── scripts/
│   └── collect-diff.sh
├── references/
│   └── security-checklist.md
└── assets/
    └── review-template.md

其中只有 SKILL.md 是核心入口,其余结构由 Skill 自己约定。

3. Skill 的两阶段上下文

Pi 启动时不会把所有 SKILL.md 正文都放进 System Prompt。Resource Loader 先加载元数据; 只有 read 是 Active Tool,且 Skill 没有设置 disable-model-invocation 时,这份元数据才会 进入模型可见的 System Prompt 目录:

<available_skills>
  <skill>
    <name>code-review</name>
    <description>审查代码变更……</description>
    <location>/.../code-review/SKILL.md</location>
  </skill>
</available_skills>

任务匹配后,Agent 才用 read 读取完整文件。以下自动匹配链都以这两个可见性条件为前提。

sequenceDiagram
    participant L as Resource Loader
    participant P as System Prompt
    participant A as Agent
    participant F as SKILL.md

    L->>P: 可见 Skill 的名称 + 描述 + 位置
    P->>A: 可用能力目录
    A->>A: 当前任务匹配 code-review
    A->>F: read 完整指令
    F-->>A: 工作流、边界、资源引用

这叫 Progressive Disclosure,渐进式披露。

4. 它怎样减少长期 Context

假设有 20 个 Skill,每个正文 1500 Token:

全部常驻:约 30,000 Token
只放元数据:远小于完整正文
实际任务:再加载匹配 Skill 的正文

Pi 的源码体现了这个分层:

  • loadSkillFromFile() 解析 Frontmatter,形成轻量 Skill 元数据;
  • read 且 Skill 允许模型调用时,formatSkillsForPrompt() 才把 namedescriptionlocation 输出到 System Prompt;
  • 模型自动选择时,再通过 read 获取全文;
  • 用户显式输入 /skill:name 时,AgentSession 才读取正文并展开到输入。

节省的不是文件存储,而是每次模型请求都要携带的长期上下文。

5. Skill 不是 Tool

Tool 是可执行接口:

{
  name: "read",
  description: "Read a file",
  parameters: { /* JSON Schema */ },
  execute: async (...) => { /* ... */ },
}

Skill 通常是一份给模型阅读的 Markdown 工作流。它自身没有统一的 execute()

Tool:能做什么动作
Skill:遇到这类任务时,应该怎样组织动作

代码审查 Skill 可能要求依次使用:

  • read 理解代码;
  • bash 运行静态检查;
  • 搜索工具找调用方;
  • 最后输出审查结论。

真正执行副作用的是 Tool,不是 Skill 文件。

6. Skill 也不只是“一段 Prompt”

从传给模型的角度,SKILL.md 的正文确实会成为上下文。但把 Skill 简化为 Prompt 片段会漏掉 它的包结构和运行约定:

  • 它有可发现的名称和描述;
  • 它有自己的基准目录;
  • 相对路径必须从 Skill 目录解析;
  • 它可以引用脚本、模板和长篇参考资料;
  • 它能单独安装、启用、诊断和分发;
  • 它可以选择不允许模型自动调用。

因此更准确的理解是:

Skill 是以指令为入口、可携带资源的能力包。

7. Skill 与 System Prompt 的区别

对比 System Prompt Skill
作用范围 Agent 的基础身份和长期规则 某类任务的专业方法
加载方式 每次请求都在 Context 中 可见元数据随 System Prompt 常驻,正文按需
数量 通常一个动态构建结果 可以有很多
复用方式 通过配置组合 能作为独立目录或包分发
典型内容 角色、安全边界、环境 步骤、检查单、脚本和模板

项目中“禁止修改生产密钥”是长期约束,适合 AGENTS.md 或 System Prompt;“怎样审查 OAuth 回调”是特定任务方法,更适合 Skill。

8. Skill 与 Extension 的区别

Extension 会进入 coding-agent 的运行机制,可以注册:

  • Tool;
  • Command;
  • Event Handler;
  • Provider;
  • 资源发现逻辑;
  • Prompt 改写逻辑。

Skill 默认只是模型可读取的工作流,不直接获得这些运行时钩子。

flowchart TD
    NEED["新增能力需求"] --> Q{"需要改变运行机制吗?"}
    Q -->|"只需要方法、知识、步骤、资源"| S["Skill"]
    Q -->|"需要事件、命令、工具或生命周期控制"| E["Extension"]
    E --> ES["Extension 也可以分发 Skill"]

如果“错误日志分析”只需要读取日志、识别模式和输出报告,用 Skill 即可。若需要持续订阅 系统日志、注册新的流式工具或拦截 Provider 请求,则需要 Extension。

9. 四种机制放到同一个例子

桌面 Agent 要做安全代码审查:

机制 在例子中的职责
System Prompt 遵守项目规则,不泄露密钥,高风险操作先确认
Tool 读文件、搜索引用、执行测试
Skill 安全审查步骤、威胁清单、报告格式
Extension 在工具调用前做审批,注册内部扫描器,记录审计事件

它们不是互斥方案,而是不同层次的组合。

10. 自动匹配依赖 Description

read 已启用且 Skill 允许模型调用时,Pi 才在 System Prompt 中暴露它的 Description, 并告诉模型:

任务与描述匹配时,用 read 加载 Skill 文件。

因此 Description 不是给人看的装饰,而是模型的路由信息。

较差:

description: Helps with reviews.

较好:

description: Reviews code changes for correctness, security,
  regressions, and missing tests. Use when asked to review a
  diff, pull request, patch, or implementation.

后者同时说明“做什么”和“什么时候用”,模型更容易正确选择。

11. 自动选择不是确定性路由

模型看到元数据后是否真的调用 read,仍是模型行为。Pi 文档明确提醒:模型并不总会自动 加载匹配 Skill。

所以宿主需要区分:

  • 自动建议:让模型依据 Description 判断;
  • 显式调用:用户输入 /skill:name
  • 产品强制:宿主在已知工作流中直接把 Skill 内容加入输入或用 Extension 约束。

Skill 本身不是一个保证百分之百命中的规则引擎。

12. 显式调用发生了什么

用户输入:

/skill:code-review 检查当前改动

AgentSession._expandSkillCommand()

  1. 按名称查找已加载 Skill;
  2. 同步读取 Skill 文件;
  3. 去掉 Frontmatter;
  4. 包装成带名称和位置的 <skill> Block;
  5. 告诉模型相对引用的基准目录;
  6. 把用户参数附在 Block 后。

这条输入随后作为普通 User Message 进入 Agent。

显式调用和自动匹配的差别是:前者由 AgentSession 展开正文,后者由模型看到目录后自行 调用 read

13. disable-model-invocation

Skill Frontmatter 可以设置:

disable-model-invocation: true

formatSkillsForPrompt() 会过滤这类 Skill,所以模型不会在可用目录中看到它;用户仍可通过 /skill:name 显式调用。

适用场景:

  • 可能产生昂贵操作;
  • 只有用户明确要求才能执行;
  • Description 容易与普通任务误匹配;
  • 需要把启动权保留给 UI 或用户。

这只是隐藏自动发现,不是安全沙箱。Skill 仍可能指导模型执行高风险 Tool。

14. Skill 是能力分发机制

桌面 Agent 通常有多个项目、团队和角色。Skill 的文件包形式带来:

  • 用户级复用:个人所有项目可用;
  • 项目级共享:随仓库分发团队流程;
  • 包级分发:Extension 或 npm Package 携带 Skills;
  • 显式路径:宿主按配置装配能力;
  • 跨 Harness 复用:符合 Agent Skills 约定的目录可以被其他实现读取。

它比修改主程序更轻,也比复制粘贴 Prompt 更容易追踪来源。

15. 项目级 Skill 的信任边界

Skill 可以指示模型运行脚本,也可能包含恶意指令。Pi 文档要求在使用前审查内容。

项目级 .pi/skills 和祖先 .agents/skills 只有在项目受信任后才会作为自动资源进入。 但“被加载”不等于“安全”:

  • 脚本仍应经过审查;
  • Tool 权限仍应最小化;
  • 高风险副作用仍应审批;
  • 不应把 Skill 当作越过 Tool 安全策略的通道。
flowchart LR
    SK["Skill 指令"] --> MODEL["模型决定调用 Tool"]
    MODEL --> POLICY["宿主权限 / 审批"]
    POLICY -->|允许| TOOL["Tool 执行"]
    POLICY -->|拒绝| STOP["停止并说明"]

16. 一个桌面 Agent 的使用过程

用户问:

请审查我刚改的登录逻辑。

完整过程可以是:

  1. read 已启用,且 System Prompt 中已有可见 code-review 的名称、描述和位置;
  2. 模型判断任务匹配;
  3. 模型调用 read 读取 SKILL.md
  4. Skill 要求先读取 Diff 和认证模块背景;
  5. 模型使用现有 Tool 收集证据;
  6. Skill 引用安全检查单,模型按需继续读取;
  7. 模型按规定格式输出问题;
  8. 整个过程仍运行在同一个 Agent Loop 和 Session 中。

Skill 没有创建第二个 Agent,也没有绕过对话历史。

17. 什么时候不该创建 Skill

以下情况通常不需要 Skill:

  • 一次性、很短的用户要求:直接 User Prompt;
  • 所有任务都必须遵守的规则:System Prompt 或项目 Context;
  • 需要新的外部动作:Tool;
  • 需要拦截生命周期或修改运行时:Extension;
  • 只有一条简单固定模板:Prompt Template 可能更直接。

判断标准不是“内容能否写成 Markdown”,而是它是否代表可复用的特定任务方法。

18. 常见误区

“装了 Skill 就新增了一个 Tool”

错误。Skill 通常指导模型使用已有 Tool。

“所有 Skill 正文都会常驻 Context”

错误。默认常驻的是轻量目录,正文按需加载。

“Description 随便写也没关系”

错误。自动匹配主要依据 Description。

“Skill 自动选择是确定性的”

错误。它仍依赖模型判断;显式调用更确定。

“disable-model-invocation 能阻止执行”

错误。它只把 Skill 从模型可见目录中隐藏,仍允许显式调用。

“Skill 可以替代 Tool 的权限控制”

错误。Skill 是指令,安全边界仍必须在 Tool 和宿主层实施。

19. 本章小结

  • Skill 解决“如何完成某类任务”,Tool 解决“能执行什么动作”;
  • Skill 是带元数据、相对路径和资源的能力包,不只是散落的 Prompt;
  • Pi 在 read 启用且 Skill 允许模型调用时,用可见元数据常驻、正文按需读取实现渐进式披露;
  • Description 是自动匹配的路由信息,但模型选择不是确定性规则;
  • /skill:name 会由 AgentSession 显式读取并展开正文;
  • disable-model-invocation 隐藏自动发现,不构成安全权限;
  • System Prompt、Tool、Skill、Extension 各自承担不同层次;
  • 用户级、项目级和 Package Skill 让它适合作为桌面 Agent 的能力分发方式;
  • Skill 可能携带可执行脚本,必须结合项目信任、Tool 权限与审批。

20. 自测

  1. 为什么不应把所有专业流程都写进 System Prompt?
  2. Skill 与 Tool 的根本区别是什么?
  3. Pi 的渐进式披露包含哪两个阶段?
  4. Description 为什么会影响自动触发?
  5. 自动匹配与 /skill:name 在正文加载上有什么区别?
  6. disable-model-invocation 能否作为权限控制?
  7. 什么需求更适合 Extension 而不是 Skill?
  8. 项目已经受信任后,为什么仍要审查 Skill?