聚合层日志与可观测性实践:监控多模型 API 调用
当你的应用同时调用多个大模型时,“出问题不知道问谁”是最头疼的情况——是上游超时?是聚合层路由错了?还是你的 Prompt 触发了内容过滤?完善的可观测性体系让这些问题有迹可查。
我自己踩过一次比较典型的坑:线上某天下午客户反馈”回复变慢了”,打开监控一看请求成功率 100%,CPU 也没打满,但客户投诉一直没停。后来才发现问题出在平均延迟这个指标本身——平均值把长尾拉平了,真正的问题是 P99 延迟从 3 秒蹿到了 18 秒,但这批慢请求只占总量的 2%,平均值几乎不受影响。这也是这篇文章想强调的核心:监控大模型调用,光看”平均”是会被坑的,得看分布。下面从需要盯哪些指标,到怎么落地 Prometheus/日志/告警,再到几个真实排查案例,一步步说。
聚合层需要监控什么
大模型调用的可观测性和传统服务有所不同,核心关注点:
| 类别 | 关键指标 | 说明 |
|---|---|---|
| 延迟 | TTFT、总延迟、P95/P99 | 按模型、上游分别统计 |
| 吞吐 | 请求数/分钟、并发数 | 判断容量是否充足 |
| 成本 | Token 用量、按模型/项目分摊费用 | 防止账单失控 |
| 错误 | 4xx/5xx 比例、上游超时率 | 区分网关错误与上游错误 |
| 质量 | 内容过滤率、重试率、Fallback 触发率 | 反映服务稳定性 |
| 路由 | 各上游流量分布 | 验证路由策略是否生效 |
这张表里最容易被新手忽略的是”延迟”这一行的 P95/P99,而不是平均延迟。原因很简单:大模型的响应时间不是正态分布,而是长尾分布——大部分请求几百毫秒到几秒就回来了,但总有一小撮请求因为上游排队、超长上下文、或者触发了重试逻辑而拖到十几秒甚至超时。如果你只看平均值,这一小撮慢请求会被大量快请求”平均”掉,等你发现问题时往往已经有一批用户在骂了。所以监控面板上,P95/P99 要单独开一条曲线,而不是只放一个均值数字。
同理,“吞吐”里的并发数比”请求数/分钟”更值得盯——大模型调用是长连接、慢响应的场景,同样是每分钟 100 个请求,如果平均响应时间是 1 秒,并发量大概是个位数;但如果响应时间涨到 10 秒,同样的请求量并发数会涨到大几十甚至上百,很容易把你给上游配置的并发上限打满,进而引发排队和超时——这也是为什么延迟劣化经常会连带引发吞吐类的告警,两者要联动着看,而不是孤立盯着某一个指标。
LiteLLM Proxy 内置监控
LiteLLM Proxy 内置 Prometheus 指标端点,无需额外开发:
# litellm_config.yaml
general_settings:
enable_prometheus: true
litellm_settings:
success_callback: ["prometheus"]
failure_callback: ["prometheus"]
启动后访问 http://localhost:4000/metrics,即可看到标准 Prometheus 格式的指标,包括:
litellm_requests_metric— 请求总数(按模型、状态码分标签)litellm_total_tokens— Token 用量litellm_deployment_latency_histogram— 延迟分布litellm_remaining_requests_metric— 各上游剩余配额
这几个指标里 litellm_deployment_latency_histogram 是重点,也是最容易被误用的一个。它是直方图(histogram)类型而不是普通的 gauge,意味着 Prometheus 存的不是”当前延迟是多少”,而是”落在每个延迟桶(bucket)里的请求数各有多少”。查询的时候不能直接对它取平均,得用 PromQL 的 histogram_quantile 函数按分位数还原出 P95/P99,比如:
histogram_quantile(0.95, sum(rate(litellm_deployment_latency_histogram_bucket[5m])) by (le, model))
这条查询的意思是:取最近 5 分钟的数据,按 model 标签分组,算出每个模型的 P95 延迟。刚接触 Prometheus 的人容易在这里踩两个坑:一是直接对 histogram 求 avg(),这在语义上是错的(histogram 本身没有单点数值可平均);二是 rate() 的时间窗口开太短(比如 1m),在请求量不大的场景下样本太少,算出来的分位数会剧烈抖动,一般建议至少 5m 起步,流量小的服务甚至要拉到 15m。
如果你想看某个上游是不是配额快用完了,直接盯 litellm_remaining_requests_metric 比等它报 429 再排查要主动得多——一般设个”剩余配额低于 20%“的预警,能提前十几分钟到几小时发现容量问题,而不是等用户报错。
Grafana 仪表盘
LiteLLM 提供官方 Grafana Dashboard JSON,导入后即可可视化上述指标。关键面板建议:
- 请求成功率(分模型)
- TTFT P50/P95/P99 趋势
- 每日 Token 费用(分项目/key)
- Fallback 触发频率
面板布局上有个小经验:把”Token 费用”和”请求成功率”放在同一行、同一时间轴对齐着看,很容易发现异常——比如某天费用突然涨了 30%,但请求数没怎么变,配合成功率曲线一眼就能看出是不是某个模型触发了大量重试(重试会重复计费但不一定计入”新增请求数”),还是有人在传超长的上下文把单次 Token 消耗顶上去了。这个组合面板比单独看费用曲线更容易定位根因。
结构化日志:让每次调用可追溯
聚合层的每次调用日志应包含:
{
"request_id": "req_01j...",
"timestamp": "2026-06-17T10:23:45Z",
"model_requested": "chat-model",
"model_actual": "anthropic/claude-3-5-sonnet",
"upstream": "anthropic",
"input_tokens": 523,
"output_tokens": 187,
"ttft_ms": 312,
"total_ms": 2841,
"cost_usd": 0.004215,
"status": "success",
"api_key_hash": "sha256:ab12...",
"user_id": "user_xyz"
}
model_actual(实际路由到的上游)与 model_requested(请求的逻辑名)分开记录,便于排查路由问题。
这份日志里还有几个字段值得多说两句为什么要这么设计:
request_id一定要在网关这一层生成,而不是等上游返回后再补。原因是很多超时、连接被断开的请求根本拿不到上游的响应,如果 ID 依赖上游返回,这些失败请求就没法被追溯,而这些恰恰是你最需要排查的那批。ttft_ms(首字节时间)和total_ms(总耗时)要分开记录,不要只留一个总耗时。流式返回场景下,用户体验主要取决于 TTFT,而不是总耗时——一个请求哪怕总共花了 8 秒,只要 300 毫秒内开始吐字,用户体感也是”很快”;反过来如果 TTFT 要 5 秒,哪怕总耗时不长,用户也会觉得”卡住了”。只记总耗时会让你误判问题出在哪一段。api_key_hash而不是明文 key。这个是安全底线,日志系统的访问权限往往比密钥管理系统宽松得多,一旦明文 key 进了日志,日志系统就成了新的攻击面。cost_usd精确到小数点后 4~6 位。大模型调用的单价通常是”每百万 Token 多少美元”这种量级,单次请求的成本可能只有几厘钱,如果只保留 2 位小数,累计到月账单对账时会出现明显的取整误差,尤其是高频小请求场景。
如果你的应用还接入了链路追踪(比如 OpenTelemetry),建议在这份日志里再加一个 trace_id 字段,把网关这一跳和上下游服务的调用链串起来——这样排查”用户点击按钮后到底卡在哪一步”时,不用在网关日志、业务日志、前端日志之间来回切换,一个 trace_id 搜到底。
LLM 专属可观测工具
传统 APM 工具(Datadog、New Relic)对 LLM 调用的支持较弱,推荐使用 LLM 专属工具:
| 工具 | 特点 | 适用场景 |
|---|---|---|
| LangSmith | LangChain 官方,追踪链路和 Prompt | LangChain/LangGraph 项目 |
| Langfuse | 开源,支持 Prompt 管理和评估 | 需要自托管、成本敏感 |
| Helicone | 轻量代理模式,接入成本低 | 快速接入,不需要改代码结构 |
| Arize Phoenix | 关注模型质量评估 | 需要 LLM 评估和漂移检测 |
以 LangSmith 为例,LiteLLM 原生集成只需加回调:
litellm_settings:
success_callback: ["langsmith"]
failure_callback: ["langsmith"]
environment_variables:
LANGSMITH_API_KEY: "ls__xxxx"
LANGSMITH_PROJECT: "my-gateway"
四个工具怎么选,我的判断依据大致是这样的:如果你团队本来就用 LangChain/LangGraph 写业务逻辑,选 LangSmith,因为链路里每一步(工具调用、子链、Prompt 模板渲染)都能自动被追踪,不用额外埋点;如果你对数据出境和自托管有硬要求(比如公司安全合规不允许调用链数据发到境外 SaaS),选 Langfuse,它开源、可以整套部署在自己的机房;如果你现在的接入方式就是直接调 API、没有用任何 Agent 框架,只是想”先接上看看”,选 Helicone 最省事,改一行 base_url 指向它的代理就能拿到全部调用记录,不需要碰业务代码;如果你已经过了”能不能跑通”的阶段,现在关心的是”这批回复是不是变差了”(比如换了个上游模型之后质量有没有退化),才需要上 Arize Phoenix 这类做质量评估和漂移检测的工具,因为前三个工具本质上是”记录调用”,不负责”评价好坏”。
这四个工具不是互斥的——实际项目里很常见的组合是 LiteLLM 的 Prometheus 指标负责基础设施层面的延迟/流量/错误率告警,LangSmith 或 Langfuse 负责调试单次调用的完整上下文(比如某个用户投诉”回答文不对题”,你需要能翻出那次调用完整的输入输出和中间步骤),两边各司其职,不用纠结”只能选一个”。
告警策略
可观测性的价值在于能及时发现问题,关键告警:
错误率 > 5% 持续 5 分钟 → 立即告警
P95 延迟 > 10s → 立即告警
单日费用 > 预算 80% → 预警
上游 X 成功率 < 90% → 考虑暂停路由到该上游
Fallback 触发率 > 20% → 主上游可能有问题
这几条阈值不是拍脑袋定的,是踩过坑之后倒推出来的经验值,说说背后的道理:
错误率阈值为什么是 5% 而不是 1%? 大模型上游本身就有一定的自然失败率(限流、模型侧偶发超时),设得太敏感(比如 1%)会导致告警一天响好几次、大家慢慢就把告警静音了,等真正出问题时反而没人理——这叫”告警疲劳”,是可观测性体系里最容易忽视也最致命的反模式。5% 是一个经验上”明显异常但不会被日常抖动触发”的平衡点,具体数值要结合你自己历史数据里的正常波动区间来定,不是照抄就完事。
P95 延迟阈值要按模型分开设,不能一刀切。 一个轻量模型(响应通常 12 秒)和一个需要深度推理的模型(响应通常 58 秒)用同一个”10 秒告警”的阈值,前者提前很久就该告警了却没触发,后者可能正常波动就把阈值蹿穿了天天误报。落地时建议把阈值做成按 model 标签区分的规则,而不是全局一条线。
“考虑暂停路由”和”立即告警”要分级,不要一个通道全炸。 上游成功率跌破 90% 这种情况,本质上是给你一个”要不要手动切走这个上游”的决策信号,通常不需要半夜把人叫起来,放到工作时间处理的通道就行;但错误率和延迟这两条真正影响用户体验的,才应该走电话/短信这种强打扰的告警渠道。把所有告警都塞进同一个群或者同一个优先级,最后的结果往往是重要告警被淹没在噪音里。
一次真实排查:P95 延迟蹿高但错误率没变
分享一个真实碰到的场景,方便你照着排查思路走一遍。现象:错误率一直稳定在 0.3% 左右没有变化,但 P95 延迟从平时的 2.5 秒涨到了 9 秒,Fallback 触发率也没有明显上升。按下面的顺序排查:
- 先看是不是所有模型都在变慢,还是只有某一个。 在 Grafana 里把延迟面板按
model拆开看,发现只有路由到某个特定上游的请求慢了,其他模型正常——说明问题大概率在这个上游,而不是网关本身或者网络链路。 - 确认是不是这个上游的配额或者限流在起作用。 查
litellm_remaining_requests_metric,发现这个上游的剩余配额一直在 0 附近徘徊——原来是这个上游的限流阈值比预期低,请求都在排队等配额释放,排队时间直接体现为延迟升高,但因为 LiteLLM 配了重试和排队而不是直接拒绝,所以错误率没有涨。 - 确认是不是输入变长了。 查了一下
input_tokens字段的分布,发现没有明显变化,排除”用户传了超长上下文”这个可能性。 - 结论和修法: 给这个上游申请提高限流额度,同时把它在路由权重里的比例临时调低,配合 降低聚合层延迟 里提到的延迟感知路由策略,把更多流量分给延迟更低的上游,问题在申请生效前先靠路由权重缓解了。
这个案例想说明的是:延迟和错误率是两个独立的维度,延迟涨了不代表一定有请求失败(很多时候是在”优雅排队”),但用户体验一样会受损,所以两个指标都要单独设告警,不能用错误率没涨就当作”没问题”。
常见问题
日志里要不要记录请求内容和响应内容? 这是一把双刃剑:记录有助于问题排查,但涉及用户隐私和数据合规风险。建议默认不记录明文内容,仅在调试模式下开启,生产环境记录 hash 或截断后的摘要。确保日志存储符合你的数据合规要求。
如何区分网关错误和上游错误?
在日志中分别记录 gateway_error(网关自身异常)和 upstream_error(上游返回错误)。LiteLLM 的错误日志中会标注错误来源,Prometheus 指标也有对应标签区分。
多个上游的延迟差异很大,怎么在 Grafana 里直观展示?
按 upstream 标签分组的延迟热图是最直观的方式。如果不同上游的延迟分布差异显著,通常说明路由策略有优化空间,可以考虑切换到延迟感知路由,详见降低聚合层延迟。
延伸阅读:
- 多模型聚合 API 完整指南
- 降低聚合层延迟
- 聚合网关的密钥与合规安全
- 更多聚合 API 内容见 聚合API 专题