AI应用监控与运维
从理论到实践,掌握LLM应用生产环境监控的核心技能。涵盖指标体系搭建、成本追踪、延迟优化、告警配置等关键主题。
将大型语言模型(LLM)从实验室部署到生产环境,需要一套完整的监控与运维体系。传统的软件监控(关注 uptime 和错误率)已无法满足 AI 应用的独特需求——我们需要评估非确定性文本输出的质量、追踪 Token 成本、检测幻觉(hallucination)现象,以及识别模型行为的漂移。
传统监控告诉你系统是否在运行;AI 监控告诉你系统是否正常工作。这两者同样重要,但衡量维度完全不同。
AI 应用在生产环境中面临诸多开发阶段不存在的挑战:
| 维度 | 传统ML监控 | AI监控(LLM/Agent) |
|---|---|---|
| 数据类型 | 结构化数值特征和预测结果 | 自由形式语言输出、多步骤推理 |
| 输出质量 | 预测准确率、AUC等指标 | 相关性、事实准确性、流畅性 |
| 成本结构 | 计算资源成本 | Token消耗、API调用成本 |
| 失败模式 | 精度下降、异常预测 | 幻觉、工具选择错误、循环 |
| 监控频率 | 批量评估为主 | 实时追踪与异步评估结合 |
构建完整的监控指标体系是 AI 运维的基础。根据 MLflow 和行业最佳实践,我们推荐以下六大类指标:
性能监控是基础,如果模型无法可靠地服务请求,其他一切都无从谈起。
| 指标名称 | 描述 | 监控方式 |
|---|---|---|
| throughput | 每秒处理的请求数 | 容量规划和成本管理 |
| error_rate | 失败请求、超时、限流的发生率 | 指示基础设施问题 |
| resource_utilization | GPU内存、CPU使用率、网络带宽 | 决定扩展需求 |
质量监控是 AI 监控的核心,它超越了传统的运行状态检查,评估输出本身的价值。
AI 监控必须评估自由形式语言输出、多步骤 Agent 推理、工具调用链、检索准确性和 Token 成本。这些在传统监控中都不存在。
| 质量指标 | 定义 | 测量方法 |
|---|---|---|
| 幻觉率 | 生成虚假或不支持内容的比率 | 基于检索的groundedness评估 |
| 响应相关性 | 回答与用户问题的相关程度 | 语义相似度、用户信号追踪 |
| 任务完成率 | 成功完成用户请求的比率 | 端到端追踪 |
| 意图识别准确率 | 正确理解用户意图的比率 | 采样人工验证 |
模型漂移发生在输入或输出的统计属性随时间变化时。LLM 中的漂移表现为:
漂移检测需要建立初始部署期间的基线,并持续将生产行为与这些基线进行比较。使用 Jensen-Shannon 散度、嵌入距离分析等统计方法量化漂移程度。
LLM 成本随使用量以难以预测的方式增长。基于 Token 的定价意味着冗长的提示词和长响应直接增加成本,而多步骤 Agent 工作流会将每次用户交互的成本翻倍。
| 追踪维度 | 具体指标 | 业务价值 |
|---|---|---|
| Token消耗 | 每个请求的输入/输出Token数,按端点和用例分类 | 识别高消耗操作 |
| 单次交互成本 | 服务单个用户请求的总成本(含所有模型调用、检索等) | Unit Economics分析 |
| 成本效率 | 支出与质量的比值关系 | 优化决策依据 |
| 浪费识别 | 冗余模型调用、不必要的长上下文、未使用的检索结果 | 降本机会发现 |
使用 OpenTelemetry GenAI 语义约定进行 Token 追踪:
# OpenTelemetry GenAI 语义约定 - Token追踪 # Span属性定义 gen_ai.system # LLM提供商: openai, anthropic gen_ai.request.model # 模型名称: gpt-4, claude-3 gen_ai.usage.input_tokens # 输入Token数 gen_ai.usage.output_tokens # 输出Token数 gen_ai.usage.total_tokens # 总Token数 gen_ai.response.finish_reasons # 生成停止原因
设置多级成本阈值告警,防止意外支出:
# LangSmith成本告警规则配置 # 告警规则1:小时成本超限 Name: Hourly Cost Alert Metric: total_cost (sum) Threshold: > $10.00 Window: 60 minutes Filter: tags contains env:production Severity: Medium # 告警规则2:平均Token数异常 Name: Avg Tokens per Run Metric: total_tokens (mean) Threshold: > 4000 Window: 15 minutes Filter: tags contains env:production AND name = "agent-chain" Severity: Low # 告警规则3:预算消耗百分比 # 建议设置: 50%, 80%, 100% 三档 Name: Budget 80% Consumed Metric: cost_percentage Threshold: > 0.80 Window: Daily rolling Severity: High
设置 Token 使用量跨越 50%、80% 和 100% 预算阈值的告警。一个失控的提示词可能一夜之间烧掉数千美元。
延迟监控需要特别注意 LLM 的复杂性。LLM 延迟由多个组件构成:
| 指标 | 描述 | P50/P90/P95/P99 |
|---|---|---|
| E2E Latency | 端到端请求延迟 | P50: 2s, P95: 8s, P99: 15s |
| TTFT | 首Token时间 | P50: 0.5s, P95: 2s, P99: 5s |
| Inter-token Latency | Token间延迟 | P50: 50ms, P95: 150ms, P99: 300ms |
| Queue Time | 调度队列中的等待时间 | P95: 500ms, P99: 2s |
vLLM 原生支持 Prometheus 指标暴露,可通过以下方式集成监控:
# 启动vLLM并暴露metrics端点 vllm serve mistralai/Mistral-7B-v0.1 \ --max-model-len 2048 \ --disable-log-requests # 访问 http://localhost:8000/metrics 查看原始Prometheus指标
# Prometheus配置 - prometheus.yml scrape_configs: - job_name: 'vllm' static_configs: - targets: ['vllm-server:8000'] metrics_path: '/metrics' scrape_interval: 10s
# Grafana PromQL查询 - 延迟指标 # P95 端到端延迟 histogram_quantile(0.95, sum by (model_name, le) ( rate(vllm:request_latency_seconds_bucket[5m]) ) ) # P99 推理延迟 histogram_quantile(0.99, sum by (batch_size) ( rate(vllm:inference_latency_seconds_bucket{model_name="example"}[5m]) ) ) # 每Token平均延迟 sum(rate(vllm:time_per_output_token_seconds_sum[5m])) by (model_name) / sum(rate(vllm:time_per_output_token_seconds_count[5m])) by (model_name)
AI 应用中的错误追踪比传统软件更复杂,需要区分可重试和不可重试的错误类型。
| 错误类型 | 特征 | 处理策略 |
|---|---|---|
| 瞬态错误 | 网络故障、临时过载 | 指数退避重试 |
| 限流错误 | 配额超出、速率限制 | 尊重限流头、队列延迟 |
| 输入错误 | 格式错误、超出上下文长度 | 验证输入、不重试 |
| 内容策略错误 | 触发安全过滤器 | 记录审查、重新表述 |
| 服务错误 | 500/502等服务器问题 | 备用提供商、熔断器 |
| 模型错误 | JSON解析失败、格式违规 | 输出验证、参数调整重试 |
# LangSmith错误率告警配置 # 告警规则:错误率超过5% Name: Error Rate Alert Metric: error_rate Aggregation: Percentage Threshold: > 0.05 # 5% Window: 10 minutes Filter: tags contains env:production Severity: High # 过滤特定错误类型 Additional Filter: name contains "llm" AND error_type = "RateLimitExceeded" Note: 监控特定端点的限流问题
熔断器模式防止持续调用失败的服务:
# Python 熔断器实现示例 import time from functools import wraps class CircuitBreaker: def __init__(self, failure_threshold=5, timeout=60): self.failure_threshold = failure_threshold self.timeout = timeout self.failures = 0 self.last_failure_time = None self.state = 'CLOSED' # CLOSED, OPEN, HALF_OPEN def call(self, func, *args, **kwargs): if self.state == 'OPEN': if time.time() - self.last_failure_time > self.timeout: self.state = 'HALF_OPEN' else: raise Exception("Circuit is OPEN") try: result = func(*args, **kwargs) if self.state == 'HALF_OPEN': self.state = 'CLOSED' self.failures = 0 return result except Exception as e: self.failures += 1 self.last_failure_time = time.time() if self.failures >= self.failure_threshold: self.state = 'OPEN' raise # 使用示例 breaker = CircuitBreaker(failure_threshold=5, timeout=60) result = breaker.call(llm_api_call, prompt)
# Python 重试处理器实现 import asyncio import random class RetryHandler: def __init__(self, max_retries=3, base_delay=1): self.max_retries = max_retries self.base_delay = base_delay async def retry_with_backoff(self, func, *args, **kwargs): for attempt in range(self.max_retries): try: return await func(*args, **kwargs) except RateLimitError: if attempt == self.max_retries - 1: raise delay = self.base_delay * (2 ** attempt) + random.uniform(0, 1) await asyncio.sleep(delay) except AuthenticationError: raise # 不重试认证错误 except NetworkError: if attempt == self.max_retries - 1: raise await asyncio.sleep(self.base_delay * random(1, 3))
可观测性是 AI 运维的基石。使用 OpenTelemetry 构建统一的可观测性体系。
OpenTelemetry 为 LLM 应用定义了标准的语义约定:
# OpenTelemetry GenAI 语义约定 - Span事件 # Span事件名称和触发时机 gen_ai.system.message # LLM调用前 - 系统消息 gen_ai.user.message # LLM调用前 - 用户消息 gen_ai.assistant.message # LLM调用后 - 助手响应 gen_ai.tool.message # 工具调用后 - 工具结果 # 事件属性 gen_ai.prompt.content # 提示词内容 gen_ai.completion.content # 补全内容 gen_ai.prompt.role # 角色 (system/user/assistant)
# 安装 OpenTelemetry instrumentation pip install opentelemetry-instrumentation-openai pip install opentelemetry-instrumentation-anthropic pip install opentelemetry-instrumentation-langchain pip install opentelemetry-instrumentation-llamaindex # Python代码集成 from opentelemetry import trace from opentelemetry.sdk.trace import TracerProvider from opentelemetry.sdk.resources import Resource from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter # 配置TracerProvider resource = Resource.create({ "service.name": "my-llm-app", "deployment.environment": "production" }) provider = TracerProvider(resource=resource) trace.set_tracer_provider(provider) # 设置OTLP导出器 exporter = OTLPSpanExporter( endpoint="http://collector:4318/v1/traces" ) provider.add_span_processor(BatchSpanProcessor(exporter))
LLM 调用较慢(100ms-30s)且生成大型 Span,需要应用不同的采样率:
| 场景 | 策略 | 采样率 |
|---|---|---|
| 开发环境 | AlwaysOn | 100% |
| 生产-成功调用 | TraceIdRatioBased | 5-10% |
| 生产-错误 | Tail-based | 100% |
| 高Token请求(>2K) | Tail-based | 100% |
| Agent运行 | Tail-based | 100%(稀有且高价值) |
# Python采样器配置 from opentelemetry.sdk.trace.sampling import ParentBased, TraceIdRatioBased # 采样10%的根Span,子Span继承决策 sampler = ParentBased(root=TraceIdRatioBased(0.10)) provider = TracerProvider(sampler=sampler, resource=resource)
将完整提示词文本存储在 Span 属性中是反模式。属性总是被索引、有大小限制,并在后端暴露 PII。使用 Span 事件存储内容,可以在 Collector 级别过滤或删除。
# OpenTelemetry Collector配置 - PII保护 # otel-collector-config.yaml processors: transform: trace_statements: - context: spanevent statements: # 删除提示词和补全内容 - delete_matching_keys(attributes, "gen_ai.prompt.content") - delete_matching_keys(attributes, "gen_ai.completion.content") # 或使用哈希进行关联(不暴露内容) attributes: actions: - key: gen_ai.prompt.content action: hash hash_salt: ${env:HASH_SALT}
在生产环境中验证提示词变更需要科学的实验方法。
# LangSmith实验配置 - 比较提示词变体 # 创建实验 Experiment Name: prompt-v2-vs-v3 Variants: - Control: prompt_version = "v2" - Treatment: prompt_version = "v3" # 对比指标 Primary Metrics: - feedback_score (mean) - latency_p95 - total_cost Statistical Significance: Min Sample Size: 100 runs per variant Confidence Level: 95%
建议的分阶段发布比例:
每个阶段都需要监控:质量分数是否下降、延迟是否增加、成本是否超预期。任何指标异常都应该暂停并回滚。
2026年主流 AI 可观测性工具对比:
厂商:LangChain
原生 LangChain 集成优秀
实时追踪和仪表盘
内置评估功能
与 LangChain 强绑定
适用:使用 LangChain 的团队
类型:开源可自托管
完全开源,可自部署
支持 OpenTelemetry
无供应商锁定
缺乏内置评估
适用:需要数据主权的团队
类型:开源平台
完整的 AI 监控栈
LLM Judge 评估
Apache 2.0 许可
配置相对复杂
适用:需要企业级监控的团队
类型:企业级 SaaS
基础设施监控完善
LLM 可视性集成
不是专门的 LLM 监控
成本较高
适用:已使用 Datadog 的团队
| 维度 | 开源工具 | 商业 SaaS |
|---|---|---|
| 数据控制 | 完全自控,部署在自己的基础设施 | 数据发送到第三方服务器 |
| 成本结构 | 无 per-trace 费用,无使用限制 | 按 trace 或席位收费,规模扩大成本增加 |
| 供应商锁定 | 无锁定,可导出到任何后端 | 锁定于供应商生态系统 |
| 合规考虑 | 生产数据完全受控 | 敏感 trace 引发隐私合规担忧 |
# LangSmith Python SDK 配置 from langchain_core.tracers import LangSmithTracer from langchain_core.runnables import RunnableConfig # 追踪配置 @traceable( project_name="production-agent", tags=["env:production", "feature:qa"], metadata={ "model": "gpt-4o", "version": "v2.1" } ) def answer_question(question: str) -> str: # 业务逻辑 return response
# LangChain Chain 调用配置 chain.invoke( {"question": user_question}, config={ "tags": ["feature:qa", "env:production"], "metadata": { "user_id": user_id, "session_id": session_id } } ) # 规则:每个生产调用至少需要 env:production 和 feature:* 标签
# LangSmith 生产告警配置 - 四个核心规则 # ============================================ # 告警1:P95延迟SLA违规 # ============================================ Name: P95 Latency > 5s Metric: run_latency_ms (p95) Threshold: > 5000 ms Window: 5 minutes Filter: tags contains env:production Severity: Critical # ============================================ # 告警2:错误率飙升 # ============================================ Name: Error Rate > 5% Metric: error_rate Threshold: > 0.05 Window: 10 minutes Filter: tags contains env:production Severity: High # ============================================ # 告警3:成本异常 # ============================================ Name: Hourly Cost > $10 Metric: total_cost (sum) Threshold: > $10.00 Window: 60 minutes Filter: tags contains env:production Severity: Medium # ============================================ # 告警4:Token预算超限 # ============================================ Name: Avg Tokens per Run > 4000 Metric: total_tokens (mean) Threshold: > 4000 Window: 15 minutes Filter: tags contains env:production AND name = "answer-question" Severity: Low
# LangSmith Webhook 配置 - Slack通知 import httpx LANGSMITH_API_KEY = "ls__your_key_here" PROJECT_ID = "your-project-id" webhook_payload = { "name": "slack-production-alerts", "type": "webhook", "config": { "url": "https://hooks.slack.com/services/T00/B00/xxx", "headers": {"Content-Type": "application/json"} } } response = httpx.post( f"https://api.smith.langchain.com/api/v1/projects/{PROJECT_ID}/alert-channels", headers={"x-api-key": LANGSMITH_API_KEY}, json=webhook_payload ) print(response.json())
# Slack 告警消息格式 🚨 [Critical] P95 Latency > 5s Project: my-llm-app Value: 7,340ms (threshold: 5,000ms) Window: 2026-03-09 14:05 → 14:10 UTC View runs: https://smith.langchain.com/...
# 用户反馈追踪 from langsmith import Client client = Client() def record_user_feedback(run_id: str, score: int, comment: str = ""): # score: 1 = positive, 0 = negative client.create_feedback( run_id=run_id, key="user_thumbs", score=score, comment=comment, source_info={"source": "webapp", "version": "v2.1"} ) # API端点调用 @app.post("/feedback") async def submit_feedback(run_id: str, rating: int): record_user_feedback(run_id=run_id, score=rating) return {"status": "ok"}
# Grafana Dashboard JSON 配置片段 { "title": "LLM Application Monitoring", "panels": [ { "title": "P95 Latency", "type": "timeseries", "datasource": {"type": "prometheus", "uid": "${DS_PROMETHEUS}"}, "expr": "histogram_quantile(0.95, sum(rate(llm_latency_seconds_bucket[5m])) by (le))" }, { "title": "Token Usage", "type": "timeseries", "datasource": {"type": "prometheus"}, "expr": "sum(rate(gen_ai_usage_input_tokens[5m]))" }, { "title": "Error Rate", "type": "stat", "datasource": {"type": "prometheus"}, "expr": "sum(rate(llm_errors_total[5m])) / sum(rate(llm_requests_total[5m]))" } ] }
| 领域 | 关键行动 |
|---|---|
| 指标体系 | 建立性能、质量、成本、漂移、延迟、反馈六大维度监控 |
| 成本追踪 | 设置50%/80%/100%预算告警,按用户/功能/模型归因 |
| 延迟优化 | 追踪TTFT、P99延迟,识别Prefill/Decode/Queue瓶颈 |
| 错误处理 | 实现熔断器、重试策略、分类错误处理 |
| 可观测性 | 采用OpenTelemetry,使用采样策略保护PII |
| A/B测试 | 灰度发布+统计显著性验证 |
env:production, feature:*)
添加评论