为什么要做看板
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 实操技巧:
