背景 链接到标题
Pi Coding Agent 的扩展机制允许向 AgentSession 注册自定义 Tool,LLM 在对话中可以直接调用这些工具。扩展是 ESM 模块,通过 pi.registerTool() 注册,自动加载到当前 Session。
注册 Tool 链接到标题
Extension 文件导出一个默认函数,接收 pi: ExtensionAPI 作为参数:
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
import { Type } from "typebox";
export default function (pi: ExtensionAPI) {
pi.registerTool({ /* ... */ });
}
pi.registerTool() 接收一个对象,关键字段如下:
| 字段 | 类型 | 说明 |
|---|---|---|
name |
string |
工具唯一标识,LLM 据此调用 |
label |
string |
显示名称,UI 中展示 |
description |
string |
工具描述,LLM 据此判断何时调用 |
promptSnippet |
string |
简短提示,注入系统提示词 |
promptGuidelines |
string[] |
调用 guideline,LLM 参考 |
parameters |
TypeBox Schema |
参数 Schema,LLM 据此生成参数 |
execute |
function |
工具执行函数 |
TypeBox Schema 链接到标题
parameters 用 TypeBox 定义:
parameters: Type.Object({
query: Type.String({ description: "搜索关键词" }),
topK: Type.Optional(Type.Number({ default: 5 })),
})
支持 Type.String、Type.Number、Type.Boolean、Type.Array、Type.Optional 等。
execute 函数 链接到标题
async execute(_toolCallId, params, signal, onUpdate, ctx) {
// toolCallId: 本次调用 ID
// params: LLM 生成的参数,按 Schema 解析
// signal: AbortSignal,支持取消
// onUpdate: 流式更新回调
// ctx: 扩展上下文
}
返回格式:
{
content: [{ type: "text", text: "结果文本" }],
details: {},
}
content 给 LLM 阅读,details 给 UI 展示(如结构化 JSON)。
完整示例 链接到标题
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
import { Type } from "typebox";
export default function (pi: ExtensionAPI) {
pi.registerTool({
name: "my_tool",
label: "我的工具",
description: "接收一个搜索关键词,返回相关结果",
promptSnippet: "搜索相关信息",
promptGuidelines: [
"优先使用此工具获取实时数据",
"参数不足时向用户确认",
],
parameters: Type.Object({
query: Type.String({ description: "搜索关键词" }),
topK: Type.Optional(Type.Number({ default: 5, description: "最多返回条数" })),
}),
async execute(_toolCallId, params, signal) {
const resp = await fetch("https://api.example.com/search", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ q: params.query, limit: params.topK }),
signal,
});
const data = await resp.json();
return {
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
details: {},
};
},
});
}
关键点:
description写清楚工具用途,LLM 据此判断是否调用signal透传给fetch,支持中断- 返回
content用text类型,LLM 直接阅读
Extension 加载方式 链接到标题
项目级扩展 链接到标题
将扩展目录放在项目根目录的 .pi/extensions/ 下,DefaultResourceLoader 根据 cwd 自动发现:
项目根目录/
└── .pi/
└── extensions/
└── my-tool/
├── index.ts
└── package.json # 可选,额外 npm 依赖
内联加载 链接到标题
在 main.js 中直接 import,传入 extensionFactories:
const { default: myExt } = await import('./extensions/my-tool.js');
const resourceLoader = new DefaultResourceLoader({
cwd: expandHome(config.cwd),
agentDir: getAgentDir(),
extensionFactories: [myExt],
});
热重载 链接到标题
扩展 handler 中调用 ctx.reload(),不中断当前 Session 即可重载所有扩展。