A reusable OpenClaw usage library with two entrypoints: an interactive Streamlit dashboard and a short-report skill CLI.
OpenClaw Usage Observer 现在明确是一个“核心库 + 多入口”项目:
Streamlit入口:适合人来观察、筛选、归因、下钻skill入口:适合 OpenClaw / cron / 自动化流程生成简短汇报
共享链路如下:
OpenClaw JSON / JSONL -> src.ingest -> SQLite -> src.analytics -> src.reporting -> Streamlit UI / Skill CLI
| 能力 | 说明 |
|---|---|
| 监控 | 查看总 token、总 cost、时间趋势和高消耗 turn |
| 统计 | 按 skill、model、session、channel 聚合 |
| 分析 | 发现上下文膨胀、异常 cron job、增长来源 |
| 钻取 | 按单个 session 查看 turn usage、上下文历史和对话内容 |
| 汇报 | 生成短 Markdown 简报或结构化 JSON 汇报 |
直接从 GitHub 安装核心能力:
pip install "openclaw-usage-observer @ git+https://github.com/wanng-ide/openclaw-usage-observer.git"安装后可直接运行:
openclaw-usage-brief --output-format markdown --output ./out/openclaw-usage-report.md如果你需要结构化 JSON:
openclaw-usage-brief --output-format json --output ./out/openclaw-usage-report.json --state ./out/openclaw-usage-state.json如果你是本地克隆仓库后使用 UI,推荐安装 streamlit 扩展依赖:
pip install -e ".[streamlit]"
streamlit run app.py默认访问地址通常是 http://localhost:8501。
推荐把 GitHub Raw 作为 skill 文档入口:
https://raw.githubusercontent.com/wanng-ide/openclaw-usage-observer/main/skills/openclaw-usage-brief/SKILL.md
这样 GitHub 就是唯一正式分发入口,文档、代码和输出契约始终保持同源。
如果仓库保持私有,则 git+https 安装和 Raw 文档访问都需要 GitHub 认证。
除了环境变量,这个项目现在也支持一个可直接编辑的 JSON 配置文件。
默认优先读取:
--config /path/to/config.jsonOPENCLAW_USAGE_CONFIG- 当前工作目录下的
./config/openclaw-usage-observer.json - 用户级配置
~/.config/openclaw-usage-observer/config.json
配置示例:
{
"openclaw_root": "/path/to/openclaw-root",
"db_path": "/path/to/openclaw_usage.sqlite",
"lookback_days": 7,
"top_n": 3,
"refresh": true,
"output_format": "markdown",
"report_path": "./out/openclaw-usage-report.md",
"state_path": "./out/openclaw-usage-state.json"
}如果你希望只初始化配置文件:
openclaw-usage-brief --init-config --yes如果你希望首次运行时确认默认路径是否正确:
openclaw-usage-brief --interactive-setup当 CLI 检测到尚无配置文件,且当前终端可交互时,也会主动询问是否用默认 OpenClaw 路径和 SQLite 路径初始化配置。
本地克隆仓库后,可以直接执行:
python scripts/bootstrap.py --profile streamlit它会:
- 自动安装对应依赖
- 生成配置文件
- 在可交互终端中确认默认路径
如果你只想初始化而不安装依赖:
python scripts/bootstrap.py --skip-install --yes所有关键配置都支持统一读取顺序:
- CLI 参数优先
- 环境变量次之
- JSON 配置文件再次之
- 代码默认值最后兜底
推荐变量如下:
export OPENCLAW_ROOT=/path/to/openclaw-root
export OPENCLAW_USAGE_DB_PATH=/path/to/openclaw_usage.sqlite
export OPENCLAW_USAGE_LOOKBACK_DAYS=7
export OPENCLAW_USAGE_TOP_N=3
export OPENCLAW_USAGE_REFRESH=true
export OPENCLAW_USAGE_OUTPUT_FORMAT=json
export OPENCLAW_USAGE_REPORT_PATH=./out/openclaw-usage-report.json
export OPENCLAW_USAGE_STATE_PATH=./out/openclaw-usage-state.json例如:
openclaw-usage-brief在没有额外 CLI 参数时,会自动读取上述环境变量。
如果你更希望手动执行摄取:
python -m src.ingest --openclaw-root "$OPENCLAW_ROOT" --db-path "$OPENCLAW_USAGE_DB_PATH"当前接入以下数据源:
| 路径 | 用途 |
|---|---|
<OPENCLAW_ROOT>/agents/main/sessions/*.jsonl |
assistant usage、session_status、对话明细 |
<OPENCLAW_ROOT>/agents/main/sessions/sessions.json |
session 快照与元数据 |
<OPENCLAW_ROOT>/cron/runs/*.jsonl |
cron run token、状态、耗时 |
<OPENCLAW_ROOT>/cron/jobs.json |
cron job 配置与 skill 关联 |
<OPENCLAW_ROOT>/agents/main/agent/models.json |
模型定价、上下文窗口、max tokens |
这个项目不会自己重新分词,也不会用本地 tokenizer 估算 token。
所有 token 数字都直接来自 OpenClaw 日志里已经存在的 usage 或 status 信息。
主口径来自 <OPENCLAW_ROOT>/agents/main/sessions/*.jsonl 中的 assistant 消息:
- 只处理
type == "message" - 只统计
message.role == "assistant"
写入 session_usage_fact 时的字段映射如下:
input_tokens<-message.usage.inputoutput_tokens<-message.usage.outputcache_read_tokens<-message.usage.cacheReadcache_write_tokens<-message.usage.cacheWritetotal_tokens<-message.usage.totalTokenstotal_cost<-message.usage.cost.total
因此首页和大多数图表里的:
- 总 Tokens
- 总 Cost
- Session 排行
- Model 排行
- Channel 排行
- Top turn 消耗
本质上都是在聚合 session_usage_fact。
因为一轮真实的模型调用结果,通常会在 assistant 消息里携带完整 usage。
当前实现只把 assistant 消息记为一条 usage 事实,避免把同一轮调用在多个来源里重复累计。
核心原则:
- 全局 token / cost 以
session_usage_fact为准 cron_run_fact不再叠加进全局总 token,避免双重统计
skill 排行来自:
<OPENCLAW_ROOT>/cron/runs/*.jsonl<OPENCLAW_ROOT>/cron/jobs.json
其中:
cron_run_fact.total_tokens<-usage.total_tokenscron_run_fact.input_tokens<-usage.input_tokenscron_run_fact.output_tokens<-usage.output_tokens
随后再通过 cron_job_dim.parsed_skill_name 关联聚合出 skill 排行。
上下文压力来自 session_status 工具输出文本,而不是 assistant usage。
当前会解析出以下字段并写入 session_context_fact:
tokens_intokens_outcontext_used_tokenscontext_limit_tokenscompactions
这一部分只用于分析,不再计入全局总 token。
- session cost:直接读取
message.usage.cost.total - cron cost:根据
cron日志中的 input / output token,结合models.json中的模型单价回填近似值
总 token= assistant session 消息里usage.totalTokens的累计值skill token= cron run 中usage.total_tokens按 skill 聚合后的值context token=session_status解析出的上下文指标,仅用于上下文分析
| 页面 | 说明 |
|---|---|
监控 |
看总 token / cost、趋势和最高消耗 turn |
统计 |
看 skill、model、session、channel 排行 |
分析 |
看上下文压力、cron 异常、增长来源和高消耗 session |
Session 钻取 |
深入查看单个 session 的 usage、上下文与对话 |
openclaw-usage-observer/
├── .env.example
├── README.md
├── pyproject.toml
├── requirements-core.txt
├── requirements-streamlit.txt
├── requirements-dev.txt
├── requirements.txt
├── app.py
├── scripts/
│ └── bootstrap.py
├── skills/
│ └── openclaw-usage-brief/
│ ├── SKILL.md
│ ├── README.md
│ └── run.py
├── src/
│ ├── __init__.py
│ ├── settings.py
│ ├── db.py
│ ├── ingest.py
│ ├── analytics.py
│ ├── reporting.py
│ ├── skill_cli.py
│ └── views.py
└── data/
└── openclaw_usage.sqlite
各模块职责如下:
| 文件 | 作用 |
|---|---|
app.py |
Streamlit 入口,负责页面控件、刷新和视图组织 |
src/ingest.py |
从 OpenClaw 日志读取数据并写入 SQLite |
src/analytics.py |
SQL 聚合、排行、异常分析、session 查询 |
src/reporting.py |
统一生成简报文本和结构化 report payload |
src/skill_cli.py |
安装后可直接调用的 skill CLI 入口 |
skills/openclaw-usage-brief/run.py |
仓库内 skill 包装器,复用同一份 CLI 逻辑 |
scripts/bootstrap.py |
自动安装依赖并生成初始配置文件 |
src/views.py |
Streamlit 页面样式、卡片、图表和表格渲染 |
src/settings.py |
统一配置解析、默认值和正则配置 |
- 当前 token 统计依赖日志中已有的
usage字段,不做独立 tokenizer 校验 - 全局 token 与 skill token 使用不同事实表,设计上避免了重复累计
Session 钻取会显示真实对话内容;如果用于公开演示,建议使用匿名数据源data/、out/和 SQLite / report 文件都是运行时产物,不应提交到仓库
- Overview
- Highlights
- GitHub Install And Usage
- JSON Config And Initialization
- Environment Variables
- Data Sources
- How Token Accounting Works
- Pages
- Project Structure
- Current Boundaries
OpenClaw Usage Observer is now explicitly a reusable core library with two entrypoints:
Streamlit: interactive monitoring, investigation, and drill-downskillCLI: short reports for OpenClaw, cron, or other automation
Shared pipeline:
OpenClaw JSON / JSONL -> src.ingest -> SQLite -> src.analytics -> src.reporting -> Streamlit UI / Skill CLI
| Capability | Description |
|---|---|
| Monitoring | Track total tokens, total cost, time trends, and expensive turns |
| Statistics | Aggregate by skill, model, session, and channel |
| Analysis | Inspect context growth, anomalous cron jobs, and contribution spikes |
| Drill-down | Open a single session and inspect usage, context history, and conversation text |
| Reporting | Generate short Markdown briefs or structured JSON reports |
Install the core runtime directly from GitHub:
pip install "openclaw-usage-observer @ git+https://github.com/wanng-ide/openclaw-usage-observer.git"Then run the brief CLI:
openclaw-usage-brief --output-format markdown --output ./out/openclaw-usage-report.mdFor structured JSON output:
openclaw-usage-brief --output-format json --output ./out/openclaw-usage-report.json --state ./out/openclaw-usage-state.jsonIf you cloned the repository locally and want the UI, install the streamlit extra:
pip install -e ".[streamlit]"
streamlit run app.pyBy default, Streamlit opens at http://localhost:8501.
Use GitHub Raw as the canonical skill document endpoint:
https://raw.githubusercontent.com/wanng-ide/openclaw-usage-observer/main/skills/openclaw-usage-brief/SKILL.md
This keeps documentation, code, and output contracts sourced from the same GitHub repository.
If the repository stays private, both git+https installation and Raw document access require GitHub authentication.
Besides environment variables, the project now supports a directly editable JSON config file.
Lookup order:
--config /path/to/config.jsonOPENCLAW_USAGE_CONFIG./config/openclaw-usage-observer.jsonin the current working directory- user-level config at
~/.config/openclaw-usage-observer/config.json
Example:
{
"openclaw_root": "/path/to/openclaw-root",
"db_path": "/path/to/openclaw_usage.sqlite",
"lookback_days": 7,
"top_n": 3,
"refresh": true,
"output_format": "markdown",
"report_path": "./out/openclaw-usage-report.md",
"state_path": "./out/openclaw-usage-state.json"
}To only initialize the config file:
openclaw-usage-brief --init-config --yesTo interactively confirm detected defaults:
openclaw-usage-brief --interactive-setupWhen the CLI detects that no config file exists and the terminal is interactive, it will also offer to initialize the config file automatically.
After cloning the repository locally, run:
python scripts/bootstrap.py --profile streamlitThis will:
- install the requested dependency profile
- write the config file
- confirm detected default paths in an interactive terminal
To only initialize without installing dependencies:
python scripts/bootstrap.py --skip-install --yesAll key configuration now follows one precedence order:
- CLI arguments first
- environment variables second
- JSON config third
- code defaults last
Recommended variables:
export OPENCLAW_ROOT=/path/to/openclaw-root
export OPENCLAW_USAGE_DB_PATH=/path/to/openclaw_usage.sqlite
export OPENCLAW_USAGE_LOOKBACK_DAYS=7
export OPENCLAW_USAGE_TOP_N=3
export OPENCLAW_USAGE_REFRESH=true
export OPENCLAW_USAGE_OUTPUT_FORMAT=json
export OPENCLAW_USAGE_REPORT_PATH=./out/openclaw-usage-report.json
export OPENCLAW_USAGE_STATE_PATH=./out/openclaw-usage-state.jsonFor example:
openclaw-usage-briefwill automatically pick those values when matching CLI flags are omitted.
If you want to ingest manually:
python -m src.ingest --openclaw-root "$OPENCLAW_ROOT" --db-path "$OPENCLAW_USAGE_DB_PATH"| Path | Purpose |
|---|---|
<OPENCLAW_ROOT>/agents/main/sessions/*.jsonl |
assistant usage, session_status, and conversation details |
<OPENCLAW_ROOT>/agents/main/sessions/sessions.json |
session snapshots and metadata |
<OPENCLAW_ROOT>/cron/runs/*.jsonl |
cron run tokens, status, and duration |
<OPENCLAW_ROOT>/cron/jobs.json |
cron job configuration and skill linkage |
<OPENCLAW_ROOT>/agents/main/agent/models.json |
model pricing, context window, and max token metadata |
This project does not re-tokenize message text locally and does not estimate token counts with a separate tokenizer.
All token numbers come directly from usage or status fields already written by OpenClaw.
The primary source is assistant messages in <OPENCLAW_ROOT>/agents/main/sessions/*.jsonl:
- only rows where
type == "message" - only rows where
message.role == "assistant"
Those rows are mapped into session_usage_fact as:
input_tokens<-message.usage.inputoutput_tokens<-message.usage.outputcache_read_tokens<-message.usage.cacheReadcache_write_tokens<-message.usage.cacheWritetotal_tokens<-message.usage.totalTokenstotal_cost<-message.usage.cost.total
That means most dashboard totals and rankings are aggregates over session_usage_fact.
In practice, a completed model call usually carries its full usage payload on the assistant message.
The current implementation records one usage fact per assistant response to avoid double counting the same call across multiple log sources.
Core rule:
- global token / cost totals come from
session_usage_fact cron_run_factis not added again to the global total
Skill ranking comes from:
<OPENCLAW_ROOT>/cron/runs/*.jsonl<OPENCLAW_ROOT>/cron/jobs.json
Specifically:
cron_run_fact.total_tokens<-usage.total_tokenscron_run_fact.input_tokens<-usage.input_tokenscron_run_fact.output_tokens<-usage.output_tokens
Context pressure is derived from session_status tool output rather than assistant usage.
The parser extracts these fields into session_context_fact:
tokens_intokens_outcontext_used_tokenscontext_limit_tokenscompactions
These metrics are used only for context analysis and are not added again to global totals.
- session cost: read directly from
message.usage.cost.total - cron cost: approximated from cron input / output tokens using model prices from
models.json
global tokens= sum ofusage.totalTokensfrom assistant session messagesskill tokens= sum ofusage.total_tokensfrom cron runs grouped by skillcontext tokens= parsed context metrics fromsession_status, used only for context analysis
| Page | Description |
|---|---|
监控 (Monitoring) |
Total token / cost monitoring, trends, and expensive turns |
统计 (Statistics) |
Rankings by skill, model, session, and channel |
分析 (Analysis) |
Context pressure, cron anomalies, growth breakdowns, and high-cost sessions |
Session 钻取 (Session Drill-down) |
Detailed inspection of one session's usage, context, and conversation |
openclaw-usage-observer/
├── .env.example
├── README.md
├── pyproject.toml
├── requirements-core.txt
├── requirements-streamlit.txt
├── requirements-dev.txt
├── requirements.txt
├── app.py
├── scripts/
│ └── bootstrap.py
├── skills/
│ └── openclaw-usage-brief/
│ ├── SKILL.md
│ ├── README.md
│ └── run.py
├── src/
│ ├── __init__.py
│ ├── settings.py
│ ├── db.py
│ ├── ingest.py
│ ├── analytics.py
│ ├── reporting.py
│ ├── skill_cli.py
│ └── views.py
└── data/
└── openclaw_usage.sqlite
Core responsibilities:
| File | Responsibility |
|---|---|
app.py |
Streamlit entrypoint, controls, refresh actions, and view routing |
src/ingest.py |
Reads OpenClaw logs and writes normalized facts into SQLite |
src/analytics.py |
SQL aggregation, rankings, anomaly detection, and session queries |
src/reporting.py |
Shared report payload and summary markdown generation |
src/skill_cli.py |
Installed skill CLI entrypoint |
skills/openclaw-usage-brief/run.py |
In-repo wrapper that reuses the same CLI logic |
scripts/bootstrap.py |
Installs dependency profiles and writes the initial config file |
src/views.py |
Streamlit styling, cards, charts, and tables |
src/settings.py |
Unified config resolution, defaults, and parsing regexes |
- Token accounting depends on usage fields already present in logs and does not independently validate them with a tokenizer
- Global totals and skill totals intentionally come from different fact tables to avoid double counting
Session Drill-downshows real conversation text; use anonymized logs for public demosdata/,out/, SQLite files, and generated reports are runtime artifacts and should not be committed





