从零到一拆解 Pi Agent 的核心架构,讲透模型流、Agent Loop、工具调用与上下文压缩的实现细节。
1 最小概念与架构
纵观 Pi 的基础骨架,维持一个智能体运转只需要五个最基础的核心概念:
| 概念 | 做什么 | 如果没有它会怎样 |
|---|---|---|
| 消息 Message | 保存用户、助手、工具结果的历史记录 | 模型不知道前面发生过什么,缺乏记忆 |
| 模型 Model | 根据上下文生成下一步动作或文本 | 系统没有任何推理和规划能力 |
| 工具 Tool | 把模型连接到外部世界产生实际副作用 | 只能聊天,不能读文件、执行命令、改代码 |
| 循环 Loop | 反复处理模型输出 -> 工具结果 -> 再请求模型 | 只能执行单步对话,无法自主完成复杂任务 |
| 事件 Event | 把内部执行过程暴露给 UI 和日志系统 | 前端只能看到最终答案,看不到中间的思考与执行过程 |
参考 Pi 的开源仓库,整个系统被清晰地划分为三个职责明确的层级。这种分层让代码极其易于测试和扩展:
| 包名称 | 核心定位与理解 | 关键职责 |
|---|---|---|
| @earendil-works/pi-ai | 模型适配层 | 统一多家大模型 API 差异、流式事件、消息格式、工具 schema |
| @earendil-works/pi-agent-core | Agent 内核层 | 维护 Agent Loop、工具调用核心、事件总线、状态管理、队列控制 |
| @earendil-works/pi-coding-agent | 产品运行层 | 会话持久化管理、资源自动加载、内置系统工具、扩展挂载、TUI/RPC/print 多模式适配 |
pi-ai:把供应商差异关在一层
不同的模型供应商(如 OpenAI、Anthropic 等)对于工具调用、推理内容(Reasoning)、缓存机制、错误处理以及流式协议的表达方式千差万别。Pi 通过这一层将所有差异统一封装成标准的 Message、Tool、AssistantMessageEvent,并对外暴露统一的 streamSimple() 接口。上层 Agent Loop 完全不需要关心当前面对的是 Anthropic 的 tool_use 还是 OpenAI 的 function call,它只需要处理统一后的标准化 toolCall 内容块。
pi-agent-core:纯粹的运行时
这一层剥离了所有终端 UI 和扩展文件加载的逻辑。它只专注于管理 Agent 的状态,对外暴露 prompt()、steer()、followUp()、abort() 等控制指令。真正的循环(Agent Loop)在此运行:发起模型请求、拦截工具执行、判断是否需要进行下一轮(Turn)对话。由于它的职责足够单一,开发者可以非常轻松地编写单元测试来验证控制流的正确性。
pi-coding-agent:工程化产品层
写出一个循环并不困难,困难在于把它变成每天都能稳定运行的开发工具。产品层接管了所有麻烦但极其关键的工程化任务。它负责会话的 JSONL 序列化保存,确保重启后任务能够无缝继续;它负责扫描并加载项目的 AGENTS.md 规则、可用的技能库以及扩展插件;它还内置了读写文件、执行 bash 命令等编码基础工具,并实现了针对长会话的上下文压缩机制。
2 消息与流式事件
Agent 系统中流动的数据血脉不是简单的字符串拼接,而是结构化的消息列表。模型每次进行推理时,都需要看到一个结构严谨、逻辑连贯的上下文。工具的调用细节也必须作为消息返回给模型视野,否则模型完全无法判断刚才的外部操作是否成功、输出了什么。
在 Pi 的模型适配层中,基础消息被精简为三类核心结构:
typeMessage=UserMessage|AssistantMessage|ToolResultMessage;interfaceUserMessage{role:"user";content:string|ContentBlock[