LangChain4j RAG 实战:手把手教你让大模型精准回答文档问题,告别幻觉!

📚 什么是 RAG?

简单来说,RAG 是一种在发送给大模型(LLM)之前,从你的数据中找到并注入相关信息片段到提示中的方法。

这样 LLM 将获得相关信息,并能够使用这些信息回复——这应该会降低产生幻觉的概率

相关信息片段可以使用各种信息检索方法找到,最流行的方法有三种:

1️⃣ 全文(关键词)搜索

使用 TF-IDF 和 BM25 等技术,通过匹配查询中的关键词与文档数据库进行搜索。 根据每个文档中这些关键词的频率和相关性对结果进行排名。

2️⃣ 向量搜索(语义搜索)

文本文档使用嵌入模型转换为数字向量。 然后根据查询向量和文档向量之间的余弦相似度或其他相似度度量找到并排序文档,从而捕捉更深层次的语义含义。

3️⃣ 混合搜索

结合多种搜索方法(例如,全文 + 向量)通常可以提高搜索的有效性。


🔄 RAG 的两个核心阶段

RAG 过程分为两个不同的阶段:索引检索。 LangChain4j 为这两个阶段提供了完整的工具支持。

📥 阶段一:索引(Indexing)

在索引阶段,文档会被预处理,以便在检索阶段进行高效搜索。

对于向量搜索,这通常涉及:

  • 清理文档
  • 用额外数据和元数据丰富文档
  • 将文档分割成更小的片段(也称为分块
  • 嵌入这些片段
  • 最后将它们存储在嵌入存储(向量数据库)中

💡提示:索引阶段通常是离线进行的,最终用户不需要等待其完成。 例如:可以通过定时任务在周末每周重新索引一次公司内部文档。

但在某些情况下,最终用户可能希望上传自己的自定义文档,使 LLM 能够访问这些文档。 在这种情况下,索引应该在线进行,并成为主应用程序的一部分。

索引阶段流程图:


🔍 阶段二:检索(Retrieval)

检索阶段通常在线进行,当用户提交一个应该使用索引文档回答的问题时。

对于向量搜索,这通常涉及:

  1. 嵌入用户的查询(问题)
  2. 在嵌入存储中执行相似度搜索
  3. 将相关片段(原始文档的片段)注入到提示中
  4. 发送给 LLM 生成回答

检索阶段流程图:


💻 RAG 实战案例:Java 8 文档问答

📄 步骤一:准备资料

首先准备一份 PDF 文档作为知识来源——《Java 8 从入门到精通》。

我们的目标是:投喂这个 PDF,让大模型回答文档中的问题


🏗️ 步骤二:创建项目结构

创建标准的 Spring Boot 项目结构:

pom.xml 依赖配置

核心依赖包括:

  • Spring Boot Web
  • LangChain4j 核心库
  • 阿里百炼平台(DashScope)集成
  • Qdrant 向量数据库
  • Easy RAG 模式
  • AllMiniLmL6V2 本地嵌入模型
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.whc</groupId> <artifactId>langChain4j-whc</artifactId> <version>1.0-SNAPSHOT</version> </parent> <artifactId>langchain4j-whc-rag</artifactId> <properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- LangChain4j 核心 --> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-open-ai</artifactId> </dependency> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j</artifactId> </dependency> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-reactor</artifactId> </dependency> <!-- 阿里百炼平台 --> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-community-dashscope-spring-boot-starter</artifactId> </dependency> <!-- 工具库 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.22</version> </dependency> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-spring-boot-starter</artifactId> </dependency> <!-- Qdrant 向量数据库 --> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-qdrant</artifactId> <version>1.5.0-beta11</version> </dependency> <!-- Easy RAG 模式 --> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-easy-rag</artifactId> </dependency> <!-- AllMiniLmL6V2 本地嵌入模型 --> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-embeddings-all-minilm-l6-v2</artifactId> <version>1.5.0-beta11</version> </dependency> </dependencies></project>

📂注意:将 PDF 文件放到resources/doc目录下。


⚙️ 步骤三:YAML 配置

配置服务端口、DashScope API 和 Qdrant 连接信息:

server: port: 9009 servlet: encoding: enabled: true force: true charset: UTF-8spring: application: name: langchain_whc_ragai: dashScope: apiKey: ${AI_DASHSCOPE_API_KEY} modelName: ${AI_DASHSCOPE_MODEL_NAME} baseUrl: ${AI_DASHSCOPE_BASE_URL}qdrant: host: ${QDRANT_HOST} port: ${QDRANT_PORT} collectionName: ${QDRANT_COLLECTION_NAME}

🔧 步骤四:大模型配置类

这是最核心的配置部分,需要配置三个关键 Bean:

  1. 向量存储实例(Qdrant)
  2. 嵌入模型实例(本地 AllMiniLmL6V2)
  3. 聊天模型实例(DashScope)
  4. 内容检索器实例(重点!)
@Slf4j@Getter@Configurationpublic class LlmConfig { @Value("${ai.dashScope.apiKey}") private String dashScopeApiKey; @Value("${ai.dashScope.modelName}") private String dashScopeModelName; @Value("${ai.dashScope.baseUrl}") private String dashScopeBaseUrl; @Value("${qdrant.host}") private String qdrantHost; @Value("${qdrant.port}") private int qdrantPort; @Value("${qdrant.collectionName}") private String qdrantCollectionName; /** * 创建向量存储实例 * 使用 Qdrant 作为向量数据库 */ @Bean public EmbeddingStore<TextSegment> embeddingStore() { return QdrantEmbeddingStore.builder() .host(qdrantHost) .port(qdrantPort) .collectionName(qdrantCollectionName) .build(); } /** * 创建嵌入模型实例 * 使用本地的 AllMiniLmL6V2 嵌入模型,避免 DashScope 兼容性问题 */ @Bean public EmbeddingModel embeddingModel() { return new dev.langchain4j.model.embedding.onnx.allminilml6v2.AllMiniLmL6V2EmbeddingModel(); } /** * 创建聊天模型实例 * 使用 OpenAI 兼容的聊天模型,通过 DashScope 提供的 API 服务进行对话 */ @Bean public ChatModel chatModel() { return OpenAiChatModel.builder() .apiKey(dashScopeApiKey) .modelName(dashScopeModelName) .baseUrl(dashScopeBaseUrl) .logRequests(true) .build(); } /** * 创建内容检索器实例 * ⚠️ 重点:必须明确指定嵌入模型,避免冲突! */ @Bean public ContentRetriever contentRetriever(EmbeddingStore<TextSegment> embeddingStore, EmbeddingModel embeddingModel) { return EmbeddingStoreContentRetriever.builder() .embeddingStore(embeddingStore) .embeddingModel(embeddingModel) .build(); }}

🤖 步骤五:定义 AI 服务接口

使用@AiService注解,显式指定要使用的模型和检索器:

@AiService(wiringMode = AiServiceWiringMode.EXPLICIT, chatModel = "chatModel", contentRetriever = "contentRetriever")public interface RagAssist { /** * 聊天接口 * @param message 用户问题 * @return AI 回答 */ String chat(String message);}

🌐 步骤六:Controller 实现

实现 REST API,完成文档解析、嵌入存储和问答的完整流程:

@RestController@RequiredArgsConstructorpublic class RagController { private final EmbeddingModel embeddingModel; private final EmbeddingStore<TextSegment> embeddingStore; private final RagAssist ragAssist; @GetMapping(value = "/rag/chat") public String java8(@RequestParam String message) throws IOException { // 1. 读取并解析 PDF 文档 ClassPathResource resource = new ClassPathResource("doc/java8从入门到精通.pdf"); InputStream inputStream = resource.getInputStream(); Document document = new ApacheTikaDocumentParser().parse(inputStream); // 2. 创建文档嵌入存储处理器 EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder() .documentSplitter(DocumentSplitters.recursive(1000, 0)) .embeddingStore(embeddingStore) .embeddingModel(embeddingModel) .build(); // 3. 将文档处理并存储到向量数据库 ingestor.ingest(document); // 4. 使用 RAG 助手回答问题 String result = ragAssist.chat(message); System.out.println(result); return result; }}

✅ 测试效果验证

测试一:询问第九章内容

文档第九章是关于「本地时间和时间戳」的内容:

接口调用:

GET http://localhost:9009/rag/chat?message=第九章主要讲了啥 ```![](http://cdn.zhipoai.cn/6cd51e2d.jpg) **AI 回答:** > “ > > 第九章主要讲述了本地时间和时间戳的相关概念。内容包括如何在程序中处理不同地区的本地时间,时区的转换,以及时间戳(Timestamp)的定义和使用方法。本章还介绍了如何将时间戳与本地时间进行相互转换,帮助开发者更好地理解和操作时间数据,确保在跨时区应用中的时间一致性与准确性。 🎉 **完美!达到预期效果!** --- ### 测试二:询问 Stream 创建方法 **接口调用:** ```plaintext GET http://localhost:9009/rag/chat?message=如何创建Stream ```![](http://cdn.zhipoai.cn/9fdbbbb6.jpg) **调用结果:** ![](http://cdn.zhipoai.cn/4a3b079b.jpg) 很显然,AI 回答参考了文档中的内容,并明确说明了对应的章节来源。 ✅ **再次验证成功!** --- ### 📊 向量数据库存储效果 文档被分割成了一个又一个的 `text_segment` 存储在 Qdrant 中: ![](http://cdn.zhipoai.cn/c5ab4da4.jpg) --- ⚠️ 踩坑记录与解决方案 ------------ ### 坑一:多个嵌入模型冲突 **问题:**配置内容检索器时,如果不明确指定 model,会报错: ```plaintext Conflict: multiple embedding models have been found in the classpath

解决方案:EmbeddingStoreContentRetriever必须显式指定要使用的嵌入模型:

@Beanpublic ContentRetriever contentRetriever(EmbeddingStore<TextSegment> embeddingStore, EmbeddingModel embeddingModel) { return EmbeddingStoreContentRetriever.builder() .embeddingStore(embeddingStore) .embeddingModel(embeddingModel) // ✅ 关键:明确指定模型 .build();}

坑二:DashScope 兼容性问题

**问题:**尝试使用百炼平台的向量模型(TEXT-EMBEDDING-V4)时,报 OpenAI 不兼容异常。

**解决方案:**改用本地的AllMiniLmL6V2EmbeddingModel嵌入模型,完美避开兼容性问题:

@Beanpublic EmbeddingModel embeddingModel() { // ✅ 使用本地嵌入模型,避免兼容性问题 return new dev.langchain4j.model.embedding.onnx.allminilml6v2.AllMiniLmL6V2EmbeddingModel();}

有技术底子的人,正站在AI大模型开发的黄金入口

先问自己一个问题:

你写了这么多年代码,薪资是不是已经很久没动了?

面试的时候,“会Spring Boot”“会Vue”"会MySQL"已经变成了基本操作,没有人在乎了。大家都会的东西,就不值钱了。

但另一边,有人在疯狂涨薪
拉勾、BOSS直聘上,“AI应用开发”“大模型开发”"Agent开发"的岗位数量在过去一年翻了3倍,薪资中位数比同级别后端开发高出 40%-60%。

不是因为他们比你聪明,而是因为他们踩对了赛道。

你可能觉得:我又不是搞算法的,大模型跟我有什么关系?

这就是最大的误区。

AI大模型应用开发 ≠ 训练大模型
说清楚一点:训练大模型的是那几家大厂,但用大模型做应用的,是千千万万的普通企业和团队。

而这些团队需要的,不是PhD,而是——

能用大模型API搭出可用产品的应用开发者
能设计Agent工作流、调用工具链的Agent工程师
能把RAG、Function Calling、多轮对话落地到真实业务的AI全栈
这些活儿,有编程基础的你,完全能干。

你需要补的不是"算法基础",而是"AI开发的技术栈和工程思维"。

Agent开发,为什么是程序员最好的切入点?
因为Agent开发本质上就是"用自然语言编程"——而这恰恰需要你已有的工程能力:

你有代码功底 → 理解Function Calling、工具调用、API集成,比零基础快10倍
你有系统设计经验 → 设计多Agent协作架构、状态管理、错误处理,逻辑一脉相承
你懂工程化 → 部署、监控、性能优化,这些AI项目同样需要
你理解数据 → RAG系统的数据清洗、向量检索、效果调优,你的DB经验直接复用
说白了,你已有的能力是资产,不是沉没成本。差的只是"AI这一层"的认知和工具链。

学完之后,你值多少钱?
转型 从传统后端/前端转AI应用开发,打开薪资天花板,跳槽议价权拉满
升职 在现有团队主导AI项目落地,从"写代码的"变成"定方向的"
独立 用Agent开发能力做SaaS产品、接AI外包项目,技术变现多一条腿
不可替代 当AI能写CRUD了,你是那个"用AI写代码"的人,而不是"被AI替代"的人
这不是危言耸听。GitHub Copilot已经能写出70%的CRUD代码了,纯执行层面的程序员价值在快速缩水。但"能用AI构建AI应用"的人,目前严重不够用。

这门课会教你什么?
面向有编程基础的开发者,从AI大模型应用开发的工程实践出发:

✅ 大模型API调用与Prompt工程实战
✅ RAG系统搭建:从数据处理到向量检索全流程
✅ Agent开发:Function Calling、工具链、多步推理
✅ 多Agent协作与工作流编排
✅ 真实项目落地:从需求到部署的完整工程链路
不讲虚的,全是能直接用在项目里的东西。

🚀 AI大模型应用开发课程

有编程基础?这就是你的下一个赛道

“程序员最大的风险,不是技术过时,而是用旧技术赚新钱的心态。”

你可能还在想"再等等看"——但AI这个赛道,窗口期就这么长。

等大模型开发变成"标配技能"的时候,你就不是先行者了,而是追赶者。

你有技术底子,这是你最大的优势。别浪费它。