如何设计AI调用记录表

AI 产品的成本结构和传统 SaaS 有一个本质差异:传统 SaaS 的边际成本趋近于零,而 AI 产品每多服务一个用户,就多一笔真实的 API 账单。GPT-4o 的输入 Token 价格为 $2.50 / 1M tokens,Claude Sonnet 的输入 Token 价格为 $3.00 / 1M tokens,输出一侧通常更贵。当你的产品日活过万、每个用户每天产生几十轮对话,月度 API 账单轻松到达四位数甚至五位数美元。

没有精确的调用记录,成本就是一笔糊涂账。你不知道哪个功能在烧钱,不知道哪个用户的消耗异常,不知道缓存优化到底省了多少。本章系统讲解 AI 调用记录表的设计方法,覆盖日志表结构、Token 消耗记录、成本追踪、性能监控和数据归档五个层面。


AI 调用日志表设计

AI 调用日志表(ai_call_log)是整个成本追踪体系的原始数据源。每一次对 LLM API 的请求——无论成功还是失败——都应该在这里留下一条记录。

核心字段

字段类型说明
idUUID / BIGSERIAL主键,建议 UUID 避免暴露业务量
request_idVARCHAR(64)请求唯一标识,用于幂等校验和日志关联
user_idUUID关联用户表,标识调用发起者
session_idVARCHAR(64)会话 ID,将同一轮对话的多轮调用关联起来
providerVARCHAR(50)模型供应商:openaianthropicgoogledashscope
modelVARCHAR(100)具体模型名称,如 gpt-4oclaude-sonnet-4-20250514
api_endpointVARCHAR(255)调用的 API 端点路径
request_paramsJSONB请求参数快照:temperaturemax_tokenstop_p
statusENUM调用状态:successfailedtimeoutrate_limited
http_status_codeSMALLINTHTTP 响应状态码
error_codeVARCHAR(50)错误码(如有),如 rate_limit_exceededcontext_length_exceeded
error_messageTEXT错误详情
retry_countSMALLINT重试次数
latency_msINTEGER端到端响应耗时(毫秒)
ttft_msINTEGERTime To First Token,首 Token 响应时间(流式场景)
created_atTIMESTAMPTZ记录创建时间

设计要点

请求参数用 JSONB 而非独立列。AI API 的参数组合繁多,不同模型的参数也不一致。将 temperaturemax_tokensstop_sequences 等参数统一放在 request_params 中,查询时按需提取,避免表结构随参数变化频繁迁移。

同时记录 providermodel。供应商和模型是分开的两个维度。同一个 gpt-4o 模型可能通过 OpenAI 官方 API 调用,也可能通过 Azure OpenAI 调用,两者的价格和限速策略不同。分开存储方便按供应商对账,也方便按模型做成本分析。

status 要区分失败原因failed 是一个笼统的状态,实际需要区分:timeout(超时)、rate_limited(限流)、context_length_exceeded(上下文超限)、invalid_request(参数错误)、server_error(供应商服务端错误)。不同失败类型对应不同的优化策略。

记录 retry_count。重试机制会成倍放大 Token 消耗。一个请求重试 3 次,意味着消耗了 4 倍的输入 Token(原始请求 + 3 次重试)。这个字段帮助识别重试带来的隐性成本。


Token 消耗记录

Token 是 AI API 的计费单位。大部分 LLM 供应商在 API 响应中返回 usage 字段,包含本次调用的 Token 消耗明细。OpenAI 兼容格式通常包含 prompt_tokenscompletion_tokenstotal_tokens 三个值;Anthropic 的格式使用 input_tokensoutput_tokens,并额外区分 cache_read_input_tokenscache_creation_input_tokens

Token 记录表字段

字段类型说明
call_log_idUUID关联 ai_call_log 表的主键
input_tokensINTEGER输入 Token 数(含 system prompt、历史对话、用户输入)
output_tokensINTEGER输出 Token 数(模型生成的内容)
total_tokensINTEGER总 Token 数 = input_tokens + output_tokens
cached_tokensINTEGER命中缓存的 Token 数(如有),这部分通常按折扣价计费
cache_creation_tokensINTEGER写入缓存的 Token 数(如有)
reasoning_tokensINTEGER推理 Token 数(o1/o3 等推理模型的内部思考 Token)
input_costDECIMAL(12,8)输入侧费用(美元)
output_costDECIMAL(12,8)输出侧费用(美元)
total_costDECIMAL(12,8)本次调用总费用(美元)
cost_currencyCHAR(3)费用币种,默认 USD

设计要点

优先使用供应商返回的值,不要自行估算。不同模型的 Tokenizer 不同,同一个词在不同模型中的 Token 数量可能不一样。自行用 tiktoken 或其他方式估算容易产生误差,特别是遇到中文、日文等多字节字符时。供应商返回的 usage 字段是实际计费依据,应该直接入库。

分开记录输入和输出的 Token 及费用。输入 Token 和输出 Token 的单价通常不同,输出侧往往是输入侧的 3-4 倍。分开记录才能做精细的成本分析:如果一个功能的输入 Token 占比极高,优化方向是压缩 system prompt 或缩短上下文窗口;如果输出 Token 占比高,则需要关注输出内容的冗余度。

缓存 Token 单独记录。OpenAI 的 Prompt Caching 和 Anthropic 的 Prompt Caching 对缓存命中的 Token 按折扣价计费(通常是原价的 10%-50%)。将 cached_tokensinput_tokens 中单独拆出来,才能在报表中区分「全价 Token」和「折扣 Token」,准确计算实际成本。

推理 Token 是新兴类别。OpenAI 的 o1、o3 系列模型会产生内部推理 Token(reasoning tokens),这些 Token 不会出现在最终输出中,但会计费。如果不单独记录,你会发现实际费用比根据可见输出估算的值高出一截。

两种表结构选择

Token 消耗记录有两种存储方式:

方案 A:和调用日志合并在同一张表。将 Token 字段直接放在 ai_call_log 表中。优点是一次 JOIN 都不用,查询简单。缺点是表变宽,且失败请求的 Token 字段全部为空。

方案 B:独立的 ai_token_usage。通过 call_log_id 关联。优点是职责清晰,只有成功的调用才有 Token 记录。缺点是查询时需要 JOIN。

对于中小规模产品(日调用量 10 万以下),方案 A 足够。当日调用量到达百万级别、需要做高频聚合查询时,方案 B 更合适,因为可以对 Token 记录表单独做分区和索引优化。


成本追踪和统计

有了每次调用的 Token 消耗和费用数据,接下来要解决「这些钱花在了哪里」的问题。成本追踪的核心是多维度的费用归属(cost attribution)。

成本归属维度

维度说明典型用途
用户级user_id 聚合识别高消耗用户,设置用量配额或提醒
功能级按功能标识(如 feature_tag)聚合判断哪个功能的 AI 成本最高
团队/租户级team_idtenant_id 聚合多租户 SaaS 的成本分摊和 showback
模型级model 聚合评估模型切换的成本影响
环境级environment 聚合区分生产环境和开发/测试环境的消耗
会话级session_id 聚合计算单次对话的平均成本

价格表设计

为了实现自动化的成本计算,需要维护一张模型价格表:

字段类型说明
providerVARCHAR(50)供应商
modelVARCHAR(100)模型名称
input_price_per_1mDECIMAL(10,4)每 1M 输入 Token 的价格(美元)
output_price_per_1mDECIMAL(10,4)每 1M 输出 Token 的价格(美元)
cached_input_price_per_1mDECIMAL(10,4)缓存命中的输入 Token 价格(如有折扣)
cache_write_price_per_1mDECIMAL(10,4)缓存写入的 Token 价格(如有额外费用)
effective_fromDATE价格生效日期
effective_toDATE价格失效日期(NULL 表示当前有效)

成本计算公式:

input_cost = input_tokens / 1_000_000 * input_price_per_1m
output_cost = output_tokens / 1_000_000 * output_price_per_1m
total_cost = input_cost + output_cost

如果有缓存命中的 Token,需要从标准输入 Token 中扣除后按折扣价计算:

standard_input = input_tokens - cached_tokens
cached_cost = cached_tokens / 1_000_000 * cached_input_price_per_1m
input_cost = standard_input / 1_000_000 * input_price_per_1m + cached_cost

统计查询示例

按天统计各模型的总消耗和费用:

SELECT
    DATE(created_at) AS call_date,
    model,
    COUNT(*) AS call_count,
    SUM(input_tokens) AS total_input_tokens,
    SUM(output_tokens) AS total_output_tokens,
    SUM(total_cost) AS total_cost_usd
FROM ai_call_log
WHERE created_at >= CURRENT_DATE - INTERVAL '30 days'
  AND status = 'success'
GROUP BY DATE(created_at), model
ORDER BY call_date DESC, total_cost_usd DESC;

按功能维度统计过去 7 天的费用分布:

SELECT
    feature_tag,
    COUNT(*) AS call_count,
    SUM(total_cost) AS total_cost_usd,
    AVG(total_cost) AS avg_cost_per_call,
    SUM(input_tokens + output_tokens) AS total_tokens
FROM ai_call_log
WHERE created_at >= NOW() - INTERVAL '7 days'
  AND status = 'success'
GROUP BY feature_tag
ORDER BY total_cost_usd DESC;

性能监控

AI API 的响应时间是用户体验的关键瓶颈。一次 Chat Completion 调用可能需要 500ms 到 30s 不等,取决于模型、Token 数量和供应商负载。性能监控需要从调用日志中提取关键指标,建立基线和告警。

核心监控指标

指标计算方式告警阈值建议
平均响应时间(P50)latency_ms 的中位数因模型而异,GPT-4o 通常 < 2s
P95 响应时间latency_ms 的 95 分位通常设为 P50 的 3-5 倍
P99 响应时间latency_ms 的 99 分位超过 30s 需排查
首 Token 时间(TTFT)ttft_ms 的中位数流式场景 < 1s
成功率status = 'success' 的占比< 99% 需关注
错误率(按类型)error_code 的占比rate_limited > 5% 需扩容
超时率status = 'timeout' 的占比> 1% 需检查网络和超时设置
重试率retry_count > 0 的占比> 10% 需排查供应商稳定性
每分钟调用量按分钟聚合的 COUNT接近限速阈值时告警
Token 吞吐率total_tokens / (latency_ms / 1000)显著下降时可能供应商负载高

监控视图设计

性能监控通常分为三个层级:

实时仪表盘:展示最近 5-15 分钟的指标快照,包括成功率、P50 延迟、每分钟调用量。用于发现突发故障。

趋势分析:展示过去 24 小时到 30 天的指标趋势,包括日均成本变化、Token 消耗增长曲线、各模型的延迟分布。用于识别慢速退化和成本增长趋势。

异常检测:基于历史基线自动识别异常模式。例如某模型的平均延迟突然从 1.5s 跳到 5s、某个用户的 Token 消耗量比昨天增长了 10 倍、特定错误码的频率突然升高。

关键告警规则

# 示例告警规则(Prometheus/Grafana 风格)
alerts:
  - name: ai_api_high_error_rate
    condition: error_rate_5m > 0.01
    severity: warning
    message: "AI API 错误率超过 1%,当前 {{ $value | humanizePercentage }}"
 
  - name: ai_api_latency_spike
    condition: p95_latency_5m > 15000
    severity: warning
    message: "AI API P95 延迟超过 15s,当前 {{ $value }}ms"
 
  - name: ai_api_cost_spike
    condition: hourly_cost > daily_avg_hourly_cost * 3
    severity: critical
    message: "过去 1 小时 AI 调用费用超过日均小时费用的 3 倍"
 
  - name: ai_api_rate_limit_approaching
    condition: rpm_1m > rate_limit * 0.8
    severity: warning
    message: "每分钟调用量接近限速的 80%"

数据归档策略

AI 调用日志是典型的「写入量大、查询频率随时间递减」的数据。一条调用记录在产生后的前 7 天被频繁查询(用于实时监控和异常排查),30 天后查询频率大幅下降,90 天后几乎只在审计或回溯场景才会访问。如果所有数据都留在主表中,表的体积会快速膨胀,影响查询性能。

分层存储方案

层级存储位置数据范围查询性能存储成本
热数据PostgreSQL 主表(分区表)最近 7-30 天毫秒级
温数据PostgreSQL 归档分区 / Citus30-180 天百毫秒级
冷数据对象存储(S3)+ Parquet 格式180 天以上秒级

分区策略

PostgreSQL 的表分区(Table Partitioning)是实现归档的基础。按月对 ai_call_log 做范围分区:

CREATE TABLE ai_call_log (
    id UUID NOT NULL DEFAULT gen_random_uuid(),
    request_id VARCHAR(64) NOT NULL,
    user_id UUID NOT NULL,
    -- ... 其他字段
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    PRIMARY KEY (id, created_at)
) PARTITION BY RANGE (created_at);
 
-- 按月创建分区
CREATE TABLE ai_call_log_2026_06 PARTITION OF ai_call_log
    FOR VALUES FROM ('2026-06-01') TO ('2026-07-01');
CREATE TABLE ai_call_log_2026_07 PARTITION OF ai_call_log
    FOR VALUES FROM ('2026-07-01') TO ('2026-08-01');

按月分区的好处是:归档操作只需要 DETACH PARTITION,不会影响主表的在线查询。分离后的分区可以迁移到低成本存储,或者直接 DROP 掉(如果数据已经导出到 S3)。

索引策略

不同时间范围的数据使用模式不同,索引策略也应该不同:

热数据(最近 30 天)索引:

-- 按用户查询最近的调用记录
CREATE INDEX idx_call_log_user_recent ON ai_call_log (user_id, created_at DESC)
    WHERE created_at >= CURRENT_DATE - INTERVAL '30 days';
 
-- 按会话查询多轮对话
CREATE INDEX idx_call_log_session ON ai_call_log (session_id, created_at)
    WHERE created_at >= CURRENT_DATE - INTERVAL '30 days';
 
-- 按供应商和状态查询异常
CREATE INDEX idx_call_log_provider_status ON ai_call_log (provider, status, created_at DESC)
    WHERE created_at >= CURRENT_DATE - INTERVAL '7 days';

温数据和冷数据不需要这么多索引,甚至不需要索引——它们通常只在批量分析任务中被全表扫描。


实战案例

案例一:AI 写作助手的成本失控

一个面向海外用户的 AI 写作助手,使用 GPT-4o 做内容生成。上线初期功能迭代快,没有建立调用记录体系。两个月后收到 OpenAI 账单,月费用 $8,000,远超预期。

排查后发现三个问题:

  1. system prompt 膨胀。经过多轮迭代,system prompt 从最初的 500 tokens 增长到 12,000 tokens,每一轮对话都会携带完整的 prompt,但团队对此毫无感知。
  2. 没有缓存。同一个用户在一篇文章中反复修改同一段落,每次修改都重新发送完整的上下文,没有利用 Prompt Caching。
  3. 失败请求也在消耗 Token。部分请求因为 max_tokens 设置过大导致超时,但 Token 已经计费。

解决方案:上线调用记录表后,团队建立了按功能维度的成本看板。发现「段落重写」功能占了总成本的 45%,但用户只占 12%。进一步优化了 system prompt(压缩到 3,000 tokens)、启用了 Prompt Caching、对重试次数做了上限控制(最多 2 次)。三个月后,月费用降至 $3,200,降幅 60%。

案例二:多租户 AI 平台的成本分摊

一个面向企业客户的 AI 对话平台,支持多个租户(企业)接入。每个租户有自己的 API Key 和用量配额。平台需要回答两个问题:每个租户花了多少钱?每个租户的哪个功能花了多少钱?

调用记录表的设计中增加了 tenant_idfeature_tag 两个字段。每次 API 调用时,中间件从请求上下文中提取租户标识,从路由中提取功能标识,写入调用记录。

成本分摊报表基于以下查询:

SELECT
    t.tenant_name,
    acl.feature_tag,
    DATE(acl.created_at) AS usage_date,
    SUM(atu.total_tokens) AS total_tokens,
    SUM(atu.total_cost) AS total_cost_usd
FROM ai_call_log acl
JOIN ai_token_usage atu ON atu.call_log_id = acl.id
JOIN tenants t ON t.id = acl.user_id  -- 简化示意
WHERE acl.created_at >= '2026-06-01'
  AND acl.created_at < '2026-07-01'
GROUP BY t.tenant_name, acl.feature_tag, DATE(acl.created_at)
ORDER BY total_cost_usd DESC;

基于这些数据,平台实现了 showback 报表——每个租户登录后可以看到自己的 Token 消耗分布和费用明细。对于超出配额的高消耗租户,系统会自动发送用量提醒邮件。这套机制将租户的用量投诉降低了 70%。


AI 调用记录的数据流

流程图画布 · 115%
Mermaid 流程图加载中...

设计检查清单

在设计 AI 调用记录表之前,逐项确认以下要点:

  • 每次 API 调用都有唯一 request_id,用于幂等校验和日志关联
  • providermodel 分开存储,支持同一模型多渠道对账
  • 输入 Token 和输出 Token 分开记录,支持差异化的成本分析
  • 缓存 Token(cached_tokens)和推理 Token(reasoning_tokens)单独记录
  • 费用通过供应商返回的实际值计算,而非本地 Tokenizer 估算
  • 模型价格表有版本控制,支持历史价格回溯
  • 调用状态细分:successfailedtimeoutrate_limited
  • 重试次数 retry_count 有记录,防止隐性成本膨胀
  • 性能指标完整:latency_ms(端到端延迟)和 ttft_ms(首 Token 时间)
  • 有功能维度的归属标识(feature_tag),支持按功能做成本分析
  • 有用户 / 租户维度的归属,支持 showback 和配额管理
  • 表结构支持分区(如按月分区),为数据归档做准备
  • 有对应的告警规则:错误率、延迟、费用异常、接近限速
  • 定期归档策略已定义:热数据 → 温数据 → 冷数据的流转规则
  • 敏感信息(如用户输入的完整内容)不直接存储在调用日志中,或做了脱敏处理

小结

AI 调用记录表不是一张简单的日志表,它是成本管控、性能优化和运营决策的数据基础。设计的核心原则是:记录完整的调用上下文(谁在什么场景下调用了什么模型),精确追踪 Token 消耗(区分输入、输出、缓存和推理 Token),建立多维度的费用归属(用户、功能、团队、环境),以及为未来的数据归档预留结构。

从工程实践的角度,建议从一张合并了 Token 字段的 ai_call_log 表开始,配合一张模型价格表做自动化的成本计算。当数据量增长到需要优化查询性能时,再拆分 Token 记录和做表分区。不要一开始就过度设计——但调用记录这件事,从第一天就应该有。


参考资料

  1. LiteLLM - What is stored in the DB — LiteLLM 的数据库 Schema 文档,包含 LiteLLM_SpendLogs 表的结构设计
  2. Langfuse - Token & Cost Tracking — Langfuse 的 Token 和成本追踪机制,支持多种模型的自动费用计算
  3. Braintrust - How to track LLM token usage (2026) — 三层 Token 可见性模型:per-call、context window、per-step
  4. Portkey - Tracking LLM token usage across providers, teams and workloads — 跨供应商、团队和工作负载的 Token 追踪框架
  5. AWS 中国博客 - 让每一分 Token 消耗都可量化的全栈实践 — Agentic AI 场景下的 Token 可观测性方案
  6. Braintrust - How to track LLM costs (2026) — per-request 级别的成本追踪和归属方法
  7. LiteLLM - Spend Tracking — 跨 100+ 模型的 Token 消耗和费用自动追踪
  8. MLflow - Token Usage and Cost Tracking — MLflow 的 LLM Token 使用量和成本追踪文档