Pi Mono 底层架构解析:从设计到实战
Pi Mono 是最近火爆的 Openclaw 的底层框架模块。
Pi Mono 是 @mariozechner 开源的 AI Agent 工具包。
以 Monorepo 形式将统一 LLM API、智能体运行时、终端 UI、Web UI 等七个包打包在一起,让你用一套代码调用 20+ 个大模型提供商,构建真正可用的 AI 助手。
1. 项目概览与架构
Pi Mono 采用分层 Monorepo 架构,7 个包按依赖关系清晰分层:
| 包名 | 功能 | 类比 |
|---|
pi-ai | 统一调用 20+ LLM 提供商 | 万能遥控器,控制所有品牌电视 |
pi-agent-core | 智能体运行时,状态机 + 工具调用 | 机器人的'大脑' |
pi-tui | 终端 UI 组件库 | 在终端里绘制漂亮界面的画笔 |
pi-coding-agent | 交互式编程助手(类似 Claude Code) | 终端 AI 编程搭档 |
pi-web-ui | Web 聊天界面组件 | 浏览器里的 AI 对话框 |
pi-mom | Slack 机器人集成 | 住在 Slack 里的 AI 助手 |
pi-pods | GPU 服务器/Pod 管理 | 云端 AI 模型的'部署管家' |
架构图

整体分为四层,自上而下:应用层(4 个业务包)→ 核心运行时层(pi-agent-core)→ 统一 LLM API 层(pi-ai)→ LLM 提供商层(20+)。pi-tui 作为独立的终端 UI 框架,由 pi-coding-agent 按需引用。
包依赖关系
pi-ai(基础,无内部依赖) ↓ pi-agent-core(依赖 pi-ai) ↓ pi-coding-agent ── 依赖 pi-agent-core + pi-tui + pi-ai pi-mom ── 依赖 pi-agent-core + pi-ai pi-web-ui ── 依赖 pi-agent-core + pi-ai pi-pods ── 独立(无内部依赖) pi-tui ── 独立(无内部依赖)
2. 安装与快速上手
安装信息来源:github.com/badlogic/pi-mono
2.1 系统要求
- Node.js >= 20
- npm >= 9(Workspaces 支持)
2.2 克隆并安装
git clone https://github.com/badlogic/pi-mono
cd pi-mono
npm install
npm run build
2.3 开发常用命令
npm run check
./test.sh
./pi-test.sh
npm run dev
2.4 单包测试
cd packages/ai
npx vitest --run
2.5 验证安装
node -v
node packages/coding-agent/dist/cli/index.js --help
node packages/ai/dist/cli/index.js list
node packages/ai/dist/cli/index.js list --provider anthropic
3. 核心模块:pi-ai 统一 LLM API
3.1 为什么需要 pi-ai?
直接调用各厂商 SDK 的最大痛点是格式不统一:
const res = await client.messages.create({...});
const text = res.content[0].text;
const res2 = await openai.chat.completions.create({...});
const text2 = res2.choices[0].message.content;
切换提供商需要改大量代码。pi-ai 的解决方案是提供统一的 completeSimple / streamSimple 接口,配合 getModel 选择提供商:
import { completeSimple, getModel } from "@mariozechner/pi-ai";
const model = getModel("minimax-cn", "MiniMax-M2.5");
const context = {
systemPrompt: "你是一个友好的 AI 助手。",
messages: [
{
role: "user" as const,
content: "Hello",
timestamp: Date.now(),
},
],
};
const reply = await completeSimple(model, context, {
apiKey: process.env.MINIMAX_CN_API_KEY!,
});
3.2 内部架构与数据流

请求从 stream() 统一入口进入,经 model-resolver 根据模型名路由到对应 Provider 实现,翻译成各厂商格式后发出 HTTP 请求,响应再解析回统一的 StreamResult(包含 messages、tokens、cost)返回给调用方。
packages/ai/src/ 目录结构:
packages/ai/src/
├── types.ts
├── stream.ts
├── tokens.ts
├── models.generated.ts
├── model-resolver.ts
└── providers/
├── anthropic.ts
├── openai-completions.ts
├── google.ts
├── aws-bedrock.ts
└── ...
3.3 核心类型
Context(对话上下文):
type Context = {
systemPrompt?: string;
messages: Message[];
tools?: ToolDef[];
};
type UserMessage = {
role: "user";
content: string;
timestamp: number;
};
type AssistantMessage = {
role: "assistant";
content: (TextContent | ToolCall)[];
usage: {
input: number;
output: number;
cost: { total: number };
};
};
两个核心函数:
completeSimple(model, context, { apiKey }): Promise<AssistantMessage>
streamSimple(model, context, { apiKey }): AsyncIterable<StreamEvent>
StreamEvent 事件类型:
type StreamEvent =
| { type: "start" }
| { type: "text_delta"; delta: string }
| { type: "text_end" }
| { type: "thinking_delta"; delta: string }
| { type: "done"; message: AssistantMessage; reason: string }
| { type: "error"; error: { errorMessage: string } };
4. 完整示例:从简单问答到工具调用
运行方法

示例 1:简单问答
对应 examples/01-pi-ai/01-simple-chat.ts
import { completeSimple, getModel } from "@mariozechner/pi-ai";
const model = getModel("minimax-cn", "MiniMax-M2.5");
const apiKey = process.env.MINIMAX_CN_API_KEY!;
const context = {
systemPrompt: "你是一个友好的 AI 助手,回答简洁清晰。",
messages: [
{
role: "user" as const,
content: "用一句话解释什么是递归?",
timestamp: Date.now(),
},
],
};
const reply = await completeSimple(model, context, { apiKey });
const text = reply.content
.filter((c) => c.type === "text")
.map((c) => (c as { type: "text"; text: string }).text)
.join("");
console.log("AI 回复:", text);
console.log(`输入 tokens: ${reply.usage.input}`);
console.log(`输出 tokens: `);
.();
示例 2:流式输出(打字机效果)
对应 examples/01-pi-ai/02-streaming.ts
import { streamSimple, getModel } from "@mariozechner/pi-ai";
const model = getModel("minimax-cn", "MiniMax-M2.5");
const apiKey = process.env.MINIMAX_CN_API_KEY!;
const context = {
systemPrompt: "你是一个会写故事的 AI,回答要有创意。",
messages: [
{
role: "user" as const,
content: "写一个关于程序员和 AI 的三句话短故事。",
timestamp: Date.now(),
},
],
};
console.log("AI 正在输出(流式):\n");
const eventStream = streamSimple(model, context, { apiKey });
for await (const event of eventStream) {
switch (event.type) {
case "text_delta":
process.stdout.write(event.delta);
break;
case "thinking_delta":
process.stdout.write(`[思考] ${event.delta}`);
break;
case "done":
console.();
.();
.();
;
:
.(, event..);
;
}
}
示例 3:多轮对话
对应 examples/01-pi-ai/04-multi-turn.ts
import * as readline from "node:readline";
import { streamSimple, getModel } from "@mariozechner/pi-ai";
import type { Context, UserMessage, AssistantMessage } from "@mariozechner/pi-ai";
const model = getModel("minimax-cn", "MiniMax-M2.5");
const apiKey = process.env.MINIMAX_CN_API_KEY!;
const context: Context = {
systemPrompt: "你是一个友好的 AI 助手。记住对话历史,保持连贯回答。",
messages: [],
};
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const ask = (p: string) => new Promise<string>((resolve) => rl.question(p, resolve));
console.log('多轮对话(输入 exit 退出)\n');
while (true) {
const userInput = await ask("你: ");
(userInput.() === ) {
rl.();
;
}
: = {
: ,
: userInput,
: .(),
};
context..(userMsg);
process..();
( event (model, context, { apiKey })) {
(event. === ) process..(event.);
(event. === ) {
process..();
context..(event. );
}
(event. === ) .(, event..);
}
.();
}
示例 4:工具调用(Tool Calling)
工具调用是构建 AI Agent 的关键能力。整个过程分两轮完成:

对应 examples/01-pi-ai/03-tool-calling.ts
import { completeSimple, getModel, Type } from "@mariozechner/pi-ai";
import type { Context, ToolResultMessage, AssistantMessage, ToolCall } from "@mariozechner/pi-ai";
const model = getModel("minimax-cn", "MiniMax-M2.5");
const apiKey = process.env.MINIMAX_CN_API_KEY!;
const tools = [
{
name: "get_weather",
description: "获取指定城市的当前天气",
parameters: Type.Object({
city: Type.String({ description: "城市名称,如 '北京'" }),
}),
},
{
name: "calculate",
description: "执行数学计算",
parameters: Type.Object({
expression: Type.String({ description: "数学表达式,如 '2 + 3 * 4'" }),
}),
},
];
function executeToolCall(tc: ToolCall): string {
if (tc. === ) {
{ city } = tc. { : };
.({ city, : , : , : });
}
(tc. === ) {
{ expression } = tc. { : };
result = ()();
.({ result, expression });
}
.({ : });
}
: = {
: ,
: [
{
: ,
: ,
: .(),
},
],
tools,
};
turn = ;
() {
.();
: = (model, context, { apiKey });
context..(reply);
toolCalls = reply..((c): c is => c. === );
(toolCalls. === ) {
text = reply..( c. === ).( c.).();
.(, text);
;
}
( tc toolCalls) {
.(, tc.);
result = (tc);
.();
: = {
: ,
: tc.,
: tc.,
: [{ : , : result }],
: ,
: .(),
};
context..(toolResult);
}
}
5. 多提供商无缝切换
只需换模型名,代码不变:
const question = "用一句话解释递归";
const baseMessages = [
{
role: "user" as const,
content: [{ type: "text" as const, text: question }],
},
];
const r1 = await stream({
model: "claude-haiku-4-5-20251001",
messages: baseMessages,
apiKey: process.env.ANTHROPIC_API_KEY!,
maxTokens: 100,
});
const r2 = await stream({
model: "gpt-4o-mini",
messages: baseMessages,
apiKey: process.env.OPENAI_API_KEY!,
maxTokens: 100,
});
const r3 = await stream({
model: "gemini-2.0-flash",
messages: baseMessages,
apiKey: process.env.GOOGLE_API_KEY!,
maxTokens: 100,
});
console.log("Claude:", getLastText(r1.));
.(, (r2.));
.(, (r3.));
支持的提供商
| 提供商 | 示例模型 | 环境变量 |
|---|
| Anthropic | claude-opus-4-6, claude-haiku-4-5-20251001 | ANTHROPIC_API_KEY |
| OpenAI | gpt-4o, gpt-4o-mini | OPENAI_API_KEY |
| Google | gemini-2.0-flash | GOOGLE_API_KEY |
| AWS Bedrock | anthropic.claude-3-5-sonnet | AWS 配置 |
| Mistral | mistral-large-latest | MISTRAL_API_KEY |
| Groq | llama-3.3-70b-versatile | GROQ_API_KEY |
| OpenRouter | openrouter/... | OPENROUTER_API_KEY |
| MiniMax | MiniMax-Text-01, MiniMax-M1 | MINIMAX_API_KEY |
| 自定义兼容 | 任意 OpenAI 兼容接口 | 自定义 baseURL |
MiniMax 配置
MiniMax 提供 OpenAI 兼容接口,在 pi-mono 中有两种接入方式:
方式一:pi-ai 直接调用(代码中使用)
import { stream } from "@mariozechner/pi-ai";
const result = await stream({
model: "MiniMax-Text-01",
messages: [
{
role: "user",
content: [{ type: "text", text: "你好" }],
},
],
apiKey: process.env.MINIMAX_API_KEY!,
baseUrl: "https://api.minimax.chat/v1",
maxTokens: 1000,
});
切换到推理模型 MiniMax-M1:
const result = await stream({
model: "MiniMax-M1",
messages: [...],
apiKey: process.env.MINIMAX_API_KEY!,
baseUrl: "https://api.minimax.chat/v1",
thinkingTokenBudget: 5000,
maxTokens: 8000,
});
方式二:models.json 注册(供 pi-coding-agent 使用)
编辑 ~/.pi/agent/models.json,将 MiniMax 注册为自定义提供商:
{
"providers": {
"minimax": {
"baseUrl": "https://api.minimax.chat/v1",
"apiKey": "MINIMAX_API_KEY",
"api": "openai-completions",
"models": [
{
"id": "MiniMax-Text-01",
"name": "MiniMax Text 01",
"input": ["text"],
"contextWindow": 1000000,
"maxTokens": 4096,
"cost": { "input": 0.7, "output": 2.1, "cacheRead"
配置完成后,在 pi-coding-agent 中直接使用 /model MiniMax-Text-01 切换模型。
环境变量:配置中 "apiKey": "MINIMAX_API_KEY" 是环境变量名,pi 会自动从环境中读取其值。也可写成字面量字符串,但不推荐直接硬编码密钥。
6. 高级特性
6.1 Token 计数(不调用 LLM)
import { countTokens } from "@mariozechner/pi-ai";
const tokenCount = await countTokens({
model: "claude-opus-4-6",
messages: [...],
apiKey: "...",
});
console.log(`预计 Token 数:${tokenCount}`);
6.2 思考模式(Extended Thinking)
让 Claude 在回复前进行深度推理:
const result = await stream({
model: "claude-opus-4-6",
messages: [...],
apiKey: process.env.ANTHROPIC_API_KEY!,
thinkingTokenBudget: 5000,
maxTokens: 8000,
});
console.log(`思考 Token: ${result.thinkingTokens}`);
6.3 取消请求
const controller = new AbortController();
setTimeout(() => controller.abort(), 3000);
try {
const result = await stream({
model: "claude-opus-4-6",
messages: [...],
apiKey: "...",
signal: controller.signal,
});
} catch (err) {
if (err.name === "AbortError") {
console.log("请求已取消");
}
}
7. 开发规范与最佳实践
Pi Mono 的 AGENTS.md 是所有贡献者的'宪法',核心规范:
- 禁止使用
any 类型 —— 除非绝对必要
- 禁止内联 import —— 所有 import 必须在文件顶部
- 升级依赖而不是降级代码 —— 遇到 API 变化,升级包,不要绕过
- 快捷键必须可配置 —— 不允许硬编码快捷键
- 修改后必须运行
npm run check
- 提交前只提交修改的文件(不使用
git add .)
技术栈
语言:TypeScript 5.9.2
运行时:Node.js 20+
模块系统:ES Modules
包管理器:npm Workspaces
构建工具:tsgo
代码检查:Biome 2.3.5(格式化 + Lint)
测试框架:Vitest 3.2.4
Git Hooks: Husky 9.1.7
总结
Pi Mono 的核心价值在于抽象的彻底性:
- pi-ai 消除了 LLM 提供商的 API 差异,一套代码走天下
- pi-agent-core 将工具调用的复杂状态机封装好,专注业务逻辑
- Monorepo 设计让七个包保持一致的代码规范和构建流程
无论你是想用 Claude 写代码助手、用 Slack 构建工作流机器人,还是部署 GPU 上的模型服务,Pi Mono 都提供了生产就绪的基础设施。