用 Pi 的接口做一个自己的 Agent
前八章的教学内核把 Agent Loop 的状态逐个摊开了。做真实项目时,资源加载、终端界面和 Session 产品层可以直接使用 Pi 已有的实现。
下面做一个仓库维护 Agent。它需要按固定步骤审查项目,快速读取仓库概况,拒绝直接修改凭据文件,也能被脚本或桌面应用调用。
三个需求分别落在 Pi 的 Skill、Extension 和 SDK 上。
| 入口 | 适合放什么 | 运行形态 |
|---|---|---|
| Skill | 领域术语、步骤、模板和参考资料 | Markdown,按需进入上下文 |
| Extension | 新工具、命令、事件策略、Provider 与 UI | TypeScript,与 Pi 同进程运行 |
| SDK | 自定义 CLI、批处理、服务或图形界面 | 由你的程序创建并管理 AgentSession |
Extension API 可以注册 Tool、Command、Shortcut、CLI flag,并监听 Session、Model、Message 与 Tool 事件。查看固定版本的 ExtensionAPI。
Skill 不执行代码。Pi 启动时只读取名称和 description,模型需要时再打开完整的 SKILL.md 与引用文件。这样一大批操作手册不会挤进每一次请求。查看 Skill 加载约定。
SDK 的主要入口是 createAgentSession()。它接收模型、工具 allowlist、custom tools、ResourceLoader、SettingsManager 与 SessionManager,再返回可订阅、可 prompt、可 dispose 的 AgentSession。查看 SDK 参数与返回值。
把审查方法写成 Skill
Section titled “把审查方法写成 Skill”在项目中创建:
.pi/└── skills/ └── repository-maintenance/ ├── SKILL.md └── references/ └── report-format.mdSKILL.md 可以这样写:
---name: repository-maintenancedescription: 审查仓库结构、依赖、未提交状态和验证入口。用于项目接手、升级前检查和技术债盘点。---
# Repository Maintenance
1. 确认工作目录、分支与未提交状态。2. 查找 package manifest、构建配置和项目说明。3. 分开记录文件或命令证明的事实,以及仍需验证的推断。4. 未得到授权时只读取和诊断。5. 报告格式遵循 references/report-format.md。references/report-format.md 只放这份 Skill 独有的输出合同:
# 审查报告格式
## 已确认- 结论 - 证据:文件路径或实际命令
## 仍需确认- 问题 - 缺少什么证据
## 建议的下一条只读命令description 决定模型何时会想到这份 Skill。“帮助处理仓库”太泛,“用于项目接手、升级前检查和技术债盘点”给出了明确场景。Pi 会校验名称、description 和目录结构;重名 Skill 会按发现顺序处理并发出警告。查看 Skill 校验规则。
需要确定性调用时,直接输入:
/skill:repository-maintenance 检查当前仓库,只报告,不修改观察三件事:是否按 reference 中的结构输出,是否引用实际文件路径,会话里是否出现任何写入。自然语言匹配可以作为便利入口,不能代替显式实验。
用 Extension 增加工具
Section titled “用 Extension 增加工具”Skill 会教模型怎样审查,却不能创建新的运行时工具。项目 Extension 可以增加一个只读的 repo_snapshot。把下面代码保存到 .pi/extensions/repository-maintenance.ts:
import { readdir, readFile } from "node:fs/promises";import { join, resolve } from "node:path";import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";import { Type } from "typebox";
export default function repositoryMaintenance(pi: ExtensionAPI) { pi.registerTool({ name: "repo_snapshot", label: "Repository Snapshot", description: "读取仓库顶层文件与 package scripts,不修改文件。", parameters: Type.Object({}), async execute(_id, _params, _signal, _onUpdate, ctx) { const entries = (await readdir(ctx.cwd)).sort(); let scripts: string[] = []; try { const manifest = JSON.parse( await readFile(join(ctx.cwd, "package.json"), "utf8"), ); scripts = Object.keys(manifest.scripts ?? {}).sort(); } catch {} return { content: [{ type: "text", text: JSON.stringify({ cwd: ctx.cwd, entries, scripts }, null, 2), }], details: { cwd: ctx.cwd, entries, scripts }, }; }, });
pi.on("tool_call", async (event, ctx) => { if (event.toolName !== "write" && event.toolName !== "edit") return; const input = event.input as { path?: unknown }; if (typeof input.path !== "string") return; const target = resolve(ctx.cwd, input.path); const protectedFiles = [ resolve(ctx.cwd, ".env"), resolve(ctx.cwd, ".env.local"), ]; if (protectedFiles.includes(target)) { return { block: true, reason: "本 Agent 不直接修改本地凭据文件" }; } });}开发时显式加载:
pi -e .pi/extensions/repository-maintenance.ts受信项目也会自动发现 .pi/extensions/ 下的代码。Pi 使用 TypeScript loader 直接加载文件,不要求预先打包。查看 Extension loader。
接着做三次观察:
- 要求调用
repo_snapshot,结果应包含当前目录与 scripts; - 要求修改普通测试文件,记录
tool_call事件; - 要求修改
.env,handler 应在内建 write/edit 之前被阻止。
第三项验证的是你写的 Extension policy,不代表 Pi 自带 OMP 那种 read、write、exec 审批体系。
Extension 与 Pi 在同一进程中运行,继承当前用户权限。Project Trust 只决定项目代码是否加载,加载后的代码仍不在沙箱里。查看 Extension 安全说明。
这段示例只拦截两个规范化后的词法路径,没有覆盖 symlink、大小写差异或其他 .env.* 文件。真实凭据保护应使用更完整的路径策略,并配合进程级隔离。
用 SDK 接入自己的程序
Section titled “用 SDK 接入自己的程序”CLI 适合人机对话;定时任务、桌面应用或批量仓库检查需要自己管理 Session。安装与教材一致的版本:
npm install @earendil-works/pi-coding-agent@0.82.1先运行一次 pi 完成 Provider 与模型配置,再把以下入口保存为 repository-agent.mjs。这段代码使用相同的用户配置;它不是前面离线 Lab 的一部分,运行时会调用你选定的模型:
import { createAgentSession, DefaultResourceLoader, getAgentDir, SessionManager, SettingsManager,} from "@earendil-works/pi-coding-agent";
const cwd = process.cwd();const agentDir = getAgentDir();const settingsManager = SettingsManager.create(cwd, agentDir);const resourceLoader = new DefaultResourceLoader({ cwd, agentDir, settingsManager,});await resourceLoader.reload();
const { session, extensionsResult } = await createAgentSession({ cwd, settingsManager, resourceLoader, sessionManager: SessionManager.inMemory(cwd), tools: ["read", "grep", "find", "ls", "repo_snapshot"],});
if (extensionsResult.errors.length) { throw new Error("仓库维护 Extension 加载失败");}
try { await session.prompt("按 repository-maintenance Skill 生成只读审查报告。");} finally { session.dispose();}运行入口:
node repository-agent.mjsDefaultResourceLoader 统一装载 Extension、Skill、prompt、theme 与 context file,查看 reload 流程。session.dispose() 也属于交付要求,否则可能留下订阅或后台句柄。
在教学内核里验证组合方式
Section titled “在教学内核里验证组合方式”第九个 Lab 给 AgentCore 注入一个 count_lines Extension。它没有修改 core 文件,只增加 system prompt 片段与工具。
cd agent-harness-labnpm run lab -- 09预期观察:
{ "terminal": "completed", "exposedTools": ["read", "count_lines"], "systemPromptContainsExtension": true}Lab 09 只验证教学内核可以在不改 core 的情况下增加 prompt 片段和工具。Pi 的 Skill 发现、Extension 加载与 SDK 入口,需要用本章前面的三组真实 Pi 操作分别验证。
下一章进入 OMP。它把审批、Hashline、Memory、Browser 和 Task 放进一套独立运行时。我们会按改动大小选择 Config、Skill、Agent Definition、Extension 或 Fork,做出一个能维护的领域 Agent。