开头个人经历
我搭了个个人Wiki系统,用RAG技术让AI回答我的技术问题。
一开始,准确率只有60%。
我问"Spring Boot自动配置原理",AI答出一堆无关的Spring Security内容。
我花了三周时间,试了5种优化方案,最后准确率提升到95%。
今天我把这些经验整理出来,帮你绕过我踩过的坑。
坑1:分块太碎,语义丢失
问题: 我一开始按段落分块,每个段落200字。
结果:RAG检索时,只返回零散片段,AI无法理解完整上下文。
具体例子:
我的Wiki里有"Spring Boot自动配置原理"的文章,分成了10个片段:
片段1:Spring Boot自动配置是@SpringBootApplication注解实现的... 片段2:@SpringBootApplication包含@EnableAutoConfiguration... 片段3:@EnableAutoConfiguration通过ImportSelector导入配置类... ...
当我问"Spring Boot自动配置原理是什么",RAG返回了片段1、3、7。
AI只能看到片段1、3、7,缺了片段2、4、5、6,无法给出完整答案。
解决方案:按语义分块,而不是按段落
我用langchain的RecursiveCharacterTextSplitter,按段落、标题、句子三个层级切分:
from langchain.text_splitter import RecursiveCharacterTextSplitter text_splitter = RecursiveCharacterTextSplitter( chunk_size=1000, chunk_overlap=200, separators=["\n\n", "\n", "。", "!", "?", ";", ",", " ", ""] ) chunks = text_splitter.split_text(text)
关键点:
chunk_size=1000:每个块1000字符,够完整但不会太长chunk_overlap=200:块之间重叠200字符,保留上下文separators:按段落、标题、句子优先切分,而不是按固定长度效果: 准确率从60%提升到75%。
坑2:Embedding模型不匹配
问题: 我一开始用OpenAI的text-embedding-ada-002,但我的文档主要是中文技术术语。
结果:RAG检索时,相似度计算不准确,找不到最相关的片段。
具体例子:
我的Wiki里有"RocketMQ顺序消息"的文章,但用text-embedding-ada-002检索时,"顺序消息"和"异步消息"的向量距离很近。
AI经常混淆这两个概念,答错了。
解决方案:用中文Embedding模型
我改用了Moka-ai/m3e-base,这是专门为中文优化的Embedding模型:
from langchain.embeddings import HuggingFaceEmbeddings embeddings = HuggingFaceEmbeddings( model_name="moka-ai/m3e-base", model_kwargs={'device': 'cpu'}, encode_kwargs={'normalize_embeddings': True} )
关键点:
model_name="moka-ai/m3e-base":用中文优化的模型normalize_embeddings=True:归一化向量,提升相似度计算精度效果: 准确率从75%提升到85%。
坑3:检索只用相似度,不够精准
问题: 我一开始只按相似度排序,返回最相似的5个片段。
结果:有时候最相似的片段并不是最相关的。
具体例子:
我的Wiki里有"Spring Boot自动配置原理"和"Spring Security自动配置"两篇文章。
当我问"Spring Boot自动配置原理",RAG返回了3个"Spring Boot自动配置原理"的片段,但其中1个片段已经过时,还有2个是"Spring Security自动配置"的片段。
因为"Spring Security自动配置"的片段相似度更高(因为都包含"自动配置"这个词),所以被优先返回。
解决方案:用Hybrid Search(混合检索)
我用Cohere的rerank模型,对检索结果重新排序:
from langchain.retrievers import ContextualCompressionRetriever from langchain.retrievers.document_compressors import CohereRerank compressor = CohereRerank( cohere_api_key="your-api-key", top_n=3 ) compression_retriever = ContextualCompressionRetriever( base_compressor=compressor, base_retriever=retriever ) results = compression_retriever.get_relevant_documents(query)
关键点:
top_n=3:只保留最相关的3个片段,而不是5个效果: 准确率从85%提升到90%。
坑4:Prompt太简单,AI不会推理
问题: 我一开始用最简单的Prompt:
根据以下内容回答问题: {context} 问题:{question}
结果:AI只会照搬片段内容,不会推理和整合。
具体例子:
我Wiki里有"RocketMQ顺序消息"的文章,分成了3个片段:
片段1:RocketMQ顺序消息通过MessageQueueSelector实现... 片段2:RocketMQ顺序消息要求同一订单的消息发到同一个队列... 片段3:RocketMQ顺序消息在消费者端也要保证顺序消费...
当我问"RocketMQ如何保证顺序消息",AI只回复了片段1的内容,没有把片段2和片段3整合起来,答案不完整。
解决方案:用CoT(Chain of Thought)Prompt
我改用了CoT Prompt,让AI一步步推理:
你是一个技术专家,需要根据以下内容回答问题。 回答步骤: 1. 先阅读所有片段,理解每个片段的内容 2. 找出与问题相关的片段 3. 整合这些片段的信息,给出完整答案 4. 如果信息不够,明确说明需要哪些额外信息 内容: {context} 问题:{question} 请按照以上步骤回答问题:
关键点:
效果: 准确率从90%提升到93%。
坑5:没有反馈机制,不知道哪里错了
问题: 我一开始没有记录RAG的检索结果和AI的答案,只关注准确率。
结果:即使准确率提升了,我也不知道具体是哪个环节提升了。
具体例子:
我用了Hybrid Search后,准确率从85%提升到90%,但我不知道:
没有数据支撑,我没法继续优化。
解决方案:记录RAG的完整链路
我写了个日志系统,记录RAG的完整链路:
import json from datetime import datetime def log_rag_chain(query, retrieved_docs, answer, is_correct): log = { "timestamp": datetime.now().isoformat(), "query": query, "retrieved_docs": [ { "content": doc.page_content, "metadata": doc.metadata, "score": doc.metadata.get("score", 0) } for doc in retrieved_docs ], "answer": answer, "is_correct": is_correct } with open("rag_logs.jsonl", "a") as f: f.write(json.dumps(log, ensure_ascii=False) + "\n")
关键点:
效果: 我可以分析日志,找出准确率提升的具体原因,继续优化。
最终优化结果
优化前:
优化后:
避坑清单
▪ 坑1:分块太碎,语义丢失
▪ 坑2:Embedding模型不匹配
▪ 坑3:检索只用相似度,不够精准
▪ 坑4:Prompt太简单,AI不会推理
▪ 坑5:没有反馈机制,不知道哪里错了
总结
RAG准确率从60%提升到95%,我踩了5个坑:
最关键的两点: