Java大模型开发入门 (10/15):连接外部世界(下) - 端到端构建完整的RAG问答系统
前言
在过去的几篇文章中,我们已经完成了RAG系统所有的数据准备工作:
- 我们加载了私有文档并将其分割成了文本片段。
- 我们使用OpenAI的嵌入模型将这些片段向量化。
- 我们把这些向量存储在了内存向量数据库中。
- 我们实现了语义搜索,能够根据用户问题找出最相关的文档片段。
我们已经成功地“开卷”并“找到了答案所在的页面”。但是,我们的search
方法目前返回的,还只是原始的、未经加工的文本片段列表。这并不是一个用户友好的最终答案。
今天,我们的任务就是完成这最后一步:将检索到的这些“参考资料”与用户的原始问题一起,交给一个强大的语言模型(我们将使用最新的gpt-4o-mini
),让它基于这些上下文,用自然、流畅的语言“总结”出最终答案。
第一部分:RAG的最后一公里 - 增强与生成
让我们回顾一下RAG的完整流程,并聚焦于最后两个步骤:
-
检索 (Retrieve):根据用户问题,从向量数据库中找出最相关的文档片段。(我们已在上一篇实现)
-
增强 (Augment):这是关键的一步。我们需要将检索到的片段(上下文)和用户的原始问题,组合成一个新的、信息更丰富的提示词。这个提示词的结构通常如下:
请根据以下提供的上下文信息来回答问题。 如果根据上下文无法回答,请说“根据我所掌握的资料,我无法回答这个问题”。[上下文信息] --- 片段1:[检索到的第一个文本片段内容] --- 片段2:[检索到的第二个文本片段内容] --- ...[问题] [用户的原始问题]
-
生成 (Generate):将这个“增强版”的提示词发送给大语言模型,获取最终的、经过整合的答案。
手动完成这个“增强”步骤需要编写不少逻辑。幸运的是,LangChain4j为我们提供了极其优雅的高级抽象来自动化这个过程。
第二部分:LangChain4j的RAG核心组件
-
ContentRetriever
:- 职责:内容检索器。它的工作就是接收一个查询,然后从某个地方(比如我们的向量数据库)返回相关的内容。
- 具体实现:
EmbeddingStoreContentRetriever
。这是一个连接了EmbeddingStore
(数据在哪)和EmbeddingModel
(如何搜索)的检索器。
-
RetrievalAugmentor
:- 职责:检索增强器。这是整个RAG流程的“大脑”。它是一个中间件,位于我们的AI服务和最终的语言模型之间。
- 工作流程:当你向一个配置了
RetrievalAugmentor
的AI服务提问时,它会:- 使用内部的
ContentRetriever
去检索相关内容。 - 自动将检索到的内容按照预设的模板,与你的问题组合起来,形成我们上面看到的“增强版”提示词。
- 将这个新提示词传递给语言模型。
- 使用内部的
第三部分:实战 - 构建端到端的RAG服务
-
更新
application.properties
以使用gpt-4o-mini
gpt-4o-mini
是OpenAI最新推出的高性价比、高性能模型,非常适合RAG场景。# application.properties # ... 其他配置保持不变 ...# 将聊天模型更新为 gpt-4o-mini langchain4j.open-ai.chat-model.model-name=gpt-4o-mini
-
在
LangChain4jConfig
中组装RAG组件
我们将在这里创建ContentRetriever
、RetrievalAugmentor
以及最终的RAG AI服务的Bean。
package com.example.aidemoapp.config;import com.example.aidemoapp.service.KnowledgeBaseAssistant;import dev.langchain4j.data.segment.TextSegment;import dev.langchain4j.model.chat.ChatLanguageModel;import dev.langchain4j.model.embedding.EmbeddingModel;import dev.langchain4j.rag.DefaultRetrievalAugmentor;import dev.langchain4j.rag.RetrievalAugmentor;import dev.langchain4j.rag.content.retriever.ContentRetriever;import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever;import dev.langchain4j.service.AiServices;import dev.langchain4j.store.embedding.EmbeddingStore;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configurationpublic class LangChain4jConfig {// ... 保留 chatLanguageModel, embeddingModel, embeddingStore 的Bean定义 ...// 步骤1: 创建ContentRetriever Bean@Beanpublic ContentRetriever contentRetriever(EmbeddingStore<TextSegment> embeddingStore, EmbeddingModel embeddingModel) {// 这个检索器知道如何从向量存储中根据语义相似度检索内容return EmbeddingStoreContentRetriever.builder().embeddingStore(embeddingStore).embeddingModel(embeddingModel).maxResults(3) // 每次检索最多返回3个最相关的片段.build();}// 步骤2: 创建RetrievalAugmentor Bean@Beanpublic RetrievalAugmentor retrievalAugmentor(ContentRetriever contentRetriever) {// 这是默认的检索增强器,它会使用提供的内容检索器return DefaultRetrievalAugmentor.builder().contentRetriever(contentRetriever).build();}// 步骤3: 创建最终的、具备RAG能力的AI服务Bean@Beanpublic KnowledgeBaseAssistant knowledgeBaseAssistant(ChatModel chatLanguageModel, RetrievalAugmentor retrievalAugmentor) {return AiServices.builder(KnowledgeBaseAssistant.class).chatModel(chatLanguageModel).retrievalAugmentor(retrievalAugmentor).build();} }
-
定义
KnowledgeBaseAssistant
接口
在service
包下创建一个新的接口,代表我们的知识库问答服务。package com.example.aidemoapp.service;public interface KnowledgeBaseAssistant {String chat(String userMessage); }
-
创建新的Controller来调用RAG服务
在controller
包下创建KnowledgeBaseController.java
。package com.example.aidemoapp.controller;import com.example.aidemoapp.service.KnowledgeBaseAssistant; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController;@RestController @RequestMapping("/api/kb") @RequiredArgsConstructor public class KnowledgeBaseController {private final KnowledgeBaseAssistant knowledgeBaseAssistant;@GetMapping("/chat")public String chat(@RequestParam("query") String query) {return knowledgeBaseAssistant.chat(query);} }
第四步:运行并见证奇迹
- 确保你的
OPENAI_API_KEY
环境变量已设置。 - 启动Spring Boot应用。DocumentService 中的
@PostConstruct
方法会自动将文档加载并嵌入到内存向量库。 - 现在,向新的API端点发送请求:
http://localhost:8080/api/kb/chat?query=What is the X-Wing AI Assistant?
或者
http://localhost:8080/api/kb/chat?query=What are the setup requirements?
发生了什么?
当你发送请求时,KnowledgeBaseAssistant
的代理实例被调用。它内部的RetrievalAugmentor
启动,使用ContentRetriever
从向量库中找到了关于“X-Wing”或“setup requirements”的文本片段。然后,它构建了一个包含这些上下文的、增强版的提示词,并将其发送给gpt-4o-mini
。最后,gpt-4o-mini
基于这些精确的资料,生成了一个流畅、准确的回答,并返回给你。
如果你的文档中没有相关信息,它会告诉你它不知道,而不是胡乱猜测!
总结
恭喜你!我们已经成功地构建了一个完整的、端到端的RAG(检索增强生成)系统。这个系统能够:
- 摄入私有文档。
- 进行语义理解和检索。
- 基于检索到的上下文生成准确的回答。
这是目前企业应用大模型最主流、最核心的技术之一。掌握了RAG,你就掌握了将通用大模型转变为领域专家的钥匙。
我们的AI应用现在既有“记忆”,又有“知识”。但它还只是一个被动的问答工具。如何让它更主动,能够像一个真正的助理一样,根据我们的指令去调用外部工具(比如查询天气、计算器、订票API)呢?
源码获取
本文中所有实战代码均已同步更新至Gitee仓库。建议您git pull
拉取最新代码,对照本文进行学习。
- Gitee仓库地址: https://gitee.com/chaocloud/springboot-langchain4j-demo.git
下一篇预告:
《Java大模型开发入门 (11/15):让AI自主行动 - 初探LangChain4j中的智能体(Agents)》—— 我们将进入一个更激动人心的新领域:AI智能体(Agents)。我们将学习如何赋予AI使用工具的能力,让它从一个“问答机器人”进化成一个能执行任务的“智能助理”。