Skip to content

token_stats - Token 用量统计

字数
1239 字
阅读时间
6 分钟

概述

token_stats.py 提供线程安全的 AI Token 用量统计管理器。支持按会话、模型、用户多维度记录,自动计算费用,提供排行榜和历史查询。

数据结构

每条用量记录(record)包含以下字段:

字段类型说明
idstringUUID 唯一标识
timestampstringISO 格式时间戳
datestring日期 YYYY-MM-DD
modelstring模型名称
session_idstring会话 ID
channel_typestring频道类型 (web / qq / qq_group)
user_idstring用户标识
inputint输入 Token 数
outputint输出 Token 数
totalint总 Token 数
costfloat估算费用(美元)
sourcestring来源标识
purposestring用途分类(chat / plot / memory / review / vision / stt / image_gen
duration_msfloat响应耗时(毫秒,可选)

模型定价

内置主流模型定价表($/M tokens):

模型输入价格输出价格
claude-opus-4-7$15.0$75.0
claude-sonnet-4-6$3.0$15.0
claude-haiku-4-5$1.0$5.0
gpt-4o$2.5$10.0
gpt-4o-mini$0.15$0.60
deepseek-v3$0.27$1.10
deepseek-r1$0.55$2.19

未匹配的模型默认按 $1.0/$4.0 估算。

用途分类(Purpose)

每条用量记录可携带 purpose 字段,标识该次 Token 消耗的业务用途,用于多维度统计分析。

常量说明
PURPOSE_CHATchat主要对话(Web、QQ、Telegram 等)
PURPOSE_PLOTplot剧情模式(选择生成、剧情推进)
PURPOSE_MEMORYmemory记忆提取与注入
PURPOSE_REVIEWreviewReview Pipeline 审查评分
PURPOSE_VISIONvision图片/视频识别
PURPOSE_STTstt语音转文字
PURPOSE_IMAGE_GENimage_gen图片生成

get_stats() 返回结果中包含 purposes 字段,按用途维度聚合 input/output/total/message_count/cost。

核心 API

初始化与单例

python
from nbot.core.token_stats import init_token_stats_manager, get_token_stats_manager

# 服务启动时初始化
init_token_stats_manager(data_dir="data/web")

# 任意位置获取单例
manager = get_token_stats_manager()

record_usage()

记录一次 AI 调用用量。所有频道(Web、QQ)的回调中自动调用。

python
manager.record_usage(
    prompt_tokens=1500,         # 输入 Token
    completion_tokens=800,      # 输出 Token
    total_tokens=2300,          # 总 Token(可选,默认 prompt+completion)
    model="claude-sonnet-4-6",  # 模型名(用于定价匹配)
    session_id="abc123",        # 会话 ID
    channel_type="web",         # 频道类型
    user_id="user_001",         # 用户 ID
    source="web",               # 来源标识
    purpose="chat",             # 用途分类(默认 chat)
    duration_ms=1234.5,         # 响应耗时 ms(可选)
)

自动调用点

  • WebCallbacks.on_usage()_update_web_token_stats() — Web 频道流式响应完成后
  • QQCallbacks.on_usage() → 直接调用 — QQ 频道响应完成后
  • source 参数用于区分 web / qq 来源

get_stats()

返回指定时间范围的统计数据。

python
# 支持 "today" / "7d" / "30d" / "all"
stats = manager.get_stats(date_range="7d")

print(stats["total_tokens"])       # 总 Token
print(stats["message_count"])      # 消息数
print(stats["estimated_cost"])     # 估算费用
print(stats["avg_response_time"])  # 平均响应时间(秒)
print(stats["active_sessions"])    # 活跃会话数
print(stats["recent_records"])     # 最近 100 条记录
print(stats["history"])            # 每日汇总历史

返回字段说明:

字段说明
today今日总 Token(顶层聚合)
month本月总 Token
total全部历史总 Token
total_tokens所选范围总 Token
today_input所选范围输入 Token
today_output所选范围输出 Token
message_count所选范围消息数
avg_tokens_per_msg平均每条消息 Token
estimated_cost估算费用(美元,字符串)
active_sessions活跃会话数
avg_response_time平均响应时间(秒)
history每日汇总列表
recent_records最近 100 条详细记录
sessions会话维度统计
models模型维度统计
users用户维度统计
purposes用途维度统计(按 purpose 聚合 input/output/total/message_count/cost)

get_rankings()

返回会话 / 模型 / 用户排行榜。

python
rankings = manager.get_rankings(limit=10)

# 会话排行榜
for s in rankings["sessions"]:
    print(s["name"], s["value"], s["cost"])

# 模型排行榜(含 input/output/message_count/cost)
for m in rankings["models"]:
    print(m["name"], m["input"], m["output"], m["cost"])

# 用户排行榜
for u in rankings["users"]:
    print(u["name"], u["value"])

重置

python
manager.reset_daily()   # 重置今日计数器
manager.reset_monthly() # 重置本月计数器

持久化

数据保存在 {data_dir}/token_stats.json,每次 record_usage() 后自动写入。文件结构:

json
{
  "today": 0,
  "month": 0,
  "total": 0,
  "estimated_cost": 0.0,
  "history": [],
  "sessions": {},
  "models": {},
  "users": {},
  "records": []
}
  • history: 每日汇总,自动合并同日重复条目,保留 90 天
  • records: 详细调用记录,最多保留 5000 条(FIFO)
  • 跨天/跨月: 加载时自动检测并重置对应计数器

用量归一化

model_adapter.py 中的 normalize_usage_dict() 负责将不同提供商的 Token 字段统一为 OpenAI 命名:

python
from nbot.core.model_adapter import normalize_usage_dict

# 任意格式的 usage → 标准格式
usage = normalize_usage_dict({
    "input_tokens": 100,         # Anthropic 风格
    "output_tokens": 50,
})
# → {"prompt_tokens": 100, "completion_tokens": 50, "total_tokens": 150}

支持的输入字段:

方向识别的字段名
输入prompt_tokens, input_tokens, prompt_token_count
输出completion_tokens, output_tokens, completion_token_count, generated_tokens
总计total_tokens, total_token_count
缓存cached_tokens, prompt_tokens_details.cached_tokens
推理reasoning_tokens, completion_tokens_details.reasoning_tokens

流式响应中的 Usage 提取

_stream_to_web() 中,每个 SSE chunk 如果包含 usage 字段,会通过 normalize_usage_dict() 归一化后 yield 出去:

python
# ai_service.py: _stream_to_web()
data = json.loads(data_str)
usage = normalize_usage_dict(data.get("usage"))
if usage:
    yield {"usage": usage}

同时请求体默认携带 stream_options: {"include_usage": True},如果提供商返回 400/422(不支持),会自动回退到不带 usage 选项的请求。

页面历史