10 min read教程

PiAgent 02|读懂包分层与会话装配

从 pi-ai 到 pi-coding-agent,沿依赖方向理解模型、循环、工具、会话与 UI 为什么没有混在一起

PiAgent 02|读懂包分层与会话装配#

pi-aipi-coding-agent,沿依赖方向理解模型、循环、工具、会话与 UI 为什么没有混在一起

打开 Pi 的 monorepo,最容易得到的错觉是:它只是一个功能很多的 CLI。更准确的理解是,它把“模型协议”“Agent 控制流”“编码产品装配”和“终端界面”放在了不同边界内。包的数量不是重点,重点是每层拥有的状态不同、变化速度不同、被谁依赖不同。

1. 先看依赖方向#

可以先用下面的简化图建立空间感:

text
                         pi-coding-agent
                 ┌──────────┼──────────┐
                 │          │          │
          AgentSession   extensions    pi-tui

           pi-agent-core

                pi-ai

这不是严格的包依赖图,而是职责图:

  • pi-ai 只关心模型、上下文、消息和流式 Provider,不应该知道会话文件或终端控件。
  • pi-agent-core 负责 Agent 状态与循环,可以被非编码场景复用。
  • pi-coding-agent 把模型、资源、设置、会话、默认工具和扩展装配成产品级 Session。
  • pi-tui 负责把事件投影成人可以操作的终端界面。

依赖方向一旦倒置,问题会变得难以隔离。例如模型适配器直接引用 TUI,就无法在 Web 服务或测试中复用;工具把会话文件路径写死,就无法在多用户环境安全运行。

2. pi-ai:统一协议,而不是抹平能力差异#

上层希望调用一个统一的 stream(model, context),但 Provider 的现实差异很多:消息角色名称不同,工具参数的 JSON 约束不同,thinking、缓存、停止原因和错误码也不一致。

ts
const model = getModel(provider, modelId);
const context = { systemPrompt, messages };

for await (const event of stream(model, context)) {
  if (event.type === "text_delta") {
    process.stdout.write(event.delta);
  }
}

适配层做两次转换:

  1. 把 Pi 的上下文模型转换成具体 Provider 的请求格式。
  2. 把 Provider 的响应转换成统一的文本、thinking、tool call、usage 和错误事件。

统一接口的含义是上层拥有稳定的最低公约数,不是所有模型都拥有相同能力。某个模型没有 thinking 或不支持工具时,适配器不能凭空创造能力,只能用能力字段和错误协议让上层知道差异。

3. pi-agent-core:把模型输出变成控制流#

核心 Agent 不负责“这个产品应该显示什么”,它只关心一件事:当前消息和工具状态如何驱动下一次模型调用。

这层的价值在于把 ReAct 之类的控制流从业务代码中抽出来。业务代码不需要手写“如果模型返回 tool call 就再请求一次”,而是向 Agent 提供工具和规则,由 Loop 持续推进直到达到终止条件。

4. pi-coding-agent:产品层的装配器#

pi-coding-agent 不是另一个更大的“模型类”,它是把多个运行时组件接到一起的外壳。最小装配可以理解为:

text
ModelRuntime       选择模型与认证
SettingsManager    加载默认设置、重试与压缩策略
SessionManager     保存或恢复会话树
ResourceLoader     发现系统提示词、skills、extensions、上下文文件
ExtensionRuntime   注册工具、命令和生命周期钩子
Agent              执行循环、消息与工具调用
AgentSession       对外暴露 prompt/subscribe/abort 等产品接口

因此 createAgentSession() 的意义不是“new 一个 Agent”,而是完成依赖注入。它把这些组件的默认实现组合起来,同时给宿主留下替换入口。

5. 从一个调用看五步装配#

ts
const modelRuntime = await ModelRuntime.create();
const { session } = await createAgentSession({
  model: (await modelRuntime.getAvailable())[0],
  modelRuntime,
});

在默认配置下,可以把内部流程理解为:

text
1. ModelRuntime:读 auth.json / models.json,合并内置模型
2. SettingsManager:读 settings.json,获得默认设置
3. SessionManager:决定会话写入 JSONL 还是留在内存
4. ResourceLoader:加载系统提示词、skills、extensions、AGENTS.md
5. Agent:把模型、消息、工具和事件循环接起来

默认 Coding Agent 通常启用 readbasheditwrite 四个核心工具;其他内置工具可能需要显式加入。源码里能找到某个工具,并不代表新建的 Session 一定把它暴露给模型。

6. Session、Runtime、Extension 和 Tool 的关系#

Session:持续对话的容器#

Session 持有当前对话和生命周期入口。prompt() 触发运行,subscribe() 观察运行,abort() 中止运行,dispose() 释放资源。它不应该承担 Provider 适配,也不应该把 Web 的用户身份直接写进每条模型消息。

Runtime:Session 背后的基础设施#

Runtime 解决的是“Session 如何工作”:模型从哪里来、资源从哪里加载、历史怎么保存、设置如何生效。替换 Runtime 组件,就是在改变产品的运行环境,而不必重写 Agent Loop。

Extension:运行时的策略注入点#

扩展可以注册模型可调用的工具,也可以订阅或拦截生命周期事件,还可以提供用户显式触发的命令。它位于产品层和核心循环之间,因此既能观察事实,也可能改变事实。

Tool:模型触达外部世界的边界#

工具是模型能力的物化契约。没有查询工具,模型就不能可靠获得数据库事实;有了没有权限控制的写入工具,模型就可能把自然语言判断转成危险副作用。

7. 为什么 Web 场景不能照搬默认装配#

默认会话持久化是为本地、单用户、按工作目录隔离的 Coding Agent 设计的。它通常把 JSONL 写入 ~/.pi/agent/sessions/<encoded-cwd>/

Web 服务的约束不同:

  • 多个用户不能共享一个 Session。
  • 会话历史应该按用户和会话 ID 存入受控存储,而不是任意写服务器磁盘。
  • 系统提示词和工具权限不能由不可信工作目录随意决定。
  • 一个请求断开时,必须只中止对应运行,不能影响其他用户。

因此常见做法是使用 SessionManager.inMemory() 作为运行时容器,然后通过事件或消息边界把需要恢复的事实写入应用自己的数据库。这个选择不是性能优化,而是把“本地 CLI 的默认假设”与“Web 产品的多租户边界”分开。

8. 源码阅读的边界判断法#

看一个符号时,可以连续问四个问题:

  1. 它拥有的是模型状态、Agent 状态、会话状态还是 UI 状态?
  2. 它接收的是内部消息,还是已经可以发送给 LLM 的消息?
  3. 它是在改变运行策略,还是只观察运行结果?
  4. 它的默认实现是否针对本地单用户场景?

例如 session.subscribe() 是观察入口,不等于扩展的 pi.on()SessionManager 管理可恢复历史,不等于 Agent 的 transcript;ToolDefinition 描述模型能调用什么,不等于工具执行结果已经安全。

小结

Pi 的包分层可以浓缩成一句话:pi-ai 处理模型方言,pi-agent-core 处理行动循环,pi-coding-agent 处理产品装配,TUI 或 Web 负责事件投影。理解这四个边界后,改模型只需要找适配器,改提示词只需要找资源加载,改工具只需要进入扩展或工具契约,不必把整套 CLI 拆开重写。

源码定位

  • 模型类型与事件:packages/ai/src/types.ts
  • Agent 核心类型与循环:packages/agent/src/types.tspackages/agent/src/agent-loop.ts
  • Session 装配:packages/coding-agent/src/sdk.ts
  • Session 生命周期:packages/coding-agent/src/agent-session.ts
  • 默认工具:packages/coding-agent/src/core/tools/
  • 终端投影:packages/tui/

相关文章