首页 / pi-agent 入门教程 / Extensions 扩展入门

pi-agent 入门教程

Extensions 扩展入门

本教程共 30 篇 · 第 16 篇 · 更新于 2026-08-10 · 约 8 分钟阅读

pi-agentExtension扩展自定义

本节目标:搞清楚 Extension 是什么、它和 Skill 的本质区别,掌握扩展的加载位置与文件结构,能写出第一个可运行的 Hello World 扩展。

到这一章为止,你已经能用 pi 写代码、配模型、用 Skill 定制行为。但如果想深入一步——给 pi 加一个它原本没有的工具、拦截每次工具调用做安全检查、在 AI 回复前后插入自定义逻辑——这些 Skill 都做不到。

Skill 是”教 AI 怎么想”。Extension 是”让 pi 怎么做”。

Extension 和 Skill 到底差在哪

这两个概念容易搞混,一句话说清:

SkillExtension
本质提示词级别代码级别
写的什么Markdown 文件(告诉 AI 规则和流程)TypeScript 文件(注册工具、监听事件)
能力边界只能影响 AI 的思考方向可以改 pi 的运行行为
能不能加新工具
能不能拦截操作
能不能改 UI 渲染

类比一下:Skill 像给 AI 写了一份”工作手册”,告诉它碰到什么情况怎么想、按什么步骤来。Extension 像给 pi 装了一个”硬件模块”,直接增加它原来没有的功能。

两个机制不互斥。实际项目里,Extension 负责”有没有这个能力”,Skill 负责”用这个能力时按什么规矩来”。

Extension 能做哪些事

一个 Extension 的入口是一个 TypeScript 工厂函数,拿到 ExtensionAPI 实例后,你可以做这些:

  • 注册自定义工具pi.registerTool() —— AI 能调用的新功能,比如查数据库、调内部 API
  • 监听生命周期事件pi.on() —— 在 agent 启动、工具调用、会话切换等节点插入逻辑
  • 注册自定义命令pi.registerCommand() —— 用户打 /mycommand 就能调
  • 修改 LLM 上下文:在消息发给模型之前动手脚
  • 渲染自定义 UIctx.ui 系列方法,做终端内的选择框、确认弹窗、通知条
  • 覆盖内置工具:注册同名工具替换 readbashedit 等默认行为
Note

Extension 以你当前用户的系统权限运行,能做任何事。只装来源可信的扩展。

扩展从哪加载

pi 会在几个固定位置自动发现扩展:

位置作用范围说明
~/.pi/agent/extensions/*.ts全局对所有项目生效
~/.pi/agent/extensions/*/index.ts全局子目录形式的扩展
.pi/extensions/*.ts项目仅当前项目,需信任
.pi/extensions/*/index.ts项目项目子目录扩展

自动发现的扩展可以用 /reload 热重载,不用重启 pi。

临时测试不想放目录的话,用 CLI 参数直接加载:

pi -e ./my-extension.ts

也可以在 settings.json 里指定路径:

{
  "extensions": [
    "/path/to/local/extension.ts",
    "/path/to/local/extension-dir"
  ]
}

扩展的文件结构

三种常见组织形式,按复杂度递增。

单文件扩展

最简形式,一个 .ts 文件就够了,适合几十行的小工具:

~/.pi/agent/extensions/
└── my-extension.ts

目录扩展

包含 index.ts 作为入口,适合拆成多个模块:

~/.pi/agent/extensions/
└── my-extension/
    ├── index.ts      # 入口,导出一个默认函数
    ├── tools.ts      # 工具实现
    └── utils.ts      # 辅助函数

带 npm 依赖的扩展

需要第三方包时,加 package.json 然后 npm install

~/.pi/agent/extensions/
└── my-extension/
    ├── package.json
    ├── node_modules/
    └── index.ts          # 入口必须放在扩展根目录,自动发现规则只匹配 */index.ts

pi 用 jiti 加载 TypeScript,不需要编译。如果工厂函数返回 Promise,pi 会等它 resolve 再继续启动。

Tip

开发时放在 ~/.pi/agent/extensions/ 目录里,改了代码 /reload 一下就生效。pi -e 只适合快速验证。

可用的内置 Import

写扩展时可以直接 import 这些 pi 自带包,不需要额外安装:

包名用途
@earendil-works/pi-coding-agent扩展类型:ExtensionAPIExtensionContext、事件类型
typebox定义工具参数的 Schema
@earendil-works/pi-aiAI 工具,如 StringEnum(Google API 兼容的枚举)
@earendil-works/pi-tuiTUI 组件,用于自定义终端渲染

这些应该列为 peerDependencies,不要打进你的扩展包里。

第一个扩展:从零到跑起来

下面写一个能实际跑起来的扩展。它做三件事:

  1. 会话启动时弹一条通知
  2. 注册一个 greet 工具,让 AI 能跟人打招呼
  3. 注册一个 /hello 命令,用户直接敲就能响应

~/.pi/agent/extensions/ 下创建 my-extension.ts(如果目录不存在先建目录):

import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
import { Type } from "typebox";

export default function (pi: ExtensionAPI) {
  // 1. 会话启动时通知
  pi.on("session_start", async (_event, ctx) => {
    ctx.ui.notify("我的扩展已加载!", "info");
  });

  // 2. 注册 greet 工具——AI 可以调用
  pi.registerTool({
    name: "greet",
    label: "打招呼",
    description: "向指定的人打招呼,返回问候语",
    parameters: Type.Object({
      name: Type.String({ description: "要打招呼的人名" }),
    }),
    async execute(_toolCallId, params) {
      return {
        content: [{ type: "text", text: `你好,${params.name}!` }],
        details: {},
      };
    },
  });

  // 3. 注册 /hello 命令——用户直接敲
  pi.registerCommand("hello", {
    description: "向世界问好",
    handler: async (args, ctx) => {
      ctx.ui.notify(`你好 ${args || "世界"}!`, "info");
    },
  });
}

测试它:

pi -e ~/.pi/agent/extensions/my-extension.ts

进去之后试试:

  • 直接敲 /hello,看终端会不会弹出通知
  • 对 AI 说”用 greet 工具跟小明打个招呼”,看 AI 会不会调用你的工具
Tip

放在 ~/.pi/agent/extensions/ 目录里的扩展会被自动发现。上面的 -e 参数只是临时加载方式——开发完后把文件放对位置,以后启动 pi 就会自动加载。

开发时要注意什么

几条从踩坑里总结的经验:

  • 不要在工厂函数里启动后台资源(进程、socket、文件监听、定时器)。工厂函数只是注册,把资源启动延迟到 session_start 事件里,在 session_shutdown 里清理。
  • 改了扩展代码用 /reload 热重载,不用退出 pi 再进。
  • tool_call 事件里 event.input 可以直接改,你改完的参数就会传给工具执行。

这一章建立了 Extension 的基础认知。下一章深入自定义工具——怎么定义一个参数完备、错误处理到位、能跑在生产环境里的工具。