乐于分享
好东西不私藏

跟我学 AI Agent : 第 2 课 — Agent 开发环境与 pi-mono 框架全景

跟我学 AI Agent : 第 2 课 — Agent 开发环境与 pi-mono 框架全景

模块: 模块一: Agent 基础认知 | 难度: ⭐ | 预计时长: 2 小时原书参考: Appendix C + 框架搭建pi-mono API: Agent, AgentTool, getModel, stream, complete, transformContext, subscribe


1. 开场引言

本节以介绍 pi-mono 为主,编程的内容多些,不需要关注细节,从中感受 Agent 如何与 LLM 进行沟通即可,其中的代码相对简单,也很好读。

上一课我们从概念上理解了 Agent 的四级进化。但概念只是概念 —— 我们需要一件趁手的兵器。

AI Agent 框架从 ChatGPT 时刻以后就开始有大师公司在研究,业界比较有名的包括 LangChain/LangGraph(Python)、Google ADK(Python)、CrewAI(Python)等等。

前天在群上有个朋友问关于向量数据库的问题,问 pg 和 milvus 该学哪一个?我的观点是,在 AI 时代,不要再学习某个具体的产品或框架,而是要学习这个产品背后要想到解决的问题。 AI Agent 领域也是如此。

前段时间在研究 OpenClaw 时,我接触到 pi-mono。被它的设计理念所吸引,本次课程将只使用这一套 TypeScript API,贯穿全部 Agent 设计模式。

为什么是它?一句话:它把 Agent 的本质 —— 状态、工具、事件流 —— 用最少的抽象表达出来了。 没有黑魔法,没有过度封装,你能看清每一步发生了什么。

💡 核心问题: pi-mono 的架构是什么样的?核心 API 怎么用?如何用它构建一个完整的 Agent?


2. 核心概念

2.1 pi-mono 全景架构

pi-mono 是一个 TypeScript monorepo,由 Mario Zechner(badlogic)开发,GitHub 38k star。

pi-mono/├── packages/ai/          ← @mariozechner/pi-ai│   统一 LLM API,15+ provider│   token counting, cost tracking├── packages/agent/        ← @mariozechner/pi-agent-core│   有状态 Agent + 工具执行 + 事件流│   (本课程核心)├── packages/coding-agent/ ← 编程 Agent CLI│   TUI / VS Code 扩展 / Skills│   (L17 高级实战)├── packages/tui/          ← 终端 UI 组件├── packages/web-ui/       ← Web UI 组件├── packages/mom/          ← Agent 管理工具└── packages/pods/         ← vLLM 部署工具

课程中我们主要用两个包:

  • • pi-ai: 底层 LLM 调用(对应原书的 “哪个模型” 问题)
  • • pi-agent-core: Agent 编排(对应原书的 “怎么编排” 问题)

2.2 三层 API 设计

pi-mono 有三个抽象层次,由低到高:

层次
API
适用场景
底层
stream() / complete()
直接调 LLM,不需要 Agent
中层
agentLoop()
自定义 Agent 循环
高层
Agent 类
生产级 Agent,自带状态管理和事件流

课程建议: 90% 场景用 Agent 类就够了。底层 API 只在 L01 对比时用。

2.3 核心概念: AgentState

每个 Agent 维护一个状态对象:

interfaceAgentState {systemPromptstring;      // 系统提示词modelModel<any>;          // LLM 模型实例thinkingLevelThinkingLevel// 推理深度toolsAgentTool<any>[];    // 可用工具列表messagesAgentMessage[];   // 对话历史readonlyisStreamingboolean;readonlystreamingMessage?: AgentMessage;}

理解 AgentState 就理解了 Agent 的一切 —— 它就是「记忆 + 人设 + 能力」的容器。

2.4 核心概念: 消息流

Agent 内部的消息处理管线:

AgentMessage[] → transformContext() → AgentMessage[] → convertToLlm() → Message[] → LLM                   (可选: 剪枝/压缩)                  (必须: 过滤自定义类型)
  • • transformContext(): 你可以在这里剪掉老消息、注入外部知识、压缩上下文
  • • convertToLlm(): 把自定义消息类型转成 LLM 能理解的格式

这个处理流程会涉及到课程后面的 L10-Memory-ManagementL11-RAG-and-MCPL14-Guardrails-and-Safety 等基础设施。此处仅需要理解这个过程即可。

2.5 核心概念: 事件流

每次 prompt() 调用会触发一系列事件:

prompt("你好")├─ agent_start├─ turn_start├─ message_start  { userMessage }├─ message_end    { userMessage }├─ message_start  { assistantMessage }├─ message_update { text_delta: "你" }├─ message_update { text_delta: "好" }├─ message_update { text_delta: "!" }├─ message_end    { assistantMessage }├─ turn_end└─ agent_end

如果有工具调用,事件流会更丰富:

prompt("读 config.json")├─ agent_start, turn_start├─ message_start/end { userMessage }├─ message_start → update → end { assistantMessage (含 toolCall) }├─ tool_execution_start { toolName: "read_file" }├─ tool_execution_end { result }├─ message_start/end { toolResultMessage }├─ turn_end├─ turn_start  ← LLM 根据工具结果继续回复├─ message_start → update → end { assistantMessage }├─ turn_end└─ agent_end

这个事件流模型是 pi-mono 最大的教学优势 —— 你可以精确观察 Agent 的每一步决策。


3. 架构图解

pi-mono 分层架构

应用代码层

应用代码层职责:最顶层,封装你的核心业务逻辑。你只需关注如何设计 Agent 的意图,而无需关心底层的实现细节。

核心 Agent 层

Agent 类 -> 由 @mariozechner/pi-agent-core 提供职责:Agent 的“大脑”。它负责维护 Agent 的状态(如 initialState、记忆),编排工具(Tool Orchestration)的调用顺序,并提供系统级的事件流(subscribe),让你能观察到 Agent 的每一个步骤。

循环与推理层

agentLoop() / stream/complete()) -> 由 @mariozechner/pi-agent-core 和 @mariozechner/pi-ai 共同支持职责:实现 Agent 的核心运行逻辑。

  • • agentLoop():实现 Agent 的多步循环,接收 LLM 的决策并执行(如:推理 -> 决策 -> 调用工具 -> 感知反馈 -> 再次推理)。
  • • stream()/complete():负责具体的 LLM 调用,提供同步 (complete) 和流式 (stream) 两种底层模式。

模型接口层

getModel() – 统一接口 -> 由 @mariozechner/pi-ai 提供职责:化繁为简的核心。 它提供了一个单一、统一的 API,让你能用相同的方法调用 15+ 个 不同的模型提供商。无论你想换哪个模型,对上层代码来说都是无感的。

模型服务层

Provider职责:最底层的真实能力来源。这里包括了业界主流的大模型服务,如图中的 OpenAI、Anthropic、Google、DeepSeek 等。pi-mono 负责对它们进行集成和适配。

Agent 工具执行流程

LLM 的推理能力只是 Agent 的“大脑”,而工具才是它的“手脚”。

Agent 真正的核心力量体现在 “工具调用循环”中:模型在接收目标后,会自主决策是否需要使用工具,并能感知外部世界的反馈

它不是简单地执行工具调用,而是将反馈结果(Tool Result)重新压入对话历史(messages),带着新信息再次进行推理,从而实现多步骤、可反思、可修正的自主行动,直到目标达成。

没有这个循环,AI 就只能是一个“足不出户的书呆子”;有了它,它才是能真正解决复杂问题的“自主助手”。


4. 代码实战

4.1 环境搭建

# 需要 Node.js 18+node -v  # v18.x 或更高# 需要 Node.js 18+node -v  # v18.x 或更高# 克隆课程代码仓库git clone https://github.com/OmniTexts/learning-ai-agent.gitcd learning-ai-agent/code# 安装依赖npm install

设置 API Key(以 Minimax CN 为例):

export MINIMAX_CN_API_KEY="sk-..."

4.2 最小可运行: Hello Agent

// hello-agent.tsimport { Agent } from"@mariozechner/pi-agent-core";import { getModel } from"@mariozechner/pi-ai";const agent = newAgent({initialState: {systemPrompt"你是一个友好的助手。用中文回复。",modelgetModel("minimax-cn""MiniMax-M2.7"),  },});// 流式输出agent.subscribe((event) => {if (    event.type === "message_update" &&    event.assistantMessageEvent.type === "text_delta"  ) {    process.stdout.write(event.assistantMessageEvent.delta);  }});await agent.prompt("你好!介绍一下你自己。");

运行:

npx tsx hello-agent.ts

4.3 事件流观察器

完整的 Agent 运行就像一出舞台剧 —— 每个角色都有上台和下台的时刻。下面这段代码让你看到完整的「剧本」:

// event-watcher.tsimport { Agent } from"@mariozechner/pi-agent-core";import { getModel } from"@mariozechner/pi-ai";import { Type } from"@sinclair/typebox";const timeTool = {name"get_current_time",description"获取当前时间",parametersType.Object({timezoneType.String({ description"时区,如 Asia/Shanghai" }),  }),executeasync (_idstringparams: { timezonestring }) => {const time = newDate().toLocaleString("zh-CN", {timeZone: params.timezone,    });return {content: [{ type"text"asconsttext: time }],    };  },};const agent = newAgent({initialState: {systemPrompt"你是一个助手。用中文回复。",modelgetModel("minimax-cn""MiniMax-M2.7"),tools: [timeTool],  },});// 事件观察器agent.subscribe((event) => {const indent = "  ";switch (event.type) {case"agent_start":console.log("🎬 Agent 开始");break;case"turn_start":console.log(`${indent}🔄 Turn 开始`);break;case"message_start":console.log(`${indent}${indent}📝 消息开始 (${event.message.role})`);break;case"message_update":if (event.assistantMessageEvent.type === "text_delta") {        process.stdout.write(event.assistantMessageEvent.delta);      }break;case"message_end":console.log(`\n${indent}${indent}✅ 消息结束 (${event.message.role})`);break;case"tool_execution_start":console.log(`${indent}${indent}🔧 工具调用: ${event.toolName}(${JSON.stringify(event.args)})`      );break;case"tool_execution_end":console.log(`${indent}${indent}📦 工具返回: ${JSON.stringify(event.result?.content?.[0]?.text)}`      );break;case"turn_end":console.log(`${indent}🔄 Turn 结束`);break;case"agent_end":console.log("🎬 Agent 结束");break;  }});await agent.prompt("现在北京时间几点了?");

运行结果:

🎬 Agent 开始  🔄 Turn 开始    📝 消息开始 (user)    ✅ 消息结束 (user)    📝 消息开始 (assistant)    🔧 工具调用: get_current_time({"timezone":"Asia/Shanghai"})    📦 工具返回: "2026/4/21 13:05:32"    ✅ 消息结束 (assistant)  🔄 Turn 结束  🔄 Turn 开始    📝 消息开始 (assistant)现在北京时间是 2026年4月21日 13:05。    ✅ 消息结束 (assistant)  🔄 Turn 结束🎬 Agent 结束

注意:Agent 触发了两个 Turn —— 第一个 Turn 调工具,第二个 Turn 根据工具结果生成自然语言回复。

4.4 完整工具定义: 带进度流和错误处理

// full-tool-demo.tsimport { Agent } from"@mariozechner/pi-agent-core";import { getModel } from"@mariozechner/pi-ai";import { Type } from"@sinclair/typebox";import * as fs from"fs";const readFileTool = {name"read_file",description"读取文件内容",parametersType.Object({pathType.String({ description"文件路径" }),encodingType.Optional(Type.String({ description"编码,默认 utf-8" })),  }),// 强制顺序执行(文件操作不适合并行)executionMode"sequential"asconst,executeasync (_idstring,params: { pathstringencoding?: string },_signalAbortSignal,onUpdate?: (updateany) =>void  ) => {// 流式进度    onUpdate?.({content: [{ type"text"asconsttext`正在读取 ${params.path}...` }],details: {},    });if (!fs.existsSync(params.path)) {// 抛错!不要返回错误文本thrownewError(`文件不存在: ${params.path}`);    }const content = fs.readFileSync(params.path, params.encoding || "utf-8");return {content: [{ type"text"asconsttext: content.slice(05000) }],details: {path: params.path,size: content.length,truncated: content.length > 5000,      },    };  },};const agent = newAgent({initialState: {systemPrompt"你是一个文件分析助手。读取文件并总结其内容。",modelgetModel("minimax-cn""MiniMax-M2.7"),tools: [readFileTool],  },// 全局并行执行(但 readFileTool 自身是 sequential)toolExecution"parallel",// 工具调用前检查beforeToolCallasync ({ toolCall, args }) => {if (args.path?.includes("..")) {return { blocktruereason"不允许路径遍历" };    }  },// 工具调用后处理afterToolCallasync ({ toolCall, result, isError }) => {if (!isError) {console.log(`[审计] ${toolCall.name} 调用成功`);    }  },});agent.subscribe((event) => {if (    event.type === "message_update" &&    event.assistantMessageEvent.type === "text_delta"  ) {    process.stdout.write(event.assistantMessageEvent.delta);  }});await agent.prompt("读取 package.json 并总结这个项目的信息。");

关键设计点:

  • • executionMode: "sequential" — 文件操作不宜并行
  • • throw new Error() — 工具错误要抛异常,不要返回错误文本
  • • beforeToolCall — 安全检查(阻止路径遍历)
  • • afterToolCall — 审计日志
  • • onUpdate — 流式进度反馈

4.5 与三大框架的对照

概念
LangChain/LangGraph
Google ADK
CrewAI
pi-mono
Agent 定义
Runnable/Graph
Agent 类
Agent 类
Agent 类
工具 Schema
JSON Schema
函数装饰器
装饰器
TypeBox
状态管理
StateGraph
Session
Context
AgentState
事件流
Callbacks
Events
Events
subscribe()
上下文剪枝
自定义
自定义
自定义
transformContext()
并行工具
需编排
需编排
需编排
内置 parallel
推理控制
thinkingLevel
运行时转向
Steering
语言
Python
Python
Python
TypeScript

pi-mono 的独特优势:

  1. 1. 一套 API 不分裂 — 不需要在三个框架间切换
  2. 2. 事件流完整可观测 — 每个模式都能看到完整事件链
  3. 3. TypeScript 类型安全 — 工具参数在编译期检查
  4. 4. Steering + Follow-up — 运行时干预能力,原书三个框架都没有

5. 要点总结

#
Takeaway
说明
1
pi-ai 是底层 getModel()

 + stream()/complete() 统一 15+ Provider
2
Agent 是核心 new Agent()

 创建有状态 Agent,prompt() 触发执行
3
AgentTool 定义能力
name + description + TypeBox schema + execute()
4
事件流是教学利器
subscribe() 让你看到 Agent 内部的每一步决策
5
transformContext 是关键扩展点
后续 Memory/RAG/Guardrails 都依赖这个管线
6
beforeToolCall/afterToolCall 是安全网
工具执行前拦截、执行后审计
7
工具错误要 throw
不要返回错误文本,让 Agent 自动处理

6. 动手练习

练习 1: 基础 (⭐) — 运行 Hello Agent

完成 4.2 的环境搭建,确保 hello-agent.ts 能运行。

练习 2: 进阶 (⭐⭐) — 自定义工具

创建一个 calculate 工具,支持加减乘除:

const calcTool = {name"calculate",description"执行数学计算",parametersType.Object({expressionType.String({ description"数学表达式,如 '2+3*4'" }),  }),executeasync (_id, params) => {// 提示: 可以用 Function 构造器安全执行// 或者用 math.js 库const result = /* 你的实现 */;return {content: [{ type"text"asconsttextString(result) }],    };  },};

测试:让 Agent 回答「如果我有 12 个苹果,分给 3 个人每人 2 个,还剩几个?」

练习 3: 挑战 (⭐⭐⭐) — 多工具协作

给 Agent 添加三个工具:read_filelist_fileswrite_file。让它能:

  1. 1. 列出当前目录的文件
  2. 2. 读取一个文件
  3. 3. 创建一个摘要文件

观察:Agent 会按什么顺序调用这些工具?事件流是什么样的?


7. 延伸阅读

  • • 🔗 pi-ai README: https://github.com/badlogic/pi-mono/blob/main/packages/ai/README.md
  • • 🔗 pi-agent-core README: https://github.com/badlogic/pi-mono/blob/main/packages/agent/README.md
  • • 🔗 TypeBox 文档: https://github.com/sinclairzx81/typebox