PAYATHON 2026

如何查询 Watson Assistant 每位用户的 API calls 与 billing details

支付老李

结论

Watson Assistant 控制台不提供“每位终端用户的 API 调用次数和账单明细”。

context.metadata.user_id 主要用于识别活跃用户和关联会话日志,Billing Usage 页面不会根据这个字段拆分费用。IBM Cloud 通常按账号、资源实例和计费指标汇总用量与账单,而不是按 chatbot 的终端用户统计。

如需获取这类数据,可以采用以下两种方式:

  1. 通过 Watson Assistant 日志查询指定 user_id 的交互记录,分析历史调用情况。
  2. 由 client application 的后端记录每次 Watson Assistant 请求,长期、准确地统计各用户的调用量,再结合实例账单计算或分摊费用。

为什么控制台中看不到

user_id 是应用传入的业务标识,例如:

{
  "context": {
    "metadata": {
      "user_id": "user-12345"
    }
  }
}

Watson Assistant 可以用这个标识统计活跃用户,也可能将其保留在日志上下文中,但 IBM Cloud Billing 不会将它作为计费维度。

因此:

  • Active users 图表显示的是去重后的用户数量;
  • Improve 页面主要用于分析对话和日志,不是逐用户计费报表;
  • Billing 的 Usage 页面显示资源实例的总用量,不会按 context.metadata.user_id 拆分;
  • user_id 也无法证明某个请求一定对应一个可计费单位。

API 请求数、消息数、活跃用户数和最终计费用量不一定相同,具体取决于使用的 Watson Assistant 版本、套餐和当期计费规则。

查询指定用户的历史日志

如果所用的 Watson Assistant API 支持日志查询,可以读取日志并按 context.metadata.user_id 过滤。以下示例采用旧版 Workspace API 的调用方式:

curl -u "apikey:${WATSON_API_KEY}" \
  "https://api.<region>.assistant.watson.cloud.ibm.com/instances/<instance-id>/v1/workspaces/<workspace-id>/logs?version=<api-version>&filter=context.metadata.user_id::user-12345&page_limit=100"

其中:

  • <region> 是服务实例所在区域;
  • <instance-id> 是 Watson Assistant 服务实例 ID;
  • <workspace-id> 是对应的 workspace ID;
  • <api-version> 必须替换为目标环境实际支持的 API 版本日期;
  • page_limit 只控制每页返回的数量,完整统计还需要处理分页。

Watson Assistant 的 Classic、V1 Workspace 和较新的 Assistant V2 架构使用不同的接口及过滤语法。如果当前使用 V2 assistant、environment 或 session API,不能直接使用上面的 Workspace URL,应以该实例实际开放的日志或分析接口为准。

取得日志后,可以按 user_id 聚合记录:

const counts = {};

for (const log of logs) {
  const userId = log.response?.context?.metadata?.user_id
    ?? log.request?.context?.metadata?.user_id;

  if (!userId) continue;

  counts[userId] = (counts[userId] ?? 0) + 1;
}

console.log(counts);

这段代码统计的是返回的日志条数,不一定等于实际 API 请求数。是否一致取决于日志生成规则,例如失败请求、重试请求、被采样或已经过期的日志,都可能导致记录不完整。

推荐的准确统计方式

更可靠的办法是在应用后端调用 Watson Assistant 时记录审计事件:

async function sendMessage(userId, message) {
  const startedAt = Date.now();

  try {
    const response = await assistant.message({
      assistantId: process.env.WATSON_ASSISTANT_ID,
      sessionId: await getSessionId(userId),
      input: {
        message_type: "text",
        text: message
      }
    });

    await saveUsageEvent({
      userId,
      operation: "message",
      success: true,
      occurredAt: new Date().toISOString(),
      durationMs: Date.now() - startedAt
    });

    return response.result;
  } catch (error) {
    await saveUsageEvent({
      userId,
      operation: "message",
      success: false,
      occurredAt: new Date().toISOString(),
      durationMs: Date.now() - startedAt,
      statusCode: error.code
    });

    throw error;
  }
}

建议至少保存以下字段:

{
  "userId": "user-12345",
  "operation": "message",
  "occurredAt": "2026-09-01T08:30:00Z",
  "success": true,
  "statusCode": 200,
  "requestId": "application-generated-request-id"
}

随后按用户和账期进行聚合:

SELECT
  user_id,
  COUNT(*) AS api_calls,
  SUM(CASE WHEN success = TRUE THEN 1 ELSE 0 END) AS successful_calls
FROM watson_usage_events
WHERE occurred_at >= :billing_period_start
  AND occurred_at < :billing_period_end
GROUP BY user_id;

这些数据最好由可信的应用后端记录,不要依赖浏览器或移动端自行上报。客户端数据可能被篡改或遗漏,也可能因网络重试而产生重复记录。

如何得到每位用户的费用

IBM Cloud 提供的账单金额通常对应整个服务实例。如果业务上必须展示“每位用户费用”,需要自行制定分摊规则。例如,在确认套餐确实按相关调用量计费后,可以按调用占比分摊:

用户估算费用 = 实例账期费用 × 用户有效调用量 ÷ 实例有效调用总量

这个结果只适合内部成本分摊,并不等同于 IBM Cloud 出具的逐用户账单。如果套餐包含固定费用、免费额度、阶梯价格、活跃用户计费、超额用量或其他服务费用,简单按 API 调用次数分摊会产生偏差,需要根据实际账单中的计费指标建模。

注意事项

  • 不要直接用邮箱、手机号等敏感信息作为 user_id,应优先使用稳定的内部匿名 ID。
  • 同一用户应始终使用相同的 user_id,否则活跃用户统计可能重复计数。
  • 日志通常有保留期限,不适合作为永久计量数据库。
  • 记录重试、失败请求和幂等标识,并明确业务上“一次调用”的计算口径。
  • 最终费用以 IBM Cloud 账单为准。应用侧数据适合用于用户归因、用量统计和内部费用分摊。

备注:内容仅供参考。