1. 从 Lobster 脚本到 Gateway:OpenClaw 插件系统到底解决了什么问题
如果你已经把 OpenClaw 的基础对话跑通了,接下来大概率会遇到一个瓶颈:单轮对话能做的事太碎。比如你想让它读一封邮件、判断意图、生成草稿、再等你确认后发出去,这套流程如果全靠对话里一步步手打指令,既慢又容易断。OpenClaw 的插件系统就是为这种场景准备的,它把 Lobster 脚本、LLM Task 和 Gateway 串成一条可复用的工作流,你只需要触发一次,后面多步自动跑完。
我先把三个核心概念用一句话对齐。Lobster 是工作流运行时,负责按顺序执行 CLI 命令和 JSON 管道;LLM Task 是插件里专门调用大模型的一步,输入输出都是结构化 JSON;Gateway 是 OpenClaw 的常驻进程,插件加载、路由分发、工具注册都在它里面完成。三者关系可以理解为:Gateway 是插座,插件是插头,Lobster 是电线,LLM Task 是电线上那个会思考的开关。
适合谁看这篇?已经能正常和 OpenClaw 对话、装过至少一个官方插件、想自己写一个 LLM Task 插件并挂到 Gateway 上验证的开发者。如果你还没跑通基础对话,建议先把openclaw plugins list和 Gateway 重启流程走一遍再回来。
插件系统最容易被低估的地方是它的加载机制。很多人装完插件发现没生效,第一反应是插件坏了,其实九成情况是 Gateway 没重启,或者插件没进白名单。OpenClaw 的插件运行在 Gateway 进程内,和 Gateway 共享信任边界,所以它的启用是显式的:要么在plugins.allow里列出来,要么在plugins.entries里单独开。这个设计牺牲了一点便利,换来的是可控性,对生产环境来说是值得的。
下面我会按「目录结构 → manifest 配置 → Gateway 路由 → 新增 LLM Task 插件 → 加载验证 → 日志排查」的顺序走一遍,每一步都给可复制的片段。你跟着做,最后应该能看到自己的插件被 Gateway 加载,并且通过 Lobster 调起来。
2. 前置准备:TaoToken 接入与 OpenClaw 插件目录结构
在写插件之前,先把模型调用这条链路准备好。OpenClaw 的 LLM Task 需要一个可用的模型提供方,我这边用的是 TaoToken 的兼容接口,Base URL 填https://taotoken.net/api,Key 在控制台生成。如果你还没建 Key,可以去 TaoToken API Keys 建一个,模型 ID 按你实际要用的填,比如gpt-5.4这类。接入文档在 TaoToken 文档,里面有各语言的调用示例,配 OpenClaw 时主要看 Base URL 和鉴权头这两项。
准备好之后,看插件目录。OpenClaw 的插件可以放在两个位置:全局插件目录和项目本地目录。全局目录一般在~/.openclaw/plugins/,项目本地目录通过plugins.load.paths指定。我建议开发阶段用本地目录,方便改完直接重启验证。一个最小可用的插件目录长这样:
my-llm-plugin/ ├── openclaw.plugin.json # 插件清单,必须 ├── package.json # npm 包信息,可选但推荐 ├── src/ │ └── index.ts # 入口文件,注册工具/命令/通道 └── config.schema.json # 配置的 JSON Schema,可选openclaw.plugin.json是清单文件,Gateway 靠它识别插件身份和能力。src/index.ts是入口,导出一个函数,函数收到api对象,你通过api.registerTool、api.registerCommand这些方法把能力注册进去。config.schema.json用来约束插件配置,写了之后 Gateway 会在启动时校验你的配置字段,写错了会直接报错,比运行时才发现要好。
这里有个容易踩的坑:入口文件支持.js和.ts,但如果你用.ts,要确认 OpenClaw 的构建链能处理。我实测下来,开发阶段直接用编译好的.js最省事,避免类型加载问题。另外package.json里的name最好和清单里的插件 ID 保持一致,不然日志里会出现两个名字,排查时容易混。
目录建好后,先别急着写逻辑,把清单文件写对。清单里最关键的是id、entry和capabilities三个字段。id是插件唯一标识,后面plugins.entries里用的就是它;entry指向入口文件;capabilities声明这个插件提供哪些能力,比如tools、commands、channels。声明了但没注册,Gateway 不会报错,但调用时会找不到,所以声明和实现要对应上。
3. 可复制配置:manifest、Gateway 路由与 LLM Task 插件片段
这一节给三份可直接抄的配置。第一份是插件清单openclaw.plugin.json:
{ "id": "my-llm-task", "name": "My LLM Task Plugin", "version": "0.1.0", "entry": "./src/index.js", "capabilities": { "tools": ["my_llm_task"] }, "configSchema": { "type": "object", "properties": { "defaultModel": { "type": "string" }, "maxTokens": { "type": "number", "default": 800 } } } }第二份是 OpenClaw 主配置里的插件段,路径按你实际安装位置改。这份配置同时演示了白名单、加载路径、entries 和 slots:
{ "plugins": { "enabled": true, "allow": ["my-llm-task", "llm-task"], "load": { "paths": ["~/Projects/my-llm-plugin"] }, "entries": { "my-llm-task": { "enabled": true, "config": { "defaultModel": "gpt-5.4", "maxTokens": 800 } }, "llm-task": { "enabled": true, "config": { "defaultProvider": "taotoken", "defaultModel": "gpt-5.4", "maxTokens": 800 } } }, "slots": { "memory": "memory-core" } }, "agents": { "list": [ { "id": "main", "tools": { "allow": ["my_llm_task", "llm-task"] } } ] } }第三份是入口文件src/index.js,注册一个 LLM Task 工具,内部走 TaoToken 的兼容接口:
import { Type } from "@sinclair/typebox"; export default function (api) { api.registerTool({ name: "my_llm_task", description: "对输入文本做意图分类并生成草稿", parameters: Type.Object({ subject: Type.String(), body: Type.String() }), async execute(_id, params) { const baseUrl = "https://taotoken.net/api"; const apiKey = process.env.TAOTOKEN_API_KEY; const model = "gpt-5.4"; const resp = await fetch(`${baseUrl}/v1/chat/completions`, { method: "POST", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${apiKey}` }, body: JSON.stringify({ model, messages: [ { role: "system", content: "你是邮件助手,输出 JSON,字段为 intent 和 draft。" }, { role: "user", content: `主题:${params.subject}\n正文:${params.body}` } ], response_format: { type: "json_object" } }) }); const data = await resp.json(); const text = data.choices?.[0]?.message?.content ?? "{}"; return { content: [{ type: "text", text }] }; } }); }注意TAOTOKEN_API_KEY走环境变量,不要硬编码进代码。Gateway 启动时如果读不到这个变量,工具调用会返回鉴权错误,日志里能看到 401。模型 ID 和 Base URL 这两项要和你在 TaoToken 控制台看到的一致,改错一个字符就会连不上。
Gateway 路由这块,OpenClaw 的分发逻辑是:请求进来先看 agent 的tools.allow,再看插件是否在plugins.allow里,最后看plugins.entries里是否 enabled。三层都过才会真正调用插件。所以如果你发现工具在对话里不可见,按这个顺序倒着查:先确认 entries 开了,再确认 allow 里有,最后确认 agent 的 tools.allow 没漏。
4. 验证请求:新增 LLM Task 插件后的加载与调用
配置写完,重启 Gateway。重启命令按你的部署方式,本地一般是:
openclaw gateway restart重启后先看插件列表,确认你的插件被识别:
openclaw plugins list正常输出里应该能看到my-llm-task,状态是 enabled。如果没出现,说明清单文件路径不对或者load.paths没指对。这一步过了再往下。
接着验证工具注册。在对话里让 agent 列出可用工具,或者直接触发一次调用。用 Lobster 调的话,管道长这样:
openclaw.invoke --tool my_llm_task --action json --args-json '{ "subject": "Hello", "body": "Can you help me with the report?" }'预期返回是一段 JSON 文本,里面有intent和draft两个字段。如果返回的是空对象或者报错,先看 Gateway 日志。日志里会打印工具调用的入参和出参,以及模型请求的耗时。我实测下来,第一次调用因为要建连接,会比后续慢一两秒,这是正常的。
再验证 Lobster 工作流串联。写一个.lobster文件,把 LLM Task 作为其中一步:
name: mail-triage args: tag: default: "work" steps: - id: collect command: inbox list --json - id: classify command: openclaw.invoke --tool my_llm_task --action json stdin: $collect.stdout - id: approve command: inbox apply --approve stdin: $classify.stdout approval: required跑这个工作流:
lobster run mail-triage.lobster如果classify这一步成功,你会看到它把上一步的邮件列表喂给 LLM Task,返回结构化结果,然后approve因为标了approval: required会暂停等你确认。这一步能跑通,说明 Lobster、LLM Task、Gateway 三者已经串起来了。
成功结果长这样:终端先打印 collect 的输出,然后打印 classify 的 JSON,最后提示你审批。审批通过后继续执行 apply。整个过程只触发了一次 AI 调用,但完成了多步操作,这就是插件系统编排的价值。
5. 常见报错排查:401、local proxy failed、reading choices 与 OAuth
这一节列几个我实际遇到过的报错,以及对应的排查路径。
401 Unauthorized。最常见,原因是 API Key 没读到或者填错。先确认环境变量在 Gateway 进程里可见,echo $TAOTOKEN_API_KEY在启动 Gateway 的同一个 shell 里执行。如果用了 systemd 或容器,环境变量要显式注入。其次确认请求头是Authorization: Bearer <key>,少个空格都会 401。最后确认 Base URL 是https://taotoken.net/api,不要多加/v1之外的路径。
local proxy failed。这个报错通常出现在 Gateway 尝试转发请求但目标不可达时。检查你的网络出口是否允许访问taotoken.net,以及是否有本地代理配置干扰。OpenClaw 的 Gateway 会读系统代理设置,如果你之前配过代理,建议在插件里显式指定fetch不走代理,或者清掉相关环境变量。这个报错和插件本身无关,是链路问题。
reading choices 报错。典型信息是Cannot read properties of undefined (reading 'choices')。这说明模型返回体里没有choices字段,通常是请求体格式不对,或者模型 ID 写错导致返回了错误对象。打印完整响应体再解析,不要直接取data.choices[0]。我习惯先console.log(JSON.stringify(data)),确认结构对了再取值。
OAuth 相关报错。如果你用的是需要 OAuth 的提供方,报错会提示 token 过期或 scope 不足。OpenClaw 的 LLM Task 支持指定 provider,如果你在entries里配了defaultProvider,要确认这个 provider 的凭证是有效的。用 TaoToken 的 Key 方式不涉及 OAuth,所以如果你看到 OAuth 报错,先检查是不是配置里残留了别的 provider。
排查顺序建议:先看 Gateway 日志定位是加载阶段还是调用阶段,再看环境变量,再看请求体,最后看网络。大部分问题在前两步就能解决。日志级别可以临时调高,OpenClaw 支持在配置里设logLevel: "debug",能看到完整的请求和响应。
6. 把插件挂到 Coding Plan 上长期跑
插件调通之后,下一步是让它稳定跑起来。如果你打算把这类 LLM Task 工作流用在日常编码或 Agent 场景里,建议走 TaoToken Coding Plan,它的额度模型更适合长期、高频的调用,不用每次担心单次请求的成本。配置方式和你现在用的兼容接口一致,Base URL 和 Key 换一下就行。
验证模型连通性的时候,可以顺手用 TaoToken 模型对话 测一下同一个模型 ID,确认返回格式和你在插件里拿到的一致。这样排查时能快速区分是插件问题还是模型侧问题。
最后给一个实用技巧:把插件的配置项尽量外置到entries.config里,不要写死在代码中。这样换模型、调 maxTokens、切 provider 都不用改代码,只改配置重启即可。我现在的做法是每个 LLM Task 插件只保留一个默认模型,具体用哪个由 Gateway 配置决定,插件本身不关心。这样一套插件可以在不同项目里复用,迁移成本几乎为零。