1. 为什么单条指令撑不起真实业务
OpenClaw 的 Skill 和 Plugin 单独拿出来都很好用:一个 Skill 能抓网页、查数据库、发通知,一个 Plugin 能挂载工具、监听事件、暴露接口。但真实业务很少是“一步到位”的。比如“每天早上抓竞品价格,低于阈值就告警,否则生成日报归档”,这句话里其实藏着四个动作、一个条件分支、两种收尾路径。你直接丢给 Agent,它今天可能先告警再归档,明天可能忘了对比基准价,后天干脆把日报发到告警群里。
这就是任务编排要解决的问题:把 Agent 的“自由发挥”收敛成一条可预测、可重复、可观测的流水线。OpenClaw 没有可视化拖拽设计器,但它给了三样东西——Skill 内的步骤指令、Hook 事件链、Plugin 状态机。这三样对应三种复杂度层级,你可以从最简单的顺序列表开始,逐步升级到能扛住 Gateway 重启的长周期工作流。
这篇面向需要把 Skill、Plugin 串成可复用流程的开发者,给出可复制的编排骨架、Skill 调用顺序、Plugin 挂载方式,以及用统一 Key/API 通道 TaoToken 接入模型调用的完整路径。读完之后,你应该能自己搭一条“抓取→判断→分支→归档”的完整工作流,并跑通一次验证。
2. 前置准备:TaoToken 统一 Key 与 OpenClaw 环境
编排工作流里,Agent 每一步都可能要调模型做判断、做摘要、做格式转换。如果每个 Skill 各自配一套 Key,管理起来会很乱。我习惯用一个统一的 API 通道来收口,TaoToken 就是干这个的:它提供兼容 OpenAI 风格的接口,OpenClaw 里所有需要模型调用的节点都指向同一个 base_url 和同一个 Key。
先拿到 Key。打开 https://taotoken.net/api-keys ,登录后创建一个 API Key,复制保存。注意这个 Key 只在创建时完整显示一次,丢了就重新建一个。
然后在 OpenClaw 的配置里设置环境变量。找到你的 workspace 配置目录,通常在~/.openclaw/下,编辑config.yaml或对应的.env文件:
# ~/.openclaw/.env TAOTOKEN_API_KEY=sk-你的实际key TAOTOKEN_BASE_URL=https://taotoken.net/api如果你用的是 OpenClaw 的模型配置文件,可以这样写:
# ~/.openclaw/config.yaml models: default: provider: openai-compatible base_url: https://taotoken.net/api api_key: ${TAOTOKEN_API_KEY} model: claude-sonnet-4-20250514这里base_url后面不要加/v1,TaoToken 的兼容层会自动处理路径。模型名按你实际订阅的填,Claude 系列、GPT 系列都支持。配好之后,OpenClaw 里所有 Skill 和 Plugin 发起的模型调用都会走这条通道,你只需要维护一个 Key。
提示:如果你还没决定用哪个模型,可以先到 https://taotoken.net/models 看看可用列表,再回来填
model字段。
环境验证一步:在终端跑
curl https://taotoken.net/api/v1/models \ -H "Authorization: Bearer $TAOTOKEN_API_KEY"返回 JSON 里能看到模型列表,说明 Key 和网络都通了。这一步过了再往下走,否则后面工作流报错你分不清是编排问题还是鉴权问题。
3. 可复制配置:从 Skill 顺序编排到 Plugin 状态机
3.1 L1 顺序编排:Skill 内的步骤列表
最轻量的编排就是在SKILL.md里写编号步骤。Agent 会按顺序执行,中间结果用变量名传递。适合步骤固定、没有分支的任务。
--- name: price_monitor description: 抓取竞品价格并与基准对比,记录差异 --- # 价格监控工作流 ## 输入参数 - `product_url`: 商品链接 - `product_id`: 商品 ID ## 执行步骤 必须严格按照以下顺序执行,不得跳步: 1. 调用 `web_scraper` 工具,URL 为 `product_url`,提取当前价格,保存为变量 `current_price` 2. 调用 `db_query` 工具,SQL 为 `SELECT base_price FROM products WHERE id = '{product_id}'`,结果保存为 `base_price` 3. 计算 `diff = current_price - base_price` 4. 调用 `db_insert` 工具,将 `{product_id, current_price, diff, timestamp}` 写入 `price_history` 表 5. 回复用户:“已记录商品 {product_id} 当前价格 {current_price},与基准相差 {diff}”关键点是第 1 步和第 2 步的输出都显式命名成变量,第 3 步才能引用。如果你不命名,Agent 可能把两个结果混在一起算。另外“必须严格按照以下顺序执行”这句话不是装饰,它是对 Agent 的强约束,实测下来能明显减少跳步。
3.2 L2 条件编排:Skill 条件语法 + Hook 事件链
当工作流需要 if/else 或重试时,在 Skill 里写条件描述:
## 执行步骤 1. 调用 `check_stock` API,获取库存数量 `stock` 2. 如果 `stock > 0`: - 调用 `create_order` 工具 - 调用 `send_notification` 发送“订单已创建” 否则: - 调用 `send_notification` 发送“缺货,无法下单” - 终止流程 3. 仅当订单创建成功时,调用 `generate_invoice` 工具但 Skill 里的条件描述依赖 Agent 理解,不是 100% 可靠。要更稳,用 Hook 监听tool:after事件,根据结果动态调 Skill:
// ~/.openclaw/workspace/hooks/price_alert/handler.ts const handler = async (event: any) => { if (event.type !== 'tool:after') return; if (event.toolName !== 'db_query') return; const priceDiff = event.result.diff; if (priceDiff > 100) { await callSkill('send_alert', { message: `价格波动过大:${priceDiff}` }); } else { await callSkill('log_normal', { diff: priceDiff }); } };Hook 的触发点比 Skill 描述精确得多,适合生产环境。
3.3 L3 状态机编排:Plugin 实现可恢复工作流
长周期、可中断、需审计的流程,必须用 Plugin 写状态机。核心是每一步执行后把状态存盘,Gateway 重启后从断点继续。
// ~/.openclaw/workspace/extensions/report-orchestrator/index.ts import { definePluginEntry } from "@openclaw/plugin-sdk"; import fs from 'fs/promises'; interface WorkflowState { step: 'init' | 'fetch_source1' | 'fetch_source2' | 'clean' | 'generate_report' | 'send' | 'completed'; data: any; errors: string[]; createdAt: number; } class ReportWorkflow { private stateFile: string; private state: WorkflowState; constructor(workflowId: string) { this.stateFile = `/tmp/workflow_${workflowId}.json`; this.state = this.loadState() || this.initState(); } private initState(): WorkflowState { return { step: 'init', data: {}, errors: [], createdAt: Date.now() }; } private loadState(): WorkflowState | null { try { return JSON.parse(fs.readFileSync(this.stateFile, 'utf-8')); } catch { return null; } } private saveState() { fs.writeFileSync(this.stateFile, JSON.stringify(this.state)); } async run() { while (this.state.step !== 'completed') { switch (this.state.step) { case 'init': await this.fetchSource('source1'); this.state.step = 'fetch_source1'; break; case 'fetch_source1': await this.fetchSource('source2'); this.state.step = 'fetch_source2'; break; case 'fetch_source2': await this.cleanData(); this.state.step = 'clean'; break; case 'clean': await this.generateReport(); this.state.step = 'generate_report'; break; case 'generate_report': await this.sendToDingtalk(); this.state.step = 'send'; break; case 'send': this.state.step = 'completed'; break; } this.saveState(); await this.sleep(1000); } } private async fetchSource(source: string) { this.state.data[source] = { mock: true }; } private async cleanData() { /* 清洗逻辑 */ } private async generateReport() { /* 生成报表 */ } private async sendToDingtalk() { /* 发送通知 */ } private sleep(ms: number) { return new Promise(r => setTimeout(r, ms)); } } export default definePluginEntry({ id: 'report-orchestrator', register(api) { api.registerTool({ name: 'start_report_workflow', description: '启动月度报表生成工作流', async execute() { const workflow = new ReportWorkflow(Date.now().toString()); workflow.run().catch(console.error); return { content: [{ type: 'text', text: '工作流已启动,可通过 /workflow status 查询进度' }] }; } }); } });这个骨架的关键点:每一步saveState()落盘,loadState()在构造时恢复。Gateway 重启后重新触发run(),会从上次的step继续,不会从头再来。Plugin 挂载方式就是把整个目录放到~/.openclaw/workspace/extensions/下,OpenClaw 启动时自动加载。
3.4 并行编排与并发控制
独立步骤可以并行。Plugin 里用Promise.all,但任务多时要限流:
import pLimit from 'p-limit'; const limit = pLimit(5); const urls = ['https://a.com', 'https://b.com', 'https://c.com']; const results = await Promise.all( urls.map(url => limit(() => fetch(url))) );p-limit(5)表示最多 5 个并发,避免把下游打挂。
4. 验证请求:跑通一次完整工作流
配置写完了,得验证。分两步:先验证模型通道,再验证编排链路。
第一步,确认 TaoToken 通道在 OpenClaw 里生效。在 OpenClaw 对话里发一条:
请调用模型,回复“通道正常”四个字。如果返回正常,说明base_url和 Key 都对。如果报 401,检查.env里的 Key 有没有多余空格;如果报 404,检查base_url是不是误加了/v1。
第二步,触发编排工作流。以价格监控为例,在对话里输入:
执行 price_monitor,product_url=https://example.com/item/123,product_id=123预期结果是 Agent 依次调用web_scraper、db_query、db_insert,最后回复一条包含当前价格和差值的消息。你可以在~/.openclaw/logs/下看到每一步的工具调用记录。
第三步,验证状态机恢复。手动触发start_report_workflow,等它跑到fetch_source1之后,重启 OpenClaw Gateway。重启后再触发一次run(),观察日志里是不是从fetch_source1继续,而不是从init重来。这一步过了,说明状态持久化生效。
第四步,验证条件分支。把price_monitor的基准价改成一个极低值,让diff > 100,看 Hook 是否触发send_alert。再改回正常值,看是否走log_normal。
5. 本篇常见错排查
Agent 不按 Skill 顺序执行。步骤描述太模糊。改成“步骤 1”“步骤 2”编号,加“必须严格按照顺序执行,不得跳步”。实测这句话能显著降低跳步率。
条件分支不生效。条件写法有歧义。用明确的“如果 X,则执行 A;否则执行 B”,不要写“根据情况选择”。涉及数值比较时,把变量名和阈值都写清楚。
工作流跑到一半 Gateway 重启后丢失进度。没用状态机。L1/L2 的 Skill 和 Hook 不持久化中间状态,只有 Plugin 状态机每步saveState()才能恢复。长周期任务必须上 L3。
并行任务太多导致下游限流。没控制并发。用p-limit限制并发数,一般 5 到 10 比较稳。
工作流执行时间过长被 Gateway 杀掉。默认超时 30 秒。改config.yaml里的workflow.defaultTimeout,或者改成异步模式:Plugin 里run()不 await,立即返回“已启动”,让用户轮询状态。
模型调用报鉴权错误。检查.env里TAOTOKEN_API_KEY是否完整,base_url是否为https://taotoken.net/api。如果用了多个 Skill 各自配 Key,统一改成读环境变量。
Hook 不触发。检查 Hook 目录是否在~/.openclaw/workspace/hooks/下,handler.ts是否默认导出函数,事件类型字符串是否拼写正确(tool:after不是tool_after)。
6. 下一步:把编排接进你的真实流程
编排能力搭好之后,你可以把日常重复的多步操作都收进来。需要长期跑编码类或 Agent 类工作流的,可以看看 Coding Plan,它适合把 OpenClaw 的编排节点和代码生成、代码审查串成持续运行的流水线:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan_cta&utm_campaign=rewrite
如果你只是想先验证某个模型在编排节点里的判断效果,直接开模型对话试:https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=model_chat_cta&utm_campaign=rewrite
接入过程中遇到鉴权或路径问题,接入文档里有完整的 base_url 和参数说明:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc_cta&utm_campaign=rewrite
Key 管理和用量查看在控制台:https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console_cta&utm_campaign=rewrite
最后提醒一句:别试图把一切逻辑都塞进工作流。保留 Agent 一定程度的自主决策,系统会更灵活。固定流程负责稳定,智能决策负责应变,找到两者的平衡点,才是编排设计里最难也最值得的部分。