
上一篇文章里我们讨论了 RAG 如何降低成本。一个重要结论是缓存、摘要、分层检索和上下文控制能显著降低系统运行成本。但当 RAG 真正上线以后还有一个更现实的问题会很快出现出问题了怎么查RAG 的问题通常不会只发生在一个点上。它可能出在文档源 解析 清洗 切分 Embedding 召回 Rerank Prompt 生成 权限 缓存 多租户 多轮对话如果没有足够的日志和可观测性系统一旦答错就很难知道到底错在哪一步。所以这篇文章讨论一个企业 RAG 必须具备的能力RAG 上线后怎么排查问题为什么日志与可观测性这么重要。为什么 RAG 比普通接口更难排查普通接口出问题时通常比较好查。请求参数 业务分支 数据库查询 返回结果链路相对固定。但 RAG 不一样。一个用户问题可能经历一整条动态链路用户提问 ↓ query 改写 ↓ 多路检索 ↓ 候选合并 ↓ Rerank ↓ 上下文拼接 ↓ Prompt ↓ 模型生成而且如果是 Agentic RAG 或多轮 RAG链路还会更长。所以用户看到的只是最终答案。真正的问题可能藏在前面的某一步。比如检索根本没召回 召回了但排序错了 排序对了但上下文拼错了 Prompt 没约束住模型 模型看到的是过期资料 权限过滤漏了 缓存返回了旧答案没有日志就只能靠猜。而 RAG 不能靠猜定位问题。必须记录哪些信息RAG 日志不能只记“请求来了、答案出去了”。至少要记录这些关键字段request_id user_id tenant_id space_id query query_rewrite retrieval_filters retrieved_doc_ids retrieved_chunk_ids retrieved_scores rerank_results final_context prompt_version model_version answer citations latency error_code这些字段的作用各不相同。比如query用户到底问了什么 retrieval_filters检索时用了什么限制 retrieved_doc_ids到底召回了哪些文档 retrieved_scores召回分数如何 rerank_results排序是否合理 final_context最终喂给模型的内容是什么 prompt_version是不是 Prompt 版本引入了问题 citations答案依据来自哪里 latency慢在哪一步如果缺少其中一部分排查就会断层。所以 RAG 日志要从一开始就设计好而不是出问题后再补。日志要能还原整条链路一个好的 RAG 日志应该能帮助你回答这样的问题这个答案是怎么来的 为什么召回了这些文档 为什么没有召回更合适的文档 为什么 Rerank 把某条内容排在前面 为什么 Prompt 里出现了这些片段 为什么最终回答和资料不一致也就是说日志必须能还原链路。比如某次请求可以这样记录request_id: req_001 query: 试用期员工能不能休年假 tenant_id: tenant_a retrieval_filters: statusactive, departmenthr retrieved_doc_ids: doc_001, doc_002, doc_003 rerank_top: doc_002 final_context: chunk_002_1, chunk_002_3 prompt_version: v12 answer: 试用期员工可按入职时间折算年假 citation: doc_002如果答案错了你至少可以继续追查是文档内容错了 还是召回错了 还是 Rerank 错了 还是 Prompt 版本有问题 还是模型输出偏了没有这种可回放能力RAG 很难长期维护。延迟、成本、错误率都要看可观测性不只是日志。还要有指标。至少可以看这几类1. 延迟看每一步耗时query 改写耗时 检索耗时 Rerank 耗时 Prompt 拼接耗时 模型生成耗时 总耗时如果系统慢要能知道慢在哪一步。2. 成本看每个请求消耗了多少Embedding 次数 检索次数 Rerank 次数 token 消耗 缓存命中率3. 错误率看哪些环节失败最多解析失败 检索失败 Rerank 失败 模型调用失败 引用生成失败 权限过滤失败4. 质量看答案是不是稳定答案准确率 引用命中率 幻觉率 重复回答率 用户追问率 人工纠错率RAG 的可观测性最终要落到这些指标上。脱敏和隐私保护不能忘日志很重要但日志也可能变成新的风险源。如果你把所有 query、文档片段、答案全文都原样写入日志日志本身就可能泄露敏感信息。所以日志设计要注意敏感字段脱敏 原文片段最小化记录 必要时只记 hash 或 ID 分级权限查看日志 保留期可控比如用户问题可以记录但内部资料全文不要无控制写入日志。 引用可以记 doc_id 和 chunk_id不一定要把全文写进去。 错误日志要避免暴露租户和权限细节。日志的可用性和安全性要一起考虑。否则你解决了排障问题又引入了新的泄露问题。问题复盘链路要标准化RAG 出问题时最怕每次都靠临时猜。所以最好把排查流程标准化。可以按下面的顺序看1. 用户问题是否正确记录 2. 过滤条件是否正确 3. 召回内容是否正确 4. Rerank 是否合理 5. 最终上下文是否正确 6. Prompt 是否合适 7. 模型输出是否偏离 8. 引用是否对得上 9. 是否有缓存干扰 10. 是否存在版本和权限问题这个顺序很实用。因为它能帮你快速缩小问题范围。比如如果召回都不对问题在检索。 如果召回对但排序错问题在 Rerank。 如果上下文对但答案错问题在 Prompt 或模型。 如果只有某个租户错问题可能是多租户或权限。 如果只有缓存命中时错问题可能是缓存失效。有了标准化链路复盘就不再靠经验硬猜。观测面板应该看什么除了日志最好还有统一面板。比如面板上可以看到总请求量 平均延迟 P95 延迟 检索成功率 Rerank 成功率 缓存命中率 错误率 租户分布 热门 query 异常 query 高成本请求 低命中请求这样你能很快看出系统状态。比如某天延迟突然上升 某个租户错误率异常高 某类 query 命中率很低 某个知识空间的成本突然暴涨这些都是排障入口。没有观测面板问题只能等用户反馈。一个可落地的排查流程一个比较实用的 RAG 排障流程可以这样设计用户反馈问题 ↓ 定位 request_id ↓ 查看 query 和 tenant ↓ 检查检索过滤条件 ↓ 检查召回结果 ↓ 检查 Rerank ↓ 检查 Prompt ↓ 检查模型输出 ↓ 检查引用和版本 ↓ 检查缓存和权限 ↓ 形成复盘结论这样排查会更稳。关键是每一步都要能拿到数据。如果链路中某一步完全不可见就会很难定位。常见问题怎么从日志看出来几个典型问题可以这样判断检索没命中表现召回文档少 召回分数低 最终上下文很空可能原因文档没入库 chunk 切错 query 太短 Embedding 不合适 过滤条件太严Rerank 排错了表现召回结果看起来对但最终排在前面的不是最相关内容可能原因Rerank 模型不合适 候选太杂 重复内容太多 标题和正文权重不合理Prompt 没约束住表现资料是对的但回答自由发挥可能原因Prompt 版本不稳定 上下文太长 模型没有被要求引用缓存返回旧答案表现文档更新后还是旧说法可能原因缓存未失效 知识版本没更新 tenant 或 space 缓存 key 不完整权限串了表现某个用户看到了不该看的内容可能原因filter 漏配 元数据缺失 后置校验没做有了日志很多问题都能更快归因。落地建议第一尽早设计 request_id 和链路日志。第二日志要能还原 query、召回、Rerank、Prompt、答案和引用。第三监控延迟、成本、错误率和命中率。第四日志要脱敏避免二次泄露。第五问题排查流程要标准化不要每次靠猜。第六缓存、权限、多租户和版本信息都要进入日志。第七最好保留可以回放的最小证据链。第八真正上线前就要做观测面板而不是等出事再补。总结RAG 上线之后真正决定系统能不能长期运行的不只是效果还有可观测性。如果没有日志你看不见检索过程。如果没有指标你看不见成本和性能变化。如果没有审计你看不见权限和租户边界。所以企业级 RAG 必须把日志、指标、审计和回放机制一起设计进去。这样系统出问题时才能知道问题到底出在文档、切分、检索、排序、Prompt 还是模型生成。下一篇文章可以继续讨论从普通 RAG 到企业级 RAG 的完整架构复盘。因为系列快到收官了正好可以把前面的能力串成一套完整架构视角。