3. 核心架构¶
3.1 一句话总结¶
Pi 是一个 六层同心圆 的 monorepo:telemetry / tui 在最外层;
pi-ai提供统一 provider 适配;agent-core提供基于streamFn注入的 runtime;session-backends提供持久化;protocol + client + server提供远程能力;最后coding-agent拼出 CLI。
3.2 主链架构图(来自 cli.ts → main.ts → sdk.ts 静态阅读)¶
┌─────────────────────────────────────────────────────────────────────┐
│ 用户键入 pi <args> │
└─────────────────────────────────┬───────────────────────────────────┘
▼
┌────────────────────────────────────────┐
│ coding-agent/src/cli.ts (21 行) │
│ • process.title=APP_NAME │
│ • env PI_CODING_AGENT=true │
│ • env AI_AGENT=pi │
│ • configureHttpDispatcher() │
│ • main(argv) │
└────────────────────┬───────────────────┘
▼
┌────────────────────────────────────────┐
│ coding-agent/src/main.ts (972 行) │
│ 1. parseArgs(cli/args.ts) │
│ 2. runAuthCommand 短路 │
│ 3. createSessionManager (8 种入口) │
│ 4. resolveCliModel │
│ 5. applyHttpProxySettings │
│ 6. dispatch by AppMode: │
│ interactive → InteractiveMode │
│ print/json → runPrintMode │
│ rpc → runRpcMode │
└────────────────────┬───────────────────┘
▼
┌────────────────────────────────────────┐
│ coding-agent/src/core/sdk.ts │
│ createAgentSession() — 真正装配点 │
│ • ModelRuntime · SettingsManager │
│ • SessionManager · DefaultResourceLoader│
│ • convertToLlm (with block_images) │
│ • streamFn (注入 retry/timeout/headers) │
│ • new Agent({...}) │
│ • new AgentSession({...}) │
└────────────────────┬───────────────────┘
▼
┌────────────────────────────────────────┐
│ agent-core (pi-agent-core) │
│ Agent class · state · agent-loop │
│ harness/ │
│ • agent-harness.ts (主循环) │
│ • messages / system-prompt / tools │
│ • session (持久化抽象) │
│ • skills (技能加载) │
│ • search (检索索引) │
│ proxy.ts · node.ts · agent-loop.ts │
└────────────────────┬───────────────────┘
▼
┌────────────────────────────────────────┐
│ pi-ai + pi-ai/compat │
│ Unified LLM API │
│ api/ │
│ • anthropic-messages │
│ • openai-responses / -codex-responses│
│ • openai-completions │
│ • google-generative-ai / -vertex │
│ • bedrock-converse-stream │
│ • mistral-conversations │
│ • azure-openai / pi-messages / lazy │
│ auth/ · providers/ · models-store │
│ streamSimple() · getModel() · uuidv7 │
└────────────────────┬───────────────────┘
▼
┌────────────────────────────────────────┐
│ 模型 API(外部 HTTP / WebSocket) │
│ anthropic / openai / google / │
│ azure-openai / bedrock / mistral / │
│ openai-codex / openai-compat 等 │
└─────────────────────────────────────────┘
3.3 "它解决什么问题"——叙事性回答¶
Pi 关心三件事:
1. 把 "模型差异" 抽象掉¶
每个 LLM provider 有自己的协议 / streaming 行为 / 鉴权 / thinking 控制 / token 计算方式。pi-ai 通过 streamSimple(model, context, options) 统一这些差异——调用者只需考虑:Model + Message[] + options。
2. 把 "工具调用" 标准化¶
内建七件套工具:read / bash / edit / write / grep / find / ls。这些工具在 pi-agent-core 的 harness/tools/ 中以统一 Tool 接口实现。Agent 通过 agent-loop 反复迭代:模型返回 tool_call → 执行 → 把结果回灌 → 模型再判断。每步都可以被 hook / extension 切入。
3. 把 "会话" 持久化、可恢复¶
SessionManager 用 JSONL 持久化整棵消息树 + 分支(fork/continue/resume)。Agent.state.messages 是真相之源;session 文件只是它的镜像。
三件事合在一起¶
用户说一句话"修这个 bug",pi 把这句话变成"和 LLM 的几次有结构的往返",把 LLM 的工具调用变成"对工作树的文件系统操作",把这一切变成"以后能 resmue 的一个 JSONL 文件"——全程对用户可观察、可审计、可扩展。
3.4 关键设计模式(可复用)¶
| 模式 | 体现位置 | 含义 |
|---|---|---|
| Hexagonal / Ports & Adapters | streamFn 注入到 Agent |
Agent 不绑定 provider,只调用可替换的 stream 函数 |
| Dependency Injection by Options | CreateAgentSessionOptions(40+ 字段) |
全部依赖通过对象注入;可独立单测 |
| Hooks / Event Bus | before_provider_request, after_provider_response, before_provider_headers |
扩展点不污染核心 |
| Transport Abstraction | transport setting + proxy.ts |
切换 LLM transport(HTTP/SSE/WebSocket) |
| State Machine per Session | agent.state.messages + JSONL |
session 不可变分支(fork) |
| Strict Module Boundaries | tools/index.ts 用 withFileMutationQueue |
工具调用并发安全 |
| Tool Registry 三维 | tools? · noTools? · excludeTools? |
三层工具开关 |
| Pinned Deps + Offline-Build | min-release-age=2, build:offline |
供应链抗风险 |
| Converters | convertToLlm(messages) |
屏蔽 AgentMessage 与 Message 差异 |
| Telemetry Schema | defineTelemetrySchema() |
整库 0 依赖 |
3.5 几个有趣的"非显然"细节¶
setDefaultStreamFn(streamSimple)在coding-agent/src/core/sdk.ts第 36 行被调用。它是为扩展可使用低层 API 路径留的兼容性口子:扩展或第三方库即使不接入 agent-core 的 streamFn,也能拿到"今天我们希望它做什么"的全局行为。providerRetrySettings与httpIdleTimeoutMs都是 settings 注入——这意味着"用户改了重试策略"会被下一次 stream 调用立刻看到,不需要重启 session。thinkingLevel先 settings 取,再 model.clamp——模型不支持多档思考时会被强制降到off。sessionManager.appendThinkingLevelChange(thinkingLevel)把 thinking level 作为一个 append-only 事件写进 JSONL——这让"上次会话的 thinking 状态"无须额外字段就能恢复。