Agent harness · 技术调研报告

pi 这个 agent
到底是什么

一个被"做减法"做到极致的编码 agent —— 也是 OpenClaw 真正的发动机

你正在 OpenClaw 之上搭多 agent 平台。那么有件事必须先讲清楚:OpenClaw 自己不是一个 agent 运行时,它只是 pi 的一层封装。真正跑 agent 循环、管会话、调模型的,是 pi。看懂 pi,等于看懂了你脚下那层地基的脾气。

作者 Mario Zechner / badlogic 仓库 earendil-works/pi(原 badlogic/pi-mono) 语言 TypeScript 许可 开源
报告有效性 · 半衰期标注

把这份报告当成"基线快照",而不是永久真相

内容截止:2026 年 5 月。agent 工具领域是目前变化最快的技术赛道之一,下面这些是本轮调研期内真实发生的变动,用来说明"为什么时效性重要",而不是抽象地警告你:

层级内容类型半衰期建议复查
快变具体模型名 / 价格 / star 排名 / 包版本号 / 第三方扩展清单1–3 个月每月扫一次
中变四层包的 API 细节(事件名、方法签名、扩展 hook 点)3–6 个月每季度核对
慢变分层架构 / 会话 JSONL 模型 / SDK 嵌入方式 / 工作流6–12 个月半年回看
稳定极简主义设计哲学 / "trust through execution" / 判断框架结构性稳定基本不变
§ 01

pi 是什么:来历与定位

pi 是一个用 TypeScript 写的编码 agent 工具包(agent harness)。它的作者是 Mario Zechner(网名 badlogic)——就是写了 libGDX 那套游戏框架的人。

故事的起点很"工程师式怨气":他用 Claude Code 用得越来越烦,觉得这类工具像"一艘 80% 功能根本用不上的宇宙飞船"。于是他反向而行,做了一个只有 4 个工具、系统提示词不到 1000 token 的 agent。这个 agent 叫 pi。

然后剧情反转:pi 成了 OpenClaw 的底层发动机,而 OpenClaw 在一周内冲到 14 万+ star,把 pi 一起带上了热搜。所以你现在选 OpenClaw 当运行时,本质上就是在用 pi。

关键澄清
OpenClaw、MoltBot、各种 Slack/Telegram bot 都不是独立的 agent 引擎,它们是 pi 四个核心包的应用层封装。pi 负责 agent 循环、会话、模型调用;OpenClaw 负责多渠道路由、记忆、用户管理。这条边界对你后面"基于它做什么"至关重要。

定位上,pi 同时是三样东西:一个能直接用的 CLI 编码助手、一套能嵌进自己产品的 SDK、以及一个可被 agent 自我改写的开放系统(你可以让 pi 自己给自己写扩展)。

§ 02

设计哲学:把"做减法"做成信仰

理解 pi,先理解它的"反框架"立场。文中那句被反复引用的话最能概括:

其他每个框架都是在"加法"——在已有 agent 上叠流程、记忆、协调层。pi 正相反,它是一次刻意的"减法"。 —— 对 pi 设计原则的概括(Interesting Engineering 技术评述)

核心论点:给模型加的每个功能,都是模型不再能自由推理的东西

Mario 的逻辑链是这样的:所有前沿模型都被 RL 训练得"懂什么是编码 agent"了。模型天然知道 bash 是什么、文件怎么读写。所以——

每烤进一个专用工具,就等于替模型预先决定了"该怎么做这件事",反而限制了它。所以 pi 只给模型四样东西:

read

读文件内容和图片(jpg/png/gif/webp)。文本截断到 2000 行或 50KB,支持 offset/limit 翻页。

write

写文件,不存在则创建,存在则覆盖,自动建父目录。

edit

精确替换文件里的文本,oldText 必须逐字(含空白)匹配。做外科手术式的小改。

bash

在工作目录执行 shell 命令,返回 stdout/stderr。这是"无限能力"的入口——其余一切靠它。

几个被刻意"砍掉"的东西

trust through execution
还有一个产品取向叫 "YOLO mode"——默认不做权限确认弹窗,直接执行,靠执行本身建立信任、换取生产力。这一点你做多用户平台时要反过来处理(见 §7 的工作区隔离)。

另外有一个常被低估的工程亮点:pi 的 TUI 用了类似 React diff 算法的差分渲染(differential rendering),所以终端里 Markdown 和语法高亮完全不闪烁,而且"回到之前任意一轮"的能力是同类里最强的。

§ 03

系统层级架构:四层栈

pi 是一个 npm workspaces 的 monorepo(锁步版本),核心是四个层层叠加的包。每一层只加一点能力,你用多少拿多少。

你的应用层 OpenClaw · CLI 工具 · Slack/Telegram bot · 你的多 agent 平台 pi-coding-agent 完整 runtime:内置工具 会话持久化 · 压缩 · 扩展 · 技能 pi-tui 终端 UI · 差分渲染 Markdown · 编辑器 · 不闪烁 pi-agent-core agent 循环 · 工具执行 · 事件流 · 状态管理(你想自定义就下到这层) pi-ai 统一多 provider LLM 接口 · 2000+ 模型目录 · 流式 · 工具定义 · 成本追踪 Anthropic · OpenAI · Google · Bedrock · Groq · xAI · OpenRouter · Ollama · DeepSeek · 任何 OpenAI 兼容端点
四层栈:每层只加一点能力,向下依赖。应用层(含 OpenClaw 与你的平台)坐在最上面。

pi-ai —— 最底层:跟任何模型说话

一个统一接口调所有 LLM:Anthropic、OpenAI、Google、Bedrock、Mistral、Groq、xAI、OpenRouter、Ollama……每家流式格式都不同,pi-ai 把它们归一化成同一套事件text_deltathinking_deltatoolcall_*doneerror)。你只写一次流处理逻辑,换 provider 只改一行 getModel(...)

底层用各家官方 SDK,靠 api 字段决定走哪个 SDK——这就是为什么任何 OpenAI 兼容端点(Ollama、vLLM、DeepSeek)都能直接接进来。还内置 2000+ 模型目录和成本追踪。

对你尤其关键
你之前的"Claude Code 身体 + DeepSeek 大脑"想法,在 pi 这一层是原生支持的:直接 models.json 里加一个 provider 指向 DeepSeek 的兼容端点即可,连官方 Claude Agent SDK 都不一定需要。pi-ai 本身就是那个"多 provider 路由层"。

pi-agent-core —— agent 循环

把 pi-ai 包成一个 Agent 类,跑标准 agent loop:发消息给模型 → 执行模型要调的工具 → 把结果喂回去 → 重复,直到模型停。你不用自己写循环。工具用 TypeBox schema 定义(执行前用 AJV 校验),每个工具有 name / label / description / parameters / execute

它向外发一整套事件:agent_start / turn_start / message_update / tool_execution_start / tool_execution_end / agent_end 等——这套事件流是你后面做"LiveStream 执行可视化"的现成数据源

pi-coding-agent —— 生产级 runtime(多数人从这层开始)

在 core 之上加了:7 个内置工具(默认 4 个 + grep/find/ls 三个可选)、JSONL 会话持久化、自动上下文压缩(compaction)、技能(skills)、扩展系统,以及 AuthStorage(多 provider 凭证 + OAuth)和 ModelRegistry(自定义 provider/模型)。一句 createAgentSession() 把这些全接好。

pi-tui —— 终端 UI 库

差分渲染的 TUI:语法高亮的 Markdown、带 slash 命令和文件路径自动补全的多行编辑器、loading 动画、零闪烁。如果你做的是 Web 而非终端界面,这一层你会换成 pi-web-ui(见 §5)。

§ 04

核心机制深挖

这一节是真正决定"你能在上面盖什么楼"的几个机制。每一个都直接对应你平台里的某个需求。

① 会话 = 一棵 JSONL 树(不是一条线)

会话存成 JSONL 文件,每条记录有 idparentId——这是一棵,不是线性历史。于是你能:导航回任意历史点继续(branch(entryId) 实现"从这里重试"),整棵树都能 getTree() 出来渲染分支选择器。

因为 JSONL 是 append-only,崩溃最多丢一行。OpenClaw 就是每个频道线程一个会话文件~/.openclaw/agents/<agentId>/sessions/<sessionId>.jsonl),各会话独立、崩溃安全。

映射到你的平台
"群聊 + 多个数字员工"天然就是多会话结构。pi 的"一线程一 JSONL"正好是你"每个 agent / 每个任务一条独立时间线"的现成底座,而树结构让你能做"任务分叉 / 回滚重做"。

② steering vs follow-up —— 两种"插话"

OpenClaw 用 steering 处理实时用户消息,用 follow-up 做程序化串联。对你的"群聊里 @某个 agent 改需求",这俩就是核心原语。

③ 扩展系统 —— 改"agent 怎么行为",且模型看不见

工具让模型"做事";扩展让你改"agent 怎么运转",而 LLM 完全不知道扩展存在。扩展挂在生命周期事件上:

扩展还能 registerCommand(注册面向用户的命令,不是 LLM 工具)、registerShortcut 等。

④ 工具工厂 + operations 覆写 —— 把 agent 关进沙箱

默认工具操作的是 process.cwd()。多用户产品里这不行——每个 agent 必须锁死在自己的工作区。createReadTool(workspace) 这类工厂能把所有路径限定到某个目录。更狠的是每个工厂可传一个 operations 对象,覆写底层 I/O

OpenClaw 正是用工厂给每个 agent 造工作区限定的工具,再裹一层中间件:权限检查、图片归一化,以及 Claude Code 参数兼容别名file_pathpathold_stringoldText)——所以 Claude Code 的 skills 能直接在 pi 上跑。

⑤ streamFn 中间件 —— 在"对 LLM 说话"这一步插手

session.agent.streamFn 是 agent 每次要调 LLM 时实际调用的函数。默认是 streamSimple,但你能包一层来注入 header、调参数、按 provider 加逻辑。OpenClaw 用它加 OpenRouter 归因 header、给 Anthropic 开 prompt cachingcacheRetention: "long")。

回到你之前研究过的坑
你之前深挖过 Claude Code 的 prompt cache TTL 从 1 小时悄悄回退到 5 分钟。在 pi 里,缓存策略就是 streamFn 里一个可控参数——你能在自己平台里显式管理 TTL 策略,而不是被默认值摆布。

⑥ 压缩(compaction)

长对话超出上下文窗口时,pi 把旧消息摘要、保留近期消息。createAgentSession 默认开自动压缩,接近窗口上限时触发。完整历史永远留在 JSONL 文件里,只有内存里的上下文被压。你也能手动 session.compact("保住所有文件路径和代码改动") 给摘要导向。

⑦ 四种运行模式

interactive

交互式 CLI,配 pi-tui。日常人机协作。

print / JSON

一次性输出,适合脚本和管道。

RPC

进程间集成,把 pi 当子进程驱动。

SDK

嵌进你自己的应用——OpenClaw 走的就是这条。你的平台也应该走这条。

§ 05

更大的包生态

四个核心包之外,monorepo 还有几个把系统往不同方向延伸的包,对你都直接有用:

pi-web-ui

Lit 写的浏览器聊天组件。开箱 ChatPanel:流式、文件附件、在沙箱 iframe 里渲染 HTML/SVG/Markdown 工件。你做群聊 Web 界面的起点。

pi-mom

把消息委派给 pi-coding-agent 的 Slack bot。每频道 agent 隔离、Docker 沙箱、定时事件、自管理的工具安装。多渠道 + 隔离的参考实现。

pi-pods

用 vLLM 在 GPU pod 上部署开源模型的 CLI(DataCrunch / RunPod / Vast.ai / 裸机)。每个模型暴露 OpenAI 兼容端点给 pi-ai 消费。

pi-skills

pi 的技能库,兼容 Claude Code 和 Codex CLI 的 skills。意味着现有 skill 生态你能直接复用。

第三方生态也在长:pi-subagents(给 pi 加 Claude Code 风格的并行子 agent、可中途 steering)、oh-my-pi(coding-first 的分支,能继承 .claude/.cursor/.codex 等已有配置)、以及 mitsuhiko 等人维护的扩展集(/review/todos、CDP 浏览器技能)。

§ 06

你能用 pi 来干什么

分两类:作为成品工具直接用,和作为 SDK 搭东西。

当成品用

当 SDK 搭东西(约 120 行就能起一个持久化编码 agent)

§ 07

基于 pi,你的多 agent 平台该怎么搭

这是对你最有价值的一节。你的"群聊式 + 数字员工自主认领任务 + 真实交付物"平台,几乎每个设计点都能在 pi 的原语上找到落点。下面把你的架构决策逐条映射。

你的平台概念 群聊 · 每个数字员工 三级任务认领机制 LiveStream 执行可视化 三层记忆系统 Claude 身体 + DeepSeek 脑 多用户工作区隔离 pi 对应原语 一 agent 一 session JSONL steer / followUp + before_agent_start 扩展 session.subscribe 事件流 JSONL 树 + context 扩展 + 自定义 compaction pi-ai + models.json tool factory + operations
你的平台概念 → pi 现成原语的映射。左边几乎不用自己造轮子。

逐条对应

  1. 群聊 + 数字员工 → 每个 agent 一个 createAgentSession + 独立 JSONL。群聊本质是"多个 session 共享一个消息总线"。你只需要在上面做一层"消息路由 + UI 聚合"。
  2. 三级认领(@提及 → 调度器路由 → 自我评估) → 第一级用 steer() 直接把 @某 agent 的消息插进它的 session;第二级你自己写一个调度器扩展决定派给谁,用 followUp() 派活;第三级用 before_agent_start 给候选 agent 注入"你是否该接这个任务"的自评提示。
  3. LiveStream 执行可视化 → 这是 pi 几乎白送的:session.subscribe() 把每个 tool_execution_start/endmessage_update 都吐给你,你只要把这些事件渲染到群聊 UI 即可。pi-web-ui 的 ChatPanel 已经做了一半。
  4. 三层记忆系统 → 短期 = session 内消息;中期 = 自定义 compaction(用 session_before_compact 把要长期保留的东西摘要进一个"记忆条目");长期 = 你自己的存储,用 context 扩展在每次调用前把相关长期记忆注入消息数组。JSONL 树还顺手给你"可回溯的情景记忆"。
  5. Claude 身体 + DeepSeek 脑 → pi-ai 原生支持。models.json 加一个指向 DeepSeek 兼容端点的 provider,getModel 选它即可。甚至可以做到"同一个群里不同 agent 用不同模型/不同价位"。
  6. 多用户工作区隔离createReadTool(workspace) 等工厂锁死目录,operations 把 bash 丢进 Docker。这正是 OpenClaw 多用户安全的做法,照抄即可。
一个值得现在就想清楚的战略选择
你的 "Path A" 是"用 OpenClaw 当运行时、不 fork、只在上面盖群聊 UI + 薄云同步层"。看完 pi 你会发现一个更精确的判断:OpenClaw 帮你处理了"多渠道 + 记忆 + 用户管理",但你的核心创新(群聊式多 agent 协作、三级认领)其实活在 pi 的 session / 扩展 / 事件层。所以真正要吃透的不是 OpenClaw 的封装,而是 pi 的 createAgentSession + 扩展 API + 事件流这三样。建议你早点写一个最小 demo:两个 pi session 共享一个消息总线,用 steer 互相喊话——这能在一两天内验证你整个平台的核心假设。

需要自己造、pi 不给的部分

· · ·