一次模型调用,
如何留下完整的账?

把多个模型接到同一个入口之后,另一个问题随之出现:一次请求的状态、用量和费用,应该怎样对应起来?

先分清请求与调用尝试

一个客户端请求,可能经历主模型调用、失败重试或备用模型调用。如果只保存一个最终成功状态,中间发生过的调用和费用就很容易丢失。

在网关实践中,使用 request_id 标识客户端请求,使用 attempt_id 标识每一次调用尝试。前者串起一次访问,后者记录实际发往模型的操作。两者分开后,最终结果和中间尝试都能保留各自的上下文。

费用需要有可以说明的来源

模型响应中的 usage 是厂商提供的用量信息。网关记录用量时,同时保留模型身份、价格版本和用量来源,使后续的核对有明确依据。金额采用整数微单位保存,减少浮点数累计带来的偏差。

超时和网络断开只说明客户端没有拿到完整结果,不能据此判断厂商是否处理了请求。用量缺失时,应将估算与已确认记录区分开,并保留待核实状态。

未知用量需要保留为未知。
把超时调用写成零费用,会让表面上完整的报表失去可信的依据。

恢复能力从持久化边界开始

账本的作用是保留可以追溯的事实。项目将调用开始记录写入持久化存储,再通过完成事件更新结算状态。后台处理事件时,需要识别已经处理过的记录,避免消息重复投递造成重复记账。

这里的幂等,指的是同一操作执行多次,最终只产生一次有效的业务结果。实现它需要稳定的标识和明确的状态推进规则,而不是仅依赖消息“通常只到达一次”。

数据库和消息队列可以帮助恢复已经写下的事实。对于从未收到的厂商用量,仍需要其他核对渠道;重放事件无法凭空补出不存在的信息。

边界明确,才能讨论取舍

路由、重试、流式输出和计量之间存在联动。流式输出已经开始后,直接切换模型继续拼接,可能让一个回答混入两次不同推理的内容,也会让请求归属和费用解释更加困难。

在这类系统中,我更关注三件事:每一步是否留下了足够的信息;失败后哪些状态可以恢复;仍然未知的部分是否被如实展示。吞吐与容量则需要独立的负载验证,不能从功能演示推导。