Harness Engineering:Pi Agent 架构深度解析
在之前的文章中,我们系统性地讨论了 Harness Engineering 的七大设计准则------从 Loop、Tool Registry、可观测性到上下文管理与压缩。这些准则构成了企业级 AI Agent 的工程化底座。但好的理论需要有好的实践作为参照。
今天,我们把目光投向一个具体的 Agent 实现------Pi。
Pi(全称 pi-mono)是由 Mario Zechner 开发的开源 Agent 运行时框架,MIT 协议,在 GitHub 上拥有活跃的社区生态。与 Claude Code、Cursor 等封装好的"成品 AI 助手"不同,Pi 提供的是一个**"造 AI 助手的工厂"**------它不是一个封闭的产品,而是一个可塑的平台,让你可以按需定制自己的 Agent。
Pi 在设计哲学上与 Harness Engineering 高度契合------极简主义、工程化优先、深度可扩展。这正是我们选择它作为案例的原因。本文将深入拆解 Pi 的架构设计,看看一个生产级的 Agent 框架是如何构建的。
一、核心理念:极简主义,而非功能堆砌
在了解具体架构之前,先理解 Pi 的设计哲学。
当前 AI Agent 领域有一种趋势------不断往框架里堆功能、堆工具、堆场景化的封装。每个场景单独开发一套工具集,最终导致代码库膨胀、维护成本飙升、开发者难以驾驭。
Pi 走了一条完全相反的路------回归 Unix 设计哲学。它只封装了 4 个核心原子工具:
| 工具 | 功能 | 设计意图 |
|---|---|---|
read |
读取文件内容 | 信息获取 |
write |
创建/覆盖文件 | 内容产出 |
edit |
基于字符串匹配的精准修改 | 迭代优化 |
bash |
执行 Shell 命令 | 与系统交互 |
这 4 个工具就像 Unix 的管道一样基础。不需要为每个细分场景单独开发工具------Agent 可以通过组合这些原子工具,自主组合出场景化的执行能力。
WPS 社区的一份技术分析报告对这个设计思路有精辟的概括:Pi 彻底摒弃了"场景化工具堆砌"的传统思路,通过原子工具的组合调用,让 Agent 自主组合出场景化执行能力,降低工具生态的基础开发维护成本,适配用户个性化、非标准化的任务需求。
与 Harness Engineering 的对应关系 :Pi 的原子化工具体系,恰好呼应了我们之前讨论的 Tool Registry 设计------工具应该可治理、可发现、可组合,而不是硬编码堆砌。
二、分层架构:三层协作模型
Pi 的架构遵循清晰的分层设计。从底层到上层,依次是 pi-ai、pi-agent-core、pi-coding-agent 三个核心包。
2.1 整体架构图
#mermaid-svg-b04sAek1TCONnNUg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-b04sAek1TCONnNUg .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-b04sAek1TCONnNUg .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-b04sAek1TCONnNUg .error-icon{fill:#552222;}#mermaid-svg-b04sAek1TCONnNUg .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-b04sAek1TCONnNUg .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-b04sAek1TCONnNUg .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-b04sAek1TCONnNUg .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-b04sAek1TCONnNUg .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-b04sAek1TCONnNUg .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-b04sAek1TCONnNUg .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-b04sAek1TCONnNUg .marker{fill:#333333;stroke:#333333;}#mermaid-svg-b04sAek1TCONnNUg .marker.cross{stroke:#333333;}#mermaid-svg-b04sAek1TCONnNUg svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-b04sAek1TCONnNUg p{margin:0;}#mermaid-svg-b04sAek1TCONnNUg .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-b04sAek1TCONnNUg .cluster-label text{fill:#333;}#mermaid-svg-b04sAek1TCONnNUg .cluster-label span{color:#333;}#mermaid-svg-b04sAek1TCONnNUg .cluster-label span p{background-color:transparent;}#mermaid-svg-b04sAek1TCONnNUg .label text,#mermaid-svg-b04sAek1TCONnNUg span{fill:#333;color:#333;}#mermaid-svg-b04sAek1TCONnNUg .node rect,#mermaid-svg-b04sAek1TCONnNUg .node circle,#mermaid-svg-b04sAek1TCONnNUg .node ellipse,#mermaid-svg-b04sAek1TCONnNUg .node polygon,#mermaid-svg-b04sAek1TCONnNUg .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-b04sAek1TCONnNUg .rough-node .label text,#mermaid-svg-b04sAek1TCONnNUg .node .label text,#mermaid-svg-b04sAek1TCONnNUg .image-shape .label,#mermaid-svg-b04sAek1TCONnNUg .icon-shape .label{text-anchor:middle;}#mermaid-svg-b04sAek1TCONnNUg .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-b04sAek1TCONnNUg .rough-node .label,#mermaid-svg-b04sAek1TCONnNUg .node .label,#mermaid-svg-b04sAek1TCONnNUg .image-shape .label,#mermaid-svg-b04sAek1TCONnNUg .icon-shape .label{text-align:center;}#mermaid-svg-b04sAek1TCONnNUg .node.clickable{cursor:pointer;}#mermaid-svg-b04sAek1TCONnNUg .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-b04sAek1TCONnNUg .arrowheadPath{fill:#333333;}#mermaid-svg-b04sAek1TCONnNUg .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-b04sAek1TCONnNUg .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-b04sAek1TCONnNUg .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-b04sAek1TCONnNUg .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-b04sAek1TCONnNUg .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-b04sAek1TCONnNUg .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-b04sAek1TCONnNUg .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-b04sAek1TCONnNUg .cluster text{fill:#333;}#mermaid-svg-b04sAek1TCONnNUg .cluster span{color:#333;}#mermaid-svg-b04sAek1TCONnNUg div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-b04sAek1TCONnNUg .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-b04sAek1TCONnNUg rect.text{fill:none;stroke-width:0;}#mermaid-svg-b04sAek1TCONnNUg .icon-shape,#mermaid-svg-b04sAek1TCONnNUg .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-b04sAek1TCONnNUg .icon-shape p,#mermaid-svg-b04sAek1TCONnNUg .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-b04sAek1TCONnNUg .icon-shape .label rect,#mermaid-svg-b04sAek1TCONnNUg .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-b04sAek1TCONnNUg .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-b04sAek1TCONnNUg .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-b04sAek1TCONnNUg :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} pi-ai (LLM抽象)
pi-agent-core (循环逻辑)
pi-coding-agent (运行时)
AgentSession
会话编排器
SessionManager
会话持久化
ExtensionSystem
扩展系统
Compaction
上下文压缩
工具集
read/write/edit/bash
Agent 类
状态容器
agentLoop
双嵌套循环
工具执行器
三阶段流水线
消息系统
7种内部消息
Provider 抽象
20+ 提供商
ModelRegistry
模型注册
流式处理
宿主应用
2.2 三层模型详解
Layer 1:pi-ai(LLM 提供商抽象层)
这一层负责与各种 LLM 提供商通信,提供统一的接口抽象。它支持 20+ 提供商(OpenAI、Anthropic、Google、DeepSeek 等),负责流式处理、Token 计数和模型注册。上层完全不感知底层 API 的差异。
Layer 2:pi-agent-core(Agent 循环逻辑层)
这是 Pi 的心脏。Agent 类是状态容器,管理对话历史(transcript)、工具注册表和流式状态。核心的 agentLoop 函数实现了双嵌套循环:
- 外循环:处理 follow-up 消息(用户追加的输入)
- 内循环:流式获取 LLM 响应 → 检查工具调用 → 执行工具 → 将结果反馈给模型
循环将持续运行,直到 LLM 不再调用工具或达到终止条件。
此外,这一层提供了两个消息队列------steering 用于中途干预,followUp 用于完成后追加,并支持 AbortController 取消机制。
与 Harness Engineering 的对应关系 :Pi 的 agentLoop 双嵌套循环,正是我们之前在 Loop 设计要点中讨论的"有条件循环结构"的完整实现------具备执行节点、判断节点和状态累积器的完整能力。
Layer 3:pi-coding-agent(应用运行时层)
这一层构建在 core 之上,提供了完整的 Coding Agent 运行时:
- AgentSession:会话编排器,管理逐轮交互循环、事件路由、状态变更
- SessionManager:基于树形 JSONL 的会话持久化
- ExtensionSystem:Hooks 与扩展系统
- Compaction:上下文压缩机制
三、工具执行管道:三阶段流水线
工具执行是 Agent 的核心能力。Pi 的设计是一套三阶段流水线:
#mermaid-svg-2EWFRT57QajhuAjv{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-2EWFRT57QajhuAjv .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-2EWFRT57QajhuAjv .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-2EWFRT57QajhuAjv .error-icon{fill:#552222;}#mermaid-svg-2EWFRT57QajhuAjv .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-2EWFRT57QajhuAjv .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-2EWFRT57QajhuAjv .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-2EWFRT57QajhuAjv .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-2EWFRT57QajhuAjv .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-2EWFRT57QajhuAjv .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-2EWFRT57QajhuAjv .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-2EWFRT57QajhuAjv .marker{fill:#333333;stroke:#333333;}#mermaid-svg-2EWFRT57QajhuAjv .marker.cross{stroke:#333333;}#mermaid-svg-2EWFRT57QajhuAjv svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-2EWFRT57QajhuAjv p{margin:0;}#mermaid-svg-2EWFRT57QajhuAjv .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-2EWFRT57QajhuAjv .cluster-label text{fill:#333;}#mermaid-svg-2EWFRT57QajhuAjv .cluster-label span{color:#333;}#mermaid-svg-2EWFRT57QajhuAjv .cluster-label span p{background-color:transparent;}#mermaid-svg-2EWFRT57QajhuAjv .label text,#mermaid-svg-2EWFRT57QajhuAjv span{fill:#333;color:#333;}#mermaid-svg-2EWFRT57QajhuAjv .node rect,#mermaid-svg-2EWFRT57QajhuAjv .node circle,#mermaid-svg-2EWFRT57QajhuAjv .node ellipse,#mermaid-svg-2EWFRT57QajhuAjv .node polygon,#mermaid-svg-2EWFRT57QajhuAjv .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-2EWFRT57QajhuAjv .rough-node .label text,#mermaid-svg-2EWFRT57QajhuAjv .node .label text,#mermaid-svg-2EWFRT57QajhuAjv .image-shape .label,#mermaid-svg-2EWFRT57QajhuAjv .icon-shape .label{text-anchor:middle;}#mermaid-svg-2EWFRT57QajhuAjv .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-2EWFRT57QajhuAjv .rough-node .label,#mermaid-svg-2EWFRT57QajhuAjv .node .label,#mermaid-svg-2EWFRT57QajhuAjv .image-shape .label,#mermaid-svg-2EWFRT57QajhuAjv .icon-shape .label{text-align:center;}#mermaid-svg-2EWFRT57QajhuAjv .node.clickable{cursor:pointer;}#mermaid-svg-2EWFRT57QajhuAjv .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-2EWFRT57QajhuAjv .arrowheadPath{fill:#333333;}#mermaid-svg-2EWFRT57QajhuAjv .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-2EWFRT57QajhuAjv .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-2EWFRT57QajhuAjv .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2EWFRT57QajhuAjv .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-2EWFRT57QajhuAjv .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2EWFRT57QajhuAjv .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-2EWFRT57QajhuAjv .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-2EWFRT57QajhuAjv .cluster text{fill:#333;}#mermaid-svg-2EWFRT57QajhuAjv .cluster span{color:#333;}#mermaid-svg-2EWFRT57QajhuAjv div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-2EWFRT57QajhuAjv .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-2EWFRT57QajhuAjv rect.text{fill:none;stroke-width:0;}#mermaid-svg-2EWFRT57QajhuAjv .icon-shape,#mermaid-svg-2EWFRT57QajhuAjv .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2EWFRT57QajhuAjv .icon-shape p,#mermaid-svg-2EWFRT57QajhuAjv .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-2EWFRT57QajhuAjv .icon-shape .label rect,#mermaid-svg-2EWFRT57QajhuAjv .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2EWFRT57QajhuAjv .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-2EWFRT57QajhuAjv .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-2EWFRT57QajhuAjv :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Finalize 阶段
Execute 阶段
Prepare 阶段
解析工具名
参数规范化
JSON Schema 验证
beforeToolCall 钩子
调用 tool.execute
支持串行/并行模式
afterToolCall 钩子
可覆盖结果/错误标志
LLM 工具调用
返回结果给 LLM
这套流水线的设计价值在于可拦截、可扩展------在准备阶段可以修改参数、阻断执行;在收尾阶段可以覆盖结果、记录审计日志。结合扩展系统的 Hooks,开发者可以在任意阶段插入自定义逻辑。
四、扩展系统:25+ Hooks 挂载点
如果说 Pi 的核心循环是引擎,那么扩展系统就是方向盘。Pi 的架构本质上就是 "一个核心循环 + 无数个深度的 Hooks 挂载点" 。
4.1 四层 Hooks 分布
Pi 的 Hooks 分布在四个层次,每一层对应系统的不同生命周期:
#mermaid-svg-QY9wFxVZ82GCJSru{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-QY9wFxVZ82GCJSru .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-QY9wFxVZ82GCJSru .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-QY9wFxVZ82GCJSru .error-icon{fill:#552222;}#mermaid-svg-QY9wFxVZ82GCJSru .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-QY9wFxVZ82GCJSru .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-QY9wFxVZ82GCJSru .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-QY9wFxVZ82GCJSru .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-QY9wFxVZ82GCJSru .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-QY9wFxVZ82GCJSru .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-QY9wFxVZ82GCJSru .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-QY9wFxVZ82GCJSru .marker{fill:#333333;stroke:#333333;}#mermaid-svg-QY9wFxVZ82GCJSru .marker.cross{stroke:#333333;}#mermaid-svg-QY9wFxVZ82GCJSru svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-QY9wFxVZ82GCJSru p{margin:0;}#mermaid-svg-QY9wFxVZ82GCJSru .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-QY9wFxVZ82GCJSru .cluster-label text{fill:#333;}#mermaid-svg-QY9wFxVZ82GCJSru .cluster-label span{color:#333;}#mermaid-svg-QY9wFxVZ82GCJSru .cluster-label span p{background-color:transparent;}#mermaid-svg-QY9wFxVZ82GCJSru .label text,#mermaid-svg-QY9wFxVZ82GCJSru span{fill:#333;color:#333;}#mermaid-svg-QY9wFxVZ82GCJSru .node rect,#mermaid-svg-QY9wFxVZ82GCJSru .node circle,#mermaid-svg-QY9wFxVZ82GCJSru .node ellipse,#mermaid-svg-QY9wFxVZ82GCJSru .node polygon,#mermaid-svg-QY9wFxVZ82GCJSru .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-QY9wFxVZ82GCJSru .rough-node .label text,#mermaid-svg-QY9wFxVZ82GCJSru .node .label text,#mermaid-svg-QY9wFxVZ82GCJSru .image-shape .label,#mermaid-svg-QY9wFxVZ82GCJSru .icon-shape .label{text-anchor:middle;}#mermaid-svg-QY9wFxVZ82GCJSru .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-QY9wFxVZ82GCJSru .rough-node .label,#mermaid-svg-QY9wFxVZ82GCJSru .node .label,#mermaid-svg-QY9wFxVZ82GCJSru .image-shape .label,#mermaid-svg-QY9wFxVZ82GCJSru .icon-shape .label{text-align:center;}#mermaid-svg-QY9wFxVZ82GCJSru .node.clickable{cursor:pointer;}#mermaid-svg-QY9wFxVZ82GCJSru .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-QY9wFxVZ82GCJSru .arrowheadPath{fill:#333333;}#mermaid-svg-QY9wFxVZ82GCJSru .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-QY9wFxVZ82GCJSru .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-QY9wFxVZ82GCJSru .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-QY9wFxVZ82GCJSru .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-QY9wFxVZ82GCJSru .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-QY9wFxVZ82GCJSru .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-QY9wFxVZ82GCJSru .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-QY9wFxVZ82GCJSru .cluster text{fill:#333;}#mermaid-svg-QY9wFxVZ82GCJSru .cluster span{color:#333;}#mermaid-svg-QY9wFxVZ82GCJSru div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-QY9wFxVZ82GCJSru .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-QY9wFxVZ82GCJSru rect.text{fill:none;stroke-width:0;}#mermaid-svg-QY9wFxVZ82GCJSru .icon-shape,#mermaid-svg-QY9wFxVZ82GCJSru .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-QY9wFxVZ82GCJSru .icon-shape p,#mermaid-svg-QY9wFxVZ82GCJSru .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-QY9wFxVZ82GCJSru .icon-shape .label rect,#mermaid-svg-QY9wFxVZ82GCJSru .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-QY9wFxVZ82GCJSru .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-QY9wFxVZ82GCJSru .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-QY9wFxVZ82GCJSru :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Layer 4: Provider 传输层
onPayload
onResponse
Layer 3: 工具执行管道
tool_call
tool_execution_start
tool_execution_update
tool_result
afterToolCall
Layer 2: Agent 核心循环
before_agent_start
context
before_provider_request
after_provider_response
message_start
message_update
message_end
agent_end
Layer 1: Session 生命周期
session_start
session_shutdown
session_before_switch
session_before_fork
session_before_compact
Session
AgentLoop
Tools
Provider
4.2 关键 Hooks 与能力
| Hook | 触发时机 | 可做的事情 |
|---|---|---|
before_agent_start |
Agent 启动前 | 修改 systemPrompt、注入消息 |
context |
构建上下文时 | 替换整个 messages 数组 |
before_provider_request |
发送给 LLM 前 | 替换 provider payload |
message_update |
每个流式 chunk | 拦截每个 text_delta |
tool_call |
工具调用前 | 拦截/修改参数/阻断执行 |
session_before_compact |
上下文压缩前 | 自定义摘要逻辑 |
4.3 加载与绑定的两阶段架构
扩展的加载分为两个阶段:
- 加载阶段 :通过
jiti从文件系统发现扩展,此时只能注册 Hooks、工具和命令,不能调用运行时 API - 绑定阶段 :将真实实现注入共享运行时,此后扩展可以调用
sendMessage、setModel等运行时操作
这种设计的价值在于安全隔离------扩展在加载时无法执行有副作用的操作,必须等运行时显式授权。
与 Harness Engineering 的对应关系 :Pi 的扩展系统实质上提供了我们之前讨论的 HIL(Human-in-the-Loop) 和 可观测性 的基础设施------通过 Hooks 拦截关键节点,插入审批逻辑和监控埋点。
五、会话持久化:树形 JSONL
Pi 的会话管理使用了一种精巧的设计------追加写入(append-only)的树形 JSONL 存储。
5.1 核心数据结构
每次会话中的每个动作(用户消息、助手回复、工具调用、模型切换、压缩操作)都以一行 JSON 的形式追加到文件中。每个 Entry 包含:
yaml
Entry:
id: "唯一标识"
parentId: "父节点ID(支持分支导航)"
timestamp: "时间戳"
type: "message | compaction | model_change | branch_summary | custom"
content: "具体内容(按类型不同)"
5.2 分支与时间旅行
这种设计天然支持分支会话 。当你从某个节点分叉出一个新分支时,只需创建一个 parentId 指向该节点的新 Entry,即可形成树形结构:
#mermaid-svg-848U3ja6dMLLLmQA{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-848U3ja6dMLLLmQA .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-848U3ja6dMLLLmQA .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-848U3ja6dMLLLmQA .error-icon{fill:#552222;}#mermaid-svg-848U3ja6dMLLLmQA .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-848U3ja6dMLLLmQA .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-848U3ja6dMLLLmQA .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-848U3ja6dMLLLmQA .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-848U3ja6dMLLLmQA .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-848U3ja6dMLLLmQA .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-848U3ja6dMLLLmQA .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-848U3ja6dMLLLmQA .marker{fill:#333333;stroke:#333333;}#mermaid-svg-848U3ja6dMLLLmQA .marker.cross{stroke:#333333;}#mermaid-svg-848U3ja6dMLLLmQA svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-848U3ja6dMLLLmQA p{margin:0;}#mermaid-svg-848U3ja6dMLLLmQA .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-848U3ja6dMLLLmQA .cluster-label text{fill:#333;}#mermaid-svg-848U3ja6dMLLLmQA .cluster-label span{color:#333;}#mermaid-svg-848U3ja6dMLLLmQA .cluster-label span p{background-color:transparent;}#mermaid-svg-848U3ja6dMLLLmQA .label text,#mermaid-svg-848U3ja6dMLLLmQA span{fill:#333;color:#333;}#mermaid-svg-848U3ja6dMLLLmQA .node rect,#mermaid-svg-848U3ja6dMLLLmQA .node circle,#mermaid-svg-848U3ja6dMLLLmQA .node ellipse,#mermaid-svg-848U3ja6dMLLLmQA .node polygon,#mermaid-svg-848U3ja6dMLLLmQA .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-848U3ja6dMLLLmQA .rough-node .label text,#mermaid-svg-848U3ja6dMLLLmQA .node .label text,#mermaid-svg-848U3ja6dMLLLmQA .image-shape .label,#mermaid-svg-848U3ja6dMLLLmQA .icon-shape .label{text-anchor:middle;}#mermaid-svg-848U3ja6dMLLLmQA .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-848U3ja6dMLLLmQA .rough-node .label,#mermaid-svg-848U3ja6dMLLLmQA .node .label,#mermaid-svg-848U3ja6dMLLLmQA .image-shape .label,#mermaid-svg-848U3ja6dMLLLmQA .icon-shape .label{text-align:center;}#mermaid-svg-848U3ja6dMLLLmQA .node.clickable{cursor:pointer;}#mermaid-svg-848U3ja6dMLLLmQA .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-848U3ja6dMLLLmQA .arrowheadPath{fill:#333333;}#mermaid-svg-848U3ja6dMLLLmQA .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-848U3ja6dMLLLmQA .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-848U3ja6dMLLLmQA .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-848U3ja6dMLLLmQA .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-848U3ja6dMLLLmQA .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-848U3ja6dMLLLmQA .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-848U3ja6dMLLLmQA .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-848U3ja6dMLLLmQA .cluster text{fill:#333;}#mermaid-svg-848U3ja6dMLLLmQA .cluster span{color:#333;}#mermaid-svg-848U3ja6dMLLLmQA div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-848U3ja6dMLLLmQA .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-848U3ja6dMLLLmQA rect.text{fill:none;stroke-width:0;}#mermaid-svg-848U3ja6dMLLLmQA .icon-shape,#mermaid-svg-848U3ja6dMLLLmQA .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-848U3ja6dMLLLmQA .icon-shape p,#mermaid-svg-848U3ja6dMLLLmQA .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-848U3ja6dMLLLmQA .icon-shape .label rect,#mermaid-svg-848U3ja6dMLLLmQA .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-848U3ja6dMLLLmQA .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-848U3ja6dMLLLmQA .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-848U3ja6dMLLLmQA :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-848U3ja6dMLLLmQA .active>*{fill:#e1f5fe!important;}#mermaid-svg-848U3ja6dMLLLmQA .active span{fill:#e1f5fe!important;}#mermaid-svg-848U3ja6dMLLLmQA .inactive>*{fill:#fff3e0!important;}#mermaid-svg-848U3ja6dMLLLmQA .inactive span{fill:#fff3e0!important;} 根节点 A
节点 B
节点 C
节点 D
节点 E
节点 F
A
当前活跃路径
已废弃分支
上下文重建 时,buildSessionContext() 从当前叶子节点回溯到根节点,重建完整的消息链。这意味着你可以随时"跳回"到任意历史状态,分支出全新的执行路径。
与 Harness Engineering 的对应关系 :Pi 的树形 JSONL 持久化,就是我们在 State Store 设计要点中讨论的"状态持久化与断点续传"的具体实现------而且它更进一步,支持分支和时间旅行。
六、上下文压缩:三种触发机制
Pi 的上下文压缩机制是我们上一篇文章讨论的核心主题的完整落地实现。
6.1 三种触发方式
Pi 提供了三种压缩触发路径:
| 触发方式 | 说明 |
|---|---|
| 手动触发 | 用户输入 /compact 命令主动压缩 |
| Token 阈值触发 | 在 agent_end 时检查,若超过阈值则自动压缩 |
| 溢出恢复触发 | LLM 返回上下文溢出错误时,自动触发压缩后重试 |
6.2 压缩算法的关键设计
切割点规则:
压缩时,Pi 必须从有效的切割点进行切割。有效的切割点包括:
- 用户消息(User messages)
- 助手消息(Assistant messages)
- BashExecution 消息
- 自定义消息(custom_message, branch_summary)
绝对不能在工具结果(Tool Result)处切割------工具调用和工具结果必须配对保留。
跨回合切割(Split Turn):
当单次对话轮次超出 keepRecentTokens 预算时,切割点会落在助手消息中间。这种情况下,Pi 会生成两个摘要并合并:
- 历史摘要:之前的上下文(如有)
- 轮次前缀摘要:被切割轮次的前半部分
累积文件跟踪:
无论是压缩还是分支摘要,Pi 都会累计追踪文件操作(读取了哪些文件、修改了哪些文件)。这个信息会从工具调用中提取,并随压缩历史逐层累积,确保即使经过多次压缩,文件操作的完整历史仍然可追溯。
6.3 CompactionEntry 结构
压缩操作本身也被持久化,形成会话历史中的一个特殊 Entry:
typescript
interface CompactionEntry {
type: "compaction";
id: string;
parentId: string;
timestamp: number;
summary: string; // LLM 生成的摘要
firstKeptEntryId: string; // 保留的第一条 Entry ID
tokensBefore: number; // 压缩前的 Token 数
fromHook?: boolean; // 是否由扩展触发
details: {
readFiles: string[]; // 本次压缩涉及的读取文件
modifiedFiles: string[]; // 本次压缩涉及的修改文件
};
usage?: Usage; // 生成摘要的 LLM 调用消耗
cumulativeUsage?: Usage; // 累积消耗
}
与 Harness Engineering 的对应关系 :Pi 的压缩机制完整覆盖了我们之前讨论的上下文压缩 设计要点------包括自动触发、跨回合切割、累积追踪。尤其是 CompactionEntry 的持久化设计,与我们的"版本控制与回滚"理念完全一致。
七、扩展生态:社区实践的启示
Pi 的开源生态提供了丰富的扩展实现,可以作为 Harness Engineering 设计的实践参考。
7.1 pi-context-loader:配置驱动的上下文注入
这个扩展通过在 session_start 时加载配置、在 before_agent_start 时注入上下文,实现了配置驱动的上下文注入。
它支持两种注入模式:
| 模式 | 行为 | 生命周期 | 适用场景 |
|---|---|---|---|
systemPrompt |
追加到 System Prompt | 每轮都出现 | 身份定义、规则约束 |
message |
作为对话消息注入 | 仅第一轮 | 运营状态、当前任务 |
Harness 映射 :这正是我们讨论的上下文管理中"会话上下文初始化"的一种优雅实现。
7.2 pi-fold:无损上下文折叠
pi-fold 实现了一种**"无损上下文折叠"**机制------Agent 可以把旧上下文折叠成摘要,但保留指向原始内容的指针,需要时可以精确展开恢复。
这与传统压缩的关键区别在于可逆性:
- 传统压缩:丢弃内容,不可恢复
- pi-fold:折叠内容,通过 SHA-256 校验可精确恢复
它的设计融合了 Lossless Contextual Compression(LCM,arXiv:2605.04050)和 Self-GC(arXiv:2607.00692)的思想------Agent 自己通过工具调用自主决定何时折叠、展开、重新折叠。
Harness 映射 :这正好呼应了我们之前在上下文压缩中提到的"可逆压缩"策略。
7.3 pi-auto-context:锚点与缓存优化
pi-auto-context 实现了基于"锚点"(Anchor)的智能上下文管理。其核心机制包括:
- 锚点锚定:Agent 在任务边界(如开始新任务)设置锚点,工具结果只保留到最近锚点
- 缓存优化 :在 Anthropic API 上,
cache_control标记固定在锚点位置,保持前缀缓存命中 - 状态行:在每轮对话末尾显示上下文使用率、工具结果占比、锚点位置
数据价值 :Anthropic API 允许每个请求最多 4 个缓存断点(cache_control),通过将断点固定在稳定的锚点位置而非滚动的 last_user,可以避免频繁的缓存失效,从而大幅降低每次请求的 Prefill 成本。
Harness 映射 :这是上下文管理 与成本优化交叉的典型案例------通过精细化的上下文控制,在保证质量的前提下降低 Token 消耗。
八、总结:Pi 与 Harness Engineering 的对应关系
回顾全文,Pi 的设计几乎完整覆盖了 Harness Engineering 的各个核心设计要点:
| Harness 设计要点 | Pi 中的对应实现 |
|---|---|
| Loop | agentLoop 双嵌套循环(外循环处理 follow-up,内循环处理工具调用) |
| Tool Registry | 4 个原子工具 + ToolExecutor 三阶段流水线 |
| 可观测性 | Agent 事件订阅 + 25+ Hooks + pi-auto-context 状态行 |
| 多智能体编排 | 会话分叉(Fork)+ 子 Agent 并行调度(parallel-agent 扩展) |
| HIL | tool_call Hook 可拦截/阻断工具调用 |
| 上下文管理 | 树形 JSONL 持久化 + 三种注入模式 |
| 上下文压缩 | 三种触发方式 + 跨回合切割 + 累积追踪 |
| State Store | Append-only 树形 JSONL,支持分支恢复 |
Pi 的设计哲学------极简主义、工程化优先、深度可扩展------与 Harness Engineering 追求的目标完全一致。它不是功能最多的 Agent 框架,但它在可理解性、可维护性和可扩展性上做到了极致。
对于想要构建生产级 Agent 系统的团队,Pi 不仅是一个可以直接使用的工具,更是一本**"生产级 Agent 架构设计的活教材"**。正如冬瓜的 AI 笔记所说:Pi 不是玩具框架,而是一套真正能上线的 Agent 工程参考实现------同类产品(Claude Code、Cursor、Cline 等)的内部架构都能在 Pi 里找到对应。看懂 Pi,等于看懂一个完整的 Agent SDK 应该怎么设计。