关于 oodle-dashboards
oodle-dashboards 是指导 Agent 通过 Oodle CLI 管理仪表板与文件夹的 skill,适用于需要维护仪表板结构的团队。它强调先读取完整定义、保留面板配置,并将仪表板放入明确的文件夹。
主要功能
- 01列出、读取、创建和删除仪表板
- 02创建文件夹并将仪表板归类
- 03更新前读取完整定义以保留面板
- 04安全删除共享仪表板的分阶段流程
- 05提供 PromQL 窗口与统计面板建议
适用场景
- 01按文件夹查找并整理团队仪表板
- 02修改仪表板标题而不丢失面板
- 03创建仪表板并放入指定文件夹
- 04优化查询窗口以减少不必要的数据读取
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ oodle-ai/ agent-skills --skill oodle-dashboards安装后在 Claude Code 中输入/oodle-dashboards确认已加载。
详细介绍
Oodle 仪表板 — CRUD 和组织管理
本技能将教会 Agent 如何管理 Oodle 仪表板和文件夹,避免丢失面板状态,也避免仪表板孤立在根文件夹中。
前提条件
brew install oodle-ai/oodle/oodle
oodle configure
# or
export OODLE_API_KEY=<key>
export OODLE_INSTANCE=<instance>
export OODLE_DEPLOYMENT=<url>
验证仪表板端点是否正常:
oodle dashboards list -o json | jq 'length'
oodle folders list -o json | jq 'length'
命令执行顺序
运行任何 oodle 命令前:
- 检查所需的资源 ID 或名称是否已在上下文中。
- 如果没有,运行发现命令(例如,
oodle dashboards list -o json)。 - 如果结果不明确,请先让用户确认,再继续操作。
- 使用已解析的 ID 运行目标命令。
- 不要运行试探性命令(例如,未先
get资源就不要delete)。
快速参考
| 任务 | 命令 |
|---|---|
| 列出仪表板 | oodle dashboards list -o json |
| 获取仪表板 | oodle dashboards get <uid> -o json |
| 创建仪表板 | oodle dashboards create -f save.json |
| 更新仪表板 | 使用 "overwrite": true 运行 oodle dashboards create -f save.json(没有 dashboards update) |
| 删除仪表板 | oodle dashboards delete <uid> --force |
| 列出文件夹 | oodle folders list -o json |
| 创建文件夹 | oodle folders create --title "Platform" -o json |
| 删除文件夹 | 没有 CLI 命令;请使用 Oodle UI |
常见操作
列出仪表板
# ✅ CORRECT
oodle dashboards list -o json
# ✅ CORRECT — filter by folder (the list also has folders, with `type` "dash-folder")
oodle dashboards list -o json | jq '.[] | select(.type=="dash-db" and .folderUid=="platform")'
# ❌ WRONG — parsing table output to find an ID
oodle dashboards list | grep "API Overview" | awk '{print $1}'
更改仪表板前先读取
# ✅ CORRECT — fetch the full definition first; preserves all panels and queries
oodle dashboards get dash_123 -o json > current.json
jq '{dashboard: .dashboard, folderUid: .meta.folderUid, overwrite: true}' current.json > save.json
$EDITOR save.json
oodle dashboards create -f save.json
# ❌ WRONG — sending an incomplete payload removes panels
oodle dashboards create -f <(echo '{"dashboard":{"uid":"dash_123","title":"new title"},"overwrite":true}')
创建仪表板
oodle dashboards create -f 接受一个保存请求:dashboard 中是 Grafana 仪表板 JSON,另有 folderUid 和 overwrite。面板会在 targets[].expr 中保留其 PromQL。完整的保存请求会将仪表板放入指定文件夹:
{
"dashboard": {
"title": "API Overview",
"description": "Latency, error rate, and throughput for the API service.",
"tags": ["service:api", "team:platform"],
"panels": [
{
"title": "Request rate",
"type": "timeseries",
"targets": [{"refId": "A", "expr": "sum(rate(http_requests_total{service=\"api\"}[$__rate_interval]))"}]
},
{
"title": "Error rate",
"type": "timeseries",
"targets": [{"refId": "A", "expr": "sum(rate(http_requests_total{service=\"api\",status=~\"5..\"}[$__rate_interval]))"}]
},
{
"title": "P99 latency",
"type": "timeseries",
"targets": [{"refId": "A", "expr": "histogram_quantile(0.99, sum by (le) (rate(http_request_duration_seconds_bucket{service=\"api\"}[$__rate_interval])))"}]
}
]
},
"folderUid": "platform",
"overwrite": false
}
# ✅ CORRECT
oodle dashboards create -f save.json
# ❌ WRONG — no folderUid, dashboard ends up in root and is hard to find
oodle dashboards create -f <(echo '{"dashboard":{"title":"API Overview","panels":[...]}}')
文件夹管理
# ✅ CORRECT — create folder first, capture id, then create dashboards in it
FOLDER_UID=$(oodle folders create --title "Platform" -o json | jq -r '.uid')
jq --arg f "$FOLDER_UID" '.folderUid=$f' save.json > save.with-folder.json
oodle dashboards create -f save.with-folder.json
# ❌ WRONG — creating dashboards before folders, then trying to move them later
oodle dashboards create -f save.json
oodle folders create --title "Platform"
安全删除 — 分两阶段
运行手册、Slack 消息和书签中都可能包含仪表板链接。请分两阶段删除。
# ✅ CORRECT — phase 1: rename so users see it's about to be removed
oodle dashboards get dash_123 -o json > dash.json
jq '{dashboard: (.dashboard | .title = "[MARKED FOR DELETION] " + .title), folderUid: .meta.folderUid, overwrite: true}' dash.json > dash.deletion.json
oodle dashboards create -f dash.deletion.json
# wait at least 7 days, then:
oodle dashboards delete dash_123 --force
# ❌ WRONG — immediate hard delete, breaks every existing link
oodle dashboards delete dash_123 --force
最佳实践
切勿将 $__range 用作面板回看窗口
$__range 会扩展为整个仪表板时间范围,因此窗口在每个步长都保持同样的宽度,并且每个步长重新读取的数据几乎与前一个步长相同。一个 6h 的仪表板若使用 [$__range] 窗口,读取的样本量约为同一面板使用 [$__dd_interval] 时的 180 倍;用户缩小视图时,这个倍数还会增加——而这正是仪表板超时发生的时刻。Oodle 会用警告三角形标记此类面板。
请改用随时间范围变化的窗口:
| 变量 | 含义 | 用途 |
|---|---|---|
$__rate_interval | Grafana 内置的速率窗口,等于 max(step + scrape interval, 4 x scrape interval),确保速率计算始终有足够的样本。 | 在图表上使用 rate() 和 increase() |
$__dd_interval | Oodle 根据时间范围选择的汇总窗口,与 Datadog 默认值一致。上限为 4h。 | 默认汇总,以及统计面板总计 |
$__large_interval | 同一时间范围下比 $__dd_interval 更粗略的汇总窗口。上限为 12h。 | 柱状图和较长的回看窗口 |
两个 Oodle 汇总窗口的解析结果如下:
| 仪表板范围 | $__dd_interval | $__large_interval |
|---|---|---|
| 1 小时 | 20s | 1m |
| 6 小时 | 2m | 5m |
| 24 小时 | 5m | 20m |
| 7 天 | 1h | 4h |
| 30 天 | 4h | 12h |
这三个窗口都会随仪表板时间范围增减,因此无论用户将视图缩小到多大范围,每个步长读取的样本数都大致相同。
# ✅ CORRECT — the window shrinks and grows with the dashboard range
"expr": "sum(rate(http_requests_total[$__rate_interval]))"
# ✅ CORRECT — coarser rollup for a bar chart
"expr": "topk(10, sum by (route) (rate(http_request_duration_seconds_sum[$__large_interval])))"
# ❌ WRONG — one flat line at the range average, read 180 times over
"expr": "sum(rate(http_requests_total[$__range]))"
# ❌ WRONG — `$__range_s` is there to undo an `increase`; use `rate` instead
"expr": "sum(increase(http_requests_total[$__range])) / $__range_s"
对窗口求和作为统计面板结果,而不是对 $__range 求和
显示整个时间范围内一个数值的统计面板确实需要每个样本,但不需要一次性读取。将时间范围拆分为多个窗口,再把各个窗口的结果加起来。
# ✅ CORRECT — window == min step, range query, reduced back to one number
"expr": "sum(increase(http_requests_total[$__dd_interval]))"
"interval": "$__dd_interval"
"instant": false
"transformations": [{"id": "reduce", "options": {"reducers": ["sum"]}}]
# ❌ WRONG — instant query that reads the whole range on every refresh
"expr": "sum(increase(http_requests_total[$__range]))"
"instant": true
将最小步长设为与窗口相同,这样各个窗口才能恰好覆盖整个时间范围:步长更小会重复计算样本,步长更大则会漏掉样本。根据函数选择对应的归约器——increase 使用 sum,sum_over_time 和 count_over_time 使用 sum,max_over_time 使用 max,min_over_time 使用 min,avg_over_time 使用 mean。不要用这种方式拆分 quantile_over_time、changes 或 resets;归约后的值并不等于整个时间范围内的值。
始终先执行 get 再执行 update,以保留面板配置
使用 "overwrite": true 保存会替换整个文档。载荷中缺少的面板会被删除。
# ✅ CORRECT
oodle dashboards get dash_123 -o json > dash.json
jq '{dashboard: (.dashboard | .panels[0].title = "Request rate (per second)"), folderUid: .meta.folderUid, overwrite: true}' dash.json > dash.new.json
oodle dashboards create -f dash.new.json
# ❌ WRONG — sends a single-field payload; all panels disappear
oodle dashboards create -f <(echo '{"dashboard":{"uid":"dash_123","description":"updated"},"overwrite":true}')
创建仪表板时始终设置 folderUid
根文件夹中的仪表板很难被团队发现。
# ✅ CORRECT
"folderUid": "platform"
# ❌ WRONG — omitting folderUid means root
"folderUid": null
对共享仪表板使用“两阶段重命名 → 等待 → 删除”模式
直接硬删除仪表板会破坏所有外部链接(操作手册、Slack 表情反应、书签)。
# ✅ CORRECT — phase 1: rename
oodle dashboards create -f dash.deletion.json
# wait, confirm no traffic, then phase 2: delete
oodle dashboards delete dash_123 --force
# ❌ WRONG — same-day delete on a shared dashboard
oodle dashboards delete dash_123 --force
使用 service 和 team 标签为仪表板添加标签
标签可让仪表板更容易搜索,也方便其他工具(例如服务目录)链接到它们。
# ✅ CORRECT
"tags": ["service:api", "team:platform", "env:prod"]
# ❌ WRONG
"tags": []
故障处理
| 错误 | 原因 | 修复方法 |
|---|---|---|
| 401 Unauthorized | API 密钥无效或缺失 | 运行 oodle configure 或设置 OODLE_API_KEY |
| 404 Not Found | 仪表板或文件夹 ID 不存在 | 使用 oodle dashboards list -o json 验证 |
| connection refused | OODLE_DEPLOYMENT URL 错误 | 检查 OODLE_DEPLOYMENT 环境变量 |
folder not found | folderUid 指向已删除的文件夹 | 使用 oodle folders list -o json 列出文件夹;选择现有 ID 或创建一个 |
| 更新后面板消失 | 保存时 "overwrite": true 使用了不完整的载荷 | 根据最近一次 get 快照重新创建;今后始终遵循 get → 编辑 → 保存 |
| 无法删除文件夹 | 文件夹中仍有仪表板,且 CLI 没有 folders delete | 先移动或删除仪表板(`oodle dashboards list -o json |
| 429 Too Many Requests | 批量同步仪表板 | 添加 --retries 3,将创建频率限制在每秒少于 10 个 |