展示 Pi 的自定义 Extension 配置。
未标记
考查重点:扩展入口、注册与运行验证。展示实际Extension入口、注册的命令或工具与配置文件,用最小触发任务说明如何加载和生效;具体API以所用Pi版本为准
本题目录
参考答案
PiTypeScript
基于 Pi核心回答
可以用一个 /inspect 扩展展示完整配置:默认导出函数接收 ExtensionAPI,注册命令切换检查工具集,再注册 context 事件处理器,为当轮模型输入补一条阶段说明。下面示例采用 @earendil-works/pi-coding-agent 1.4.0 的接口,/inspect off 会恢复进入前的工具集合。扩展入口与加载方式。
最小扩展文件
将文件保存为项目中的 .pi/extensions/inspect.ts:
import type { UserMessage } from "@earendil-works/pi-ai";
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
export default function inspectExtension(pi: ExtensionAPI) {
let previousTools: string[] | undefined;
const upgradeTask = {
targetVersion: "3.0(示例)",
compatibility: "保留旧接口入口;列出必须修改的调用点"
};
pi.registerCommand("inspect", {
description: "切换检查阶段;/inspect off 恢复原工具集",
handler: async (args, ctx) => {
if (args.trim() === "off") {
if (previousTools) pi.setActiveTools(previousTools);
previousTools = undefined;
ctx.ui.notify("已退出检查阶段", "info");
return;
}
previousTools ??= pi.getActiveTools();
const available = new Set(pi.getAllTools().map((tool) => tool.name));
pi.setActiveTools(["read", "grep", "find", "ls"].filter((name) => available.has(name)));
ctx.ui.notify(`检查工具:${pi.getActiveTools().join(", ")}`, "info");
}
});
pi.on("context", (event) => {
if (!previousTools) return;
const notice: UserMessage = {
role: "user",
content: `[inspect 阶段说明] 目标 SDK 版本:${upgradeTask.targetVersion};兼容约束:${upgradeTask.compatibility}。当前只检查代码并提出建议,不修改文件或执行命令。`,
timestamp: Date.now()
};
const messages = [...event.messages];
const at = messages.at(-1)?.role === "user" ? messages.length - 1 : messages.length;
messages.splice(at, 0, notice);
return { messages };
});
}
这里用了三个具体入口:
registerCommand()注册/inspect,命令回调保存原工具集合,再选取已注册的read、grep、find、ls;重复开启不会覆盖最初保存的集合。setActiveTools()改变向模型声明的活动工具;getAllTools()用于排除当前环境中不存在的工具名。它切换的是工具集合,不是重新注册工具。on("context", ...)在模型请求前返回新的消息列表,把明确标识的阶段说明放在最后一条用户问题之前。原历史数组没有被改写,当前问题也仍在末尾。API 与事件类型、context 事件执行。
context 处理的是不含系统消息的对话列表,运行器会恢复系统提示和工具状态;示例没有调用持久化消息 API,只为当轮输入补充说明。previousTools 是扩展实例内状态,若要支持重新加载和会话分支切换,需要另行保存并恢复这份状态。
upgradeTask 用示例值展示目标版本和兼容约束怎样进入当轮消息;业务接入时应从当前升级任务装载这些字段,切换任务或迁移分支后重新绑定,避免沿用上个任务的约束。阶段消息只补充模型输入,代码是否兼容仍由实际调用点和测试结果判断。
怎样加载和触发
项目扩展在授予项目信任后加载;开发时也可以显式指定文件:
pi --extension ./.pi/extensions/inspect.ts
不需要为这个本地 TypeScript 文件单独打包。修改文件后,可在交互会话中执行 /reload 重新加载。项目扩展目录。
验证时可按三个观察点检查:
- 输入
/inspect,界面应显示本环境可用的检查工具名;命令本身不需要发起模型请求。 - 触发一次
context事件时,返回列表增加一条阶段说明,原问题保持末尾,传入的历史列表保持原样。可以先用模拟事件检查,不必调用模型。 - 再次输入
/inspect后执行/inspect off,应恢复第一次进入前的工具集合;退出后context钩子不再增加说明。
单文件转译只能检查可转译性,模拟 API 能检查命令与事件逻辑;完整依赖类型检查、真实加载和模型行为需要分别验证。import type 在转译后会被移除,不会成为运行时模块导入。
工具集合与执行权限的区别
这个示例定制了模型看到的工具和当前任务阶段,并不提供操作系统沙箱。该版本的 codemode、deferred 工具即使不在活动集合中,仍可能由 codemode 脚本调用;需要约束真实动作时,还应按工具调用入口和执行环境设置相应策略。工具暴露与活动集合。
参考资料
- 国内:JavaGuide:Harness Engineering:参考工具和钩子由运行系统提供的分工,将阶段说明与真正改变工具集合的代码分开。
- 国内:zero2Agent:工具管理:参考按任务阶段选择本轮工具定义,解释注册工具与激活工具的区别。
- 国内:Spring AI Alibaba:Hooks 和 Interceptors:参考在模型调用前通过钩子调整上下文的组织;示例接口采用
Pi自身的事件 API。 - 国内:WeThinkIn:Agent Harness 工程:参考任务动作、运行环境和预期结果分别定义,为工具集合、消息变化与退出恢复设置独立观察点。
- 国外:TypeScript:Modules Reference:依据类型导入被擦除的规则,说明示例的接口类型与运行时模块加载不同。
- 国外:TypeScript:isolatedModules:依据单文件转译的限制,区分转译检查、完整类型检查与实际运行验证。
相关面经
1 篇哆咔互娱
Agent 开发工程师展示Pi的自定义Extension配置(可选,加分项)
