返回首页
01
Pi-Agent编码Agent框架选型极简主义SDK

Pi-Agent 总览:一个框架的三重身份

为什么说"做减法"本身就是一项功能——编码工具、学习教材与开发 SDK

带着问题读

读完本章,你应该能回答这三个问题:

  1. 1Pi 把会话存成树结构而不是线性日志,这在工程上解决了什么实际问题?答案见正文中对应的「面试题 1」气泡
  2. 2让你给自己的 Agent 接入国内的 DeepSeek 或智谱模型,仅靠环境变量够吗?还差哪些配置?答案见正文中对应的「面试题 2」气泡
  3. 3Pi 默认采用无审批的 YOLO 模式,作者用什么理由来辩护这种'不安全'的设计?答案见正文中对应的「面试题 3」气泡

第 1 章:Pi-Agent 总览 —— 一个框架的三重身份

你可能因为三种不同的原因开始学习 Pi:

  1. 想找个好用的编码 Agent——受够了臃肿的工具,想要极简、透明、快的东西;
  2. 想知道 Agent 到底怎么做的——翻过的框架要么太复杂(几万行起跳),要么太简陋(一个 while 循环就敢叫 Agent);
  3. 要做自己的 Agent——有垂直场景需求,需要基于 SDK 二次开发,不想从零造轮子。

这三个问题恰好对应 Pi 的三个身份:编码工具、学习教材、开发 SDK。三个身份指向同一个项目,这本身就值得好奇。

Pi 是什么

一句话定义:Pi 是一款极简、可扩展的终端编码 Agent 外壳(coding agent harness),由 libGDX 作者 Mario Zechner 创建,全部用 TypeScript 编写,MIT 协议开源。

拆开看:

  • "编码 Agent"——能读懂代码库,写代码、改代码、跑命令,像坐在你旁边的结对编程伙伴;
  • "终端外壳"——住在终端里,没有 GUI 和 IDE 插件,输出写进终端回滚缓冲区。这个形态决定了一切后续设计选择;
  • "极简"——核心四个内置工具(read / write / edit / bash)、约 90 词的静态系统提示词模板(运行时拼接后通常 200–400 词)、约 12000 行 TUI 代码。它刻意不构建 MCP、子 Agent、计划模式、权限弹窗、后台 bash;
  • "可扩展"——缺失的功能通过 TypeScript 扩展、技能、Pi Package 补充。

关键数字(基于 v0.80.2 源码)

指标 数值 说明
GitHub Stars 64,000+(截至 2025 年中) 十个月的增长,社区验证了需求
内置工具数 4 核心 + 3 辅助 核心:read / write / edit / bash;辅助:grep / find / ls
系统提示词 静态模板约 90 词,运行时 200–400 词 对比 Claude Code 的数万 token
TUI 代码量 约 12000 行 核心 tui.ts 单文件约 1700 行
支持供应商 30+ 家 源码 KnownProvider 枚举实际 35 个(含区域变体),独立品牌约 27 个
核心包数量 4 个 pi-ai / pi-agent-core / pi-tui / pi-coding-agent
运行模式 4 种 交互 / print-JSON / RPC / SDK

关于数字的说明:官网早期材料常说"4 个内置工具"、"15+ 家供应商"、"约 600 行 TUI"——前两者分别指核心工具(不含辅助工具)和早期版本列举的知名厂商;"600 行 TUI"是早期版本数字,v0.80.2 已增长到约 12000 行。本表按源码实际数字呈现,避免你对照源码时困惑。

四个核心包,各司其职

┌──────────────────────────────────────────┐
│          pi-coding-agent                 │  ← 完整 CLI 产品 + SDK
│  系统提示词 · 内置工具 · 会话管理 · 扩展  │
├──────────────────────────────────────────┤
│  pi-tui              │  pi-agent-core    │  ← 终端 UI + Agent 引擎
│  差分渲染 · 组件系统  │  AgentLoop · 工具 │
│                      │  系统 · 事件流    │
├──────────────────────┴───────────────────┤
│              pi-ai                       │  ← 多供应商 LLM 抽象
│  统一 API · 上下文交接 · 流式 · Token 追踪│
└──────────────────────────────────────────┘

其中 pi-ai / pi-agent-core / pi-coding-agent 构成一条三层堆栈(每层可独立使用),pi-tui 是一个正交的 UI 库,与 Agent 体系完全解耦——你可以只用 pi-ai 调模型,也可以只用 pi-agent-core 在自己应用里跑 Agent Loop,完全不碰 CLI。这是 Pi 作为 SDK 的核心价值。

Pi-Agent 四层架构

外围还有一个实验性的 pi-orchestrator(v0.80.x 新增),负责多 Agent 编排,不在核心学习主线内。

视角一:作为编码工具

上下文干净得令人羡慕

Pi 的系统提示词 + 工具定义加起来不到 1000 个 token。对比 Claude Code 的数万 token,差距不是一点半点。上下文窗口是 Agent 最稀缺的资源——固定指令占得越少,留给你的代码和项目上下文的空间就越多。而且 Pi 不会在你背后偷偷注入任何东西:所有 prompt 源码公开可见,你甚至可以用 SYSTEM.md 文件把整个系统提示词替换掉。

技能(Skills)采用渐进式披露:只在被调用时才加载,不预加载进每个会话。你可以拥有一座能力库,而不必为用不上的会话付上下文开销。

透明到骨头里

你能看到模型收到的每一条消息、每个工具调用的完整输入输出、跨会话的成本追踪、会话的 HTML/JSON 导出。在其他编码 Agent 里,Agent 做了奇怪决定、你却看不到它"看到"了什么——在 Pi 里没有这种黑箱。

模型自由与树状会话

Pi 支持 30+ 家供应商,更重要的是可以在会话中途切换模型(/model 或 Ctrl+L)——比如用 Claude 做复杂推理,再切到便宜模型做简单文本处理。pi-ai 在底层处理跨供应商的上下文交接(思考轨迹转换、供应商特定签名 blob 回放等),本质上有损,但远比"切换等于重新开始"好。

会话存成**树结构(DAG)**而不是线性日志:用 /tree 跳到任意历史消息,从那里分叉出新分支继续探索,所有分支活在同一个文件里。调试时可以在同一起点尝试三种修复方案,不必担心"回不去了"。

YOLO 模式的安全哲学

Pi 默认不经审批弹窗直接执行动作。Mario 的论点是:基于审批的安全措施会导致"弹窗疲劳",最终要么被整体禁用、要么沦为看都不看就机械点同意的"安全表演(security theater)"。他建议以容器化作为真正的安全边界。如果确实需要审批流程,大约 50 行扩展代码就能自己实现——框架提供了所有必要的钩子。

用 models.json 接入第三方模型

官方默认让你设 ANTHROPIC_API_KEY,但实际项目里你大概率想用国内的智谱、DeepSeek、Kimi、Qwen——这些不可能靠一个环境变量搞定,你需要告诉 Pi:base URL 在哪、用哪种 API 协议、模型 ID 叫什么、上下文窗口多大。

Pi 的解法是本地配置文件 ~/.pi/agent/models.json,由 ModelRegistry.create() 在启动时自动读取,无需命令行参数:

{
  "providers": {
    "zhipu": {
      "baseUrl": "https://open.bigmodel.cn/api/paas/v4",
      "api": "openai-completions",
      "apiKey": "<your-zhipu-key>",
      "models": [
        { "id": "glm-4.5-air", "name": "GLM-4.5-Air" },
        { "id": "glm-4-flash", "name": "GLM-4-Flash" }
      ]
    },
    "deepseek": {
      "baseUrl": "https://api.deepseek.com",
      "api": "openai-completions",
      "apiKey": "<your-deepseek-key>",
      "models": [
        { "id": "deepseek-chat", "name": "DeepSeek V3" },
        {
          "id": "deepseek-reasoner",
          "name": "DeepSeek R1",
          "contextWindow": 128000,
          "maxTokens": 64000
        }
      ]
    }
  }
}

上面的模型 ID 和窗口数值仅作示意(deepseek-chat / deepseek-reasoner 是 DeepSeek 官方真实模型 ID),实际以各厂商文档为准。

关键字段:

  • providers——键名(zhipu/deepseek)是你自己起的,会作为模型的 provider 字段显示;
  • api——选协议,最常见的是 openai-completions(国内厂商几乎都支持 OpenAI 兼容接口),还有 anthropic-messages、openai-responses。这个字段决定 Pi 用哪种请求格式去调;
  • apiKey——明文存放,务必把 .pi/ 加进 .gitignore;
  • models——id 是调 API 时传的真实模型名,name 是 TUI 里的显示名;
  • contextWindow / maxTokens——可选,影响上下文压缩策略。

配置后三种用法:会话中 /model 或 Ctrl+L 临时切换;在 ~/.pi/agent/settings.json 里设 "defaultProvider" 和 "defaultModel" 作为默认;用 pi models 命令查列表(出错时会在终端顶部打印解析错误,方便排查)。进阶用法还有 modelOverrides(给内置 provider 打补丁,比如把 baseUrl 指向自部署网关)和 compat 字段(处理非标准接口兼容性),schema 完整定义见 model-registry.ts:158-218。

视角二:作为学习教材

很多 Agent 框架动辄几万行代码,光是搞清楚启动流程就要读几十个文件。Pi 的核心循环只有几百行,但设计质量毫不"简陋"——它在 TerminalBench 基准测试中排名第二(使用 Claude Opus 4.5,截至 2025 年中),仅次于 Terminus,尽管缺少 MCP、子 Agent、计划模式等功能。

这意味着你可以在有限时间内真正"读完"一个高质量 Agent 的全部核心代码——这对 Claude Code 和 LangChain 都是不可能的。

更宝贵的是它的减法哲学。Pi 官网的 "What we didn't build" 是一份倒过来的宣言:竞争对手在罗列功能,Pi 在罗列舍弃。每一次舍弃背后都有清晰的工程理由:

Pi 不做的 为什么不做 替代方案
MCP 支持 MCP 服务器(如 Playwright MCP)会在会话开始灌入 13,700+ token 的工具描述 带 README 的 CLI 工具,Agent 按需读取
子 Agent 增加复杂度,降低可观察性 tmux 多实例,或专用扩展
权限弹窗 导致"弹窗疲劳",沦为安全表演 容器化隔离,或用扩展搭审批流程
计划模式 计划写到 markdown 文件里更持久、可复用 写 plan.md
后台 bash tmux 已经解决了这个问题 用 tmux
内置待办 TODO.md 文件更灵活 markdown 文件或自建扩展

看一个"什么都做了"的框架,你只能学到"他们做了什么";看一个刻意什么都不做的框架,你才能学到"做 Agent 到底需要什么"。

视角三:作为开发 SDK

三层堆栈 + 一个正交 UI 库

Layer 1: pi-ai——只管调模型。 不依赖任何 Agent 概念,可以用在任何需要调 LLM 的项目里:

// 入口在 compat 子模块(不在主入口)
import { getModel, stream } from '@earendil-works/pi-ai/compat';
import type { Context } from '@earendil-works/pi-ai';

const model = getModel('anthropic', 'claude-sonnet-4-5');
// Context 是 interface(不是 class),用对象字面量构造
const context: Context = {
  systemPrompt: 'You are helpful.',
  messages: [{ role: 'user', content: 'Hello!' }],
};

// stream() 返回事件流;complete() 则直接 await 拿到最终 AssistantMessage
const eventStream = stream(model, context);
for await (const event of eventStream) {
  if (event.type === 'text_delta') process.stdout.write(event.delta);
}

Layer 2: pi-agent-core——只管跑循环。 依赖 pi-ai,但不依赖 pi-coding-agent 或 pi-tui。任何需要"模型思考 → 调工具 → 看结果 → 再思考"循环的场景都能用,不限于编码:

// 教学示意(简化);真实 API 见 agent.ts 的 Agent 类
// 构造函数只接受 AgentOptions(钩子、streamFn、convertToLlm 等)
// model/tools/systemPrompt 在调用 prompt() 时通过 AgentSessionConfig 传入
import { Agent } from '@earendil-works/pi-agent-core';

const agent = new Agent({ /* AgentOptions */ });
// 运行入口是 agent.prompt(),事件通过 subscribe(listener) 订阅

Layer 3: pi-coding-agent——完整 CLI + SDK。 把下面两层组装成产品,同时暴露"无头"(headless)SDK 接口:

import { createAgentSession } from '@earendil-works/pi-coding-agent';
import { getModel } from '@earendil-works/pi-ai/compat';

const session = await createAgentSession({
  cwd: '/path/to/project',
  model: getModel('anthropic', 'claude-sonnet-4-5'), // Model 对象,不是 {id, api}
});

session.subscribe((event) => {
  if (event.type === 'turn_end') console.log('Agent 完成了一轮思考');
});

await session.prompt('Read the codebase and explain the architecture.');

侧库: pi-tui——与 Agent 无关的终端 UI 库。 它的 package.json 只依赖 get-east-asian-width 和 marked 两个包,源码里零处 import 来自 @earendil-works/pi-* 兄弟包。约 12000 行实现了差分渲染(每帧只重绘变化的单元格)、保留模式 UI(类似 React 的声明式组件,而非 ncurses 的命令式)和内置组件(带自动补全的输入框、markdown 渲染、语法高亮、模糊搜索)。任何需要终端交互界面的 Node.js 程序都能用——这是游戏引擎作者 Mario 按游戏引擎思路重写的"副产物",反而成了最容易脱离 Pi 单独复用的部分。

四种运行模式

模式 用途 示例
交互模式 日常编程的经典 TUI pi
print/JSON 模式 脚本和 CI/CD 流水线 pi -p "explain this code"
RPC 模式 通过 stdin/stdout 交换 JSON 集成进非 Node.js 程序
SDK 模式 嵌入自己的应用 createAgentSession()

多模式设计意味着 Pi 可以从"开发者手边的工具"无缝演进为"生产系统中的 Agent 能力提供者",不需要在项目成长后换框架。

总结

Pi 是一个"三位一体"的项目:作为工具,它极简、透明、可驾驭;作为教材,它高质量且读得完,每行代码都有"为什么"的答案;作为 SDK,它层次分明、可独立复用。

但最重要的是,Pi 证明了做减法是一种有竞争力的产品立场。在一个朝着"全包"狂奔的赛道里,"我不需要的,就不会被构建"这句话本身,就是一项真正的功能。


版本说明:本系列基于 Pi v0.80.2 编写,代码分析以源码为准。官方仓库已从 badlogic/pi-mono 迁移至 earendil-works/pi。本文改编自 CC-BY-SA-4.0 许可的开源教程。