今日开源[第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.devexe.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):首次启动询问是否信任当前目录;非交互用 defaultProjectTrustask/always/never)或 --approve/--no-approve 覆盖。
  • 环境变量:PI_OFFLINE=1PI_SKIP_VERSION_CHECK=1PI_TELEMETRY=0PI_CODING_AGENT_DIR 等。

2.6 依赖的软件 / 硬件条件

  • Node.js ≥ 22.19.0(根 package.json engines 约束)。
  • 包管理:npm workspaces(根 package-lock.json 为真理源);.npmrcsave-exact=truemin-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() 从现有上下文恢复(最后一条须为 usertoolResult)。低层可用 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() 监听器按注册顺序 awaitagent_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(参数校验后、执行前,可 blockterminate: 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.tsenv-api-keys.ts(从环境变量取 key)、oauth.tscli.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-monorepoversion=0.0.3、MIT、engines.node>=22.19.0;全部脚本(build 固定顺序构建、check 多重门禁、generate:modelsversion:*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=truemin-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.tscompat.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) AgentStateAgentOptionsAgentToolAgentMessage、事件类型等。
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.mdCHANGELOG.mdpackage.jsontsconfig*.jsonvitest.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.mdpackage.json 包文档与元数据。

4.8 packages/serverpackages/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)

  1. 极简核心 + 强扩展哲学:刻意不内置子代理/计划/MCP/权限弹窗/后台 bash,全部交由扩展拼装——核心轻量、可审计、易演化。
  2. 真正的"自我扩展":通过 TypeScript 扩展、Skills(Agent Skills 标准)、Prompt Templates、Themes、Pi 包(npm/git 分发)让用户与代理都能装载能力,甚至能实现子代理、计划模式、压缩逻辑、权限门、MCP 集成乃至小游戏。
  3. 统一多供应商 LLM APIpi-ai 屏蔽 OpenAI/Anthropic/Google/Bedrock 差异,统一流式调用与模型注册表(含生成式模型清单)。
  4. 有状态、事件流的代理运行时pi-agent-core 把 agent loop、工具调用(并行/顺序 + 前后钩子 + 终止语义)、状态管理、事件订阅做成干净的可组合抽象,SDK/RPC/CLI 同源复用。
  5. 差分渲染 TUIpi-tui 只重绘变化区域,终端交互流畅;交互模式含会话树(branching/fork/clone)、消息队列(steering/follow-up)、自动压缩。
  6. 供应商中立遥测pi-telemetry 用契约 + 一致性测试保证可观测性,便于评估与改进编码代理。
  7. 供应链安全实践到位save-exact、锁文件为真理源、pre-commit 阻止误改 lockfile、npm ci --ignore-scripts、shrinkwrap 固定传递依赖、生命周期脚本白名单、定期 npm audit 与 Release 冒烟测试。
  8. 部署灵活:交互/打印/RPC/SDK 四种集成;可装 npm 包或 curl 安装;可从 Release 源码离线构建 Bun 独立二进制;支持容器化/沙箱隔离。

5.2 不足 / 风险(Weaknesses / Risks)

  1. 无内置权限系统:默认以启动用户全权限运行,安全隔离完全依赖用户自行容器化(Gondolin/Docker/OpenShell)——对安全意识弱的用户是隐患。
  2. Monorepo 复杂度与版本早期:根版本仅 0.0.3,仍快速演进;CHANGELOG.md 极大(coding-agent 达 527KB),API 稳定性与文档同步压力大。
  3. Node 版本硬约束:要求 node>=22.19.0,且大量生成代码、Bun 二进制、SQLite 原生后端,环境门槛高于纯前端工具。
  4. 新贡献者 issue/PR 默认自动关闭:虽便于维护者过滤,但社区参与度与透明度受限。
  5. 模型数据强依赖外部目录刷新generate:models 需联网各供应商目录;离线靠快照,可能滞后于最新模型。
  6. 学习曲线:扩展机制、四种模式、配置分散在多个 JSON、RPC 的严格 JSONL 帧(不能用通用 readline)等,对初学者不够"开箱即用"。

5.3 创新点与亮点(Innovation / Highlights)

  1. "自我扩展"作为一等公民:把编码代理从"封闭产品"变成"可装载插件的平台",代理本身能通过 Extension API 演化——这是相对于多数闭源代理的范式差异。
  2. 抽象干净的 agent looptransformContext → convertToLlm 两段式消息流 + 细粒度事件流(message_update 流式增量、tool_execution_* 进度)+ 前后钩子终止语义,是构建可靠代理的优良骨架。
  3. 差分渲染 TUI 库:把终端 UI 的工程复杂度收敛到一个可复用库,兼顾性能与体验。
  4. 供应商中立遥测 + OSS 会话分享:鼓励把真实编码会话(工具使用/失败/修复)分享到 Hugging Face(badlogic/pi-share-hf),用真实数据而非玩具基准改进代理。
  5. 可复现/可审计构建:从 Release 源码包 + 生成模型快照 + 锁文件 + shrinkwrap 构建独立二进制,并把依赖变更当代码审查——把"供应链安全"落到工程细节。
  6. 统一 LLM 抽象 + 多后端会话createModels().setProvider() 动态切换供应商,SessionManager 支持内存/SQLite/远程后端,云边协同(浏览器 streamProxy)天然支持。
posted @ 2026-08-16 14:44  zhang-yd  阅读(169)  评论(0)    收藏  举报