生产工程 · 2025年8月13日 · 4 分钟

LLM 应用的可观测性与评估体系

Trace / Evaluation / Guardrails——LLM 应用独有的性能、质量和安全三维观测体系。

传统软件的监控很成熟——接口延迟、错误率、吞吐量,一层层监控系统早就完善了。但 LLM 应用不一样。HTTP 200 不代表回答正确,延迟正常不代表输出了无毒内容。

LLM 应用的监控需要全新的维度。这篇文章讲讲怎么做好 LLM 应用的观测和评估。

为什么传统监控不够

传统 API 的监控:

请求 → 处理 → 响应 → 记录状态码和延迟

LLM 调用的监控:

用户输入 → LLM 调用 → 工具调用 → 再次 LLM 调用 → ...
  → 最终输出
  ↓
我们需要知道:
  - 输出有没有幻觉?
  - 工具调用正确吗?
  - 多轮之后有没有偏离主题?
  - 成本花了多少?
  - 延迟在哪里花了最多时间?

传统的 HTTP 状态码和时间戳解决不了这些问题。

三大观测维度

LLM 应用的可观测性分三个维度:

1. 性能观测(Performance)
   延迟、吞吐量、Token 用量、成本
   → 回答"系统跑得怎么样"

2. 质量观测(Quality)
   输出准确率、忠实度、相关性
   → 回答"输出质量怎么样"

3. 安全观测(Safety)
   有害内容、越狱、数据泄露
   → 回答"系统安全吗"

1. 性能观测

基本的指标

延迟:
  TTFT(Time to First Token):首 token 延迟
  TPOT(Time per Output Token):每 token 生成时间
  总延迟

用量:
  输入/输出 token 数
  模型调用次数(包括工具调用的多轮)
  KV Cache 命中率

成本:
  每次请求的总 cost
  按用户、按功能拆分的成本
  成本趋势(是否有突发增长)

并发:
  最大并发数
  排队长度
  限流触发次数

Trace 是必须的。没有 trace,你无法知道一个"慢请求"到底慢在哪里——是 LLM 调用慢、工具调用慢、还是网络传输慢?

每个 LLM 调用都需要记录完整的 trace:

Request ID: req_abc123
  Step 1: LLM call (claude-sonnet-4)
    - Input tokens: 1520
    - Output tokens: 35 (含 tool call)
    - Duration: 1.2s
  
  Step 2: Tool call (search_knowledge_base)
    - Query: "分布式锁 Redisson 配置"
    - Result: 3 documents found
    - Duration: 0.3s
  
  Step 3: LLM call (claude-sonnet-4)
    - Input tokens: 2100 (含检索结果)
    - Output tokens: 256
    - Duration: 2.1s
  
  Total: 3.6s | Cost: $0.008

2. 质量观测

质量观测是 LLM 应用的独有维度——传统软件不需要问"这个响应正确吗",但 LLM 应用必须问。

离线评估(Eval)

离线评估是系统上线前做的评估。用标注好的测试集(Golden Dataset)评估模型输出质量。

关键指标:

Faithfulness(忠实度):
  输出是否基于提供的上下文?
  最常见的失败模式:模型在检索结果之外自行编造信息

Answer Relevance(回答相关性):
  回答是否针对用户问题?
  失败模式:答非所问、泛泛而谈

Context Precision(上下文精确度):
  有多少检索到的上下文真正被使用了?
  失败模式:检索到了但没用上

Hallucination Rate(幻觉率):
  模型输出了不在上下文中的信息?
  这是最重要的指标之一

在线评估(Monitoring)

离线评估不能覆盖所有线上场景。在线监控需要自动化的评估器(LLM-as-Judge)。

def evaluate_response(question: str, answer: str, context: list[str]) -> dict:
    """用另一个 LLM 评估回答质量"""
    prompt = f"""你是一个评估助手。评估以下回答的质量。

用户问题:{question}
参考上下文:{context}
模型回答:{answer}

评估维度(1-5分):
1. 忠实度:回答是否有事实基础?
2. 相关性:回答是否针对问题?
3. 完整性:回答是否覆盖了问题的所有方面?

请输出 JSON:
{{"faithfulness": 4, "relevance": 5, "completeness": 3, "explanation": "..."}}
"""
    return json.loads(llm.generate(prompt))

LLM-as-Judge 的陷阱

  • 位置偏差:Judge LLM 偏好列表中的第一个选项
  • 自我强化:Judge 偏好与自己风格相似的输出
  • 长度偏差:Judge 认为"越长的越好"
  • 谨慎偏差:Judge 倾向于给中等分数

缓解方法:使用不同的模型做 Judge(不要用输出模型本身),多次评估取平均,在 prompt 中明确要求忽略长度和格式。

评估频率

上线前:完整 Golden Set 评估(100-500 个样本)
每周:抽样评估(50-100 个最新对话)
每次迭代:Diff 评估(新旧版本在相同测试集上的对比)
持续监控:关键指标自动化(忠实度、拒绝率、用户反馈)

3. 安全观测

LLM 应用的安全观测关注三个方面:

有害内容检测

用户输入 → 检测有害内容(PII、仇恨言论、攻击)
模型输出 → 检测有害内容(越狱、敏感话题)

"检测"可以用内容安全 API(如 OpenAI 的 Moderation API),也可以用小模型做分类。

Prompt Injection 检测

目标:检测用户是否在尝试劫持模型的指令
方式:
  - 检测用户输入中的"忽略之前指令"
  - 检测用户输入中的角色扮演请求
  - 检测输入格式的异常变化

数据泄露监控

目标:确保模型输出不包含敏感信息
方式:
  - 输出中是否包含 API Key、密码、个人隐私信息
  - 输出中是否包含内部文档的原始文本
  - 跨会话的相关信息泄露

工具链

工具             做什么                            什么时候用
────────────────────────────────────────────────────────────────
Langfuse         LLM trace + 成本跟踪 + 评估         日常开发 + 生产
MLflow           Eval + Prompt 版本管理 + 实验跟踪    模型实验 + 对比测试
LangSmith        Trace + 调试 + 测试集管理            LangChain 生态
Phoenix (Arize)  在线监控 + 漂移检测                  生产环境监控
WhyLabs          LLM 安全 + 异常检测                  安全合规场景
Guardrails       输出校验 + 格式保证                  生产环境输出稳定

我个人的推荐:从小开始,Langfuse 起步。它的开源版免费,部署简单,trace + eval + cost tracking 都有。等需要更专业的功能再换。

配置一个最基础的 trace:

# 只需要在初始化 LLM 客户端时加一个 wrapper
from langfuse import Langfuse
from langfuse.openai import OpenAI

# 自动 trace 所有 OpenAI 调用
client = OpenAI()

# Agent 调用时自动记录
response = client.chat.completions.create(
    model="gpt-4o",
    messages=[...],
    tools=[...],
    # 自动生成 trace
)

Eval 设计的最佳实践

构建 Golden Dataset

一个好的 Golden Dataset 应该包含:

正常用例(60%):
  常见问题、典型场景
  验证常规功能是否正常

边界用例(20%):
  极短输入("?""好")
  极长输入(超过 4K tokens)
  多意图问题
  含糊不清的问题

失败用例(10%):
  已知的 Bad Case(之前答错的问题)
  常见的用户错误

安全用例(10%):
  Prompt Injection 尝试
  敏感信息查询
  有害内容请求

评估的自动化

一次好的评估流程应该是自动化的、可重复的:

修改 prompt / 切换模型 → 自动跑 Golden Set
  → 计算各维度分数 → 对比基线版本
  → 生成报告 → 判定通过/不通过

每次上线前应该对比新旧版本的分数差异。如果新版在某些维度上有下降,在做决定前需要明确知道下降的原因。

Eval 的陷阱

陷阱 1:用同一个 LLM 做生成和评估

如果 Agent 用 GPT-4o,Judge 也是 GPT-4o,那评估结果会偏高(自我强化偏差)。如果你发现评估分数一直很高但用户反馈不好,先检查是不是在"自己评自己"。

陷阱 2:只看分数不看 Bad Case

分数从 85 升到 90 不值得高兴——真正重要的是那 10% 的失败案例是什么。每次评估都应该去读几个失败的案例,理解失败模式。

陷阱 3:没有版本管理

Prompt 改了,效果好了还是差了?如果每次改 prompt 不做测评、不做版本对比,你永远不知道哪个版本更好。Prompt 要像代码一样版本化,每版都要有对应的评估结果。

生产级监控体系

┌─────────────────────────────────────────────────────────┐
│                   监控体系总览                            │
│                                                         │
│  实时告警                         每日报告                │
│  ├─ 错误率 > 5% → 钉钉            ├─ 用户反馈统计         │
│  ├─ 延迟 P99 > 10s → 邮件         ├─ Bad Case 列表        │
│  └─ 成本 > 阈值 → 短信            └─ 质量趋势             │
│                                                         │
│  系统层             应用层                 业务层        │
│  ├─ GPU 利用率      ├─ Call trace        ├─ 忠实度       │
│  ├─ KV Cache 命中   ├─ Tool 成功率       ├─ 用户满意度   │
│  ├─ 模型延迟        ├─ Token 用量        └─ 拒绝率       │
│  └─ 队列深度        └─ Cost 追踪                        │
└─────────────────────────────────────────────────────────┘

总结

LLM 应用的可观测性是一个新领域,很多工具和实践还在快速演进中。但有几件事是确定的:

  1. Trace 是一切的基础——没有 trace,你无法理解 LLM 在对话中做了什么
  2. Eval 是持续的过程——不是上线前做一次就完事,而是随着数据积累持续改进
  3. Bad Case 驱动迭代——与其追求全面覆盖,不如从真实用户的 Bad Case 做起
  4. 安全观测不能省——LLM 应用的攻击面比传统 API 大得多

随着 LLM 应用越来越普及,我相信可观测性会成为 LLM 工程的标配,就像今天的 APM 一样。

继续阅读

评论