前言
RAG(检索增强生成)系统的质量很大程度上取决于各个环节的精细调优。本文将从文档处理、向量转换、检索优化到查询增强,系统性地介绍 RAG 的最佳实践。
一、文档收集和切割
文档的质量决定了 AI 回答的质量,这是 RAG 系统中最基础也最重要的环节。
1.1 优化原始文档
知识完备性是文档质量的首要条件。如果知识库缺失相关内容,大模型将无法准确回答对应问题。
优化建议:
- 内容结构化:将零散内容组织成有逻辑的结构
- 内容规范化:统一术语和表达方式
- 格式标准化:使用统一的文档格式
💡 通过收集用户反馈或统计知识库检索命中率,不断完善和优化知识库内容
1.2 文档切片
切片的目的是将长文档分割成更小的片段,方便大模型处理和理解。
最佳实践:结合智能分块算法和人工二次校验
| 切片方式 | 优点 | 缺点 |
|---|---|---|
| 固定长度切分 | 实现简单 | 容易导致语义断裂 |
| 智能分块算法 | 保持语义完整性 | 需要更多计算资源 |
实施建议:
- 使用 Spring AI 的 ETL Pipeline 提供的 DocumentTransformer
- 或使用云服务(如阿里云百炼)的智能切分功能
1.3 元数据标注
元数据是对文档内容的补充描述,能够帮助大模型更好地理解文档的上下文和语义。
标注方式:
- 手动添加元信息(单个文档)
- 利用 DocumentReader 批量添加
- 自动添加:Spring AI 提供基于 AI 的关键词解析
- 云服务智能元数据标注(如阿里云百炼)
二、向量转换和存储
向量转换和存储是 RAG 系统的核心环节,直接影响检索的效率和准确性。
2.1 向量存储配置
选择向量存储方案时需考虑:
- 费用成本
- 数据规模
- 性能要求
- 开发成本
常见方案:内存 / Redis / MongoDB / 专业向量数据库
2.2 选择合适的嵌入模型
嵌入模型负责将文本转换为向量,其质量直接影响相似度计算和检索准确性。
⚠️ 不同场景可能需要不同的嵌入模型,建议根据实际数据进行效果测试
三、文档过滤和检索
这是开发者最能大显身手的地方,优化这个环节可以显著提升系统整体效果。
3.1 多查询扩展
在多轮场景中,用户输入的提示词可能不完整或存在歧义。多查询扩展可以提高检索的准确性和完整性。
注意事项:
- 设置合适的查询数量(一般建议 3-5 个)
- 保留原始的核心语义,避免引入新的歧义
代码示例:
MultiQueryExpander queryExpander = MultiQueryExpander.builder()
.chatClientBuilder(chatClientBuilder)
.numberOfQueries(3)
.build();
List<Query> queries = queryExpander.expand(new Query("程序员是干嘛的?"));
完整使用流程:
- 使用扩展后的查询召回文档
- 整合召回的文档(去重合并)
- 使用召回的文档改写 Prompt
3.2 查询重写和翻译
查询重写和翻译可以使查询更加精确和专业。
实现方式:
- 配置
TranslationQueryTransformer支持多语言 - 云服务中开启多轮会话改写功能
代码示例:
@Component
public class QueryRewriter {
private final QueryTransformer queryTransformer;
public QueryRewriter(ChatModel dashscopeChatModel) {
ChatClient.Builder builder = ChatClient.builder(dashscopeChatModel);
queryTransformer = RewriteQueryTransformer.builder()
.chatClientBuilder(builder)
.build();
}
public String doQueryRewrite(String prompt) {
Query query = new Query(prompt);
Query transformedQuery = queryTransformer.transform(query);
return transformedQuery.text();
}
}
3.3 检索器配置
检索器配置是影响检索质量的关键因素,主要包括三个方面:相似度阈值、返回文档数量和过滤规则。
设置合理的相似度阈值
| 问题 | 解决方案 |
|---|---|
| 召回结果不完整,缺少相关文本切片 | 降低相似度阈值,提高召回片段数 |
| 召回结果中包含大量无关文本 | 提高相似度阈值,排除低相似度信息 |
代码示例:
DocumentRetriever documentRetriever = VectorStoreDocumentRetriever.builder()
.vectorStore(vectorStore)
.similarityThreshold(0.5)
.build();
控制返回文档数量(Top-K)
控制返回给模型的文档数量,平衡信息完整性和噪音水平。
DocumentRetriever documentRetriever = VectorStoreDocumentRetriever.builder()
.vectorStore(vectorStore)
.similarityThreshold(0.5)
.topK(3)
.build();
📌 在多路召回场景下,系统会从多个知识库检索文本切片,通过重排序选出最相关的前 K 条
配置文档过滤规则
| 场景 | 解决方案 |
|---|---|
| 知识库包含多类别文档,希望限定范围 | 添加标签,检索时根据标签筛选 |
| 多篇结构相似文档,希望精确定位 | 提取元数据,进行结构化搜索 |
工厂模式示例:
public class LoveAppRagCustomAdvisorFactory {
public static Advisor createLoveAppRagCustomAdvisor(VectorStore vectorStore, String status){
// 过滤特定状态的文档
Filter.Expression expression = new FilterExpressionBuilder()
.eq("status", status)
.build();
DocumentRetriever documentRetriever = VectorStoreDocumentRetriever.builder()
.vectorStore(vectorStore)
.filterExpression(expression)
.similarityThreshold(0.5)
.topK(3)
.build();
return RetrievalAugmentationAdvisor.builder()
.documentRetriever(documentRetriever)
.build();
}
}
四、查询增强和关联
经过文档检索后,系统已获取相关文档。此时需要进一步优化生成效果。
4.1 错误处理机制
实际应用中可能出现多种异常:找不到相关文档、相似度过低、查询超时等。
自定义错误处理示例:
public class LoveAppContextualQueryAugumneterFactory {
public static QueryAugmenter createInstance(){
PromptTemplate emptyContextPromptTemplate = new PromptTemplate("""
你应该输出下面的内容:
抱歉,我只能回答恋爱相关的问题,别的没办法帮到您哦,
有问题可以联系我们的客服:https://www.baidu.com
""");
return ContextualQueryAugmenter.builder()
.allowEmptyContext(false)
.emptyContextPromptTemplate(emptyContextPromptTemplate)
.build();
}
}
应用到 Advisor:
RetrievalAugmentationAdvisor.builder()
.documentRetriever(documentRetriever)
.queryAugmenter(LoveAppContextualQueryAugmenterFactory.createInstance())
.build();
4.2 其他建议
- 分离检索阶段和生成阶段的知识块
- 使用不同粒度的文档:针对不同阶段使用不同粒度的文档
- 选择合适的模型:查询重写、元信息增强等场景可选择轻量大模型,不必整个项目只用一种大模型
总结
| 环节 | 核心要点 |
|---|---|
| 文档处理 | 知识完备性 + 智能切分 + 元数据标注 |
| 向量存储 | 根据场景选择合适的存储方案和嵌入模型 |
| 检索优化 | 多查询扩展 + 查询重写 + 合理配置阈值和 Top-K |
| 增强关联 | 错误处理 + 分阶段优化 + 模型选型 |
RAG 系统的优化是一个持续迭代的过程,需要根据实际效果不断调整各个环节的参数和策略。希望本文能为你提供实用的参考。