Pi Skill 的渐进加载:元数据、System Prompt 与正文
Pi 的 Skill 不是在启动时把所有 SKILL.md 正文塞进模型上下文。它采用渐进加载:启动或重载时发现 Skill 并保存元数据,构建 System Prompt 时只向模型展示目录,真正选中某个 Skill 后才读取正文。
理解这三层状态很重要:磁盘上的 Skill、运行时内存里的 Skill 元数据、模型上下文里的 Skill 正文不是同一件事。
三个加载时机
完整链路可以压缩成三步:
1 | |
第一步发生在 DefaultResourceLoader.reload()。createAgentSession() 创建会话前会先执行资源重载,随后 loadSkills() 扫描默认目录、项目目录、配置路径和包提供的 Skill。
这一阶段虽然会读取 SKILL.md 来解析 frontmatter,但内存中的 Skill 对象只保留这些字段:
1 | |
正文没有长期保存在这个对象里。reload() 的职责是发现和注册资源,不是扩大模型上下文。
元数据如何进入 System Prompt
AgentSession 创建运行时并确定 active tools 后,会重建 System Prompt:
1 | |
_rebuildSystemPrompt() 从 ResourceLoader 取得已加载的 Skill,交给 formatSkillsForPrompt(),最终生成类似下面的目录:
1 | |
模型第一次被调用时已经能看到这份目录,但还看不到 Skill 正文。这样做可以让大量 Skill 同时可发现,又不必为没有使用的 Skill 支付上下文成本。
这里还有一个容易忽略的条件:当前实现只有在 active tools 中存在名为 read 的工具时,才会把 Skill 目录加入 System Prompt。原因很直接——如果模型没有能力读取目录中的文件,向它展示 Skill 也无法完成后续加载。
正文什么时候进入上下文
正文有两条加载路径。
第一条是模型自主选择:
1 | |
这是标准的懒加载路径。Skill 正文通过工具结果进入会话,不会提前占用首次请求的上下文。
第二条是用户显式调用:
1 | |
这条路径不需要模型先从目录中发现 Skill,也不依赖内置 read。因此即使 Skill 设置了 disable-model-invocation: true,用户仍然可以显式调用它;该配置只控制是否向模型展示。
/reload 改变了什么
修改或新增 Skill 后,/reload 会重新执行资源扫描、更新内存中的 Skill 列表,并重建运行时和 System Prompt。新的元数据从下一次模型请求开始生效。
它不会追溯修改已经进入历史消息的 Skill 正文。已经产生的 ToolResult 或展开后的用户消息仍然是当时的内容,这是对话历史不可变带来的自然结果。
另外,Skill 加载存在两个值得排查的边界:
- 缺少
description的 Skill 不会成功注册,因为模型无法据此判断用途。 - 同名 Skill 发生冲突时,按资源优先级保留先加载者,并产生诊断信息,而不是合并两份正文。
Java Runtime 当前的差异
Java Runtime 的 openSession() 每次请求都会新建 DefaultResourceLoader 并执行 reload(),所以本地 Skill 仍然会被扫描到内存。
但当前 Java Runtime 同时配置了:
1 | |
这会禁用 Pi 内置工具,包括 read。结合前面的 System Prompt 条件,实际结果是:
- Skill 可以被 ResourceLoader 发现;
- 模型通常看不到
<available_skills>; - 用户显式输入
/skill:name仍可加载正文; - 如果 Java MCP 提供一个激活且名称为
read的自定义工具,目录才可能重新进入 System Prompt。
因此,给 Java Runtime 接入云端或租户级 Skill,不能只解决“Skill 存在哪里”。运行时还必须明确解决三件事:目录如何注入、正文由谁读取、读取时如何执行租户和权限校验。
设计上的边界
Pi 当前的渐进加载把职责分得比较清楚:
- ResourceLoader 负责发现与元数据注册;
- System Prompt 负责让模型发现能力;
read或/skill:name负责按需加载正文;- 会话消息负责保存本次实际使用过的内容。
如果后续把 Skill 改成云端存储,适合保留这条边界。云端列表接口只返回可信元数据,正文读取走独立的鉴权接口,并在读取时校验租户、用户、Skill 版本和授权范围。不要为了接云端存储,直接把所有正文提前拼进 System Prompt。