1. Spring AI与RAG技术深度解析
在当今人工智能技术快速发展的时代,大型语言模型(LLM)已经成为技术领域的热点。然而,这些强大的模型在实际应用中仍面临诸多挑战,比如知识更新滞后、专业领域知识不足等问题。检索增强生成(Retrieval-Augmented Generation,简称RAG)技术应运而生,它巧妙地将信息检索与文本生成相结合,为这些问题提供了优雅的解决方案。
Spring AI作为Java生态中重要的AI框架,为开发者提供了便捷的RAG实现路径。本文将深入探讨如何基于Spring AI构建高效的RAG系统,从核心原理到实战应用,再到优化策略,为开发者提供全方位的技术指导。
2. RAG技术核心原理
2.1 RAG与传统LLM的对比
传统的大型语言模型存在几个明显的局限性:
- 知识时效性问题:模型训练完成后,其知识就固定了,无法获取最新的信息
- 幻觉问题:模型可能会生成看似合理但实际上错误的内容
- 上下文窗口限制:处理长文本时能力有限
- 领域专业性不足:通用模型缺乏特定领域的深度知识
RAG技术通过引入外部知识检索机制,有效解决了这些问题。其核心思想是:当模型需要回答问题时,先从外部知识库中检索相关信息,然后将这些信息作为上下文提供给模型,最后让模型基于这些信息生成回答。
2.2 RAG工作流程详解
RAG系统的工作流程可以分为两个主要阶段:
离线索引阶段:
- 文档加载:从各种格式(如PDF、Word、Excel等)中提取文本内容
- 文本分割:将长文档切分为适当大小的片段
- 向量化:使用Embedding模型将文本转换为向量表示
- 存储:将向量化后的数据存入向量数据库(如Milvus、Redis等)
在线检索生成阶段:
- 查询向量化:将用户问题转换为向量
- 相似度匹配:在向量数据库中查找最相关的文本片段
- 上下文组装:将检索到的信息整合到Prompt中
- 生成回答:模型基于提供的上下文生成最终回答
3. Spring AI + Milvus实战实现
3.1 环境准备与配置
要构建基于Spring AI的RAG系统,首先需要准备以下环境:
- 依赖配置:
<dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-openai</artifactId> </dependency> <dependency> <groupId>io.milvus</groupId> <artifactId>milvus-sdk-java</artifactId> <version>2.2.10</version> </dependency>- 应用配置:
# OpenAI配置 spring.ai.openai.api-key=your-api-key spring.ai.openai.embedding.api-key=your-api-key spring.ai.openai.embedding.base-url=https://api.openai-hk.com # Milvus配置 milvus.host=localhost milvus.port=195303.2 核心代码实现
3.2.1 创建向量集合
public void createCollection() { List<FieldType> fieldTypes = Arrays.asList( FieldType.newBuilder() .withName("id") .withDataType(DataType.Int64) .withPrimaryKey(true) .withAutoID(true) .build(), FieldType.newBuilder() .withName("feature") .withDataType(DataType.FloatVector) .withDimension(1536) .build(), FieldType.newBuilder() .withName("instruction") .withDataType(DataType.VarChar) .withMaxLength(65535) .build(), FieldType.newBuilder() .withName("output") .withDataType(DataType.VarChar) .withMaxLength(65535) .build() ); CreateCollectionParam createParam = CreateCollectionParam.newBuilder() .withCollectionName("springai_rag") .withFieldTypes(fieldTypes) .build(); client.createCollection(createParam); CreateIndexParam indexParam = CreateIndexParam.newBuilder() .withCollectionName("springai_rag") .withFieldName("feature") .withIndexType(IndexType.IVF_FLAT) .withMetricType(MetricType.L2) .build(); client.createIndex(indexParam); }3.2.2 数据向量化与存储
@Autowired private EmbeddingModel embeddingModel; public void insertData(List<FaqItem> items) { for (FaqItem item : items) { float[] embeddings = embeddingModel.embed(item.getInstruction()); List<InsertParam.Field> fields = new ArrayList<>(); fields.add(new InsertParam.Field("feature", Collections.singletonList(embeddings))); fields.add(new InsertParam.Field("instruction", Collections.singletonList(item.getInstruction()))); fields.add(new InsertParam.Field("output", Collections.singletonList(item.getOutput()))); InsertParam insertParam = InsertParam.newBuilder() .withCollectionName("springai_rag") .withFields(fields) .build(); client.insert(insertParam); } }3.2.3 RAG检索与生成
@Test void ragTest() { String userQuestion = "预算8000元以内,适合深度学习的笔记本电脑推荐"; float[] queryEmbedding = embeddingModel.embed(userQuestion); SearchParam searchParam = SearchParam.newBuilder() .withCollectionName("springai_rag") .withMetricType(MetricType.L2) .withTopK(3) .withVectors(Collections.singletonList(queryEmbedding)) .withVectorFieldName("feature") .withOutFields(Arrays.asList("instruction", "output")) .build(); SearchResults results = client.search(searchParam).getData(); List<RowRecord> records = new SearchResultsWrapper(results).getRowRecords(); StringBuilder context = new StringBuilder(); for (RowRecord record : records) { context.append("Q: ").append(record.get("instruction")).append("\n"); context.append("A: ").append(record.get("output")).append("\n\n"); } String enhancedPrompt = String.format(""" # 角色设定 你是资深数码产品顾问,擅长根据用户需求推荐笔记本电脑。 请严格基于以下商品库信息回答,不要推荐库中不存在的商品。 # 商品库信息(Top-3匹配结果): %s # 用户需求: %s # 回答要求: 1. 先分析用户需求(用途、预算、性能要求) 2. 从商品库中推荐1-2款最合适的,说明理由 3. 如果商品库中没有匹配项,请明确告知"暂无符合要求的商品" 4. 最后可简要补充选购建议(非强制) """, context, userQuestion); String answer = chatClient.prompt(enhancedPrompt).call().content(); System.out.println(answer); }4. RAG优化策略与实践
4.1 文本分割策略优化
文本分割(chunking)是影响RAG效果的关键因素之一。不同的分割策略适用于不同的场景:
| 分割策略 | 实现方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 固定字符数 | 每N个字符切分 | 简单高效 | 可能切断语义 | 对上下文连续性要求不高的场景 |
| 滑动窗口 | 固定大小+重叠部分 | 保留上下文衔接 | 数据冗余,存储增加 | 需要连贯性的长文档 |
| 递归字符 | 按标点优先级切分 | 保持语义完整 | 实现复杂 | 专业文档、论文 |
| 基于语义 | 使用NLP模型判断语义边界 | 最优语义保留 | 计算成本高 | 高精度要求的知识库 |
最佳实践建议:
- 结合文档结构(Markdown标题、PDF段落)进行分层切分
- chunk大小建议控制在256-512 tokens之间
- 设置适当的重叠(overlap)保持上下文连贯性
4.2 检索优化技巧
- 混合检索(Hybrid Search): 结合关键词检索(BM25)和向量检索,可以同时获得精确匹配和语义理解的优势。
// 伪代码示例 List<Document> keywordResults = keywordSearch(query); List<Document> vectorResults = vectorSearch(query); List<Document> hybridResults = rerank(merge(keywordResults, vectorResults));重排序(Reranking): 使用专门的交叉编码器(Cross-Encoder)模型对初步检索结果进行精排,提高最终结果的相关性。
查询重写(Query Rewriting): 对用户的模糊查询进行扩展和澄清,提高检索的准确性。
4.3 RAG常见问题与解决方案
RAG系统在实际应用中可能遇到多种问题,以下是常见问题及解决方案:
| 问题类别 | 具体表现 | 解决方案 |
|---|---|---|
| Missing Content | 知识库中没有答案 | 设置兜底回复;持续扩充知识库 |
| Missed Top Ranked | 正确答案未进入Top-K | 调整similarity阈值;引入重排序 |
| Not in Context | 检索到内容但与问题无关 | 优化文本分割;提升向量模型质量 |
| Wrong Format | 需要JSON但返回了文本 | Prompt中明确输出格式示例 |
| Incomplete | 答案只覆盖问题部分 | 引导模型逐步思考(CoT);拆分复杂问题 |
| Not Extracted | 上下文中有答案但模型未提取 | 优化Prompt模板;增强上下文标识 |
5. RAG效果评估与未来展望
5.1 RAGAS评估框架
RAGAS(Retrieval-Augmented Generation Assessment)是评估RAG系统的标准框架,主要包含以下核心指标:
检索质量指标:
- Context Relevancy(上下文相关性):衡量检索到的上下文与问题的相关程度
- Context Recall(上下文召回率):衡量检索结果是否包含回答问题所需的所有信息
生成质量指标:
- Faithfulness(忠实性):评估生成答案是否基于检索到的上下文
- Answer Relevancy(答案相关性):衡量答案与问题的直接相关程度
评估数据集示例:
{ "question": "预算8000元以内,适合深度学习的笔记本电脑推荐", "contexts": [ "机械革命翼龙15 Pro配备RTX4060显卡,支持CUDA加速,售价7499元", "联想拯救者Y7000P 2024款搭载i7-14650HX和RTX4060,售价8499元超出预算", "轻薄本不适合深度学习,缺乏独立显卡" ], "answer": "推荐机械革命翼龙15 Pro,配备RTX4060显卡支持CUDA加速,价格7499元符合预算", "ground_truths": ["机械革命翼龙15 Pro,RTX4060,7499元"] }5.2 RAG与微调的选择指南
在实际项目中,RAG和微调(Fine-tuning)各有适用场景:
选择RAG的场景:
- 需要实时更新的知识(如新闻、股价)
- 数据量庞大且频繁变动
- 需要解释性强的应用场景(可溯源到具体文档)
- 预算有限,无法承担微调成本
选择微调的场景:
- 需要改变模型行为风格(如特定语气、格式)
- 领域知识非常固定且通用模型表现极差
- 对延迟敏感(RAG需要额外检索时间)
5.3 RAG技术未来发展趋势
- Agentic RAG:结合ReAct模式,让模型自主决定何时检索、检索什么
- 多模态RAG:支持图片、音频、视频的检索增强
- Graph RAG:结合知识图谱,处理复杂的多跳推理问题
在实际项目开发中,建议先从简单的关键字检索+向量混合方案开始,逐步引入重排序、查询重写等优化策略,根据实际效果和需求不断调整系统架构。