
Pi 源码解析(一):整体架构
Pi 源码解析(一):整体架构
Pi Agent Core 源码系列(1/8)|下一篇:Agent Loop:生命周期、事件与控制流
Pi 是一个包含多个 package 的 monorepo:既要适配不同 LLM provider,也要处理 agent loop、会话、工具、终端 UI 和扩展。直接顺着 import 追代码,很快就会在模块之间来回跳。
先按职责把它分成几层会比较清楚:pi-ai 统一模型接口,pi-agent-core 运行 agent,pi-coding-agent 加入工具、会话和扩展,pi-tui 负责终端交互。
Package 之间的关系
关系很直接:pi-ai 和 pi-tui 提供基础功能,pi-agent-core 实现 agent 的执行流程,pi-coding-agent 把它们组装成 CLI 产品,pi-orchestrator 用来管理多个 coding-agent 实例。
五个主要 package
1. pi-ai:把不同模型变成同一种接口
packages/ai 解决的是多 provider 差异问题。OpenAI、Anthropic、Google、Bedrock 等服务拥有不同的鉴权方式、消息协议、流式事件和模型能力;如果这些差异直接泄漏到 agent 层,上层代码会充满 provider 分支。
Pi 用一组公共类型和 API 隔开这些差异:
types.ts定义消息、模型、工具调用、流式响应等核心类型;index.ts暴露稳定的公共 API;providers/all.ts注册内置 provider 和模型目录;api/下的实现负责适配 Anthropic Messages、OpenAI Responses、OpenAI Completions 等具体协议。
上层 agent 只依赖这些公共消息和事件。更换模型时,agent loop、工具执行和会话状态不需要跟着重写。
2. pi-agent-core:定义 agent 如何运行
packages/agent 是整个系统的执行内核。它不关心终端长什么样,也不负责具体的编辑器体验;它关心的是一次 agent 运行必须解决的通用问题:如何接收用户消息、调用模型、处理工具调用、更新上下文并继续下一轮推理。
几个主要模块分工如下:
agent-loop.ts描述模型响应与工具结果之间的循环;agent-harness.ts将执行循环、工具和环境组合为可复用 harness;session.ts维护消息、上下文与会话状态;compaction.ts在长会话中压缩历史,控制上下文增长;env/nodejs.ts把文件和进程操作落到 Node.js 宿主环境。
推理循环和执行环境在这里是分开的。同一套 agent 流程可以接入不同 transport、状态存储或受限环境,不会和某一个 CLI 实现绑定。
3. pi-tui:终端 UI
packages/tui 提供终端组件、编辑器、Markdown 渲染、自动补全、按键识别和图片显示。渲染器使用 differential rendering,只更新发生变化的终端区域,并通过 synchronized output 减少闪烁。
packages/tui/src/index.ts 是公共入口。理解这一层时,不必先读完所有组件;先掌握三个概念即可:
- 组件根据终端宽度渲染为文本行;
- 输入事件被分发给当前交互组件;
- 渲染器比较前后帧,只刷新变化部分。
这解释了为什么 Coding Agent 能在纯终端中同时展示流式输出、编辑器、工具执行状态、选择器和主题,而不必依赖浏览器 UI。
4. pi-coding-agent:组装 CLI 产品
packages/coding-agent 是用户运行 pi 时真正接触到的产品层,也是仓库中最大的 package。它依赖前面的 pi-ai、pi-agent-core 和 pi-tui,并补上 coding agent 所需的产品能力:
main.ts处理 CLI 启动、参数、配置和运行模式;agent-session.ts提供产品级会话行为;session-manager.ts负责会话持久化、恢复和迁移;core/tools/index.ts组装 read、write、edit、bash 等 coding tools;core/extensions/loader.ts加载扩展并构造运行时;interactive-mode.ts将会话事件映射为终端交互。
pi-coding-agent 不再实现一套 agent loop。它负责选择模型、加载配置和扩展、建立会话、提供工具,再把底层事件转成终端中的交互反馈。
5. pi-orchestrator:从单个 agent 走向多实例任务
packages/orchestrator 仍处于实验阶段。它依赖 pi-coding-agent,尝试把单个 CLI 进程提升为可管理的任务执行单元。
supervisor.ts 负责实例生命周期,ipc/protocol.ts 和 ipc/server.ts 定义本地通信边界。由于这一层的 API 和行为尚未稳定,学习时应把它放在最后,而不是用它反推整个项目的稳定架构。
一次用户请求如何穿过系统
把这些 package 串起来,可以得到一条简化的运行链路:
- 用户在终端输入请求,
interactive-mode接收输入并更新界面状态; pi-coding-agent的AgentSession将请求、配置、模型和工具组合起来;pi-agent-core的 harness 启动 agent loop,并把当前上下文交给模型;pi-ai选择对应 provider,完成鉴权和协议转换,再把响应转成统一流式事件;- 如果模型产生 tool call,runtime 暂停模型循环,调用 Coding Agent 提供的 read、edit、bash 等工具;
- 工具结果写回会话上下文,agent loop 继续请求模型,直到得到最终结果;
- 会话事件持续推送给 TUI,最终状态由 session manager 保存。
模型接入、agent loop、工具、执行环境和 UI 分属不同模块。因此每个部分都可以单独测试、替换或扩展。
几个实现选择
统一事件流,而不是只返回一个字符串
Agent 需要处理文本增量、thinking、tool call、结束原因、错误和 token/cost 信息。pi-ai 将这些过程建模为流式事件,使 TUI 可以实时渲染,runtime 也能在工具调用出现时及时接管控制权。
扩展优先,而不是把所有能力写进核心
Coding Agent 的工具和扩展都有明确契约。扩展 loader 负责发现和加载,自定义行为不必直接修改核心循环。这与 Pi “self extensible coding agent”的定位一致。
长会话需要单独处理
Session 和 compaction 并非附属功能。Coding agent 会持续读取文件、执行命令并积累工具结果,如果没有结构化会话和上下文压缩,长任务很快会受到上下文窗口与成本限制。
权限边界由运行环境决定
Pi 本身不会自动提供完整的文件系统、进程、网络或凭据沙箱。工具最终通过执行环境访问宿主资源,因此在不可信任务中,应结合容器或其他 sandbox 机制限制能力范围。理解 security.md 和 containerization.md,与理解 agent loop 同样重要。
推荐的源码阅读顺序
如果目标是学习并继续写系列文章,可以按下面的顺序推进:
- 建立边界:根
README.md与各 package 的package.json; - 理解模型契约:
packages/ai/src/types.ts、index.ts、一个具体 provider 和对应 API adapter; - 理解执行循环:
packages/agent/src/agent-loop.ts、agent-harness.ts、session.ts; - 理解产品组装:
packages/coding-agent/src/main.ts、agent-session.ts、工具入口; - 理解扩展与交互:extension loader、
interactive-mode.ts、TUI 公共入口; - 最后阅读外围系统:compaction、安全隔离、CI/CD、发布脚本和 orchestrator。
每一阶段只跟踪一个问题。例如阅读 pi-ai 时,只看不同 provider 如何接入公共接口;阅读 agent runtime 时,只看工具结果如何让模型继续下一轮。这样比同时展开所有 import 更容易跟住代码。
总结
Pi 把模型、agent 运行时、终端和产品逻辑拆成了几个 package:
pi-ai统一模型与 provider;pi-agent-core定义 agent 的运行语义;pi-tui提供高效终端交互;pi-coding-agent把这些能力组装成可用产品;pi-orchestrator探索更高层的多实例任务编排。
后面阅读某个实现时,先确认它属于哪一层,再继续跟踪调用关系。后续文章会依次展开 agent loop、工具执行、Session 和上下文压缩。
- 感谢你的欣赏!




