乐于分享
好东西不私藏

OpenClaw 子模块深度解析:Pi Agent 引擎——LLM Agent 运行时

OpenClaw 子模块深度解析:Pi Agent 引擎——LLM Agent 运行时
前面两篇拆了 Gateway 的协议层和插件系统。今天我们要钻到 OpenClaw 最核心的模块——Pi Agent 引擎。如果说 Gateway 是心脏,插件是四肢,那 Agent 引擎就是大脑。它负责把一条"帮我查一下 xxx"的聊天消息,变成完整的推理链:构造上下文 → 注入 Skills → 绑定工具 → 调用大模型 → 执行工具 → 再推理 → 生成回复。

这不是"调一次 API 就完了"的简单对话——这是一个真正的 Agent Loop。


一、Agent Loop 全景:从消息到回复的完整链路

当你在 WhatsApp 里发了一条"帮我看看 openclaw 项目里有多少个 TypeScript 文件",整个链路是:

WhatsApp 消息到达 Gateway  → Routing Engine(验证 allowFrom)    → Session Manager(查找/创建发送者的 Session)      → Agent Loop 启动:        ① 构造 System Prompt ← AGENTS.md + CLAUDE.md + .cursorrules        ② 注入 Skills ← workspace skills + shared skills + plugin skills        ③ 绑定 Tools ← 内置工具 + plugin 注册的工具 → TypeBox Schema → JSON Schema        ④ 调用 LLM API ← 主 provider → 备1 → 备2(failover chain)        ⑤ 模型返回 tool_call:{name: ”search_files”, args: {pattern: ”*.ts”}}        ⑥ 执行工具 → 拿到结果 → 回传模型        ⑦ 模型再推理 → tool_call:{name: ”terminal”, args: {command: ”find ... | wc -l”}}        ⑧ 执行工具 → 拿到精确数字 → 回传模型        ⑨ 模型生成最终回复:”这个项目有 1,247 个 .ts 文件”      ← 流式 event:agent 推送(每个 step 都可见)    ← Channel Adapter 反向适配 → WhatsApp 发送

整个循环的核心是 ReAct 模式:Reasoning(推理)→ Acting(调用工具)→ Observing(看结果)→ 再 Reasoning,直到模型认为任务完成(输出 end_turn)。


二、System Prompt 构造:上下文注入的优先级

Agent 的 System Prompt 不是一段写死的文本——它是多层注入拼接出来的:

  1. 项目上下文(最高优先级):AGENTS.md / CLAUDE.md / .cursorrules——告诉 Agent"你是谁、什么项目、什么规则"
  2. Skills 注入:所有匹配的 Skills 的 SKILL.md 内容——告诉 Agent"你有哪些能力、在什么场景下怎么做"
  3. Tools 定义:所有可用工具的 JSON Schema——告诉 Agent"你能调什么函数"
  4. Session 上下文:当前会话的历史消息、用户偏好、记忆

注入顺序很重要:Skills 必须在 Tools 之前。如果顺序反了,模型会先看到 50 个工具然后才看到 Skills 说"在这个场景下用这个工具"——模型可能已经基于"第一印象"在心里选错工具了。


三、Tool Binding:TypeBox Schema 的类型安全屏障

Agent 能调什么工具,完全取决于 System Prompt 里注入了什么 JSON Schema。这些 Schema 不是手写的——手写 JSON Schema 极易出错(你写了 type: "string" 但代码里处理 number,编译期完全不报错)。

OpenClaw 的解决方案:每个工具用 TypeBox 定义入参 Schema:

const SearchFilesSchema = Type.Object({  pattern: Type.String(),  path: Type.Optional(Type.String()),  target: Type.Union([Type.Literal(”content”), Type.Literal(”files”)])});

TypeScript 自动推导出类型 {pattern: string; path?: string; target: "content" | "files"}——而实际实现的函数签名也用这个类型。如果 Schema 定义和实现代码不一致,TS 编译直接报错,根本不需要运行时才发现。


四、Session 隔离:per-sender JSONL 存储

每个发送者(WhatsApp 号码、Telegram 用户 ID)都有自己独立的 Session。存储方式:

  • 物理存储:~/.openclaw/agents//sessions/.jsonl
  • 格式:每行一条 JSON,追加写入
  • 隔离级别:不同发送者的文件物理隔离——不是靠 SQL WHERE 子句保证

为什么用 JSONL 而不是 SQLite?

维度
JSONL
SQLite
Migration
不需要——加字段直接加
需要 migration script
调试
tail -f
 实时看
需要 SQL 查询
安全隔离
文件级天然隔离
需要 WHERE 子句保证
查询能力
只能顺序读
支持 JOIN、过滤、聚合
并发写入
append-only,无锁冲突
写锁竞争

对话记录的查询模式是"读某个 Session 的全部历史"——不需要 JOIN, 不需要 WHERE 多条件过滤。JSONL 的简单性在这里是优势而非限制。

同一个人的 WhatsApp 和 Telegram 消息可以路由到同一个 Session——通过 session-key 的跨通道绑定配置。这意味着你在 WhatsApp 上聊了三轮,切到 Telegram 上 Agent 还记得上下文。


五、Provider Failover:为什么需要自动切换模型?

OpenClaw 支持 30+ 个模型供应商。但真正的价值不是你"可以选 30 个",而是你"可以配一条 fallback 链":

主 provider(Anthropic Claude)  → 超时/限流/故障 → 备1(OpenAI GPT)    → 也挂了 → 备2(Google Gemini)      → 还挂 → 备3(DeepSeek)

配置方式:

{  ”models”: {    ”mode”: ”merge”,    ”providers”: {      ”anthropic”: { ”apiKey”: ”sk-...”, ”models”: [”claude-sonnet-4-20250514”] },      ”openai”: { ”apiKey”: ”sk-...”, ”models”: [”gpt-4o”] }    }  }}

每个 Agent run 开始时,Gateway 按顺序尝试 provider,第一个成功响应的就用。这解决了自托管 AI 助手最大的痛点——依赖单一 API 服务,一挂全挂。


六、流式事件推送:用户看到的"正在思考..."

Agent 的推理过程通过 WebSocket 流式推送给所有订阅了 agent 事件的客户端:

  1. status: "thinking"
     → 模型正在推理(用户看到"..."或 spinner)
  2. tool_call
     → 模型决定调用工具(用户看到"正在搜索...")
  3. tool_result
     → 工具执行完成(用户看到搜索结果预览)
  4. text_delta
     → 模型逐字生成回复(流式输出,低延迟感知)
  5. status: "completed"
     → 最终结果

这套事件模型让 Agent 的行为完全透明——你不是在等一个黑盒输出,而是能看到"它先搜了什么、然后读了什么文件、最后怎么得出这个结论"的全过程。


七、Sub-Agent 委派:让 Agent 并行干活

delegate_task 工具让 Agent 能派生子 Agent 并行处理任务。比如:

"帮我同时查一下 DPDK、SPDK、OVS 的最新 release notes"

Agent 可以:

  1. 派生子 Agent A → 查 DPDK release notes
  2. 派生子 Agent B → 查 SPDK release notes
  3. 派生子 Agent C → 查 OVS release notes
  4. 三个并行跑,结果汇总后生成统一回复

Sub-Agent 有独立的 terminal session、独立的上下文窗口、独立的工具权限。它们不能调用 clarify(不能反问用户)、不能调用 delegate_task(默认 max_spawn_depth=1 防止无限递归)。

结果通过 Gateway 的事件总线回传给父 Agent——父 Agent 在等待期间可以继续处理其他消息。


总结

Pi Agent 引擎的核心设计原则:

  1. Agent Loop 不是一次调用:ReAct 模式让 Agent 能"思考→行动→观察→再思考"
  2. Skills 必须在 Tools 之前注入:模型先读"说明书"再看"工具箱",选错工具的概率大幅降低
  3. TypeBox 保证工具 Schema 的类型安全:编译期报错 > 运行时报错
  4. JSONL Session 隔离:简单、可调试、天然安全隔离
  5. Provider Failover:不依赖单一 API 服务,自托管 AI 的生命线

下一篇拆消息路由——看看 allowFrom、mentionRules、groupPolicy 三层过滤是怎么让"任何人都能给你的 Agent 发消息"变成"只有你允许的人才能触发推理"的。