前言
在人工智能快速发展的今天,大语言模型(LLM)展现出了强大的对话能力。然而,大模型存在两个明显的痛点:知识时效性限制和幻觉问题。当用户询问模型训练数据之外的最新信息时,模型要么无法回答,要么可能“编造”不存在的答案。
RAG(Retrieval-Augmented Generation,检索增强生成)技术正是为了解决这些问题而生的。本文将带你从零开始,使用 Spring AI 框架构建一个完整的 RAG 应用——恋爱大师智能助手。
一、什么是 RAG?
RAG 是一种结合信息检索技术和 AI 内容生成的混合架构。简单来说,它的工作流程是:当用户提问时,系统先从知识库中检索相关信息,然后将这些信息与用户问题一起提交给大模型,让模型基于真实的知识进行回答。
这种架构的优势显而易见:
- 知识实时更新:只需更新知识库,无需重新训练模型
- 答案可溯源:可以追溯到回答所依据的原始文档
- 降低幻觉:模型有了事实依据,减少了编造信息的可能
二、RAG 核心工作流程
RAG 主要包含以下四个核心步骤:
1. 文档收集与切割
首先需要准备知识文档。原始文档可能来自网页、PDF、数据库等多种来源。我们需要对文档进行预处理:
- 清洗:去除无关的格式、特殊字符
- 切割:将长文档分割成适当大小的片段
常见的切割策略包括:
- 基于固定大小(如 512 个 token)
- 基于语义边界(按段落、句子分割)
- 基于递归分割
2. 向量转换与存储
将文本块通过 Embedding 模型转换为高维向量。这些向量能够捕获文本的语义特征,使得语义相似的文本在向量空间中距离更近。
随后将向量和对应文本存入向量数据库,支持高效的相似性搜索。常用的向量数据库包括 SimpleVectorStore(内存型)、Pinecone、Milvus 等。
3. 文档过滤与检索
当用户提问时:
- 将用户问题也转换为向量
- 在向量数据库中查找与问题向量最相似的文档块
- 常用的相似度算法有余弦相似度、欧氏距离等
4. 查询增强与生成
将检索到的相关文档与用户问题组合成增强提示,提交给大模型生成最终回答。这个环节通常还会涉及:
- 上下文融合
- 源引用
- 后处理优化
三、RAG 实战:构建恋爱大师智能助手
下面我们通过一个完整的案例来实践 RAG 技术。我们将构建一个“恋爱大师”智能助手,它能根据知识库中的恋爱心理学知识,为用户提供专业的恋爱建议。
3.1 技术栈
- Spring AI:统一的 AI 应用开发框架
- DashScope:阿里云的大模型服务平台(本文使用其 Embedding 和 Chat 能力)
- 向量数据库:SimpleVectorStore(内存版)和阿里云知识库服务(云版)
3.2 文档加载器实现
首先,我们需要实现文档加载器,从 classpath 下的 document/ 目录读取 Markdown 文件。
@Component
@Slf4j
public class LoveAppDocumentLoader {
private final ResourcePatternResolver resourcePatternResolver;
public LoveAppDocumentLoader(ResourcePatternResolver resourcePatternResolver) {
this.resourcePatternResolver = resourcePatternResolver;
}
/**
* 读取多篇Markdown文件,获取文档列表
*/
public List<Document> loaderMarkdowns() {
List<Document> allDocuments = new ArrayList<>();
try {
// 读取document目录下的所有.md文件
Resource[] resources = resourcePatternResolver.getResources("classpath:document/*.md");
for (Resource r : resources) {
String filename = r.getFilename();
// 配置Markdown解析器
MarkdownDocumentReaderConfig config = MarkdownDocumentReaderConfig.builder()
.withHorizontalRuleCreateDocument(true)
.withIncludeCodeBlock(false)
.withIncludeBlockquote(false)
.withAdditionalMetadata("filename", filename)
.build();
MarkdownDocumentReader reader = new MarkdownDocumentReader(r, config);
allDocuments.addAll(reader.get());
}
} catch (IOException e) {
log.error("读取Markdown文件失败", e);
}
return allDocuments;
}
}
3.3 向量存储配置
接下来,配置向量存储。我们使用 Spring AI 内置的 SimpleVectorStore 来保存文档向量。
@Configuration
public class LoveAppVectorStoreConfig {
@Resource
private LoveAppDocumentLoader loveAppDocumentLoader;
@Bean
VectorStore loveAppVectorStore(EmbeddingModel dashscopeEmbeddingModel) {
// 创建基于内存的向量数据库
SimpleVectorStore loveAppVectorStore = SimpleVectorStore
.builder(dashscopeEmbeddingModel)
.build();
// 加载文档并添加到向量库
List<Document> documents = loveAppDocumentLoader.loaderMarkdowns();
loveAppVectorStore.doAdd(documents);
return loveAppVectorStore;
}
}
3.4 云知识库 RAG 配置
除了本地向量库,Spring AI 还支持对接阿里云知识库服务。这种方式更适合生产环境,因为云服务提供了更好的可扩展性和维护性。
@Configuration
public class LoveAppRagCloudAdvisorConfig {
@Value("${spring.ai.dashscope.api-key}")
private String dashScopeApiKey;
@Bean
Advisor loveAppRagCloudAdvisor() {
DashScopeApi dashScopeApi = new DashScopeApi(dashScopeApiKey);
// 知识库索引名称
final String KNOWLEDGE_INDEX = "恋爱大师";
// 创建文档检索器
DashScopeDocumentRetriever retriever = new DashScopeDocumentRetriever(
dashScopeApi,
DashScopeDocumentRetrieverOptions.builder()
.withIndexName(KNOWLEDGE_INDEX)
.build()
);
// 创建检索增强顾问
return RetrievalAugmentationAdvisor.builder()
.documentRetriever(retriever)
.build();
}
}
3.5 恋爱大师主类实现
最后,将所有组件组装起来,实现完整的对话功能。
@Component
@Slf4j
public class LoveApp {
private ChatClient chatClient;
// 系统预设提示词
private static final String SYSTEM_PROMPT = """
扮演深耕恋爱心理领域的专家。开场向用户表明身份,告知用户可倾诉恋爱难题。\
围绕单身、恋爱、已婚三种状态提问:单身状态询问社交圈拓展及追求心仪对象的困扰;\
恋爱状态询问沟通、习惯差异引发的矛盾;已婚状态询问家庭责任与亲属关系处理的问题。\
引导用户详述事情经过、对方反应及自身想法,以便给出专属解决方案。""";
@Resource
private VectorStore loveAppVectorStore;
@Resource
private Advisor loveAppRagCloudAdvisor;
public LoveApp(ChatModel dashscopeChatModel) {
// 初始化对话记忆(支持多轮对话)
ChatMemory chatMemory = new InMemoryChatMemory();
this.chatClient = ChatClient.builder(dashscopeChatModel)
.defaultSystem(SYSTEM_PROMPT)
.defaultAdvisors(
MessageChatMemoryAdvisor.builder(chatMemory).build(),
new MyLoggerAdvisor() // 自定义日志顾问
)
.build();
}
/**
* 基础对话(支持多轮对话记忆)
*/
public String doChat(String message, String chatId) {
ChatResponse chatResponse = chatClient.prompt()
.user(message)
.advisors(spec -> spec
.param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId)
.param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 10))
.call()
.chatResponse();
return chatResponse.getResult().getOutput().getText();
}
/**
* 结构化输出 - 恋爱报告
*/
record LoveReport(String title, List<String> suggestions) {}
public LoveReport doChatWithReport(String message, String chatId) {
LoveReport response = chatClient.prompt()
.system(SYSTEM_PROMPT + "每次对话后都要生成恋爱结果,标题为{用户名}的恋爱报告,内容为建议列表")
.user(message)
.advisors(spec -> spec
.param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId)
.param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 10))
.call()
.entity(LoveReport.class);
return response;
}
/**
* 基于本地向量库的 RAG 问答
*/
public String doChatWithLocalRag(String message, String chatId) {
ChatResponse chatResponse = chatClient.prompt()
.user(message)
.advisors(spec -> spec
.param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId)
.param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 10))
.advisors(new QuestionAnswerAdvisor(loveAppVectorStore))
.call()
.chatResponse();
return chatResponse.getResult().getOutput().getText();
}
/**
* 基于云知识库的 RAG 问答
*/
public String doChatWithCloudRag(String message, String chatId) {
ChatResponse chatResponse = chatClient.prompt()
.user(message)
.advisors(spec -> spec
.param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId)
.param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 10))
.advisors(loveAppRagCloudAdvisor)
.call()
.chatResponse();
return chatResponse.getResult().getOutput().getText();
}
}
四、关键技术点解析
4.1 Spring AI 的 Advisor 机制
在 Spring AI 中,Advisor(顾问)是一个强大的拦截器概念。它可以:
- MessageChatMemoryAdvisor:管理对话历史,实现多轮对话记忆
- QuestionAnswerAdvisor:简单的 RAG 问答拦截器,自动将检索结果注入提示词
- RetrievalAugmentationAdvisor:更强大的检索增强顾问,支持灵活配置
4.2 两种 RAG 实现方式对比
| 对比维度 | 本地向量库 | 云知识库服务 |
|---|---|---|
| 部署成本 | 低,无需额外服务 | 需开通云服务 |
| 扩展性 | 受限于单机内存 | 弹性扩展 |
| 维护成本 | 需自行维护 | 云服务托管 |
| 检索性能 | 适合小规模数据 | 支持海量数据 |
| 适用场景 | 开发测试、小规模应用 | 生产环境、大规模应用 |
4.3 Embedding 模型的作用
Embedding 模型是整个 RAG 系统的核心。它将文本转换为向量,使得:
- 语义相似的文本在向量空间中距离更近
- 支持基于语义的相似度搜索
- 可以跨语言进行检索(取决于模型能力)
五、实践建议与最佳实践
5.1 文档切割策略
- 固定大小切割:简单但可能切断语义完整的段落
- 语义边界切割:按段落、句子切割,保持语义完整性
- 递归切割:从大到小尝试不同分隔符,找到合适的分割点
建议根据实际文档类型选择合适的策略。
5.2 检索优化
- 混合检索:结合关键词检索和语义检索
- 重排序:检索后使用 Rank 模型对结果进行精排
- 元数据过滤:利用文档的元数据(如时间、来源)进行过滤
5.3 提示词设计
增强提示词的模板通常如下:
基于以下参考资料回答用户问题:
【参考资料】
{检索到的文档内容}
【用户问题】
{用户输入}
请基于参考资料回答问题,如果参考资料中没有相关信息,请告知用户。
六、总结
本文通过一个完整的实战案例,介绍了 RAG 技术的核心概念和实现方式。从文档加载、向量存储到检索增强,我们使用 Spring AI 框架构建了一个恋爱大师智能助手。
RAG 技术巧妙地结合了信息检索和大模型的优势,为大模型应用开辟了新的可能性。无论是开发智能客服、知识库问答系统,还是构建专业的领域专家助手,RAG 都是一种值得考虑的技术方案。
希望这篇文章能帮助你理解并掌握 RAG 技术,在实际项目中灵活运用。如果你对 Spring AI 或 RAG 技术有任何疑问,欢迎交流讨论!