字节笔记本
2026年2月15日
构建一个固执己见且极简的编程代理:我学到了什么
作者 Mario Zechner 分享了他构建 "pi" , 一个极简编程代理工具的经验和教训。探讨了为什么现有工具过于复杂,以及如何通过最小化方法实现同样有效的编程辅助。
引言
在过去三年中,我使用 LLM 辅助编程,经历了从 ChatGPT 复制粘贴、Copilot 自动补全、Cursor,到 2025 年成为日常工具的 Claude Code、Codex、Amp、Droid、opencode 等编程 agent 工具。
我最喜欢 Claude Code,但认为它已变成"80% 功能都用不上的宇宙飞船"。系统提示和工具每次发布都变化,破坏工作流,还会闪烁。
我认为上下文工程至关重要,但现有工具很难控制输入模型的内容,也无法检查交互的每个方面。我还想要可自动后处理的会话格式,以及构建替代 UI 的简单方式。
此外,自托管模型时,现有工具通常依赖 Vercel AI SDK 等库,与自托管模型配合不佳,特别是工具调用方面。
因此我决定构建自己的编程 agent 工具,命名为"pi"(完全无法 Google 的名字)。
需要构建的组件:
- pi-ai:统一 LLM API,多提供商支持
- pi-agent-core:处理工具执行、验证和事件流的 agent 循环
- pi-tui:最小化终端 UI 框架
- pi-coding-agent:实际 CLI,整合所有功能
哲学:如果不需要,就不构建。
pi-ai 和 pi-agent-core
只需四个 API
只需四个 API 就能与几乎所有 LLM 提供商对话:
- OpenAI Completions API
- OpenAI Responses API
- Anthropic Messages API
- Google Generative AI API
但每个提供商对 Completions API 的理解不同:
- Cerebras、xAI、Mistral、Chutes 不喜欢
store字段 - Mistral 和 Chutes 使用
max_tokens而非max_completion_tokens - Cerebras、xAI、Mistral、Chutes 不支持
developer角色 - Grok 模型不喜欢
reasoning_effort - 不同提供商在不同字段返回推理内容
pi-ai 有广泛的测试套件,但新模型和提供商仍可能无法即开即用。token 和缓存读写的报告方式也是"狂野西部"。pi-ai 尽力进行 token 和缓存跟踪,适合个人使用,但不适合需要精确计费的场景。
Google 至今不支持工具调用流式传输。
上下文交接
pi-ai 设计之初就支持提供商之间的上下文交接。由于每个提供商跟踪工具调用和思考痕迹的方式不同,这只能是尽力而为。例如,从 Anthropic 切换到 OpenAI 时,Anthropic 的思考痕迹会转换为带 <thinking> 标签的内容块。
提供商还会在事件流中插入签名 blob,必须在后续请求中重放。这使得抽象和转换管道变得复杂。
我们生活在多模型世界
我希望以类型安全的方式在 getModel 调用中指定模型。我从 OpenRouter 和 models.dev 解析数据生成 TypeScript 类型,包括 token 成本和能力。
许多统一 LLM API 完全忽略中止请求的功能,这对生产系统不可接受。pi-ai 从一开始就支持整个管道的中止,包括工具调用。
结构化分割工具结果
pi-ai 的工具实现允许将工具结果分成两部分:给 LLM 的部分和给 UI 显示的部分。工具参数使用 TypeBox 模式和 AJV 自动验证。
工具调用流式传输期间的 partial JSON 解析对良好 UX 至关重要。pi-ai 渐进式解析参数,可在调用完成前在 UI 中显示部分结果。
最小化 Agent 脚手架
pi-ai 提供 agent 循环,处理完整编排:处理用户消息、执行工具调用、将结果反馈给 LLM,重复直到模型产生无工具调用的响应。
为什么不使用 Vercel AI SDK?直接基于提供商 SDK 构建可完全控制,设计完全符合需求的 API,表面更小。
pi-tui
我成长于 DOS 时代,TUIs 伴随我直到 90 年代末。虽然 TUIs 可移植且易于流式传输,但信息密度差。不过我认为为 pi 先从 TUI 开始最有意义,以后可随时添加 GUI。
两种 TUI
- 全屏 TUI:拥有终端视口所有权,像像素缓冲区一样处理。失去滚动回退缓冲区,必须实现自定义搜索和滚动。Amp 和 opencode 使用此方法。
- 原生终端 TUI:像普通 CLI 程序一样写入终端,只偶尔移动"渲染光标"重绘动画旋转器或文本编辑字段。Claude Code、Codex、Droid 使用此方法。
pi-tui 选择第二种方向。
保留模式 UI
GUI 编程中有 retained mode 与 immediate mode 之分。pi-tui 使用简单的 retained mode 方法。Component 是具有 render(width) 方法的对象,返回字符串数组(适合视口宽度的行,带 ANSI 转义码)。
差异渲染
算法简单:
- 首次渲染:直接输出所有行到终端
- 宽度变化:完全清屏并重新渲染一切
- 正常更新:找到与屏幕上不同的第一行,将光标移动到该行,从那里重新渲染到末尾
为防止更新期间闪烁,pi-tui 将所有渲染包装在同步输出转义序列中,告诉终端缓冲所有输出并原子显示。
pi-coding-agent
pi 具备其他工具的预期功能:跨平台、多提供商支持、会话管理、项目上下文文件、斜杠命令、OAuth 认证、可定制主题、编辑器功能、消息排队、图像支持、会话导出、无头操作、成本和 token 跟踪。
更有趣的是 pi 在哲学和实现上的差异。
最小化系统提示
系统提示仅约 1000 token,包含:
- 角色定义:专家编程助手
- 可用工具:read、bash、edit、write
- 使用指南
- 文档位置
底部只注入用户的 AGENTS.md 文件(全局和项目特定)。可完全替换系统提示。
相比 Claude Code、Codex、opencode 的庞大系统提示,这似乎疯狂。但前沿模型都经过大量 RL 训练,inherently 理解编程 agent 是什么。不需要 10000 token 的系统提示。
最小化工具集
工具定义:
- read:读取文件内容,支持文本和图像
- write:写入文件内容,自动创建父目录
- edit:通过替换精确文本编辑文件
- bash:执行 bash 命令,可选超时
还有只读工具(grep、find、ls)可限制 agent 不修改文件或运行任意命令,默认禁用。
这四个工具就是有效编程 agent 所需的全部。系统提示和工具定义合计低于 1000 token。
默认 YOLO 模式
pi 默认全 YOLO 模式,假设用户知道自己在做什么。对文件系统无限制访问,可执行任何命令,无权限检查或安全护栏。
其他编程 agent 的安全措施大多是安全剧场。agent 能写代码和运行代码时,游戏基本结束。防止数据外泄的唯一方法是切断执行环境的网络访问,这使 agent 基本无用。
由于无法解决这三重能力(读取数据、执行代码、网络访问),pi 干脆放弃。大家为完成工作都在运行 YOLO 模式,何不使其成为默认和唯一选项?
无内置待办事项
pi 不支持内置待办事项。经验表明,待办事项列表通常使模型困惑多于帮助,增加模型必须跟踪和更新的状态,引入更多出错机会。
如需任务跟踪,外部状态化写入文件:
# TODO.md
- [x] Implement user authentication
- [x] Add database migrations
- [ ] Write API documentation
- [ ] Add rate limitingagent 可按需读取和更新此文件。简单、可见、受控。
无计划模式
pi 无内置计划模式。告诉 agent 与你一起思考问题,不修改文件或执行命令,通常足够。
如需跨会话持久计划,写入文件:
# PLAN.md
## Goal
Refactor authentication system to support OAuth
## Approach
1. Research OAuth 2.0 flows
2. Design token storage schema
...
## Current Step
Working on step 3 - authorization endpointsagent 可读取、更新和引用计划工作。与仅存在于会话中的临时计划模式不同,基于文件的计划可跨会话共享,可与代码一起版本控制。
如必须在计划期间限制 agent,可通过 CLI 指定工具访问:pi --tools read,grep,find,ls
无 MCP 支持
pi 不支持 MCP。MCP 服务器对大多数用例过度,带来显著上下文开销。
流行 MCP 服务器如 Playwright MCP(21 工具,13.7k token)或 Chrome DevTools MCP(26 工具,18k token)每次会话将整个工具描述转储到上下文中,占上下文窗口 7-9%,许多工具给定会话中从不使用。
替代方案简单:构建带 README 文件的 CLI 工具。agent 需要时读取 README,仅必要时支付 token 成本(渐进披露),可用 bash 调用工具。此方法可组合、易于扩展、token 高效。
无后台 Bash
pi 的 bash 工具同步运行命令。无内置方式启动开发服务器、后台运行测试,或命令仍在运行时与 REPL 交互。
这是故意的。后台进程管理增加复杂性:需要进程跟踪、输出缓冲、退出时清理、向运行进程发送输入的方式。
改用 tmux。Tmux 还提供 CLI 参数列出所有活动会话。无需后台 bash。
无子 Agent
pi 无专用子 agent 工具。Claude Code 需要做复杂事情时,常生成子 agent 处理部分任务。对子 agent 的行为零可见性,是黑盒中的黑盒。
如需 pi 生成自身,只需让它通过 bash 运行自身。甚至可让它在 tmux 会话中生成自身以获得完全可观察性并直接与该子 agent 交互。
更重要的是:修复工作流,至少那些关于上下文收集的。人们在会话中使用子 agent 以为在节省上下文空间,这没错。但这是错误的子 agent 思考方式。会话中需要上下文收集时使用子 agent 是事先未计划的迹象。
因为尽管普遍看法如此,模型仍不擅长找到实现新功能或修复错误所需的全部上下文。
基准测试
作者创建 Terminal-Bench 2.0 测试运行,pi 使用 Claude Opus 4.5 与 Codex、Cursor、Windsurf 等竞争。
结果显示 pi 在排行榜上的位置,以及 CET-only 运行的初步结果。
注意 Terminus 2 在排行榜上的排名。Terminus 2 是 Terminal-Bench 团队自己的最小化 agent,只给模型 tmux 会话。模型以文本形式发送命令到 tmux 并自己解析终端输出。无花哨工具,无文件操作,只有原始终端交互。与工具复杂得多的 agent 抗衡,适用于多种模型。更多证据表明最小化方法同样有效。
总结
Benchmark 结果很有趣,但真正证据在于日常工作,pi 在日常工作中表现出色。Twitter 满是上下文工程帖子和博客,但感觉现有工具都不真正让你做上下文工程。pi 是作者尝试构建尽可能受控的工具。
对 pi 的现状相当满意。还想添加一些功能,如 compaction 或工具结果流式传输,但个人认为不需要更多。
认为上述部分学习可转移到其他工具。
原文作者: Mario Zechner 原文链接: https://mariozechner.at/posts/2025-11-30-pi-coding-agent/ 翻译日期: 2026-02-16