今日开源[第51期]Pi Agent Harness(pi)源码解读
Pi Agent Harness(pi)源码解读
仓库:https://github.com/earendil-works/pi
组织:earendil-works(主要贡献者 vegarsti、badlogicgames、julien-agent 等)
版本:monorepo 根0.0.3| 许可证:MIT
定位:AI agent toolkit —— 统一的 LLM API、agent 循环(agent loop)、终端 UI(TUI)、可自我扩展的编码代理 CLI。
1. 项目名称、作者及其介绍、作用和背景
1.1 名称与作者
- 项目名称:Pi Agent Harness(仓库名
pi)。 - 作者/组织:earendil-works(GitHub 组织)。主要提交者包括
vegarsti(近期维护者)、badlogicgames(即 libGDX 作者 Mario Zechner,负责供应链与 OSS 会话分享)、julien-agent等。项目已累计 5,685+ 次提交。 - 域名:
pi.dev由exe.dev捐赠。文档站pi.dev/docs/latest,演示站pi.dev。 - 许可证:MIT(宽松,允许闭源与商用)。
1.2 作用(What It Does)
Pi 是一个开源的 AI 代理工具包(AI agent toolkit),核心价值是"可自我扩展的编码代理(self extensible coding agent)"。它提供从底层大模型接口到上层交互式命令行与终端 UI 的完整能力栈,让开发者(以及代理本身)能高效完成编码与自动化任务。项目以 npm monorepo(workspaces) 管理多个包,并特别强调供应链安全与可复现构建。
官方定义的三大核心子包:
@earendil-works/pi-coding-agent:交互式编码代理 CLI(终端里的编程助手)。@earendil-works/pi-agent-core:具备工具调用与状态管理的有状态代理运行时。@earendil-works/pi-ai:统一的多供应商 LLM API(OpenAI、Anthropic、Google 等)。
1.3 背景与动机
当前编码代理领域(如 Claude Code、Aider、Cursor 等)各家闭源、能力参差。Pi 的设计哲学是"极简核心 + 强扩展":把子代理、计划模式、MCP、权限弹窗、内置待办、后台 bash 等"重功能"刻意不内置,而是通过扩展(Extensions)、技能(Skills)、提示模板、主题、Pi 包来让用户自行拼装。这样既保持核心轻量、可审计,又把"自我扩展"作为一等公民——代理本身也能装载扩展来演化能力。配套 earendil-works/pi-chat 还把能力延伸到 Slack/聊天自动化。
2. 安装和使用教程、依赖的软件或硬件条件
2.1 从源码开发(Monorepo)
git clone https://github.com/earendil-works/pi.git
cd pi
npm install --ignore-scripts # 安装全部 workspace 依赖,但不执行生命周期脚本(供应链安全)
npm run build # 刷新模型数据 + 构建全部包(按固定顺序 tui→telemetry→ai→agent→...→coding-agent)
npm run build:offline # 用既有模型数据离线重建(无网络)
npm run check # Biome lint/format + 类型检查 + 依赖/锁文件门禁
./test.sh # 运行测试(无 API key 时跳过依赖 LLM 的测试)
./pi-test.sh # 从源码直接运行 pi(可在任意目录调用)
2.2 安装编码代理 CLI(用户视角)
npm install -g --ignore-scripts @earendil-works/pi-coding-agent
# 或
curl -fsSL https://pi.dev/install.sh | sh
认证与启动:
export ANTHROPIC_API_KEY=sk-ant-...
pi # 进入交互模式
# 或 pi /login 通过订阅登录
2.3 CLI 常用调用
pi "List all .ts files in src/" # 交互模式并带初始提示
pi -p "Summarize this codebase" # 非交互打印(print 模式)
cat README.md | pi -p "Summarize" # 管道输入
pi --provider openai --model gpt-4o "Refactor" # 指定供应商/模型
pi --model sonnet:high "Solve problem" # 带思考级别(thinking level)
pi --tools read,grep,find,ls -p "Review" # 只读工具子集
pi @prompt.md "Answer this" # @文件作为提示参数
pi -c # 继续最近会话
pi -r # 浏览历史会话
pi --session <id> / --fork <id> # 指定/分支会话
2.4 四种运行模式
| 模式 | 触发 | 用途 |
|---|---|---|
| 交互模式 | pi |
终端 TUI,完整编辑器与命令系统 |
| 打印 / JSON 模式 | -p / --mode json |
非交互、CI/管道友好 |
| RPC 模式 | --mode rpc |
以子进程协议(严格 LF 分隔 JSONL)集成进其他进程 |
| SDK 模式 | import { createAgentSession } |
将 agent loop 嵌入自有 Node 应用 |
2.5 配置与权限
- 全局:
~/.pi/agent/settings.json;项目覆盖:.pi/settings.json;键位:~/.pi/agent/keybindings.json;模型/供应商:~/.pi/agent/models.json;信任记录:~/.pi/agent/trust.json。 - 系统提示可被项目
.pi/SYSTEM.md覆盖;上下文文件支持AGENTS.md/CLAUDE.md(全局、父目录、当前目录层级)。 - 项目信任(Project Trust):首次启动询问是否信任当前目录;非交互用
defaultProjectTrust(ask/always/never)或--approve/--no-approve覆盖。 - 环境变量:
PI_OFFLINE=1、PI_SKIP_VERSION_CHECK=1、PI_TELEMETRY=0、PI_CODING_AGENT_DIR等。
2.6 依赖的软件 / 硬件条件
- Node.js ≥ 22.19.0(根
package.jsonengines约束)。 - 包管理:npm workspaces(根
package-lock.json为真理源);.npmrc设save-exact=true、min-release-age=2。 - 构建工具:Biome 2(lint/format)、TypeScript 5.9、esbuild 0.28、tsx、Husky(git hooks)、jiti。
- 独立二进制:可从 GitHub Release 源码包离线构建 Bun 可执行文件(
scripts/build-binaries.sh --offline-model-data --platform linux-x64 --out ...)。 - 会话后端:默认 SQLite(
@earendil-works/pi-session-backend-sqlite-node),需 Node 原生 SQLite factory。 - 硬件:普通开发机即可;LLM 调用依赖外部供应商 API(OpenAI/Anthropic/Google 等)的 key 或 OAuth。
2.7 容器化 / 沙箱(权限隔离)
Pi 没有内置权限系统限制文件系统、进程、网络或凭证访问,默认以启动用户权限运行。如需强隔离,官方给出三种模式(见 packages/coding-agent/docs/containerization.md):
- Gondolin 扩展:把
pi与供应商鉴权留在宿主机,仅把内置工具与!命令路由进本地 Linux 微虚拟机。 - Plain Docker:把整个
pi进程跑在本地容器里做简单隔离。 - OpenShell:把整个
pi进程跑在策略受控沙箱中。
3. 项目的工作流程和具体的执行步骤
Pi 的运行时是一条"提示 → 上下文转换 → LLM 流式生成 → 工具执行 → 事件回流 → 循环"的 agent loop。下面逐层拆解。
3.1 消息流(Message Flow)
AgentMessage[] ──transformContext()──▶ AgentMessage[] ──convertToLlm()──▶ Message[] ──▶ LLM
(可选:修剪旧消息/注入上下文) (必需:过滤 UI 专用消息,转 LLM 格式)
AgentMessage是灵活类型,可含标准 LLM 消息(user/assistant/toolResult)以及通过声明合并(declaration merging)自定义的 app 特定消息。convertToLlm在每次 LLM 调用前把自定义类型转换为 LLM 能理解的格式(必需步骤)。
3.2 Agent Loop 与事件流(Event Flow)
Agent 类内部管理循环;prompt() 追加新消息并运行,continue() 从现有上下文恢复(最后一条须为 user 或 toolResult)。低层可用 agentLoop() / agentLoopContinue() 迭代事件(观察性更强,不等待异步事件处理结算)。
一次 prompt() 的事件序列:
agent_start
→ turn_start
→ message_start / message_end (user)
→ message_start / message_end (assistant) + message_update*(流式增量)
[若有工具调用]
→ tool_execution_start / tool_execution_update* / tool_execution_end
→ toolResultMessage
→ (可能) 新 turn_start 让 LLM 回应工具结果
→ turn_end
→ agent_end
subscribe()监听器按注册顺序await;agent_end之后无更多 loop 事件,但waitForIdle()/prompt()需等agent_end的 await 监听完成才结算。- 停止钩子
shouldStopAfterTurn:在turn_end后运行,返回true则发agent_end并退出(不中止 provider 流、不取消工具)。
3.3 工具调用(Tool Calling)
- 工具用
AgentTool定义:name/label/description/parameters(TypeBox schema)/ 可选executionMode/execute函数。 - 执行模式:
parallel(默认,预检顺序、执行并发,按完成序发tool_execution_end,但持久化 toolResult 按源序)、sequential(整批顺序,任一工具设sequential则整批顺序)。 - 钩子:
beforeToolCall(参数校验后、执行前,可block并terminate: true)、afterToolCall(执行后、tool_execution_end前,可覆盖结果或terminate: true)。仅当批内所有结果都终止时才跳过后续 LLM 调用。 - 错误处理:工具失败应
throw Error,被捕获后以isError: true报给 LLM(而非返回错误内容)。
3.4 状态管理(State Management)
AgentState 接口含 systemPrompt / model / thinkingLevel / tools / messages / isStreaming / streamingMessage? / pendingToolCalls / errorMessage?。
- 通过
agent.state读写;赋tools/messages会拷贝顶层数组。 - 流式期间
streamingMessage含部分 assistant 消息,isStreaming至运行完全结算才false。 - 控制方法:
prompt()/continue()/abort()/waitForIdle()/steer()/followUp()/clearSteeringQueue()/reset()。
3.5 编码代理 Harness 的分层
packages/coding-agent/src 组织为:
main.ts(~34KB,入口编排)、cli.ts(CLI 入口)、index.ts(SDK 导出)、config.ts(配置解析)、migrations.ts(会话迁移)、package-manager-cli.ts(包/扩展管理 CLI)、rpc-entry.ts(RPC 模式入口)。- 子目录:
core/(会话运行时核心)、cli/(命令实现)、client/(客户端逻辑)、server/(服务进程)、extensions/(扩展加载与 API)、modes/(交互/打印/RPC 等模式)、bun/(Bun 可执行相关)、utils/。
3.6 统一 LLM API 层(pi-ai)
packages/ai/src 通过 providers/(各供应商实现)、api/(统一接口)、auth/(OAuth/密钥)、compat/(兼容层)、models.ts + models.generated.ts(模型注册表,含生成代码)、image-models.ts(图像模型)、model-catalog.ts、env-api-keys.ts(从环境变量取 key)、oauth.ts、cli.ts(模型 CLI)对外提供 createModels() + models.streamSimple() 等统一流式调用。generate:models 脚本从各供应商实时目录刷新模型数据。
3.7 遥测、协议与会话后端
protocol/:跨进程(RPC/CLI↔server)通信协议定义(含生成代码)。telemetry/:供应商中立的遥测契约、参考适配器、一致性测试与类型化 schema。session-backends/sqlite-node/:把会话历史持久化到 SQLite(JSONL 全量历史 + 压缩摘要)。server/与client/:支撑 RPC 模式与远程/代理场景的服务端/客户端实现。tui/:带差分渲染(differential rendering)的终端 UI 库(含native/原生绑定),为交互模式提供编辑器、状态行、会话树等。
4. 项目的各个文件的内容和作用(按模块)
仓库为 npm workspaces monorepo(根 package.json 声明 workspaces: packages/* 等),根目录共 11 文件 + 4 目录。下面按包梳理。
4.1 根目录配置文件
| 文件/目录 | 作用 |
|---|---|
package.json |
monorepo 根清单:name=pi-monorepo、version=0.0.3、MIT、engines.node>=22.19.0;全部脚本(build 固定顺序构建、check 多重门禁、generate:models、version:*、publish/release:*、shrinkwrap:coding-agent 等);devDependencies(Biome 2.3.5、TypeScript 5.9.3、esbuild 0.28.1、tsx、husky);overrides(protobufjs/rimraf 固定版本)。 |
package-lock.json |
依赖真理源(供应链安全核心)。 |
biome.json |
Biome 2 检查/格式化规则。 |
.npmrc |
save-exact=true、min-release-age=2,避免同日依赖发布。 |
.gitattributes / .gitignore |
Git 属性与忽略。 |
AGENTS.md |
给人类与代理的项目规则(贡献/代理约定)。 |
CONTRIBUTING.md |
贡献指南;新贡献者的 issue/PR 默认自动关闭,维护者每日复核。 |
SECURITY.md |
安全披露政策。 |
README.md / LICENSE |
项目主页与 MIT 许可全文。 |
.github/ |
Issue/PR 模板、workflows(含定期 npm audit 工作流)。 |
.husky/ |
Git hooks(pre-commit 阻止误改 lockfile,除非 PI_ALLOW_LOCKFILE_CHANGE=1)。 |
.pi/ |
项目自身的 Pi 配置/扩展目录(自举)。 |
4.2 packages/ai —— 统一多供应商 LLM API(@earendil-works/pi-ai)
| 路径 | 作用 |
|---|---|
src/index.ts |
包导出入口。 |
src/models.ts(34KB) |
模型注册表与 createModels() 工厂、streamSimple 等统一流式接口。 |
src/models.generated.ts / image-models.generated.ts |
由脚本生成的模型/图像模型清单。 |
src/image-models.ts / images.ts / images-models.ts / images-api-registry.ts |
图像生成 API 与注册表。 |
src/model-catalog.ts / models-store.ts |
模型目录与本地存储。 |
src/types.ts(34KB) |
全部类型定义(消息、工具、流式事件等)。 |
src/providers/ |
各供应商实现(OpenAI/Anthropic/Google/Bedrock 等)。 |
src/api/ |
统一 API 接口层。 |
src/auth/ |
OAuth / 鉴权逻辑。 |
src/compat/、legacy-api-aliases.ts、compat.ts |
向后兼容别名与兼容层。 |
src/env-api-keys.ts(7.5KB) |
从环境变量解析各供应商 API key。 |
src/oauth.ts / bun-oauth.ts |
OAuth 流程(含 Bun 运行时变体)。 |
src/cli.ts |
模型相关 CLI。 |
src/utils/ |
工具函数(含 abort.ts 等)。 |
scripts/ |
generate-models / generate-image-models / hydrate-model-data / check:model-data 等模型数据刷新脚本。 |
README.md(80KB)、CHANGELOG.md |
包文档与变更日志。 |
4.3 packages/agent —— 有状态代理运行时(@earendil-works/pi-agent-core)
| 路径 | 作用 |
|---|---|
src/agent.ts(18.7KB) |
Agent 类:状态管理、prompt()/continue()/subscribe()/abort()/waitForIdle() 等。 |
src/agent-loop.ts(22KB) |
核心 agent loop 实现(agentLoop / agentLoopContinue),事件生成与工具执行调度。 |
src/types.ts(17KB) |
AgentState、AgentOptions、AgentTool、AgentMessage、事件类型等。 |
src/proxy.ts(10.5KB) |
streamProxy:把 LLM 调用代理到远端服务器(浏览器后端场景)。 |
src/stream-fn.ts |
流式函数类型与工具。 |
src/node.ts |
Node 运行时适配。 |
src/harness/ |
运行时代码(harness 辅助模块)。 |
src/search/ |
上下文搜索/检索(用于 transformContext)。 |
src/index.ts |
包导出。 |
scripts/、test/、docs/、README.md(17KB)、CHANGELOG.md |
脚本/测试/文档。 |
4.4 packages/coding-agent —— 交互式编码代理 CLI(@earendil-works/pi-coding-agent)
| 路径 | 作用 |
|---|---|
src/main.ts(34KB) |
入口编排:启动头、消息区、编辑器、页脚、快捷键、会话树、压缩逻辑。 |
src/cli.ts |
CLI 入口(参数解析)。 |
src/index.ts(10KB) |
SDK 导出:createAgentSession / ModelRuntime / SessionManager / createAgentSessionRuntime。 |
src/config.ts(19KB) |
配置解析(~/.pi/agent/*、.pi/*、环境变量)。 |
src/migrations.ts(9KB) |
会话历史版本迁移。 |
src/package-manager-cli.ts(28KB) |
扩展/技能/主题/Pi 包的安装与管理 CLI(含 npm/git 包)。 |
src/rpc-entry.ts |
RPC 模式入口(严格 LF 分隔 JSONL 帧)。 |
src/core/ |
会话运行时核心。 |
src/cli/、src/client/、src/server/ |
命令实现、客户端、服务进程。 |
src/extensions/ |
扩展加载与 ExtensionAPI 实现(注册工具/命令/快捷键/事件/UI)。 |
src/modes/ |
交互 / 打印 / RPC 等运行模式。 |
src/bun/ |
Bun 可执行文件相关产物。 |
src/utils/ |
工具函数。 |
docs/ |
文档(含 containerization.md 三种隔离模式、CLI 参考)。 |
examples/ |
扩展示例(with-deps、custom-provider-anthropic、custom-provider-gitlab-duo、sandbox、gondolin),被 workspaces 收录。 |
install-lock/、npm-shrinkwrap.json(61KB) |
安装锁与 shrinkwrap(由根锁文件生成,固定传递依赖供 npm 用户)。 |
scripts/、test/、README.md(32KB)、CHANGELOG.md(527KB) |
脚本/测试/文档。 |
4.5 packages/protocol —— 跨进程通信协议
| 路径 | 作用 |
|---|---|
src/ |
RPC / CLI↔server 通信协议定义与生成代码(protobuf 风格契约,见 override 中 protobufjs)。 |
README.md、CHANGELOG.md、package.json、tsconfig*.json、vitest.config.ts |
包元数据与配置。 |
4.6 packages/telemetry —— 供应商中立遥测(@earendil-works/pi-telemetry)
| 路径 | 作用 |
|---|---|
src/ |
遥测契约、参考适配器、一致性测试、类型化 schema(供应商中立,便于观测代理行为)。 |
README.md(20KB)、package.json |
包文档与元数据。 |
4.7 packages/tui —— 差分渲染终端 UI 库(@earendil-works/pi-tui)
| 路径 | 作用 |
|---|---|
src/ |
TUI 组件与差分渲染引擎(只重绘变化区域,提升终端性能)。 |
native/ |
原生绑定(终端底层能力)。 |
README.md(29KB)、CHANGELOG.md、package.json |
包文档与元数据。 |
4.8 packages/server 与 packages/client
| 路径 | 作用 |
|---|---|
server/src/ |
支撑 RPC 模式与远程/代理场景的服务端实现(代理 LLM 调用、会话服务)。 |
client/src/ |
对应客户端实现(与 server 通过 protocol 通信)。 |
各自 README.md / package.json / test/ |
包文档、元数据、测试。 |
4.9 packages/session-backends/sqlite-node —— 会话持久化
| 路径 | 作用 |
|---|---|
src/ |
SQLite 会话后端(接受运行时特定 SQLite factory,把会话历史持久化为 JSONL 全量 + 压缩摘要)。被 agent 包作为可选后端引入。 |
4.10 packages/evals —— 评估
| 路径 | 作用 |
|---|---|
src/ |
编码代理评估套件,经根 npm run eval --workspace=@earendil-works/pi-evals -- 运行,用真实任务衡量工具使用/失败/修复。 |
4.11 根级脚本与构建
scripts/build-binaries.sh:从 Release 源码构建 Bun 独立可执行文件(--offline-model-data/--platform/--skip-install --skip-deps)。scripts/check-pinned-deps.mjs/check-ts-relative-imports.mjs/generate-coding-agent-shrinkwrap.mjs/generate-coding-agent-install-lock.mjs/publish-model-catalog.mjs/diff-model-catalog.mjs/local-release.mjs/release.mjs/publish.mjs/sync-versions.js/check-browser-smoke.mjs:构建、门禁、发布、模型目录校验等。test.sh/pi-test.sh/pi-test.ps1/pi-test.bat:测试与源码运行包装(跨平台)。
5. 项目的优势、不足、创新点和亮点
5.1 优势(Strengths)
- 极简核心 + 强扩展哲学:刻意不内置子代理/计划/MCP/权限弹窗/后台 bash,全部交由扩展拼装——核心轻量、可审计、易演化。
- 真正的"自我扩展":通过 TypeScript 扩展、Skills(Agent Skills 标准)、Prompt Templates、Themes、Pi 包(npm/git 分发)让用户与代理都能装载能力,甚至能实现子代理、计划模式、压缩逻辑、权限门、MCP 集成乃至小游戏。
- 统一多供应商 LLM API:
pi-ai屏蔽 OpenAI/Anthropic/Google/Bedrock 差异,统一流式调用与模型注册表(含生成式模型清单)。 - 有状态、事件流的代理运行时:
pi-agent-core把 agent loop、工具调用(并行/顺序 + 前后钩子 + 终止语义)、状态管理、事件订阅做成干净的可组合抽象,SDK/RPC/CLI 同源复用。 - 差分渲染 TUI:
pi-tui只重绘变化区域,终端交互流畅;交互模式含会话树(branching/fork/clone)、消息队列(steering/follow-up)、自动压缩。 - 供应商中立遥测:
pi-telemetry用契约 + 一致性测试保证可观测性,便于评估与改进编码代理。 - 供应链安全实践到位:
save-exact、锁文件为真理源、pre-commit 阻止误改 lockfile、npm ci --ignore-scripts、shrinkwrap 固定传递依赖、生命周期脚本白名单、定期npm audit与 Release 冒烟测试。 - 部署灵活:交互/打印/RPC/SDK 四种集成;可装 npm 包或 curl 安装;可从 Release 源码离线构建 Bun 独立二进制;支持容器化/沙箱隔离。
5.2 不足 / 风险(Weaknesses / Risks)
- 无内置权限系统:默认以启动用户全权限运行,安全隔离完全依赖用户自行容器化(Gondolin/Docker/OpenShell)——对安全意识弱的用户是隐患。
- Monorepo 复杂度与版本早期:根版本仅
0.0.3,仍快速演进;CHANGELOG.md极大(coding-agent 达 527KB),API 稳定性与文档同步压力大。 - Node 版本硬约束:要求
node>=22.19.0,且大量生成代码、Bun 二进制、SQLite 原生后端,环境门槛高于纯前端工具。 - 新贡献者 issue/PR 默认自动关闭:虽便于维护者过滤,但社区参与度与透明度受限。
- 模型数据强依赖外部目录刷新:
generate:models需联网各供应商目录;离线靠快照,可能滞后于最新模型。 - 学习曲线:扩展机制、四种模式、配置分散在多个 JSON、RPC 的严格 JSONL 帧(不能用通用 readline)等,对初学者不够"开箱即用"。
5.3 创新点与亮点(Innovation / Highlights)
- "自我扩展"作为一等公民:把编码代理从"封闭产品"变成"可装载插件的平台",代理本身能通过 Extension API 演化——这是相对于多数闭源代理的范式差异。
- 抽象干净的 agent loop:
transformContext → convertToLlm两段式消息流 + 细粒度事件流(message_update流式增量、tool_execution_*进度)+ 前后钩子终止语义,是构建可靠代理的优良骨架。 - 差分渲染 TUI 库:把终端 UI 的工程复杂度收敛到一个可复用库,兼顾性能与体验。
- 供应商中立遥测 + OSS 会话分享:鼓励把真实编码会话(工具使用/失败/修复)分享到 Hugging Face(
badlogic/pi-share-hf),用真实数据而非玩具基准改进代理。 - 可复现/可审计构建:从 Release 源码包 + 生成模型快照 + 锁文件 + shrinkwrap 构建独立二进制,并把依赖变更当代码审查——把"供应链安全"落到工程细节。
- 统一 LLM 抽象 + 多后端会话:
createModels().setProvider()动态切换供应商,SessionManager支持内存/SQLite/远程后端,云边协同(浏览器streamProxy)天然支持。

浙公网安备 33010602011771号