乐于分享
好东西不私藏

OpenClaw 开发系列:第8篇-运行时 API — 深入 api.runtime

OpenClaw 开发系列:第8篇-运行时 API — 深入 api.runtime

前置阅读:第 1 篇 — Plugin SDK 入门

本篇讲解 api.runtime 命名空间——插件在运行时可以调用的辅助能力。这些能力包括持久化存储、配置管理、审批系统、HTTP 请求、TTS、媒体理解、子代理等。理解 api.runtime,就是理解插件在运行环境中「能做什么」。


01

api.runtime 概览

api.runtime 是 register(api) 回调中 api 对象的一个字段,提供了一组运行时辅助功能。在前面的篇章中,我们学习了各种 api.registerXxx() 方法——它们用来「注册能力」。而 api.runtime 中的方法不是用来注册能力的,而是在插件的执行逻辑(如工具的 execute 函数、钩子的处理器等)中直接调用的。

可以将 api.runtime 理解为插件的后台工具箱——它让插件能够完成存储数据、发起网络请求、调用其他 AI 能力等操作。没有 api.runtime,插件就只能注册能力但不能做任何需要与外部世界交互的事情。


02

持久化存储

插件经常需要在运行期间存储和读取数据——用户偏好、缓存、会话状态等。api.runtime 提供了存储管理能力。

createPluginRuntimeStore

createPluginRuntimeStore 创建一个键值存储实例。第一个参数是存储的名称(同一个插件可以创建多个命名存储):

const store = api.runtime.createPluginRuntimeStore("my-store");// 写入await store.set("user-preferences", { theme: "dark", language: "zh" });// 读取const prefs = await store.get("user-preferences");// => { theme: "dark", language: "zh" }// 删除await store.delete("user-preferences");// 检查是否存在const exists = await store.has("user-preferences");// 获取所有键const keys = await store.keys();

关键特性

  • 作用域隔离:每个插件的存储相互隔离,不会互相覆盖

  • 持久化:数据在 Gateway 重启后依然存在

  • 类型安全:可以通过泛型指定值的类型

interface UserPrefs {  theme: "dark" | "light";  language: string;  lastSeen?: number;}const store = api.runtime.createPluginRuntimeStore<UserPrefs>("user-prefs");// TypeScript 会对值类型进行检查await store.set("current", { theme: "dark", language: "zh" });const prefs = await store.get("current"); // 类型为 UserPrefs | undefined

使用场景

  • 缓存 API 响应,减少重复请求

  • 存储用户配置的默认值

  • 维护插件级别的状态(如认证 token 的刷新时间)

  • 记录使用统计


03

配置管理

api 对象本身就提供了配置访问能力(在前面的篇章中我们已经用过 api.pluginConfig),这里做一个系统的总结。

配置层次

// 全局配置快照(只读)const globalConfig = api.config;// => OpenClawConfig 对象,包含所有配置项// 插件专属配置(从 plugins.entries.<id>.config 读取)const myConfig = api.pluginConfig;// => Record<string, unknown>,需要在 openclaw.plugin.json 的 configSchema 中定义 Schema

配置读取

interface MyPluginConfig {  apiUrlstring;  maxRetriesnumber;  features: {    enabledboolean;    limitnumber;  };}const config = api.pluginConfig as MyPluginConfig;const url = config.apiUrl;const retries = config.maxRetries ?? 3;

配置安全性

api.pluginConfig 中的值已经过 openclaw.plugin.json 中 configSchema 的 JSON Schema 验证。如果用户提供了不符合 Schema 的配置值,系统会在启动时拒绝并报错,插件代码不会执行。

这意味着在插件内部读取 api.pluginConfig 时,可以假设数据格式是正确的——不需要额外的运行时校验。


04

审批系统

某些工具的操作可能有副作用或安全风险(如执行 shell 命令、发送消息、删除文件等),需要用户确认后才能执行。api.runtime.requestApproval() 让插件可以主动请求用户审批:

执行审批

// 请求用户审批const approval = await api.runtime.requestApproval({  type"tool_execution",  tool"exec",  description"执行 shell 命令: ls -la",  risk"medium",  params: { command"ls -la" },});if(approval.granted) {  // 用户批准,执行操作  const result = await executeCommand("ls -la");else {  // 用户拒绝  api.logger.info("User declined approval");}

审批超时

审批请求可以有超时时间:

const approval = await api.runtime.requestApproval({  type"tool_execution",  description: "删除文件",  timeout: 30000// 30 秒超时  // ...});if (approval.timedOut) {  // 超时处理  return { content: [{ type"text", text: "审批超时,操作已取消" }] };}

05

HTTP 请求

插件经常需要与外部服务通信。api.runtime 提供了封装的 HTTP 请求工具。

基础请求

const response = await api.runtime.fetch("https://api.example.com/data", {  method"GET",  headers: {    "Authorization"`Bearer ${token}`,    "Content-Type""application/json",  },});const data = await response.json();

代理支持

OpenClaw 网关的代理配置会自动应用到 api.runtime.fetch

// 如果用户在 OpenClaw 配置中设置了 HTTP 代理,// api.runtime.fetch 会自动使用该代理// 插件无需手动处理代理配置const response = await api.runtime.fetch("https://api.example.com/data");

固定查找

某些 API 可能有多个可用端点(如 CDN 节点),api.runtime 提供了固定查找功能来选择最优端点:

// 自动选择延迟最低的端点const response = await api.runtime.fetchWithPinpoint({  urls: [    "https://cdn1.example.com/api",    "https://cdn2.example.com/api",    "https://cdn3.example.com/api",  ],  options: { method"GET" },});

06

错误处理与重试

错误图

api.runtime 提供了结构化的错误处理工具:

import { PluginError } from "openclaw/plugin-sdk/errors";// 创建带分类的错误throw new PluginError({  code"API_ERROR",  message"Failed to call external API",  details: { statusCode500endpoint"/data" },  recoverabletrue// 标记为可恢复错误});

重试机制

const result = await api.runtime.retry(  async () => {    const response = await fetch("https://api.example.com/data");    if (!response.okthrow new Error(`HTTP ${response.status}`);    return response.json();  },  {    maxAttempts3,    delay1000,        // 初始延迟 1 秒    backoff"exponential"// 指数退避    retryOn: [500502503504], // 对这些状态码重试  });

重试配置选项



07

TTS 与媒体理解

api.runtime 提供了对其他 AI 能力的便捷访问接口。这些能力由对应的 Provider 提供(参见第 3 篇),api.runtime 只是封装了调用方式。如果对应的 Provider 没有注册,调用会失败。

文本转语音

const audioBuffer = await api.runtime.tts.synthesize(  "Hello, this is a test message.",  {    voice"alloy",    speed1.0,  });// audioBuffer 可以直接发送到支持语音的通道

媒体理解

const description = await api.runtime.mediaUnderstanding.analyze({  type"image",  url"https://example.com/image.jpg",  prompt"Describe this image in detail.",});// => "A white cat sitting on a wooden desk..."
支持的媒体类型

网络搜索

const searchResults = await api.runtime.webSearch.search(  "OpenClaw plugin development guide",  { limit5 });// searchResults 是搜索结果数组for (const result of searchResults) {  console.log(`${result.title}${result.url}`);  console.log(result.snippet);}

08

子代理

插件可以启动子代理(Sub Agent)来处理复杂任务。子代理是一个独立的 Agent 实例——它有自己的对话历史和工具集,不会污染父 Agent 的上下文。适合用于需要独立完成的子任务(如代码审查、数据分析等):
const result = await api.runtime.subAgent.run({  prompt"分析这段代码的安全性问题并提出改进建议。",  context: {    code: userCode,    language"typescript",  },  model"anthropic/claude-sonnet-4-20250514",  tools: ["read""edit"], // 子代理可用的工具  timeout60000,});

子代理的特点

  • 独立上下文:子代理有独立的对话历史,不污染父 Agent 的上下文

  • 工具限制:可以限制子代理只能使用指定的工具

  • 超时控制:可以设置最大执行时间

  • 结构化输出:子代理的返回值可以指定格式


09

日志

api.logger 提供了带作用域的日志功能:
api.logger.debug("Detailed debug info", { requestId"abc123" });api.logger.info("Operation completed", { duration1500 });api.logger.warn("Rate limit approaching", { remaining10 });api.logger.error("API call failed", { error: err.message, code: err.code });
日志自动带上插件 ID 作为前缀,便于在日志中区分不同插件的输出:
[hello-world] INFO Operation completed {"duration":1500}[hello-world] ERROR API call failed {"error":"timeout","code":"ETIMEDOUT"}

10

路径解析

api.resolvePath 用于解析相对于插件根目录的路径:
// 假设插件根目录是 /opt/openclaw/plugins/my-plugin/const templatePath = api.resolvePath("./templates/email.md");// => "/opt/openclaw/plugins/my-plugin/templates/email.md"const configPath = api.resolvePath("../shared/config.json");// => 解析相对于根目录的路径(不是相对于当前文件)

这在插件需要读取自身附带的静态文件(模板、配置、字典等)时非常有用。


11

小结

本篇覆盖了 api.runtime 的核心能力:

  • 持久化存储createPluginRuntimeStore 提供隔离的键值存储

  • 配置管理api.config 和 api.pluginConfig 提供配置访问

  • 审批系统:请求用户确认敏感操作

  • HTTP 请求:封装的 fetch,自动代理,固定查找

  • 错误处理:结构化错误 + 指数退避重试

  • AI 能力调用:TTS、媒体理解、网络搜索、子代理


下一篇:HTTP 路由与 CLI 扩展 — 构建完整基础设施。我们将学习如何为 OpenClaw 网关添加自定义 HTTP 端点和 CLI 子命令。