跳转至

02 · Pi 的设计思想与整体架构

2.1 先理解 Harness

大模型可以返回文本和工具调用,但不会自行读取你服务器上的文件。Harness 是围绕模型的运行程序:组装上下文、发请求、执行工具、记录结果、安排下一轮、处理取消,并把进展交给界面。

例如你说「找出项目的入口文件」:模型决定调用 find;Pi 校验参数并运行工具;工具结果进入下一次模型请求;模型根据真正存在的文件回答。你自己的 Agent,本质上需要决定这几个边界的行为,而不是另造一个模型。

flowchart LR U[用户任务] --> H[Harness] H --> M[模型推理] M --> C[文本或工具调用] C --> H H --> T[实际工具与业务系统] T --> R[结果与错误] R --> H H --> S[会话记录] H --> V[终端或应用界面]

2.2 设计思想一:把产品策略留给使用者

官方称 Pi 是一个精简、可扩展、可以变成自己工具的 Harness。CLI 提供文件操作、会话、模型、终端 UI 等基础能力,不预置统一的 plan mode 和 subagent 工作流。你可以用扩展自己实现,或者安装 Pi package。

这是一种产品取舍:相同的「计划」对代码修改、资料整理、服务器运维有不同含义。把它固定进内核会让业务选择变成框架限制。

应用方法:先把自己的工作流写成简单步骤;只有需要可执行行为或状态约束时才用 Extension。不能因为没有内建模式,就认为 Pi 缺少工具循环或会话能力。

边界:pi-durable 实验运行时确有 child tasks / subagent 支持;「CLI 默认没有 subagents」不等于整个 monorepo 从来没有子任务 API。

2.3 设计思想二:分层组合,按需要取用

flowchart TB UI[CLI Interactive / Print / JSON / RPC] --> CA[pi-coding-agent: AgentSession] APP[自己的 TypeScript 应用] --> CA CA --> AC[pi-agent-core: Agent + Loop] CUSTOM[自己的业务运行时] --> AC AC --> AI[pi-ai: Models + Providers + Message] AI --> LLM[不同模型服务] CA --> SM[SessionManager JSONL 树] CA --> RL[资源加载与扩展] CA --> TUI[pi-tui] RL --> MCP[MCP / Code Mode 内建扩展] D[pi-durable 实验 Harness] --> AI D --> CH[Chord + Storage] SC[pi-server / pi-client] --> PR[pi-protocol]

这张图表达依赖职责,不表示所有包都在默认 CLI 的同一条调用链上。Durable 是另一种持久化 Harness;protocol/client/server 是额外的服务化组件,不应与 stdin/stdout RPC 混为一谈。

层 它承担什么 它不替你决定什么
pi-ai provider、模型发现、认证、消息和流 业务任务和工具执行策略
pi-agent-core 轮次、工具、队列、取消、事件 CLI 资源发现与文件会话
pi-coding-agent 编码工具、扩展、会话、压缩、恢复 你的产品权限模型
pi-tui 终端输入和渲染 业务判断
pi-mcp / pi-codemode 外部工具接入与脚本执行 工具本身的安全性
pi-durable 存储先行、恢复、任务状态 外部系统恰好执行一次
Chord 服务组合、复制状态、插件生命周期 应用的网络部署和用户认证

2.4 设计思想三:历史是数据,当前上下文是投影

一个长会话包含很多旧消息、分支、模型切换和压缩记录。直接把所有东西推给模型,会超窗口,也会把废弃分支混入请求。

Pi 用 JSONL 记录树状历史;SessionManager 从当前 leaf 走父节点,重建活跃分支;压缩 entry 决定哪些旧消息用摘要替代。原始历史和当前模型输入因此是两个不同对象。

应用方法:SDK 里恢复会话应使用 SessionManager。不要只给 session.agent.state.messages 赋一个历史数组并期待它会覆盖持久化记录;下次请求会从 canonical projection 重建。

2.5 设计思想四:把协议事实记录进 transcript

1.0 中 system messages 携带 prompt sections 和工具声明变化。工具可执行实现存在运行时,而模型能看见的工具声明是 transcript 中的可重放状态。

例如研究助手从只读切到编辑状态,工具集合发生变化。Pi 在后续请求前记录 toolsAdded / toolsRemoved,不只偷偷修改某个内存数组。

这是把「当时模型被允许看到什么」变成可追溯事实。分段 prompt 补丁也让更新某个 skill 或 MCP 摘要时不必重写完整历史前缀,有利于 provider prompt caching。具体缓存命中仍由 provider 决定。

2.6 设计思想五:事件驱动,但有明确屏障

流式 token 只是观察值;message_end 才是完整消息。Core Agent 处理事件后按注册顺序等待订阅者,因此 assistant 结束事件可以在工具预检前完成状态处理。

原始 agentLoop() 的 EventStream 是观察流,不会等待消费者异步处理。若你的权限或存储依赖这种先后顺序,应使用 Agent 类或者自己实现显式屏障。

SDK 层还存在自动重试和溢出恢复,所以 agent_end 不是最终产品完成标志;应用应等待 session.prompt() 返回,或观察 agent_settled。

2.7 设计思想六:扩展覆盖多条路径,嵌套工具也走统一管线

模型直接调用、MCP 调用、Code Mode 内部调用都需要统一参数校验、拦截和结果处理。否则你在外层禁止 bash,脚本却能从侧门执行它。

ctx.executeTool() → nested runner → runToolCall() 复用预检/执行/后处理。子调用带 parentToolCallId,可以记录归属。

产品建议:你的业务授权要挂在执行边界,而不是只隐藏 UI 按钮。工具曝光控制描述的是可发现性和可调用性,它本身不是操作系统沙箱。

2.8 设计思想七:简单起步,明确承担安全边界

Pi 不默认对每次工具调用弹确认框,也不内建文件、网络和进程沙箱。它拥有启动账户的权限。cwd 是路径和资源发现的默认位置,不是限制访问的牢笼。

对个人可信项目,你可以接受这种直接性;对多租户产品,你必须自己提供 OS / 容器边界、身份认证和工具授权。模型提示中的「不要访问目录外」无法替代真实隔离。

这不是抽象提醒:给模型一个任意 bash 工具,就同时给了它当前账户可以执行的程序、网络访问与凭据读取能力。课程实验先用窄业务工具,让你看清权力从哪里产生。

2.9 如何判断是否需要改内核

想要的能力 优先实现位置
固定回答风格、项目规则 AGENTS.md / APPEND_SYSTEM.md
一套反复使用的提示 Prompt template
复杂工作步骤和参考资料 Skill
API、工具、拦截、命令、持久状态 Extension
自己的网页或后台程序 Coding Agent SDK
不需要默认编码工具和资源发现 Agent Core
模型协议不受支持 Provider extension / pi-ai provider
崩溃后长期任务恢复 调研 Durable,或自己实现任务持久化

先组合,再改内核。只有现有接口无法表达你的需求,且你愿意维护版本差异时,才考虑 fork。

主要依据:官方 README、Agent、AgentSession、安全文档。