沿调用链打开 Pi

概念篇读完,小A觉得自己“懂”了:模型怎么生成、工具怎么调用、循环怎么转、会话怎么存。他把概念书一合,打开了 pi-mono 的源码目录,打算挑一个真实 Agent 看看。
十分钟后,他盯着满屏的 packages/ 目录发呆。ai、agent、coding-agent、tui、protocol……十几个文件夹,哪个是入口?哪条是主路?
他去找老Z:“概念我会背了,可这代码我看不懂从哪下手。”
老Z笑了:“概念回答的是‘通常如何工作’。你现在要回答的是另一个问题:一个真实 Agent 在哪里创建请求、怎样推进状态、何时中断或保存会话。这需要一份能复核的源码快照,而不是凭印象猜。我们选 pi-mono,按包边界和一条端到端调用链来读。”
“那我看的是哪个版本?会不会过两天又变了?”
本篇的基线与证据规则
- 仓库:
earendil-works/pi(原 badlogic/pi-mono,本书仍称 pi-mono)- 固定标签与提交:
v1.0.0,a13d35a742c6ef8462812a28fbe1d8c8b7431c32- 版本边界: 根仓库
0.0.3;本书选取的八个包1.0.0- 提交日期:
2026-10-01本书仓库根目录的
pi/是这个标签的源码快照,全部 TypeScript 文件加了中文注释,没有自己的 git 历史。路径和符号与上游一致;行号以上游标签为准,快照里会因注释而偏移。要核对原文,在别处克隆上游并检出v1.0.0。
小A追问:“书里的代码和结论,我该怎么核对?”
老Z说:“记住三条证据规则,读到哪都用得上:”
证据规则
- 已确认事实必须能回到该快照的文件路径、符号名或官方原始资料;行号仅是该快照下的辅助定位。
- 基于源码的推断只从结构和调用关系说明“这意味着”或“可以推断”,不把它写成作者意图。
- 示意或简化的伪代码、删节代码、流程图和假设场景会明确标注;它们不是完整真实源码。
“还有,”老Z补了一句,“‘讲清源码’不等于逐行讲完所有源码。本篇承诺讲清核心入口、关键类型、主要调用链、状态变化、错误路径和设计代价,并给出能继续独立阅读的走查路线。”
小A猜:“那我是不是先把十几个文件夹的名字都过一遍?”
“先别急着记名字,要盯的是一个更具体的问题。”老Z在屏幕上敲了一行字,“一次用户输入,如何从命令行进入系统,变成模型流、工具执行、会话记录和终端更新?答案就藏在这条链路上。”
本篇解决什么
读完源码篇,你能沿两条路线独立走查:一条是请求穿过入口、模型流、循环、会话、终端与协议的主调用链;另一条是
pi-durable的存储内核。pi-durable不依赖pi-agent-core,把它当成同一条链的末端会读错边界。你能:从入口找到模型请求怎么发出、流怎么穿过适配层、循环怎么推进状态、会话怎么活过进程重启、终端怎么刷新、工具与扩展怎么接入、协议怎么变成字节、远程会话怎么保持一致、评测怎么判断一次修改。核心是建立一张可验证的地图:遇到 bug 或想扩展时,你知道该往哪个文件看。这里暂不解决:逐行讲完所有源码、覆盖全部边缘功能、把有限样本升级成全局结论。没有覆盖的部分会在对应章节标明,而不会被暗示为已完整解释。
“为什么不选别的仓库,非得是 pi-mono?”
“因为它是一个真实的、能端到端跑通的 TypeScript Agent 仓库,而且它的包边界和我们概念篇画的边界对得上。”老Z说,“概念篇讲的 Provider、循环、会话、工具、协议、评测,在 pi-mono 里都能找到对应的包和调用链。选一个具体仓库,你才能指着某一行说‘就是这里实现了它’,而不是对着抽象概念空谈。”
“读源码有没有通用的方法?”小A问。
“有,但记住三条就够起步。”老Z说,
读源码的三个习惯
- 先入口后细节:从
index.ts的导出和某个create*/run*函数进,先看清主路径再钻分支- 先契约后实现:读模块前先读它的
types.ts或接口定义,实现细节只在怀疑 bug 时才需要- 先状态后逻辑:一个类有哪些成员、谁在何时改它们,比某个 if 写得巧不巧重要十倍
八包,不是八个孤岛
小A打开 packages/ 目录,数出 13 个文件夹。老Z提醒他:
“多出来的先不管。本书从 13 个包中选取八个:pi-ai、pi-agent-core、pi-coding-agent、pi-tui、pi-protocol、pi-server、pi-client 与 pi-evals。它们不是八个孤立目录,而是端到端调用链上的不同责任边界。chord、codemode、mcp、telemetry 只在用到时点名(codemode 与 mcp 另有附录细读);durable 是源码篇第13章的专章对象;扩展示例等其他 workspace 也不在这份清单里。”
13 章源码路线
- 源码篇第1章 地图的比例尺——pi-mono:建立仓库、包边界、依赖方向和主调用链的地图。
- 源码篇第2章 目录与户口——pi-ai:追踪
pi-ai的 Provider、模型目录、鉴权与请求入口。 - 源码篇第3章 先开票,后办事——pi-ai:检查消息转换、事件流、重试、中止、错误与兼容层。
- 源码篇第4章 双层循环——pi-agent-core:阅读
pi-agent-core的 Agent、循环、消息队列、工具事件与停止路径。 - 源码篇第5章 调度台——pi-coding-agent:分析
AgentSession如何接到Agent的插口上,以及 JSONL 会话、fork、compaction 与 skills;明确会话 fork 不等于 Git worktree。 - 源码篇第6章 调度台与工位——pi-tui:走查
pi-tui的组件树、差分渲染、宽度计算、输入与降级。 - 源码篇第7章 户口本上的住址——pi-coding-agent:从
pi-coding-agent的参数、配置和资源加载走到模型、会话与交互装配。 - 源码篇第8章 收银台排队——pi-coding-agent:检查内置工具、文件并发队列、输出截断、扩展 runner、Hooks、项目信任与沙箱边界。
- 源码篇第9章 发货与报关——pi-protocol:阅读
pi-protocol的信封 schema、CBOR、framing、版本与错误语义。 - 源码篇第10章 黑板与便签——pi-server+pi-client:追踪
pi-server与pi-client的连接、握手、请求路由、会话附着、订阅快照与重连边界。 - 源码篇第11章 每次考试换新考场——pi-evals:区分
pi-evals的行为验证、文档对照实验、judge 结果与主观质量局限。 - 源码篇第12章 接力赛——End-to-End:把 CLI 输入、模型流、工具执行、会话记录与 TUI 更新收束为一条控制流和数据流。
- 源码篇第13章 先落盘,后可见——pi-durable:读第二台引擎——原子提交线、任务检查点与重放规则,回答源码篇第5章留下的“进程死在中途谁来收尾”。
小A扫完清单:“每章都怎么读?”
“每章都会从一条可走查的主路径出发,同时至少检查一个错误、中断或边界路径;它不会替代你对整个仓库每个文件的阅读。”
“时间有限,只读主干,该读哪几章?”
“挑一条贯穿主线就够了。”老Z说,“源码篇第2章看模型请求怎么发,第4章看循环怎么转,第5章看会话怎么活过进程,第12章看一次完整请求怎么从 CLI 走到 TUI。这四章串起来就是主干;加上第13章耐久运行时,源码篇一共有13个篇内章节。其他章节是主干上的分支,按需再回头补。”
源码篇的主干阅读线
源码篇第1章 地图 → 源码篇第2章 模型请求 → 源码篇第4章 循环 → 源码篇第5章 会话 → 源码篇第12章 端到端
时间充裕时再沿主干横向铺开:源码篇第3章 流式适配、源码篇第6-7章 终端与启动、源码篇第8章 工具与扩展、源码篇第9-10章 协议与远程、源码篇第11章 评测。
“读完和读之前,差在哪?”
“差在‘能不能指着一行代码说清楚它为什么存在’。”老Z说,“概念篇你知道了边界应该怎么画;源码篇你看到了一个真实系统真的这么画了,还看到了画得不彻底时会出什么问题。读完这一篇,你手里的概念才算从‘听过’变成‘核对过’。”
老Z最后提醒:“源码篇的结论都锚定在一个提交上。你读到的行号、符号、调用关系,都以 v1.0.0(a13d35a7)为基准;如果去看更新版本的代码,结构可能已经变了。这不是书的问题,是源码阅读的固有属性。把‘这个快照里如此’和‘永远如此’分开,是读源码的基本功。”
从这里开始:源码篇第1章 地图的比例尺——pi-mono
