【earendil-works/pi】模块化 Agent Harness 架构深度解析:Skill、Compaction 与多 Provider 统一抽象
【earendil-works/pi】模块化 Agent Harness 架构深度解析:Skill、Compaction 与多 Provider 统一抽象
引子
在 Agent 框架遍地开花的今天,earendil-works/pi 是一个独特的存在。它不只是一个 Coding Agent 终端工具,而是一套分层清晰、职责分明的 Agent 运行时架构。截至 2026 年 5 月,pi 在 GitHub 拥有约 49,000+ ⭐,由独立开发者 badlogic 维护,采用全 TypeScript/Monorepo 架构,包含了 5 个正交分层的包。
今天我们就来深度拆解它的核心设计。
项目概览
| 指标 | 值 |
|---|---|
| GitHub | earendil-works/pi |
| 语言 | TypeScript (Monorepo) |
| Stars | ~49,000 ⭐ |
| 包数量 | 5 个(pi-ai, pi-agent-core, pi-coding-agent, pi-tui, pi-web-ui) |
| 最近更新 | 2026-05-14(极度活跃) |
| License | MIT |
核心定位:一套可扩展的 Agent 运行时框架,核心亮点是** Skill 机制**(可组合的 prompt 模板)、Context Compaction(长会话压缩)和多 Provider 统一 LLM API。
整体架构
pi 的架构遵循严格的分层模型,各层职责清晰、依赖单向:
graph TB
subgraph "Layer 5: UI"
UI["pi-tui (Terminal)\npi-web-ui (Web)"]
end
subgraph "Layer 4: Application"
CA["pi-coding-agent\nCLI Application"]
end
subgraph "Layer 3: Agent Runtime"
AC["pi-agent-core\nAgent Loop + Harness + Session"]
end
subgraph "Layer 2: LLM Interface"
AI["pi-ai\nMulti-Provider Unified API"]
end
subgraph "Layer 1: Foundation"
FS["File System / Node.js\nEnvironment Abstraction"]
end
UI --> CA
CA --> AC
AC --> AI
AI --> FS
AC -->|"Tool Execution"| FS
AC -->|"Session Storage"| FS
AI -->|"API Calls"| FS包职责一览
| 包名 | 职责 |
|---|---|
@earendil-works/pi-ai | 统一多 Provider LLM API(OpenAI/Anthropic/Google/Mistral/Bedrock 等) |
@earendil-works/pi-agent-core | Agent 运行时核心:Agent Loop、Tool Calling、Session 管理、Compaction |
@earendil-works/pi-coding-agent | 交互式 Coding Agent CLI,整合所有层 |
@earendil-works/pi-tui | 终端 UI 库(差分渲染) |
@earendil-works/pi-web-ui | Web 组件库(ChatPanel、Artifacts 等) |
核心一:Agent Loop 机制
pi-agent-core 的核心是一个事件驱动的 Agent Loop,定义在 packages/agent/src/agent-loop.ts 中。
循环结构
Agent Loop 采用双层嵌套循环:
sequenceDiagram
participant User as User Message
participant Loop as Outer Loop
participant Inner as Inner Loop
participant LLM as LLM Provider
participant Tool as Tool Executor
participant Emit as Event Emitter
User->>Loop: new prompts + context
Loop->>Emit: agent_start
Loop->>Inner: while(hasToolCalls OR pendingMessages)
Inner->>Emit: turn_start
Inner->>LLM: streamAssistantResponse()
LLM-->>Inner: AssistantMessage
Emit->>Emit: message_start/update/end
alt hasToolCalls
Inner->>Tool: executeToolCalls()
Tool-->>Inner: ToolResultMessage[]
Inner->>Emit: tool_execution_start/end
end
Inner->>Emit: turn_end
alt shouldStopAfterTurn()
Loop->>Emit: agent_end
Loop-->>User: Done
else followUpMessages exist
Loop->>Inner: continue outer loop
else no more messages
Loop->>Emit: agent_end
Loop-->>User: Done
end核心类型定义
1 | // packages/agent/src/types.ts |
消息流向转换
关键设计:AgentMessage[] 在 Agent Loop 内部全程使用,只在 调用 LLM 时才转换为 Provider 兼容的 Message[]:
flow LR
A[AgentMessage<br/>内部格式] -->|convertToLlm| B[Message<br/>LLM Provider 格式]
B --> C[LLM API]
C --> D[AssistantMessageEvent]
D --> E[AssistantMessage<br/>转换回内部格式]
E --> A1 | // Agent Loop 中调用 LLM 的关键转换 |
核心二:Tool Calling 生命周期
pi 的工具调用设计非常精细,分为 Prepare → Execute → Finalize 三阶段:
三阶段执行模型
flowchart TD
A[Tool Call from LLM] --> B{Prepare}
B -->|Block| F[Return Error Result]
B -->|Prepare| C[Execute]
C -->|Success| D{Finalize}
C -->|Exception| E[catch block]
E --> D
D --> G[ToolResultMessage]
F --> G
G --> H{Every tool<br/>terminate=true?}
H -->|Yes| I[Terminate batch]
H -->|No| J[Continue]核心代码
1 | // 1. Prepare 阶段:验证参数,可选择阻止执行 |
顺序 vs 并行执行
1 | // 支持两种工具执行模式 |
- Sequential(顺序):上一个工具完成才执行下一个,适合有依赖的调用
- Parallel(并行):所有工具同时执行,结果按 LLM 输出顺序排序返回
核心三:Skill 机制
pi 最具创新性的设计之一是 Skill——一种自包含的 prompt 模板单元,以 SKILL.md 文件形式存在。
SKILL.md 结构
1 | <!-- SKILL.md --> |
some example code
1 | ``` |
Skill 触发机制
Skill 不需要代码注册。Agent 在推理过程中自然地发现并调用 Skill,Skill 内容被格式化为特殊的 XML 标签嵌入到 prompt 中:
1 | <skill name="add-block" location="/path/to/SKILL.md"> |
这意味着:
- 无需修改代码即可扩展 Agent 行为
- Skill 可以版本控制(存在仓库中)
- Skill 之间可以组合(一个 Skill 调用另一个 Skill)
核心四:Context Compaction(上下文压缩)
长会话的上下文会不断增长,pi 实现了自动上下文压缩机制。
压缩触发条件
当会话长度超过阈值(可通过配置),自动触发压缩:
- 提取对话中的文件操作(read/edit/write)
- 使用 Branch Summary 机制总结分支对话
- 生成压缩摘要消息注入 context
- 删除中间的消息历史
核心代码
1 | // packages/agent/src/harness/compaction/compaction.ts |
Branch Summary(分支摘要)
pi 的 Session 支持树形分支,分支合并不只是简单丢弃,而是生成”分支摘要”:
1 | // 生成分支摘要 |
核心五:多 Provider 统一 AI 层
@earendil-works/pi-ai 提供了统一的多 Provider LLM API,解决了”换 Provider 需要改代码”的问题。
Provider 架构
classDiagram
class ApiProvider {
<<interface>>
api: Api
stream()
streamSimple()
}
class OpenAIProvider {
api: "openai-responses"
stream()
}
class AnthropicProvider {
api: "anthropic-messages"
stream()
}
class GoogleProvider {
api: "google-generative-ai"
stream()
}
class ApiProviderRegistry {
- Map~Api, RegisteredApiProvider~ registry
+ registerApiProvider()
+ getApiProvider()
+ unregisterApiProviders()
}
ApiProviderRegistry o--> ApiProvider
ApiProvider <|-- OpenAIProvider
ApiProvider <|-- AnthropicProvider
ApiProvider <|-- GoogleProviderProvider 注册
1 | // packages/ai/src/providers/register-builtins.ts |
统一调用接口
1 | import { getModel, streamSimple } from "@earendil-works/pi-ai"; |
支持的 Provider
| Provider | API 类型 | 模型示例 |
|---|---|---|
| Anthropic | anthropic-messages | claude-sonnet-4, claude-opus-4 |
| OpenAI | openai-responses | gpt-4o, gpt-4o-mini |
google-generative-ai | gemini-2.5-pro, gemini-2.5-flash | |
| Azure OpenAI | azure-openai-responses | azure-gpt-4o |
| Mistral | mistral | mistral-large, mistral-small |
| Amazon Bedrock | amazon-bedrock | claude-on-bedrock |
| GitHub Copilot | github-copilot | copilot 模型 |
核心六:Session 树形管理
pi 的 Session 不是简单的消息列表,而是一个树形结构,支持分支和合并。
Session Tree Entry 类型
1 | type SessionTreeEntry = |
JSONL 持久化
Session 以 JSONL 格式存储,每行一个 JSON 对象:
1 | {"type":"session","version":3,"id":"abc123","timestamp":"2026-05-15T08:00:00Z","cwd":"/project"} |
1 | // packages/agent/src/harness/session/jsonl-storage.ts |
与同类框架对比
vs LangChain
| 维度 | LangChain | pi |
|---|---|---|
| 语言 | Python + JS | TypeScript |
| 抽象层级 | 较高(Chain/LLM/Agent) | 中等(Loop/Harness/Session) |
| 多 Provider | 通过 LangChain 集成 | 统一 pi-ai API Registry |
| Skill 机制 | Prompt Template | SKILL.md 文件(更自然) |
| Context 管理 | 自行管理 | Session Tree + Compaction |
| 状态持久化 | 多种方式 | JSONL Session 文件 |
vs smolagents(HuggingFace)
| 维度 | smolagents | pi |
|---|---|---|
| 核心 | 单 Agent + Tool | 模块化 Harness + 多包 |
| 多 Provider | 主要 OpenAI/Anthropic | 7+ Provider |
| Skill | 通过装饰器注册 | SKILL.md 文件 |
| Context | 滚动窗口 | Compaction + Branch Summary |
vs CrewAI
| 维度 | CrewAI | pi |
|---|---|---|
| 核心 | Multi-Agent Team | 单 Agent Harness |
| 协作 | Agent 间通过 Task 协作 | Session 内分支合并 |
| 工具注册 | 代码装饰器 | 运行时发现 + SKILL.md |
优缺点分析
✅ 优点
架构简洁性:
- Monorepo 分层清晰,每层职责单一
- Agent Loop 只有 200 行核心逻辑,可读性极高
- Session Tree 设计优雅地解决了分支问题
扩展性:
registerApiProvider机制让添加新 Provider 只需 100 行代码- Skill 机制让非程序员也能扩展 Agent 行为
- Extension 机制允许 Hook 介入 Tool 执行各阶段
易用性:
createAgentSession()一行启动完整 Agent- CLI 开箱即用,无需配置
- 多 Provider 切换只需改模型名
⚠️ 缺点
性能:
- TypeScript 运行时开销,Python 框架无法直接比较
- JSONL Session 在超长会话下可能需要定期压缩
复杂度:
- 5 个包的依赖关系需要一定学习成本
- 完整源码有 675 个 TS 文件,大型项目
维护性:
- 独立开发者维护,商业支持依赖社区
- 路线图不透明(无 public roadmap)
功能:
- 缺乏内置 Multi-Agent 协作(需要自己基于 Session 扩展)
- 没有官方 RAG 集成(需自行对接向量库)
快速上手
安装
1 | # npm |
最简使用
1 | // 01-minimal.ts |
全控制模式
1 | // 12-full-control.ts |
创建自定义 Skill
1 | <!-- SKILL.md --> |
将 SKILL.md 放入 .pi/skills/ 目录,Agent 会自动发现并使用。
总结
earendil-works/pi 是一个架构设计极为优雅的 Agent 框架。它的核心价值在于:
- 分层解耦:5 个包各司其职,替换任意层不影响其他层
- Skill 机制:开创性的 prompt 模板单元化设计
- Compaction:长会话管理的成熟方案
- 多 Provider 统一:一次编写,随处运行
对于需要深度定制 Agent 行为的团队,pi 提供的 Harness 层(pi-agent-core)是一个极佳的基础。相比直接使用 LangChain,从零构建的复杂度更低,扩展点更清晰。
推荐指数:⭐⭐⭐⭐(扣一星因为缺乏 Multi-Agent 原生支持)
本文分析基于 earendil-works/pi 2026-05-14 最新源码。