我为什么将主力 Coding Agent 切换为 Pi?


本文总阅读量次

why-i-choose-pi-coding-agent.png


现在的 coding agent 领域,读写代码、执行终端指令、挂载 MCP 早就是标配了。既然大家功能都差不多,真正让我决定去留的其实是个工程问题:为了实现这些功能,系统在后台到底塞了多少隐性状态?浪费了多少提示词?

我最终把主力开发工具换成了 pi-coding-agent。不是因为它功能最全,而是因为它没有用那种过度封装的黑盒来掩盖复杂度。它只是给了一个极简的脚手架,剩下的边界和安全策略,全都交给我自己来定。

状态管理:拒绝黑盒

用别的平台级 Agent 时,最让我头疼的就是状态管理太不透明。

比如 Codex,或者其他号称“开箱即用”的高级 Agent,很喜欢把执行日志、遥测数据和上下文状态全都塞进 SQLite 数据库,甚至深层的二进制缓存里。平时用着没感觉,但一旦出了幺蛾子,你根本不知道系统在后台到底偷偷读取和做了什么。

去翻翻 6 月份Codex 的社区讨论就知道了。有个老哥偶然发现,因为系统在后台疯狂写入 TRACE 和遥测日志,Codex 的本地 SQLite 数据库在短短 21 天里竟然吃掉了 37TB 的写入量。这是什么概念?照这个速度,一年能报废好几块固态硬盘,这让人毛骨悚然。

再看 Pi,它干脆不要内置的状态机。所有的会话状态就明明白白地存在一个纯文本的 JSONL 文件里。做任务规划?直接读写 PLAN.md 或者 TODO.md。

我就喜欢这种“土”办法。因为这意味着我跟 Agent 看到的是同一份事实。我不必担心 LLM 上下文一旦被压缩,整个任务链就莫名其妙断裂;我甚至可以直接用 Git 来追踪它的状态,跨模型复用也毫无压力。

甩掉冗余的上下文包袱

现在很多 Agent 还有个通病:为了显得自己“无所不能”,默认会给模型注入一大堆预设工具和全局调度逻辑。有时候我只是想让它帮忙改一行 CSS,模型却要背着上千 tokens 的工具 Schema 负重前行。

Anthropic 显然也意识到了这个问题,刚把 Claude Code 的核心系统提示词削减了 80%。事实证明,只要模型够聪明,精简的指令反而更高效。

Pi 从一开始就是这个思路。它默认只提供四个最基础的工具:read、write、edit、bash。现在的前沿模型早就知道 coding agent 是干嘛的了,真不需要你再用几千行废话去教它怎么读文件。

举个例子,别的系统喜欢搞个专门的 todowrite 工具来管理任务。Pi 的逻辑是:你有 read,有 write,再建个 TODO.md 文件,这事儿不就结了?搞什么黑盒工具。

砍掉这些乱七八糟的工具后,空载提示词直接降到了 1.2K 左右。更少的选择意味着模型更少产生幻觉,出了 bug 翻调用链也轻松得多。如果真遇到复杂任务,再按需写个扩展就行。

把底层控制权拿回来

当然,光是“轻量”没用,搞不好就成了简陋。Pi 真正吸引我的,是它把运行时的底层控制权通过 TypeScript 扩展(Extension API)完全交了出来。

你不是在系统外面敲敲打打,而是直接接管底层逻辑。

比如,我实在不放心它乱动环境配置。那就在工具调用前加一层正则拦截,死死盯住 .env 文件;再比如,给 bash 工具加个人工确认,只要看到 rm -rf,必须我点头才能执行:

import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";

export default function (pi: ExtensionAPI) {
  // 拦截并阻断敏感文件修改
  pi.on("tool_call", event => {
    if (
      ["write", "edit"].includes(event.toolName) &&
      String(event.input.path).endsWith(".env")
    ) {
      return { block: true, reason: "禁止修改 .env 文件" };
    }
  });

  // 对危险命令增加人工确认拦截
  pi.on("tool_call", async (event, ctx) => {
    if (event.toolName === "bash" && event.input.command?.includes("rm -rf")) {
      const allowed = await ctx.ui.confirm("危险命令", "是否允许执行 rm -rf?");
      if (!allowed) return { block: true, reason: "用户拒绝执行" };
    }
  });
}

这种解耦不仅限于指令拦截。模型以为自己调用的只是普通的 read 和 bash,但通过路由,我可以把这些操作重定向到远程 SSH、Docker 容器甚至安全沙箱里。不用为了不同的环境去重复折腾 Agent。

为了控制上下文大小,它的 Skills 体系也是按需加载的。当然,我得承认这也不是完美的——只要挂载了扩展,启动时多少还是得消耗一点 tokens 来注入名称和描述。但它起码给了你一个开关,不用看着无关技能白白烧掉你的 API 额度。

自由的代价

天下没有免费的午餐。Pi 给了你极大的自由,代价就是你得自己承担所有的工程复杂度。

它的默认权限非常奔放。你给了它 bash 工具,它就等于拥有了你当前账号的全部权限,连个确认弹窗都不会默认给你加。如果对安全要求高,你只能自己去配 Docker 隔离或者写拦截脚本。

而且它根本不提供什么“最佳实践”。容器环境怎么搞、插件兼不兼容,全得自己折腾。如果在团队里推行,因为高度可定制,很容易出现“在我的机器上明明能跑”的尴尬局面,必须严格把配置和 Skill 打包进代码审查。

如果你极度厌烦后台悄悄加载一堆没用的功能,并且愿意自己写代码去组装一套完全受控的工作流,Pi 会用起来很爽。但如果你只想开箱即用,不想折腾环境, Claude Code 或 Codex 相对来说更适合。

最后留下来用 Pi,不是因为它现在无所不能,仅仅是因为它没让我为那些不需要的功能买单。


本站总访问量次