Pi Coding Agent:极简主义终端编码 Agent 深度解析
Pi(也称 pi-coding-agent)是由 Mario Zechner(libGDX 游戏框架作者,网名 badlogic)开发的开源(MIT 许可)、极简、有态度的终端编码 Agent。它不仅是一个 coding agent,更是一套围绕极简主义、可观测性和自修改性构建的完整技术哲学。
核心亮点:
- 自修改:Agent 可以修改自身的代码
- 极简工具集:只有
read、bash、edit、write四个工具 - OpenClaw 的基础:与 Armin Ronacher(Flask/Jinja2)和 Peter Steinberger 共同开发的更广泛 Agent 生态系统的核心引擎
- 通过 npm 安装:
npm install -g @mariozechner/pi-coding-agent - GitHub 组织:earendil-works/pi
Mario 的动机很直接:Claude Code 越来越臃肿——从简洁好用的工具变成了”宇宙飞船”,80% 的功能他用不到,系统 prompt 和工具定义每次更新都在变,破坏工作流,而且还闪烁。他的哲学:“如果我不需要它,它就不会被构建。“
架构:四层组件
Pi 由四个独立但可组合的包构成:
┌─────────────────────────────────────────┐│ Your Application ││ (OpenClaw, a CLI tool, a Slack bot) │├────────────────────┬────────────────────┤│ pi-coding-agent │ pi-tui ││ Sessions, tools, │ Terminal UI, ││ extensions │ markdown, editor │├────────────────────┴────────────────────┤│ pi-agent-core ││ Agent loop, tool execution, events │├─────────────────────────────────────────┤│ pi-ai ││ Streaming, models, multi-provider LLM │└─────────────────────────────────────────┘每一层递增添加能力,你可以按需取用——从最底层的统一 LLM API 到最上层的完整 CLI agent。
| 组件 | 功能 |
|---|---|
| pi-ai | 统一 LLM API,支持 Anthropic/OpenAI/Google/xAI/Groq/Cerebras/OpenRouter 及任何 OpenAI 兼容端点。流式输出、tool calling、thinking 支持、跨 provider 上下文交接、token/成本追踪 |
| pi-agent-core | Agent 循环:处理用户消息 → 执行工具调用 → 反馈给 LLM → 重复。事件流式输出,方便构建响应式 UI |
| pi-tui | 自研终端 UI 框架,差异渲染(differential rendering)、同步输出(几乎无闪烁)、编辑器组件(自动补全、Markdown 渲染) |
| pi-coding-agent | 把上面三个串起来的 CLI,加 session 管理、自定义工具、主题、项目上下文文件 |
三大核心理念
理念一:极简主义——“如果我不需要它,它就不会被构建”
这不仅仅是”少即是多”的口号,而是一套严格的工程决策体系。
系统 prompt 不到 1000 token(实际约 200 token),对比 Claude Code 的 ~10K token、OpenCode 的模型专属 prompt。Mario 的论点:前沿模型经过大量 RL 训练,天生就理解什么是 coding agent。10K token 的 prompt 更多是给人类看的”产品文档”而非模型必需品。Terminal-Bench 2.0 基准测试和几个月的实际使用验证了这一点——极简 prompt 并没有降低性能。
只有 4 个工具(read/write/edit/bash)。不需要 glob 工具——用 find via bash。不需要 grep 工具——用 rg via bash。不需要 web search 工具——用 curl via bash。每次增加工具都是在系统 prompt 里加 token,消耗上下文窗口。
没有 MCP、没有内置 Todo、没有 Plan Mode、没有 Background Bash、没有 Sub-agents。每个”不支持”都有明确的技术论据而非偷懒:
- MCP 的 token 开销:Playwright MCP 21 个工具吃 13.7K tokens(占上下文窗口 7-9%),Chrome DevTools MCP 26 个工具吃 18K tokens
- Todo 列表让模型追踪额外状态,反而增加出错机会
- Plan Mode 可以用文件实现(PLAN.md),且文件版跨 session 持久化、可版本控制
- Background Bash 用 tmux 天然解决,tmux 自带会话列表和附着功能
pi-ai 的四协议抽象:Mario 识别出所有 LLM provider 本质上只说四种协议(OpenAI Completions、OpenAI Responses、Anthropic Messages、Google Generative AI),在内部统一处理几十种 provider 特有的怪癖(Cerebras 不支持 store 字段、Mistral 用 max_tokens 而非 max_completion_tokens、Google 至今不支持 tool call streaming 等)。
核心 tradeoff:极简意味着更少的 token 开销(系统 prompt + 工具定义 < 1000 token),把上下文窗口留给真正的工作内容。代价是”开箱即用”的功能更少,需要用户自己构建。
理念二:可观测性——用户必须能看到一切
这是 Mario 从 Claude Code 等工具中感到最痛的点:背后的黑箱操作。
完整的事件流:pi-agent-core 的 agent loop 对每一步都发出事件——agent_start、turn_start、message_update(含 text_delta)、tool_execution_start、tool_execution_end、agent_end。任何 UI 或监控都可以订阅这个事件流,看到模型在做什么。
Sub-agent 问题的核心:Mario 强烈反对 Claude Code 的 sub-agent 模式——“zero visibility into what that sub-agent does. It’s a black box within a black box”。如果 sub-agent 犯错,调试极其痛苦,因为你完全看不到完整对话。他的替代方案:用 bash 调用 pi 自身,这样输出是可见的。
Context 的完全控制:Pi 的扩展系统有一个 context 钩子,能在消息发送给 LLM 之前拦截和修改消息数组。你可以精确控制什么进入上下文窗口——不像其他 agent 在背后偷偷注入内容。
Session 文件完全可读:JSONL 格式,每行一个 JSON 对象。你可以直接 cat、jq、grep 来检查 session 的任何细节。
Steer 和 FollowUp:steer() 中断当前执行注入用户消息(跳过剩余 pending 工具),followUp() 在 agent 自然结束后排队消息。这两个机制让用户能实时引导 agent,而不是被动等待。
理念三:自修改性——Agent 可以修改自己的代码
这是 Pi 最具革命性的特性。Armin Ronacher 对此有最好的阐述:
软件如黏土:Pi 的整个架构围绕”software that is malleable like clay”设计。当你需要 agent 做新事情时,不是下载扩展,而是让 agent 自己写扩展。
扩展系统的设计支撑:
- 扩展用 TypeScript 写,通过
jiti(运行时 TypeScript 加载)无需预编译 - 扩展有 20+ 生命周期钩子,可以注册工具、命令、键盘快捷键、UI 组件、持久化状态
- 扩展状态可持久化到 session 文件(不发给 LLM)
- 内置热重载——agent 写完代码,重载,测试,循环直到功能正常
Session 树使自修改安全:你可以在分支上修一个坏掉的扩展,修好后回退到主 session。Pi 自动总结分支上发生了什么。
Armin 的实践:他的所有扩展(/answer、/todos、/review、/files、/control)都是让 Pi 自己写的。“There is no MCP, there are no community skills, nothing. Don’t get me wrong, I use tons of skills. But they are hand-crafted by my clanker and not downloaded from anywhere.”
系统提示词
Pi 的完整系统提示词:
You are an expert coding assistant. You help users with coding tasks by reading files,executing commands, editing code, and writing new files.
Available tools:- read: Read file contents- bash: Execute bash commands- edit: Make surgical edits to files- write: Create or overwrite files
Guidelines:- Use bash for file operations like ls, grep, find- Use read to examine files before editing- Use edit for precise changes (old text must match exactly)- Use write only for new files or complete rewrites- When summarizing your actions, output plain text directly - do NOT use cat or bash to display what you did- Be concise in your responses- Show file paths clearly when working with files
Documentation:- Your own documentation (including custom model setup and theme creation) is at: /path/to/README.md- Read it when users ask about features, configuration, or setup, and especially if you ask about adding a custom model or provider, or create a custom theme.就这些。唯一在底部注入的是你的 AGENTS.md 文件。Pi 的系统提示词和工具定义加起来不到 1000 个 token。
四个内置工具
read
读取文件内容或查看图片。支持文本文件和图片(jpg、png、gif、webp)。图片以附件形式发送。文本文件默认读取前 2000 行,大文件用 offset/limit 分段。
write
写入内容到文件。文件不存在则创建,存在则覆盖。自动创建父目录。
edit
通过替换精确文本来编辑文件。oldText 必须完全匹配(包括空格)。用于精确、外科手术式的编辑。要求精确字符串匹配——不支持正则、不支持模糊匹配。如果搜索字符串不是精确匹配一次,会报出明确的错误。这迫使模型先读取文件再使用精确字符串。
bash
在当前工作目录执行 bash 命令。返回 stdout 和 stderr。可选择提供超时(秒)。不支持后台进程——如果需要,使用 tmux。
五大关键特性
特性一:跨 Provider 上下文交接
这是 pi-ai 从设计之初就内建的能力。
问题有多难:每个 provider 有自己的 tool call 格式、thinking trace 格式、签名 blob 回放机制。切换模型时甚至同一 provider 内的不同模型也可能不兼容。
Pi 的解法:
- Anthropic 的 thinking trace 转换为
<thinking>标签的文本块 - Tool call 和 tool result 格式统一为
Context.messages数组 - 每个 provider 的签名 blob 在后台处理,用户无感
Context对象完全可序列化为 JSON,可以存储、传输、之后反序列化继续
实际使用场景:
// 开始用 Claudeconst claude = getModel('anthropic', 'claude-sonnet-4-5');// 中途切到 GPT — 它能看到 Claude 的 thinking 作为 tagged textconst gpt = getModel('openai', 'gpt-5.1-codex');// 再切到 Geminiconst gemini = getModel('google', 'gemini-2.5-flash');// 序列化,以后用任何模型继续const serialized = JSON.stringify(context);对 token 成本的影响:你可以在简单任务上用便宜模型(Groq 的 Llama、Gemini Flash),复杂任务切到 Opus,同一个 session 内。Pi 内建每 session 的 token 和成本追踪,让 provider 路由变成数据驱动的决策。
特性二:Session 树(非线性历史)
这是 Pi 最独特的架构决策之一。
数据结构:Session 存储为 JSONL 文件,每条记录有 id 和 parentId,形成一个有向无环图(DAG)。切换分支只改变”叶指针”,不创建新文件。整个历史完整保留。
interface SessionEntry { id: string; parentId?: string; // 形成树结构 type: 'message' | 'tool_call' | 'tool_result' | 'meta'; timestamp: number; data: unknown;}实际工作流:
/tree命令在 session 内导航,选择任何历史节点,从那里分支出去/fork从任何分支点创建新 session- 在分支上修 bug 或实验新方案,不影响主线
- 修好后回退主线,Pi 自动总结分支发生了什么
- 可以保存完整 session 为 HTML、分享到 GitHub gist
对比线性历史:Claude Code / Codex / 大多数 agent 的 session 是线性的。Claude Code 有 compaction(压缩旧消息释放上下文窗口),但没有真正的分支和回退。Pi 的树结构意味着你可以无风险探索——不满意就切回另一个分支。
自定义消息:Session 文件支持 meta 类型条目,扩展可以用它持久化状态(不发给 LLM)。OpenClaw 用这个做每通道的 session 隔离和崩溃恢复——JSONL 是追加写的,崩溃最多丢失一行。
特性三:差异渲染的终端 UI(pi-tui)
Mario 不满意现有的 TUI 方案(Ink 要写 React、Blessed 无人维护、OpenTUI 明确标注不生产就绪),所以自己写了一个。
两种 TUI 路线的选择:
- 全屏 TUI(Amp、OpenCode 用):接管终端视口,像像素缓冲区一样管理。缺点:丢失原生滚动缓冲区和搜索功能,鼠标滚动体验差
- 追加式 TUI(Claude Code、Codex、Pi 用):正常输出到终端,只在需要时回退渲染光标重画动画。保留原生滚动和搜索
Pi 选择追加式,因为 coding agent 天然是线性对话,追加式有更好的信息密度。
差异渲染算法:
- 首次渲染:直接输出所有行
- 宽度变化:清屏完全重绘(因为软换行变了)
- 正常更新:找到第一行不同的地方,移动光标到那一行,从那里重绘到末尾
防闪烁:用同步输出转义序列(CSI ?2026h / CSI ?2026l)包裹所有渲染,告诉终端缓冲所有输出然后原子显示。在 Ghostty/iTerm2 上效果极佳,VS Code 终端有轻微闪烁但比 Claude Code 少。
组件模型:保留模式(Retained Mode)——Component 有 render(width) 方法返回行数组,可以缓存已完成的渲染结果。Container 垂直排列子组件。TUI 类协调一切。
性能:存储整个滚动缓冲区的之前渲染行做比较,加上组件缓存,在 25 年内的任何计算机上都不是性能问题(几百 KB 内存用于非常大的 session)。
Mario 甚至在 pi-tui 里跑过 Doom——不是为了实用,而是证明如果能在 TUI 里跑 Doom,你一定能构建有用的 dashboard 或调试界面。
特性四:结构化工具结果分离
这是一个在其他统一 LLM API 中很少见的抽象。
设计:每个工具返回两个东西——
content:发给 LLM 的内容(文本/JSON)details:给 UI 渲染的结构化数据(不发到模型)
interface ToolResult { output: string; // 模型看到的 details?: { // UI 可以渲染的 type: 'file' | 'diff' | 'terminal' | 'error'; data: unknown; };}为什么重要:
- 模型的上下文保持干净——不需要为了 UI 显示在文本里塞结构化信息
- UI 可以渲染丰富内容(diff 高亮、文件树、终端输出着色)而不需要解析文本
- 工具还可以返回图片附件,以 provider 原生格式发送
partial JSON 解析:tool call 参数在流式传输时就被渐进解析,UI 可以在调用完成前显示部分结果——比如 edit 工具的 diff 可以一边流一边显示。
对比:大多数统一 API 只返回文本给模型和 UI 共用。Pi 的分离让模型看到精简信息,UI 看到丰富信息,各取所需。
特性五:扩展系统(20+ 生命周期钩子)
扩展系统是 Pi 自修改能力的载体。
运行时加载:用 jiti 做 TypeScript 运行时加载,无需预编译。Agent 写完扩展代码,热重载立即生效。
核心钩子分类:
| 类别 | 钩子 | 作用 |
|---|---|---|
| 上下文控制 | context | 在消息发给 LLM 前拦截、修改消息数组 |
| 压缩控制 | session_before_compact | 用自定义 summarization 替换默认压缩 |
| 工具拦截 | tool_call | 拦截或阻止工具调用 |
| Session 生命周期 | session_start、session_switch | 响应 session 创建/切换 |
| Agent 控制 | before_agent_start | 注入上下文或修改 prompt |
扩展能做的事:
- 注册自定义工具(
registerTool) - 注册命令(
registerCommand) - 注册键盘快捷键
- 注册 UI 组件(spinner、progress bar、文件选择器、数据表、预览面板)
- 持久化状态到 session
- 拦截 bash 命令(比如 Armin 的
pip→uv重定向) - 替换系统 prompt、切换模型、修改工具集
分发模型:
- npm 包:
pi install npm:@foo/bar - Git 仓库:
pi install git:github.com/user/repo - 本地路径:直接引用
- 单次使用:
pi -e npm:@foo/bar(ephemeral) - 可组合:多个扩展堆叠,互不干扰
Skills 系统的渐进披露:Skill 是 YAML-frontmatter 的 Markdown 文件。关键设计——只有 description 常驻上下文,完整内容按需加载。Armin 用 skill 让 Pi 自动维护 uv 替代 pip 的行为、自动生成 commit message、更新 changelog。甚至添加了一个扩展拦截 pip 和 python 调用重定向到 uv。
设计决策:为什么 Pi 没有 X
YOLO 模式——默认全部放行
Pi 默认以完全 YOLO 模式运行。没有权限检查,没有安全围栏。完整的文件系统访问。可以用你的用户权限执行任何命令。
Mario 的论点:“Security in coding agents is mostly security theater. As soon as your agent can write code and run code, it’s pretty much game over.” 核心问题:如果 LLM 有能力读取私人数据和发起网络请求的工具,你在攻击向量上玩的是打地鼠游戏。
不支持 MCP——设计如此
MCP 服务器在每次会话时将整个工具描述 dump 到上下文中。Playwright MCP:21 个工具,13.7k tokens。Chrome DevTools MCP:26 个工具,18k tokens。那是上下文窗口的 7-9% 在你开始之前就没了。
替代方案:构建带 README 文件的 CLI 工具。Agent 需要工具时读取 README,只在必要时支付 token 成本(渐进披露)。Mario 维护了 agent-tools 在 github.com/badlogic/agent-tools。
不支持 Background Bash
使用 tmux 代替。tmux 提供会话列表、附着和完全的可观测性。Claude Code 的 background bash 可观测性差,早期版本甚至在上下文压缩后忘记进程的存在。
不支持 Sub-agents
“zero visibility into what that sub-agent does. It’s a black box within a black box”
如果你需要 pi 生成自身,只需要求它通过 bash 运行自身。你甚至可以让它在 tmux session 内生成自身以获得完全的可观测性。
Mario 的核心洞察:“Using a sub-agent mid-session for context gathering is a sign you didn’t plan ahead.” 相反,应该先在自己的 session 中收集上下文,创建一个 artifact,然后在新的 session 中使用这个 artifact。
Session 管理
工厂方法:
SessionManager.inMemory()——临时会话SessionManager.create(process.cwd())——新的持久会话SessionManager.open("/path/to/session.jsonl")——打开特定文件SessionManager.continueRecent(process.cwd())——继续最近的会话
关键方法:
buildSessionContext()——从 JSONL 重建对话getLeafEntry()——获取当前分支的最后一条记录branch(entryId)——从特定点分叉appendMessage(message)——以编程方式注入消息getTree()——获取完整的树结构
工具工厂与自定义
Pi 的工具可以通过工厂方法深度定制:
const customCodingTools = createCodingTools("/path/to/workspace");const customRead = createReadTool("/workspace", { operations: { readFile: async (path) => fetchFileFromRemote(path), },});const sandboxedBash = createBashTool("/workspace", { operations: { exec: async (command, cwd, opts) => runInDockerContainer(command, cwd, opts), },});operations 对象让你覆盖底层 I/O——适用于 Docker、SSH 或虚拟文件系统。
工具定义使用 TypeBox 模式 + AJV 校验。当校验失败时,错误作为 tool result 返回给模型,让它能自我纠正。这消除了整类运行时故障。
RPC 模式
pi --mode rpc 将 Pi 变成一个无头子进程,通过 stdin/stdout 上的 JSONL 控制。26+ 个双向命令。没有 TUI,没有终端,只有纯粹的编程控制。这就是 OpenClaw 嵌入 Pi 的方式。
pi-ai Provider 怪癖处理
Pi 在内部处理数十个 provider 特有的怪癖:
- Cerebras、xAI、Mistral 不支持
store字段 - Mistral 使用
max_tokens而非max_completion_tokens - Cerebras、xAI、Mistral 不支持
developer角色 - Google 至今不支持 tool call streaming
- 不同 provider 以不同字段返回推理内容
基准测试与排名
Terminal-Bench 2.0
Mario 使用 Claude Opus 4.5 运行了 Terminal-Bench 2.0。尽管工具远少于其他 agent,Pi 排名接近榜首,接近或超过 Codex、Cursor、Windsurf。
值得注意的是,Terminus 2(Terminal-Bench 团队自己的最小 agent,只给模型一个 tmux session)也表现良好。这证明了极简方法的有效性。
Pinggy 排名(2026)
| CLI Agent | GitHub Stars | License | Best For |
|---|---|---|---|
| OpenCode | ~165k | MIT | 默认开源选择 |
| OpenAI Codex CLI | ~85k | Apache-2.0 | OpenAI 用户,沙箱化 |
| OpenHands | ~75k | MIT | 自主、沙箱化、CI/CD |
| Cline | ~62k | Apache-2.0 | 跨 IDE、CLI、SDK 的统一 agent |
| Pi | ~54k | MIT | 精简、可 hack、token 高效 |
| Goose | ~46k | Apache-2.0 | MCP 驱动的通用自动化 |
| Aider | ~45k | Apache-2.0 | Git 原生的精确编辑 |
Pi 被描述为”最有趣的新入场者”。
Pi vs Claude Agent SDK 对比
| 维度 | Pi | Claude Agent SDK |
|---|---|---|
| 模型锁定 | 无(15+ provider) | 仅 Claude |
| 语言 | 仅 TypeScript | TypeScript + Python |
| 许可证 | MIT | 专有 |
| 默认工具 | 4 | 10+ |
| MCP 支持 | 无(设计如此) | 原生、一等公民 |
| Sub-agents | 通过扩展/tmux | 内建 |
| Session 模型 | 树(非线性) | 线性 + 压缩 |
| 安全默认 | YOLO | 拒绝优先(5 种模式) |
| 成本追踪 | 内建 | 无内建 |
| 自托管模型 | 原生支持(Ollama, vLLM) | 不支持 |
| 系统 prompt 大小 | ~200 tokens | ~10K tokens |
| 社区验证 | OpenClaw(145K+ stars) | Claude Code(数百万用户) |
其他 Pi 生态包
- pi-web-ui——基于 Lit 的 Web 组件,用于浏览器端聊天界面
- pi-mom——Slack 机器人,委托给 pi-coding-agent,具有每通道隔离和 Docker 沙箱
- pi-pods——CLI 工具,通过 vLLM 在 GPU pods 上部署开源模型(支持 DataCrunch、RunPod、Vast.ai、裸金属)
Agentic Engineering 四维度分析
从 agentic engineering 的视角,agent 控制有四个维度:
- Context——什么进入模型的上下文窗口
- Model——使用哪个模型
- Prompt——系统提示词和指令
- Tools——agent 有哪些能力
Claude Code:强大的默认值,~10K token 系统 prompt,10+ 工具,锁定到 Claude 模型家族。
Pi:空白画布,~200 token 系统 prompt,4 个工具,20+ provider 的 324 个模型。
关键引用
Mario Zechner:
“I’m a simple boy who likes simple, predictable tools.”
“Context engineering is paramount. Exactly controlling what goes into the model’s context yields better outputs.”
“All frontier models have been RL-trained up the wazoo, so they inherently understand what a coding agent is.”
“Security in coding agents is mostly security theater.”
“If I don’t need it, it won’t be built.”
“There are many coding agents, but this one is mine.”
Armin Ronacher:
“Pi is interesting because it has a tiny core with the shortest system prompt and only four tools.”
“It makes up for its tiny core by providing an extension system that allows extensions to persist state into sessions.”
“Pi itself is written like excellent software. It doesn’t flicker, it doesn’t consume a lot of memory, it doesn’t randomly break.”
“None of this was written by me, it was created by the agent to my specifications.”