最近在折腾 Pi 的 subagent。
一开始我对 Pi Package 的理解非常朴素:
pi install npm:my-subagent
那大概就是把一个插件安装到了:
~/.pi/agent/extensions/...
然后 Pi 启动的时候扫一下这个目录,于是模型就知道:
哦,我现在有一个 subagent。
后来仔细看了一遍 Pi 的 package、extension、skill 和 settings 机制,发现这个理解基本上把几个不同层次的东西揉到了一起。
真正有意思的问题其实不是:
pi install把文件放哪了?
而是:
一个 npm package,是怎么一步一步变成 LLM 眼里的一个能力的?
这篇就把这条链路完整拆一下。
1. 先说一个反直觉的结论
执行:
pi install npm:my-subagent
之后,模型其实并不知道你安装了一个叫 my-subagent 的 package。
甚至可以说:
LLM 根本不需要知道 package 这种东西存在。
Package 是 Pi Host 层的概念。
LLM 最终看到的是另外一些东西:
Tools
Skills
System Prompt / Instructions
Context
所以整个问题实际上分成四层:
理解 Pi Package,最重要的就是不要把这几层混在一起。
2. pi install 到底安装了什么?
我们先从最外层开始。
比如我写了一个 package:
my-subagent/
├── package.json
├── extensions/
│ └── subagent.ts
└── skills/
└── delegation/
└── SKILL.md
然后发布 npm:
npm publish
用户安装:
pi install npm:my-subagent
Pi Package 是一个可以一起分发多种资源的单元,包括:
extensions
skills
prompts
themes
它本质上仍然是一个普通目录或者 npm package。
这里第一个很容易产生的误区是:
Pi Package == Extension
其实不是。
更准确地说:
Package 更像是一个 distribution container。
Extension、Skill 等才是里面真正能够被 Pi 消费的 Resource。
3. 那全局 package 到底记录在哪里?
我们这篇只讨论 global package。
执行:
pi install npm:my-subagent
以后,Pi 会把这个 personal/global package declaration 写进:
~/.pi/agent/settings.json
概念上可以理解成:
{
"packages": [
"npm:my-subagent"
]
}
注意这里有一个很重要的变化。
以前我的脑子里是:
但实际上,更准确的模型是:
Pi 的 settings 里有 package declarations,同时 Pi 的 Resource System 又分别认识:
extensions
skills
prompts
themes
这已经暗示了一件事情:
Package 和 Resource 是两个层次。
4. Package 不需要被复制进 extensions/
比如:
my-subagent/
├── package.json
├── extensions/
│ └── subagent.ts
└── skills/
└── delegation/
└── SKILL.md
Pi 可以直接从 package 内部发现这些资源。
默认情况下,它认识这些 conventional directories:
extensions/
skills/
prompts/
themes/
也可以在 package.json 里面显式声明:
{
"name": "my-subagent",
"pi": {
"extensions": [
"./extensions/subagent.ts"
],
"skills": [
"./skills"
]
}
}
所以:
~/.pi/agent/extensions/
和:
npm:my-subagent
└── extensions/subagent.ts
其实是两种不同的 Resource 来源。
所以 package 安装以后,你跑:
ls ~/.pi/agent/extensions
没看到 my-subagent,完全正常。
它根本没有必要出现在这里。
5. 真正关键的问题来了
到这里其实只解决了:
Pi 在磁盘和配置层面怎么知道这个 package。
但这不是我们真正关心的问题。
真正有意思的是:
我新开一个 Pi session,它怎么知道我以前装过这个 package?
以及更进一步:
LLM 又是怎么知道自己有一个 subagent 可以调用的?
这两个问题其实还不是同一个问题。
6. 每次 Pi 启动,它都会重新建立 Resource 世界
假设现在:
~/.pi/agent/settings.json
里面已经有:
npm:my-subagent
我随便找一个项目:
cd ~/projects/foo
pi
新的 Pi process 启动。
这时候可以把启动过程抽象成:
也就是说:
Package 并不是写进某个 session 的。
它属于 user/global configuration。
每一个新的 Pi runtime 都会从这个全局配置重新建立自己的 Resource 集合。
所以不是:
install package
│
├── modify session A
├── modify session B
└── modify session C
而是:
这就是为什么一个 global package 会自然出现在之后启动的各个 Pi 会话里。
7. 但是,到这里 LLM 仍然不知道它存在
这是整个问题最容易漏掉的一层。
到目前为止发生的一切:
settings.json
package resolution
resource discovery
extension loading
都发生在:
Pi Host / Runtime
这一侧。
LLM 不会自己:
ls ~/.pi/agent
也不会:
cat package.json
更不会主动扫描:
node_modules
所以:
Pi runtime 知道 package
并不等于:
LLM 知道 package
中间还差了一座桥。
8. 这座桥就是 Extension 注册出来的能力
假设我们的:
extensions/subagent.ts
大概做这么一件事:
export default function (pi) {
pi.registerTool({
name: "subagent",
description:
"Delegate a task to a specialized subagent",
parameters: {
// ...
},
execute: async (...) => {
// spawn / run subagent
}
})
}
Pi 加载这个 extension。
extension 执行:
pi.registerTool("subagent")
于是当前 Pi runtime 原本可能有:
read
bash
edit
write
现在变成:
read
bash
edit
write
subagent
这里发生了一次非常关键的语义转换:
到了这里,npm package 已经不重要了。
现在 Pi Runtime 里真正存在的是:
Tool: subagent
这才是真正能够继续进入 Model 世界的东西。
9. LLM 最终看到的是什么?
假设 Pi 下一次向模型发请求。
模型收到的东西,可以粗略理解成:
System Prompt
Conversation
Available Tools
其中 Available Tools 现在包含:
read(...)
bash(...)
edit(...)
write(...)
subagent({
task: string,
...
})
并且带有 description:
Delegate a task to a specialized subagent.
于是模型终于知道:
我有一个叫
subagent的工具。
注意它知道的不是:
用户安装了 npm package
my-subagent。
而是:
当前环境提供了一个
subagenttool。
这两个说法看起来很接近,实际上是完全不同的抽象层。
这个边界非常重要。
10. 所以 Package 本身对 LLM 是透明的
站在模型视角:
npm package name?
不知道。
安装路径?
不知道。
是不是从 GitHub clone 的?
不知道。
是不是 ~/.pi/agent/extensions 里的?
不知道。
是谁 install 的?
也不重要。
模型真正关心的是:
What tools do I have?
What instructions do I have?
What skills are available?
When should I use them?
因此:
到了最后,最开始那个 npm package 已经完全消失在模型的抽象世界里了。
11. Skill 又是什么?
这里还有另外一个很容易混淆的问题。
既然 extension 已经给模型提供:
subagent(...)
为什么很多 package 还要提供:
SKILL.md
因为:
能调用,和知道怎么调用,是两件事。
比如 extension 给模型的是:
subagent(task)
这是 Capability。
但什么时候应该 delegate?
什么任务适合 subagent?
subagent 返回结果以后应该怎么办?
什么时候不应该创建 subagent?
这些更像:
Policy / Knowledge / Workflow
这就是 Skill 更适合解决的问题。
你能做什么"] S --> K["Knowledge / Workflow
什么时候、怎么做"] C --> L["LLM"] K --> L
所以一个比较完整的 subagent package,其实同时解决两个问题:
Extension 让 Agent 有手。
Skill 告诉 Agent 什么时候伸手、怎么伸手。
这比“package 给模型增加了一个功能”要准确得多。
12. Skill 也不是简单粗暴地全部塞进 Context
还有一个细节很值得注意。
Skill 的发现和 Skill 全文进入 Context,也不是完全同一件事情。
一个 Skill 可以先以类似:
name
description
location
这样的发现信息出现在 available skills 中,然后模型真正需要时再读取完整内容。
这背后的思想其实非常合理。
假设我有 100 个 Skill:
Kubernetes
PostgreSQL
React
Security Review
Code Review
Subagent Delegation
...
如果每次请求都:
把 100 个 SKILL.md 全文
塞进 system prompt
Context 很快就炸了。
更合理的思路是:
先告诉模型“这些能力存在”。
真正需要的时候,再加载具体说明。
于是可以继续把它拆成:
这里其实已经有一点 progressive disclosure 的味道了。
13. 所以一个 Subagent Package 实际有两条进入模型的路径
现在整个东西终于可以拼起来了。
假设:
my-subagent/
├── package.json
├── extensions/
│ └── subagent.ts
└── skills/
└── delegation/
└── SKILL.md
安装:
pi install npm:my-subagent
之后真正发生的是:
这才是一个比较完整的 subagent package。
这里尤其值得记住的是:
Extension → Capability
Skill → Guidance
两条路径最后才在模型这一层汇合。
14. 为什么已经打开的 Session 不一定马上认识新 Package?
顺着这个模型,还能解释一个非常实际的问题。
假设 Terminal A 已经:
pi
跑起来了。
然后 Terminal B:
pi install npm:my-subagent
磁盘上的:
~/.pi/agent/settings.json
已经变了。
但 Terminal A 里的 LLM 不一定突然就知道:
subagent
为什么?
因为:
磁盘配置发生变化
和:
当前 Runtime 的 Resource / Tool Registry 发生变化
不是一回事。
可以把它画成:
需要重新加载 Resource:
于是下一次模型请求,它才能看到新的能力。
15. pi install 真正做的事情
所以回头再看:
pi install npm:my-subagent
我现在不会把它简单理解成:
安装一个插件。
而更愿意理解成:
向 Pi 的全局 Resource System 注册并 materialize 一个新的 Package Source。
这个 Package Source 可以暴露:
extensions
skills
prompts
themes
然后 Pi Runtime 在启动或者 reload 时,把这些 Resource 转换成:
Runtime Capability
+
Model-visible Context
整个生命周期可以压缩成一张图:
这张图基本就是整篇文章。
16. Package、Extension、Skill,其实属于三个不同问题
最后可以把这三个概念压缩成三句话。
Package:东西怎么分发?
它解决的是:
Distribution
Dependency
Version
Resource Packaging
来源可以是:
npm
git
local path
Extension:Pi 怎么获得新的运行时能力?
比如:
tool
command
event handler
UI
runtime behavior
它解决的是:
Capability
Skill:模型怎么知道什么时候、如何使用能力?
它解决的是:
Knowledge
Workflow
Instructions
所以最终可以压成:
这三个东西放在一起,才组成一个比较完整的 Agent Extension System。
17. 最终的心智模型
如果整篇文章只记住一张图,我觉得记这个就够了:
所以,“安装了一个 subagent package,为什么之后每个 Pi session 都知道它?”
现在可以给出一个更准确的回答:
它其实不知道你“安装了一个 package”。
pi install只是把 package 加入全局配置;之后每个 Pi runtime 启动时,从~/.pi/agent/settings.json发现这个 package,再发现其中的 extension / skill 等 Resource。Extension 注册运行时能力,Skill 提供使用这些能力所需的信息;最终 Pi 再把这些东西投影成模型可见的 Tools 和 Context。
模型认识的从来不是 Package。
模型认识的是 Package 最终投影到 Runtime 和 Context 中的能力。
我觉得这是理解 Pi Package 最重要的一点。
Package 是给 Pi 看的。
Tool 和 Skill,才是给 Agent 看的。
而真正有趣的地方,也正好发生在这两者之间。