为什么AI应用需要不一样的监控思路
传统Web应用的监控思路是盯着服务器CPU和响应时间。AI应用不一样,一个用户请求背后可能触发embedding查询、LLM调用、rerank排序三步,每一步都有可能出问题。
你以为接口返回了200就没事了?LLM返回了一段空内容、向量检索召回了无关文档、rerank把最好的结果排到了第五位。这些问题传统监控根本发现不了。
Chrome使用数据显示,用户90%的时间花在页面加载之后的交互上。AI应用更是如此,用户发出一个提问,等待响应的几秒钟里,任何卡顿都会被放大感知。可观测性不是锦上添花,是AI应用上线后的刚需。
第一层:结构化日志
日志是最基础的监控手段,但大多数项目的日志方式还停留在print大法。AI应用需要的不是”出错了打一行log”,而是结构化、可查询的日志。
每个日志条目应该包含这些字段:时间戳、用户ID、请求ID、操作类型(embedding/llm/rerank)、耗时、输入摘要、输出摘要、错误信息。这样出了问题才能按用户ID或操作类型筛选。
Python里用structlog或标准库logging配JSON formatter就能实现。关键是别把所有信息塞进一个字符串,要拆成字段。一个实用的做法:在LLM调用的入口和出口各打一条日志,记录模型名称、token用量、耗时。这样你能在日志系统里直接画出每次调用的耗时分布。
日志的成本要注意。如果每天1万次LLM调用,每次打4条日志,一个月就是120万条。用CloudWatch或Datadog的话,日志费用可能比API费用还高。建议只保留ERROR级别日志7天,INFO级别日志3天,DEBUG级别只在开发环境开启。
第二层:指标监控
日志解决的是”出了什么问题”,指标解决的是”整体怎么样”。AI应用需要关注的指标分成三类。
第一类是业务指标:请求成功率、平均响应时间、P95响应时间、用户满意度(如果有点赞/踩按钮)。这些指标直接反映用户体验,应该配告警阈值。比如P95响应时间超过5秒就报警。
第二类是AI特有指标:token使用量(分输入和输出)、模型调用次数、embedding维度、向量检索召回率、rerank前后排序变化。这些指标帮你判断AI组件是否健康。token使用量尤其重要,直接关系到成本。
第三类是基础设施指标:CPU、内存、GPU利用率(如果自部署模型)、数据库连接池、缓存命中率。这些用Prometheus + Grafana就能搞定,不需要花大钱。
一个容易忽略的指标:LLM输出的空响应率。如果用户提问后LLM返回空内容或”我无法回答”,这不会触发HTTP错误,但用户体验已经崩了。建议在代码里加一个判断,当LLM输出为空或长度小于阈值时,打一条WARN级别日志并递增一个计数器。这个计数器超过阈值就告警。
第三层:分布式追踪
日志和指标能发现问题,但定位根因需要追踪。AI应用的一个请求可能经过API网关、向量数据库、LLM API、rerank服务四个节点。出问题时,你得知道时间花在了哪一步。
OpenTelemetry是目前主流的可观测性框架,支持Traces、Metrics、Logs三种信号。它的工作原理是给每个请求分配一个trace_id,经过的每个服务生成一个span,span之间通过parent_id串联,最终形成一棵调用树。
对于AI应用,关键是在LLM调用和向量检索这两个地方加span。OpenTelemetry的Python SDK使用很简单:
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
# 初始化tracer
provider = TracerProvider()
processor = BatchSpanProcessor(OTLPSpanExporter(endpoint="http://localhost:4317"))
provider.add_span_processor(processor)
trace.set_tracer_provider(provider)
tracer = trace.get_tracer(__name__)
# 给LLM调用加追踪
def call_llm(prompt, model="gpt-4o-mini"):
with tracer.start_span("llm_call") as span:
span.set_attribute("llm.model", model)
span.set_attribute("llm.prompt_length", len(prompt))
# 实际调用LLM
response = openai.chat.completions.create(
model=model,
messages=[{"role": "user", "content": prompt}]
)
span.set_attribute("llm.tokens_input", response.usage.prompt_tokens)
span.set_attribute("llm.tokens_output", response.usage.completion_tokens)
span.set_attribute("llm.latency_ms", response.usage.total_tokens)
return response.choices[0].message.content
这样每个LLM调用的模型名称、token用量、耗时都会记录在trace里。在Jaeger或Grafana Tempo里查看时,你能清楚看到一个用户请求的完整链路:API入口200ms → 向量查询150ms → LLM调用2000ms → rerank300ms。一眼就能看出LLM调用是瓶颈。
成本控制:别让监控比应用还贵
可观测性的成本很容易失控。几个控制建议:
- 采样率:不是每个请求都需要追踪。生产环境设1%-10%采样率就够了,OpenTelemetry支持配置ParentBasedSampler
- 数据保留:Traces保留7天,Metrics保留30天,Logs按级别分别保留3-7天
- 自建vs托管:小项目用Jaeger + Prometheus自建,月成本不到$10。中大型项目用Datadog或New Relic,但要做好预算监控
- 用免费的:Cloudflare Workers可以用Logpush把日志推到R2存储,成本几乎为零。Vercel自带Web Analytics和Speed Insights
从哪一步开始
如果你现在什么监控都没有,别急着上OpenTelemetry。按这个顺序来:
第一步,把print换成结构化日志,记录每次LLM调用的模型、token、耗时。一周后你就能发现哪些请求最慢、哪些最贵。
第二步,加几个关键指标:请求成功率、P95响应时间、日token消耗。用Prometheus或简单的计数器都行。
第三步,等用户量上来、链路变复杂了,再上分布式追踪。这时候你已经有足够的日志和指标数据来做基线对比。
监控不是越多越好,是够用就行。能发现问题、能定位根因、成本可控,这三点满足就够了。
