Pi 架构分析

> 从架构分层与设计思想角度,对 `pi` 做的系统性梳理。基于当前 monorepo 源码与文档(`@ear...

> 从架构分层与设计思想角度,对 pi 做的系统性梳理。基于当前 monorepo 源码与文档(@earendil-works/pi-*,版本约 0.80.x)。

1. 项目定位

Pi 是一个 agent harness(代理运行时外壳),产品形态是终端编码代理 CLI,但真正的产品主张不是「功能最全的 coding agent」,而是:

> 保持核心极小,用扩展把工作流交给用户塑形。

官方表述(packages/coding-agent/README.md):

minimal terminal coding harnessAdapt pi to your workflows, not the other way aroundwithout having to fork and modify pi internals

默认只给模型四个工具:read / write / edit / bash。子代理、plan mode、权限弹窗、MCP、内置 TODO、后台 bash 等常见「全家桶」功能故意不做进核心,而是通过 Extensions / Skills / Packages 由用户或社区补齐。

对外交付形态:

形态入口用途
Interactive默认 CLI人工会话 TUI
Print / JSON-p / --mode json脚本化、一次性响应
RPC--mode rpc进程间 JSONL 集成
SDKcreateAgentSession()嵌入其它应用

2. 设计哲学

哲学章节是理解整仓架构的钥匙。核心不是「少写代码」,而是 控制核心承诺面(surface area)

2.1 五条「故意不做」

决策理由替代路径
No MCP工具面可走 CLI + README(Skills),不必把协议栈绑进核心Skills,或自己写 MCP extension
No sub-agents子代理语义极多,硬编码一种会锁死工作流tmux 多实例 / 自定义 extension / 第三方 package
No permission popups权限策略高度环境相关容器化,或 extension 做确认流
No plan mode计划可落在文件或自定义流程写 plan 文件 / extension / package
No built-in todos内置 TODO 易混淆模型TODO.md / 自定义工具
No background bash需要可观测、可交互tmux

这些「否定」共同定义了 Pi 的架构边界:核心负责 agent loop + 会话 + 多厂商 LLM + 可扩展点;产品化行为下沉到扩展生态。

2.2 与同类产品的对照思路

维度典型「全家桶」AgentPi
功能增长内核不断加 feature内核稳住,feature 走 extension
权限模型内置 allow/deny UI默认信任用户进程权限;隔离靠 OS/容器/扩展
工具协议MCP 一等公民Skills(文档化 CLI)优先;MCP 可选
嵌入方式常绑死单一 CLICLI / RPC / SDK 四模式共用 AgentSession
会话多为线性 transcriptJSONL (可 /tree 分支回溯)

2.3 设计口号背后的工程含义

1. 可替换优于可配置 配置项解决的是参数;扩展解决的是行为与 UI。Pi 把「行为变更」做成 TypeScript 模块契约,而不是无穷无尽的 YAML 开关。

2. 进程权限即能力边界 安全文档明确:Pi 默认以启动用户权限运行。更强边界靠 Gondolin / Docker / OpenShell 等容器化方案,而不是在 agent 内再造一套 sandbox OS。

3. 会话是一等资产 会话可分支、可压缩、可导出 HTML、可分享到 HF 数据集。OSS session 数据被视为改进模型与工具链的真实反馈源,这影响了 session 格式的长期稳定性投入。

4. 模型无关、厂商有关 上层只谈统一的 Message / Tool / stream 事件;下层按 API dialect(Anthropic Messages、OpenAI Completions/Responses、Gemini、Bedrock…)分别实现。只收录 支持 tool calling 的模型——因为 agentic 是前提。

3. 仓库与包分层

packages/ tui/ # 终端 UI 框架(差分渲染) ai/ # 多厂商 LLM 统一 API agent/ # Agent loop + 状态 +(演进中的)AgentHarness coding-agent/ # CLI 产品:会话、工具、扩展、四种运行模式 orchestrator/ # 实验性编排(不稳定,可忽略生产依赖)

3.1 依赖方向(严格向下)

┌─────────────────────┐ │ coding-agent │ 产品层 / 组装层 │ (CLI · SDK · RPC) │ └──────────┬──────────┘ ┌───────────────────┼───────────────────┐ ▼ ▼ ▼ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ pi-tui │ │ agent-core │ │ (tools, │ │ │ │ │ │ extensions)│ └────────────┘ └──────┬─────┘ └────────────┘ ▼ ┌────────────┐ │ pi-ai │ 协议与流式抽象 └────────────┘

pi-ai:无 UI、无会话树、无编码工具;只做 LLM 适配、鉴权、用量与上下文序列化。 – pi-agent-core:无编码语义;通用 agent 循环、工具执行、事件流、AgentMessage 扩展点。 – pi-tui:无 agent 语义;通用终端组件与差分渲染。 – pi-coding-agent:把上述三层 + 编码工具 + 会话/扩展/资源发现组装成产品。

构建顺序(package.json scripts)也体现了这一点:

tui → ai → agent → coding-agent → orchestrator

3.2 版本与发布策略

锁步版本(lockstep):各 package 同版本号,patch = 修复/增量,minor = 破坏性变更。 – coding-agent 发布带 npm-shrinkwrap,把传递依赖钉死,配合 --ignore-scripts 与供应链 hardening。 – 直接外部依赖 精确 pin;workspace 内部用版本范围。

4. 各包职责与内部结构

4.1 pi-ai:统一 LLM 层

问题:每个厂商有不同的消息格式、tool schema、thinking/reasoning 字段、缓存策略、OAuth 与错误语义。

解法:两层拆分——

路径职责
API dialectsrc/api/*协议实现:anthropic-messagesopenai-completionsopenai-responsesgoogle-generative-aibedrock-converse-stream
Providersrc/providers/*具体厂商:endpoint、模型目录、鉴权、兼容开关

关键抽象:

ContextsystemPrompt + messages + tools,可序列化、可跨模型 handoff。 – Model:provider/id/api/cost/contextWindow/capabilities。 – stream / streamSimple:统一事件流(text_ / thinking_ / toolcall_* / done / error)。 – Auth:环境变量、credential store、OAuth(Codex / Copilot / xAI 等),getApiKey 支持运行期刷新短时 token。 – Faux provider:测试与 harness 用假厂商,避免 e2e 依赖真密钥。

设计要点:

1. 只支持 tool-capable 模型 —— 库的存在理由是 agentic workflow。 2. 失败不靠抛异常冲垮调用方:stream 契约要求错误编码进事件与 final AssistantMessagestopReason: error|aborted)。 3. 懒加载 API 模块*.lazy.ts)以利于 tree-shaking / 启动成本。 4. 模型目录生成models.generated.ts 由脚本生成,禁止手改。

4.2 pi-agent-core:Agent 运行时

问题:LLM 调用与 tool 执行要组成可靠循环;UI/会话需要细粒度事件;应用侧消息类型可能比 LLM 协议更丰富。

核心类型分离

AgentMessage[] ──transformContext──► AgentMessage[] ──convertToLlm──► Message[] ──► LLM (应用语义) (剪裁/注入) (仍应用语义) (协议边界) (厂商消息)

AgentMessage:可扩展(declaration merging / 自定义 role),可含 UI-only 消息。 – Message(pi-ai):仅 user / assistant / toolResult。 – convertToLlm:边界适配器,过滤不可送入模型的消息。 – transformContext:在 Agent 层做 compaction 前处理、外部上下文注入等。

Agent loop 事件模型(UI 与持久化的共同真相源):

agent_start turn_start message_start / message_update / message_end # user | assistant | toolResult tool_execution_start / update / end turn_end agent_end

工具执行策略

– 默认 parallel:preflight 顺序执行(含 beforeToolCall),允许的工具并发跑;结果消息按 assistant 源顺序落盘。 – 可全局或 per-tool 强制 sequential。 – beforeToolCall / afterToolCall:拦截、改写结果、terminate 提示跳过 follow-up LLM。 – Steering / follow-up 队列:流式过程中注入用户消息(one-at-a-time | all)。

Agent class vs 低层 agentLoop

agentLoop / runAgentLoop:纯函数式循环 + emit sink。 – Agent:状态机外壳(model、tools、messages、streaming 标志、队列、subscribe)。

演进中的 AgentHarnesspackages/agent/src/harness/ + docs/agent-harness.md):

更完整的编排层,明确区分:

状态类别含义
Harness config最新运行时配置(model/tools/resources…),getter 永远读最新
Turn snapshot单次 turn 的冻结视图,in-flight 请求不受 setter 影响
Session仅已持久化条目
Pending session writes忙时排队、在 save point / settlement 刷盘

phase:idle | turn | compaction | branch_summary | retry。 这是 agent-core 从「库」走向「可复用 harness 产品内核」的方向;coding-agent 当前主路径仍以 AgentSession 组装为主。

4.3 pi-tui:差分渲染终端 UI

问题:全屏重绘闪烁、大 paste 破坏输入、图像协议碎片化。

解法

Component 接口render(width) → string[],可选 handleInput。 – 差分渲染 + CSI 2026 synchronized output:只更新变化区域,原子提交。 – Overlay 系统:对话框/选择器不毁底层内容。 – Bracketed paste、Kitty/iTerm2 图像、主题接口。

coding-agent 的 interactive mode 是 tui 的重度消费者,而不是把 TUI 逻辑塞进 agent 循环。

4.4 pi-coding-agent:产品组装层

src/ main.ts / cli.ts # 参数解析 → 组装 runtime → 选 mode core/ agent-session.ts # 跨 mode 的会话/生命周期中枢 session-manager.ts # JSONL 树存储 sdk.ts # createAgentSession extensions/ # 扩展加载与事件总线 tools/ # read/write/edit/bash/grep/find/ls compaction/ # 上下文压缩与分支摘要 resource-loader.ts # skills/prompts/themes/AGENTS.md/extensions model-runtime.ts # 模型与鉴权运行时 modes/ interactive/ # TUI print-mode.ts rpc/

关键洞察:Mode 是 I/O 层,不是业务层。

AgentSession 注释写得很清楚:

> shared between all run modes (interactive, print, rpc). Modes use this class and add their own I/O layer on top.

因此 SDK 与 CLI 共用同一会话语义:扩展、压缩、分支、工具包装行为一致。

4.5 pi-orchestrator(实验)

多进程/RPC 监督与编排雏形。文档标明不稳定,不进入主架构承诺

5. 端到端运行时数据流

一次用户输入从 CLI 到模型再回到屏幕的路径:

┌──────────────┐ │ User Input │ (TUI editor / stdin / RPC prompt) └──────┬───────┘ ▼ ┌──────────────┐ │ AgentSession │ 写 session、触发 extension hooks、压队列 └──────┬───────┘ ▼ ┌──────────────┐ │ Agent │ agent_start → turn → stream → tools → … └──────┬───────┘ │ convertToLlm(messages) ▼ ┌──────────────┐ │ pi-ai │ 选 provider API、鉴权、stream events │ streamSimple│ └──────┬───────┘ ▼ ┌──────────────┐ │ Tool execute │ beforeToolCall → bash/read/edit… → afterToolCall └──────┬───────┘ ▼ ┌──────────────┐ │ Session JSONL│ append message / toolResult entries (tree) └──────┬───────┘ ▼ ┌──────────────┐ │ Mode render │ TUI components / JSON lines / RPC replies └──────────────┘

Streaming 与订阅契约

Agent.subscribe 的 listener 按注册顺序 await。 – agent_end 之后仍会等待 listener 完成,才让 waitForIdle / prompt settle。 – 这保证 UI 持久化与扩展 hook 不会在「逻辑结束但副作用未完成」时被切断。

6. 会话系统:树形 JSONL

6.1 存储形态

路径模式:

~/.pi/agent/sessions/—-/_.jsonl

每行一个 JSON entry,id / parentId 构成 ,不是只能 append 的线性 log。

版本演进:

Version特征
v1线性
v2树(id/parentId)
v3hookMessagecustom(扩展统一)

加载时自动迁移到当前版本。

6.2 Entry 类型分工

Entry是否进 LLM 上下文用途
message是(经 convert)user/assistant/toolResult 等
compaction摘要替代旧消息上下文压缩检查点
branch_summary是(摘要)/tree 跳转时保留旁支语义
model_change / thinking_level_change否(元数据)运行时配置变更
custom默认否扩展状态持久化
custom message / CustomMessage可注入扩展往上下文塞内容
label / session_info书签、显示名

append-only 文件 + 逻辑树:物理上持续追加;逻辑 leaf 指向当前分支 tip。/tree/fork/clone 依赖这套模型,而无需复制整文件语义混乱。

6.3 Compaction 与 Branch Summarization

机制触发作用
Compaction上下文超阈值或 /compact摘要旧消息,保留 firstKeptEntryId 之后的明细
Branch summarization/tree 导航切分支时用摘要保住另一支上下文

阈值默认:contextTokens > contextWindow - reserveTokensreserveTokens 默认 16k)。 摘要可迭代(带着上一次 summary),extension 可接管自定义 compaction。

设计思想:压缩是 会话层策略,不是 provider 层偷偷截断。这样跨模型 handoff、导出与审计都看得到压缩点。

7. 扩展体系(第一公民扩展点)

7.1 为什么扩展是架构中心

Pi 把「用户会改的一切」收成几类资源:

资源形态能力级别
ExtensionsTypeScript 模块最高:工具、命令、hook、自定义 TUI、会话条目
SkillsMarkdown + frontmatter按需能力说明书(模型用 read 加载)
Prompt templates模板文件斜杠命令展开
ThemesJSON外观
Pi packagesnpm/git打包分发上述资源
Context filesAGENTS.md / CLAUDE.md项目指令注入 system prompt

扩展发现路径:

– 全局:~/.pi/agent/extensions/ – 项目:.pi/extensions/(需 project trust) – settings / CLI -e / packages

7.2 Extension API 能力面

pi.on(event, handler):session / agent / tool / model / resource 生命周期 – pi.registerTool:LLM 可调用工具(TypeBox schema) – pi.registerCommand:用户斜杠命令 – ctx.ui:confirm / select / input / notify / custom TUI componentpi.appendEntry:扩展私有状态写入 session

安全模型直白:扩展 = 任意代码,与用户同权。信任边界在「是否加载项目扩展 / 是否安装第三方 package」,而不是 sandbox 解释器。

7.3 与「配置开关」的对比

做法结果
内核加 feature flag组合爆炸、默认行为臃肿、升级负担
Extension 契约行为在用户侧组合;核心只保证 hook 时序与 API 稳定

这就是「aggressively extensible so it doesn’t have to dictate your workflow」的工程落地。

8. 工具层设计

8.1 默认工具集刻意偏小

默认启用:readwriteeditbash。 另有发现类:grepfindls(可通过 CLI allow/deny 控制)。

思想:

– 足够完成编码任务的最小闭包。 – 发现类工具可视为 bash 的结构化替代,降低模型乱拼 shell 的概率,但不是强制权限系统。 – 文件变更可走 mutation queuefile-mutation-queue),避免并行 edit 互相踩踏。

8.2 Tool 定义跨层映射

pi-ai Tool # 协议:name/description/parameters schema ▲ AgentTool # agent-core:execute + streaming + executionMode ▲ ToolDefinition # coding-agent:渲染、HTML export、extension 包装

Extension 注册的工具经 wrapRegisteredTools 进入同一执行管线,与内置工具共享 before/after hooks 与 session 持久化。

8.3 Skills vs Tools

Tools:结构化、schema 校验、可并行、可 hook。 – Skills:文档化能力包,注入 system prompt 索引,内容由模型按需 read。对齐「No MCP:用 README 教模型用 CLI」路线。

9. 四种运行模式的架构角色

createAgentSession() / AgentSession │ ┌────────────────────┼────────────────────┐ ▼ ▼ ▼ InteractiveMode Print / JSON RPC Mode (pi-tui 组件树) (stdout 事件/文本) (JSONL 请求响应)

模式输入输出典型用途
Interactive键盘/粘贴/选择器差分 TUI日常开发
PrintCLI 参数 + pipe最终文本脚本、CI 片段
JSON同 print全事件 JSONL可观测流水线
RPCstdin JSONL 命令stdout JSONLIDE / 宿主进程 / openclaw 类集成
SDK程序 API事件订阅深度嵌入

同一 session 语义,多种投影 —— 这是「harness」而非「单一 CLI 工具」的关键标识。

10. 模型与鉴权架构

10.1 Provider 矩阵

支持面极宽:官方 API、订阅 OAuth(Claude/Codex/Copilot)、聚合网关(OpenRouter、Vercel AI Gateway)、区域变体(ZAI CN、Xiaomi token plan 多区域)、本地(llama.cpp router、Ollama 兼容端点)。

架构上拆成:

1. 静态/动态模型目录(生成 + 运行时 refresh) 2. Auth resolution 链(env → store → OAuth refresh) 3. API dialect 适配 4. 用量与 cost 记账(cache read/write 单独计)

10.2 Cross-provider handoff

Context 可序列化并换模型继续。会话树里的 model_change 记录切换点。这对「一个会话里前半 Claude、后半本地模型」是一等场景,而不是边角。

10.3 Coding-agent 侧组装

ModelRuntime / ModelRegistry / ModelResolver:解析 CLI、settings、scoped models(Ctrl+P 循环)。 – provider-composer / remote-catalog-provider:组合内置与自定义目录。 – 自定义 provider:compatible JSON 配置,或 extension 实现完整 OAuth/API。

11. System Prompt 组装

buildSystemPrompt 的优先级逻辑:

1. 若提供 customPrompt → 以其为基底(仍可 append skills / context / cwd)。 2. 否则默认 prompt:工具说明、指南、文档路径提示等。 3. 统一追加:AGENTS.md/CLAUDE.md 项目上下文、skills 索引、appendSystemPrompt、cwd。

Context files 向上游目录发现,把 monorepo / 多包仓库的指令继承做成约定,而不是配置中心。

Skills 仅在 read 可用时注入——没有读取能力时塞 skill 路径没有意义。

12. 安全与信任模型

分层清晰,避免「半吊子沙箱」假安全感:

机制
进程用户 OS 权限即工具权限
项目Project trust:未信任项目不加载本地扩展等敏感资源
供应链pin 依赖、shrinkwrap、--ignore-scripts、lifecycle allowlist、audit
隔离(可选)Gondolin micro-VM、Docker、OpenShell
扩展文档强调只装可信来源

No permission popups 不是忽视安全,而是把策略决策推到:

1. 运行环境(容器/策略引擎),或 2. 用户自定义 extension(可做 rm -rf 确认等)。

13. 工程与质量体系中的架构信号

这些约束本身就是架构的一部分:

1. Erasable TypeScript only(Node strip-only):无 enum / parameter properties 等需 emit 的语法 → 运行与类型边界更简单。 2. 无 inline dynamic import:依赖图静态可读。 3. npm run check:biome + pinned deps + ts imports + shrinkwrap + tsgo + browser smoke。 4. 测试分流:默认 ./test.sh 跳过需密钥的 e2e;coding-agent suite 用 faux provider。 5. 多 agent 并行改仓的 Git 纪律:禁止 git add -A / hard reset —— 反映「人 + 多 coding agent 同仓」的真实工作方式,与 Pi 自身 dogfood 一致。 6. 文档可被 agent 自解释:大量 docs 指向源文件路径,支持「ask pi to explain itself」。

14. 架构优势与张力

14.1 优势

优势说明
边界清晰ai / agent / tui / product 分层,可独立复用
扩展优先避免内核膨胀,工作流可替换
会话可审计JSONL 树 + compaction 检查点,适合分享与研究
多入口同核CLI/RPC/SDK 不复制业务逻辑
厂商广度dialect + provider 拆分,新增厂商成本可控
流式事件一等UI、日志、JSON mode 共用事件语义

14.2 内在张力与演进点

张力表现
核心「极简」vs coding-agent 体量产品层必然变大;需持续把可复用部分下沉到 agent-core harness
AgentSession 与 AgentHarness 双轨harness 文档描述目标生命周期;coding-agent 仍主用 Session 组装
扩展同权灵活且危险;信任与容器方案必须文档化到位
无内置权限 UX新用户可能误以为「不安全」;需要 onboarding 讲清威胁模型
并行 tool + 文件修改需要 mutation queue 等补丁;扩展作者需理解并发语义
orchestrator 实验态多 agent 编排产品化路径尚未收敛

14.3 适合 / 不适合

适合:

– 希望深度定制工作流、愿写 TypeScript 扩展的人 – 需要嵌入 agent(SDK/RPC)的宿主应用 – 重视会话可分支、可导出、可研究的用法 – 多模型/多订阅混用

不适合作为「开箱即用全家桶」预期:

– 期望内置 MCP 浏览器栈、子代理编排、plan UI、细粒度权限弹窗且不愿装扩展/容器的用户

15. 一张总图

┌─────────────────────────────────────────────────────────────────┐ │ User / Host App │ └────────────┬───────────────────┬───────────────────┬────────────┘ │ Interactive │ RPC/SDK │ Print/JSON ▼ ▼ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ pi-coding-agent │ │ ResourceLoader │ Extensions │ Skills │ Tools │ Settings │ │ AgentSession │ │ SessionManager (JSONL tree + compaction) │ └────────────────────────────┬────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ pi-agent-core │ │ Agent / agentLoop │ Tool exec │ Events │ (AgentHarness) │ │ AgentMessage ↔ convertToLlm ↔ Message │ └────────────────────────────┬────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ pi-ai │ │ Providers × API dialects │ Auth │ Stream │ Cost │ Models │ └────────────────────────────┬────────────────────────────────────┘ │ ▼ LLM vendor APIs / local servers

┌─────────────────────────────────────────────────────────────────┐ │ pi-tui: Component tree, differential render, overlays │ │ (primarily driven by interactive mode) │ └─────────────────────────────────────────────────────────────────┘

16. 结论

Pi 的架构可以概括为三句话:

1. 分层 harnesspi-ai 统一模型协议,pi-agent-core 统一 agent 循环与事件,pi-tui 统一终端呈现,pi-coding-agent 只做编码产品与扩展生态的组装。 2. 极简核心 + 一等扩展:用「故意不做」划定产品边界,把工作流差异推到 TypeScript 扩展与 packages,而不是内核 feature flag。 3. 会话即状态机外存:append-only JSONL 树承载分支、压缩、扩展状态与跨模式复现,使 CLI、RPC、SDK 共享同一真相源。

若只记一个设计口号:

> Pi 不是试图替你决定如何 coding 的 agent,而是让你(和你的扩展)定义 agent 如何工作的 harness。

附录:关键源码地图

主题路径
产品哲学packages/coding-agent/README.md § Philosophy
CLI 入口packages/coding-agent/src/main.ts
会话中枢packages/coding-agent/src/core/agent-session.ts
SDK 工厂packages/coding-agent/src/core/sdk.ts
Session 存储packages/coding-agent/src/core/session-manager.ts
扩展类型/加载packages/coding-agent/src/core/extensions/
压缩packages/coding-agent/src/core/compaction/
Agent 循环packages/agent/src/agent-loop.ts
Agent 类packages/agent/src/agent.ts
Harness 设计packages/agent/docs/agent-harness.md
LLM 类型packages/ai/src/types.ts
Provider 实现packages/ai/src/providers/
API dialectpackages/ai/src/api/
TUI 核心packages/tui/src/tui.ts
开发结构说明packages/coding-agent/docs/development.md

发表回复

人生梦想 - 关注前沿的计算机技术 acejoy.com 🐾 步子哥の博客 🐾 背多分论坛 🐾 借一步网 🐾 智柴网 沪ICP备2024052574号-1