Pi Agent 学习笔记

Pi Agent 是 Mario Zechner(libGDX 创始人)开发的开源 AI 编程助手,核心理念是 radical simplicity:agent loop 仅 ~418 行 TypeScript,system prompt <1000 tokens,4 个核心工具。仓库 star 数以 GitHub 实时页面为准(本笔记补源时为 57k+)。

架构总览

pi-agent-architecture

指向原始笔记的链接

Pi 采用 4 层 monorepo 架构(NPM Workspaces),从底到顶:

包名职责
1pi-mono构建基础设施
2pi-ai统一 LLM API,15+ provider(OpenAI / Anthropic / Gemini / Bedrock 等),300+ 模型
3pi-agent-coreAgent 执行引擎:Agent class、agentLoop、事件流、状态管理
4pi-coding-agent领域实现:CLI(pi 命令)、Session、工具集、扩展加载

旁路包:

  • pi-tui — 终端 UI 库,差分渲染(只刷新变化部分),ANSI-aware
  • pi-web-ui — Lit web components,聊天面板 + artifact sandbox
  • pi-mom — Slack Bot,Docker sandbox 中安全执行
  • pi-pods — vLLM GPU 部署管理(RunPod / DataCrunch)

Agent Loop

核心循环在 packages/agent/src/agent-loop.ts,~418 行。执行流程:

flowchart TD
    A[用户输入] --> B[transformContext\n裁剪历史 / 注入外部数据]
    B --> C[convertToLlm\nAgentMessage → 标准 LLM Message]
    C --> D[stream\n调用 LLM 流式响应]
    D --> E{响应含\ntool call?}
    E -- Yes --> F[beforeToolCall\n参数校验 / 可 block]
    F --> G[执行 tools\n并行 Promise.all 或串行]
    G --> H[afterToolCall\n可覆盖结果]
    H --> I{检查 steering\n消息队列}
    I -- 有 steering --> B
    I -- 无 steering --> D
    E -- No --> J{有 follow-up\n消息?}
    J -- Yes --> B
    J -- No --> K[输出结果 / 结束]

    style A fill:#a5d8ff,stroke:#1971c2
    style D fill:#a5d8ff,stroke:#1971c2
    style E fill:#ffc9c9,stroke:#e03131
    style G fill:#b2f2bb,stroke:#2f9e44
    style K fill:#dee2e6,stroke:#868e96

关键机制

消息生命周期

AgentMessage 是富类型(支持 user / assistant / toolResult + 自定义类型),发送给 LLM 前经过两步管线:

  1. transformContext — 可选 hook,裁剪历史或注入外部数据(RAG、系统状态)
  2. convertToLlm — 过滤 UI-only 消息,转成标准 LLM 角色

Tool 执行模式

  • 并行(默认):先串行校验参数确保状态一致,然后 Promise.all 并发执行,事件按原始顺序 emit
  • 串行:逐个执行

Steering & Follow-up

  • Steering — agent 执行中途注入消息(例如用户中断补充指令),每个 turn 开头检查
    • all 模式:一次注入全部
    • one-at-a-time:每 turn 注入一条
  • Follow-up — agent 完成后追加任务(自动清理、二级任务等)

Tool Hooks

  • beforeToolCall — 参数校验后、执行前。可返回 { block: true } 阻止执行
  • afterToolCall — 执行后。可覆盖 content / details / isError

工具系统

两个工具集:

集合工具用途
codingToolsread, bash, edit, write可修改文件系统
readOnlyToolsread, grep, find, ls只读探索

核心工具

  • read — 读文件,支持图片(JPG/PNG/GIF/WebP → ImageContent),50KB / 2000 行截断,支持 offset + limit 分页
  • edit — 精确替换(oldText / newText),exact match 失败时 fuzzy fallback(去尾部空格、Unicode 引号/破折号归一化)
  • write — 整文件写入
  • bash — shell 命令执行,流式输出 + 截断

文件安全

withFileMutationQueue — 以绝对路径为 key 的 Map<string, Promise>,串行化同一文件的并发写操作,防 race condition。

resolveToCwd / resolveReadPath — 确保所有路径操作在工作目录内。

扩展机制

通过 ExtensionRunner 管理插件生命周期,支持:

  • Skills — 预设 prompt 模板
  • Themes — UI 主题
  • Pi Packages — NPM 包形式的扩展
  • 动态工具注册 — 运行时添加自定义 tool(如 image-gen、sub-agent)
  • Tool override — 替换内置工具行为

扩展示例见 packages/coding-agent/examples/extensions/(50+ 示例)。

二次开发

NPM 包分层

4 个 npm 包,按抽象层级由低到高:

npm用途
packages/agent@earendil-works/pi-agent-coreAgent loop、工具执行、事件流
packages/ai@earendil-works/pi-ai统一多 provider LLM 流式 API
packages/coding-agent@earendil-works/pi-coding-agentCLI + 完整 SDK,session、扩展
packages/tui@earendil-works/pi-tui差分终端渲染库

按需引用:嵌入自己的应用只需 pi-agent-core + pi-ai;完整功能用 pi-coding-agent

Extension 开发

扩展是一个导出 default 函数的 .ts 文件,参数为 ExtensionAPI,无需编译(jiti 加载):

import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
 
export default function (pi: ExtensionAPI) {
  // 注册工具、hook、命令等
}

加载路径(优先级从高到低):

  • --extension path/to/ext.ts CLI 参数
  • settings.json"extensions" 数组
  • <cwd>/.pi/extensions/ — 项目级
  • ~/.pi/agent/extensions/ — 全局

自定义工具

通过 defineTool() + pi.registerTool() 注册:

import { defineTool, type ExtensionAPI } from "@earendil-works/pi-coding-agent";
import { Type } from "@earendil-works/pi-ai";
 
const myTool = defineTool({
  name: "hello",
  label: "Hello",
  description: "A greeting tool",
  parameters: Type.Object({
    name: Type.String({ description: "Name to greet" }),
  }),
  async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
    return {
      content: [{ type: "text", text: `Hello, ${params.name}!` }],
      details: { greeted: params.name },
    };
  },
  // 可选:自定义 TUI 渲染
  renderCall(args, theme, _context) { /* ... */ },
  renderResult(result, opts, theme, _context) { /* ... */ },
});
 
export default function (pi: ExtensionAPI) {
  pi.registerTool(myTool);
}

核心接口 AgentTool

  • execute() 返回 { content, details, terminate? }
  • executionMode"parallel" | "sequential"
  • prepareArguments — 参数预处理

事件 Hook

pi.on(event, handler) 可拦截/修改 agent 行为:

事件触发时机可拦截?
tool_call工具执行前{ block: true, reason }
tool_result工具执行后✅ 可修改结果
before_provider_requestLLM API 调用前✅ 可修改消息
before_agent_startsession 首次 LLM 调用前✅ 可修改 prompt
input用户输入✅ 可转换文本
turn_start / turn_end每轮开始/结束
agent_start / agent_endagent 开始/结束
session_*session 生命周期部分可拦截
resources_discover资源发现✅ 可注入 skills/prompts

示例——权限网关:

pi.on("tool_call", async (event, ctx) => {
  if (event.toolName === "bash" && isDangerous(event.input.command)) {
    const choice = await ctx.ui.select("Allow?", ["Yes", "No"]);
    if (choice !== "Yes") return { block: true, reason: "Blocked" };
  }
});

自定义 LLM Provider

三种方式,灵活度递增:

1. models.json(零代码) — 适合 OpenAI/Anthropic 兼容代理:

  • ~/.pi/agent/models.json 中配置 baseUrl 和模型参数

2. pi.registerProvider()(Extension 级) — 完整 provider:

pi.registerProvider("my-provider", {
  baseUrl: "https://my-api.com",
  apiKey: "$MY_API_KEY",
  api: "my-api",
  models: [{ id: "my-model", name: "My Model", ... }],
  oauth: { /* PKCE OAuth 流程 */ },
  streamSimple: myStreamFn, // 返回 AssistantMessageEventStream
});

3. registerApiProvider()(pi-ai 底层) — 最低层注册

已有示例:custom-provider-anthropic/custom-provider-gitlab-duo/

SDK 嵌入

三个抽象层级,按需选择:

高层 — createAgentSession()

import { createAgentSession } from "@earendil-works/pi-coding-agent";
 
const { session } = await createAgentSession({
  model: "claude-sonnet-4",
  tools: ["read", "bash", "edit", "write"],
  customTools: [myTool],
  thinkingLevel: "medium",
});
 
session.subscribe((event) => {
  if (event.type === "message_update") {
    process.stdout.write(event.assistantMessageEvent.delta);
  }
});
await session.prompt("What files are here?");
session.dispose();

中层 — Agent class(pi-agent-core):

import { Agent } from "@earendil-works/pi-agent-core";
 
const agent = new Agent({
  initialState: { systemPrompt: "...", model, tools: [...] },
  beforeToolCall: async ({ toolCall }) => { /* 拦截 */ },
  afterToolCall: async ({ toolCall, result }) => { /* 后处理 */ },
});
await agent.prompt("Hello!");

底层 — agentLoop() async generator

for await (const event of agentLoop([msg], context, config)) {
  console.log(event.type);
}

RPC 模式

Pi 可作为子进程运行,通过 stdio JSON 通信,任何语言都能驱动。详见 packages/coding-agent/docs/rpc.md

自定义命令与快捷键

pi.registerCommand("todos", {
  description: "Show todos",
  handler: async (args, ctx) => { /* ... */ },
});
 
pi.registerKeybinding({
  key: "ctrl+shift+t",
  description: "Open todos",
  handler: async (ctx) => { /* ... */ },
});

配置文件一览

路径用途
~/.pi/agent/auth.jsonAPI 密钥、OAuth token
~/.pi/agent/settings.jsoncompaction、retry、image 等设置
~/.pi/agent/models.json自定义模型定义
~/.pi/agent/extensions/全局扩展
~/.pi/agent/prompts/prompt 模板
~/.pi/agent/themes/自定义主题
<cwd>/.pi/extensions/项目级扩展
<cwd>/.pi/skills/agent skills

不支持的

  • MCP(Model Context Protocol)— 不支持,代码库中零引用。Pi 用自己的原生扩展系统替代。

Session 管理

  • JSONL 存储 — 每个 session 一个 .jsonl 文件,追加写入
  • 树状分支 — session 可以 fork/branch,历史是树结构而非线性
  • Compaction — 上下文窗口管理:auto-compaction、不活跃分支摘要化、CompactionEntry pipeline

执行模式

模式说明
Interactive TUI默认,差分渲染终端 UI
Print / JSON非交互输出,适合脚本
RPC进程间通信
SDK embedding嵌入其他应用

与 Claude Code / Codex 对比

维度Pi AgentClaude CodeCodex CLI
开源✅ MIT❌ 商业
模型任意(15+ provider)Claude onlyOpenAI only
Token 开销低(system prompt <1000 tok)高(~3-4x)
核心工具4 (read/write/edit/bash)~15+~10
扩展Skills / Packages / ThemesMCP有限
SessionJSONL tree branching内置异步 PR 导向
优势轻量、透明、可 hack生态成熟、多工具CI/异步工作流

值得借鉴的设计

  1. 极简 agent loop — 418 行包含全部核心逻辑,降低理解和 debug 门槛
  2. 消息管线transformContextconvertToLlm 的两步转换让 AgentMessage 支持自定义类型,UI 消息和 LLM 消息解耦
  3. File Mutation Queue — 用 promise 链串行化同文件写操作,简洁有效
  4. Steering — 中途注入消息的能力让 agent 可被人类实时校正
  5. System prompt <1000 tokens — 证明了”少即是多”,prompt 越短模型越容易遵循

参考依据

补充时间:2026-05-30。以下为本笔记主要依据,后续以官方仓库最新内容为准。

  1. 仓库主页(stars、许可证、整体定位):https://github.com/earendil-works/pi
  2. 项目 README(monorepo 分层、核心理念、子包概览):https://raw.githubusercontent.com/earendil-works/pi/main/README.md
  3. Agent loop 源码(核心循环与 hook 机制):https://raw.githubusercontent.com/earendil-works/pi/main/packages/agent/src/agent-loop.ts
  4. 扩展机制文档(extensions、注册方式、加载路径):https://raw.githubusercontent.com/earendil-works/pi/main/packages/coding-agent/docs/extensions.md
  5. 自定义 provider 文档(models.json、registerProvider 等):https://raw.githubusercontent.com/earendil-works/pi/main/packages/coding-agent/docs/custom-provider.md
  6. 示例扩展目录(扩展样例集合):https://github.com/earendil-works/pi/tree/main/packages/coding-agent/examples/extensions
  7. RPC 文档(子进程 + stdio JSON):https://raw.githubusercontent.com/earendil-works/pi/main/packages/coding-agent/docs/rpc.md