跳转至

pi-mono — 模块化 AI Agent 构建平台(OpenClaw 执行引擎核心)

📊 Level ⭐⭐⭐⭐⭐ | 18.7KB

项目信息

维度 数值
作者 Mario Zechner(badlogicgames)
仓库 https://github.com/badlogic/pi-mono
Stars 43.3k
Forks 5.1k
Commits 3,900
Tags 263
语言 TypeScript(npm workspace monorepo)
许可 MIT
官网 https://pi.dev
最新提交 8 小时前(docs(coding-agent): update subscription provider notes)
活跃度 极高,日常更新

架构概述

与 OpenClaw 的分工

  • pi-mono 负责:Agent 执行引擎(Agent Loop / 会话持久化 / 基础工具 / 模型抽象 / 系统提示构建)
  • OpenClaw 独立实现:多渠道消息网关(Telegram/Discord/WhatsApp)、认证与安全(API Key 轮换、工具策略过滤)、向量记忆系统、上下文压缩、多 Agent 路由、流式处理

核心设计理念

  1. 分层重用:高层包依赖低层包,但低层包可独立使用
  2. 类型安全:重度使用 TypeScript,确保包间接口严格约束
  3. 终端优先:强调命令行体验和编码工作流的深度集成

核心包架构

包名 描述 关键能力
@mariozechner/pi-ai 统一多提供商 LLM API 20+ 提供商抽象、流式响应、工具调用(TypeBox schema 验证)、Thinking/Reasoning、跨提供商会话切换、Context 序列化
@mariozechner/pi-agent-core Agent 运行时 + 工具调用 有状态 Agent、事件流(agent_start→turn→message→tool_execution→agent_end)、transformContext/convertToLlm 双阶段转换、beforeToolCall/afterToolCall Hook
@mariozechner/pi-coding-agent 交互式编码 Agent CLI pi 命令行工具,交互式编码环境,终端直接写代码/执行命令
@mariozechner/pi-tui 终端 UI 库 差分渲染终端界面
@mariozechner/pi-web-ui Web 聊天界面组件 AI 聊天界面 Web 组件
@mariozechner/pi-mom(已移除) Slack 机器人 原 Slack 渠道(已移入独立项目 earendil-works/pi-chat)
@mariozechner/pi-pods(已移除) GPU pod 管理器 vLLM GPU 管理器(已移除)

依赖关系

pi-ai(模型抽象层)
├── pi-agent-core(Agent 运行时)
├── pi-coding-agent(编码 CLI)
├── pi-tui(终端 UI)
├── pi-web-ui(Web UI)

pi-ai 详解(核心模型抽象层)

支持的提供商(20+)

OpenAI、Azure OpenAI、OpenAI Codex、DeepSeek、Anthropic、Google、Vertex AI、Mistral、Groq、Cerebras、Cloudflare AI Gateway、Cloudflare Workers AI、xAI、OpenRouter、Vercel AI Gateway、MiniMax、GitHub Copilot、Amazon Bedrock、OpenCode Zen、OpenCode Go、Fireworks、Kimi For Coding,以及任意 OpenAI 兼容 API(Ollama/vLLM/LM Studio 等)。

事件流架构(注册表 + 事件流)

与 LangChain 的"抽象基类+工厂模式"不同,pi-ai 使用事件流模式,通过 stream() 函数返回异步迭代器,发出标准化事件类型: | 事件类型 | 触发时机 | |----------|---------| | start | 流开始 | | text_start/delta/end | 文本块生命周期 | | thinking_start/delta/end | Thinking 块生命周期 | | toolcall_start/delta/end | 工具调用生命周期(含流式部分 JSON 解析) | | done | 完成,含 stopReason(stop/length/toolUse/error/aborted) | | error | 错误,含 reason 和 partial content |

工具系统

  • TypeBox schema 定义工具参数(编译时类型安全 + 运行时验证)
  • validateToolCall() 在工具执行前验证参数,失败自动返回错误让模型重试
  • 流式工具调用支持:toolcall_delta 事件中参数部分解析,可用于实时 UI 更新
  • 工具结果支持文本和图片两种 content block

跨提供商会话切换

支持在同一会话中无缝切换不同 LLM 提供商。来自不同提供商的 assistant message 自动转换:thinking 块转为 <thinking> 的文本标签,工具调用和文本保留不变。

其他关键特性

  • Context 序列化:Context 对象可直接 JSON.stringify,支持持久化/传输
  • OAuth 支持:Anthropic、OpenAI Codex、GitHub Copilot 需要 OAuth 认证,提供 cli login + programmatic OAuth- 浏览器兼容:支持浏览器环境,API key 需显式传入
  • 自定义模型:支持定义本地推理端点(Ollama/vLLM/LiteLLM)的 Model 对象
  • Faux 测试提供器:内存中的模拟提供器,用于确定性测试

pi-agent-core 详解(Agent 运行时)

Agent 事件循环

Agent.prompt() 的完整事件序列:

promt("Hello")
├─ agent_start
├─ turn_start
├─ message_start/end  (userMessage)
├─ message_start      (assistantMessage)
│  ├─ message_update   (流式文本块)
│  └─ message_end      (完整响应)
│
├─ 如果有工具调用:
│  ├─ tool_execution_start (toolCallId, name, args)
│  ├─ tool_execution_update (流式部分结果)
│  ├─ tool_execution_end   (toolCallId, result)
│  ├─ message_start/end  (toolResultMessage)
│  └─ turn_end
│
├─ 下一轮 (LLM 回应工具结果)
├─ turn_end
└─ agent_end

核心机制

机制 描述
transformContext() 在消息发送给 LLM 前转换/修剪(用于上下文压缩、注入外部上下文)
convertToLlm() 从 AgentMessage 转换为 LLM Message(过滤 UI 消息、转换自定义类型)
beforeToolCall Hook 工具执行前权限检查,可 block 阻止执行
afterToolCall Hook 工具执行后处理,可 terminate 提前结束循环
工具执行模式 parallel(默认,并发执行)或 sequential(逐一执行),per-tool 可覆盖
steeringMode Agent 干预模式:one-at-a-time 或 all
followUpMode 后续轮次模式

与同类框架的对比

维度 pi-mono LangChain
架构模式 注册表 + 事件流 抽象基类 + 工厂模式
类型安全 编译时类型检查(TypeScript) 运行时类型检查(Python/JS 动态)
提供商支持 内置 20+,统一接口 社区驱动,生态丰富但质量参差
事件处理 标准化事件流(异步迭代器) 回调机制(较松散)
工具参数验证 TypeBox schema(编译+运行时) 一般仅运行时
流式工具调用 支持部分 JSON 实时解析 部分支持

与 Hermes Agent 的关系

pi-mono 提供了 Agent 底层的 LLM 访问抽象、工具执行引擎和 Session 管理。Hermes Agent 的架构思路与 pi-mono 有一定重叠:

  • 都强调模块化、分层设计
  • 都有 Session/会话管理
  • 但 Hermes 更侧重 Skill 系统和自我进化,pi-mono 更侧重 LLM 层面的抽象和开发者工具链

深度分析

1. 事件流架构的工程价值

pi-mono 选择注册表 + 异步迭代器事件流而非 LangChain 的抽象基类模式,体现了鲜明的工程立场。 异步迭代器天然适合流式输出场景,事件类型标准化(start/text_delta/toolcall_end/done)让上层 UI 和中间件可以统一订阅,无需理解提供商细节。pi-ai 的 stream/complete/streamSimple/completeSimple 四接口设计覆盖了从流式到一次性、从简单到复杂的所有调用模式 。

2. TypeBox Schema 的双重安全机制

工具参数验证同时在编译时(TypeScript 类型)和运行时(TypeBox 执行)进行,这是 pi-mono 区别于大多数 Agent 框架的关键选择 。编译时类型安全让开发者 IDE 内直接获益,运行时验证则保障了动态输入(如用户输入、API 回调)的安全性。pi-mono 的 validateToolCall() 在工具执行前拦截无效参数,失败后自动让模型重试,形成了一个健壮的错误恢复循环 。

3. 分层解耦策略

pi-mono 的分层设计有明确规则:高层包依赖低层包,但低层包可独立使用 。这意味着 pi-ai(模型抽象层)可以完全不依赖 pi-agent-core 独立存在,开发者只需 LLM 访问能力时无需引入整个 Agent 运行时。这种设计在需要嵌入轻量 LLM 调用能力的场景中极具价值。

4. 跨提供商会话切换的现实意义

20+ 提供商的抽象层不仅是为了便携性,更核心的是跨模型能力对比:同一会话中可以切换不同提供商进行对话 。对于需要在多模型之间选择最优解的开发者,这意味着可以实时 A/B 测试不同模型的工具调用效果,而无需维护多套会话状态。

5. OpenClaw 分工边界的设计哲学

pi-mono 专注 Agent 执行引擎,OpenClaw 负责网关层,二者边界清晰 。这种分离使得 pi-mono 可以独立演进,不被渠道集成(Telegram/Discord/WhatsApp)或安全策略(API Key 轮换)等问题绑架。OpenClaw 的多渠道消息网关与 pi-mono 的 Agent 运行时通过标准接口解耦,各自独立迭代。

实践启示

1. 构建 Agent 系统时优先考虑事件流而非回调

当你需要搭建自定义 Agent 框架时,采用异步迭代器 + 标准事件类型的模式(参考 pi-ai 的 start/done/error 事件系),可以让流式输出、工具调用、错误处理共享同一套订阅机制,代码更简洁也更易于扩展。

2. 工具参数验证必须双层保险

仅依赖 LLM 的参数校验是不够的。参考 pi-mono 的 TypeBox + validateToolCall() 模式,在工具执行前增加运行时 schema 验证,可以有效拦截注入攻击或模型幻觉导致的无效调用,避免生产环境事故。

3. 优先选择可独立使用的低层包

如果你的项目只需要 LLM 访问能力,不要引入整个 Agent 运行时。先评估 pi-ai 这类独立包,只有当确实需要会话管理、工具调用编排时才引入 pi-agent-core。这种按需引入的思路可以显著减小依赖体积。

4. 利用 Hook 机制实现权限和安全控制

pi-agent-core 的 beforeToolCall 和 afterToolCall Hook 是实现权限控制(如只读模式禁止文件写入)、审计日志、成本拦截的绝佳位置 。在任何多 Agent 协作场景中,这些 Hook 都是插入策略控制的首选注入点。

5. 考虑基于 stream 的 UI 响应而非轮询

pi-mono 的流式事件架构天然支持实时 UI 更新(如终端差分渲染工具调用进度)。如果你在构建 Agent 前端,应该利用 toolcall_delta 事件实现部分 JSON 的实时解析和 UI 更新,而不是等待完整响应后一次性渲染。

运行时拆解:少做功能、更可控的 Harness(若飞源码级拆解,SUPP 2026-09-09)

若飞对 Pi 仓库(最新提交 f4d802c3)的源码级拆解揭示了其"产品工作流与 Agent 基础运行分离"的设计哲学,与 Claude Code 的"成品化工作台"方向几乎相反。

  • Monorepo 分层:pi-ai(模型提供方接口)→ pi-agent-core(消息/工具/事件)→ pi-coding-agent(编码场景)→ pi-tui(终端界面),上层不污染底层契约,各包可独立使用。
  • Loop 内外两层:agent-loop.ts 内层围绕当前 Turn(请求→流式→执行工具→放回),外层处理 followUp 队列;steer(改方向)、followUp(做完继续)、nextRun(下次启动再处理)三个队列分离;停止条件明确(无工具调用自然结束/报错中止/工具或 Hook 请求终止/followUp 为空);Ctrl+C 时已完成工具结果先写历史再统一中止。
  • Session 是"发生过什么"的记录:JSONL Entry(id/seq/parentId/时间戳 + 模型切换/思考等级/活动工具/压缩摘要/分支摘要/扩展数据)+ Operation(运行过程:起止/尝试次数/工具启动/队列/延迟写入/用量);buildSessionContext 从当前分支投影工作集——/tree 回旧节点、/fork 不是复制文本,而是同一份历史上换路径。
  • 压缩切点规则:预留 16384 token 给摘要,保留最近约 20000 token;toolResult 不能作切点(工具调用/结果会被拆开);落在任务中间时识别本轮起点对前缀单独摘要;压缩新增 compaction Entry 而非删旧;分支摘要找旧叶与目标最近公共祖先。
  • 安全边界:默认使用启动进程权限、无内置沙箱;beforeToolCall 只做确认/白名单/审计,不是进程隔离——不可信环境应交给 Gondolin/Docker/OpenShell。
  • 契约未全实现:prompt/compact/navigateTree/resume/队列操作及部分 hooks/events 仍返回 HarnessNotImplemented——先定"谁保存、谁执行、谁能中断、失败后从哪恢复"的架构契约,再逐步填实现。

相关

相关实体

若飞第 2 次拆解:主路径与边界(2026-10-11,/48 SUPP)

若飞重读 Pi 仓库(main 分支 commit 96abe9a,2026-10-11,包版本 1.1.0),从"四个默认工具"转向主路径最短化 + 边界接口视角。与第 2 来源(9-08 runtime 拆解)互补,本次增量框架全库零覆盖:

  • 分层与主循环:pi-ai(抹平模型供应商差异)/ pi-agent-core(Agent 循环,不知 Git/Markdown/MCP/TUI)/ pi-coding-agent(文件工具/会话/压缩/扩展/TUI);主路径 = 用户消息→模型响应→工具调用→工具结果→再问模型→结束。同回复多工具调用默认并行、声明串行则按序,写回按原调用顺序(并发完成顺序不改变模型意图);截断回复的残缺 JSON 参数标失败交还模型而非碰文件。
  • steering vs follow-up 双插话语义:任务中途纠偏(steering message,一轮工具执行结束后插入)vs 收尾追加(follow-up message,任务自然停止后开新轮);核心循环只留 prepareRequest()/prepareNextTurn()/finishTurn() 三个位置清楚的接口,压缩/模型切换/扩展从接口接入。
  • 工具集合是会话状态:8 内置工具(read/bash/powershell/edit/write/grep/find/ls),日常默认 4+只读集合;declareToolChanges() 把工具增删作为会话记录写下,恢复会话沿记录还原当时工具状态——"工具不是启动时塞进系统提示词的静态文本,是任务状态的一部分"。
  • MCP 四种暴露方式(零覆盖):direct(直接声明)/ deferred(搜索后加载)/ codemode(默认,脚本内调用) / hidden(接运行时不对模型开放)——"接入 MCP ≠ 把整个工具箱摊在上下文里";MCP 文本结果 >20KB 截中间留头尾、完整结果另存临时文件,Codemode 拿完整返回值在脚本里筛聚只交回几行。
  • 会话树 + projection(零覆盖):append-only JSONL 树,每条记录 id+parentId,分支=新叶子;buildSessionProjection() 沿当前叶子回溯、识别最近一次 compaction、把摘要+保留消息+压缩后新消息拼成模型上下文——"会话负责留下事实,projection 负责服务当下";纠正不用改原记录而是追加 context edit;压缩摘要保存读过/改过的文件列表(旧工具输出可不带但不失忆);溢出自动 compact 后重试一次,再失败明确结束。系统提示词分 section 记 patch 从当前状态组装(最终仍发完整上下文,压缩的是模型眼前的接口不是宿主能力)。
  • Codemode 边界澄清(零覆盖):模型生成 JS 在 QuickJS VM 完成多次工具调用+筛选汇总,嵌套调用只存名称/参数/状态/耗时/错误等有上限元数据;VM 无 Node API/fetch/process/require/WASM,过界走 JSON 序列化,有限时/内存/中断——但 QuickJS 解决的是编排与上下文污染,不是安全沙箱:脚本调 bash 时真正执行的是宿主侧工具,权限取决于 Pi 进程;Pi 默认无逐工具确认/无目录级权限系统,project trust 只管"陌生仓库能否加载自己的扩展/Skill/配置",且 AGENTS.md/CLAUDE.md 默认不受 trust 控制(trust 是加载策略不是安全边界);隔离交给 Plain Docker/Docker Sandboxes/OpenShell/Gondolin 由使用者选择。
  • 极简重定义:极简不是代码少/提示词短/工具少,而是核心路径替上层做了多少决定——主循环只守模型/工具/结果循环,durable 运行时放 experimental 不压进主链路;"仓库长大了,主循环依然能顺着读下来"。

→ 原文存档(若飞第 2 次拆解)