一次聚合 LLM 调用里,请求日志到底记了哪些字段
本文解释在一次 LLM API 聚合调用中,网关侧通常记录的请求日志字段,包括时间戳、key 标识、模型 ID、token 数、延迟和状态,并说明如何在你自己的客户端埋点中获取相同信息。
为什么需要关注请求日志字段
当你通过一个聚合 API 调用多个模型时,每一次请求都会在网关侧产生一条日志。了解这条日志里有哪些字段,能帮助你排查问题、分析成本、优化性能。同样,如果你在自己的客户端也做了埋点,就能在本地获得对应信息,形成端到端的可观测性。
网关侧通常记录的字段
虽然不同聚合服务的实现细节不同,但下面这些字段是常见且通用的:
- 时间戳:请求到达网关的时间,通常精确到毫秒。有时也会记录响应完成的时间。
- Key 标识:你不是直接暴露 API key,而是用它的哈希或前缀来标识是哪个 key 发起的请求,用于计费和权限。
- 模型 ID:实际调用的模型标识,比如
claude-3-5-sonnet、gpt-4o、deepseek-chat等。注意,聚合网关可能会把用户请求的模型名映射到实际的供应商模型。 - Token 数:包括输入 token 和输出 token。有些网关还会记录总 token 数。这是计费的基础。
- 延迟:通常包括总延迟(从收到请求到返回响应)和可能的首 token 延迟(流式响应时第一个 token 到达的时间)。
- 状态:HTTP 状态码或自定义状态,比如成功、失败、超时、限流等。
- 其他可选字段:请求 ID、客户端 IP、用户自定义元数据、请求路径、上游供应商等。
这些字段的组合,让你能追踪每一次调用的完整生命周期。
如何从客户端埋点获取相同信息
你可以在自己的代码中,在发起请求前后记录类似的数据:
- 请求开始时间:记录本地时间戳,与网关时间可能有微小偏差,但可用于计算相对延迟。
- 请求结束时间:收到完整响应或流式结束的时间。
- 使用的模型:你传给 API 的 model 参数。
- Key 标识:如果你有多个 key,可以在客户端标记用的是哪一个(注意不要明文记录 key 本身)。
- Token 数:如果 API 响应中返回了 usage 字段,直接提取;如果没有,可以借助 tokenizer 估算。
- 延迟:结束时间减去开始时间,也可以单独记录首 token 时间(流式)。
- 状态:根据 HTTP 状态码或异常类型记录成功/失败。
将这些数据写入你的日志系统或监控面板,就能与网关侧的记录对照。
一个简单的埋点示例(伪代码)
start = time.time()
try:
response = client.chat.completions.create(
model="gpt-4o",
messages=[...],
stream=True
)
# 处理流式响应,记录首 token 时间
first_token_time = None
for chunk in response:
if first_token_time is None:
first_token_time = time.time()
# 处理 chunk
end = time.time()
log({
"model": "gpt-4o",
"start": start,
"end": end,
"first_token_latency": first_token_time - start if first_token_time else None,
"total_latency": end - start,
"status": "success",
"usage": response.usage # 如果提供
})
except Exception as e:
log({
"model": "gpt-4o",
"start": start,
"end": time.time(),
"status": "error",
"error": str(e)
})
这个例子展示了如何捕获关键字段。实际中,你可以根据需求增加字段,比如请求 ID、重试次数等。
字段对齐与差异
网关侧记录的字段可能和客户端不完全一致,例如:
- 网关的时间戳可能更准确,因为它不依赖客户端时钟。
- 网关的 token 计数是权威的,客户端可能只能依赖响应中的 usage。
- 延迟定义可能不同:网关可能只计算上游处理时间,而客户端包含网络传输。
因此,在分析时要注意区分。通常,网关日志更适合计费和审计,客户端埋点更适合用户体验监控。
总结
无论是网关还是客户端,请求日志的核心字段都是相似的:时间、身份、模型、用量、延迟、状态。理解这些字段,能让你更好地利用聚合 API 的可观测性,快速定位问题,并优化调用策略。