平台做薄,能力外挂:从接管飞书,看 OpenClaw 优雅的插件架构设计

一、OpenClaw 为什么需要插件

OpenClaw 是一个 AI Agent 平台,它的核心是对话引擎和工具调度。但一个平台不可能内置所有能力------今天要接飞书,明天要接钉钉;今天要查日历,明天要操作数据库。如果每接一个新平台、新能力都要改 OpenClaw 的核心代码,这个架构很快就会崩掉。

所以 OpenClaw 选择了一条经典的路:「平台做薄,能力外挂。」

核心引擎只负责对话管理、模型调用和工具调度,所有跟外部系统打交道的能力------接入哪个消息平台、能操作什么 API------全部通过**「插件」**来扩展。每装一个插件,OpenClaw 就多一项能力;卸掉插件,能力随之消失,核心不受影响。

本文就来拆解这套插件机制是怎么设计的。我们以官方飞书插件(@larksuite/openclaw-lark)为例,从安装到运行,把整个链路讲透。不贴大段源码,只讲你需要理解的核心逻辑。


二、插件到底是什么

先说它"不是"什么

很多人一听"插件",脑子里会蹦出各种架构:

  • 「是不是把插件代码编译进 OpenClaw 主程序?」 不是。

  • 「是不是像微服务一样,插件单独起一个进程?」 不是。

  • 「是不是通过 HTTP/gRPC 远程调用?」 也不是。

它是什么

「插件就是一个普通的 npm 包,被 OpenClaw 主进程动态 import() 加载,运行在同一个 Node.js 进程里。」

你可以类比这些你熟悉的东西:

宿主 扩展形式 加载方式
VS Code 扩展 (.vsix) 同进程加载
Chrome 扩展 (.crx) 独立沙箱进程
Webpack Plugin (npm 包) 同进程加载
「OpenClaw」 「插件 (npm 包)」 「同进程加载」

OpenClaw 的插件跟 VS Code 扩展最像:装进去、加载到宿主进程、共享运行时上下文、宿主控制生命周期。

插件怎么开发:依赖 Plugin SDK

编写插件需要用到 OpenClaw 提供的 「Plugin SDK」openclaw/plugin-sdk)。SDK 定义了插件和平台之间的所有接口------注册频道用什么类型、工具返回什么格式、配置怎么读取。插件在 package.json 里把 openclaw 声明为 peerDependency,开发时依赖它获取类型定义,但不会打包进插件产物里------运行时由 OpenClaw 主程序提供。就像写 React 组件库要依赖 react,但不会把 react 打包进去一样。

插件能做三件事

一个插件可以向 OpenClaw 平台注册三种能力,按"重量"从轻到重:

「1. 注册工具(Tool)------ 最轻量」

给 AI Agent 增加一个可调用的函数。比如飞书插件注册了 feishu_calendar_event 工具,Agent 就能调用它来创建日程。

这就像给 Agent 配了一把螺丝刀------它知道什么时候该用,用的时候调一下插件提供的函数就行。

「2. 注册命令(Command)------ 中等」

给用户提供斜杠命令,比如 /feishu doctor 可以诊断飞书连接状态。这不经过 AI,是用户直接触发的快捷操作。

「3. 注册频道(Channel)------ 最重」

接入一整个消息平台。注册频道意味着插件要负责:消息的收(入站网关)、消息的发(出站适配器)、用户鉴权、消息格式转换......这是全套工程。

飞书插件就是一个注册了完整频道的重量级插件------它不仅注册了 30 多个工具,还接管了飞书消息的整个收发链路。


三、插件的声明与发现:OpenClaw 怎么知道有这个插件

安装

假设你的服务器上已经跑着 OpenClaw,安装飞书插件就一条命令:

go 复制代码
openclaw install @larksuite/openclaw-lark

这条命令背后,OpenClaw 就是用 npm 把这个包下载到自己的插件目录下(类似 ~/.openclaw/extensions/)。跟你在项目里 npm install 一个依赖没有本质区别。

两个声明文件

安装完成后,OpenClaw 怎么知道这个 npm 包是一个合法的插件、提供了什么能力?靠两个声明文件。

「第一个:package.json 里的 openclaw 字段」

go 复制代码
{
  "name": "@larksuite/openclaw-lark",
"openclaw": {
    "extensions": ["./dist/index.mjs"],
    "channel": {
      "id": "openclaw-lark",
      "label": "Feishu",
      "aliases": ["lark"]
    },
    "install": {
      "npmSpec": "@larksuite/openclaw-lark"
    }
  }
}

这个字段告诉平台三件事:

  • 「入口文件在哪」./dist/index.mjs------平台要 import 这个文件

  • 「我是什么频道」:id 叫 openclaw-lark,显示名叫 Feishu,别名 lark

  • 「怎么安装我」 :npm 包名是 @larksuite/openclaw-lark

「第二个:openclaw.plugin.json

go 复制代码
{
  "id": "openclaw-lark",
  "channels": ["feishu"],
  "skills": ["./skills"],
  "configSchema": { ... },
  "channelConfigs": {
    "feishu": { "schema": { "type": "object" } }
  }
}

这个文件告诉平台:

  • 「插件 ID」:唯一标识

  • 「提供了哪些频道」:feishu

  • 「有哪些 AI 技能」 :指向 ./skills 目录,里面有 9 个技能定义(多维表格操作指南、日历管理指南等),这些技能文件(SKILL.md)是给 AI 看的,帮助它理解什么时候该用什么工具、参数怎么填

  • 「配置 Schema」:告诉平台这个频道需要哪些配置项(appId、appSecret、各种策略等)

加载过程

OpenClaw 启动(或执行热重载)时,按这个顺序完成加载:

go 复制代码
① 扫描插件目录
   └── 找到 @larksuite/openclaw-lark

② 读取 package.json 的 openclaw 字段
   └── 拿到入口文件路径:./dist/index.mjs

③ 读取 openclaw.plugin.json
   └── 知道它提供 feishu 频道 + 9 个技能

④ 动态 import('./dist/index.mjs')
   └── 拿到默认导出的 plugin 对象

⑤ 调用 plugin.register(api)
   └── 插件在这个函数里完成所有注册

注意第 ⑤ 步------这是整个插件机制的核心。平台给插件传了一个 api 对象,插件通过这个对象把自己的能力"挂"到平台上。


四、注册机制:插件如何把能力"挂"到平台上

register(api):一切的起点

每个 OpenClaw 插件都必须导出一个对象,其中包含一个 register 方法:

go 复制代码
const plugin = {
  id: 'openclaw-lark',
  name: 'Feishu',
  register(api) {
    // 在这里注册所有能力
  }
};

export default plugin;

api 是平台传进来的"注册中心",它提供了这些关键方法:

方法 作用
api.registerChannel() 注册一个消息频道
api.registerTool() 注册一个 AI 可调用的工具
api.registerCommand() 注册一个斜杠命令
api.registerCli() 注册一个 CLI 命令
api.on() 监听平台事件
api.config 读取当前配置
api.runtime 访问运行时环境

飞书插件的 register() 做了这些事情(按顺序):

第一步:注入运行时

go 复制代码
LarkClient.setRuntime(api.runtime);

把平台的运行时环境存下来,后续发消息、读配置都要用到。

第二步:注册频道

go 复制代码
api.registerChannel({ plugin: feishuPlugin });

这一行把一个实现了 ChannelPlugin 接口的对象注册进平台。这个接口是平台和频道插件之间的"契约",定义了频道必须提供的所有能力:

  • 「入站网关(Gateway)」:怎么连接飞书、怎么收消息

    • 飞书插件的实现:建立 WebSocket 长连接到飞书服务器

    • 平台在启动时调用 gateway.startAccount(),插件建连

    • 平台在关闭时调用 gateway.stopAccount(),插件断连

  • 「出站适配器(Outbound)」:AI 生成了回复,怎么发到飞书

    • 平台调用 outbound.sendText() → 插件调飞书 API 发消息

    • 平台调用 outbound.sendMedia() → 插件上传文件再发送

  • 「能力声明(Capabilities)」:告诉平台这个频道支持什么

    go 复制代码
    capabilities: {
      chatTypes: ['direct', 'group'],  // 支持私聊和群聊
      media: true,                      // 支持发送图片/文件
      reactions: true,                  // 支持表情回应
      threads: true,                    // 支持消息线程
      blockStreaming: true,             // 支持流式卡片
    }
  • 「配置管理」:多账号支持、白名单、权限策略

  • 「消息目标解析」 :把 user:ou_xxxchat:oc_xxx 解析成飞书能识别的 ID

  • 「状态探针」:定期检查连接是否正常

第三步:注册工具

go 复制代码
registerOapiTools(api);          // 日历、任务、多维表格等 30+ 工具
registerFeishuMcpDocTools(api);  // 文档读写工具
registerFeishuOAuthTool(api);    // OAuth 授权工具
registerAskUserQuestionTool(api);// 交互式提问工具

每个工具注册时需要提供:

  • 「名称和描述」:AI 靠这个判断什么时候该调用这个工具

  • 「参数 Schema」:工具接受哪些参数(JSON Schema 格式),AI 会据此生成正确的参数

  • 「execute 函数」:实际执行逻辑

以日历搜索工具为例,注册时大致是这样的:

go 复制代码
api.registerTool({
  name: 'feishu_search_doc_wiki',
  description: '飞书文档与 Wiki 统一搜索工具...',
  parameters: { /* JSON Schema:query, filter, page_size 等 */ },
async execute(toolCallId, params) {
    // 1. 获取工具客户端(自动处理身份认证)
    const client = toolClient();
    // 2. 以用户身份调用飞书搜索 API
    const res = await client.invoke(
      'feishu_search_doc_wiki.search',
      (sdk, opts, uat) => sdk.request({ ... }),
      { as: 'user' }
    );
    // 3. 返回结果给 AI
    return { content: [{ type: 'text', text: JSON.stringify(res.data) }] };
  }
});

这里有个关键设计:「工具不需要关心身份认证的细节」toolClient() 内部会自动判断是用用户身份(UAT)还是应用身份(TAT)调用 API,自动处理 Token 刷新、权限检查。工具代码只需要声明 { as: 'user' } 表示"我要以用户身份执行"就够了。

第四步:注册事件钩子和命令

go 复制代码
// 监听工具调用,只追踪飞书相关的
api.on('before_tool_call', (event) => {
if (event.toolName.startsWith('feishu_')) {
    log.info(`tool call: ${event.toolName}`);
  }
});

// 注册聊天命令
api.registerCommand({
  name: 'feishu_doctor',
async handler(ctx) {
    const markdown = await runFeishuDoctor(ctx.config);
    return { text: markdown };
  }
});

// 注册 CLI 命令
api.registerCli((ctx) => {
  ctx.program.command('feishu-diagnose')
    .action(async () => { /* 诊断逻辑 */ });
});

「注册完成后」 ,OpenClaw 平台就"认识"了飞书这个频道------知道怎么连接它、知道 AI 可以用哪些飞书工具、知道用户可以输入什么命令。整个过程,「插件没有修改平台的任何代码,只是通过 api 对象"注册"了自己的能力。」


五、插件机制的设计哲学

看完飞书插件的注册流程,我们可以提炼出 OpenClaw 插件机制背后的几个核心设计思想。

1. 契约式集成

平台和插件之间通过**「接口契约」** 交互。ChannelPlugin 接口定义了频道必须实现的方法,ToolResult 定义了工具必须返回的格式。

这意味着:

  • 平台不关心你内部怎么实现,只要你按接口约定交付结果

  • 插件不需要了解平台内部实现,只需要调 api 上的方法

  • 两边可以独立迭代,只要接口不变

2. 声明式发现

插件通过两个 JSON 文件(package.json 的 openclaw 字段 + openclaw.plugin.json)声明自己的能力。平台扫描这些声明就知道每个插件能干什么,不需要加载代码就能在 UI 上展示可用插件列表。

这跟 Chrome 扩展的 manifest.json、VS Code 扩展的 package.json#contributes 是一个思路------「先声明,再加载。」

3. 同进程、共享上下文

插件跑在 OpenClaw 主进程里,共享 Node.js 的事件循环、内存空间。这带来几个好处:

  • 「性能好」:工具调用没有 RPC 开销,就是一次函数调用

  • 「上下文共享」:插件可以通过 AsyncLocalStorage 获取当前请求的完整上下文(谁发的消息、在哪个群、哪个账号),不需要额外传参

  • 「生命周期统一」:平台启动,插件加载;平台关闭,插件卸载。不存在"插件进程挂了但平台不知道"的问题

当然也有代价:「一个插件崩了可能影响整个进程」。所以插件质量很重要,OpenClaw 在这方面也做了一些防护(比如工具执行有 try-catch 兜底、WebSocket 断连自动重连等)。

4. 能力可组合

一个插件可以按需注册能力:

  • 「只注册工具」 :最简单,写一个函数就行。比如做一个"翻译工具"插件,只需要注册一个 translate 工具

  • 「注册工具 + 命令」:稍微复杂一点。工具给 AI 用,命令给人用

  • 「注册完整频道」:最复杂,需要实现消息收发的全套链路。但你获得的是让 AI Agent 接入一整个消息平台的能力

飞书插件是最复杂的那种------「一个插件同时注册了频道 + 30 多个工具 + 多个命令」,覆盖了消息、文档、日历、任务、多维表格等几乎所有飞书开放 API 能力。

5. 与其他平台的对比

维度 OpenClaw 插件 VS Code 扩展 ChatGPT Plugin Grafana 插件
加载方式 同进程 import 同进程 require HTTP 远程调用 同进程 + iframe
通信方式 直接函数调用 VS Code API RESTful API 数据源接口
声明文件 package.json + plugin.json package.json ai-plugin.json plugin.json
能力类型 频道/工具/命令 命令/视图/语言 API 端点 数据源/面板
沙箱隔离 有(Extension Host) 天然隔离(HTTP) 部分(iframe)

OpenClaw 的方案更像 VS Code 早期版本------「信任插件,给予完全的进程内访问权限,换取最低的调用开销和最大的灵活性」。随着生态成熟,未来可能会引入更多隔离机制。


六、结语

回到开头的问题:一个 AI Agent 平台如何在不改核心代码的前提下,不断扩展能力?

OpenClaw 给出的答案是一套三步走的插件机制:

  1. 「声明」------用两个 JSON 文件告诉平台"我是谁、我能干什么"

  2. 「注册」------在 register() 函数里把频道、工具、命令挂到平台上

  3. 「执行」------平台在需要时调用插件注册的函数,插件完成实际工作

飞书插件是这套机制的一个完整实践------一个 npm 包,注册了完整的消息频道 + 30 多个工具 + 多个诊断命令,覆盖了飞书消息、文档、日历、任务、多维表格等几乎所有开放 API。所有这些能力,都通过一条 openclaw install 命令接入,运行在同一个进程里,不需要额外部署任何服务。

「平台做薄,能力外挂。」 这个设计让 OpenClaw 的能力边界不再受限于核心团队的开发速度,而是取决于整个插件生态的广度。今天是飞书,明天可以是钉钉、企业微信、Notion、Jira------只要有人写一个插件。

相关推荐
gf132111120 小时前
【python_往飞书群或者话题下发送卡片和发送文件】
java·python·飞书
AppOS1 天前
手把手教你 Openclaw 在 Mac 上本地化部署,保姆级教程!接入飞书打造私人 AI 助手
人工智能·macos·飞书
算家云2 天前
OpenClaw进阶玩法:多飞书机器人部署指南
人工智能·飞书·openclaw
70asunflower2 天前
OpenClaw 飞书机器人集成完全指南
飞书·openclaw·小龙虾
gf13211112 天前
【python_使用指定应用发送飞书卡片】
java·python·飞书
正经教主2 天前
【可接入openclaw】飞书开放平台创建应用及机器人
飞书
CHQIUU3 天前
OpenClaw 飞书配对、QQ 插件升级与全局依赖补全:踩坑记录与处理办法
飞书
博风3 天前
飞书推送文件给指定用户
飞书