恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

RAG 可观测性实战:上线后必须能定位“为什么答错“(五)

  • 首页
  • 资讯中心
  • /
  • RAG 可观测性实战:上线后必须能定位“为什么答错“(五)

相关资讯

Transformer构建三维世界:从图片生成可探索场景的开源实践 2026/8/28 18:52:53
算法竞赛中范围覆盖与概率计算问题的建模与求解实战 2026/8/28 18:52:53
OctoLong:用跨仓库代码上下文增强代码大模型长上下文能力 2026/8/28 18:47:53

最新资讯

MarkItDown 上手指南:把 20 种文件格式一键转成 Markdown 喂给大模型
航拍水体污染检测数据集实战:YOLOv8训练与优化全流程
从训练到部署:Paddle DeepSpeech语音识别模型工业级落地实战
自我改进型Agent与事件溯源:从经验回放到策略进化
免疫算法(IA)原理与Matlab实现:从仿生机制到多峰优化实战
LangChain RAG 实战 | 稠密稀疏向量、Milvus 建库、增删检索数据

今日推荐

2026学术工具专业测评|Paperxie全维度性能实测报告[特殊字符]
凭什么稳居论文工具顶流[特殊字符]Paperxie综合实力深度全解析
2026论文工具深度测评|为什么Paperxie是目前最稳的学术工具✅

本周热门

Nextcloud 桌面客户端:把同步交给它,你只管改文件
如何将 HTML 转成 Word 文档且格式不丢失?html-to-docx 使用教程
Anki 批量操作卡片完整指南:一次搞定上千张,不再逐张修改

本月精选

如何用DamaiHelper实现演唱会门票的智能自动化抢购:完整技术解决方案指南
第4篇:59 倍性能差距的索引瓶颈定位——一次教科书级的全表扫描调优
终极歌词批量下载神器:5分钟解决离线音乐库歌词同步难题

RAG 可观测性实战:上线后必须能定位“为什么答错“(五)

发布时间:2026/8/28 18:52:53
RAG 可观测性实战:上线后必须能定位“为什么答错“(五) 目录前言1、为什么 RAG 比普通应用更需要可观测性2、全链路追踪六个节点一个都不能漏2.1 RAG 全链路的六个节点2.2 每个节点记录什么2.3 Trace 的代码实现3、结构化日志每个字段都有它的用途3.1 九个字段锁定问题根因3.2 结构化日志的写入3.3 日志分级4、监控指标六把尺子量系统健康度4.1 每个指标的深层含义4.1.1 延迟要看分布不看平均值4.1.2 Token 成本关注趋势而非绝对值4.1.3 检索命中率最需要基线的指标4.1.4 拒答率系统诚实的信号4.1.5 用户点赞 / 点踩率最真实的质量信号4.1.6 人工修正率最硬核的质量指标5、问题定位四类错误四种查法5.1 检索不到相关文档压根没被召回5.2 检索错了召回了但排序不对5.3 生成错了上下文有正确答案但模型没用5.4 幻觉模型编造了上下文中没有的信息6、生产最佳实践与工具链6.1 可观测性工具链6.2 上线检查清单一句话总结前言RAG 系统上线只是开始。Demo 阶段能不能回答很容易验证但生产环境里每天都会出现答错、答偏、答不出来的 case而 RAG 是一条多阶段流水线——查询改写错了检索没召回重排序把对的排下去了上下文组装丢了关键片段还是 LLM 生成阶段幻觉了没有可观测性工程师面对用户投诉只能反复盲猜 prompt有了可观测性任何一个 bad case 都能在十分钟内定位到具体环节。这就是 RAG 运营和 RAG 玩具的分水岭。RAG 系统上线后不能只看能不能回答还要能定位为什么答错。可观测性不是日志打得越多越好而是围绕归因来设计每一层留下足够的证据让错误可以逐环节排除。1、为什么 RAG 比普通应用更需要可观测性普通的 API 服务出了问题通常很明显500 报错、延迟飙升、内存溢出。但 RAG 系统出问题的方式非常隐蔽——它不会报错不会崩溃它只是答得不够好。而且这个不够好可能发生在链路的任何一个环节查询理解错了— 用户问上季度的利润增长Query 改写把增长理解成了增长率检索没召回到— 相关文档存在但 Embedding 相似度不够排在了 Top-K 之外召回对了但排序错了— 最相关的文档排在第 15 位你的窗口只取了 Top 10Reranker 把好文档排下去了— Cross-Encoder 对某些 Query 的判断不如向量检索准上下文组装截断了— 关键信息在文档末尾被 Token 限制截掉了模型生成跑偏了— 上下文有正确答案但模型选择性失明编了一个不同的核心痛点上述六个环节每一个都可能独立出错也可能组合出错。如果没有全链路追踪你只能看到最终回答不好但根本不知道问题出在哪一步。就像一个黑盒——输入和输出你都能看到但中间发生了什么完全是盲区。更麻烦的是RAG 系统的问题往往是渐进式退化而非突发式崩溃。文档库增长导致检索精度慢慢下降新文档质量参差不齐拉低了整体效果模型 API 的行为随着版本更新悄悄漂移——这些都不是今天突然坏了而是不知不觉变差了。没有基线对比和趋势监控你甚至意识不到问题在发生。2、全链路追踪六个节点一个都不能漏全链路追踪的核心思想给每个请求分配一个唯一trace_id从用户输入到最终输出经过的每一个环节都记录在这个 trace 上。出了问题拿着trace_id一查全链路每一步的状态一目了然。2.1 RAG 全链路的六个节点① 用户输入 (query) ↓ ② 查询改写结果 (rewritten_query) ← 改写/扩展/多轮改写/HyDE ↓ ③ 检索结果 (retrieved_chunks) ← 向量检索 关键词检索 ↓ ④ 重排序结果 (reranked_chunks) ← 顺序变了必须记 ↓ ⑤ 最终上下文 (final_context) ← 截断/去重/组装后的真实输入 ↓ ⑥ LLM 输出 (completion) ← 含引用、拒答与否为什么记查询改写多轮对话场景下改写是错误重灾区。它多少钱被改写成错误指代后面全链路跟着错。改写环节的输入原始 query 对话历史和输出都要留。为什么检索和重排序分开记这是归因的关键分界。正确的 chunk 在检索结果里、但被重排序挤出了 top-k——问题在 rerank检索结果里根本没有——问题在召回。两个阶段各记chunk_id 列表 分数一比就知道错在哪一跳。为什么记最终上下文而不是只记检索结果检索到 ≠ 进了 prompt。截断策略、去重、token 预算都可能把正确答案在组装阶段丢掉。第⑤环节记录的是模型真实看到的东西这是审 case 时的 ground truth。工程要点trace 全量落库成本高常见策略是采样 全量留存 bad case正常请求按 1–5% 采样但用户点踩、拒答、低置信度的请求100% 留全量 trace——因为你要排查的恰恰就是这些。工 Existing 上可直接用 OpenTelemetry 的 span 模型或 LangSmith / Langfuse / Phoenix 这类 LLM 观测平台的 trace 能力不必自研全部。每个节点必须记录输入是什么、输出是什么、耗时多少、是否成功。这样出了问题你能精确定位到具体哪一步。2.2 每个节点记录什么节点记录输入记录输出关键指标用户输入原始 Query——查询改写原始 Query改写后 Query可能多个改写是否改变语义、耗时检索改写后 Query召回的 Chunk 列表 相似度分数召回数量、Top-1 分数、分数分布重排序召回的 Chunk 列表重排后的 Chunk 列表 新分数排序变化哪些被提上来了/打下去了上下文组装重排后 Top-K Chunks最终 Prompt含截断信息Token 数、是否截断、截断位置LLM 生成最终 PromptCompletion 文本延迟、Token 消耗、是否触发了安全策略2.3 Trace 的代码实现import uuid from dataclasses import dataclass, field from typing import Any import time dataclass class RAGTrace: trace_id: str field(default_factorylambda: str(uuid.uuid4())) timestamp: float field(default_factorytime.time) # 六个节点的完整记录 raw_query: str rewritten_query: str retrieved_chunks: list[dict] field(default_factorylist) # [{text, score, doc_id, chunk_id}] reranked_chunks: list[dict] field(default_factorylist) final_context: str # 实际送给 LLM 的 Prompt completion: str # 每步耗时 rewrite_latency_ms: float 0 retrieval_latency_ms: float 0 rerank_latency_ms: float 0 llm_latency_ms: float 0 total_latency_ms: float 0 # 成本 input_tokens: int 0 output_tokens: int 0 estimated_cost: float 0.0 # 用户反馈 user_feedback: str | None None # up / down / None def to_dict(self) - dict: return { trace_id: self.trace_id, timestamp: self.timestamp, raw_query: self.raw_query, rewritten_query: self.rewritten_query, retrieved_chunks: [ {doc_id: c[doc_id], score: c[score], text_preview: c[text][:100]} for c in self.retrieved_chunks ], reranked_chunks: [ {doc_id: c[doc_id], score: c[score]} for c in self.reranked_chunks ], final_context_tokens: len(self.final_context) // 4, # 粗估 completion: self.completion[:200], latency: { rewrite: self.rewrite_latency_ms, retrieval: self.retrieval_latency_ms, rerank: self.rerank_latency_ms, llm: self.llm_latency_ms, total: self.total_latency_ms, }, tokens: {input: self.input_tokens, output: self.output_tokens}, cost: self.estimated_cost, user_feedback: self.user_feedback, } # 在 RAG 管道中使用 def rag_pipeline(query: str) - str: trace RAGTrace(raw_queryquery) # Step 1: 查询改写 t0 time.time() trace.rewritten_query rewrite_query(query) trace.rewrite_latency_ms (time.time() - t0) * 1000 # Step 2: 检索 t0 time.time() trace.retrieved_chunks vector_search(trace.rewritten_query, top_k20) trace.retrieval_latency_ms (time.time() - t0) * 1000 # Step 3: 重排序 t0 time.time() trace.reranked_chunks rerank(trace.retrieved_chunks, query, top_k5) trace.rerank_latency_ms (time.time() - t0) * 1000 # Step 4: 组装上下文 trace.final_context build_prompt(query, trace.reranked_chunks) # Step 5: LLM 生成 t0 time.time() response llm.invoke(trace.final_context) trace.llm_latency_ms (time.time() - t0) * 1000 trace.completion response.text trace.input_tokens response.usage.input_tokens trace.output_tokens response.usage.output_tokens # 记录完整 Trace log_trace(trace.to_dict()) return response.text关键设计点retrieved_chunks和reranked_chunks都要记录完整的 score 和 doc_id。因为排查问题时你首先看的就是相关文档有没有被召回和召回后排序对不对——如果只存了最终 Prompt这些中间信息就丢了。3、结构化日志每个字段都有它的用途全链路追踪告诉你每一步做了什么结构化日志告诉你每一步的详细数据是什么。日志必须结构化JSON不能是纯文本——因为你要按字段查询、聚合、统计。3.1 九个字段锁定问题根因字段类型记录什么排查时怎么用querystring用户原始输入判断问题是不是出在输入本身太模糊、太短rewritten_querystring改写后的查询对比原始 Query看改写有没有改变语义retrieved_chunksarray召回的文档片段doc_id score text检查相关文档有没有被召回、分数分布是否合理scoresarray检索分数和重排分数的对比看 Reranker 有没有把好文档排下去promptstring最终送给 LLM 的完整 Prompt看上下文有没有截断、格式对不对completionstringLLM 返回的回答对比上下文看模型有没有无视正确信息latencyobject各环节耗时ms定位延迟瓶颈在哪个环节token_usageobject输入 token、输出 token成本分析和预算监控user_feedbackstring用户点赞 / 点踩 / 修正标注数据质量用于离线分析和模型优化高价值技巧user_feedback 闭环user_feedback不只是统计指标——点踩的请求自动关联 trace 落入 bad case 池定期人工归因后沉淀为回归评测集。这样线上问题会持续转化为测试资产每次调整分块策略、换 embedding 模型、改 prompt 时跑一遍防止修好一个坏三个。这是 RAG 系统能持续进化的关键机制。隐私与合规提醒日志里的 query、chunks、completion 可能含敏感内容落库前要过脱敏策略并设置保留期限如 30–90 天trace 采样与全量留存的边界也要过安全评审。3.2 结构化日志的写入import json import logging # 结构化 Logger每条日志都是一个 JSON 对象 logger logging.getLogger(rag) def log_trace(trace_data: dict): # 写入结构化日志支持后续 ELK / Loki 检索 logger.info(json.dumps(trace_data, ensure_asciiFalse)) # 同时写入 trace 存储如 ClickHouse / Postgres # 便于做聚合查询和趋势分析 trace_store.insert(trace_data) # 查询示例找出所有用户点踩的请求 # SELECT trace_id, raw_query, completion, user_feedback # FROM rag_traces WHERE user_feedback down ORDER BY timestamp DESC LIMIT 50;日志存储的坑retrieved_chunks和prompt字段可能很大每个 chunk 几百字20 个 chunk 加 Prompt 轻松超过 10KB。如果用 Elasticsearch 存全量成本会很高。建议chunk 内容只存前 100 字 preview完整内容存对象存储S3 / OSS日志里只存 URL 指针。3.3 日志分级不是所有日志都要全量记录。按场景分级级别记录频率记录内容存储位置全量 Trace每个请求九字段完整记录ClickHouse / Postgres保留 30 天异常 Trace出错时全量 堆栈 环境信息Elasticsearch / Loki长期保留采样 Trace1% 正常请求全量含完整 Prompt 和 Completion对象存储用于离线分析聚合指标每分钟QPS、平均延迟、成功率Prometheus / Grafana4、监控指标六把尺子量系统健康度日志是事后排查用的指标是实时监控用的。六个核心指标覆盖 RAG 系统的延迟、成本、质量三个维度。4.1 每个指标的深层含义4.1.1 延迟要看分布不看平均值延迟必须看分位数P50 / P95 / P99不看平均值。RAG 系统的延迟长尾很重——90% 的请求 2 秒返回10% 因为检索了更多文档或触发了重试要 15 秒。平均值看起来 3.5 秒还行但那 10% 的用户体验已经崩了。# 分段延迟监控每个环节独立追踪 latency_breakdown { rewrite: {p50: 480, p99: 1200}, # ms retrieval: {p50: 85, p99: 320}, rerank: {p50: 210, p99: 580}, llm_first_token: {p50: 850, p99: 3200}, llm_total: {p50: 1800, p99: 6500}, end_to_end: {p50: 2625, p99: 8600}, } # 上面可以看出瓶颈在 LLM 生成阶段尤其是 P99 # 重排 P99 580ms 也有优化空间考虑换更快的 Reranker4.1.2 Token 成本关注趋势而非绝对值单日成本的绝对值意义不大取决于流量但成本趋势非常重要。如果日均单请求成本从 0.03 元涨到 0.05 元可能是因为检索召回的文档变长了分块策略变了或文档更新了上下文组装逻辑有 Bug塞了多余的 Chunk模型版本更新Token 计数方式变了Query 改写生成了更多变体导致多路检索4.1.3 检索命中率最需要基线的指标检索命中率RecallK需要一个标注数据集作为基线——人工标注 100-500 个 Query 对应的正确文档定期跑一遍看正确文档是否出现在 Top-K 内。如果命中率突然下降第一时间检查有没有新文档入库拉低了相似度分布Embedding 模型有没有被意外更换向量索引有没有被重建重建时可能参数变了4.1.4 拒答率系统诚实的信号拒答率不是越低越好。一个从不拒答的系统要么是检索太强什么都能找到要么是模型在硬编答案。适当的拒答率5-10%说明系统的忠实度约束在工作——该说不知道的时候说了。警惕拒答率突升如果拒答率突然从 8% 涨到 25%通常不是系统变诚实了而是检索出了问题——文档没召回导致系统频繁说找不到相关信息。先查检索层再查 Prompt 层。4.1.5 用户点赞 / 点踩率最真实的质量信号这是唯一来自用户的主观质量指标。但要注意采样偏差——会主动点踩的用户通常是极度不满的满意的用户不一定点赞。所以绝对值不重要趋势变化才重要。如果点踩率从 8% 涨到 15%系统一定出了问题。4.1.6 人工修正率最硬核的质量指标如果你的系统有用户编辑答案后重新提交的功能修正率是最硬核的质量指标——它直接量化了AI 回答离用户满意有多远。修正率高说明回答方向对但细节不够修正率低但点踩率高说明回答方向就错了。5、问题定位四类错误四种查法有了全链路追踪和结构化日志问题定位就有章可循。用户反馈回答不好时按以下四条路径逐一排查5.1 检索不到相关文档压根没被召回症状系统说找不到相关信息或答非所问。但你知道知识库里明明有相关文档。排查清单查分块策略— 是不是 chunk 太小导致相关段落被切散了或者 chunk 太大相关内容被稀释了查 Embedding— 换个 Embedding 模型试试。中文场景下 BGE 和 m3e 的表现差异很大查索引— 向量索引有没有被正确构建HNSW 的ef_search参数是否太小查过滤条件— 元数据过滤是否把相关文档排除了比如时间范围、权限过滤查 Query 改写— 改写后 Query 的语义是否偏了原 Query 明明是对的改写后反而检索不到# 快速诊断用原始 Query 直接检索对比改写后 Query 的结果 results_original vector_search(raw_query, top_k20) results_rewritten vector_search(rewritten_query, top_k20) # 如果原始 Query 能召回但改写后不能 → 改写出问题了 # 如果都召不回 → 检索层/索引/分块出问题了 # 如果能召回但分数很低 → Embedding 模型对该类 Query 效果差5.2 检索错了召回了但排序不对症状相关文档被召回了但排在第 15 名你的 Top-K10 取不到它。排查清单查相似度分数分布— Top-1 到 Top-20 的分数差距是否很小如果是说明 Embedding 区分度不够查混合检索权重— 如果用了 BM25 向量混合检索权重配比对排序影响很大查重排序— Reranker 是否把相关文档排下去了对比 rerank 前后的排序变化查 Top-K 设置— K 是否太小考虑增大 K 但加 Reranker 筛选# 对比 Reranker 前后排序变化 for i, chunk in enumerate(trace.retrieved_chunks[:10]): rerank_pos next( (j for j, rc in enumerate(trace.reranked_chunks) if rc[doc_id] chunk[doc_id]), None ) if rerank_pos is not None and rerank_pos i: print(f⚠️ doc {chunk[doc_id]} 从第{i1}名被排到第{rerank_pos1}名)5.3 生成错了上下文有正确答案但模型没用症状最终上下文里明明包含了正确信息但 LLM 的回答完全无视了它。排查清单查 Prompt— System Prompt 有没有明确要求基于上下文回答上下文的格式是否清晰查上下文位置— 正确信息在上下文的什么位置如果在中间可能是 Lost in the Middle查 Token 截断— 上下文是否被截断正确信息可能在被截掉的部分查模型能力— 换一个更强的模型试试。某些小模型对长上下文的利用能力确实差查指令冲突— System Prompt 中的指令是否互相矛盾比如既要求简洁又要求详细# 检查正确信息在上下文中的位置 correct_info 利润增长15% # 已知正确答案关键词 position trace.final_context.find(correct_info) total_len len(trace.final_context) if position -1: print(❌ 正确信息不在最终上下文中 → 检索/截断问题) elif position total_len * 0.3: print(✅ 在开头模型应该能注意到) elif position total_len * 0.7: print(⚠️ 在末尾可能被忽略) else: print(⚠️ 在中间Lost in the Middle 风险)5.4 幻觉模型编造了上下文中没有的信息症状回答看起来很合理、很自信但内容是编的。上下文中根本没有这个信息。排查清单查忠实度约束— System Prompt 有没有只能基于以下信息回答的硬约束查拒答策略— 上下文不包含答案时系统是否被指示说我不知道还是默认尽力回答查上下文质量— 上下文是否包含误导性内容模型可能被低质量 Chunk 带偏查温度设置— Temperature 是否过高降到 0-0.3 减少创造性查引用溯源— 是否要求模型标注信息来源没有溯源的答案很难判断真假# 幻觉检测答案中的事实是否都能在上下文中找到 def detect_hallucination(completion: str, context: str) - list[str]: 提取答案中的数字/日期/名称等事实 检查是否在上下文中出现 import re # 提取数字、百分比、日期等关键事实 facts re.findall(r\d\.?\d*%?|\d{4}年|\d{1,2}月\d{1,2}日, completion) missing [] for fact in facts: if fact not in context: missing.append(fact) return missing # 返回上下文中找不到的事实 # 如果 missing 非空说明答案包含上下文中没有的数据 → 幻觉排查流程总结拿到一个回答不好的 case按顺序走四条路径先查检索有没有召回 → 再查排序对不对 → 再查模型有没有用上下文 → 最后查有没有幻觉。每一步都用trace_id拉出全链路数据对照排查清单逐项检查。90% 的问题都能在 10 分钟内定位。6、生产最佳实践与工具链6.1 可观测性工具链追踪层OpenTelemetry / LangSmith / Langfuse — 负责全链路 Trace 的采集、存储、可视化。LangSmith 和 Langfuse 专门为 LLM 应用设计原生支持 Prompt / Completion / Token 等字段的展示。日志层ELKElasticsearch Logstash Kibana或 Grafana Loki — 存储结构化日志支持按trace_id、user_feedback、latency等字段检索和聚合。指标层Prometheus Grafana — 实时监控延迟分布、Token 成本、检索命中率等指标。设置告警阈值如 P99 10s 或 成本日环比 20% 触发告警。评估层Ragas / TruLens — 离线评估 RAG 质量的框架。定期在标注数据集上跑评估生成 Faithfulness忠实度、Answer Relevancy答案相关性、Context Precision上下文精度等指标作为系统质量的基线。6.2 上线检查清单每个请求都有唯一trace_id贯穿全链路所有日志六个节点输入→改写→检索→重排→上下文→生成的输入输出都被记录检索结果保存完整 score 和 doc_id不只是 text延迟按环节分段记录至少有 P50 和 P99Token 用量和成本每次请求都记录有用户反馈通道点赞/点踩/修正反馈绑定到 trace有标注数据集做定期基线评估至少 100 个 Query关键指标有告警阈值延迟、成本、成功率日志有保留策略全量 30 天采样长期保留有问题 case 归档机制点踩的 case 自动进入待分析队列一句话总结RAG 的可观测性 全链路留痕能查 结构化日志有据 指标大盘能盯 故障归因树会判 用户反馈闭环能进化。RAG 可观测性的核心不是装一堆监控工具而是建立一条从用户反馈到问题根因的完整链路用户说答错了 → 拿 trace_id 拉全链路数据 → 看是检索没召回、排序不对、上下文截断、还是模型幻觉 → 针对性修复。没有这条链路你永远在猜问题出在哪有了这条链路10 分钟定位根因。RAG文章RAG 深度解析从原理到工程实践一RAG 检索深度指南从 Embedding 到 Query 处理二RAG 进阶双引擎重排序精准化与生成可控化的工程实践三RAG 进阶之路Agent 自主决策与评估体系四

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号