为什么要做看板

OpenClaw 的很多活是「后台长跑」:定时任务、子智能体、批量抓取、长构建。

它们跑在会话之外,你不在现场时,只能等结束后的那条汇报——中途卡死、烧 token、

默默失败,你都看不见。

dashboard 工具就是为了解决这个问题:它能在 Control UI 上维护一块持久可视区域,

把任务进度、指标表格、进度条、待办清单直接摆出来,谁打开 UI 都能一眼看清现在在哪一步。

本文用真实命令、真实字段,带你从零做出一块可用的看板。

dashboard 能放什么

dashboard 不是单一控件,而是「标签页 + 控件」两层结构:

  • tab(标签页):一块看板可以有多个 tab,互相独立,互不覆盖。
  • widget(控件):每个 tab 里可放多种控件,常用稳定的是插件提供的:

- session:progress —— 当前会话的实时进度卡

- workboard:card —— 单张任务卡(标题/状态/进度)

- workboard:mini —— 精简列表(取最近 N 条)

- workboard:board —— 整个看板(多卡聚合)

控件名是 插件kind:控件名 的形式。上面这些是 OpenClaw 自带插件提供的稳定控件,
你直接按名字用即可,不需要自己写前端。

第一步:先看看当前看板长什么样

dashboard read

返回的是当前会话看板的快照:有哪些 tab、每个 tab 里有哪些 widget。

第一次用通常是空的,没关系,下面开始往里加。

第二步:建一个 tab 并放一张进度卡

假设你在跑一个「批量抓取 50 个页面」的后台任务,想实时盯进度:

dashboard tab_create title="抓取看板"

记下返回的 tabId(例如 crawl)。然后往里放一张进度卡:

dashboard widget_put
  name=crawl_progress
  pluginKind=workboard:card
  tabId=crawl
  props={"boardId":"crawl","cardId":"main","title":"批量抓取 50 页","status":"in_progress"}

放进去了。之后每次进度变化,只要再 widget_put 同一个 name,就会原地更新这张卡,

不会重复叠加。

第三步:用进度条显示百分比

workboard:card 支持进度字段。把上面的 props 升级一下:

dashboard widget_put
  name=crawl_progress
  pluginKind=workboard:card
  tabId=crawl
  props={"boardId":"crawl","cardId":"main","title":"批量抓取 50 页","status":"in_progress","progress":42}

UI 上会渲染出 <progress value="42" max="100"> 这样的进度条。

跑完一页就 widget_put 一次把 progress 加 2,全程肉眼可见。

第四步:多任务用一张 board 聚合

如果你同时盯好几条线(抓取、清洗、入库),与其堆满进度卡,不如用一张 board:

dashboard widget_put
  name=all_lines
  pluginKind=workboard:board
  tabId=crawl
  props={"boardId":"pipeline"}

然后在各自任务里分别 widget_put 到同一个 boardId 下的不同 cardId,

board 会把它们并排聚合成一张总览卡。适合「一条流水线多个阶段」的场景。

第五步:把看板和聊天框分开摆

默认聊天 dock 在左边,看板在中间,有时挤。可以用 set_chat_dock 把聊天挪到右边或底部,

甚至先藏起来专心盯数据:

dashboard set_chat_dock dock=right

看完再 set_chat_dock dock=left 挪回来。注意:这个动作需要已连接的 Control UI 才生效。

第六步:删除与重排

  • 删某个控件:dashboard widget_remove name=crawl_progress
  • 删某个 tab:dashboard tab_delete tabId=crawl
  • 调整 tab 顺序:dashboard tabs_reorder tabIds=["crawl","other"]
  • 聚焦某个 tab:dashboard focus_tab tabId=crawl

一个完整的最小脚本

把上面串起来,一个后台抓取任务的「看板自更新」骨架:

# 伪代码:每抓完一页,更新进度卡
import json, subprocess

def update(progress: int, done: int, total: int):
    props = {
        "boardId": "crawl",
        "cardId": "main",
        "title": f"批量抓取({done}/{total})",
        "status": "completed" if done >= total else "in_progress",
        "progress": progress,
    }
    subprocess.run([
        "dashboard", "widget_put",
        "name=crawl_progress",
        "pluginKind=workboard:card",
        "tabId=crawl",
        f"props={json.dumps(props, ensure_ascii=False)}",
    ])

# 初始建 tab + 卡(只建一次)
subprocess.run(["dashboard", "tab_create", "title=抓取看板"])  # 记下 tabId
update(0, 0, 50)

for i, url in enumerate(urls):
    crawl(url)
    update(int((i + 1) / 50 * 100), i + 1, 50)

这样哪怕你在睡觉,第二天打开 UI 也能看到「昨晚抓到 47/50,卡在第 48 页报错」,

而不是只收到一句「任务结束」。

和 progress_card 有什么区别

容易混:progress_card 是会话上下文里的一张卡片(跟着对话走,适合单条长任务的轻量汇报);

dashboard 是跨会话、常驻 UI 的一块独立看板(适合多线并行、需要反复回来盯的场景)。

经验法则:

  • 单次长任务、只给当前会话看 → progress_card 够用。
  • 多条线、后台跑、要常驻且可随时打开 UI 看 → 上 dashboard。

常见坑

  • widget 名要稳定:想「更新」就不能每次换 name,否则旧卡留在那、新卡不断叠加。
  • tabId 别丢:tab_create 返回的 tabId 是唯一引用,记下来再 widget_put。
  • props 是 JSON 字符串:通过命令行传时是一整段 JSON,注意引号转义;用 API 调用时直接传对象即可。
  • Control UI 才看得到:dashboard 渲染在 Control UI 上,纯命令行/微信频道里看不到画面,但它依旧写进状态,连上 UI 就能看。

小结

dashboard 的核心价值就一句话:让后台长任务从「黑盒」变成「玻璃盒」。

三步就能上手——建 tab、放 card、按进度 widget_put 原地更新。

配合 cron 长跑任务使用,能直接消灭「静默失败看不到」这个老毛病。


扫码关注公众号,获取更多 OpenClaw 实操技巧:

公众号二维码