做 OpenClaw 智能体实战,最让我肉疼的从来不是服务器内存,也不是半夜告警声响个没完,而是每天早上打开 API 账单的那一刻。我自己踩过一回真实的坑:挂了个长期任务,让智能体每隔半小时抓一次数据并总结,结果第二天早上发现账单比预期翻了三倍。问题不是模型变贵了,而是智能体在循环里反复重试同样的请求,日志里还不断追加历史上下文,每一次重试都在为前面所有内容重新付费。那时候你才会意识到,Token 消耗失控不是玄学,是缺一套“看得见、算得清、刹得住”的监控体系。
这篇文章是 OpenClaw 企业级智能体实战系列的第 7 篇,聚焦一件事:终结 Token 消耗失控。我会完整拆解两套治理方案,方案一是基于 Agent Dashboard 的可视化账本,让你把每一笔 Token 花销摊开看;方案二是基于阿里云 SLS 的实时日志监控与告警,让消费异常在第一时间触发通知。两套方案都会给完整可落地的代码,最后还会讲一个我自己在用的 Token 消耗预测与突增检测算法。适合已经在跑 OpenClaw、或者准备把它当成生产级工具的同学,也适合被“月底开盲盒式账单”折磨过的所有人。
1. 先搞清楚 Token 到底消耗在哪了
1.1 失控的三种典型场景
Token 消耗失控不是突然发生的,它是一个缓慢积累、然后某个瞬间爆发的量变过程。我复盘过自己那次三倍账单,也帮几个同行排查过类似问题,发现失控案例基本都落在三种场景里。
第一种是循环重试。智能体在执行任务时,如果调用外部接口返回了错误,或者 LLM 返回的格式不符合解析预期,它可能会自动重试。问题是很多框架的重试策略会把完整的历史对话上下文一起带上,而不是只带出错的那一步。一次重试的 Token 成本和第一次调用差不多,重试十次就是十倍。而且如果错误不是立即暴露,而是经过好几轮工具调用之后才被发现,重试时甚至会重复执行前面所有工具调用,消耗量直接滚雪球。
第二种是上下文膨胀。OpenClaw 这类智能体框架,默认会把系统提示词、历史消息、工具定义、中间输出全部拼进每一次 LLM 请求里。单看一次请求可能就几千 Token,感觉不多,但如果你让它跑一个需要持续数小时的批量任务,每轮对话都会把之前的对话摘要、原始数据片段重新塞进上下文。到任务后半段,单次请求的 Token 消耗可能是任务刚开始时的好几倍。这就等于你每次和人聊天,都要把前面所有聊过的内容重新复述一遍。
第三种是多智能体互调。企业级场景下很少有人只跑一个智能体,通常是主 Agent 调度几个子 Agent,每个子 Agent 负责不同业务线。如果设计得不够精细,子 Agent 之间会反复传递同一批大文本,主 Agent 还要把所有子 Agent 的中间结果再汇总一次。原本一个模型调用就能解决的问题,被拆成了五六次调用,而且中间数据在多个会话之间来回拷贝,Token 消耗呈指数级上升。
这三种场景的共同特点是:只看总量你根本不知道问题出在哪。所以我一直坚持一个观点,Token 治理的第一步不是优化,而是记账。明细都看不清,所有的优化动作都是盲人摸象。
1.2 记录是第一步:找到开销明细的源头
OpenClaw 默认会把消息和调用记录存在本地 SQLite 数据库里,数据目录一般在~/.openclaw/下。不同版本的存储结构可能有差异,但思路是一致的:框架每次调用 LLM 之后,都会生成一条包含模型、输入 Token 数、输出 Token 数、总 Token 数、会话 ID、时间戳的使用记录。
我先用命令行看一眼表结构,这是最稳妥的做法,别直接照抄网上代码:
sqlite3 ~/.openclaw/data/openclaw.db .tables .schema usage_events在我的环境里,usage_events表的字段大概是这样的:
| 字段名 | 含义 |
|---|---|
| id | 主键 |
| session_id | 会话 ID |
| model | 模型名称,如 gpt-4o-mini |
| prompt_tokens | 输入 Token 数 |
| completion_tokens | 输出 Token 数 |
| total_tokens | 总 Token 数 |
| timestamp | 调用时间戳(毫秒) |
如果你用的版本里表名不一样,也可以用PRAGMA table_info(表名)查看实际字段。这里要多说一句:我见过不少人把“计费 Token”和“登录鉴权 Token”搞混。网上搜 OpenClaw Token,跳出来一大堆token exchange failed、sign-in could not be completed,那是 OAuth 登录认证的问题,跟账单飞涨没有关系。如果登录报错,去查凭证配置;如果账单飞涨,来看本文的监控方案。这两件事从根上就不是一回事。
有了明细表,下一步就是把它变成能指导决策的指标。
2. 方案一:Agent Dashboard,自己动手把账本摊开
2.1 仪表盘的数据从哪来
Agent Dashboard 是 OpenClaw 生态里配套的可视化面板,默认绑定本地数据目录,能展示消息流、会话列表、任务状态这些基础信息。它的价值在于开箱即用,部署完 OpenClaw 就能看到智能体在干什么。但说实话,它默认的统计页面偏“展示”,适合看过程和状态,不适合深入做成本分析。
要看透 Token 消耗,核心是把原始明细变成三张表:按天汇总、按模型汇总、按会话汇总。这三张表不仅能回答“花了多少”,还能回答“花在哪了”和“哪一笔最可疑”。Agent Dashboard 通常支持自定义面板,我们可以把聚合结果同步进去,实现可视化看板。
这里要提醒一下:如果你的 Agent Dashboard 只是连接了本地 SQLite,最好的做法不是直接改它的底层数据,而是新建一张独立的汇总表。这样既不影响原有功能,又能让 Dashboard 读取到成本数据。如果你和我一样,用 Docker 部署 OpenClaw,数据库文件在宿主机挂载目录里,路径可能是/data/openclaw.db,先确认挂载配置再操作。
2.2 用 Python 把 OpenClaw 的 Token 账本算清楚
我的做法是写一个 Python 脚本,直接从 SQLite 读数据,用 Pandas 做聚合。脚本的核心逻辑很简单:读取usage_events表,按天、按模型、按会话分别聚合总 Token 数,然后输出统计结果。
import sqlite3 import pandas as pd from datetime import datetime DB_PATH = "/data/openclaw.db" conn = sqlite3.connect(DB_PATH) df = pd.read_sql_query(""" SELECT strftime('%Y-%m-%d', datetime(timestamp/1000, 'unixepoch', 'localtime')) AS day, model, session_id, prompt_tokens, completion_tokens, total_tokens FROM usage_events """, conn) conn.close() # 按天汇总 daily = df.groupby("day")["total_tokens"].sum().reset_index() print("=== 每日 Token 消耗 ===") print(daily) # 按模型汇总 by_model = df.groupby("model")["total_tokens"].sum().reset_index().sort_values("total_tokens", ascending=False) print("\n=== 按模型 Token 消耗 ===") print(by_model) # 按会话汇总,取 TOP 10 top_sessions = ( df.groupby("session_id")["total_tokens"] .sum() .reset_index() .sort_values("total_tokens", ascending=False) .head(10) ) print("\n=== 会话 TOP 10 ===") print(top_sessions)这段代码跑完之后,你手里就有了最核心的成本账本。按天汇总用于看趋势,按模型汇总用于评估模型选型是否划算,按会话汇总用于揪出“哪个任务在偷偷烧钱”。
我曾经靠这张按会话的 TOP 10 表,定位到一个非常隐蔽的问题:有个会话的 Token 消耗占了全天的 40%,点进去一看,原来是任务配置里的轮询间隔写错了,导致智能体每 10 秒就调用一次 LLM 做状态判断,而不是按预期等待 5 分钟。这种问题,不按会话拆开根本发现不了。
如果你想把结果直接同步到 Agent Dashboard,可以在数据库里新建一张token_daily_summary表,把聚合结果写进去,然后在 Dashboard 里配置 SQL 查询读取这张表。示例写入逻辑如下:
daily.to_sql("token_daily_summary", conn, if_exists="replace", index=False)注意,to_sql需要 Pandas 的 SQLAlchemy 支持,如果没有安装,可以改用insert语句手动写入。这里不展开,思路就是把聚合结果落库,供 Dashboard 查询。
2.3 在 Dashboard 上建立“预算警戒线”
账本摊开之后,下一步是设定预算警戒线。我建议别只盯着“花了多少钱”,要建立一个比例指标:当日消耗占当日预算的百分比。当这个比率超过 80% 时,图表颜色就要变,提醒你该踩刹车了。
在 Agent Dashboard 里,可以用一段 SQL 直接实现这个指标。假设我在数据库里维护了一张daily_budget表,里面有日期和预算金额,再结合token_daily_summary表,就可以算出消耗占比:
SELECT s.day, s.total_tokens, b.budget_tokens, ROUND(s.total_tokens * 100.0 / b.budget_tokens, 2) AS consume_percent FROM token_daily_summary s LEFT JOIN daily_budget b ON s.day = b.day WHERE s.day = CURRENT_DATE ORDER BY s.day DESC;这里有个细节:预算单位要统一。我习惯把 Token 数量和金额分开管理,因为不同模型单价不一样。更精确的做法是先在明细表里增加一列cost,用模型单价乘以 Token 数,得到每次调用的金额,然后再按天汇总。这样 Dashboard 里既能看 Token 趋势,也能看真实成本。
在 Dashboard 上设完警戒线之后,有一个认知必须建立起来:Agent Dashboard 方案的本质是“事后看清楚”。它的优点是无侵入、部署快、代码量小,特别适合个人项目和刚起步的小团队。它的缺点是只负责展示,不会主动喊你。如果预算是一天一崩,等你早上打开 Dashboard 看到警戒线变红时,钱已经花出去了。所以我一直把 Dashboard 称作“账本”,它解决的是糊涂账问题,解决不了“失控瞬间”的实时拦截。
这也正是我引入第二套方案的直接原因。
3. 方案二:阿里云 SLS,把 Token 账本变成实时监控
3.1 为什么要用 SLS 而不是本地看板
本地看板能解决“事后看账”,但解决不了“事中感知”。阿里云 SLS(日志服务)能补上三个 Dashboard 做不到的能力。
第一是实时采集。每次 LLM 调用一结束,就把用量上报到 SLS,日志从产生到可查询的延迟通常在秒级。这意味着你能看到“刚刚过去 5 分钟花了多少 Token”,而不是“昨天花了多少”。
第二是集中查询。如果你用 OpenClaw 部署了多个实例,比如开发环境一个、生产环境一个,本地 SQLite 各自为政,没法统一看总账。SLS 可以把所有实例的 Token 用量汇总到同一个 Logstore,按环境、按业务线任意切分。
第三是托管告警。SLS 自带告警能力,查询结果超过阈值就触发通知,支持钉钉、企业微信、邮件、Webhook 等多种渠道。你不用自己写 Cron 脚本轮询账单,不用自己维护告警服务,SLS 帮你把这些都托管了。
用一句话概括我的选型逻辑:Agent Dashboard 适合回答“昨天花了多少”,SLS 适合回答“现在正在花多少,是否正常”。两套方案不是二选一,而是互补。
3.2 日志采集与上报代码
要用 SLS,前置条件是有一台阿里云账号、开通日志服务、创建一个 Project 和一个 Logstore。Project 相当于日志项目容器,Logstore 是具体的日志库。创建过程在控制台点几下就能完成,不再赘述,重点说代码。
我用的上报方式是阿里云官方 Python SDK,叫aliyun-log-python-sdk。安装命令如下:
pip install aliyun-log-python-sdk然后写一个上报函数。核心是把每次 LLM 调用的用量信息构造成 LogItem,调用PutLogsRequest发送到指定 Logstore:
import os import time from aliyun.log import LogClient, PutLogsRequest, LogItem ENDPOINT = "cn-hangzhou.log.aliyuncs.com" PROJECT = "openclaw-token-monitor" LOGSTORE = "token-usage" ACCESS_KEY_ID = os.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID") ACCESS_KEY_SECRET = os.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET") client = LogClient(ENDPOINT, ACCESS_KEY_ID, ACCESS_KEY_SECRET) def report_usage(session_id, model, prompt_tokens, completion_tokens): total = prompt_tokens + completion_tokens log_item = LogItem() log_item.set_contents( session_id=str(session_id), model=str(model), prompt_tokens=str(prompt_tokens), completion_tokens=str(completion_tokens), total_tokens=str(total), timestamp=str(int(time.time() * 1000)), env=os.getenv("OPENCLAW_ENV", "dev"), ) req = PutLogsRequest(PROJECT, LOGSTORE, topic="openclaw", log_items=[log_item]) client.put_logs(req)这里有几个细节值得展开。
第一,set_contents的参数都是字符串,SDK 的日志格式要求这样。数值类字段先转成字符串,查询时再用 SLS 的sum、avg函数做计算。
第二,LOGSTORE的名称只允许小写字母、数字和短横线,命名时注意避开大写。
第三,上报时机很关键。我是在 OpenClaw 的调用回调里上报。如果框架支持事件钩子,就在 LLM 响应事件里调用report_usage;如果不支持,可以在包装层加一个中间件,统一接收所有 LLM 请求的响应体。还有一种更简单的手段:定期扫描本地 SQLite,把新增记录增量同步到 SLS。这个方案对框架无侵入,实时性差一些,但实现成本最低。下面这段代码展示增量同步的思路:
import sqlite3 import time LAST_ID_FILE = "/tmp/openclaw_last_sync_id" def sync_since(last_id): conn = sqlite3.connect(DB_PATH) rows = conn.execute(""" SELECT id, session_id, model, prompt_tokens, completion_tokens, total_tokens, timestamp FROM usage_events WHERE id > ? ORDER BY id ASC """, (last_id,)).fetchall() conn.close() for row in rows: report_usage( session_id=row[1], model=row[2], prompt_tokens=row[3], completion_tokens=row[4], ) last_id = row[0] with open(LAST_ID_FILE, "w") as f: f.write(str(last_id)) return last_id last_id = 0 try: with open(LAST_ID_FILE, "r") as f: last_id = int(f.read().strip()) except FileNotFoundError: pass while True: last_id = sync_since(last_id) time.sleep(10)增量同步的好处是不用改动 OpenClaw 内部逻辑,把 SLS 当“下游数据仓库”,缺点是节点宕机或重启时可能有几秒延迟。实时性要求严格的生产环境,我还是推荐用回调直报。
3.3 查询分析与告警配置
数据进了 SLS,接下来就是查询和告警。
SLS 的查询语法类似 SQL,但融合了日志检索和统计分析。我先给几个最常用的分析语句。
按天汇总 Token 消耗:
* | SELECT date_format(__time__, '%Y-%m-%d') AS day, sum(total_tokens) AS tokens GROUP BY day ORDER BY day查询最近 5 分钟的总消耗:
* | SELECT sum(total_tokens) AS tokens WHERE __time__ > now() - 300按模型统计消耗占比:
* | SELECT model, sum(total_tokens) AS tokens, ROUND(sum(total_tokens) * 100.0 / sum(sum(total_tokens)) OVER (), 2) AS percent GROUP BY model ORDER BY tokens DESC LIMIT 10这三条语句基本覆盖了日常监控的 80% 场景:看趋势、看实况、看分布。
告警配置我建议这样做。在 SLS 控制台进入告警设置,新建一个告警监控规则,查询语句用“最近 5 分钟总消耗”,触发条件设成tokens > 200000,检查间隔设成 1 分钟,通知渠道选钉钉机器人。这样设计的意图是:5 分钟消耗超过 20 万 Token,说明当前的运行速率异常高,需要人工介入。
这个阈值不是拍脑袋定的,我经历过一次教训。一开始我设的是“每分钟超过 5 万 Token”,结果正常的批量任务在高峰期持续触发告警,五分钟内收到十几条通知,群里直接刷屏。后来我做了调整:先跑一周积累基线数据,算出一个 P95 值,也就是 95% 的情况下每分钟消耗不超过多少,再把告警阈值设在 P95 的 1.5 倍到 2 倍之间。这样既能捕捉异常,又不会被正常波动淹没。
提示:SLS 告警里的“触发条件”和“恢复通知”要分开配置。触发条件负责发出“出事了”的信号,恢复通知负责发出“已恢复”的信号。如果只配了触发通知,异常期间告警会每隔一个检查周期就重复触发一次,造成轰炸效果。建议开启“静默期”功能,避免重复告警。
4. 终结失控的核心:Token 消耗算法构建
4.1 滑动平均预测法:提前估算今日开销
有了实时数据,下一个问题自然浮现:能不能预测今天总共要花多少 Token?如果早上九点就能预测出今天会超预算,就可以提前干预,而不是等晚上账单出来再捶胸顿足。
我采用的预测算法是指数加权移动平均(EWMA),核心思想非常简单:今天的预测消耗,由“今日已经消耗的实际值”和“昨天的预测值”共同决定。公式如下:
预测值 = α × 今日实际消耗 + (1 - α) × 昨日预测值
参数 α 取值在 0 到 1 之间,它控制了预测对近期数据的敏感度。α 越大,预测越跟随昨天的实际值;α 越小,预测越平滑。我自己实践下来,智能体负载比较稳定的场景,α 取 0.7 左右效果不错;如果业务波动大,α 可以调高到 0.9,让预测更快响应近期变化。
Python 实现非常简单:
def ewma_predict(daily_usage, alpha=0.7): """输入每日 token 消耗列表,返回今天结束时的预测总消耗""" pred = daily_usage[0] for v in daily_usage[1:]: pred = alpha * v + (1 - alpha) * pred return pred用法也很直白:读取最近 7 天的每日消耗,传入函数,得到一个预测值。然后把这个预测值和今日预算放在一起比较。
但这里有个坑:EWMA 预测的是“全天总消耗”,而一天还没过完,预测值应该用“今日已消耗 + 剩余时间预测增量”来修正。完整逻辑是:
today_consumed = 80000 # 今日已消耗,从 SLS 实时查询 daily_budget = 150000 # 今日预算 # 最近 7 天每日消耗 history = [120000, 110000, 135000, 140000, 125000, 145000, 130000] predicted_total = ewma_predict(history, alpha=0.7) # 预计剩余消耗 remaining_pred = predicted_total - today_consumed # 判断是否超预算 if today_consumed + remaining_pred * 0.5 > daily_budget: # 如果按当前速率进行半天,就会超预算,触发预警 print("预警:今日预计超预算")补充一句:在很多场景下,一天的 Token 消耗并不是均匀分布的,白天业务高峰消耗快,凌晨任务少消耗慢。如果要做更精细的预测,可以把“一天 24 小时”按时间段切分,分别统计各时段的历史消耗均值,再结合当前时段做修正。这个思路相当于把 EWMA 升级成“分时段 EWMA”,代码量不大,但准确率提升比较明显。
4.2 突增检测:识别失控的瞬间
预测解决的是“今天会不会超”,突增检测解决的是“现在是不是失控”。两者场景不同,算法也不同。
我用的突增检测方法是滑动窗口 Z-score。原理是维护一个最近 N 次调用的消耗速率序列,计算它们的均值和标准差。如果当前速率比均值高出 2 个标准差以上,就认为出现了异常突增。
为什么选 2 个标准差?在正态分布假设下,大约 95% 的正常数据点落在均值 ±2 个标准差范围内。也就是说,当前速率超过均值加 2 倍标准差,意味着它比 95% 的历史情况都要高,是一个小概率事件。这样设置,既能捕获真正的异常,又不会因为偶发的正常波动而误报。
代码实现如下:
import numpy as np def detect_anomaly(rate_history, current_rate, k=2.0): arr = np.array(rate_history) mean = arr.mean() std = arr.std() if std == 0: return False, mean threshold = mean + k * std return current_rate > threshold, threshold用的时候,每隔 5 分钟计算一次“最近 5 分钟的 Token 消耗量 / 300 秒”,得到一个速率值,放进窗口数组。窗口大小我建议取 12 个点,也就是最近 1 小时的数据。窗口太短,统计不稳定;窗口太长,反应迟钝,异常维持很久才被识别。
突增检测还有一个容易被忽略的细节:要区分“单次调用量突增”和“调用频率突增”。前者是某一次请求带了超大上下文,后者是短时间内调用次数暴增。这两种情况对应的优化手段不同:单次调用量突增要从上下文管理入手,检查是不是历史消息无限累积;调用频率突增要从任务逻辑入手,检查是不是重试循环或轮询间隔过短。所以我在上报日志时,会同时记录total_tokens和request_count两个指标,检测时分别做 Z-score,效果更清晰。
4.3 成本分摊与配额控制
最后一块拼图是成本分摊与配额控制。企业级场景下,OpenClaw 往往不是一个人在跑,研发、运营、数据分析几个团队可能共用一个实例。如果总账单异常,第一步要分清楚是哪个业务线烧的。
我的做法是在上报日志时增加两个字段:business和cost_model。business标识业务线,cost_model标识成本模型,比如按部门、按项目、按任务类型。上报代码在原有的report_usage函数里扩展一下:
def report_usage(session_id, model, prompt_tokens, completion_tokens, business="default", cost_model=None): total = prompt_tokens + completion_tokens log_item = LogItem() log_item.set_contents( session_id=str(session_id), model=str(model), prompt_tokens=str(prompt_tokens), completion_tokens=str(completion_tokens), total_tokens=str(total), business=str(business), timestamp=str(int(time.time() * 1000)), ) req = PutLogsRequest(PROJECT, LOGSTORE, topic="openclaw", log_items=[log_item]) client.put_logs(req)对应地,SLS 查询语句就能按业务线切分成本:
* | SELECT business, sum(total_tokens) AS tokens, ROUND(sum(total_tokens) * 100.0 / sum(sum(total_tokens)) OVER (), 2) AS percent GROUP BY business ORDER BY tokens DESC有了成本分摊,配额控制才能落地。我设计的配额状态机很简单,只有四个状态:
正常 → 预警 → 熔断 → 恢复。
正常状态不做干预。当预测当日消耗将达到预算的 80% 时,进入预警状态,通过 SLS 告警发送通知,提醒负责人检查近期任务。当预测消耗超过预算的 100% 时,进入熔断状态,暂停新任务调度,只允许紧急任务通过。等消耗回落到安全水位,且当前速率低于正常阈值,才恢复执行。
熔断的执行方式有两种。一种是直接改 OpenClaw 调度配置,暂停任务队列;另一种更温和,是在 OpenClaw 入口前加一个拦截器,拦截新任务请求,返回“预算已用尽,请明日再试”。我个人更推荐拦截器方案,因为它不阻塞已经运行的任务,避免把正在执行的关键流程拦腰截断。
5. 实战避坑:我踩过的五个坑
5.1 表字段不同版本差异大
OpenClaw 迭代速度很快,SQLite 表名和字段名在不同版本之间可能变化。我一开始照着一个旧版本的文章写查询 SQL,结果在新版本上跑直接报错no such table: usage_events。排查了半天才发现是表名改成了llm_usage。所以不管代码从哪来,第一件事永远是PRAGMA table_info(表名)确认字段名。
5.2 同步上报日志会卡主流程
这是我踩过最疼的坑。第一次接入 SLS 时,我直接在 LLM 回调里同步调用了put_logs。结果 OpenClaw 的响应延迟从 1 秒涨到了 3 秒,因为每次请求都要等 HTTP 上报完成才返回。解决办法是改成异步上报,Python 里可以用ThreadPoolExecutor或者消息队列。
from concurrent.futures import ThreadPoolExecutor executor = ThreadPoolExecutor(max_workers=2) def report_usage_async(session_id, model, prompt_tokens, completion_tokens): executor.submit(report_usage, session_id, model, prompt_tokens, completion_tokens)把上报逻辑丢到线程池里,主流程立刻恢复丝滑。有一点要注意:线程池的max_workers不宜过大,2 到 4 个就够,避免上报线程抢占智能体主进程的 CPU 资源。
5.3 时区问题导致“按天汇总”数据错位
SLS 查询语句里用date_format(__time__)做按天聚合,但__time__是 SLS 服务端收到日志的时间,写日志时它按 UTC 存储,而 OpenClaw 本地时间用的是东八区。如果你在凌晨 0 点到 8 点之间产生日志,SLS 按天聚合会把这些日志算到前一天。这个问题很隐蔽,我会在图表上看到“每天 0 点消耗清零、早上 8 点突然多出来一堆消耗”的怪象。
解决办法有两个:一是在上报时把本地时间转换成 UTC 字符串后塞进字段,二是在查询语句里手动加时区偏移。我的习惯是上报时直接带一个local_time字符串字段,查询时优先用这个字段做聚合。
5.4 告警阈值设太小,收到“狼来了”免疫
阈值设太小的后果我已经在前面说了。这里再补充一个经验:告警阈值不是一次性设置就完事,要随着业务变化定期调整。我每个月会看一次过去 30 天的 Token 消耗 P95 值,如果业务量涨了,P95 也会涨,阈值就跟着往上调。
5.5 只监控总量,不拆维度,等于白监控
只看“今日总消耗 50 万 Token”这个数字,你无法判断它是否正常。50 万可能对 A 业务是正常水平,对 B 业务就是失控级。所以我的监控面板永远会放三个视图:总量趋势、按业务线拆分的占比、按模型的单价分析。任何一个视图单独看都有局限,三个放在一起,异常才会清晰暴露。
6. 最后分享一点我的实际运行体会
两套方案配合起来跑了一个季度之后,我最大的感受是:Token 消耗这个事,终于从“月底开盲盒”变成了“每天看盘”。Agent Dashboard 负责给我一本清清楚楚的账,SLS 负责在账目出问题的时候第一时间拍我肩膀。算法不是花哨的摆设,它解决的是“等到人来看的时候已经晚了”这个核心问题。
如果让我给一个最直接的落地建议,那就是别追求一步到位。先跑 Agent Dashboard 方案,用 Python 脚本把账算清楚,坚持一周,你一定会发现至少一个可以优化的点。优化完之后再上 SLS 监控和告警,最后再让预测算法接管“提前预警”这件事。这个顺序,每一步都能独立产生价值,不会让人觉得白折腾。
下一步我打算做两件事:一是把预测算法做成定时任务,每天早上 9 点把当日预算预估推到企业微信群,让团队自己心里有数;二是在熔断的拦截器里加上细粒度的控制,比如允许“低优先级任务”在预算超限后继续执行,但限制它的并发数。这个扩展做通了,OpenClaw 的 Token 治理就算真正闭环了。