日志不是"打得越多越好"
AI 系统的日志常见两个极端 ✗:只打一行"请求成功" ✔(出问题查不出 ✔),
或者把整个请求体全塞进去 ✔(日志爆炸、还带敏感数据 ✔)。
目标:让日志成为可查询的证据 ✔ —— 出问题时能用几条查询定位 ✔。
一、字段规范化(一次定好,长期受益)
必带(每条日志都有 ✔):
ts(UTC ISO8601 ✔)| trace_id | request_id | user_id/tenant_id(脱敏 ✔)
feature(功能名 ✔)| stage(阶段:prepare/retrieve/generate/validate ✔)
status | error_code(稳定枚举 ✔)| latency_ms(分段 ✔)
model | prompt_version | index_version(见可复现那篇 ✔)
按需(大字段单独存,日志只放引用 ✔):
input_hash | output_summary(截断 ✔)| retrieved_chunk_ids(片段 id 列表 ✔)
cost(token 数 + 折算 ✔)| retry_count
要点:阶段与分段延迟最常被省 ✗ —— 没有它就无法回答"慢在哪一段" ✔。
二、三条纪律
- 结构化(JSON 行 ✔,不要自由文本 ✔)—— 否则无法聚合 ✔;
- 字段名稳定(改名等于破坏历史查询 ✔ —— 要改就加新字段 ✔);
- 脱敏(日志也是数据 ✔ —— 见脱敏那篇 ✔)。
三、四条该能"一次查询搞定"的问题
① 某 trace_id 的完整链路(含上游与工具调用 ✔)
② 某功能最近 1 小时的失败率与错误码分布 ✔
③ 某次调用的 prompt_version / index_version / 检索片段 ✔
④ 某租户今日用量与成本 ✔
做不到,说明字段缺项或不成结构 ✗。
四、两个反模式
- 只打自然语言日志 ✗("处理完成,用时 2 秒" ✔ —— 无法聚合 ✔);
- 把所有大字段塞进主日志 ✗(查询变慢、成本升高、敏感数据扩散 ✔)。
五、落地顺序
- 先统一
ts/trace_id/feature/stage/status/latency_ms六个字段 ✔(改动小、收益大 ✔); - 再加
model/prompt_version/index_version✔; - 大字段单独存,日志里放
input_hash与chunk_ids✔; - 最后把上面那四条问题写成现成查询(值班手册里 ✔)。
一条判据
值班时遇到"某用户说刚才的回答不对" ✔,你能否在 5 分钟内用日志还原"当时模型看到了什么" ✔?
不能,说明日志还不是证据 ✗。