复现一次糟糕返回:为了排障,客户端该自己记下什么
本文介绍在 LLM API 聚合场景下,当补全结果异常时,客户端应记录哪些关键信息以复现问题。涵盖请求哈希、模型标识、参数、原始响应等字段,并提供日志结构与脱敏建议,帮助开发者不依赖网关日志进行排障。
为什么客户端需要自己留证据
使用 LLM API 聚合站时,一次补全请求会经过网关路由到实际模型。当返回结果异常(乱码、截断、不符合预期),你首先会想:“是模型的问题,还是网关转发错了?” 如果只能依赖网关日志,排查会受限于日志保留策略、访问权限,甚至网关方的记录粒度。客户端自己记录关键产物,就能独立重建调用现场,快速定位。
必须记录的请求侧信息
请求侧的信息用于唯一标识你发出的内容,以及影响模型行为的参数。
- 请求哈希:对请求体(消息数组、模型、参数)计算 SHA-256 等哈希,作为该次调用的唯一 ID。即使内容很长,哈希也能快速比对。
- 模型标识:你指定的模型名称(如
claude-3-5-sonnet或聚合站的自定义别名)。注意记录你实际传给 API 的值,而非你以为的。 - 生成参数:
temperature、topp、maxtokens、stop等。不同参数会导致同一提示产生不同输出,复现时必须一致。 - 消息数组:完整的对话历史,包括 system、user、assistant 角色。注意脱敏敏感信息,但保留结构。
- 时间戳:请求发出的精确时间(UTC),精确到毫秒。用于关联服务端事件。
必须记录的响应侧信息
响应侧信息用于还原模型实际返回了什么,以及是否被网关修改。
- 原始响应体:尽可能保存未解析的原始字节或 JSON。解析后的文本可能丢失错误细节(如 finish_reason、usage)。
- 响应头:部分聚合站会在响应头中携带路由信息、请求 ID 或限流状态,这些对排障很有用。
- HTTP 状态码:区分成功、客户端错误、服务端错误。
- 完成原因:
finishreason(如stop、length、contentfilter)能解释输出为何截断或为空。 - Token 用量:
usage字段中的 prompt/completion tokens,可辅助验证是否被意外截断。
建议的日志结构
将上述信息组织成 JSON 行(JSONL)格式,便于追加和检索。例如:
{
"request_id": "客户端生成的UUID",
"request_hash": "sha256:...",
"timestamp": "2025-01-01T00:00:00.000Z",
"model": "claude-3-5-sonnet",
"parameters": {
"temperature": 0.7,
"max_tokens": 1024
},
"messages": [...],
"response": {
"status": 200,
"headers": {...},
"body": {...}
}
}
每条记录一行,写入本地文件或日志系统。注意控制日志轮转,避免磁盘写满。
脱敏与隐私
记录原始请求和响应可能包含敏感数据。务必:
- 对用户输入中的 PII(个人身份信息)进行脱敏或哈希处理。
- 若保存完整消息数组,确保存储位置加密且访问受限。
- 明确日志保留期限,定期清理。
实际排障流程示例
假设用户反馈某次补全结果错误。你可以:
- 根据用户提供的时间范围,在本地日志中检索对应记录。
- 提取
request_hash和model,确认请求参数是否与预期一致。 - 检查
response.body中的finishreason和usage,判断是否因maxtokens过小导致截断。 - 如果参数无误、响应完整但内容异常,则可能是模型本身或网关路由问题。此时可将请求哈希和原始响应提供给聚合站支持,加速定位。
不要只依赖网关日志
网关日志通常用于计费、限流和路由,未必保留完整的请求体和响应体,也可能因隐私政策快速删除。客户端自记日志让你拥有第一手证据,无论你用的是哪家聚合站,都能自主排障。
小结
为每次 LLM 调用记录请求哈希、模型 ID、参数、原始响应和关键响应头,是复现异常的最低要求。结合合理的日志结构和脱敏措施,你就能在不依赖网关日志的情况下,重建调用现场,快速找到问题根源。