Pi Agent Core 实战(2/10):三层拆开看

很多人第一次看 pi 的仓库会困惑:为什么一个 Agent 框架要拆成三个包?装一个不行吗?答案藏在一个数字里——真正的循环层只占整个体系代码量的 1.7%。拆开不是因为讲究,是因为不拆就没法只替换其中一层。

统计口径:各包 dist 目录下 .js 文件的物理行数(含空行与注释),版本 1.4.0。这不是有效代码行数,但用来比较相对体量足够稳。

三层分别解决什么问题

最底下是 pi-ai,管模型接入。它把 OpenAI、Anthropic、Gemini、Bedrock、本地 Ollama 这些不同的 API 抹平成一套接口:统一的流式协议、统一的工具调用格式、统一的用量统计。上层代码不需要知道背后是哪家模型,换模型只换一个 provider 对象。

中间是 pi-agent-core,管循环。它拿一段上下文发给模型,模型说要调工具,它执行,把结果塞回去,再来一轮。它还管状态、事件流、消息队列、钩子——但不管“你要做什么业务”。

最上面是 pi-coding-agent,是一个完整产品:命令行界面、会话管理、主题、扩展加载、斜杠命令。你在终端里用的那个 pi,是这一层。

为什么这个切分值得学

大多数 Agent 框架把这三件事揉在一个包里。后果是:你想换模型,得改框架内部;你想换界面,得 fork 整个仓库;你只想用循环,被迫装进一整套 CLI 依赖。

切成三层之后,每一层的替换成本是独立的。做后端服务只需要 pi-ai 加 pi-agent-core,不用拖进 25MB 的终端界面。做终端工具就三层全要。想做嵌入式或浏览器端,中间那层是纯 TypeScript,没有任何 Node 原生依赖。

这个思路可以直接搬到自己项目里:接入层、循环层、交互层分开,界面永远不参与决策。

体量比例:1.7% 是核心,74.6% 是壳

我把三个包的 dist 目录全量扫了一遍,数物理行数。结果比预想的更极端:核心循环 1445 行,模型接入层 20551 行,产品层 64769 行。

这个比例说明一件事:Agent 的复杂度不在循环,在模型适配和交互。循环本身是十几行伪代码就能说清的东西——发请求、收工具调用、执行、回填、再来一轮。真正吃掉工程量的是各家 API 的差异、流式解析的边界、终端界面的渲染。

所以那些“用框架写了三个月还在调”的项目,时间大概率花在了产品层,而不是循环层。这个认知很重要:换掉循环,省不下多少代码;换掉循环,省下的是决策权的归属。

三层代码量横向条形图:pi-ai 模型接入层 20551 行,pi-agent-core 循环层 1445 行,pi-coding-agent 产品层 64769 行

三层代码量对比:核心循环只占 1.7%,产品层占了近四分之三。想改行为,改的是那 1445 行;想改体验,改的是那 6 万行。

三层的依赖与导出

包 职责 一级依赖 导出符号 占比
pi-ai 模型接入 10 36 23.7%
pi-agent-core Agent 循环 2 5 1.7%
pi-coding-agent CLI 产品 22 44 74.6%

循环层只有 2 个一级依赖,这是刻意的。依赖越少,越容易嵌进别人的运行时——Cloudflare Worker、浏览器、你自己的后端服务。

导出符号只有 5 个,也说明它的 API 面很小。小的 API 面意味着升级不容易炸:你依赖的东西少,别人改的时候波及你的概率就低。

每一层你实际会碰什么

在 pi-ai 这层,你打交道的是 provider 和 model 对象。日常操作是选模型、配 baseUrl、处理密钥轮换。如果要做私有化部署,这层是唯一需要改的地方。

在 pi-agent-core 这层,你打交道的是 Agent 实例、工具定义、事件订阅、钩子。这一层是你写业务逻辑的地方,也是这个系列后面八篇的主战场。

在 pi-coding-agent 这层,你打交道的是扩展、命令、主题、会话存储。如果你只是要用它当工具,这层是配置问题;如果你要把它嵌进自己产品,这层大概率整个不要。

只用前两层的最小安装

npm install @earendil-works/pi-agent-core @earendil-works/pi-ai
# 不需要 pi-coding-agent,省下 25MB 和 22 个依赖

背景:一个常见的误判:以为装了 Agent 框架就自动获得“工具生态”。pi 的官方态度是不内置 MCP、不内置子 Agent、不内置沙箱。这些能力在 pi-mcp、pi-codemode 这些独立包里,或者由你自己实现。选型时把这块算进成本。

三层切分对自建系统的启发

如果你正在维护一个内部 Agent 平台,这套切分可以直接借。接入层只负责“把请求变成统一格式”,循环层只负责“决定下一步”,交互层只负责“把事件渲染出来”。三层之间只通过事件和消息通信。

这样做最直接的好处是可观测。所有决策都发生在循环层,所有决策都会发出事件;交互层不参与决策,所以你能确定“看到的”就是“发生的”。反过来,如果界面层能偷偷改工具参数,你的日志就永远对不上现场。

第二个好处是可替换。模型换代时只换接入层,界面改版时只改交互层。循环层的代码一年可能都不动一次——因为它要做的事本来就没变过。

下一篇进入实操:不装 CLI、不配真实模型,用 40 行代码把循环跑起来,看看一次 prompt 到底发出了哪些事件、按什么顺序发。这是后面所有中级技巧的地基。

引用与来源

C code80.ai · AI 编码 API 聚合 Claude / GPT 多模型统一接入,稳定不限速,按量计费,几行配置接入 Claude Code。 了解一下 ›

抢沙发

评论前必须登录!

立即登录   注册