技术架构¶
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 只负责模型协议适配。它定义统一的 Model、Message、ToolCall、ToolResultMessage、AssistantMessageEventStream,再由 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
设计关键点¶
- 核心 loop 纯粹:
agent-loop.ts是可复用算法,不背负产品状态。 - 产品层可替换:
AgentSession可以被交互模式、RPC、SDK 共用。 - 会话是树,不是数组:branch/fork/compact 都依赖
id/parentId/leafId。 - 上下文是运行时构建的视图:会话文件保留全历史,发给模型的是
buildSessionContext()解析后的当前分支视图。 - 扩展是一等产品机制:工具、命令、事件、UI、provider、资源发现都可插入。
- MCP 非内置能力:Pi 明确把 MCP 放到扩展层,而不是核心层。