Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Skills:不是代码,是按需加载的文档

TL;DR:Skill 是一个带 frontmatter 的 SKILL.md(可附带脚本、参考资料、模板),Pi 实现的是 Agent Skills 标准。它的核心是渐进披露:启动时只把每个 skill 的名字、描述和文件路径放进 system prompt,模型判断任务匹配时再用 read 工具读全文,用户也可以用 /skill:name 强制加载。所以 skill 再多也只占几行 prompt。它没有执行入口、没有 handler:extension 改系统的行为,skill 改模型的行为,prompt template 只是复用一段用户输入。

它解决什么问题

有些工作需要一大段专门的指导:怎么做 PDF 抽取、团队的发布流程、某个内部框架的坑。把这些都写进 AGENTS.md 或 system prompt,每次请求都要背着它们,大部分时候用不上,还会稀释模型对真正相关指令的注意力;写成扩展又太重,这些内容本质上只是“给模型看的说明书”。

Skill 的做法是把说明书放在磁盘上,只在 prompt 里留一个目录:

<available_skills>
  <skill>
    <name>pdf-tools</name>
    <description>Extract text and tables from PDF files. Use when reading, converting, or inspecting PDFs.</description>
    <location>/home/me/.pi/agent/skills/pdf-tools/SKILL.md</location>
  </skill>
</available_skills>

列表前面有几行固定说明:这些 skill 提供特定任务的指令,任务匹配描述时用 read 工具加载它的文件,文件里的相对路径按 skill 所在目录解析、调用工具时用绝对路径。最后这条容易被忽略却很重要,skill 常常引用同目录下的脚本和模板。这段内容是 system prompt 的 skills 段(整体结构见 资源发现、项目信任与系统提示词),只有模型手里有 read 或 bash 这类能读文件的工具时才会加入。

一句话:description 是路由信息,正文是按需加载的内容;prompt 里只放前者。

一次使用的过程

假设 ~/.pi/agent/skills/pdf-tools/ 里有 SKILL.md、scripts/extract.sh 和 references/formats.md,你对 Pi 说“把 report.pdf 里的表格转成 CSV”:

  1. 启动时,ResourceLoader 扫描 skill 目录,解析每个 SKILL.md 的 frontmatter,校验后得到 { name, description, filePath, baseDir },全文并不读进 prompt。
  2. 构建 prompt 时,可见的 skill 被格式化成上面的 <available_skills> 列表。
  3. 模型推理时,看到任务和 pdf-tools 的描述匹配,于是发起一次普通的 read 工具调用读取 SKILL.md。这和读任何源码文件没有区别,读到的内容作为 tool result 进入上下文。
  4. 模型按说明执行:先读 references/formats.md,再用 bash 跑 scripts/extract.sh,路径都按 skill 目录解析。

Pi 在这里几乎什么都没做:没有专门的“加载 skill”工具,没有运行时状态,“加载”就是模型自己调用 read。这既是它简单的地方,也是它的局限:模型可能没意识到该加载某个 skill。这时用户可以显式调用:

/skill:pdf-tools extract report.pdf

AgentSession 在用户输入进入 agent 之前把它展开:读出 SKILL.md,去掉 frontmatter,包成 <skill name="..." location="...">References are relative to <目录>. ...正文...</skill>,参数作为用户请求附在后面,整体成为一条 user 消息。展开发生在扩展的 input 事件之后、prompt template 展开之前。

放在哪里会被发现

位置范围说明
~/.pi/agent/skills/用户Pi 自己的约定目录
~/.agents/skills/用户Agent Skills 标准的通用位置,可与其他 agent 工具共享
<cwd>/.pi/skills/项目需要项目信任
.agents/skills/(cwd 及其祖先,到仓库根为止)项目需要项目信任
settings 里的 skills 数组、Pi 包、--skill <path>显式包用于分发,CLI 用于临时试用

Pi 不会去扫描 .claude/skills/;想复用这类目录,可以把路径加进 settings 的 skills 数组,或建符号链接(同一文件经符号链接出现两次会被去重)。

目录扫描的规则:一个目录里有 SKILL.md 就把它当作 skill 根,不再往下找;否则递归子目录(跳过隐藏目录和 node_modules,遵守 ignore 文件)。~/.pi/agent/skills、.pi/skills 和包的 skills/ 根目录下的独立 .md 文件也能作为 skill,但 .agents/skills 根下的会被忽略,可移植的形式始终是“目录 + SKILL.md”。

多个来源出现同名 skill 时“先到先得”,顺序是 CLI > 项目 > 用户 > 包,后来者被丢弃并产生 collision 警告。所以项目里的同名 skill 会覆盖你的全局 skill。项目 skill 可以指示模型运行脚本、修改文件,这也是它被纳入项目信任的原因。

Frontmatter 与校验

---
name: pdf-tools
description: Extract text and tables from PDF files. Use when reading, converting, or inspecting PDFs.
disable-model-invocation: false
---

# PDF tools

Read `references/formats.md` before converting a document. Run scripts relative to this skill directory.
  • name:命令名和显示名。小写字母、数字和连字符,不能以连字符开头或结尾、不能连续连字符,最多 64 字符。省略时用父目录名。标准建议与目录名一致,Pi 不强制。
  • description:唯一的路由依据,最多 1024 字符。要同时写清“做什么”和“什么时候用”,“Helps with PDFs” 这种描述几乎无法帮助模型决策。
  • disable-model-invocation: true:不进入 <available_skills>,模型看不到,只能通过 /skill:name 手动触发。适合只在特定场合才有意义或带敏感操作的 skill。
  • 标准里还有 license、compatibility、metadata、allowed-tools(实验性)等字段。

校验是宽松的:名字不合规、描述过长等大多只产生警告,skill 照常加载;只有 SKILL.md 解析失败或缺少 description 时才不加载。这和 ResourceLoader 整体“单个资源出错不阻塞启动”的原则一致。enableSkillCommands 设置控制 /skill:name 是否出现在命令补全里,手动输入始终有效;编辑 skill 后用 /reload 生效。

Skill、Prompt template、Extension 怎么选

Prompt templateSkillExtension
本质一段可带参数的用户输入给模型读的指令文档和配套文件在 Pi 进程里执行的 TS 模块
谁触发用户输入 /name模型按描述自行加载,或用户 /skill:name事件、命令、工具调用
进入上下文的方式展开成一条 user 消息prompt 里只有目录,全文按需作为 tool result 进入不直接进入,通过注册的工具、消息、prompt 段间接影响
能做什么复用措辞教模型一套流程,可附脚本让它通过 bash 执行新工具、拦截与改写、UI、provider、权限控制
典型例子/review:审查暂存的改动pdf-tools、发布流程、TDD 工作流权限确认、子 agent、MCP 接入

经验法则:只是想少打字,用 prompt template;需要“比一段 prompt 多、但不需要新的执行能力”的专门知识,用 skill;需要改变 Pi 本身的行为或接入新能力,用 扩展。

取舍

得到的:prompt 体积与 skill 数量基本无关;格式就是 Markdown 加标准 frontmatter,能跨 agent 工具复用;没有运行时、没有 API,写一个 skill 的门槛几乎为零。

放弃的:是否加载完全取决于模型对 description 的判断,描述写得不好就会漏用或误用;加载后的全文占用上下文,且和普通 tool result 一样可能被后续 compaction 总结掉;skill 里的脚本靠模型用 bash 执行,没有额外的隔离,安全性等同于让模型直接跑命令。

常见追问

为什么不在启动时把所有 skill 全文放进 prompt? 大多数任务只用到其中一两个,全文注入会让每次请求都背着大量无关指令,既费 token 又分散模型注意力;渐进披露把成本推迟到真正需要的时候。

为什么不提供一个专门的 load_skill 工具? 读 skill 和读普通文件是同一件事,已有的 read 工具足够,skill 里的相对资源也要靠 read/bash 访问。少一个工具,prompt 和协议都更简单,也更符合 Pi 的极简内核。

/skill:name 和模型自己 read 有什么区别? 结果相似,都是把 SKILL.md 正文放进上下文;区别在于前者是用户确定性地注入(作为 user 消息,附带目录说明和参数),后者依赖模型判断、以 tool result 形式进入。

源码入口(v1.5):packages/coding-agent/src/core/skills.ts、packages/coding-agent/src/core/system-prompt.ts、packages/coding-agent/src/core/agent-session.ts(_expandSkillCommand)、packages/coding-agent/src/core/package-manager.ts、packages/coding-agent/docs/skills.md