RAG 回答异常怎么查:串起切片、检索与模型调用
RAG 回答异常怎么查串起切片、检索与模型调用RAG 回答异常时先把切片、检索结果、上下文组装和模型调用串起来。每段保存脱敏摘要与耗时就能看出内容在哪一步偏离而不是把错误都归给模型。向量检索与上下文长度的常见边界搭建 RAG 系统时开发者很容易沉迷于 Baseline 演示的顺利表现从而忽视了生产环境下的复杂边缘条件。常见问题通常来自两个相互叠加的环节第一切片粒度与向量相似度阈值配置失当。分块Chunking策略过于粗暴导致上下文缺乏关键语义关联。相似度匹配算法将高噪音的文档片段强行送入上下文窗口直接引发了大模型的理解偏差。第二缺乏 Prompt Token 溢出截断保护。当向量数据库返回了 5 个长文本片段后编排逻辑直接将它们拼接进 Prompt 中超出了上游模型 API 的最大输入限制。而上游 API 没有返回明确的错误码直接进入无限挂起状态直至网关层抛出 HTTP 504。------------------------------------------------------------------------- | RAG 线上常见故障诱因与现象对照 | ------------------------------------------------------------------------- | 故障环节 | 根因分析 | 前端外部表现 | ----------------------------------------------------------------------- | 向量化阶段 | Embedding 模型离线或超时 | 检索为空回答与背景无关 | | 检索匹配阶段 | Top-K 过大且 Threshold 设得太低 | 产生严重幻觉答非所问 | | 上下文组装 | 未计算 Token 边界致 Prompt 溢出| 服务挂起返回 504 超时 | | 模型推理阶段 | API 速率受限 (429 Rate Limit) | 界面长时间停滞或报错500 | -----------------------------------------------------------------------线上故障定位的核心证据链拓扑证据链可以从入口分配Trace-ID再记录 Chunk 得分、Token 用量和各阶段耗时。日志应脱敏并设置采样与留存期限Trace 只能帮助还原调用链不能替代责任判断。Python 可部署的 RAG 链路追踪与全链路容错断路器以下代码展示 RAG API 编排器的几个边界用 Trace ID 关联日志、重排 Top-K Chunk、限制 Token 输入并在外部 API 失败时触发熔断。每条分支仍需用超时、空结果和依赖故障测试验证。import uuid import time import logging from typing import List, Dict, Any, Optional logging.basicConfig(levellogging.INFO, format[%(asctime)s][%(levelname)s][Trace: %(threadName)s] %(message)s) logger logging.getLogger(RAGPipeline) class VectorStoreUnavailableError(Exception): 向量数据库不可用自定义异常 pass class LLMAPIError(Exception): 上游 LLM 调用异常 pass class SafeRAGPipeline: def __init__(self, similarity_threshold: float 0.72, max_prompt_tokens: int 1500): self.similarity_threshold similarity_threshold self.max_prompt_tokens max_prompt_tokens def mock_vector_search(self, query: str, trace_id: str) - List[Dict[str, Any]]: 模拟向量数据库检索带异常保护 logger.info(f[{trace_id}] 开始向量数据库检索, Query: {query}) # 模拟检索耗时 time.sleep(0.15) # 模拟边缘场景查询触发特殊条件抛出超时异常 if timeout_trigger in query: raise VectorStoreUnavailableError(Vector DB index search timed out (Connection limit reached)) return [ {chunk_id: c101, text: 爷爷在1975年参加了铁路线建设负责日常养路与调度工作。, score: 0.89}, {chunk_id: c102, text: 修筑铁路期间工人们常常在篝火旁哼唱地方民歌。, score: 0.75}, {chunk_id: c103, text: 现代电力机车采用双向供电系统额定电压为25kV。, score: 0.42} # 低相关度噪音 ] def truncate_context_by_tokens(self, chunks: List[Dict[str, Any]], max_tokens: int, trace_id: str) - str: 根据 Token 估算安全限制裁剪上下文 accumulated_text current_tokens 0 for c in chunks: # 粗略计算1 汉字 ≈ 1.5 Tokens estimated_tokens int(len(c[text]) * 1.5) if current_tokens estimated_tokens max_tokens: logger.warning(f[{trace_id}] 上下文触发安全截断! 已忽略后续 Chunk: {c[chunk_id]}) break accumulated_text f\n[参考文档]: {c[text]} current_tokens estimated_tokens return accumulated_text def call_llm_with_circuit_breaker(self, prompt: str, trace_id: str) - str: 带熔断机制的 LLM 调用 logger.info(f[{trace_id}] 请求上游 LLM, Prompt 字符数: {len(prompt)}) # 模拟 API 失败率机制 if len(prompt) 4000: raise LLMAPIError(HTTP 400: Context length exceeded threshold limit) time.sleep(0.4) return 基于记录您的爷爷曾在1975年参与铁路线养护工作那些岁月留下了踏实而温暖的足迹。 def process_query(self, user_query: str) - Dict[str, Any]: trace_id ftr-{uuid.uuid4().hex[:8]} start_timestamp time.time() evidence_chain {trace_id: trace_id, query: user_query, steps: []} try: # 1. 向量检索阶段 raw_chunks self.mock_vector_search(user_query, trace_id) evidence_chain[steps].append({step: VECTOR_SEARCH, raw_chunks_count: len(raw_chunks)}) # 2. 过滤低相关度 Chunk filtered_chunks [c for c in raw_chunks if c[score] self.similarity_threshold] logger.info(f[{trace_id}] 过滤后有效 Chunk 数: {len(filtered_chunks)}) evidence_chain[steps].append({step: SCORE_FILTER, valid_chunks: len(filtered_chunks)}) if not filtered_chunks: logger.warning(f[{trace_id}] 未找到高可信度匹配项降级为温情预置提示) return { trace_id: trace_id, answer: 抱歉关于这段回忆暂未检索到确切的文字记载。但或许您可以重新描述一下那个温暖的故事, status: FALLBACK_NO_MATCH } # 3. 组装并裁剪上下文 safe_context self.truncate_context_by_tokens(filtered_chunks, self.max_prompt_tokens, trace_id) final_prompt f上下文资料{safe_context}\n\n问题{user_query}\n请用亲切温暖的语气回答。 # 4. LLM 生成 answer self.call_llm_with_circuit_breaker(final_prompt, trace_id) total_duration round((time.time() - start_timestamp) * 1000, 2) evidence_chain[steps].append({step: LLM_SUCCESS, duration_ms: total_duration}) return { trace_id: trace_id, answer: answer, status: SUCCESS, evidence: evidence_chain } except VectorStoreUnavailableError as ve: logger.error(f[{trace_id}] 向量库服务不可用: {str(ve)}) return { trace_id: trace_id, answer: 回忆档案库正处于定期维护中请稍后再试。, status: DEGRADED_VECTOR_ERROR } except LLMAPIError as le: logger.error(f[{trace_id}] 大模型 API 异常: {str(le)}) return { trace_id: trace_id, answer: 智能助手思绪有些紊乱稍等片刻它就会恢复正常。, status: DEGRADED_LLM_ERROR } except Exception as e: logger.critical(f[{trace_id}] 未知运行时崩溃: {str(e)}, exc_infoTrue) return { trace_id: trace_id, answer: 系统遇到了未预期的扰动。, status: SYSTEM_CRITICAL_FAILURE } # 验证故障捕获逻辑 if __name__ __main__: pipeline SafeRAGPipeline(similarity_threshold0.7) print(\n 测试场景 A: 正常检索流程 ) res_a pipeline.process_query(讲讲爷爷修铁路的事) print(f回答结果: {res_a[answer]}\n状态: {res_a[status]}) print(\n 测试场景 B: 向量库检索超时降级 ) res_b pipeline.process_query(timeout_trigger 相关的历史) print(f回答结果: {res_b[answer]}\n状态: {res_b[status]})用证据链支持故障排查大模型应用除了正常路径还要覆盖空检索、模型超时和上下文超限。每条失败路径都应有可观察状态和明确回退。Trace ID 应贯穿检索与模型调用相似度阈值和超时则从评测集与服务目标中得出。日志能还原输入、候选片段和调用状态后RAG 问题才有稳定的排查入口。