第 09 章:进阶 —— 让 agent 长成你的样子
mini-pi 已经完成。这一章不写大功能,而是给你一套"继续生长"的地图:三个最常用的扩展手法(自定义工具、工具钩子、上下文变换)、pi 的扩展哲学,以及接下来该读什么。
手法一:自定义工具
在 pi 栈上,工具就是一个普通对象:名字、行为导向的描述、TypeBox 参数 schema、execute 函数。给第 08 章的 mini-pi 加一个工具只需几行:
回顾你在第 02–03 章学到的东西,这里没有任何新魔法:schema 负责让模型"会调",execute 负责执行,返回值分"给模型的 content"和"给 UI 的 details"两层——后者是 pi-ai 的独到设计,让你不用解析文本就能渲染界面(见设计思想:LLM 抽象层)。
工具的失败约定也和老朋友一致:失败就 throw,agent 会捕获并以 isError 回喂模型。
手法二:工具钩子 —— 权限门与结果改写
Agent 在每个工具调用的前后都留了钩子,这是不修改 loop 就能改变行为的关键扩展点:
被 block 的调用不会执行,原因文本会作为工具错误回给模型——它会看到"被策略拦截"并换方案。afterToolCall 则相反:在执行后改写结果、追加审计信息,或返回 terminate: true 让 loop 提前收尾。pi 把这类能力做到了扩展系统里,examples/extensions/permission-gate.ts 和 protected-paths.ts 就是完整的生产级例子。
pi 默认 YOLO,不是不知道风险,而是认为弹窗确认挡不住能写代码又能跑代码的 agent(论证见设计思想:"不做"清单)。钩子是"策略",不是"防线";真正的边界是容器/沙箱。两者按需组合。
手法三:上下文变换
每个 turn 调 LLM 之前,transformContext 允许你改写即将发送的消息列表——上下文工程的官方入口:
第 07 章的压缩、pi 的自定义摘要(custom-compaction.ts 示例)、往上下文里注入动态信息(时间、git 状态),都发生在这个点上。
pi 的扩展哲学:为什么核心什么都不内置
至此你看完了 mini-pi 的全部,回头看 pi 的产品形态就顺理成章了。pi 核心刻意不内置 plan mode、sub-agent、todo 等功能,而是提供四个扩展面:
- Extensions:TypeScript 模块,可注册工具/命令/快捷键/事件钩子/UI 组件,放在
~/.pi/agent/extensions/或项目.pi/extensions/。plan mode、sub-agent 在社区看来该是"核心功能"的东西,在 pi 这里是examples/extensions/下的示例——plan-mode 和 subagent 都值得一读,它们是你写复杂扩展的最好教材。 - Skills:遵循 Agent Skills 标准 的按需能力包——一个
SKILL.md(可附带脚本、参考文档),模型需要时才加载,不占常驻上下文。这是 pi 对 MCP 的回答:渐进式披露。 - Prompt templates:带参数的可复用提示词 markdown,
/name展开。 - Themes:热重载的界面主题。
这四个面共享一个思想:能力以文件形式存在、按需加载、可以用 git/npm 分享(pi packages)。你自己的 agent 长到一定复杂度后,也会需要同样的一课:不要把所有能力焊死在核心里。
接下来的路
- 把 mini-pi 用烂:给它加会话恢复(裸写版已有)、加 steering 队列(
agent.steer())、把 REPL 换成你喜欢的外壳。真正的理解来自使用中的磕碰。 - 按源码导读读 pi 源码:
agent-loop.ts→ 工具 →system-prompt.ts→session-manager.ts→agent-session.ts。有了本教程的底子,每一行你都知道为什么在那里。 - 读作者的构建手记原文:设计决策的第一手论证,比任何转述都精彩。
- 给 pi 写一个扩展并分享:pi packages 可以通过 npm/git 分发,这是检验你是否真的掌握了这套哲学的最好方式。
结课语
回顾这十章:从一次 fetch 开始,我们亲手实现了 loop、四大工具、流式与中断、系统提示词、会话树、压缩,再用 pi 的官方积木重构——你已经拥有了一个 coding agent 的全部骨架,并且理解每一块的"为什么"。
pi 教给我们的最重要一课不是某个具体实现,而是一种判断力:agent 的威力来自模型本身,harness 的职责是管好上下文、提供恰好的工具、不挡路。剩下的,交给你和你的 agent 了。