---
title: 'pi 源码解读｜pi 是一个怎样的 Agent Harness？'
description: '从 pi-mono 的包边界、AgentSession、ResourceLoader、Extension 与 SessionManager 出发，解释 pi 的可编程边界，以及把它放进 Local First Agent OS 时哪些能力仍需自己构建。'
pubDate: 2026-08-12
slug: pi-agent-kernel-source-reading
tags: [harness, agent, 源码解读]
lang: zh-CN
draft: false
featured: false
showCTA: true
showComments: true
series:
  id: pi-source-reading
  order: 1
---

“pi 能不能装几个插件，就变成 Hermes 或 OpenClaw 那样的个人 Agent？”

这个问题很容易把 pi 看错。它的终端界面、文件工具和多模型登录都很像一个完整 Coding Agent；而 Extension、Skills 和 Packages 又看起来什么都能接。于是直觉会变成：底座已经有了，剩下只是找插件。

源码和文档给出的边界恰好相反：pi 有意把核心做小，把工作流特有的行为推给扩展、Skill、Prompt Template 和 Package；它默认不内置 MCP、sub-agent、权限弹窗、Plan Mode、to-do 或后台 Bash。[pi 的设计说明写得很直白](https://pi.dev/docs/latest/usage#design-principles)。所以它可以长成一个 Agent OS，却不会因为安装几个扩展自动成为一个。

先说结论：

> **pi 更适合被理解为“可编程的 Agent Kernel + Coding Agent 参考实现”。它已经负责模型调用、单 Agent loop、工具、上下文加载和会话树；跨渠道、长期记忆、任务调度、评测闭环、跨 Session 协作和 Graph Runtime 仍是上层 Control Plane 的责任。**

这不是在说 pi 功能少。相反，恰好是因为它把边界切得干净，才适合当一个需要长期演化的 Agent 系统的底座。

> **数据快照**
>
> - 最后核验：2026-08-12
> - 源码与文档：[`earendil-works/pi`](https://github.com/earendil-works/pi) 的 `main` 分支及 [pi Latest 文档](https://pi.dev/docs/latest/)
> - 阅读边界：本文讨论 pi 公开的单进程运行时、CLI 和扩展接口；不比较不同 Agent 的任务成功率，也不把扩展示例等同于 pi 的默认能力
> - 名称说明：npm 包仍使用 `@earendil-works/*` 命名；下文“pi-agent-core”指该运行时包，不把它和整个 CLI 混为一谈

## 先把产品外壳和运行时内核分开

把 pi 当成“又一个命令行 Coding Agent”并不完全错，只是观察位置太靠外。`pi-coding-agent` 是用户直接运行的程序；它把终端交互、文件工具、资源发现和 Session 接到一起。但当前 SDK 允许应用直接创建 `AgentSession`，用于嵌入自定义 UI、自动化管道、子 Agent 工具与程序化测试。[SDK 的定位](https://pi.dev/docs/latest/sdk#core-concepts)已经说明它不只为终端而存在。

可以先用下面这张图建立地图。

**图 1：从 pi-mono 包边界看，CLI 只是 Agent Runtime 的一个宿主；本文根据公开包名与 SDK 接口绘制**

```mermaid
flowchart TB
  U["终端、Web 或自定义应用"] --> C["pi-coding-agent"]
  C --> S["AgentSession 与 ResourceLoader"]
  S --> A["pi-agent-core\n单 Agent loop"]
  A --> M["pi-ai\n模型与流式协议"]
  A --> T["内置或自定义 Tools"]
  S --> R["Extensions、Skills、Prompts、Context"]
  S --> P["SessionManager\nJSONL 会话树"]
  C --> UI["pi-tui / pi-web-ui"]
```

图中最重要的是 `AgentSession`，不是 CLI。它持有 Agent、模型、消息、上下文压缩和事件订阅；调用方既可以 `prompt()` 等待一次任务结束，也能在流式过程中 `steer()` 或 `followUp()`。这意味着“界面”与“Agent 执行”已经分开，CLI 只是其中一个调用方。[`AgentSession` 的公开接口](https://pi.dev/docs/latest/sdk#agentsession)可以直接核对这些职责。

这也是本文用 Kernel 形容 pi 的原因：Kernel 不是完整操作系统，更不是最终产品；它给上层提供一组稳定而受约束的执行能力。这个类比只用于说明分层，不意味着 pi 提供了 Linux 那样的进程隔离、驱动模型或安全边界。

## `pi-agent-core`：单 Agent loop 是内核，不是工作流引擎

Agent 最小闭环并不神秘：模型读到当前消息和工具描述，决定生成文本还是工具调用；工具结果写回消息；只要仍有工具调用，循环继续。

```typescript
// 分析性示例：展示职责，不是 pi 源码原样摘录
while (agent.hasNextStep()) {
  const reply = await model.stream(messages, tools);
  messages.push(reply);

  for (const call of reply.toolCalls) {
    messages.push(await tools.execute(call));
  }
}
```

pi 的 `AgentSession` 把这个循环包进更高一层的运行状态：消息历史、模型选择、thinking level、压缩、流式事件与中止。对于一个“读代码—修改—跑测试—继续修”的任务，这就是足够关键的底座。你不必先造一次模型流式协议、工具结果格式和取消语义，才能写上层行为。

但它的边界也在这里。单个 Agent loop 即使会反复调用工具，也仍然不是 Graph Runtime：它没有把 Planner、Coder、Reviewer、Judge 表达成一等节点，也没有为节点间共享状态、条件边、fan-out/fan-in、checkpoint 和恢复提供通用的图语义。

因此，下面两件事不能混为一谈：

- 在一次 pi Session 内，用 Extension 在测试失败后追加一条“分析失败原因”的消息；
- 运行一张持久的“规划—并行实现—验证—人工审批—回滚”执行图。

前者是**改造 loop**，pi 已经提供入口；后者是**编排多个 loop**，需要上层 Graph Controller，或交给专门的图框架。把 pi Agent 当作图里的一个 Node 通常很自然；把单 Agent loop 本身叫成完整 Graph Engine，就把“可以组合”误写成“已经提供”。

## `pi-ai`：统一模型层是路由插口，不是路由策略

pi 的模型层承接不同供应商的鉴权、模型定义和流式调用。当前文档列出的内置或可配置路径覆盖订阅型 Provider、API Key Provider、Cloud Provider、`llama.cpp`，并支持通过 `models.json` 配置 Ollama、LM Studio、vLLM 等兼容端点。[Provider 文档](https://pi.dev/docs/latest/providers)给出的重点是“怎样接入”，不是替你决定“此刻该用哪个模型”。

如果需要非标准 API、企业 OAuth 或代理，Extension 可以调用 `pi.registerProvider()` 注册完整 Provider；自定义流式实现最终仍围绕 `pi-ai` 的 `streamSimple` 协议组织。[Custom Providers 文档](https://pi.dev/docs/latest/custom-provider)给出了这一层的接口和边界。

这让一个 Local First 系统可以把本地与远端模型接到同一个 Agent Runtime；但“根据任务难度、数据敏感度、预算和延迟选择 Qwen、Codex 或 Claude”仍是一条**路由策略**。它可以放在 Provider 之前、Extension 里，或放在上层 Control Plane，取决于策略是否要跨 Session、跨渠道或被审计。pi 提供的是插槽，不是默认的策略委员会。

## `ResourceLoader` 和 Extension：可编程性真正从这里开始

如果只看 `pi.registerTool()`，容易把 Extension 理解成“给模型多塞几个函数”。实际接口宽得多。

`createAgentSession()` 通过 `ResourceLoader` 提供 Extension、Skill、Prompt Template、Theme 和 Context File；默认 Loader 会发现全局与项目内资源。[SDK 的加载说明](https://pi.dev/docs/latest/sdk#createagentsession)明确列出了这一点。对 Coding Agent 来说，这意味着同一个 loop 可以根据项目加载不同的工具、工作约束与交互层，而不用修改 Agent Core。

Extension 的工厂函数拿到 `ExtensionAPI` 后，至少可以订阅事件、注册工具、命令和快捷键；文档还提供 UI、动态工具、Provider 与 Session 相关能力。[Extension API 概览](https://pi.dev/docs/latest/extensions#extension-api)是最可靠的接口清单。

**表 1：pi 的扩展点与 Agent OS 上层能力的分工；pi 列基于 2026-08-12 的公开 SDK 与 Extension 文档**

| 需求              | pi 已提供的插口                          | 仍需要由系统定义的部分                        |
| ----------------- | ---------------------------------------- | --------------------------------------------- |
| 增加工具          | `customTools` 或 `pi.registerTool()`     | 工具目录、权限模型、凭据治理与审计            |
| 改变单 Agent 行为 | 生命周期事件、消息注入、工具拦截         | 成功标准、重试预算、评测标准与终止协议        |
| 接入本地/私有模型 | `models.json`、`registerProvider()`      | 路由规则、成本/延迟策略与 fallback            |
| 加载项目知识      | Skill、Prompt、Context File、`AGENTS.md` | 知识更新、冲突处理、长期记忆的提取与遗忘      |
| 改变终端体验      | 命令、快捷键、TUI UI API                 | Web/消息渠道、多用户身份和组织权限            |
| 管理会话          | `SessionManager` 与 Session Runtime      | 跨 Session 检索、用户画像、事实校验与保留策略 |

以“测试失败后自动进入反思”为例，Extension 能在工具事件后检查结果，并追加新的约束或工具；官方示例也展示了危险 Bash 先经过确认的拦截方式。[工具事件和拦截示例](https://pi.dev/docs/latest/extensions#tool-events)说明了这个方向。

但“能拦截”不是“已经安全”。一个真正的权限系统还要定义：谁能授权、授权多久、哪些对象可写、网络和文件系统的隔离在哪里做、日志能否证明实际副作用。pi 的扩展能力是实现这些策略的接缝，不应被误解为默认完成了安全治理。

## Session 是可分叉的工作记录，不是长期记忆

pi 的会话不是一段简单的聊天文本。当前格式把 Session 存成 JSONL，条目以 `id` / `parentId` 形成树，因此可以在同一文件中切换分支；SDK 同时公开 `SessionManager.create()`、`open()`、`inMemory()`、`forkFrom()` 等入口。[Session File Format](https://pi.dev/docs/latest/session-format)和 [SDK 的会话管理章节](https://pi.dev/docs/latest/sdk#session-management)都能验证这点。

这对 Coding Agent 特别有用：一个错误的调试方向可以 fork，新的路径不用重新丢掉所有已有上下文；Context compaction 也能把长会话缩到模型还能继续工作的范围内。

不过这仍然不等于“长期记忆”。Session 保留的是一次执行的原始过程和分支关系；长期记忆至少还要回答另外几件事：

- 什么信息值得从一段 Session 提炼为稳定事实；
- 哪些事实属于项目、用户、团队或某个渠道；
- 新证据与旧记忆冲突时，谁可以覆盖、怎样保留依据；
- 检索到的记忆怎样标注来源、时效与置信度；
- 删除、过期与隐私边界怎样处理。

把 Session 文件直接当知识库，短期很省事，长期会得到一个很大的“以前说过什么”的文件夹。它能帮助回放，却不能自动回答“现在还应不应该相信”。

## 从 pi 向上长出 Agent OS，缺的是 Control Plane

把前面的模块放回最初的问题，答案就很清楚：pi 并不缺一个新的 Prompt，也不缺更多 Tool；它缺的是一个跨多个执行单元做决策的上层。

**图 2：把 pi 放进 Local First Agent OS 时，pi 是执行层，Control Plane 负责跨任务语义；本文解读**

```mermaid
flowchart TB
  CH["TUI、Web、飞书、Telegram、Cron"] --> CP["Control Plane\n身份、任务、权限、预算"]
  CP --> G["Graph Controller\n路由、并行、重试、停止"]
  G --> PI1["pi AgentSession"]
  G --> PI2["pi AgentSession"]
  PI1 --> TOOLS["Tools / MCP / Sandbox"]
  PI2 --> TOOLS
  CP --> MEM["Memory\n提炼、检索、遗忘"]
  CP --> EVAL["Judge / Eval / Observability"]
  MEM --> PI1
  EVAL --> G
```

这张图没有试图把所有东西都塞进 Extension。原因很简单：一旦 Scheduler、渠道身份、跨任务 Memory、评测历史和 Graph 状态都只存在于某个终端 Session 的扩展里，它们就很难独立观察、恢复和治理。

一个较稳妥的分层是：

- **pi 负责执行**：模型交互、工具调用、单任务上下文、项目资源发现和可扩展 CLI/SDK；
- **Graph Controller 负责编排**：创建哪些 Session、哪些可以并行、结果怎样汇总、失败回到哪里、何时要求人工确认；
- **Memory Layer 负责事实**：从记录中提炼可复查的记忆，而不是把全部消息再次塞回 Prompt；
- **Eval 与 Policy 负责停止和边界**：测试通过不一定代表任务完成，模型说“完成”也不应替代权限和验收；
- **Channel Layer 负责产品入口**：把消息、定时任务、Web UI 或设备事件规范化成可以提交给 Control Plane 的任务。

这也是为什么“pi + 插件 = Hermes”不够准确。插件当然能实现很多单点能力，甚至可以做 sub-agent、MCP 或权限门；而 [pi 的 Package 机制](https://pi.dev/docs/latest/packages)也确实能把 Extension、Skill、Prompt 和 Theme 打包分发。但一个 Agent OS 的困难不在于把能力装进去，而在于这些能力怎样共享身份、状态、权限、预算和完成证据。

## 源码阅读顺序：先追执行边界，再追产品能力

如果目标是做二次开发，建议不要从 CLI 的命令列表开始。更高效的顺序是：

1. 先读 `pi-agent-core` 与 `AgentSession`：确认一次模型回复、工具调用、消息追加、取消与压缩怎样构成单任务 loop。
2. 再读 `ResourceLoader` 和 `DefaultResourceLoader`：确认项目的 Extension、Skill、Prompt 与 `AGENTS.md` 在什么范围、什么顺序加载。
3. 然后读 Extension 事件与 `registerTool()`：找清可插入的生命周期位置，以及工具、Provider、UI 与 Session 的实际能力。
4. 接着读 `SessionManager` 的 JSONL 树与 Session Runtime：区分“原始会话持久化”“分支”和“长期 Memory”。
5. 最后才设计 Graph Controller、Memory、Policy 和 Eval：这些不应由某个局部 Hook 的方便程度反推架构。

这个顺序的好处是，每一步都先确定 pi 已经承诺的接口，再决定自己的系统要在哪一层加能力。否则很容易先写出一个“自进化 Extension”，最后才发现跨 Session 的状态、失败恢复和评测数据都无处安放。

## 总结：pi 不是成品 OS，但正因如此值得作为底座

pi 的价值不在于替你打包了多少“聪明功能”，而在于它把最难复用、最容易被 CLI 外壳遮住的部分做成了可调用的运行时：`AgentSession`、工具、模型层、资源加载、Extension 和树状 Session。

它能支持 Loop engineering：你可以改变单 Agent 在何时观察、验证、重试或停止。它也能成为 Graph engineering 的执行节点：上层把 Planner、Worker、Judge 和人工审批组织起来，再让每个节点使用 pi 的 loop 和工具。但这两句话都不等于 pi 已经带着完整的 Graph、Memory、Channel、Scheduler 和治理系统。

所以，若目标只是尽快得到一个个人助手，选择已经把渠道、任务和记忆打包好的产品往往更省力；若目标是构建一个可本地部署、可评测、可演化的 Agent OS，pi 的克制反而是一种优势。下一篇可以继续沿着这条边界，具体追 `ResourceLoader` 与 Extension 如何把“外部能力”接进一次 Agent Session。

## 参考资料

- [Pi：SDK](https://pi.dev/docs/latest/sdk)
- [Pi：Extensions](https://pi.dev/docs/latest/extensions)
- [Pi：Session File Format](https://pi.dev/docs/latest/session-format)
- [Pi：Providers](https://pi.dev/docs/latest/providers)
- [Pi：Custom Providers](https://pi.dev/docs/latest/custom-provider)
- [Pi：Packages](https://pi.dev/docs/latest/packages)
- [Pi：Design Principles](https://pi.dev/docs/latest/usage#design-principles)
- [pi-mono 源码仓库](https://github.com/earendil-works/pi)
