ai 知道 provider 但不知道循环;agent 知道循环但不知道文件系统长什么样;coding-agent 知道用户,并且承担全部产品决策。| 层 | 它知道 | 它刻意不知道 |
|---|---|---|
pi-ai | 39 家 provider 的请求格式、认证、流式差异、token 计费 | 什么是「一轮对话」;什么是工具执行 |
pi-agent-core | 循环、工具调用协议、会话存储、压缩 | 工具具体做什么;UI 怎么渲染;用户是谁 |
pi-coding-agent | 7 个内建工具、扩展、TUI、设置、权限(没有)、产品行为 | —(它是终点) |
这个分层最实用的用途是定位问题:模型返回格式不对 → ai;工具跑了但结果没进上下文 → agent;工具行为不符合预期 → coding-agent。
它的自我定义写在 README 第一句:统一的 LLM API,带 provider 集合、自动认证解析、token 与成本跟踪,以及简单的上下文持久化与中途换模型的交接。ai/README.md:3
它还有一条显式的收录标准:
**Note**: This library only includes models that support tool calling (function calling),
as this is essential for agentic workflows.
packages/agent 里确实有 bash/read/write/edit 四个工具实现,但它们接受一个注入的执行环境(ExecutionEnv、FileSystem、Shell),而不是直接调 node:fs。harness/types.ts:315
这就是 第 15 章里 Gondolin 扩展能把工具路由进 micro-VM 的原因:工具逻辑与「在哪执行」是分开的。而 coding-agent 有自己那套 7 个工具(多了 grep/find/ls),面向真实文件系统。
顺带一提,这是全书第二处「同名双份实现」——第一处是压缩(第 06 章,漂移 d3)。两处的成因相同:harness-v2 迁移期,新旧两套并存。
锚点之后两处双份实现都已经没有了,但不是因为 coding-agent 迁到了新 harness:7fd478a(2026-10-01) 随 harness 一起删掉了 agent 包里的工具与压缩副本,随 v1.0.0 发布。见 漂移记录 · 上游后续。
| 决策 | 在哪一层 | 本书章节 |
|---|---|---|
| 「不做权限系统」 | coding-agent | 15 |
| 压缩阈值 = window − 16384 | coding-agent | 06 |
!!cmd 不进上下文 | coding-agent | 05 |
| 会话格式 v3 | coding-agent | 07 |
| 扩展事件有哪 25 类 | coding-agent | 12 |
packages/agent 对这些一无所知——它只提供机制。
If your feature does not belong in the core, it should be an extension.
PRs that bloat the core will likely be rejected.
Pi's core exists to be minimal and to be extensible so that it can be influenced and
manipulated by extensions. Even hook points for extensions however should be well
considered and discussed to avoid adding unmaintainable bloat and complex interactions.
锚点之后锚点时,「做成扩展」最有代表性的一条表态写在 coding-agent 的 README 里:coding-agent/README.md:498。这句在 25cc5c7(2026-09-22)的文档重写中删去,8562bcf(2026-09-29)随后加入了内建的 MCP 与 codemode,coding-agent 直接依赖这两个包:package.json:45、package.json:46。按本章对「核心」的定义(packages/agent)这不算破例,MCP 也仍以扩展形式加载(cli.md:197);变的是产品层的默认:从「需要就自己写扩展」变成「默认自带」。贡献指南这两段在 v1.2.0 上未变。
把「一轮对话」的概念留在 agent 而不是 ai,意味着换 provider 不影响循环;把「文件系统」留在注入接口而不是硬编码,意味着换执行环境不影响工具逻辑;把「UI」留在 coding-agent 而 tui 零依赖,意味着无头运行不需要 mock UI。
三条边界都遵循同一个判据:一个东西如果可能有第二种实现,就把它做成注入点。
一条消息在 ai 里是 Message,在 agent 里是 AgentMessage,在 coding-agent 里还有扩展出来的自定义 role。第 05 章那两道闸就是这个代价的具体形态——每次 LLM 调用都要做一次类型转换。
换来的是每层可以独立演化。coding-agent 加一种消息类型不需要动 ai;ai 加一个 provider 不需要动 agent。
容易误读的一点:CONTRIBUTING.md 说核心要最小,但 coding-agent 有 12 万行。这不矛盾——「核心」指的是 packages/agent(2 万行)。产品层大是正常的,因为产品决策就是多。
真正的约束落在别处:coding-agent 里那些「本可以做成扩展」的功能,才是这条规则要拦的。而它给出的出路是扩展系统(第 12 章)——所以扩展系统的能力边界,直接决定了这条规则可执行到什么程度。
场景模型返回的流式 chunk 解析出错、字段对不上。按三层分工,最先查哪个包?
B。「模型返回格式不对 → ai;工具结果没进上下文 → agent;工具行为不符预期 → coding-agent」——分层的定位价值就在这里。
ac4ac9e(2026-08-07 快照)。