技术架构

Pi 的架构核心是分层清晰:底层模型适配不理解产品交互,agent loop 不理解 CLI,会话和扩展不侵入 provider。这样它既能做一个交互式 coding agent,也能作为 SDK/RPC 被别的程序嵌入。

总体分层

flowchart TD User["用户 / 外部程序"] --> Mode["交互模式 / RPC / SDK"] Mode --> Coding["pi-coding-agent: AgentSession"] Coding --> Resources["资源层: settings / skills / prompts / extensions / context files"] Coding --> Session["SessionManager: JSONL 会话树"] Coding --> Core["pi-agent-core: Agent / agentLoop"] Core --> Tools["AgentTool: read / bash / edit / write / extension tools"] Core --> AI["pi-ai: streamSimple"] AI --> Providers["OpenAI / Anthropic / Google / Bedrock / custom providers"]

源码入口:

  • packages/coding-agent/src/core/sdk.ts: createAgentSession() 装配 auth、model、settings、resource loader、session manager、Agent。
  • packages/coding-agent/src/core/agent-session.ts: 产品层生命周期,处理 prompt、扩展事件、工具注册、自动压缩、重试、会话持久化。
  • packages/agent/src/agent-loop.ts: 低层 agent 循环,不关心 CLI 和磁盘。
  • packages/ai/src/stream.ts: 根据 model.api 找 provider 并发起统一流式请求。

更细的模块覆盖和行号见源码索引

窄腰接口

Pi 里有几个非常关键的“窄腰”:

窄腰 上层看到什么 下层隐藏什么
Context systemPrompt + messages + tools 不同模型 API 的 message/tool 格式差异
AgentMessage 可扩展消息联合类型 coding-agent 的 bash/custom/summary 消息如何转成 LLM 消息
AgentTool name、schema、execute、partial update 内置工具、扩展工具、远程工具的具体实现
AgentEvent agent_start、message_update、tool_execution_end 等事件 UI 渲染、会话持久化、扩展 hook 的复杂性
SessionEntry append-only JSONL 树节点 分支、fork、compaction、label、custom entry 的存储细节

这种窄腰让 Pi 很适合渐进扩展。比如模型 provider 是 pi-ai 的 registry,扩展 provider 是 coding-agent 的 extension API;二者最终都流向 streamSimple(model, context, options)

四个核心包的边界

pi-ai

pi-ai 只负责模型协议适配。它定义统一的 ModelMessageToolCallToolResultMessageAssistantMessageEventStream,再由 provider 把这些结构翻译成 OpenAI、Anthropic、Google 等 API 的真实请求。

优秀设计点:

  • provider 通过 api-registry.ts 注册,stream.ts 只按 model.api 分发。
  • 内置 provider lazy-load,减少启动成本,也让运行时缺失 provider 模块时能用错误消息进入统一流协议。
  • SimpleStreamOptions 把 reasoning/thinking、重试、超时、metadata、cache retention 都收敛在 provider 选项里。

pi-agent-core

agent-core 是最小可复用 agent runtime。它只知道:

  • 上下文里有哪些消息、系统提示和工具。
  • 如何调用模型流。
  • 如何根据 assistant message 中的 tool call 执行工具。
  • 如何发出生命周期事件。

它不负责:

  • 从哪里加载技能和项目说明。
  • 会话如何落盘。
  • 终端 UI 怎样显示。
  • 扩展如何被发现。
  • API key 怎样存储。

这个边界很干净,是 Pi 可以同时支持 CLI、SDK、RPC 的基础。

pi-coding-agent

这是产品层,也是代码量最大的一层。它把低层 agent 组装成 coding agent:

  • AgentSession 管理 prompt 生命周期、工具、模型、thinking level、自动压缩、自动重试。
  • SessionManager 用 JSONL append-only 树保存所有会话。
  • DefaultResourceLoader 发现项目/全局资源:扩展、skills、prompts、themes、AGENTS.md/CLAUDE.md。
  • ExtensionRunner 把扩展注册的工具、命令、事件、UI 统一接到 session runtime。
  • tools/* 提供 read、bash、edit、write、grep、find、ls。

pi-tui

pi-tui 是终端 UI 库。它独立于 agent runtime,让交互模式可以专注在输入编辑器、消息渲染、选择器、主题、快捷键等体验问题。扩展也可以通过 ExtensionUIContext 请求选择、确认、输入、通知、自定义组件等 UI 能力。

数据流概览

一次普通 prompt 的主干路径:

sequenceDiagram participant U as User participant S as AgentSession participant E as ExtensionRunner participant A as Agent participant L as agentLoop participant M as pi-ai/provider participant T as Tools participant J as SessionManager U->>S: prompt(text) S->>E: input / before_agent_start S->>A: agent.prompt(messages) A->>L: runAgentLoop L->>M: streamSimple(model, context, options) M-->>L: assistant stream events L-->>A: message_start/update/end L->>T: execute tool calls T-->>L: tool results A-->>S: AgentEvent S->>E: extension events S->>J: append message / model / settings entries S->>S: retry / compaction checks

设计关键点

  1. 核心 loop 纯粹agent-loop.ts 是可复用算法,不背负产品状态。
  2. 产品层可替换AgentSession 可以被交互模式、RPC、SDK 共用。
  3. 会话是树,不是数组:branch/fork/compact 都依赖 id/parentId/leafId
  4. 上下文是运行时构建的视图:会话文件保留全历史,发给模型的是 buildSessionContext() 解析后的当前分支视图。
  5. 扩展是一等产品机制:工具、命令、事件、UI、provider、资源发现都可插入。
  6. MCP 非内置能力:Pi 明确把 MCP 放到扩展层,而不是核心层。