AI 学习笔记(十五):LLM 应用可观测性(Observability)最小实践
上一阶段我们把“接入模型 -> 串流程 -> 做评估与发布门禁”跑通了,下一步进入生产运维实践。
这第一篇我选择从 Observability 开始,因为没有可观测性,后面的成本治理、事故响应、质量回归都容易变成“拍脑袋 + 猜”。
你可以把目标定得很朴素:让每一次线上请求,都能回答三个问题:
- 发生了什么(What)
- 为什么会这样(Why)
- 影响有多大、该不该回滚(So what)
1. 先把链路拆开:一次 LLM 请求到底发生了什么
不拆链路就很难观测。一个常见的线上请求,通常至少包含这些阶段:
入口:HTTP 请求 / 鉴权 / 参数校验上下文:会话状态、用户画像、权限与灰度策略检索:query 改写、向量检索、过滤、重排组装:提示词模板选择、上下文拼接、提示词长度控制调用:请求模型、处理重试/超时/熔断后处理:结构化输出校验、引用标注、内容安全、降级兜底出站:返回响应、写缓存/写库、异步事件
可观测性要做的事,本质上是:给这些阶段打点,并且串起来。
2. 结构化日志:先把“最小字段集合”落下来
日志是可观测性里最划算的一步,但前提是你别写“人类可读的散文”,而是写机器可聚合的 JSON。
建议每条日志都至少有:
request_id:一次请求的唯一 ID(贯穿全链路)trace_id:如果你接入了 tracing,就把它也写进日志(方便互跳)event:事件名(例如retrieval.search/llm.call.success/llm.call.error)latency_ms:这个阶段耗时error_*:失败就必须有稳定的错误分类字段(别只记 message)
再加上 LLM/RAG 特有的关键字段(按你的系统裁剪):
provider/model:供应商与模型名prompt_version:提示词模板版本(不要只在代码里写死)prompt_hash:提示词最终文本 hash(用于排查同模板不同拼接导致的漂移)input_tokens/output_tokens:token 用量(有就记)cost:成本(可以先不精确,至少能聚合到“趋势”)retrieval_topk、retrieval_doc_ids:检索关键结果(注意脱敏与截断)guardrail_hit/fallback_used:是否触发安全策略/降级
2.1 一段最小的 JSON 日志示例(Node.js)
下面这段不引入任何依赖,直接可用,适合作为“最小落地版本”的基线:
1 | import crypto from "node:crypto"; |
在调用模型的地方,把成功/失败都打出来:
1 | const request_id = newRequestId(); |
2.2 日志里最容易踩的坑
不要记录原始敏感信息:用户输入、知识库原文、检索结果正文,默认都要脱敏/截断/加密,必要时只存 hash。不要只打成功日志:失败日志必须稳定、可聚合,避免“线上报错了但找不到发生了什么”。不要没有版本:prompt/template、检索策略、重排模型都要有版本号,否则你很难判断“是不是刚发布的改动导致的”。
3. 指标(Metrics):先抓住 8 个就够
指标的价值是“看趋势 + 做告警”,所以别一上来就做一大堆。
我建议先把下面 8 个做出来:
request_total:总请求量request_error_total:入口错误量(参数/鉴权/系统异常)llm_call_total:模型调用次数(注意重试会放大)llm_call_error_total:模型调用错误次数(含超时/限流/5xx)llm_latency_ms:模型调用耗时分布(至少 P50/P95)tokens_in_total:输入 token 总量tokens_out_total:输出 token 总量cost_total:成本总量(可以先按粗粒度估算)
有了它们,你就能在一个看板里回答:
- 最近 1 小时错误率有没有异常
- P95 时延是不是突然变高
- 成本是不是随请求量线性增长,还是“单位请求成本”变贵了
3.1 一个“够用”的告警基线
告警不要太复杂,先从最能救命的开始:
错误率:llm_call_error_total / llm_call_total连续 5 分钟 > 阈值时延:P95llm_latency_ms连续 5 分钟 > 阈值成本:cost_total(或单位请求成本)短时间内陡增
阈值别拍脑袋:可以先用一周的历史数据算基线,再逐步收紧。
4. 追踪(Tracing):让“慢在哪里”一眼看出来
日志能定位“发生了什么”,但当你遇到“整体变慢”时,最常见的问题是:
慢在检索?慢在重排?慢在组装提示词?还是慢在模型?
这时 tracing 很好用。最小原则是:
- 每个
request_id都能串起retrieval/prompt_build/llm_call/postprocess的耗时 trace_id写回日志,方便从异常日志跳到链路图
如果你已经有 OpenTelemetry 基础设施,建议直接用它;如果没有,也可以先用“阶段耗时日志”顶上,先把慢点找出来。
5. 回放包(Replay Packet):用来复现、做离线回归
很多 LLM 线上问题的难点是:你很难在事后复现当时的上下文。
建议为“采样的失败请求”保存一个最小回放包(注意合规与成本):
request_id/ts/routeprompt_version/prompt_hash/ 关键参数(temperature、top_p 等)retrieval_doc_ids+ 每段内容的 hash(避免保存原文)model/providerfinal_answer(必要时脱敏)error_class/fallback_used
它的价值在于:
- 事故复盘时可以回放:到底是检索漂了还是 prompt 漂了
- 评估体系可以复用:把线上坏样本沉淀进评测集,形成“回归基线”
6. 一天内可落地的最小 Checklist
如果你想把这篇变成可执行任务,可以按这个顺序落地:
- 给所有入口请求加
request_id(并贯穿到检索与模型调用) - 统一 JSON 日志格式,至少覆盖:
event、latency_ms、error_class - 把
provider/model/prompt_version写进日志 - 把 token 用量写进日志(没有就先空着)
- 把
llm_call_total/llm_call_error_total/llm_latency_ms先做成可视化 - 做 3 条告警:错误率、时延、成本
- 对失败请求做回放包采样(先只存 hash + 元信息)
做到这里,后续的成本治理和事故响应就有“抓手”了。
总结
LLM 应用的可观测性,不是为了“看起来专业”,而是为了两件事:
- 线上出问题能快速定位和止血
- 能把线上问题沉淀成可回归的工程资产(评测集、基线、门禁)
下一篇我打算继续写“生产运维实践”的第二部分:成本治理(Cost Governance)最小实践,把 token、缓存、模型路由、降级策略变成可控的成本曲线。
参考资料
本文永久链接: https://www.mulianju.com/learning-notes/ai-learning-notes-llm-observability-minimal-practice/