第三部分 · 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.tsresourcePrecedenceRank 给同名冲突定了序:

优先级来源位置
最高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() 做三件聪明事:

  1. --editable 安装。改技能源码立即生效,无需重装——技能是活文件,不是冻结的依赖。
  2. pyproject 哈希增量。venv 里的 .bootstrap-version 记录每个技能的 {importName, packagePath, pyprojectPath, pyprojectHash};哈希不变就跳过,改了依赖声明才重装。
  3. 技能间依赖拓扑排序。一个自写的迷你 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-heartbeathost_request 的薄包装宿主能力的 RPC 门面
agent-message / agent-observehost handler 包装多代理通信面(第 7 章)
attach-image纯 Python(PIL 压缩)+ attachment MIME让模型看见磁盘图片
websearch纯 Python(httpx → Serper)外部 API
linear / notionMcpIntegration 子类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 的工具 + 扩展对照

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__.pyasync 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

DefaultPackageManagercore/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/installAndPersistremove/removeAndPersistupdate(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 的信任设计是「来源分层 + 显式安装 + 登录门控」,不是运行时沙箱。装第三方技能前,像审查任何依赖一样审查它。

实践应用

  1. 让能力沉淀物自带执行形态。「文档 + 代码 + 依赖声明」打包成一个单元,比纯文档可复用得多,比硬编码工具灵活得多。技能的边界是包,不是工具 schema。
  2. 提示词只给路由信息。description 决定命中,全文按需自取——启动成本与能力规模解耦。
  3. editable + 内容哈希,让沉淀物保持活性。改源码即生效,改依赖才重装。冻结的产物会被绕过,活的产物会被维护。
  4. 把宿主能力包成环境里的普通函数。用户不需要学习第二套交互语法;能力之间可以自由组合(条件、循环、变量传递)。
  5. 能力清单要反映真实可用性。未登录的集成不出现在清单里,装失败的技能留下占位与报错——模型的世界模型必须与宿主一致。

总结

技能是 Prime 能力沉淀的最终形态:SKILL.md 负责发现与路由,pyproject 负责执行与依赖,kernel 引导负责让模块变成函数,host bridge 负责把宿主状态机也纳入同一手感。发现六源五级优先,注入只做 metadata,安装 editable 且增量,调用面统一为 await 名字(...)。至此第三部分结束——提示词、refine、技能,三章合起来回答了「系统如何记住与改进」。下一部分回到会话本身:这一切发生在其中的那个 400KB 的编排器。