第三部分 · Continual Harness
第 10 章:可执行技能与包管理器
当经验重到需要代码承载:技能即可安装的 Python 包
比记忆更重的沉淀
第 9 章的 harness 条目是轻的:一条记忆、一段策略备注、一份子代理规格。但有些经验天然更重——「调用 Serper API 搜索并整理结果」是一套带凭据、带 HTTP 细节的过程;「精确替换文件中的唯一字符串」是一套带校验、带 diff 回传的操作。这些东西写成 markdown 指令让模型每次重新实现,是浪费;做成宿主工具,又违背「工具面只有一个」的设计。
Prime 的答案把技能从「说明书」升级成可安装的 Python 包:技能目录里若带 pyproject.toml,就会被 uv pip install --editable 装进 kernel 的专用 venv,模型直接 await skill_name(...)。官方文档的说法是:Python-backed skills are a superset of instruction-only skills——它们可以提供指导、脚本、参考资料、依赖、类型化可调用对象,还能在需要时自己调用 rlm(...) 递归委派。
技能的形状:SKILL.md + 可选的 pyproject
发现规则兼容 Agent Skills 标准:目录里有 SKILL.md 就是一个技能。frontmatter 提供路由元数据:
--- name: edit description: Replace an exact, unique string in an existing file. Use for targeted single-occurrence edits to files from the IPython kernel instead of rewriting the whole file. ---
校验是「宽容的」(lenient validation):name 必须等于父目录名、小写字母数字连字符、≤64 字符;description 必填(唯一会导致不加载的硬性条件)、≤1024 字符;名字违规只警告不拒绝。
从 markdown 技能升级为 Python 技能,detectPythonSkill()(core/skills.ts)检查四件事:pyproject.toml 存在;import 名(技能名连字符转下划线)是合法 Python 标识符;src/<importName>/__init__.py 存在。任一不满足,降级回 markdown 技能并附警告——又一次「优雅降级优先于响亮失败」。
发现:六类来源,五级优先
技能可以住在很多地方。package-manager.ts 的 resourcePrecedenceRank 给同名冲突定了序:
| 优先级 | 来源 | 位置 |
|---|---|---|
| 最高 | CLI | --skill <path>(可重复) |
| 0 / 1 | 项目 | settings 显式条目 → .prime/agent/skills/ 与 .agents/skills/ 自动发现(沿祖先目录向上) |
| 2 / 3 | 用户 | ~/.prime/agent/skills/ 与 ~/.agents/skills/ |
| 4 | 资源包 | settings packages 里的 npm/git/local 包 |
| 5 | 内置 | getBundledSkillsDir() |
.agents/skills/ 这个目录名值得注意——它是跨 harness 的 agentskills.io 约定,意味着 Claude Code / Codex 的技能目录可以直接被 Prime 复用。标准在这里是真实的兼容策略,不是口号。
内置技能不是无条件在场的,发现层有一组门控:
const builtinSkillOverrides = [ ...userOverrides.skills, // Disable the bundled websearch skill unless explicitly enabled… ...(this.settingsManager.getBundledWebsearchEnabled() ? [] : ["-websearch/SKILL.md"]), // …and disable any MCP integration the user hasn't logged into. ...this.extraBuiltinSkillOverrides(), ];
websearch 默认禁用,直到配置了 Serper 凭据;linear/notion 这类 MCP 集成技能,未登录就强制排除,登录后自动启用。能力清单精确反映凭据状态——模型看到的技能表永远与它实际能用的东西一致。
package-manager.ts 里有一段防御:内置技能目录为空时发 diagnostic,注释引用了一个内部事故编号(ENG-4220)——构建曾漏打包 skills/ 目录,导致系统静默降级为零技能。这类「事故化石」在大代码库里是宝贵的文化层:修复不只是补逻辑,还留下了一道让同类问题显形的哨兵。
注入提示词:metadata only,渐进披露
模型看到的技能清单长这样(formatSkillsForPrompt(),agentskills.io 规范的 XML 格式):
const lines = [
"\n\nThe following skills provide specialized instructions for specific tasks.",
"Use ipython to inspect a skill's file when the task matches its description.",
"Skills with a python_import are prepared in the persistent IPython kernel when available and can be called directly by that import name.",
...
"<available_skills>",
];
for (const skill of visibleSkills) {
lines.push(" <skill>");
lines.push(` <name>${escapeXml(skill.name)}</name>`);
lines.push(` <type>${skill.kind}</type>`);
if (skill.kind === "python") {
lines.push(` <python_import>${escapeXml(skill.python.importName)}</python_import>`);
}
lines.push(` <description>${escapeXml(skill.description)}</description>`);
lines.push(` <location>${escapeXml(skill.filePath)}</location>`);
lines.push(" </skill>");
}
只有 name、type、import 名、description、文件路径——SKILL.md 全文不进启动提示词。任务匹配时,模型自己用 ipython 读那份文件。渐进披露没有依赖任何专用机制:读文件这个动作本身就是模型的第一技能。
还有一条强制通道:/skill:name args 命令把全文去 frontmatter 后包进 <skill> 块,作为用户消息内联——文档承认模型有时不主动读文件,这个命令是人工兜底。TUI 端用 skill-blocks.ts 的一个正则把这类消息渲染成折叠卡片。
安装:editable、增量、拓扑排序
Python 技能的安装发生在 kernel venv 引导里(第 3 章的 ensureKernelPython),syncPythonSkills() 做三件聪明事:
--editable安装。改技能源码立即生效,无需重装——技能是活文件,不是冻结的依赖。- pyproject 哈希增量。venv 里的
.bootstrap-version记录每个技能的{importName, packagePath, pyprojectPath, pyprojectHash};哈希不变就跳过,改了依赖声明才重装。 - 技能间依赖拓扑排序。一个自写的迷你 TOML 扫描器读各技能的
[project] dependencies;若依赖名恰好是另一个兄弟技能的项目名,就拉进同一批安装集并按依赖排序(有环则退回路径序,让 uv 自己报错)。单个技能装失败只警告跳过,不阻塞其他技能。
在 kernel 里变得可调用
装得上还要调得到。kernel 引导 cell(第 3 章的 buildRlmBootstrapCode)为每个技能做包装:
class _PrimeAgentCallableSkillModule(_prime_agent_types.ModuleType):
async def __call__(self, *args, **kwargs):
result = self.run(*args, **kwargs)
if _prime_agent_inspect.isawaitable(result):
return await result
return result
定义了 run() 的模块被替换成一个可调用模块对象:await websearch("query") 直达 websearch.run(...);run 的 __signature__ 与 __doc__ 挂到模块上,于是 help(websearch) 直接显示 API 文档——docstring 即说明书,模型自省即得。import 失败则绑定 _PrimeAgentUnavailableSkill 占位,调用时抛带原始错误的 RuntimeError,kernel 永不因技能而崩。
这是第 3 章「模块即函数」技巧的第二次出现——对 rlm 如此,对每个技能亦然。整个 RLM 的调用面都收敛成同一种手感:await 名字(参数)。
能力即技能
内置技能清单揭示了这套机制的真正用途:
| 技能 | 实现方式 | 本质 |
|---|---|---|
edit | 纯 Python 文件读写 + diff MIME 回传 | pi 的 edit 工具转世 |
compact / goal / refine / rlm-heartbeat | host_request 的薄包装 | 宿主能力的 RPC 门面 |
agent-message / agent-observe | host handler 包装 | 多代理通信面(第 7 章) |
attach-image | 纯 Python(PIL 压缩)+ attachment MIME | 让模型看见磁盘图片 |
websearch | 纯 Python(httpx → Serper) | 外部 API |
linear / notion | McpIntegration 子类 | MCP 集成(kernel 内直连,凭据经 auth.json) |
prime-intellect / skill-creator | 纯 markdown | 产品地图 / 造技能的教学 |
中间一行是关键:compact、goal、refine、heartbeat 这些宿主状态机,全部以「技能」的形态暴露给模型。为什么不给它们一等工具的身份?答案在第 5 章的边界哲学里:它们是权威状态,实现必须在 TS;但对模型而言,它们应该和其余一切能力拥有相同的手感——await compact.status()、await goal.create(...)。技能系统于是成了能力的统一分发单元:外部 API、宿主状态机、MCP 集成、纯计算,全部走同一条 SKILL.md + Python 包管线,可发现、可禁用、可被同名覆盖、可被 skill-creator 仿制。
子代理自动继承这一切——它是另一个 kernel 会话,技能装了就有。不需要给每种 agent 单独配工具表。
pi 的能力扩展走 TypeScript 扩展系统:写代码、注册工具、重新构建。Prime 保留了扩展系统,但把「能力沉淀」的主渠道换成了 Python 技能——模型自己就能参与制造(skill-creator 教的就是模型),且产物天然可 import、可组合、可被 harness 条目引用。扩展是给开发者的,技能在相当程度上是给 agent 的。
skill-creator:教模型造技能
skill-creator 本身是一个 markdown 技能——一份教学法:选 kind(默认 markdown,只有「天然是一次 Python 调用」的能力才做 Python-backed)、选位置(项目技能随仓库提交共享,个人技能进 ~/.prime/agent/skills/)、脚手架(Python 技能 = pyproject + src/<import_name>/__init__.py 的 async def run(...),类型标注与 docstring 同时是 API 文档和 CLI 规格)、验证(frontmatter 对照规则表、/reload 热加载、脱离 kernel 用 uv run 单测、会话内 help() 冒烟)。description 写给路由看("Use when ..."),正文做渐进披露,细节推到 references/。
它和第 9 章的 harness skill 条目是互补的两层:rlm.harness.create_skill(...) 持久化的是「某个可调用模块存在、怎么调」的描述符;skill-creator 造的是真正的包。模型可以先造包,再把描述符沉淀进 harness——一个完整的能力生命周期。
包管理器:资源包,无中心 registry
DefaultPackageManager(core/package-manager.ts,约 2,480 行)管理的「包」是资源包:一个 npm 包 / git 仓库 / 本地目录,打包 extensions、skills、prompts、themes 四类资源,经 package.json 的 pi manifest 或约定目录声明。没有中心 registry——npm 走 npm registry,git 走任意远端,local 直接指路径;安装即 npm install -g / git clone / 引用本地路径。
接口面:resolve() 汇总 settings packages、显式条目、自动发现并按优先级排序;install/installAndPersist、remove/removeAndPersist、update(npm 查 registry 最新版、git ls-remote 对比 HEAD、带版本 pin 的跳过、PI_OFFLINE 离线跳过)。CLI 侧(package-manager-cli.ts)有个诚实的比例:约六成篇幅在处理 prime-agent update 的自更新——按安装方式生成更新命令、协调 daemon 重启与会话恢复。包管理本体只是薄封装。这个比例本身是一份声明:分发渠道的运维复杂度,远大于资源解析本身。
README 的警告对技能同样成立:"Review third-party Python skills … run untrusted code or instructions in an external sandbox." Python-backed 技能会被 pip install 进 kernel 环境并被模型执行——它们就是代码,拥有你的用户权限。Prime 的信任设计是「来源分层 + 显式安装 + 登录门控」,不是运行时沙箱。装第三方技能前,像审查任何依赖一样审查它。
实践应用
- 让能力沉淀物自带执行形态。「文档 + 代码 + 依赖声明」打包成一个单元,比纯文档可复用得多,比硬编码工具灵活得多。技能的边界是包,不是工具 schema。
- 提示词只给路由信息。description 决定命中,全文按需自取——启动成本与能力规模解耦。
- editable + 内容哈希,让沉淀物保持活性。改源码即生效,改依赖才重装。冻结的产物会被绕过,活的产物会被维护。
- 把宿主能力包成环境里的普通函数。用户不需要学习第二套交互语法;能力之间可以自由组合(条件、循环、变量传递)。
- 能力清单要反映真实可用性。未登录的集成不出现在清单里,装失败的技能留下占位与报错——模型的世界模型必须与宿主一致。
总结
技能是 Prime 能力沉淀的最终形态:SKILL.md 负责发现与路由,pyproject 负责执行与依赖,kernel 引导负责让模块变成函数,host bridge 负责把宿主状态机也纳入同一手感。发现六源五级优先,注入只做 metadata,安装 editable 且增量,调用面统一为 await 名字(...)。至此第三部分结束——提示词、refine、技能,三章合起来回答了「系统如何记住与改进」。下一部分回到会话本身:这一切发生在其中的那个 400KB 的编排器。