ByteNoteByteNote

字节笔记本

2026年2月15日

构建一个固执己见且极简的编程代理:我学到了什么

API中转
¥120

作者 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 提供商对话:

  1. OpenAI Completions API
  2. OpenAI Responses API
  3. Anthropic Messages API
  4. 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

  1. 全屏 TUI:拥有终端视口所有权,像像素缓冲区一样处理。失去滚动回退缓冲区,必须实现自定义搜索和滚动。Amp 和 opencode 使用此方法。
  2. 原生终端 TUI:像普通 CLI 程序一样写入终端,只偶尔移动"渲染光标"重绘动画旋转器或文本编辑字段。Claude Code、Codex、Droid 使用此方法。

pi-tui 选择第二种方向。

保留模式 UI

GUI 编程中有 retained mode 与 immediate mode 之分。pi-tui 使用简单的 retained mode 方法。Component 是具有 render(width) 方法的对象,返回字符串数组(适合视口宽度的行,带 ANSI 转义码)。

差异渲染

算法简单:

  1. 首次渲染:直接输出所有行到终端
  2. 宽度变化:完全清屏并重新渲染一切
  3. 正常更新:找到与屏幕上不同的第一行,将光标移动到该行,从那里重新渲染到末尾

为防止更新期间闪烁,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 不支持内置待办事项。经验表明,待办事项列表通常使模型困惑多于帮助,增加模型必须跟踪和更新的状态,引入更多出错机会。

如需任务跟踪,外部状态化写入文件:

markdown
# TODO.md

- [x] Implement user authentication
- [x] Add database migrations
- [ ] Write API documentation
- [ ] Add rate limiting

agent 可按需读取和更新此文件。简单、可见、受控。

无计划模式

pi 无内置计划模式。告诉 agent 与你一起思考问题,不修改文件或执行命令,通常足够。

如需跨会话持久计划,写入文件:

markdown
# 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 endpoints

agent 可读取、更新和引用计划工作。与仅存在于会话中的临时计划模式不同,基于文件的计划可跨会话共享,可与代码一起版本控制。

如必须在计划期间限制 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

分享: