乐于分享
好东西不私藏

OpenClaw全链路调用流程深度拆解

OpenClaw全链路调用流程深度拆解

从用户提问到模型响应:OpenClaw全链路调用流程深度拆解

文 | AI编程实践

你在终端敲了一个问题,OpenClaw 背后发生了什么?消息怎么从 Channel 流到 Model Router?Agent Session 什么时候创建?Tool 调用又是怎么注入上下文的?

这篇文章把 OpenClaw 的全链路调用流程拆开——从用户输入到模型返回的每一步,消息怎么构造、参数怎么传递、路由怎么做决策。


一、先看全景:一个请求的 7 段旅程

一个请求从用户输入到模型响应的 7 段旅程:

用户输入 "帮我查一下今天的天气"
         │
         ▼
┌─────────────────────────────────────────────────────┐
│  ① Channel 层:消息入口                              │
│  Webchat / Telegram / Discord / WhatsApp / CLI      │
│  统一格式化为 OpenClaw Message                       │
└─────────────────────────────────────────────────────┘
         │
         ▼
┌─────────────────────────────────────────────────────┐
│  ② Gateway 层:核心调度中枢(127.0.0.1:18789)       │
│  会话管理 → 上下文组装 → 路由决策 → 模型调用          │
└─────────────────────────────────────────────────────┘
         │
         ▼
┌─────────────────────────────────────────────────────┐
│  ③ Model Router 层:智能路由                        │
│  规则匹配 / LLM Router → 选 ollama:11434 还是        │
│  api.anthropic.com                                  │
└─────────────────────────────────────────────────────┘
         │
         ▼
┌─────────────────────────────────────────────────────┐
│  ④ Agent Session 层:能力注入                        │
│  加载 SKILL → 注入 System Prompt → 挂载 Tool 列表     │
└─────────────────────────────────────────────────────┘
         │
         ▼
┌─────────────────────────────────────────────────────┐
│  ⑤ Tool Runtime 层:函数调用中转                     │
│  模型请求 tool_call → Runtime 执行 → 结果注入消息链    │
└─────────────────────────────────────────────────────┘
         │
         ▼
┌─────────────────────────────────────────────────────┐
│  ⑥ Model Provider 层:模型推理                       │
│  本地:Ollama / vLLM / llama.cpp                    │
│  云端:Anthropic API / OpenAI API / Google AI       │
└─────────────────────────────────────────────────────┘
         │
         ▼
┌─────────────────────────────────────────────────────┐
│  ⑦ Response 回流:响应逐层返回                       │
│  模型输出 → Gateway 格式化 → Channel 渲染 → 用户看到   │
└─────────────────────────────────────────────────────┘

二、逐层拆解:每层做什么、参数怎么传

2.1 Channel 层:消息入口

这是用户和 OpenClaw 交互的”门面”。每个 Channel 负责收消息、格式化,但不做任何业务逻辑。

支持的 Channel:
  - CLI(终端直接输入)
  - Webchat(浏览器 WebSocket 连接)
  - Telegram Bot(消息平台)
  - Discord Bot
  - WhatsApp
  - Slack
  - Google Chat
  - Signal
  - iMessage(仅 macOS)

Channel 层的职责:
  1. 接收原始消息(文本 / 图片 / 文件)
  2. 转成统一的 Message 格式
  3. 通过 WebSocket 或 HTTP 发送到 Gateway(127.0.0.1:18789)

消息统一格式

{
  "channel": "webchat",
  "sessionId": "sess_abc123",
  "message": {
    "role": "user",
    "content": "查一下今天北京天气",
    "attachments": []
  },
  "metadata": {
    "platform": "web",
    "timestamp": 1717766400
  }
}

💡 关键洞察:无论从哪个 Channel 进来,到 Gateway 层的消息格式完全一致。这意味着你可以在 Terminal 里开始一段对话,然后在 Telegram 上无缝继续——因为 sessionId 是全局统一的。


2.2 Gateway 层:核心调度中枢

Gateway 是 OpenClaw 的大脑。它驻留为系统守护进程,监听 127.0.0.1:18789,负责所有请求的调度和组装。

Gateway 核心职责:

1. 会话管理(Session Manager):
   - 根据 sessionId 找到或创建会话
   - 加载历史消息
   - 管理上下文窗口

2. 上下文组装(Context Assembler):
   - 将历史消息 + 系统提示词 + 工具定义 拼成完整的 messages 数组
   - 处理上下文截断(超长对话)
   - 注入记忆系统数据

3. 路由决策(Model Router):
   - 调用 Model Router 决定用哪个模型
   - 可能启用分层路由(小模型判简单/复杂 → 分发给对应模型)

4. 请求发送(Provider Adapter):
   - 将统一格式的消息转为具体 Provider 的 API 格式
   - 处理流式/非流式响应

Gateway 内部状态机

Gateway 处理一个请求的状态转换:

IDLE → RECEIVED → CONTEXT_ASSEMBLY → ROUTING → CALLING → STREAMING → DONE
                                                      ↓
                                                 TOOL_DETECTED
                                                      ↓
                                              TOOL_EXECUTION → CONTEXT_ASSEMBLY(循环)

2.3 Model Router 层:智能路由

这是 OpenClaw 区别于直接用 API 的核心差异。Router 决定”这个问题谁来回答”。

路由策略一:静态规则路由

配置示例(openclaw.json):

{
  "routing": [
    {"patterns": ["hello", "hi", "thanks"], "model": "ollama/qwen2.5:0.5b"},
    {"patterns": ["code", "bug", "error", "fix"], "model": "ollama/qwen2.5-coder:14b"},
    {"patterns": ["analyze", "design", "architecture"], "model": "anthropic/claude-sonnet-4-5"},
    {"default": "ollama/llama3.1:8b"}
  ]
}

工作原理:
  用户输入 → 按顺序匹配 patterns → 命中则路由到对应模型 → 未命中用 default

路由策略二:LLM Router(智能路由)

工作原理:
  1. 用户输入进入后,先用一个小模型(如 qwen2.5:0.5b)做分类
  2. 分类结果:
     SIMPLE    → 本地小模型直接回答(< 100ms)
     MEDIUM    → 本地中等模型(如 qwen2.5:14b)
     COMPLEX   → 云端强模型(Claude/GPT)
     REASONING → 云端推理模型(o3/Claude Opus)
  3. 复杂问题自动升级,简单问题本地省钱

收益:
  - 40-80% 的请求由本地模型处理
  - 云端费用大幅降低
  - 简单问题秒回(本地推理 0 网络延迟)

路由决策的输入参数

Router 做决策时参考的信息:

  1. 用户输入文本(正则匹配 or LLM 分类)
  2. 上下文长度(长上下文可能触发路由升级)
  3. 历史工具调用次数(频繁调工具 → 路由到更强模型)
  4. 用户偏好设置(某些用户强制用特定模型)
  5. 当前各 Provider 的可用性和延迟

2.4 Agent Session 层:能力注入

确定用哪个模型后,Gateway 给这次请求”装配武器”——注入 System Prompt、挂载 Tool 定义、加载 SKILL。

Agent Session 组装过程:

Step 1:加载 System Prompt
  ├── 基础 System Prompt(OpenClaw 内置)
  ├── 用户自定义 CLAUDE.md / openclaw.md
  └── 会话级 Instructions

Step 2:挂载 Tool 列表
  ├── 内置 Tools(文件读写、Shell 执行、网络请求)
  ├── MCP Server 注册的 Tools
  └── SKILL 定义的 Tools

Step 3:注入上下文
  ├── 历史消息(从 Session Manager 读取)
  ├── Memory 数据(长期记忆)
  └── RAG 检索结果(如有)

最终 messages 数组结构:

[
  {"role": "system", "content": "你是OpenClaw AI助手..." + user_instructions},
  {"role": "user",   "content": "上次对话的摘要..."},  ← Memory
  {"role": "user",   "content": "历史消息1"},
  {"role": "assistant", "content": "历史回复1"},
  {"role": "user",   "content": "查一下今天北京天气"},  ← 当前问题
]

Tool 定义注入示例

{
  "messages": [...],
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "get_weather",
        "description": "获取指定城市实时天气",
        "parameters": {
          "type": "object",
          "properties": {
            "city": {"type": "string"}
          },
          "required": ["city"]
        }
      }
    },
    {
      "type": "function",
      "function": {
        "name": "read_file",
        "description": "读取文件内容",
        "parameters": { ... }
      }
    }
    // ... 更多来自 MCP Server 的 Tools
  ]
}

2.5 Tool Runtime 层:函数调用中转

当模型返回 finish_reason: "tool_calls" 时,Tool Runtime 接手执行。

Tool Runtime 工作流:

1. 解析 tool_calls
   模型返回:
   {
     "tool_calls": [{
       "id": "call_abc",
       "function": {"name": "get_weather", "arguments": '{"city":"北京"}'}
     }]
   }

2. 匹配 Tool 实现
   从注册的 Tool Registry 中找到 get_weather 的实现

3. 权限检查
   是否需要用户确认?(如 Shell 执行、文件删除)
   是否在 allow-list 中?

4. 执行 Tool
   调用实际的 Tool 函数 → 获取结果

5. 将结果注入 messages
   messages.push({
     role: "assistant", tool_calls: [...]
   })
   messages.push({
     role: "tool", tool_call_id: "call_abc", content: '{"temp":28,"condition":"晴"}'
   })

6. 重新发送请求
   更新后的 messages → 再次调用 Provider

💡 关键洞察:Tool Runtime 不关心模型具体是谁。它从模型拿到 tool_calls → 执行函数 → 把结果塞回 messages → 再调模型。这个循环对 Anthropic、OpenAI、Ollama 全部通用——Provider Adapter 负责翻译 API 格式差异。


2.6 Model Provider 层:模型推理

Provider Adapter 把 OpenClaw 的统一格式翻译成各 Provider 的原生 API 格式。

支持的 Provider:

本地:
  ollama/*      → http://localhost:11434/api/chat
  vllm/*        → http://localhost:8000/v1/chat/completions
  llama.cpp/*   → http://localhost:8080/completion
  openai/*      → 可指向任何 OpenAI 兼容的本地服务

云端:
  anthropic/*   → https://api.anthropic.com/v1/messages
  openai/*      → https://api.openai.com/v1/chat/completions
  google/*      → Google AI API
  groq/*        → Groq API
  deepseek/*    → DeepSeek API
  xai/*         → xAI (Grok) API
  mistral/*     → Mistral API

Provider Adapter 的核心任务

OpenClaw 统一格式 → 各 Provider 原生格式的翻译:

1. messages 格式转换
   OpenClaw 内部有统一的 Message 结构
   → Anthropic 要转成 {"role": "user", "content": [...]}
   → OpenAI 要转成 {"role": "user", "content": "..."}
   → Ollama 要转成 {"role": "user", "content": "..."}

2. Tool 定义转换
   OpenClaw 内部 Tool Schema
   → Anthropic: tools: [{type, name, description, input_schema}]
   → OpenAI: tools: [{type: "function", function: {name, description, parameters}}]

3. Tool 结果重注入
   各 Provider 的 tool_calls 格式不同,统一解析后塞回 messages

4. 流式/非流式适配
   部分 Provider 不支持流式 → Adapter 做降级处理

一次实际调用的参数映射

OpenClaw 内部参数:
  model: "anthropic/claude-sonnet-4-5"
  temperature: 0.7
  max_tokens: 4096
  messages: [...]

翻译为 Anthropic API 调用:
  POST https://api.anthropic.com/v1/messages
  {
    "model": "claude-sonnet-4-5-20250514",
    "max_tokens": 4096,
    "temperature": 0.7,
    "system": "...",          ← 从 messages[0].role="system" 提取
    "messages": [...],        ← 去掉 system 消息后剩下的
    "tools": [...]
  }

翻译为 Ollama API 调用:
  POST http://localhost:11434/api/chat
  {
    "model": "llama3.1:8b",
    "messages": [...],        ← 保留 system 消息
    "options": {
      "temperature": 0.7,
      "num_predict": 4096
    },
    "stream": true
  }

2.7 Response 回流:逐层返回

模型响应是一条”回头路”,每层都可能做加工。

响应回流路径:

Provider 原始响应
  → Provider Adapter 统一格式化(去除 Provider 特定字段)
    → Gateway 做后处理(提取 tool_calls、检测 finish_reason)
      → Session Manager 存历史消息
        → Channel 适配格式化(Webchat 拼 HTML,Telegram 转 Markdown)
          → 用户看到最终回复

流式模式下每层的处理:

Provider 逐 token 输出
  → Adapter 逐 chunk 转统一格式
    → Gateway 实时转发
      → Channel 实时渲染(打字机效果)

三、一次完整的多轮 Tool Calling 调用示例

用一个实际例子串起全链路。用户问”帮我查北京天气,然后写个简短出行建议”。

第 1 轮:初始请求

① Channel:用户输入进入 Webchat
② Gateway 组装上下文:
   messages: [
     {role: "system", content: "你是OpenClaw助手..."},
     {role: "user", content: "帮我查北京天气,然后写个出行建议"}
   ]
③ Router 决策:用 anthropic/claude-sonnet-4-5(有工具调用需求)

④ 请求发送到 Anthropic API:
   POST /v1/messages
   {
     model: "claude-sonnet-4-5-20250514",
     system: "...",
     messages: [{role: "user", content: "..."}],
     tools: [
       {name: "get_weather", ...},
       {name: "web_search", ...}
     ],
     max_tokens: 4096
   }

⑤ 模型返回:
   {
     stop_reason: "tool_use",
     content: [{
       type: "tool_use",
       name: "get_weather",
       input: {city: "北京"}
     }]
   }

⑥ Tool Runtime 执行 get_weather("北京")
   返回:{temp: 28, condition: "晴", humidity: "45%"}

⑦ Tool Runtime 将结果注入 messages

第 2 轮:携带工具结果的续写

⑧ Gateway 重新组装 messages:
   messages: [
     {role: "system", content: "你是OpenClaw助手..."},
     {role: "user", content: "帮我查北京天气,然后写个出行建议"},
     {role: "assistant", content: [{type: "tool_use", name: "get_weather", input: {city: "北京"}}]},
     {role: "user", content: [{type: "tool_result", content: '{"temp":28,"condition":"晴"}'}]}
   ]

⑨ 再次调 Anthropic API → 模型生成自然语言回复

⑩ 响应回流:
    Provider → Adapter → Gateway → Channel → 用户看到:
    "北京今天晴天,28°C,湿度45%。建议穿短袖,带把遮阳伞☀️"

四、配置示例:一图看清参数在哪设

openclaw 配置位置:

全局配置:~/.openclaw/openclaw.json
项目配置:<project>/.openclaw/openclaw.json
环境变量:OPENCLAW_* 系列

配置优先级:环境变量 > 项目配置 > 全局配置 > 默认值

典型生产配置

{
  "gateway": {
    "bind": "loopback",
    "port": 18789
  },
  "agents": {
    "defaults": {
      "model": {
        "primary": "anthropic/claude-sonnet-4-5",
        "routing": {
          "enabled": true,
          "router": {
            "model": "ollama/qwen2.5:0.5b",
            "rules": {
              "alwaysPrimary": {
                "patterns": ["analyze", "explain", "design", "debug", "fix"]
              },
              "alwaysLocal": {
                "patterns": ["hello", "hi", "thanks", "ok", "yes", "no"]
              }
            }
          }
        },
        "fallback": "ollama/llama3.1:8b"
      },
      "maxTokens": 4096,
      "temperature": 0.7
    }
  },
  "tools": {
    "mcpServers": [
      {
        "name": "filesystem",
        "command": "npx",
        "args": ["-y", "@anthropic-ai/mcp-server-filesystem", "/Users/me/projects"]
      }
    ],
    "confirmBeforeRun": ["shell", "file_write", "file_delete"]
  },
  "channels": {
    "telegram": { "enabled": true },
    "discord": { "enabled": false }
  }
}

五、一张图总结全链路

如果觉得有帮助,欢迎点赞和在看!