AI调用记录管理模块

AI 调用成本是产品中一类「隐形支出」——每次用户请求触发的 LLM 调用、Embedding 计算、图像生成,都在静默消耗预算。当产品从原型走向规模化运营,调用量从每天数百次增长到数十万次时,缺乏精细化管理的成本会迅速失控。更关键的是,没有完整的调用记录,你就无法回答这几个核心问题:哪些功能在大量消耗 token?错误率是否异常?某次模型升级是否导致了延迟上升?

AI 调用记录管理模块的职责,就是让每一次 AI 调用都「可追踪、可分析、可优化」。它不是一个锦上添花的后台功能,而是产品进入生产阶段后必须具备的基础设施。

核心功能设计

一个完整的 AI 调用记录管理模块需要覆盖四个核心功能域:调用列表、成本分析、性能监控和错误追踪。它们各自解决不同的管理需求,但在数据层面高度关联。

调用列表

调用列表是管理模块的基础视图,提供所有 AI 调用的结构化记录。每条记录至少应包含以下信息:调用 ID、时间戳、调用方(用户/租户/功能模块)、目标模型、请求参数摘要、响应状态、token 消耗量和耗时。

设计调用列表时需要关注几个要点:

分页与检索。调用记录的量级通常是百万级甚至更高,列表页必须支持高效分页和多维筛选。按时间范围、模型、状态、用户 ID、功能模块进行组合筛选是基本需求。

数据脱敏。请求和响应的原始内容可能包含用户敏感数据,列表中通常只展示摘要信息(如 prompt 前 100 个字符),完整内容需要单独权限才能查看。

关联上下文。每条调用记录应能关联到具体的业务场景——是哪个页面、哪个功能触发的调用。这样当成本异常时,可以快速定位到具体的功能模块。

成本分析

成本分析模块将 token 消耗转化为财务数据,帮助团队理解 AI 调用的经济影响。

核心能力包括:

  • 实时成本统计:按小时/天/周/月统计总成本和各模型成本分布
  • 分维度归因:按用户、租户、功能模块、模型类型进行成本拆分
  • 趋势分析:识别成本增长趋势,发现异常波峰
  • 预算对比:设定预算上限后,展示实际消耗与预算的偏差
  • 预测模型:基于历史数据预测未来成本走势

不同模型的成本计算逻辑不同。OpenAI 的 GPT 系列按 input/output token 分别计价,Claude 也采用类似模式,而图像生成模型(如 DALL-E、Stable Diffusion)按图片尺寸和数量计价。成本分析模块需要维护一张模型价格表,并在模型提供商调价时及时更新。

性能监控

性能监控关注的是 AI 调用的响应质量,核心指标包括:

指标说明告警阈值参考
首 Token 延迟(TTFT)从发起请求到收到第一个 token 的时间流式场景 < 500ms
端到端延迟(E2E Latency)从发起请求到完整响应的总耗时根据业务场景设定
吞吐量(TPS)每秒处理的 token 数量低于基线 30% 时告警
成功率成功调用占总调用的比例< 99% 时告警
P95/P99 延迟95%/99% 分位的延迟值根据 SLA 设定

性能监控的难点在于,AI 调用的耗时波动远大于传统 API。同一个模型在不同时段、不同负载下的响应时间可能相差数倍。因此,监控策略应以分位值(P50/P95/P99)为主,而非简单的平均值。

错误追踪

AI 调用的错误类型比传统 API 更复杂,常见的包括:

  • 模型过载(429 Too Many Requests):触发速率限制
  • 上下文超长(Context Length Exceeded):输入 token 超过模型限制
  • 内容过滤(Content Filter):触发安全策略导致响应被截断
  • 模型服务不可用(503 Service Unavailable):模型提供商故障
  • 超时(Timeout):模型推理时间过长
  • 格式异常(Parse Error):模型返回内容无法解析为预期格式

错误追踪模块需要对每种错误类型进行分类统计,并支持快速定位高频错误的调用特征——是特定 prompt 模式导致的,还是特定模型版本的问题,或者是特定时间段的基础设施故障。

功能域对比

维度调用列表成本分析性能监控错误追踪
核心目标可追溯可量化可度量可定位
数据粒度单次调用聚合统计时间序列错误分类
更新频率实时写入准实时聚合实时采集实时写入
主要消费者开发/运维产品/财务运维/SRE开发/运维
存储压力高(原始数据)中(聚合数据)中(时序数据)低(仅异常数据)
关键挑战海量数据检索多模型价格映射延迟波动处理错误分类准确性

数据库设计

AI 调用记录的数据库设计需要平衡写入性能、查询效率和存储成本。推荐采用「主表 + 聚合表」的分层架构:主表记录每次调用的明细,聚合表存储预计算的统计数据。

核心表结构

调用记录主表 ai_call_logs

CREATE TABLE ai_call_logs (
  id              BIGINT PRIMARY KEY AUTO_INCREMENT,
  call_id         VARCHAR(64) NOT NULL UNIQUE,       -- 全局唯一调用 ID
  tenant_id       VARCHAR(64) NOT NULL,              -- 租户 ID
  user_id         VARCHAR(64),                       -- 用户 ID
  feature_module  VARCHAR(128) NOT NULL,             -- 功能模块标识
  provider        VARCHAR(32) NOT NULL,              -- 模型提供商:openai / anthropic / ...
  model           VARCHAR(64) NOT NULL,              -- 模型名称:gpt-4o / claude-sonnet-4-20250514 / ...
  request_type    VARCHAR(16) NOT NULL DEFAULT 'chat', -- chat / embedding / image / audio
  input_tokens    INT NOT NULL DEFAULT 0,
  output_tokens   INT NOT NULL DEFAULT 0,
  total_tokens    INT GENERATED ALWAYS AS (input_tokens + output_tokens) STORED,
  cost_usd        DECIMAL(12, 6) NOT NULL DEFAULT 0, -- 单次调用成本(美元)
  latency_ms      INT,                               -- 端到端延迟
  ttft_ms         INT,                               -- 首 Token 延迟
  status          VARCHAR(16) NOT NULL,              -- success / error / timeout / rate_limited
  error_code      VARCHAR(64),                       -- 错误码
  error_message   TEXT,                              -- 错误信息
  prompt_hash     VARCHAR(64),                       -- prompt 哈希(用于去重和聚合)
  metadata        JSON,                              -- 扩展字段:temperature、top_p 等
  created_at      TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
 
  INDEX idx_tenant_time (tenant_id, created_at DESC),
  INDEX idx_model_time (model, created_at DESC),
  INDEX idx_feature_time (feature_module, created_at DESC),
  INDEX idx_status_time (status, created_at DESC),
  INDEX idx_user_time (user_id, created_at DESC)
);

模型价格表 ai_model_pricing

CREATE TABLE ai_model_pricing (
  id              INT PRIMARY KEY AUTO_INCREMENT,
  provider        VARCHAR(32) NOT NULL,
  model           VARCHAR(64) NOT NULL,
  request_type    VARCHAR(16) NOT NULL DEFAULT 'chat',
  input_price     DECIMAL(12, 6) NOT NULL,  -- 每百万 input token 价格(美元)
  output_price    DECIMAL(12, 6) NOT NULL,  -- 每百万 output token 价格(美元)
  effective_from  DATE NOT NULL,            -- 价格生效日期
  effective_to    DATE,                     -- 价格失效日期(NULL 表示当前有效)
  currency        VARCHAR(8) NOT NULL DEFAULT 'USD',
 
  UNIQUE INDEX uk_model_effective (provider, model, request_type, effective_from)
);

日聚合表 ai_call_daily_stats

CREATE TABLE ai_call_daily_stats (
  id              BIGINT PRIMARY KEY AUTO_INCREMENT,
  stat_date       DATE NOT NULL,
  tenant_id       VARCHAR(64) NOT NULL,
  feature_module  VARCHAR(128),
  provider        VARCHAR(32) NOT NULL,
  model           VARCHAR(64) NOT NULL,
  status          VARCHAR(16) NOT NULL,
  call_count      INT NOT NULL DEFAULT 0,
  total_input_tokens  BIGINT NOT NULL DEFAULT 0,
  total_output_tokens BIGINT NOT NULL DEFAULT 0,
  total_cost_usd  DECIMAL(14, 6) NOT NULL DEFAULT 0,
  avg_latency_ms  INT,
  p95_latency_ms  INT,
  p99_latency_ms  INT,
  error_count     INT NOT NULL DEFAULT 0,
 
  UNIQUE INDEX uk_daily_dim (stat_date, tenant_id, feature_module, provider, model, status)
);

数据库设计对比

用途写入频率数据保留分区策略
ai_call_logs调用明细每次调用实时写入90 天热数据 + 冷归档按天/周范围分区
ai_model_pricing模型价格极低(模型调价时)永久保留历史无需分区
ai_call_daily_stats日聚合统计每日批量计算写入永久保留按年范围分区

索引设计要点

ai_call_logs 表的索引设计围绕后台管理系统的典型查询模式展开:

  • idx_tenant_time:租户维度的调用列表查询,按时间倒序。这是最高频的查询场景,尤其是多租户 SaaS 产品。
  • idx_model_time:按模型维度的成本和性能分析,帮助识别哪个模型的消耗最大。
  • idx_feature_time:按功能模块的归因分析,定位成本异常的具体功能。
  • idx_status_time:错误追踪场景,快速筛选失败的调用记录。
  • idx_user_time:用户维度的用量查询,用于计费限额和用户级分析。

call_id 的 UNIQUE 索引用于保证幂等性——网络重试场景下不会产生重复记录。prompt_hash 字段用于 prompt 去重和相似调用聚合分析,不参与索引以节省存储空间。

聚合查询示例

按功能模块统计月度成本

SELECT
  feature_module,
  DATE_FORMAT(created_at, '%Y-%m') AS month,
  COUNT(*) AS call_count,
  SUM(input_tokens) AS total_input_tokens,
  SUM(output_tokens) AS total_output_tokens,
  SUM(cost_usd) AS total_cost,
  AVG(latency_ms) AS avg_latency
FROM ai_call_logs
WHERE tenant_id = ?
  AND created_at >= DATE_SUB(CURDATE(), INTERVAL 3 MONTH)
GROUP BY feature_module, DATE_FORMAT(created_at, '%Y-%m')
ORDER BY total_cost DESC;

错误率趋势分析

SELECT
  DATE(created_at) AS stat_date,
  COUNT(*) AS total_calls,
  SUM(CASE WHEN status != 'success' THEN 1 ELSE 0 END) AS error_count,
  ROUND(SUM(CASE WHEN status != 'success' THEN 1 ELSE 0 END) * 100.0 / COUNT(*), 2) AS error_rate
FROM ai_call_logs
WHERE tenant_id = ?
  AND created_at >= DATE_SUB(CURDATE(), INTERVAL 30 DAY)
GROUP BY DATE(created_at)
ORDER BY stat_date;

对于大规模数据(日调用量 > 100 万),直接查询主表会面临性能瓶颈。此时应依赖 ai_call_daily_stats 聚合表,将报表查询的时间复杂度从 O(N) 降低到 O(天数量)。聚合任务建议通过定时任务每小时增量计算,而非全量重算。

成本分析与优化建议

成本分析不仅是展示数据,更重要的是驱动优化决策。以下是经过验证的成本优化策略:

成本归因体系

建立「租户 → 功能模块 → 模型 → 调用」四级归因体系,让每一分钱的 AI 支出都有明确的归属。

归因层级用途优化动作
租户级计费、套餐设计调整不同套餐的 AI 调用配额
功能级产品 ROI 分析评估功能价值与成本是否匹配
模型级模型选型决策在质量满足需求时切换到更便宜的模型
调用级异常检测识别异常高消耗的单次调用

优化策略

1. 模型分级与路由

不是所有任务都需要最强的模型。根据任务复杂度建立模型分级策略:

  • 简单任务(分类、提取、格式化)→ 使用轻量模型(GPT-4o-mini、Claude Haiku)
  • 中等任务(摘要、翻译、常规对话)→ 使用中等模型(GPT-4o、Claude Sonnet)
  • 复杂任务(推理、代码生成、创意写作)→ 使用旗舰模型(o3、Claude Opus)

通过智能路由,在满足质量要求的前提下,整体成本通常可以降低 40% ~ 60%。

2. Prompt 缓存与复用

对于结构相似的请求(如相同 system prompt 的多轮对话),利用模型提供商的 Prompt Caching 能力(如 Anthropic 的 Prompt Caching、OpenAI 的 Prompt Caching),相同前缀的 token 可以享受 50% ~ 90% 的价格折扣。

3. 上下文窗口管理

传入过多历史上下文是成本膨胀的常见原因。通过上下文压缩(summarize 历史对话)、滑动窗口(只保留最近 N 轮)或 RAG 替代(用检索替代长上下文),可以有效控制 input token 数量。

4. 批量与异步处理

非实时场景(如内容生成、数据标注)应使用 Batch API(如 OpenAI Batch API),成本通常为实时调用的 50%。

5. 缓存层

对确定性较高的请求(如 FAQ 回答、模板填充),在应用层引入语义缓存(Semantic Cache),相同意图的请求直接返回缓存结果,避免重复调用模型。

性能监控与告警

性能监控的核心是建立分级告警机制,避免告警疲劳的同时确保关键问题不被遗漏。

告警分级

级别触发条件响应时间通知方式
P0 - 严重成功率 < 90% 或 P99 延迟 > 30s,持续 5 分钟5 分钟内响应电话 + 即时消息
P1 - 高成功率 < 95% 或 P95 延迟 > 15s,持续 10 分钟15 分钟内响应即时消息
P2 - 中成本超日预算 150%,或错误率上升但未达 P11 小时内响应邮件 + 即时消息
P3 - 低成本趋势异常增长、非核心模型性能波动下一工作日处理邮件

监控数据采集

性能监控数据的采集应在 AI 调用的中间件层完成,不侵入业务代码:

// AI 调用中间件 —— 自动采集调用指标
interface CallMetrics {
  callId: string
  tenantId: string
  userId?: string
  featureModule: string
  provider: string
  model: string
  startTime: number
  firstTokenTime?: number  // 流式响应专用
  endTime: number
  inputTokens: number
  outputTokens: number
  status: 'success' | 'error' | 'timeout' | 'rate_limited'
  errorCode?: string
  errorMessage?: string
  metadata?: Record<string, unknown>
}
 
class AIMetricsCollector {
  private buffer: CallMetrics[] = []
  private flushInterval = 5000 // 5 秒批量写入
 
  async record(metrics: CallMetrics): Promise<void> {
    // 计算衍生指标
    const latency = metrics.endTime - metrics.startTime
    const ttft = metrics.firstTokenTime
      ? metrics.firstTokenTime - metrics.startTime
      : null
 
    // 计算成本
    const cost = await this.calculateCost(
      metrics.provider,
      metrics.model,
      metrics.inputTokens,
      metrics.outputTokens
    )
 
    // 写入缓冲区,批量落库
    this.buffer.push({
      ...metrics,
      latencyMs: latency,
      ttftMs: ttft ?? undefined,
      costUsd: cost,
    })
  }
 
  private async flush(): Promise<void> {
    if (this.buffer.length === 0) return
 
    const batch = this.buffer.splice(0, this.buffer.length)
 
    // 批量写入主表
    await db.insert(aiCallLogs).values(batch)
 
    // 异步更新聚合表
    await this.updateAggregates(batch)
  }
}

成本计算逻辑

async function calculateCost(
  provider: string,
  model: string,
  inputTokens: number,
  outputTokens: number
): Promise<number> {
  // 查询当前有效的模型价格
  const pricing = await db.query.aiModelPricing.findFirst({
    where: and(
      eq(aiModelPricing.provider, provider),
      eq(aiModelPricing.model, model),
      lte(aiModelPricing.effectiveFrom, new Date()),
      or(
        isNull(aiModelPricing.effectiveTo),
        gte(aiModelPricing.effectiveTo, new Date())
      )
    ),
  })
 
  if (!pricing) return 0
 
  // 价格单位:每百万 token
  const inputCost = (inputTokens / 1_000_000) * pricing.inputPrice
  const outputCost = (outputTokens / 1_000_000) * pricing.outputPrice
 
  return Number((inputCost + outputCost).toFixed(6))
}

实现方法

调用列表 API

// Hono 路由 —— 调用列表查询
import { Hono } from 'hono'
import { eq, and, gte, lte, desc, sql } from 'drizzle-orm'
 
const callLogRouter = new Hono()
 
callLogRouter.get('/api/admin/ai-call-logs', async (c) => {
  const query = c.req.query()
  const page = Number(query.page) || 1
  const pageSize = Math.min(Number(query.pageSize) || 50, 200)
  const offset = (page - 1) * pageSize
 
  // 构建动态筛选条件
  const conditions = []
 
  if (query.tenantId) {
    conditions.push(eq(aiCallLogs.tenantId, query.tenantId))
  }
  if (query.model) {
    conditions.push(eq(aiCallLogs.model, query.model))
  }
  if (query.status) {
    conditions.push(eq(aiCallLogs.status, query.status))
  }
  if (query.featureModule) {
    conditions.push(eq(aiCallLogs.featureModule, query.featureModule))
  }
  if (query.startTime) {
    conditions.push(gte(aiCallLogs.createdAt, new Date(query.startTime)))
  }
  if (query.endTime) {
    conditions.push(lte(aiCallLogs.createdAt, new Date(query.endTime)))
  }
 
  const where = conditions.length > 0 ? and(...conditions) : undefined
 
  // 并行查询数据和总数
  const [data, totalResult] = await Promise.all([
    db.query.aiCallLogs.findMany({
      where,
      orderBy: [desc(aiCallLogs.createdAt)],
      limit: pageSize,
      offset,
      // 不返回完整的 error_message 和 metadata,减少传输量
      columns: {
        errorMessage: false,
        metadata: false,
      },
    }),
    db
      .select({ count: sql<number>`count(*)` })
      .from(aiCallLogs)
      .where(where),
  ])
 
  return c.json({
    data,
    pagination: {
      page,
      pageSize,
      total: totalResult[0].count,
      totalPages: Math.ceil(totalResult[0].count / pageSize),
    },
  })
})

成本统计 API

// 成本统计 —— 优先查聚合表,回退到主表
callLogRouter.get('/api/admin/ai-call-logs/cost-stats', async (c) => {
  const query = c.req.query()
  const tenantId = query.tenantId
  const granularity = query.granularity || 'day' // day / week / month
 
  if (!tenantId) {
    return c.json({ error: 'tenantId is required' }, 400)
  }
 
  // 使用聚合表查询,性能可控
  const stats = await db.query.aiCallDailyStats.findMany({
    where: and(
      eq(aiCallDailyStats.tenantId, tenantId),
      gte(aiCallDailyStats.statDate, new Date(query.startDate)),
      lte(aiCallDailyStats.statDate, new Date(query.endDate))
    ),
    orderBy: [aiCallDailyStats.statDate],
  })
 
  // 按时间粒度二次聚合
  const grouped = new Map<string, {
    callCount: number
    totalCost: number
    inputTokens: number
    outputTokens: number
    errorCount: number
  }>()
 
  for (const row of stats) {
    const key = formatDate(row.statDate, granularity)
    const existing = grouped.get(key) || {
      callCount: 0,
      totalCost: 0,
      inputTokens: 0,
      outputTokens: 0,
      errorCount: 0,
    }
 
    existing.callCount += row.callCount
    existing.totalCost += Number(row.totalCostUsd)
    existing.inputTokens += Number(row.totalInputTokens)
    existing.outputTokens += Number(row.totalOutputTokens)
    existing.errorCount += row.errorCount
 
    grouped.set(key, existing)
  }
 
  return c.json({
    data: Array.from(grouped.entries()).map(([date, stats]) => ({
      date,
      ...stats,
      avgCostPerCall: stats.callCount > 0
        ? stats.totalCost / stats.callCount
        : 0,
    })),
  })
})

聚合任务

// 每小时增量聚合任务
async function runHourlyAggregation(): Promise<void> {
  const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000)
 
  // 获取需要聚合的时间范围内的原始数据
  const rawData = await db
    .select({
      statDate: sql<Date>`DATE(${aiCallLogs.createdAt})`,
      tenantId: aiCallLogs.tenantId,
      featureModule: aiCallLogs.featureModule,
      provider: aiCallLogs.provider,
      model: aiCallLogs.model,
      status: aiCallLogs.status,
      callCount: sql<number>`count(*)`,
      totalInputTokens: sql<number>`sum(${aiCallLogs.inputTokens})`,
      totalOutputTokens: sql<number>`sum(${aiCallLogs.outputTokens})`,
      totalCost: sql<string>`sum(${aiCallLogs.costUsd})`,
      avgLatency: sql<number>`avg(${aiCallLogs.latencyMs})`,
      p95Latency: sql<number>`percentile_cont(0.95) within group (order by ${aiCallLogs.latencyMs})`,
      p99Latency: sql<number>`percentile_cont(0.99) within group (order by ${aiCallLogs.latencyMs})`,
      errorCount: sql<number>`sum(case when ${aiCallLogs.status} != 'success' then 1 else 0 end)`,
    })
    .from(aiCallLogs)
    .where(gte(aiCallLogs.createdAt, oneHourAgo))
    .groupBy(
      sql`DATE(${aiCallLogs.createdAt})`,
      aiCallLogs.tenantId,
      aiCallLogs.featureModule,
      aiCallLogs.provider,
      aiCallLogs.model,
      aiCallLogs.status
    )
 
  // Upsert 到聚合表
  for (const row of rawData) {
    await db
      .insert(aiCallDailyStats)
      .values({
        statDate: row.statDate,
        tenantId: row.tenantId,
        featureModule: row.featureModule,
        provider: row.provider,
        model: row.model,
        status: row.status,
        callCount: row.callCount,
        totalInputTokens: row.totalInputTokens,
        totalOutputTokens: row.totalOutputTokens,
        totalCostUsd: row.totalCost,
        avgLatencyMs: row.avgLatency,
        p95LatencyMs: row.p95Latency,
        p99LatencyMs: row.p99Latency,
        errorCount: row.errorCount,
      })
      .onConflictDoUpdate({
        target: [
          aiCallDailyStats.statDate,
          aiCallDailyStats.tenantId,
          aiCallDailyStats.featureModule,
          aiCallDailyStats.provider,
          aiCallDailyStats.model,
          aiCallDailyStats.status,
        ],
        set: {
          callCount: sql`${aiCallDailyStats.callCount} + ${row.callCount}`,
          totalInputTokens: sql`${aiCallDailyStats.totalInputTokens} + ${row.totalInputTokens}`,
          totalOutputTokens: sql`${aiCallDailyStats.totalOutputTokens} + ${row.totalOutputTokens}`,
          totalCostUsd: sql`${aiCallDailyStats.totalCostUsd} + ${row.totalCost}`,
        },
      })
  }
}

AI 调用记录管理流程

适读画布 · 130%
Mermaid 流程图加载中...

案例分析

案例一:SaaS 写作助手的成本失控与治理

某 SaaS 写作助手产品在上线 6 个月后,月度 AI 调用成本从 $2,000 增长到 $18,000,但用户增长只有 3 倍。通过引入调用记录管理模块,团队发现了几个问题:

  1. 翻译功能使用了 GPT-4o 做简单文本翻译,每次调用约 2,000 tokens,但翻译任务的输入输出高度结构化,完全可以切换到 GPT-4o-mini,成本降低 90%。
  2. 某用户的自动化脚本在周末大量调用,占周未总调用量的 60%,但产生的内容从未被使用。
  3. Prompt 中包含了完整的用户历史文档(平均 8,000 tokens),但实际上只需要最近 3 段对话上下文。

治理措施:引入模型分级路由,翻译和格式化任务使用轻量模型;对单用户设置日调用上限;优化 Prompt 模板,将上下文窗口从全量历史缩减到最近 3 轮。三个月后,月度成本降至 $5,500,降幅约 70%。

案例二:多租户平台的成本归因实践

某面向企业的 AI 客服平台,服务 200+ 租户。平台按「座席数」收费,但 AI 调用成本在不同租户间差异极大——部分租户的客服大量使用 AI 辅助回复,部分租户几乎不用。

在没有调用记录管理时,平台无法区分不同租户的 AI 消耗,导致高消耗租户的成本被均摊到所有租户身上。引入管理模块后:

  • 建立了租户级的成本归因,准确度量每个租户的 AI 调用消耗
  • 设计了阶梯式定价:基础套餐包含每月 5,000 次 AI 调用,超出部分按实际消耗计费
  • 为每个租户提供用量仪表盘,租户可以自助查看 AI 调用统计
  • 识别出 3 个异常高消耗的租户,排查发现是其内部系统循环调用导致的

上线成本归因后,平台的 AI 调用收入覆盖了 95% 的模型成本,而此前这一比例只有 45%。

成本分析维度对比

分析维度数据源聚合方式典型应用场景
按时间ai_call_logs.created_at小时/天/周/月成本趋势、预算对比
按租户ai_call_logs.tenant_idSUM 聚合计费、套餐设计
按功能模块ai_call_logs.feature_moduleSUM 聚合产品 ROI 分析
按模型ai_call_logs.modelSUM 聚合模型选型、成本优化
按用户ai_call_logs.user_idSUM + 限额用户配额管理
按错误类型ai_call_logs.error_codeCOUNT 分组质量分析、SLA 监控

实施检查清单

在上线 AI 调用记录管理模块前,逐项确认以下事项:

  • 所有 AI 调用路径都经过统一的中间件层,确保无遗漏记录
  • call_id 生成策略保证全局唯一,支持幂等重试
  • 模型价格表已配置所有在用模型,且包含历史价格变更记录
  • 成本计算公式经过财务验证,与模型提供商账单误差 < 1%
  • 调用记录包含完整的维度信息:租户、用户、功能模块、模型、状态
  • 敏感数据脱敏策略已定义,prompt/response 内容的存储和展示符合隐私要求
  • 索引覆盖所有高频查询场景,慢查询 < 1%
  • 聚合任务正常运行,聚合数据与主表数据一致性经过校验
  • 告警规则已配置,告警通知渠道畅通(电话/即时消息/邮件)
  • 数据保留策略已定义:热数据保留周期、冷数据归档方案、过期数据清理机制
  • 批量写入机制已实现,避免逐条 INSERT 导致数据库压力过大
  • 管理后台的列表查询接口有分页限制,防止全表扫描
  • 成本异常检测逻辑已覆盖:单用户突增、总体成本偏离预算、单模型错误率上升
  • 灰度上线方案已准备,新旧系统并行运行期间数据双写校验

参考资料

  1. OpenAI Production Best Practices - OpenAI 官方生产环境最佳实践,涵盖扩展、安全和成本管理
  2. Vantage - Best AI Cost Management Tools - 2026 年 AI 成本管理工具对比,覆盖 OpenAI、Anthropic 等主流模型
  3. Holori - Top 10 AI Cost Visibility Tools in 2026 - AI 成本可视化工具评测,关注 token 使用追踪和支出分配
  4. Moesif - Tools for Logging and Monitoring API Calls - API 调用日志和监控工具全景,适用于 AI API 场景
  5. Merge.dev - API Logging Best Practices - API 日志最佳实践,包含结构化日志和安全脱敏策略
  6. Larridin - AI Dashboard: Build Usage Tracking That Drives Decisions - AI 用量仪表盘设计方法论,从数据驱动决策角度阐述 Dashboard 构建
  7. Newline - How to Build Dashboards for LLM Monitoring - LLM 监控 Dashboard 构建指南,覆盖数据流映射和字段设计