从用户提问到模型响应:OpenClaw全链路调用流程深度拆解
从用户提问到模型响应:OpenClaw全链路调用流程深度拆解
你在终端敲了一个问题,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 决定"这个问题谁来回答"。
路由策略一:静态规则路由
{
"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": "上次对话的摘要..."},
{"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": {}
}
}
]
}
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_internal": {
"model": "anthropic/claude-sonnet-4-5",
"temperature": 0.7,
"max_tokens": 4096,
"messages": []
},
"anthropic_api": {
"method": "POST",
"url": "https://api.anthropic.com/v1/messages",
"body": {
"model": "claude-sonnet-4-5-20250514",
"max_tokens": 4096,
"temperature": 0.7,
"system": "(从 messages[0].role='system' 提取)",
"messages": "(去掉 system 消息后剩下的)",
"tools": []
}
},
"ollama_api": {
"method": "POST",
"url": "http://localhost:11434/api/chat",
"body": {
"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}
}
}
五、一张图总结全链路
| 层次 | 职责 | 核心组件 |
|---|---|---|
| ① Channel | 消息入口,统一格式化 | Webchat / Telegram / CLI / Discord |
| ② Gateway | 核心调度中枢 | Session Manager + Context Assembler |
| ③ Model Router | 智能路由,选模型 | 规则匹配 / LLM Router |
| ④ Agent Session | 能力注入 | System Prompt + Tools + SKILL |
| ⑤ Tool Runtime | 函数调用中转 | Tool Registry + 权限检查 |
| ⑥ Model Provider | 模型推理 | Provider Adapter(格式翻译) |
| ⑦ Response | 响应逐层返回 | 流式/非流式适配 + Channel 渲染 |
更多推荐
所有评论(0)