011、优化篇:解决幻觉、提升相关性、降低延迟的实战技巧

张开发
2026/5/3 18:47:08 15 分钟阅读
011、优化篇:解决幻觉、提升相关性、降低延迟的实战技巧
011、优化篇解决幻觉、提升相关性、降低延迟的实战技巧一、从一次深夜告警说起上周三凌晨两点手机突然震个不停。运维告警线上RAG问答接口响应时间飙到8秒同时客服反馈用户投诉答案“胡言乱语”——问新款手机参数系统居然回答“该机型支持水下摄影电池可拆卸”。明显两个问题延迟爆炸幻觉严重。抓起电脑连上VPN查日志看到触发了冷门技术文档的向量检索召回了一堆2005年的老型号说明书。这就是典型工业场景的RAG痛点延迟、相关性、幻觉三个问题往往同时爆发。今晚就聊聊我们趟过的坑和压箱底的实战技巧。二、先治幻觉给AI加上“刹车系统”幻觉的本质是模型对检索内容信任不足开始自由发挥。我们试过三种方案方案A强化指令模板# 旧版提示词问题太温和promptf请根据以下文档回答问题{context}\n问题{query}# 新版提示词关键明确边界promptf 你只能使用以下文档内容回答问题。如果文档中没有相关信息必须明确说“根据提供资料无法回答”。 严禁添加文档外的知识。 文档内容{context}问题{query}回答格式 1. 相关依据[引用文档原句] 2. 答案[基于依据的总结] 这里踩过坑早期用“请尽量参考文档”这种模糊表述幻觉率高达30%。后来改成“只能使用”“严禁添加”等强约束词幻觉率降到12%。方案B元数据过滤链defstrict_evidence_filter(chunks,query_keywords): 别直接扔所有chunk给LLM先做一轮筛选 filtered[]forchunkinchunks:# 检查时间有效性针对技术文档ifchunk.metadata.get(year,2024)2020:continue# 丢弃过期文档# 检查来源权威性ifchunk.metadata.get(doc_type)user_comment:continue# 跳过用户评论# 简单关键词重叠检查ifnotany(kwinchunk.textforkwinquery_keywords):chunk.score*0.7# 相关性降权filtered.append(chunk)returnfiltered[:5]# 硬截断最多5段这个过滤器帮我们拦掉了60%的垃圾召回。经验是向量检索前做粗筛检索后做精筛两层过滤比单纯调阈值管用。方案C置信度打分我们给每个回答加了个置信度字段defcheck_hallucination(answer,source_chunks): 简单但有效的自查答案中的关键实体是否在原文出现 answer_entitiesextract_entities(answer)# 抽取出型号、参数等source_text .join([c.textforcinsource_chunks])missing_entities[]forentityinanswer_entities:ifentitynotinsource_text:missing_entities.append(entity)confidence1-len(missing_entities)/max(len(answer_entities),1)ifconfidence0.6:return⚠️ 检测到潜在幻觉建议人工复核线上跑下来置信度低于0.7的回答里85%确实存在幻觉。现在前端会对低置信回答展示提示用户体验反而更好了。三、提升相关性不只是向量检索的事相关性差往往不是embedding模型不行而是检索链路设计有缺陷。技巧1查询重写用户问“手机卡怎么办”直接检索基本废了。我们加了个轻量级重写# 别用复杂LLM简单规则小模型足够defrewrite_query(query):# 同义词扩展synonym_map{卡:[卡顿,反应慢,延迟高],发热:[发烫,温度高]}# 补充上下文从对话历史提取if手机inqueryand性能notinquery:query 性能 运行速度returnquery[:100]# 防止查询过长成本几乎为零但检索召回率提升了18%。关键点扩展要适度过度扩展会引入噪声。技巧2混合检索策略defhybrid_retrieve(query,top_k10):# 并行三路召回vector_resultsvector_search(query,top_ktop_k*2)keyword_resultskeyword_search(query,top_ktop_k)bm25_resultsbm25_search(query,top_ktop_k)# 融合排序简单加权all_results[]seen_idsset()# 第一梯队向量关键词都命中的forvecinvector_results:ifvec.idin[kw.idforkwinkeyword_results]:vec.score*1.5ifvec.idnotinseen_ids:all_results.append(vec)seen_ids.add(vec.id)# 第二梯队向量检索结果forvecinvector_results:ifvec.idnotinseen_ids:all_results.append(vec)seen_ids.add(vec.id)# 保底BM25结果防止向量全崩forbminbm25_results:ifbm.idnotinseen_idsandlen(all_results)top_k:all_results.append(bm)returnall_results[:top_k]这套组合拳下来相关片段召回率从71%提到89%。特别在专业术语多的领域纯向量检索容易翻车。技巧3分块策略优化# 坏实践固定256字符分块# 好实践按语义分块defsemantic_chunk(text,min_size200,max_size500): 按段落、标题、句子边界分块 chunks[]# 优先按标题分if## intext:sectionstext.split(## )forsecinsections:iflen(sec)min_size:chunks.append(sec[:max_size])else:# 按句子分保留完整句子sentencestext.split(。)current_chunkforsentinsentences:iflen(current_chunk)len(sent)max_size:ifcurrent_chunk:chunks.append(current_chunk)current_chunksentelse:current_chunksent。returnchunks测试发现按语义分块比固定长度分块在问答任务上准确率高14%。代价是存储量增加但值得。四、压延迟从秒级到毫秒级的战争8秒延迟的根因分析串行处理、重复计算、无效传输。优化1预计算缓存classRetrievalCache:def__init__(self):# 两级缓存内存Redisself.memory_cacheLRUCache(maxsize10000)self.redis_clientget_redis_client()asyncdefget_embeddings(self,text):# 先查内存cache_keyfemb_{hash(text)}ifcached:self.memory_cache.get(cache_key):returncached# 再查Redisifcached:awaitself.redis_client.get(cache_key):self.memory_cache.set(cache_key,cached)returncached# 计算并缓存embawaitcompute_embedding(text)self.memory_cache.set(cache_key,emb)awaitself.redis_client.setex(cache_key,3600,emb)# 1小时过期returnemb高频查询的embedding计算量降了90%。注意缓存键要包含模型版本避免模型升级后读到旧向量。优化2异步流水线asyncdefparallel_rag_pipeline(query):# 并行执行查询重写、向量检索、关键词检索rewritten_query,vector_search_task,keyword_search_taskawaitasyncio.gather(rewrite_query_async(query),vector_search_async(query),keyword_search_async(query))# 融合结果resultsmerge_results(awaitvector_search_task,awaitkeyword_search_task)# 与LLM生成并行检索时就开始准备LLM上下文llm_contextprepare_llm_context(results[:3])# 先传前3个# 流式输出asyncforchunkinllm_stream_generate(llm_context,query):yieldchunk# 后台继续处理剩余结果用于后续追问asyncio.create_task(process_remaining_results(results[3:]))从串行改并行后p95延迟从3.2秒降到1.1秒。核心思想能并行的绝不等待。优化3硬件级优化向量检索用FAISS IVF_PQ索引内存占用降为1/4检索速度提升5倍GPU推理时开连续批处理continuous batching吞吐量翻倍用Triton Inference Server替代原生PyTorch首token时间减少40%五、个人经验包幻觉不是洪水猛兽而是可量化的技术指标。我们设了幻觉率看板每周review高于5%就要排查。相关性优化有个甜蜜点召回片段数在5-7之间时准确率和延迟平衡最好。太多反而干扰LLM判断。延迟优化要分场景C端问答压到1秒内内部知识库可以放宽到3秒。别为了100毫秒加三台服务器。测试集别用公开数据集自己攒真实用户query。我们发现有30%的query是“帮我看看这个错误代码”“截图中那段文字什么意思”公开数据集根本不覆盖。简单方法优先先试查询扩展、缓存、分块优化这些低成本方案再考虑换模型、加硬件。我们曾花两周优化embedding模型效果不如花两天改分块策略。留个降级开关当检索系统全挂时能切到纯LLM模式虽然可能幻觉多。有降级比服务不可用强。凌晨四点优化完最后一段代码提交上线。监控显示延迟降到1.3秒幻觉率回到3%以下。喝掉冷掉的咖啡关掉IDE。RAG优化就像修老房子没有银弹每个角落都得亲手摸过才知道哪里漏风。但看着曲线一点点变平那种满足感大概就是工程师的浪漫吧。下篇预告012、工程篇构建可观测、可回滚、可迭代的RAG生产系统。聊聊我们怎么设计埋点、做AB测试、灰度发布让RAG系统不再是黑盒。

更多文章