跳转到内容

使用量跟踪

wippy/usage 模块记录 LLM 令牌消耗,并提供按时间区间、模型或用户分组的聚合查询。它绑定 wippy.llm:usage_tracker 契约,因此任何通过 LLM 模块调用的代码都会自动产生使用量记录。

将模块添加到项目:

Terminal window
wippy add wippy/usage
wippy install

声明依赖并将 target_db 要求指向用于存放使用量记录的数据库:

version: "1.0"
namespace: app
entries:
- name: app_db
kind: db.sql.sqlite
path: ./data/app.db
- name: dep.usage
kind: ns.dependency
component: wippy/usage
version: "*"
- name: target_db
kind: registry.entry
meta:
wippy.usage.target_db: app:app_db

当应用启动时,wippy/migration 会运行该模块的 01_create_token_usage_table 迁移,创建 token_usage 表及其在 user_idcontext_idmodel_idtimestamp 上的索引。

token_usage
├── usage_id text primary key (uuid v7)
├── user_id text not null
├── context_id text
├── model_id text not null
├── prompt_tokens integer
├── completion_tokens integer
├── thinking_tokens integer default 0
├── cache_read_tokens integer default 0
├── cache_write_tokens integer default 0
├── timestamp timestamp
└── meta text (JSON)

wippy/llm 在每次生成之前解析 wippy.llm:usage_tracker 契约。wippy/usage 将其实现绑定为默认值:

contracts:
- contract: wippy.llm:usage_tracker
default: true
methods:
track_usage: wippy.usage:usage_tracker

每次成功的 LLM 调用都会以模型 id、令牌计数和可选的 context_id 调用 track_usageuser_id 取自当前的安全主体;不在用户上下文中的调用会被记录为 "system"

当你需要在 LLM 流程之外记录使用量时,可直接导入跟踪器:

imports:
usage_tracker: wippy.usage:usage_tracker
local tracker = require("usage_tracker")
local usage_id, err = tracker.track_usage(
"openai:gpt-4o",
prompt_tokens,
completion_tokens,
thinking_tokens,
cache_read_tokens,
cache_write_tokens,
{ context_id = "chat-42", metadata = { feature = "summary" } }
)
参数类型说明
model_idstring规范化的模型 id
prompt_tokensnumber输入令牌
completion_tokensnumber输出令牌
thinking_tokensnumber推理令牌(未报告时为 0)
cache_read_tokensnumber提示词缓存命中
cache_write_tokensnumber提示词缓存写入
options.context_idstring自由格式标签;回退到 ctx.get("context_id")
options.timestampnumberUnix 时间戳;默认为当前(UTC)
options.metadatatable与记录一同存储的任意 JSON 元数据

返回 usage_idnil, err

wippy.usage:token_usage_repo 提供聚合查询:

imports:
usage: wippy.usage:token_usage_repo
local usage = require("usage")
local summary = usage.get_summary(start_unix, end_unix)
local by_time = usage.get_usage_by_time(start_unix, end_unix, usage.INTERVAL.DAY)
local by_model = usage.get_usage_by_model(start_unix, end_unix)
local by_user = usage.get_usage_by_user(start_unix, end_unix)
函数返回值
get_summary(start, end)范围内的总计:prompt/completion/thinking/cache 令牌、请求数、total_tokens(prompt + completion + thinking)
get_usage_by_time(start, end, interval)分桶数组,每个区间一个;缺失的桶返回零
get_usage_by_model(start, end)每模型总计,按 total_tokens 降序
get_usage_by_user(start, end)每用户总计,按 total_tokens 降序
create(user_id, model_id, prompt, completion, options)由跟踪器使用的低层插入
usage.INTERVAL.HOUR -- "hour"
usage.INTERVAL.DAY -- "day"
usage.INTERVAL.WEEK -- "week"
usage.INTERVAL.MONTH -- "month"

get_usage_by_time 将分桶对齐到所配置的区间。在 PostgreSQL 上使用 generate_series 配合区间运算;在 SQLite 上使用基于 UNIX 时间戳的递归 CTE。每个桶中的 total_tokens 不包含缓存令牌。

跟踪器和仓储在公共 API 边界都接受 UNIX 时间戳。仓储内部将其转换为 RFC3339 字符串用于存储和查询。传入 os.time()time.now():unix() 值,而不是格式化字符串。

meta 列存储自由格式的 JSON 块。使用它将记录与应用事件相关联:

tracker.track_usage(model_id, prompt, completion, 0, 0, 0, {
context_id = "chat-42",
metadata = {
session_id = "s-7",
route = "/api/summarise",
agent_id = "writer",
},
})

context_id 是顶级列,可建立索引;metadata 以文本形式存储,旨在用于展示,而非过滤。

  • LLM - LLM 生成和 usage_tracker 契约
  • 迁移 - 用于创建模式的迁移运行器
  • 框架概述 - 框架模块用法