展示 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 重新加载。项目扩展目录。

验证时可按三个观察点检查:

  1. 输入 /inspect,界面应显示本环境可用的检查工具名;命令本身不需要发起模型请求。
  2. 触发一次 context 事件时,返回列表增加一条阶段说明,原问题保持末尾,传入的历史列表保持原样。可以先用模拟事件检查,不必调用模型。
  3. 再次输入 /inspect 后执行 /inspect off,应恢复第一次进入前的工具集合;退出后 context 钩子不再增加说明。

单文件转译只能检查可转译性,模拟 API 能检查命令与事件逻辑;完整依赖类型检查、真实加载和模型行为需要分别验证。import type 在转译后会被移除,不会成为运行时模块导入。

工具集合与执行权限的区别

这个示例定制了模型看到的工具和当前任务阶段,并不提供操作系统沙箱。该版本的 codemode、deferred 工具即使不在活动集合中,仍可能由 codemode 脚本调用;需要约束真实动作时,还应按工具调用入口和执行环境设置相应策略。工具暴露与活动集合。

参考资料

相关面经

1 篇

哆咔互娱

Agent 开发工程师
实习一面、二面
查看原面经 →
展示Pi的自定义Extension配置(可选,加分项)

相关题目

4 题