跳到主要内容
Pi Mono 底层架构解析与统一 AI Agent 实战指南 | 极客日志
TypeScript Node.js AI 算法
Pi Mono 底层架构解析与统一 AI Agent 实战指南 综述由AI生成 介绍 Pi Mono 架构,一个基于 Openclaw 的底层 AI Agent 框架。通过分层 Monorepo 设计,整合了统一 LLM API、智能体运行时及终端/Web UI 等模块。核心模块 pi-ai 屏蔽了不同厂商 API 差异,支持 20+ 模型无缝切换。文章涵盖安装步骤、核心类型定义、简单问答、流式输出、多轮对话及工具调用示例,并提供了 MiniMax 等国内模型的接入方案。适合希望构建生产级 AI 助手的开发者参考。
锁机制 发布于 2026/3/27 更新于 2026/5/26 25 浏览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-uiWeb 聊天界面组件 浏览器里的 AI 对话框 pi-momSlack 机器人集成 住在 Slack 里的 AI 助手 pi-podsGPU 服务器/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 ── 依赖 -agent-core + -ai -pods ── 独立(无内部依赖) -tui ── 独立(无内部依赖)
pi
pi
pi
pi
2. 安装与快速上手
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?
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/
├── 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 核心类型 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 >
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: ${reply.usage.output} ` );
console .log (`费用:$${reply.usage.cost.total.toFixed(6 )} ` );
示例 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 .log ("\n\n--- 完成 ---" );
console .log (`停止原因:${event.reason} ` );
console .log (`tokens: ${event.message.usage.input} in / ${event.message.usage.output} out` );
break ;
case "error" :
console .error ("\nAPI 错误:" , event.error .errorMessage );
break ;
}
}
示例 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 ("你: " );
if (userInput.toLowerCase () === "exit" ) {
rl.close ();
break ;
}
const userMsg : UserMessage = {
role : "user" ,
content : userInput,
timestamp : Date .now (),
};
context.messages .push (userMsg);
process.stdout .write ("AI: " );
for await (const event of streamSimple (model, context, { apiKey })) {
if (event.type === "text_delta" ) process.stdout .write (event.delta );
if (event.type === "done" ) {
process.stdout .write ("\n" );
context.messages .push (event.message as AssistantMessage );
}
if (event.type === "error" ) console .error ("\n错误:" , event.error .errorMessage );
}
console .log (` [历史:${context.messages.length} 条]\n` );
}
示例 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.name === "get_weather" ) {
const { city } = tc.arguments as { city : string };
return JSON .stringify ({ city, temperature : 22 , unit : "celsius" , condition : "晴天" });
}
if (tc.name === "calculate" ) {
const { expression } = tc.arguments as { expression : string };
const result = Function (`"use strict"; return (${expression} )` )();
return JSON .stringify ({ result, expression });
}
return JSON .stringify ({ error : "未知工具" });
}
const context : Context = {
systemPrompt : "你是一个智能助手,可以查询天气和做数学计算。" ,
messages : [
{
role : "user" ,
content : "北京今天天气怎样?顺便算一下 (15 + 27) * 3" ,
timestamp : Date .now (),
},
],
tools,
};
let turn = 1 ;
while (true ) {
console .log (`\n--- 第 ${turn++} 轮 LLM 调用 ---` );
const reply : AssistantMessage = await completeSimple (model, context, { apiKey });
context.messages .push (reply);
const toolCalls = reply.content .filter ((c): c is ToolCall => c.type === "toolCall" );
if (toolCalls.length === 0 ) {
const text = reply.content .filter ((c ) => c.type === "text" ).map ((c : any ) => c.text ).join ("" );
console .log ("\nAI 最终回答:" , text);
break ;
}
for (const tc of toolCalls) {
console .log (` 调用:${tc.name} ` , tc.arguments );
const result = executeToolCall (tc);
console .log (` 结果:${result} ` );
const toolResult : ToolResultMessage = {
role : "toolResult" ,
toolCallId : tc.id ,
toolName : tc.name ,
content : [{ type : "text" , text : result }],
isError : false ,
timestamp : Date .now (),
};
context.messages .push (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.messages ));
console .log ("GPT-4o:" , getLastText (r2.messages ));
console .log ("Gemini:" , getLastText (r3.messages ));
支持的提供商 提供商 示例模型 环境变量 Anthropic claude-opus-4-6, claude-haiku-4-5-20251001ANTHROPIC_API_KEYOpenAI gpt-4o, gpt-4o-miniOPENAI_API_KEYGoogle gemini-2.0-flashGOOGLE_API_KEYAWS Bedrock anthropic.claude-3-5-sonnetAWS 配置 Mistral mistral-large-latestMISTRAL_API_KEYGroq llama-3.3-70b-versatileGROQ_API_KEYOpenRouter openrouter/...OPENROUTER_API_KEYMiniMax MiniMax-Text-01, MiniMax-M1MINIMAX_API_KEY自定义兼容 任意 OpenAI 兼容接口 自定义 baseURL
MiniMax 配置 MiniMax 提供 OpenAI 兼容接口,在 pi-mono 中有两种接入方式:
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 ,
});
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" : 0 , "cacheWrite" : 0 }
} ,
{
"id" : "MiniMax-M1" ,
"name" : "MiniMax M1 (Reasoning)" ,
"reasoning" : true ,
"input" : [ "text" ] ,
"contextWindow" : 1000000 ,
"maxTokens" : 40960 ,
"cost" : { "input" : 1.1 , "output" : 5.5 , "cacheRead" : 0 , "cacheWrite" : 0 }
}
]
}
}
}
配置完成后,在 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) 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-ai 消除了 LLM 提供商的 API 差异,一套代码走天下
pi-agent-core 将工具调用的复杂状态机封装好,专注业务逻辑
Monorepo 设计 让七个包保持一致的代码规范和构建流程
无论你是想用 Claude 写代码助手、用 Slack 构建工作流机器人,还是部署 GPU 上的模型服务,Pi Mono 都提供了生产就绪的基础设施。
相关免费在线工具 加密/解密文本 使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
RSA密钥对生成器 生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
Mermaid 预览与可视化编辑 基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
随机西班牙地址生成器 随机生成西班牙地址(支持马德里、加泰罗尼亚、安达卢西亚、瓦伦西亚筛选),支持数量快捷选择、显示全部与下载。 在线工具,随机西班牙地址生成器在线工具,online
Gemini 图片去水印 基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online
Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online