Skip to main content

Documentation Index

Fetch the complete documentation index at: https://openclaw.zhcndoc.com/llms.txt

Use this file to discover all available pages before exploring further.

本指南将带你构建一个为 OpenClaw 添加模型提供方(LLM)的插件。到最后,你将拥有一个带有模型目录、API 密钥认证和动态模型解析能力的提供方。
如果你之前没有构建过任何 OpenClaw 插件,请先阅读 入门指南,了解基本的软件包 结构和清单配置。
提供方插件会将模型添加到 OpenClaw 的常规推理循环中。如果模型 必须通过一个原生代理守护进程运行,并由该守护进程负责线程、压缩或工具 事件,那么应将该提供方与一个 agent harness 配对使用,而不是把守护进程协议细节放进核心中。

操作流程

1

Package and manifest

Step 1: Package and manifest

{
  "name": "@myorg/openclaw-acme-ai",
  "version": "1.0.0",
  "type": "module",
  "openclaw": {
    "extensions": ["./index.ts"],
    "providers": ["acme-ai"],
    "compat": {
      "pluginApi": ">=2026.3.24-beta.2",
      "minGatewayVersion": "2026.3.24-beta.2"
    },
    "build": {
      "openclawVersion": "2026.3.24-beta.2",
      "pluginSdkVersion": "2026.3.24-beta.2"
    }
  }
}
清单中声明 providerAuthEnvVars,这样 OpenClaw 就可以在不加载你的插件运行时的情况下检测 凭据。当某个提供方变体应复用另一个提供方 id 的认证时,请添加 providerAuthAliasesmodelSupport 是可选项,它允许 OpenClaw 在运行时钩子出现之前,就通过诸如 acme-large 这样的简写模型 id 自动加载你的提供方插件。如果你把该 提供方发布到 ClawHub,则 package.json 中的这些 openclaw.compatopenclaw.build 字段 是必需的。
2

注册提供方

一个最小的提供方需要 idlabelauthcatalog
index.ts
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth";

export default definePluginEntry({
  id: "acme-ai",
  name: "Acme AI",
  description: "Acme AI 模型提供方",
  register(api) {
    api.registerProvider({
      id: "acme-ai",
      label: "Acme AI",
      docsPath: "/providers/acme-ai",
      envVars: ["ACME_AI_API_KEY"],

      auth: [
        createProviderApiKeyAuthMethod({
          providerId: "acme-ai",
          methodId: "api-key",
          label: "Acme AI API 密钥",
          hint: "来自 Acme AI 控制台的 API 密钥",
          optionKey: "acmeAiApiKey",
          flagName: "--acme-ai-api-key",
          envVar: "ACME_AI_API_KEY",
          promptMessage: "输入你的 Acme AI API 密钥",
          defaultModel: "acme-ai/acme-large",
        }),
      ],

      catalog: {
        order: "simple",
        run: async (ctx) => {
          const apiKey =
            ctx.resolveProviderApiKey("acme-ai").apiKey;
          if (!apiKey) return null;
          return {
            provider: {
              baseUrl: "https://api.acme-ai.com/v1",
              apiKey,
              api: "openai-completions",
              models: [
                {
                  id: "acme-large",
                  name: "Acme Large",
                  reasoning: true,
                  input: ["text", "image"],
                  cost: { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
                  contextWindow: 200000,
                  maxTokens: 32768,
                },
                {
                  id: "acme-small",
                  name: "Acme Small",
                  reasoning: false,
                  input: ["text"],
                  cost: { input: 1, output: 5, cacheRead: 0.1, cacheWrite: 1.25 },
                  contextWindow: 128000,
                  maxTokens: 8192,
                },
              ],
            },
          };
        },
      },
    });
  },
});
这就是一个可工作的提供方。现在用户可以运行 openclaw onboard --acme-ai-api-key <key> 并选择 acme-ai/acme-large 作为他们的模型。如果上游提供方使用的控制 token 与 OpenClaw 不同,不要替换流路径,而是添加一个 小的双向文本转换:
api.registerTextTransforms({
  input: [
    { from: /red basket/g, to: "blue basket" },
    { from: /paper ticket/g, to: "digital ticket" },
    { from: /left shelf/g, to: "right shelf" },
  ],
  output: [
    { from: /blue basket/g, to: "red basket" },
    { from: /digital ticket/g, to: "paper ticket" },
    { from: /right shelf/g, to: "left shelf" },
  ],
});
input 会在传输前重写最终的系统提示词和文本消息内容。output 会在 OpenClaw 解析 自己的控制标记或通道投递之前,重写助手文本增量和最终文本。对于只注册一个文本提供方、使用 API 密钥认证并且只有一个基于目录的运行时的打包提供方, 优先使用更窄的 defineSingleProviderPluginEntry(...) 辅助函数:
import { defineSingleProviderPluginEntry } from "openclaw/plugin-sdk/provider-entry";

export default defineSingleProviderPluginEntry({
  id: "acme-ai",
  name: "Acme AI",
  description: "Acme AI 模型提供方",
  provider: {
    label: "Acme AI",
    docsPath: "/providers/acme-ai",
    auth: [
      {
        methodId: "api-key",
        label: "Acme AI API 密钥",
        hint: "来自 Acme AI 控制台的 API 密钥",
        optionKey: "acmeAiApiKey",
        flagName: "--acme-ai-api-key",
        envVar: "ACME_AI_API_KEY",
        promptMessage: "输入你的 Acme AI API 密钥",
        defaultModel: "acme-ai/acme-large",
      },
    ],
    catalog: {
      buildProvider: () => ({
        api: "openai-completions",
        baseUrl: "https://api.acme-ai.com/v1",
        models: [{ id: "acme-large", name: "Acme Large" }],
      }),
      buildStaticProvider: () => ({
        api: "openai-completions",
        baseUrl: "https://api.acme-ai.com/v1",
        models: [{ id: "acme-large", name: "Acme Large" }],
      }),
    },
  },
});
buildProvider 是动态目录路径,在 OpenClaw 能解析真实提供方认证时使用。它可以执行 提供方特定的发现逻辑。buildStaticProvider 仅用于离线行,这些内容在认证配置完成之前也应是安全可展示的; 它不能依赖凭据,也不能发起网络请求。OpenClaw 的 models list --all 当前只会为打包的提供方插件执行静态目录, 并且配置为空、环境变量为空,也没有 agent/workspace 路径。如果你的认证流程还需要在 onboarding 期间修补 models.providers.*、别名和 agent 默认模型, 请使用 openclaw/plugin-sdk/provider-onboard 中的预设辅助函数。最小粒度的辅助函数有 createDefaultModelPresetAppliers(...)createDefaultModelsPresetAppliers(...)createModelCatalogPresetAppliers(...)当某个提供方的原生端点在常规 openai-completions 传输上支持流式 usage 块时, 应优先使用 openclaw/plugin-sdk/provider-catalog-shared 中共享的目录辅助函数,而不是硬编码 提供方 id 判断。supportsNativeStreamingUsageCompat(...)applyProviderNativeStreamingUsageCompat(...) 会根据端点能力映射检测支持情况,因此即使插件使用了自定义提供方 id, 原生 Moonshot/DashScope 风格端点也仍然可以接入。
3

添加动态模型解析

如果你的提供方接受任意模型 ID(例如代理或路由器),请添加 resolveDynamicModel
api.registerProvider({
  // ... 上面的 id、label、auth、catalog

  resolveDynamicModel: (ctx) => ({
    id: ctx.modelId,
    name: ctx.modelId,
    provider: "acme-ai",
    api: "openai-completions",
    baseUrl: "https://api.acme-ai.com/v1",
    reasoning: false,
    input: ["text"],
    cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
    contextWindow: 128000,
    maxTokens: 8192,
  }),
});
如果解析需要网络请求,请使用 prepareDynamicModel 做异步预热 —— resolveDynamicModel 会在其完成后再次运行。
4

添加运行时钩子(按需)

大多数提供方只需要 catalog + resolveDynamicModel。随着你的提供方需要,再逐步添加钩子。现在共享辅助构建器已经覆盖了最常见的 replay/tool-compat 家族,因此插件通常不需要逐个手工连接每个钩子:
import { buildProviderReplayFamilyHooks } from "openclaw/plugin-sdk/provider-model-shared";
import { buildProviderStreamFamilyHooks } from "openclaw/plugin-sdk/provider-stream";
import { buildProviderToolCompatFamilyHooks } from "openclaw/plugin-sdk/provider-tools";

const GOOGLE_FAMILY_HOOKS = {
  ...buildProviderReplayFamilyHooks({ family: "google-gemini" }),
  ...buildProviderStreamFamilyHooks("google-thinking"),
  ...buildProviderToolCompatFamilyHooks("gemini"),
};

api.registerProvider({
  id: "acme-gemini-compatible",
  // ...
  ...GOOGLE_FAMILY_HOOKS,
});
当前可用的 replay 家族:
Family作用打包示例
openai-compatible面向 OpenAI 兼容传输的共享 OpenAI 风格 replay 策略,包括 tool-call-id 清理、assistant-first 顺序修复,以及在传输需要时进行通用的 Gemini turn 校验moonshot, ollama, xai, zai
anthropic-by-modelmodelId 选择的 Claude 感知 replay 策略,因此只有在解析出的模型确实是 Claude id 时,Anthropic 消息传输才会获得 Claude 专属的 thinking-block 清理amazon-bedrock, anthropic-vertex
google-gemini原生 Gemini replay 策略,加上 bootstrap replay 清理和标记的 reasoning 输出模式google, google-gemini-cli
passthrough-gemini用于通过 OpenAI 兼容代理传输运行的 Gemini 模型的 Gemini thought-signature 清理;不会启用原生 Gemini replay 校验或 bootstrap 重写openrouter, kilocode, opencode, opencode-go
hybrid-anthropic-openai适用于在一个插件中混合 Anthropic 消息和 OpenAI 兼容模型表面的混合策略;可选的仅 Claude thinking-block 删除仍然只作用于 Anthropic 一侧minimax
当前可用的 stream 家族:
Family作用打包示例
google-thinking共享流路径上的 Gemini thinking 负载规范化google, google-gemini-cli
kilocode-thinking共享代理流路径上的 Kilo reasoning 包装器,对 kilo/auto 和不受支持的代理 reasoning ids 跳过注入的 thinkingkilocode
moonshot-thinking基于配置 + /think 级别的 Moonshot 二进制原生 thinking 负载映射moonshot
minimax-fast-mode共享流路径上的 MiniMax fast-mode 模型重写minimax, minimax-portal
openai-responses-defaults共享的原生 OpenAI/Codex Responses 包装器:归因 headers、/fast/serviceTier、文本详细程度、原生 Codex web search、reasoning-compat 负载塑形,以及 Responses 上下文管理openai, openai-codex
openrouter-thinking面向代理路由的 OpenRouter reasoning 包装器,不支持的模型/auto 跳过由中心处理openrouter
tool-stream-default-on面向 Z.AI 等希望默认启用工具流的提供方的 tool_stream 包装器,除非显式禁用zai
每个家族构建器都由同一软件包导出的更底层公共辅助函数组合而成;当某个提供方需要脱离通用模式时, 你可以直接使用这些函数:
  • openclaw/plugin-sdk/provider-model-sharedProviderReplayFamilybuildProviderReplayFamilyHooks(...),以及原始 replay 构建器(buildOpenAICompatibleReplayPolicybuildAnthropicReplayPolicyForModelbuildGoogleGeminiReplayPolicybuildHybridAnthropicOrOpenAIReplayPolicy)。还导出 Gemini replay 辅助函数(sanitizeGoogleGeminiReplayHistoryresolveTaggedReasoningOutputMode)以及端点/模型辅助函数(resolveProviderEndpointnormalizeProviderIdnormalizeGooglePreviewModelIdnormalizeNativeXaiModelId)。
  • openclaw/plugin-sdk/provider-streamProviderStreamFamilybuildProviderStreamFamilyHooks(...)composeProviderStreamWrappers(...),以及共享的 OpenAI/Codex 包装器(createOpenAIAttributionHeadersWrappercreateOpenAIFastModeWrappercreateOpenAIServiceTierWrappercreateOpenAIResponsesContextManagementWrappercreateCodexNativeWebSearchWrapper)、DeepSeek V4 OpenAI 兼容包装器(createDeepSeekV4OpenAICompatibleThinkingWrapper)、Anthropic Messages thinking prefill 清理(createAnthropicThinkingPrefillPayloadWrapper),以及共享代理/提供方包装器(createOpenRouterWrappercreateToolStreamWrappercreateMinimaxFastModeWrapper)。
  • openclaw/plugin-sdk/provider-toolsProviderToolCompatFamilybuildProviderToolCompatFamilyHooks("gemini")、底层 Gemini schema 辅助函数(normalizeGeminiToolSchemasinspectGeminiToolSchemas),以及 xAI 兼容辅助函数(resolveXaiModelCompatPatch()applyXaiModelCompat(model))。打包的 xAI 插件使用 normalizeResolvedModel + contributeResolvedModelCompat 来确保 xAI 规则由该提供方自身拥有。
某些 stream 辅助函数会刻意保持为提供方本地私有。@openclaw/anthropic-providerwrapAnthropicProviderStreamresolveAnthropicBetasresolveAnthropicFastModeresolveAnthropicServiceTier 以及更底层的 Anthropic 包装器构建器保留在自己的公共 api.ts / contract-api.ts 接口边界内,因为它们编码了 Claude OAuth beta 处理和 context1m 门控。xAI 插件也将原生 xAI Responses 形状保留在自己的 wrapStreamFn 中(/fast 别名、默认 tool_stream、不支持的 strict-tool 清理、xAI 特定的 reasoning 负载移除)。同样的 package-root 模式也支撑着 @openclaw/openai-provider(提供方构建器、默认模型辅助函数、realtime 提供方构建器)以及 @openclaw/openrouter-provider(提供方构建器加 onboarding/config 辅助函数)。
对于需要在每次推理调用前进行 token 交换的提供方:
prepareRuntimeAuth: async (ctx) => {
  const exchanged = await exchangeToken(ctx.apiKey);
  return {
    apiKey: exchanged.token,
    baseUrl: exchanged.baseUrl,
    expiresAt: exchanged.expiresAt,
  };
},
OpenClaw 会按以下顺序调用这些钩子。大多数提供方只会用到 2-3 个: OpenClaw 不再调用的仅兼容字段,例如 ProviderPlugin.capabilitiessuppressBuiltInModel,不在此列出。
#Hook适用场景
1catalog模型目录或 base URL 默认值
2applyConfigDefaults配置实例化期间由提供方拥有的全局默认值
3normalizeModelId查找前清理旧版/预览版 model-id 别名
4normalizeTransport生成通用模型前清理提供方家族 api / baseUrl
5normalizeConfig规范化 models.providers.<id> 配置
6applyNativeStreamingUsageCompat配置提供方的原生流式 usage 兼容重写
7resolveConfigApiKey提供方拥有的 env-marker 认证解析
8resolveSyntheticAuth本地/自托管或由配置支持的合成认证
9shouldDeferSyntheticProfileAuth在 env/config 认证之下延后合成存储的 profile 占位符
10resolveDynamicModel接受任意上游模型 ID
11prepareDynamicModel解析前的异步元数据获取
12normalizeResolvedModel运行器之前的传输重写
13contributeResolvedModelCompat位于另一个兼容传输之后的厂商模型兼容标志
14normalizeToolSchemas注册前由提供方拥有的 tool-schema 清理
15inspectToolSchemas由提供方拥有的 tool-schema 诊断
16resolveReasoningOutputMode标记式 vs 原生 reasoning-output 协议
17prepareExtraParams默认请求参数
18createStreamFn完全自定义的 StreamFn 传输
19wrapStreamFn常规流路径上的自定义请求头/请求体包装器
20resolveTransportTurnState原生的每轮请求头/元数据
21resolveWebSocketSessionPolicy原生 WS 会话头/冷却时间
22formatApiKey自定义运行时 token 形状
23refreshOAuth自定义 OAuth 刷新
24buildAuthDoctorHint认证修复指引
25matchesContextOverflowError提供方拥有的溢出检测
26classifyFailoverReason提供方拥有的限流/过载分类
27isCacheTtlEligible提示缓存 TTL 门控
28buildMissingAuthMessage自定义缺失认证提示
29augmentModelCatalog合成的前向兼容行
30resolveThinkingProfile模型特定的 /think 选项集
31isBinaryThinking二进制 thinking 开/关兼容性
32supportsXHighThinkingxhigh reasoning 支持兼容性
33resolveDefaultThinkingLevel默认 /think 策略兼容性
34isModernModelRef线上/冒烟测试模型匹配
35prepareRuntimeAuth推理前的 token 交换
36resolveUsageAuth自定义用量凭据解析
37fetchUsageSnapshot自定义用量端点
38createEmbeddingProvider面向 memory/search 的提供方拥有 embedding 适配器
39buildReplayPolicy自定义对话回放/压缩策略
40sanitizeReplayHistory通用清理之后的提供方特定回放重写
41validateReplayTurns嵌入式运行器之前的严格 replay-turn 校验
42onModelSelected选择后的回调(例如遥测)
运行时回退说明:
  • normalizeConfig 会先检查匹配到的提供方,然后再检查其他支持钩子的提供方插件,直到有一个真正修改了配置。如果没有任何提供方钩子重写受支持的 Google 家族配置项,打包的 Google 配置规范化器仍会生效。
  • resolveConfigApiKey 会在暴露时使用提供方钩子。打包的 amazon-bedrock 路径在这里也有内置的 AWS env-marker 解析器,尽管 Bedrock 运行时认证本身仍然使用 AWS SDK 默认链。
  • resolveSystemPromptContribution 允许提供方为某个模型家族注入支持缓存感知的系统提示指导。若行为属于某个单独的提供方/模型家族,并且应保留稳定/动态缓存拆分,则应优先使用它,而不是 before_prompt_build
有关详细说明和真实示例,请参见 内部机制:提供方运行时钩子
5

Add extra capabilities (optional)

Step 5: Add extra capabilities

一个提供方插件可以在文本推理之外,同时注册语音、实时转写、实时语音、 媒体理解、图像生成、视频生成、网页抓取和网页搜索。OpenClaw 将这类插件归类为 hybrid-capability 插件——这是公司插件的推荐模式(每个供应商一个插件)。 参见 内部机制:能力所有权register(api) 中与你现有的 api.registerProvider(...) 调用并列注册每种能力。只选择你需要的选项卡:
import {
  assertOkOrThrowProviderError,
  postJsonRequest,
} from "openclaw/plugin-sdk/provider-http";

api.registerSpeechProvider({
  id: "acme-ai",
  label: "Acme Speech",
  isConfigured: ({ config }) => Boolean(config.messages?.tts),
  synthesize: async (req) => {
    const { response, release } = await postJsonRequest({
      url: "https://api.example.com/v1/speech",
      headers: new Headers({ "Content-Type": "application/json" }),
      body: { text: req.text },
      timeoutMs: req.timeoutMs,
      fetchFn: fetch,
      auditContext: "acme speech",
    });
    try {
      await assertOkOrThrowProviderError(response, "Acme Speech API error");
      return {
        audioBuffer: Buffer.from(await response.arrayBuffer()),
        outputFormat: "mp3",
        fileExtension: ".mp3",
        voiceCompatible: false,
      };
    } finally {
      await release();
    }
  },
});
对提供方 HTTP 失败请使用 assertOkOrThrowProviderError(...),这样插件可以共享 有上限的错误正文读取、JSON 错误解析和 request-id 后缀。
6

Test

Step 6: Test

src/provider.test.ts
import { describe, it, expect } from "vitest";
// 从 index.ts 或一个专门的文件中导出你的提供方配置对象
import { acmeProvider } from "./provider.js";

describe("acme-ai provider", () => {
  it("resolves dynamic models", () => {
    const model = acmeProvider.resolveDynamicModel!({
      modelId: "acme-beta-v3",
    } as any);
    expect(model.id).toBe("acme-beta-v3");
    expect(model.provider).toBe("acme-ai");
  });

  it("returns catalog when key is available", async () => {
    const result = await acmeProvider.catalog!.run({
      resolveProviderApiKey: () => ({ apiKey: "test-key" }),
    } as any);
    expect(result?.provider?.models).toHaveLength(2);
  });

  it("returns null catalog when no key", async () => {
    const result = await acmeProvider.catalog!.run({
      resolveProviderApiKey: () => ({ apiKey: undefined }),
    } as any);
    expect(result).toBeNull();
  });
});

发布到 ClawHub

提供方插件与其他外部代码插件的发布方式相同:
clawhub package publish your-org/your-plugin --dry-run
clawhub package publish your-org/your-plugin
这里不要使用旧的仅技能发布别名;插件包应使用 clawhub package publish

文件结构

<bundled-plugin-root>/acme-ai/
├── package.json              # openclaw.providers 元数据
├── openclaw.plugin.json      # 带有提供者认证元数据的清单
├── index.ts                  # definePluginEntry + registerProvider
└── src/
    ├── provider.test.ts      # 测试
    └── usage.ts              # 使用端点(可选)

目录顺序参考

catalog.order 控制你的目录与内置提供者合并的相对时机:
顺序时机使用场景
simple第一轮纯 API 密钥提供者
profilesimple 之后由认证配置文件控制的提供者
pairedprofile 之后合成多个相关条目
late最后一轮覆盖现有提供者(冲突时获胜)

下一步

相关内容