指导和见解

AI API 网关中的流式令牌记账:最终使用、取消和部分响应

流媒体可以改善感知延迟,但如果网关仅代理字节,则可能会破坏人工智能使用分析和计费。这是一个实用的状态机模式,用于捕获最终使用、中止的流、提供者错误和部分响应。

流式 LLM 响应很容易代理,但很难正确计费。如果AI API网关将服务器发送的事件转发到客户端,但将第一个块视为使用记录,则租户分析将会发生偏差。这种偏差通常出现在争议中,例如:“用户只看到了一半的答案”、“提供商的账单比我们的仪表板显示的要多”、“配额释放得太早”或“超时产生了代币,但没有发票行。”

根本问题是流式调用不是一个事件。它们是一个序列:请求接受、上游流打开、字节交付、最终使用情况报告、提供者停止、客户端断开连接、网关超时和计费结算。可靠的网关应该显式地对这些状态进行建模,而不是假设完整的 HTTP 响应是唯一成功的路径。

失败模式:流式传输隐藏了计费边界

非流式完成通常返回一个带有使用元数据的响应对象。网关可以标准化该使用情况、写入账本行、更新配额并一次性发出分析结果。

流媒体改变了边界。用户体验是增量式的,但计费真相可能会在特定于提供商的最终事件中、在累积增量中、通过聚合的 SDK 响应或稍后通过提供商报告 API 在最后到达。如果客户端在最终使用事件之前断开连接,则网关可能只提供了部分答案,而提供商仍然生成并计费更多令牌。

事实:OpenAI 记录了想要使用情况数据的流式调用者应将 stream_options 设置为 include_usage。 OpenAI 还提供组织级别的使用情况和成本端点,同时指出,出于财务目的,使用情况和成本可能并不总是完美协调。

事实:人为流使用服务器发送的事件,例如 message_startcontent_block_deltamessage_deltamessage_stop。它的 message_delta 使用信息是累积的,因此网关不得将每个使用增量加在一起。

事实:Gemini 和 Vertex 风格的流式 API 可以公开增量块,而 SDK 也可以提供聚合响应对象。对于网关来说,与单独的可见块相比,聚合路径可以成为完成使用的更好来源。

使用流状态机,而不是布尔成功标志

在上游调用开始之前,流式请求应该有持久的使用记录。该记录应该通过明确的状态。实际的最小值是:

  • 已接受:网关对密钥进行了身份验证,归属于租户,并创建了一个开放的账本行。
  • first_byte_sent:至少有一个输出事件到达下游客户端。
  • provider_completed:上游提供程序发出正常停止信号或已完成的响应对象。
  • client_aborted:下游套接字在正常网关完成之前关闭。
  • provider_error:上游提供程序在流开始后或最终使用到达之前返回错误。
  • gateway_timeout:网关强制执行其延迟预算并结束请求。
  • 已结算:网关将使用量转换为租户成本和配额消耗。
  • 已协调:后来的提供商使用情况或成本数据确认或调整了该行。

此模型可以防止常见的分析错误:将生成文本的每个流标记为“成功且准确”。流可能对用户有用,但来自提供商的信息不完整,估计用于计费,并且同时等待协调。

推荐的账本字段

保持请求时间行小而明确:

<前><代码>{ "request_id": "gw_req_...", “tenant_id”:“tenant_123”, "api_key_id": "key_456", "provider": "openai|anthropic|gemini|...", “provider_request_id”:空, “模型”:“提供商模型 ID”, “状态”:“已接受”, “流”:真实, “input_tokens”:空, “output_tokens_billed”:空, “output_tokens_delivered_estimate”:0, “provider_usage_source”:空, "billing_status": "pending_reconciliation", “client_abort_at”:空, “provider_completed_at”:空, “定居点”:空, “错误类”:空 }

重要的区别是 output_tokens_billedoutput_tokens_delivered_estimate。用户关心什么到达了他们的应用程序。财务关心提供商的账单。在断开连接、工具调用流、隐藏推理令牌、缓存令牌、安全停止或网关超时后,这些数字可能会有所不同。

特定于提供商的捕获规则

提供商中立的兼容 OpenAI 的 API 对于应用程序开发人员来说非常有用,但网关适配器仍然需要特定于提供商的会计规则。

兼容 OpenAI 的流媒体

对于 OpenAI 路由,公开一个网关选项,在支持的情况下启用上游使用情况报告。一种常见的模式是接受网关级默认值,例如:

<前><代码>{ “流”:真实, “流选项”:{ “include_usage”:正确 } }

如果下游调用者省略它,网关可以决定是否为兼容的路由注入它。记录此行为,因为某些客户端期望精确的线路兼容性,而某些模型或上游可能不支持以相同方式的最终使用。

建议:不要从早期的部分中解决租户成本。保持账本行打开,直到捕获最终使用事件、提供者响应在没有使用的情况下结束、或者流进入错误或取消路径。

人为流

Anthropic 的累积使用需要不同的规则。如果网关看到输出令牌计数为 10、25 和 40 的三个 message_delta 事件,则输出计数为 40,而不是 75。

让latestUsage = null;
for wait (anthropicStream 的 const 事件) {
  if (event.type === "message_delta" && event.usage) {
    if (latestUsage && event.usage.output_tokens 

建议:记录最新的累积使用值,如果出现回归,则发出可观察性事件。回归可能表明解析器错误、重复事件、提供程序更改或混合流。

Gemini 和 Vertex 风格的流媒体

Gemini 支持流式传输块以减少感知延迟。在 Vertex 风格的 SDK 中,流式传输可以公开异步流和聚合响应对象。网关应在可用时保留该聚合路径。

const StreamingResult =等待model.generateContentStream(request);
for等待(streamingResult.stream的const块){
  前向块(块);
  countDeliveredBytesOrText(块);
}
const 聚合=等待streamingResult.response;
SettleFromAggregateUsage(聚合);

建议:如果 SDK 提供完整的响应记录,请避免从可见块构建所有记帐。块是为了延迟。最终对象通常更适合计费和分析。

将客户端断开连接作为一流的记帐事件来处理

客户端断开连接是许多网关亏损或向客户收取过高费用的原因。浏览器选项卡关闭、移动网络中断或应用程序取消请求。网关注意到下游套接字已关闭,但上游提供程序可能仍在生成。

网关应该做出明确的策略选择:

  • 立即取消上游:减少浪费的生成和提供程序成本,但可能会中断 UI 断开连接后后端仍需要结果的工作流程。
  • 在后台继续上游:可以为服务器端使用者保留工作,但用户可能看不到生成和计费的所有令牌。
  • 依赖于路由的行为:取消交互式聊天,继续执行类似作业的工作流程,并使设置对租户可见。

交互式流式传输的实际默认设置是在下游客户端断开连接时取消上游,然后将账本行标记为 client_aborted。如果最终使用在取消期间到达,则根据该权威使用进行结算。如果不是,请将行标记为估计pending_reconciliation,而不是假装它是准确的。

downstream.on("close", async () => {
  if (!providerCompleted) {
    ledger.markClientAborted(requestId);
    等待上游.abort().catch(() => {
      ledger.emit("upstream_cancel_failed", requestId);
    });
  }
});

建议:公开透明的结算标签,例如 finalprovider_reconciledestimatedwaivedpending_reconciliation。这比立即准确地显示每个流式调用更具防御性。

流期间的配额强制执行

准确的计费通常取决于最终的提供商使用情况,但配额执行不能总是等到最后。不应允许具有硬预算的租户无限期地进行流式传输,因为在飞行过程中无法获得确切的使用情况。

同时使用两种机制:

  1. 预检预留:根据型号、请求的最大令牌数、租户政策和当前余额预留预计的最大值。
  2. 流式传输压力检查:估计流式传输期间交付的输出,并在请求跨越配置的安全边界时停止。

这是一种控制机制,而不是最终法案。提供者可能会以与网关估计器不同的方式计算缓存令牌、推理令牌、多模式令牌或隐藏令牌。

权衡:实时估算有助于执行预算,但它们可能与提供商计费的代币有所不同。最终结算应使用权威提供商的使用情况(如果可用),对账应稍后调整估算。

捕获会计错误的可观察性事件

当网关发出目标事件而不是仅发出通用请求日志时,流式计费故障更容易调试。添加事件,例如:

  • final_usage_missing:流在没有权威使用的情况下结束。
  • cumulative_usage_regressed:累计令牌计数向后移动。
  • stream_ending_without_stop_event:未观察到正常的提供商停止标记。
  • aborted_after_provider_completion:提供者完成,但下游客户端在网关完成转发之前关闭。
  • settled_from_estimate:租户分类账使用了估算值,因为最终使用情况不可用。
  • reconciliation_adjusted_usage:提供商报告后来更改了该行。

事实:OpenTelemetry GenAI 语义约定建议使用提供商返回的使用信息进行流式响应(如果可用),并在无法高效或准确获取令牌计数时警告不要报告使用指标。

对于人工智能使用分析,这意味着仪表板应支持置信水平。混合最终值、估计值和调节值而没有标签的图表可能看起来很干净,但会误导财务和支持团队。

流式会计的一致性测试

不要依靠快乐路径聊天提示进行手动测试。每个提供者适配器都应该针对破坏账本的情况进行一致性测试:

  • 正常流:最终使用到达,观察到停止事件,账本结算为最终
  • 工具调用流:工具调用增量被转发,使用情况被捕获,结构化元数据不会破坏令牌计数。
  • 安全或拒绝停止:提供商提前停止,使用情况仍能正确解决。
  • 强制客户端断开:部分输出后下游关闭;上游根据政策取消或继续。
  • 部分输出后的上游 5xx:网关记录部分传送,并且不会将请求标记为完全成功。
  • 最终使用前的网关超时:行变为估计或待对账。
  • 缺少最终事件:适配器发出 final_usage_missing 并避免准确的计费标签。

这些测试应该断言状态转换、账本字段、发出的可观察性事件和下游行为。逐字节流兼容性还不够;会计副作用是合同的一部分。

实际实施清单

  • 在分派上游请求之前创建使用分类账行。
  • 在请求时存储租户、密钥、用户、模型、路由、提供商和请求标识符。
  • 在支持的情况下启用提供商最终使用情况报告,例如兼容 OpenAI 的 stream_options.include_usage
  • 对于累积提供程序,存储最新的使用值而不是汇总事件。
  • 在 SDK 提供聚合响应对象时保留它们。
  • 单独跟踪交付的输出和提供商计费的使用情况。
  • 断开连接时,根据路由策略取消上游并标记 client_aborted
  • 使用透明的结算状态:最终、估计、待核对、提供商核对或放弃。
  • 发出特定于会计的可观察性事件。
  • 稍后根据提供商的使用情况或成本报告(如有)进行协调,同时保留请求时的租户归属。

向租户展示什么

租户不需要每一个内部事件,但他们确实需要诚实的标签。有用的使用表可能会显示:

  • 状态:最终、估计或核对。
  • 请求结果:已完成、客户端中止、提供商错误或网关超时。
  • 交付的输出:发送到客户端的近似文本或字节。
  • 计费令牌:用于成本的提供商标准化使用量。
  • 调整:任何以后的调节增量。

这种设计减少了支持的歧义。如果用户只看到部分响应,仪表板可以解释提供商是否已经完成、网关是否取消上游以及费用是最终费用还是估计费用。

建议与预测

建议:将流式请求视为状态机,在准确结算之前等待权威的最终使用,将交付的输出与计费使用分开,并诚实地标记估计行。提供程序适配器应该对提供程序特定的使用语义进行编码,而不是将每个流扁平化为通用字节代理。

预测:随着模型暴露更多隐藏的工作:推理令牌、缓存令牌折扣、多模式处理、工具使用跟踪和安全停止,流式会计将变得更加重要。已经将提供商计费的使用与客户端可见的输出分开的网关将比仅计算流文本的网关更容易适应。

可行的结论

如果您的网关支持流式传输,请立即审核一条路径:在前几个块之后强制客户端断开连接并检查分类帐行。如果它显示“成功”并且令牌计数看起来准确,那么您的分析可能在撒谎。

解决办法不是放弃流媒体。保持快速的用户体验,但使流完成、取消、提供者错误、丢失最终使用和协调明确的记账状态。这为产品团队提供了响应式输出,为财务团队提供了可辩护的成本,并为支持团队提供了足够的证据来解释部分响应,而无需猜测。

相关阅读

FAQ

常见问题

网关是否应该根据估计的令牌计数对流式响应进行计费?
必要时使用实时配额保护的估算,但在可用时根据提供商返回的使用情况确定准确的租户成本。如果最终使用量丢失,请将该行标记为估计或待对账。
为什么交付的输出令牌与计费令牌不同?
客户端可能会断开连接,网关可能会超时,提供者可能会对隐藏推理或多模式令牌进行计数,或者提供者可能会在用户停止接收字节后完成生成。跟踪交付的输出与提供商计费的使用情况分开。
最常见的 Anthropic 流式会计错误是什么?
汇总累积使用事件。 Anthropic message_delta 使用计数是累积的,因此网关应该存储最新值而不是添加每个事件。
当浏览器在流传输期间断开连接时会发生什么?
对于交互式路由,实际的默认设置是取消上游请求,将账本行标记为 client_aborted,并仅根据权威提供者的使用(如果到达)进行结算。否则标记估计或待调节的行。