Skip to content

11.3.1 如何做 RAG 的日志、观测和故障排查? ​

先给结论:RAG 的日志和观测必须以链路为单位来设计,否则很难定位问题。最小可用的做法是:记录“输入 -> 检索 -> 重排 -> 上下文 -> 生成”的关键字段,并能把一次请求完整串起来。

很多系统的日志只有:

  • 请求时间
  • 最终回答

这对排查几乎没有帮助。真正有用的日志必须回答:

  • 这次请求的证据从哪里来
  • 哪一层决定了最终结果
  • 哪一层开始出现异常

一、日志必须“可串联” ​

RAG 是一条链路,日志也必须能串起来。最小要求是:

  • 每一次请求有唯一 trace_id
  • 每一层都有 trace_id 对应记录

没有这个,系统只能看到孤立的片段,看不到链路。

二、每一层至少记录什么 ​

下面是一套更稳的最小记录清单:

1. 请求与输入层 ​

  • 原始问题
  • 标准化后的 query
  • 解析出的意图或过滤条件

2. 检索层 ​

  • 检索策略和参数(如 top_k)
  • 命中文档或 chunk 列表
  • 命中结果的得分分布

3. 重排层 ​

  • 重排模型或策略版本
  • 重排后的排序变化
  • 最终保留结果列表

4. 上下文构造层 ​

  • 最终上下文长度
  • 是否发生截断
  • 是否发生去重或聚合

5. 生成层 ​

  • 模型与 prompt 版本
  • 是否触发拒答或安全策略
  • 生成耗时与 token 消耗

这些字段不是为了做大而全,而是为了让你在排查时能回答:

  • 证据是否进了候选
  • 证据是否进入最终上下文
  • 生成是否忠实使用证据

三、观测要分层,而不是只看总指标 ​

很多团队只看最终评分或满意度,这会掩盖链路问题。更稳的做法是:

  • 按链路层分指标
    检索指标、生成指标、端到端指标

  • 按错误桶分指标
    召回问题、忠实性问题、版本问题等

这样你能看到:

  • 哪一层开始退化
  • 哪一类问题在变多

四、一个可落地的排查流程 ​

当你拿到一个失败案例,最稳的排查流程是:

  1. 看检索结果中是否有关键证据
  2. 看重排后关键证据是否还在
  3. 看最终上下文是否被截断或稀释
  4. 看生成是否忠实使用证据

这能把大量问题迅速归因到具体链路层。

五、常见日志误区 ​

误区一:只记录最终答案 ​

没有链路信息,几乎无法归因。

误区二:没有版本信息 ​

没有版本,你无法判断:

  • 这是模型问题
  • 还是策略变化引入的问题

误区三:只看平均值 ​

平均值会掩盖:

  • P95 / P99 延迟
  • 某一类问题的集中退化

一个最小日志结构示意 ​

python
trace = {
    "trace_id": "req_123",
    "query_raw": "...",
    "query_norm": "...",
    "retrieval": {"top_k": 20, "hits": ["docA#3", "docB#1"]},
    "rerank": {"enabled": True, "final_hits": ["docB#1", "docA#3"]},
    "context": {"tokens": 4200, "truncated": False},
    "generation": {"model": "x", "tokens": 800, "abstained": False}
}

这个示意的重点是:

  • 能把一次请求完整串起来

一句话总结 ​

RAG 的日志和观测必须围绕链路来设计:可串联、可分层、可追溯。只有这样,故障排查和调优才会真正高效。