【OpenClaw 入门指南】HTTP API 路由系统:/v1/models 与插件路由共存
模块:M1 Gateway 网关服务 | 预计阅读:40 分钟 | 难度:⭐⭐⭐⭐

💡 导语
Alex,上一篇我们讲了 WebSocket 协议——那是 OpenClaw 的”神经系统”,负责实时通信。但 Gateway 还有一个同样重要的面孔:HTTP REST API。
为什么重要?
- 1. OpenAI 兼容接口 ——
/v1/models、/v1/chat/completions,让任何支持 OpenAI API 的工具都能直接调用 OpenClaw - 2. 插件路由系统 —— 插件可以注册自己的 HTTP 端点,扩展 Gateway 的能力
- 3. 多路复用 —— 同一个端口同时服务 WebSocket、HTTP API、插件路由、静态文件
- 4. 认证统一 —— 所有路由共享同一套认证体系,不重复造轮子
理解这个路由系统,你就能明白为什么 OpenClaw 可以”一个端口走天下”,也能排查 API 调用失败、路由冲突、认证问题。
一、路由系统概览
1.1 架构图

1.2 路由优先级(关键!)
Gateway 处理 HTTP 请求时,按严格优先级匹配路由:
| 优先级 | 路由阶段 | 路径示例 | 说明 |
|---|---|---|---|
| 1 | Health Probes | /health, /ready |
存活/就绪检查,最先处理 |
| 2 | Hooks | 插件钩子 | 插件预处理 |
| 3 | OpenAI Models | /v1/models |
模型列表/详情 |
| 4 | Embeddings | /v1/embeddings |
嵌入向量 |
| 5 | Tools Invoke | /tools/invoke |
工具调用 |
| 6 | Session Kill | /sessions/*/kill |
终止会话 |
| 7 | Session History | /sessions/*/history |
会话历史 |
| 8 | OpenResponses | /v1/responses |
OpenAI Responses API |
| 9 | Chat Completions | /v1/chat/completions |
聊天补全(最核心) |
| 10 | Plugin Node Auth | 插件节点能力 | 节点能力路由认证 |
| 11 | Plugin Routes | /plugin/* |
插件自定义路由 |
| 12 | Chat Media | /api/chat/media/* |
聊天媒体文件 |
| 13 | Control UI | /control/* |
Web 控制面板 |
| 14 | 404 | – | 无匹配路由 |
核心设计原则:
- • 系统路由优先于插件路由 —— 防止插件覆盖核心功能
- • 精确匹配优先于通配匹配 ——
/v1/models在/v1/*之前 - • 认证在路由之后 —— 先确定路由,再检查权限
二、核心源码解析
2.1 路由分发器:createGatewayHttpServer
源码位置:dist/server.impl-yVEQugR-.js:846
function createGatewayHttpServer(opts) {
const {
clients,
controlUiEnabled,
openAiChatCompletionsEnabled,
openAiChatCompletionsConfig,
openResponsesEnabled,
handleHooksRequest,
handlePluginRequest,
resolvedAuth,
rateLimiter,
getReadiness
} = opts;
// 创建 HTTP/HTTPS 服务器
const httpServer = opts.tlsOptions
? createServer$1(opts.tlsOptions, handleRequestWithTrace)
: createServer(handleRequestWithTrace);
关键设计:
- • 支持 HTTP 和 HTTPS(通过
tlsOptions配置) - • 所有请求走
handleRequestWithTrace,自动附加诊断追踪 - • WebSocket Upgrade 请求在这里被拦截,转给 WebSocket 处理器
2.2 请求处理主流程
async function handleRequest(req, res) {
// 1. 设置安全头
setDefaultSecurityHeaders(res, { strictTransportSecurity: strictTransportSecurityHeader });
// 2. WebSocket Upgrade 直接返回(由 upgrade 处理器处理)
if ((req.headers.upgrade ?? "").toLowerCase() === "websocket") return;
// 3. 解析请求路径
const requestPath = parseGatewayRequestPath(req.url);
if (requestPath === void 0) {
sendGatewayAuthFailure(res, { ok: false, reason: "unauthorized" });
return;
}
// 4. 快速路径:Health Probe(无需认证)
if (GATEWAY_PROBE_STATUS_BY_PATH.get(requestPath) === "live") {
await handleGatewayProbeRequest(req, res, requestPath, ...);
return;
}
// 5. 加载配置
const configSnapshot = loadGatewayConfig();
const trustedProxies = configSnapshot.gateway?.trustedProxies ?? [];
// 6. 解析插件节点能力路由(如 /node/camera/snap)
const scopedNodeCapability = normalizePluginNodeCapabilityScopedUrl(req.url ?? "/");
...
// 7. 构建请求阶段管道
const requestStages = [...];
// 8. 按优先级执行各阶段
if (await runGatewayHttpRequestStages(requestStages)) return;
// 9. 无匹配路由 → 404
res.statusCode = 404;
res.end("Not Found");
}
解析:
- • 第 2 步:WebSocket 的 Upgrade 请求不在这里处理,由
attachGatewayUpgradeHandler专门处理 - • 第 4 步:Health Probe 是”绿色通道”,不经过认证,快速返回
- • 第 6 步:插件节点能力路由有特殊的 URL 格式(
/node/{capability}/{command}) - • 第 7-8 步:这是核心——阶段管道(Request Stages)
2.3 阶段管道设计模式
这是 Gateway 路由系统最精妙的设计:
const requestStages = [
{
name: "gateway-probes",
run: () => handleGatewayProbeRequest(...)
},
{
name: "hooks",
run: () => handleHooksRequest(...)
}
];
// OpenAI 兼容路由
if (openAiCompatEnabled && isOpenAiModelsPath(scopedRequestPath)) {
requestStages.push({
name: "models",
run: async () => (await getModelsHttpModule()).handleOpenAiModelsHttpRequest(...)
});
}
if (openAiCompatEnabled && isEmbeddingsPath(scopedRequestPath)) {
requestStages.push({
name: "embeddings",
run: async () => (await getEmbeddingsHttpModule()).handleOpenAiEmbeddingsHttpRequest(...)
});
}
// ... 更多阶段
// 插件路由
requestStages.push(...buildPluginRequestStages(...));
// 执行管道
if (await runGatewayHttpRequestStages(requestStages)) return;
为什么用阶段管道?
- 1. 优先级明确 —— 数组顺序就是优先级
- 2. 按需加载 —— 只有匹配的路径才加载对应的处理模块(懒加载)
- 3. 短路返回 —— 每个阶段返回
true表示”已处理,停止后续” - 4. 可扩展 —— 插件可以插入自己的阶段
2.4 路径匹配函数
源码位置:dist/server.impl-yVEQugR-.js:676-700
function isOpenAiModelsPath(pathname) {
return pathname === "/v1/models" || pathname.startsWith("/v1/models/");
}
function isEmbeddingsPath(pathname) {
return pathname === "/v1/embeddings";
}
function isOpenAiChatCompletionsPath(pathname) {
return pathname === "/v1/chat/completions";
}
function isOpenResponsesPath(pathname) {
return pathname === "/v1/responses";
}
function isToolsInvokePath(pathname) {
return pathname === "/tools/invoke";
}
function isManagedOutgoingImagePath(pathname) {
return pathname.startsWith("/api/chat/media/outgoing/");
}
function isSessionKillPath(pathname) {
return /^\/sessions\/[^/]+\/kill$/.test(pathname);
}
function isSessionHistoryPath(pathname) {
return /^\/sessions\/[^/]+\/history$/.test(pathname);
}
设计特点:
- • 每个路由都有独立的判断函数,清晰可维护
- • 支持精确匹配(
===)和前缀匹配(startsWith) - • 正则表达式用于动态路径(如
/sessions/{id}/kill)
三、OpenAI 兼容 API 详解
3.1 /v1/models —— 模型列表
源码位置:dist/models-http-CoMLW6oP.js
async function handleOpenAiModelsHttpRequest(req, res, opts) {
// 1. 路径检查
const requestPath = resolveRequestPath(req);
if (requestPath !== "/v1/models" && !requestPath.startsWith("/v1/models/"))
return false;
// 2. 方法检查
if (req.method !== "GET") {
sendMethodNotAllowed(res, "GET");
return true;
}
// 3. 认证
const requestAuth = await authorizeRequest(req, res, opts);
if (!requestAuth) return true;
// 4. 权限检查
const scopeAuth = authorizeOperatorScopesForMethod("models.list", ...);
if (!scopeAuth.allowed) {
sendMissingScopeForbidden(res, scopeAuth.missingScope);
return true;
}
// 5. 加载模型列表
const ids = loadAgentModelIds();
// 6. 返回模型列表
if (requestPath === "/v1/models") {
sendJson(res, 200, {
object: "list",
data: ids.map(toOpenAiModel)
});
return true;
}
// 7. 返回单个模型详情
const encodedId = requestPath.slice(11); // 去掉 "/v1/models/"
...
}
模型 ID 格式:
function loadAgentModelIds() {
const cfg = getRuntimeConfig();
const defaultAgentId = resolveDefaultAgentId(cfg);
const ids = new Set([OPENCLAW_MODEL_ID, OPENCLAW_DEFAULT_MODEL_ID]);
// 格式:openclaw/{agentId}
ids.add(`openclaw/${defaultAgentId}`);
for (const agentId of listAgentIds(cfg)) {
ids.add(`openclaw/${agentId}`);
}
return Array.from(ids);
}
返回示例:
{
"object": "list",
"data": [
{
"id": "openclaw",
"object": "model",
"created": 0,
"owned_by": "openclaw",
"permission": []
},
{
"id": "openclaw/marketing-content-creator",
"object": "model",
"created": 0,
"owned_by": "openclaw",
"permission": []
}
]
}
3.2 /v1/chat/completions —— 聊天补全

源码位置:dist/openai-http-BAjJ0ljy.js:461
这是整个 HTTP API 最复杂的端点,支持:
- • 普通请求(非流式)
- • SSE 流式响应
- • 工具调用(function calling)
- • 图片输入
- • 多轮对话
请求处理流程
async function handleOpenAiHttpRequest(req, res, opts) {
// 1. 解析限流配置
const limits = resolveOpenAiChatCompletionsLimits(opts.config);
// 2. 处理 POST JSON 请求(通用辅助函数)
const handled = await handleGatewayPostJsonEndpoint(req, res, {
pathname: "/v1/chat/completions",
requiredOperatorMethod: "chat.send",
resolveOperatorScopes: resolveOpenAiCompatibleHttpOperatorScopes,
auth: opts.auth,
maxBodyBytes: opts.maxBodyBytes ?? limits.maxBodyBytes
});
if (handled === false) return false;
if (!handled) return true;
// 3. 检查模型覆盖权限
const modelOverrideAuth = authorizeOpenAiCompatibleHttpModelOverride(...);
// 4. 解析请求体
const payload = coerceRequest(handled.body);
const stream = Boolean(payload.stream);
const model = typeof payload.model === "string" ? payload.model : "openclaw";
// 5. 解析采样参数
const maxTokens = payload.max_completion_tokens ?? payload.max_tokens;
const temperature = payload.temperature;
const topP = payload.top_p;
...
// 6. 解析工具调用
const { tools, toolChoice } = payload;
const { resolvedClientTools, toolChoicePrompt, toolChoiceConstraint } = applyChatToolChoice({ tools, toolChoice });
// 7. 解析图片
const images = await resolveImagesForRequest(activeTurnContext, limits);
// 8. 构建 Agent 命令输入
const commandInput = buildAgentCommandInput({
prompt: { message, extraSystemPrompt, images },
clientTools: resolvedClientTools,
modelOverride,
sessionKey,
runId,
...
});
// 9. 发送命令到 Agent
const result = await sendAgentCommand(commandInput);
// 10. 返回响应(流式或非流式)
if (stream) {
return await sendOpenAiStreamResponse(res, result, ...);
} else {
return await sendOpenAiJsonResponse(res, result, ...);
}
}
关键设计:
- • 第 2 步:
handleGatewayPostJsonEndpoint是通用辅助函数,处理认证、权限、请求体解析 - • 第 3 步:模型覆盖权限检查——防止用户通过
model参数切换到未授权的 Agent - • 第 6 步:工具调用解析——将 OpenAI 的
tools/tool_choice格式转换为 OpenClaw 内部格式 - • 第 7 步:图片解析——支持 base64 和 URL 两种格式
- • 第 10 步:流式响应使用 SSE(Server-Sent Events),非流式返回完整 JSON
流式响应示例
HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1698140000,"model":"openclaw","choices":[{"index":0,"delta":{"role":"assistant"},"finish_reason":null}]}
data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1698140000,"model":"openclaw","choices":[{"index":0,"delta":{"content":"你好"},"finish_reason":null}]}
data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1698140000,"model":"openclaw","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}
data: [DONE]
四、插件路由系统

4.1 插件如何注册路由
插件通过 gateway.http API 注册自定义路由:
// 插件代码示例
export default definePlugin({
name: "my-plugin",
setup(ctx) {
ctx.gateway.http.get("/plugin/my-plugin/status", async (req, res) => {
res.json({ status: "ok", version: "1.0.0" });
});
ctx.gateway.http.post("/plugin/my-plugin/webhook", async (req, res) => {
const payload = req.body;
// 处理 webhook
res.json({ received: true });
});
}
});
4.2 插件路由的优先级
插件路由在阶段管道中的位置:
// 系统路由优先
requestStages.push(
{ name: "gateway-probes", ... },
{ name: "hooks", ... },
{ name: "models", ... },
{ name: "chat-completions", ... },
// ... 更多系统路由
);
// 插件路由在后
requestStages.push(...buildPluginRequestStages(...));
// 最后是 404
设计意图:
- • 系统路由(如
/v1/chat/completions)不能被插件覆盖 - • 插件路由以
/plugin/{pluginName}/为前缀,避免冲突 - • 插件可以注册钩子(hooks)在系统路由之前执行
4.3 插件节点能力路由
特殊的路由格式:/node/{capability}/{command}
// 示例:调用节点的摄像头
POST /node/camera/snap
{
"target": "ios-device-001",
"params": { "quality": "high" }
}
处理流程:
- 1. Gateway 解析 capability 和 command
- 2. 查找具有该 capability 的在线 node
- 3. 通过 WebSocket 转发命令到 node
- 4. Node 执行后返回结果
- 5. Gateway 将结果返回给 HTTP 客户端
五、认证与限流
5.1 统一认证体系
所有 HTTP 路由共享同一套认证:
async function authorizeRequest(req, res, opts) {
// 1. 提取认证信息
const auth = extractAuthFromRequest(req);
// 2. 验证 token
const validated = await validateToken(auth.token);
if (!validated) {
sendGatewayAuthFailure(res, { ok: false, reason: "unauthorized" });
return null;
}
// 3. 检查 scopes
const scopes = validated.scopes;
if (!scopes.includes("operator.read")) {
sendMissingScopeForbidden(res, "operator.read");
return null;
}
return validated;
}
认证方式:
- • Bearer Token:
Authorization: Bearer {token} - • API Key:
X-API-Key: {key} - • 共享密钥:
Authorization: Shared {secret}(内部使用)
5.2 限流机制
const limits = resolveOpenAiChatCompletionsLimits(config);
// {
// maxBodyBytes: 10485760, // 10 MB
// maxRequestsPerMinute: 60,
// maxTokensPerRequest: 4096
// }
限流维度:
- • 请求体大小(默认 10 MB)
- • 每分钟请求数(默认 60)
- • 每请求最大 token 数(默认 4096)
- • 并发流式连接数
🎯 总结
OpenClaw 的 HTTP API 路由系统体现了几个核心设计原则:
- 1. 优先级明确 —— 系统路由优先,精确匹配优先,防止冲突
- 2. 阶段管道 —— 数组顺序即优先级,按需加载,短路返回
- 3. OpenAI 兼容 —— 零成本迁移,任何支持 OpenAI API 的工具都能用
- 4. 插件可扩展 —— 插件注册自己的路由,不侵入核心代码
- 5. 认证统一 —— 所有路由共享认证,不重复造轮子
- 6. 限流保护 —— 多维度限流,防止滥用
理解这个路由系统,你就能:
- • ✅ 排查 API 调用失败、路由冲突、认证问题
- • ✅ 开发支持 OpenAI API 的第三方工具
- • ✅ 编写插件扩展 Gateway 能力
- • ✅ 优化 Gateway 性能和安全性
💬 互动话题
你在使用 OpenClaw 的 HTTP API 时遇到过哪些问题?是路由冲突、认证失败,还是流式响应异常?欢迎在评论区分享你的排查经验 👇
📚 系列文章:OpenClaw 入门指南 | M1 Gateway 网关服务
上一篇:[OG_M1_002] WebSocket 协议设计:connect → hello-ok → req/res 全流程
下一篇:[OG_M1_004] 热重载机制:不停机更新 Gateway 配置
关注「云境易贸」公众号,回复 “OpenClaw” 获取完整源码解析系列。
夜雨聆风