补全章节说明:原教程(dg-ai-notes)插图版只覆盖到第 10 章。本章综合前 12 章的设计分析与 Pi v0.81.1 官方文档(README.md「Philosophy」、docs/security.md、docs/skills.md、docs/packages.md 等)撰写,作为全系列的收官。引用时区分了官方原话(英文引文)与教程提炼(作者观点),行文风格对齐前 10 章。
前 12 章,我们钻了 12 个子系统。每一章的结尾都有一节"设计精华"或"方法论提炼"——如果你一路读下来,手里已经攒了三十多条零散的设计经验。
这一章不再钻新的子系统。我们把 12 章的决策摆到同一张桌子上,做三件事:找出它们背后共同的思维方式(四条主线);汇总可以搬进你自己项目的方法论(一张总表);以及同样重要的——划清哪些设计是 Pi 语境的特产,照搬会翻车(边界清单)。
每章的核心决策压缩成一行:
| 章 | 子系统 | 核心决策 | 一句话本质 |
|---|---|---|---|
| 1 | 总览 | 三位一体:工具 / 教材 / SDK | 减法是一种产品立场 |
| 2 | 架构 | 三层堆栈 + 正交 UI 库 | 依赖漏斗:底层不知道上层的存在 |
| 3 | Agent Loop | stopReason 驱动 + 内核/叠加 | 循环的去留,交给模型输出模式判断 |
| 4 | 模型调用 | 事件协议 + 翻译器函数 | 协议 > 实现:不写 BaseProvider 抽象类 |
| 5 | 工具系统 | 五步管道 + 错误即消息 | 工具出错不抛异常,编码成消息让模型自愈 |
| 6 | 消息系统 | 双层消息 + 边界翻译 | 数据结构同时照顾两个读者(功能层 / 模型) |
| 7 | 事件驱动 | 同步屏障 + 两层事件 | emit 不是通知,是同步协商 |
| 8 | 上下文工程 | 多层防护 + 拉模式加载 | 没有银弹,只有层层兜底 |
| 9 | 上下文压缩 | 逆向切点 + 结构化摘要 | 用固定模板对抗 LLM 的自由发挥 |
| 10 | 会话管理 | 树形 + append-only | 不删数据的回退:历史无价,磁盘便宜 |
| 11 | 扩展系统 | 事件总线 + 两阶段绑定 | 用"约定"换"灵活",把功能决定权交给用户 |
| 12 | 测试模式 | 边界注入 + 事件断言面 | 可测试性是抽象边界设计时"留"出来的 |
盯着这张表看一会儿,你会发现 12 个决策不是 12 种智慧——它们反复在用同样几招。下面四节,就是这几招。
Pi 官方 README 的「Philosophy」章节,是整个项目最浓缩的宣言。总纲:
"Pi is aggressively extensible so it doesn't have to dictate your workflow. Features that other tools bake in can be built with extensions, skills, or installed from third-party pi packages. This keeps the core minimal while letting you shape pi to fit how you work." (Pi 激进地可扩展,所以它不必替你决定工作流。别的工具内建的功能,这里用扩展、技能或第三方包实现。核心因此保持最小,而你可以把 Pi 塑造成你的工作方式。)
六条 "No",逐条原文(README.md「Philosophy」):
| # | 官方原文 | 给出的替代方案 |
|---|---|---|
| 1 | "No MCP. Build CLI tools with READMEs (see Skills), or build an extension that adds MCP support." | 带 README 的 CLI 工具(走 Skills),或自写扩展 |
| 2 | "No sub-agents. There's many ways to do this. Spawn pi instances via tmux, or build your own with extensions, or install a package that does it your way." | tmux 多实例 / 扩展 / 第三方包 |
| 3 | "No permission popups. Run in a container, or build your own confirmation flow with extensions inline with your environment and security requirements." | 容器隔离,或按自己的安全要求用扩展自建确认流 |
| 4 | "No plan mode. Write plans to files, or build it with extensions, or install a package." | 把计划写进文件 |
| 5 | "No built-in to-dos. They confuse models. Use a TODO.md file, or build your own with extensions." | TODO.md 文件(理由罕见地直接:内置待办"会把模型搞糊涂") |
| 6 | "No background bash. Use tmux. Full observability, direct interaction." | tmux——完全可观测、直接交互 |
看完 12 章再回头读这六条 "No",你会发现每一条都不是"偷懒不做",而是有配套机制兜底的战略放弃:
前提 1:扩展系统足够强(第 11 章)。 六条 "No" 里五条的替代方案包含 "build with extensions"。官方 README 列出的扩展能力清单里,"Sub-agents and plan mode"、"Permission gates and path protection"、"MCP server integration" 赫然在列。敢做减法,是因为加法的接口已经留好了。 没有事件总线和 34 个介入点,"No permission popups" 就只是裸奔。
前提 2:现有工具已经解决(Unix 哲学)。 "No background bash. Use tmux."——后台任务管理是 tmux 花了十几年磨好的能力,重造一遍只会更差。第 8 章的 Skills 拉模式同理:"带 README 的 CLI 工具"本身就是比 MCP 更轻的集成协议。
前提 3:文件比功能更持久。 plan 写进 plan.md、待办写进 TODO.md、项目规范写进 AGENTS.md——文件能进 git、能 diff、能被任何编辑器打开、能被下一个工具读取。内置功能只活在这个工具里,文件活在整个生态里。
教程提炼:减法哲学的完整表述不是"少做功能",而是——每砍掉一个功能,都要说清楚"谁来补、用什么补、为什么补得更好"。 六条 "No" 每条都带着替代方案,这才是它和"没做完"的区别。
"核心不做"之所以不等于"用户没有",靠的是五种定制杠杆。把它们放在一张表里看,会发现每种杠杆的能力上限和上下文成本是精心错开的:
| 杠杆 | 本质 | 能力上限 | 上下文成本 | 加载位置 |
|---|---|---|---|---|
| Prompt Templates | Markdown 文件,/名字 展开 | 复用一段提示词(支持 $1/$@ 参数) | 用时才展开 | ~/.pi/agent/prompts/、.pi/prompts/ |
| Skills | 带 frontmatter 的说明书 | 教模型一套流程/工具用法 | 仅 name+description 常驻,全文按需 read | ~/.pi/agent/skills/、.agents/skills/ 等 |
| Themes | JSON 颜色定义(51 个 token) | 改 TUI 外观 | 零 | ~/.pi/agent/themes/ |
| Extensions | TypeScript 模块(第 11 章) | 任意代码:工具/命令/事件拦截/UI | 按注册内容 | ~/.pi/agent/extensions/、.pi/extensions/ |
| Pi Packages | npm/git 分发容器 | 打包以上四种 | — | pi install npm:... / git:... |
Skills 那一行是官方对"渐进式披露"最标准的实现,skills.md 的原话:
"This is progressive disclosure: only descriptions are always in context, full instructions load on-demand." (这就是渐进式披露:只有描述常驻上下文,完整指令按需加载。)
另一个细节体现了生态观:Pi 遵循 Agent Skills 标准但刻意放宽了"技能名必须等于目录名"的约束,理由是共享技能目录会被多个 Agent 工具混用(skills.md)——你甚至可以直接把 ~/.claude/skills 配给 Pi 用。兼容别人的生态,而不是再造一个自己的,这也是减法。
减法哲学最有争议的落点是安全。Pi 默认 YOLO——没有权限弹窗。官方 security.md 的论证:
"A partial in-process sandbox would be easy to misunderstand as a security boundary while still depending on the host shell, filesystem, package managers, credentials, and extension code. Real isolation needs to come from the operating system or a virtualization/container boundary." (进程内的半吊子沙箱容易被误当成安全边界,实际上它仍然依赖宿主 shell、文件系统、包管理器、凭据和扩展代码。真正的隔离必须来自操作系统或虚拟化/容器边界。)
以及对提示注入的罕见坦诚:
"Prompt injection from repository files, comments, documentation, context files, or build output is expected local-agent risk and cannot be reliably prevented by pi." (来自仓库文件、注释、文档、上下文文件或构建产物的提示注入,是本地 Agent 的预期风险,Pi 无法可靠地防住它。)
这个立场可以概括为:与其提供一个让人产生安全错觉的机制,不如诚实地说"我不安全,请把我关进容器"。(前 10 章提到的"弹窗疲劳 / security theater"等说法出自作者 Mario 的博客阐述,官方文档采用的是上面这种更工程化的表述——两者指向同一判断:形同虚设的防护比没有防护更危险。)
第 12 章其实已经见过这个思维的另一个化身:skipIf 让没有 API key 的测试显示为"跳过"而不是"通过"——不假装验证过没验证的东西。诚实一以贯之。
把 12 章里所有"两个组件怎么打交道"的答案排成一列,会看到惊人的一致性——Pi 几乎从不用继承和直接调用来连接组件,永远用"协议":
| 连接点 | 协议是什么 | 章节 |
|---|---|---|
| Agent Loop ↔ 30+ 家 LLM | StreamFunction 函数签名 + 12 种流事件 | 第 4 章 |
| Agent ↔ UI / 持久化 / 日志 | 10 种 AgentEvent + subscribe | 第 7 章 |
| Agent ↔ 工具 | Tool 三层类型 + ToolResultMessage(错误也是消息) | 第 5 章 |
| 消息系统 ↔ LLM API | 三种标准消息 + convertToLlm 边界翻译 | 第 6 章 |
| 核心 ↔ 扩展 | 34 种事件 + 返回值协议(block / cancel / 链式补丁) | 第 11 章 |
| 会话 ↔ 存储 | SessionStorage 接口(JSONL / 内存 / 你的数据库) | 第 10 章 |
| 工具 ↔ 操作系统 | Operations 最小接口(本地 / SSH / 沙箱 / Mock) | 第 5 章 |
| 系统 ↔ 测试 | 以上全部协议的免费复用 | 第 12 章 |
第 4 章解释过为什么不用 BaseProvider 抽象类:"翻译器之间几乎没有共同点——继承需要找共同代码,但协议只约定输入输出。"这个判断推广到全系统就是:
协议的三重红利:
第三条最容易被忽视,也最值钱。第 12 章的结论值得再引一次:可测试性不是测试阶段"加"上去的,而是每个抽象边界设计时"留"出来的。 协议就是那个"留"的动作。
教程提炼:设计两个组件的连接时,先写协议(事件清单 + 返回值语义 + 错误语义),再写实现。协议要窄(StreamFunction 只有一个函数)、要完备(错误也在协议内——第 5 章"错误即消息")、要有版本意识(第 6 章声明合并留扩展槽)。
Pi 里"分层"出现了三次,形态不同,原则相同:
包级分层(第 2 章):pi-ai → pi-agent-core → pi-coding-agent 依赖漏斗,pi-tui 完全正交。验证标准朴素到残酷——"去掉上层,这一层还能跑吗?"
逻辑分层(第 3 章):Agent Loop 的"内核 + 叠加"。最简循环十几行是所有 Agent 的通用法则;steering、followUp、钩子是产品功能的按需叠加。"剥掉任何一层,里层仍能跑。"
能力分层(第 11 章):扩展 API 的三层门面——ExtensionAPI(注册)、ExtensionContext(观测 + 温和动作)、ExtensionCommandContext(危险操作)。分层依据不是信任等级,而是调用时机的安全性。
三者共享同一个原则——最小知识:每一层只知道、只暴露、只依赖它职责内刚好够用的东西。pi-ai 不知道 Agent 的存在,Agent 内核不知道 TUI 的存在,事件处理器拿不到 switchSession。
分层思想在数据结构上的投影,是第 6 章那条主线:"数据结构要同时照顾两个读者。"内层 AgentMessage 字段丰富伺候功能层,外层三种标准消息伺候 LLM 协议,边界处一次有损翻译。第 10 章的 Session Tree(树形存储 vs 压扁成线性 messages)、第 9 章的结构化摘要(六段模板 vs 自由文本)都是同一思路的变体。
教程提炼:发现一个数据结构在"两拨人抢着用"时,不要折中成一个谁都不满意的形状——分成两层,各自伺候好自己的读者,边界处写翻译器。
这是四条主线里最抽象、也最"Pi"的一条。整个系统里反复出现同一个动作:核心代码拒绝做决定,把决定权移交给更合适的决策者。
| 决定 | 交给谁 | 机制 | 章节 |
|---|---|---|---|
| 循环要不要继续 | 模型 | 输出里有没有 toolCall——代码只做信号判断 | 第 3 章 |
| 工具出错后怎么办 | 模型 | 错误编码成消息,模型自己决定重试/换路/解释 | 第 5 章 |
| 加载哪个技能 | 模型 | 拉模式:清单进 prompt,模型按需 read 全文 | 第 8 章 |
| 要不要拦这个工具调用 | 用户(扩展) | tool_call 事件 + block 协议 | 第 11 章 |
| 要不要权限弹窗、要不要子 Agent | 用户(扩展/包) | 六条 "No" 的全部替代方案 | 第 1、11 章 |
| 安全边界画在哪 | 操作系统/容器 | 拒绝进程内假沙箱 | 第 1 章、security.md |
| 会话存在哪 | 集成方 | SessionStorage 接口 | 第 10 章 |
注意这不是"什么都不管"的甩锅——每一次移交都配着一份精确的协议(这就是主线二的接口费):模型拿到的是结构化错误消息,扩展拿到的是带返回值语义的事件,容器拿到的是一个诚实声明"我不做隔离"的进程。
反面教材就是各家"全包"工具的路径:工具替用户决定要不要弹窗(结果弹窗疲劳)、替模型决定任务分几步(结果计划模式僵化)、替集成方决定存储格式(结果没法上云)。每替别人做一个决定,就欠下一笔"适配所有人"的债。
教程提炼:遇到"系统该不该做 X"的争论时,先问一句——这个决定的最佳决策者是谁? 如果是模型,就把信息编码进它能看到的消息;如果是用户,就留协议化的介入点;如果是环境,就诚实声明边界。核心代码只保留"没有更合适决策者"的决定。
把 12 章所有"可迁移方法论"汇总归类(括号内为出处章节),当作你自己项目的查阅清单:
架构与分层
协议与接口
事件与插件
上下文与数据
测试与质量
Pi 的一切设计都建立在一个具体语境上:单用户、本地终端、开发者工具、作者有极强的品味和克制力。换了语境,照搬就是翻车。四个典型:
1. YOLO 默认 ≠ 你的产品可以没有权限控制。 Pi 敢默认全放行,是因为用户 = 机器主人 = 后果承担者,三位一体;跑在容器里则连后果都可控。如果你做的是多租户 SaaS、面向非技术用户的产品、或者 Agent 操作的是共享资源——权限控制就不是"安全表演",而是必需品。Pi 的启示不是"删掉弹窗",而是"弹窗要么真的保护什么,要么就别弹"。
2. 同步屏障 ≠ 所有事件系统都该 await。 第 7 章讲过代价:Agent 必须等最慢的消费者。终端场景消费者就三五个(TUI、存储、扩展),等得起。如果你的事件要广播给成百上千个消费者、或者消费者里有慢 IO——照搬同步屏障会把吞吐拖死。Pi 自己都给 tool_execution_update 开了口子,你更该按事件价值分级。
3. JSONL 文件存储 ≠ 服务端也这么存。 本地单用户,文件就是最好的数据库(可 grep、可 git、零运维)。上了服务端多用户,并发写、检索、配额全来了——好在第 10 章讲过,Pi 把"存哪里"和"长什么样"拆成了两个独立维度,SessionStorage 接口就是给你换数据库留的门。照搬的应该是"树形 + append-only"的数据结构,而不是 JSONL 这个介质。
4. 90 词系统提示词 ≠ 提示词越短越好。 Pi 的系统提示词能极简,是因为它把知识挪进了三个地方:AGENTS.md(项目规范)、Skills(按需拉取)、工具描述(行为约定)。总量没有消失,只是从"每次全款预付"变成了"按需支付"。如果你没有这套渐进式披露的机制就硬砍提示词,砍掉的是能力不是浪费。
边界清单的通用判别法:把 Pi 的每个设计还原成"决策 + 语境前提",前提在你的场景里成立,才搬决策。前提不成立时,往往上一层的思维方式(主线一到四)仍然成立——那才是真正可以无条件带走的东西。
三十条方法论记不住没关系。压缩到三样:
1. 一个标准:每个抽象边界,用"另一方可以被替换成假的吗"来检验。能——说明协议干净;不能——说明知识泄漏了。这一个标准同时买到可扩展、可维护、可测试。
2. 一个动作:做功能取舍时,把"不做"当成一等选项认真论证——它的替代方案是什么?谁来补?为什么补得更好?写不出这三个答案的"不做"是偷懒,写得出来的是设计。
3. 一个问题:每当要写"如果 X 就 Y"的逻辑时,先问"这个决定的最佳决策者是谁?"——是模型、是用户、是环境,还是真的只能是你的代码?
最后,关于怎么继续。这个教程读完只是"看懂了",离"学到手"还差一次实践。三条路线按投入排序:
Pi 证明了一件事:在一个所有人都在做加法的赛道里,把每一次"不做"都设计得有理有据,本身就是最锋利的竞争力。 愿你的下一个系统,也敢这样做减法。
(全系列完)
| 数字 | 事实 | 出处 |
|---|---|---|
| 4 / 7 | 默认给模型 4 个工具(read/write/edit/bash);内置共 7 个(+grep/find/ls) | README.md |
| 3 + 30+ | 订阅制 provider 3 家(Claude Pro/Max、ChatGPT Codex、Copilot);API-key provider 约 30 家 | README.md |
| 7 档 | 思考等级:off / minimal / low / medium / high / xhigh / max | README.md |
| 16384 / 20k | 压缩参数默认值:reserveTokens 16384(给回复留量)、keepRecentTokens 20k(近期不压缩)——第 9 章的两个阈值 | docs/compaction.md |
| 50KB / 2000 行 | read 工具内建截断上限(先到者胜)——第 8 章双重限制的官方数字 | docs/extensions.md |
| 51 个 | 主题必须定义的颜色 token 数 | docs/themes.md |
| 64 / 1024 | Skill frontmatter 的 name / description 长度上限 | docs/skills.md |
| 34 种 | 扩展事件类型总数——第 11 章 | extensions/types.ts |
| 337 / ~3600 | 测试文件数 / 用例量级——第 12 章 | 仓库统计 |
| 4 种 | 运行模式:interactive / print(-p) / json / rpc,全部构建在 createAgentSession() 之上 | docs/sdk.md |
本章素材来源(Pi v0.81.1):
- README.md「Philosophy」— 六条 "No" 原文与总纲;「What's possible」— 扩展能力清单
- docs/security.md — 信任边界、进程内沙箱批判、提示注入坦诚声明
- docs/skills.md — 渐进式披露:"only descriptions are always in context, full instructions load on-demand"
- docs/packages.md — 五种定制杠杆的分发机制;docs/sdk.md — 四包分层与 SDK/RPC 选型
- 第 1-12 章各章"总结 / 设计精华 / 方法论提炼"小节
版本说明:本章引用的官方文档与源码为 Pi v0.81.1(前 10 章基于 v0.80.2)。第 11-13 章为补全章节,官方原教程(dg-ai-notes)发布时未包含。