openai-http.ts 是 OpenClaw Gateway 提供的 OpenAI 兼容 HTTP API 实现。
这个文件实现了 /v1/chat/completions 端点,让 OpenClaw 可以伪装成 OpenAI API 服务器。

一、API 实现方式详解

1.1 整体架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
┌─────────────────────────────────────────────────────────────────────────────┐
│ 你的 PC App │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ OpenClawClient 类 │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ health_check│ │ chat │ │create_session│ │ │
│ │ └─────────────┘ └──────┬──────┘ └──────┬──────┘ │ │
│ │ │ │ │ │
│ │ ┌────────────────┼────────────────┘ │ │
│ │ │ │ │ │
│ │ ▼ ▼ │ │
│ │ ┌─────────────────────────────────────────┐ │ │
│ │ │ ChatSession 类 │ │ │
│ │ │ - history: 存储对话历史 │ │ │
│ │ │ - chat(): 发送消息并自动携带历史 │ │ │
│ │ └─────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ │ HTTP POST │
│ ▼ │
└─────────────────────────────────────────────────────────────────────────────┘

│ 网络请求

┌─────────────────────────────────────────────────────────────────────────────┐
│ OpenClaw Gateway │
│ http://服务器IP:18789 │
└─────────────────────────────────────────────────────────────────────────────┘

1.2 核心类详解

OpenClawClient 类 - 客户端入口

1
2
3
4
5
6
7
8
9
10
11
class OpenClawClient:
def __init__(
self,
base_url: str = "http://127.0.0.1:18789", # OpenClaw 服务地址
token: str = "", # 认证 Token
timeout: int = 60, # 请求超时时间
max_retries: int = 3 # 最大重试次数
):
self.base_url = base_url
self.token = token
self._sessions = {} # 存储所有用户会话

职责

  • 管理与 OpenClaw 的连接配置
  • 提供单次对话方法 chat()
  • 创建和管理用户会话 create_session()

ChatSession 类 - 会话管理

1
2
3
4
5
6
class ChatSession:
def __init__(self, client: OpenClawClient, session_id: str):
self.client = client # 关联的客户端
self.session_id = session_id # 会话/用户 ID
self.history = [] # ⭐ 对话历史(关键!)
self.system_prompt = None # 系统提示词

职责

  • 维护单个用户的对话历史
  • 每次发消息自动带上历史

1.3 请求构建过程

当你调用 session.chat("你好") 时,内部发生了什么:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
def chat(self, message: str, stream: bool = False):
# 步骤 1: 构建消息列表
messages = []

# 步骤 2: 添加系统提示(如果有)
if self.system_prompt:
messages.append({
"role": "system",
"content": "你是一个友好的助手"
})

# 步骤 3: 添加历史消息(多轮对话的关键!)
for msg in self.history:
messages.append(msg.to_dict())
# 此时 messages 可能是:
# [
# {"role": "user", "content": "我叫小明"},
# {"role": "assistant", "content": "你好小明!"}
# ]

# 步骤 4: 添加当前用户消息
messages.append({"role": "user", "content": message})
# 现在 messages 是:
# [
# {"role": "user", "content": "我叫小明"},
# {"role": "assistant", "content": "你好小明!"},
# {"role": "user", "content": "你好"} ← 新消息
# ]

# 步骤 5: 发送 HTTP 请求
response = self.client._chat_completion(messages, ...)

# 步骤 6: 保存到历史
self.history.append(Message(USER, message))
self.history.append(Message(ASSISTANT, response.content))

return response

1.4 HTTP 请求详解

非流式请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
def _chat_completion(self, messages, model, user_id):
# 构建请求 URL
url = f"{self.base_url}/v1/chat/completions"

# 构建请求体 (JSON)
payload = {
"model": "openclaw:main", # 指定使用哪个 Agent
"messages": messages, # 消息历史
"stream": False, # 非流式
"user": user_id # 用户标识(可选)
}

# 构建请求头
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {token}" # 认证
}

# 发送 POST 请求
response = requests.post(url, headers=headers, json=payload, timeout=60)

# 解析响应
data = response.json()
content = data["choices"][0]["message"]["content"]

return content

实际发送的 HTTP 请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
POST /v1/chat/completions HTTP/1.1
Host: 1.12.242.236:18789
Authorization: Bearer da8cc9fa2142b41922e1abaa05e2f9b873cec612c1cad467
Content-Type: application/json

{
"model": "openclaw:main",
"messages": [
{"role": "system", "content": "你是一个友好的助手"},
{"role": "user", "content": "我叫小明"},
{"role": "assistant", "content": "你好小明!"},
{"role": "user", "content": "我叫什么名字?"}
],
"stream": false,
"user": "user-001"
}

流式请求 (SSE)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
def _chat_stream(self, messages, model, user_id):
url = f"{self.base_url}/v1/chat/completions"

payload = {
"model": "openclaw:main",
"messages": messages,
"stream": True # ⭐ 启用流式
}

# 发送请求,启用流式接收
response = requests.post(url, json=payload, stream=True)

# 逐行读取 SSE 事件
for line in response.iter_lines():
# 每行格式: "data: {...}"
if line.startswith(b"data: "):
data_str = line[6:].decode()

if data_str == "[DONE]":
break # 结束标记

# 解析 JSON
data = json.loads(data_str)
content = data["choices"][0]["delta"].get("content", "")

if content:
yield content # 逐字返回

流式响应示例

1
2
3
4
5
6
7
8
9
10
11
data: {"choices":[{"delta":{"role":"assistant"}}]}

data: {"choices":[{"delta":{"content":"你"}}]}

data: {"choices":[{"delta":{"content":"叫"}}]}

data: {"choices":[{"delta":{"content":"小"}}]}

data: {"choices":[{"delta":{"content":"明"}}]}

data: [DONE]

二、OpenClaw 如何处理 HTTP 请求并回复

2.1 完整请求处理流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
┌─────────────────────────────────────────────────────────────────────────────┐
│ 你的 PC App │
│ POST /v1/chat/completions │
│ {"messages": [...], "model": "openclaw:main"} │
└───────────────────────────────────┬─────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────────────────┐
│ ① HTTP 服务器层 (server-http.ts) │
│ - 接收 HTTP 请求 │
│ - 路由到对应处理器 │
│ - /v1/chat/completions → handleChatCompletions() │
└───────────────────────────────────┬─────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────────────────┐
│ ② 认证层 (auth.ts) │
│ - 验证 Authorization: Bearer <token> │
│ - Token 不匹配 → 返回 401 Unauthorized │
│ - 速率限制检查 → 超限返回 429 │
└───────────────────────────────────┬─────────────────────────────────────────┘
│ 认证通过

┌─────────────────────────────────────────────────────────────────────────────┐
│ ③ OpenAI API 处理层 (openai-http.ts) │
│ - 解析请求体 (model, messages, stream, user) │
│ - 确定目标 Agent (从 model 字段解析) │
│ - 确定 Session Key (基于 user 或生成) │
└───────────────────────────────────┬─────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────────────────┐
│ ④ Agent 路由层 │
│ - model: "openclaw:main" → 路由到 main agent │
│ - model: "openclaw:assistant" → 路由到 assistant agent │
│ - 加载 Agent 配置 (系统提示、工具、模型设置等) │
└───────────────────────────────────┬─────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────────────────┐
│ ⑤ 会话管理层 │
│ - 根据 session key 查找或创建会话 │
│ - 可能加载历史上下文 (如果服务端有持久化) │
└───────────────────────────────────┬─────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────────────────┐
│ ⑥ LLM 调用层 │
│ - 构建完整的 prompt (系统提示 + 用户消息) │
│ - 调用配置的 AI 模型 (MiniMax M2.5) │
│ - API: https://api.minimaxi.com/anthropic │
│ - 等待模型生成回复 │
└───────────────────────────────────┬─────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────────────────┐
│ ⑦ 响应构建层 │
│ - 将 AI 回复转换为 OpenAI 格式 │
│ - 非流式: 一次性返回完整 JSON │
│ - 流式: 逐块返回 SSE 事件 │
└───────────────────────────────────┬─────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────────────────┐
│ 你的 PC App │
│ 收到响应: │
│ { │
│ "id": "chatcmpl_xxx", │
│ "choices": [{"message": {"content": "你叫小明"}}], │
│ "model": "openclaw:main" │
│ } │
└─────────────────────────────────────────────────────────────────────────────┘

2.2 关键源码位置

层级 文件路径 职责
HTTP 服务器 src/gateway/server-http.ts 接收请求、路由分发
认证 src/gateway/auth.ts Token 验证、速率限制
OpenAI API src/gateway/openai-http.ts 处理 /v1/chat/completions
Agent 核心 src/agent/ Agent 逻辑、工具调用
LLM 调用 src/llm/ 调用各种 AI 模型

2.3 请求处理代码分析

让我展示 OpenClaw 中关键的处理逻辑:
让我继续查看关键的处理函数:
现在让我完整解释 OpenClaw 如何处理请求:


三、OpenClaw 处理请求的详细代码流程

3.1 入口点:handleOpenAiHttpRequest

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 文件: src/gateway/openai-http.ts

export async function handleOpenAiHttpRequest(req, res, opts) {
// 1️⃣ 验证请求路径和认证
const handled = await handleGatewayPostJsonEndpoint(req, res, {
pathname: "/v1/chat/completions", // 匹配的路径
auth: opts.auth, // Token 验证
maxBodyBytes: 1024 * 1024, // 限制请求体大小 1MB
});

// 2️⃣ 解析请求参数
const payload = coerceRequest(handled.body);
const stream = Boolean(payload.stream); // 是否流式
const model = payload.model || "openclaw"; // 模型/Agent ID
const user = payload.user; // 用户标识

// 3️⃣ 解析 Agent ID 和 Session Key
const agentId = resolveAgentIdForRequest({ req, model });
const sessionKey = resolveOpenAiSessionKey({ req, agentId, user });

// 4️⃣ 构建 Agent 提示词(把 messages 数组转成内部格式)
const prompt = buildAgentPrompt(payload.messages);

// 5️⃣ 生成唯一的运行 ID
const runId = `chatcmpl_${randomUUID()}`;

// 6️⃣ 调用 Agent 处理
if (!stream) {
// 非流式:等待完成后一次性返回
const result = await agentCommand(commandInput, runtime, deps);
const content = resolveAgentResponseText(result);

sendJson(res, 200, {
id: runId,
choices: [{ message: { role: "assistant", content } }],
// ...
});
} else {
// 流式:逐块返回
// ... 见下文
}
}

3.2 消息格式转换:buildAgentPrompt

你发送的 OpenAI 格式消息:

1
2
3
4
5
6
7
8
{
"messages": [
{"role": "system", "content": "你是助手"},
{"role": "user", "content": "你好"},
{"role": "assistant", "content": "你好!"},
{"role": "user", "content": "我叫什么?"}
]
}

被转换为 OpenClaw 内部格式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function buildAgentPrompt(messages) {
// 提取 system 消息作为额外的系统提示
const systemParts = [];

// 其他消息转换为对话条目
const conversationEntries = [];

for (const msg of messages) {
if (msg.role === "system") {
systemParts.push(msg.content);
} else {
conversationEntries.push({
role: msg.role,
entry: {
sender: msg.role === "user" ? "User" : "Assistant",
body: msg.content
}
});
}
}

// 构建最终的 prompt 字符串
const message = buildAgentMessageFromConversationEntries(conversationEntries);

return {
message, // 包含所有历史的文本
extraSystemPrompt: systemParts.join("\n\n")
};
}

3.3 Agent 执行:agentCommand

1
2
3
4
5
6
7
8
9
// 核心执行函数
const result = await agentCommand({
message: "...", // 用户消息(包含历史)
extraSystemPrompt: "...", // 系统提示
sessionKey: "openai:user-001:main", // 会话标识
runId: "chatcmpl_xxx", // 本次运行 ID
deliver: false, // 不发送到 IM(只返回 HTTP)
messageChannel: "webchat"
}, runtime, deps);

这个函数内部:

  1. 加载 Agent 配置(系统提示、工具列表等)
  2. 调用配置的 LLM(你配置的是 MiniMax M2.5)
  3. 等待 AI 生成回复
  4. 返回结果

3.4 流式响应处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
if (stream) {
// 设置 SSE 响应头
res.setHeader("Content-Type", "text/event-stream");
res.setHeader("Cache-Control", "no-cache");
res.setHeader("Connection", "keep-alive");

// 监听 Agent 事件
const unsubscribe = onAgentEvent((evt) => {
if (evt.runId !== runId) return; // 只处理当前请求

if (evt.stream === "assistant") {
// AI 生成了新的文本片段
const content = resolveAssistantStreamDeltaText(evt);

// 写入 SSE 事件
writeSse(res, {
id: runId,
choices: [{
delta: { content: content },
finish_reason: null
}]
});
}
});

// 启动 Agent(后台执行)
agentCommand(commandInput, runtime, deps)
.finally(() => {
// 发送结束标记
writeDone(res); // 写入 "data: [DONE]"
res.end();
unsubscribe();
});
}

四、完整数据流图示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
┌─────────────────────────────────────────────────────────────────────────────┐
│ 你的 PC App │
│ │
│ requests.post("/v1/chat/completions", json={ │
│ "model": "openclaw:main", │
│ "messages": [ │
│ {"role": "user", "content": "我叫小明"}, │
│ {"role": "assistant", "content": "你好小明!"}, │
│ {"role": "user", "content": "我叫什么?"} │
│ ] │
│ }) │
└───────────────────────────────────┬─────────────────────────────────────────┘

════════════════╪════════════════
HTTP POST │ 网络传输
════════════════╪════════════════


┌─────────────────────────────────────────────────────────────────────────────┐
│ OpenClaw Gateway (server-http.ts) │
│ │
│ ① 接收请求 → ② 验证 Token → ③ 路由到 openai-http.ts │
└───────────────────────────────────┬─────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────────────────┐
│ handleOpenAiHttpRequest (openai-http.ts) │
│ │
│ ① 解析 messages 数组 │
│ ② 构建内部 prompt 格式 │
│ ③ 确定 agentId = "main", sessionKey = "openai:user:main" │
│ ④ 调用 agentCommand() │
└───────────────────────────────────┬─────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────────────────┐
│ agentCommand (commands/agent.ts) │
│ │
│ ① 加载 Agent 配置 (agents.defaults) │
│ ② 构建完整的 prompt: │
│ System: "你是小白🐰..." │
│ + extraSystemPrompt (如果有) │
│ + 对话历史: "User: 我叫小明\nAssistant: 你好小明!\nUser: 我叫什么?" │
│ ③ 调用 LLM │
└───────────────────────────────────┬─────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────────────────┐
│ LLM 调用层 │
│ │
│ POST https://api.minimaxi.com/anthropic/v1/messages │
│ Headers: Authorization: Bearer <MiniMax API Key> │
│ Body: { │
│ "model": "MiniMax-M2.5", │
│ "messages": [...], │
│ "max_tokens": 8192 │
│ } │
│ │
│ 等待 MiniMax AI 生成回复... │
│ 返回: "你叫小明呀!🐰" │
└───────────────────────────────────┬─────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────────────────┐
│ 响应构建 │
│ │
│ sendJson(res, 200, { │
│ "id": "chatcmpl_xxx", │
│ "object": "chat.completion", │
│ "choices": [{ │
│ "message": { │
│ "role": "assistant", │
│ "content": "你叫小明呀!🐰" │
│ }, │
│ "finish_reason": "stop" │
│ }] │
│ }) │
└───────────────────────────────────┬─────────────────────────────────────────┘

════════════════╪════════════════
HTTP Response │ 网络传输
════════════════╪════════════════


┌─────────────────────────────────────────────────────────────────────────────┐
│ 你的 PC App │
│ │
│ response = requests.post(...) │
│ data = response.json() │
│ content = data["choices"][0]["message"]["content"] │
│ print(content) # "你叫小明呀!🐰" │
└─────────────────────────────────────────────────────────────────────────────┘

五、关键点总结

方面 说明
API 格式 完全兼容 OpenAI Chat Completions API
认证 Bearer Token,在 HTTP Header 中传递
会话管理 客户端维护 history,每次请求带上完整历史
多轮对话 OpenClaw 把所有 messages 拼成一个 prompt 发给 AI
流式响应 使用 SSE (Server-Sent Events) 协议
底层 AI 调用你配置的 MiniMax M2.5 模型

希望这个详细解释能帮助你理解整个系统是如何工作的!如果还有任何疑问,随时问我。