Java开发者怎么做RAG:Spring AI和LangChain4j双框架对比
你如果是个Java开发者,可能心里一直在犯嘀咕:“难道做RAG必须学Python?我Spring Boot的祖传手艺就不能用了?”
当然不是。Java生态在2025-2026年已经补上了RAG的课——Spring AI 2.0正式发布了VectorStore和RAG Advisor,LangChain4j 1.1.0也提供了完整的EmbeddingStore和RetrievalAugmentor。两个框架都能让你用纯Java代码把RAG跑起来。
这篇我把前10篇的Python概念映射到Java实现,对比Spring AI和LangChain4j两种方案的差异,帮你选对框架,少走弯路。
先理清一个关系:Python RAG概念到Java的映射
你可能在Python文章里见过这些组件,到了Java里叫什么?先看一张映射表:
| Python(LangChain)
|
Spring AI 2.0
|
LangChain4j 1.1.0
|
作用
TextSplitter
|
DocumentTransformer
|
DocumentSplitter
|
文档切分
| |
Embeddings
|
EmbeddingModel
|
EmbeddingModel
|
文本转向量
| |
VectorStore
|
VectorStore
|
EmbeddingStore
|
向量存储和检索
| |
Retriever
|
VectorStore.similaritySearch
|
EmbeddingStore.search
|
检索接口
| |
RetrievalQA
|
QuestionAnswerAdvisor
|
AiServices + RAG
|
检索+生成组合
| |
BM25Retriever
|
无内置
|
无内置
|
需自己实现或接Elasticsearch
| |
ContextualCompressionRetriever
|
无内置
|
无内置
|
需手动实现
|
你看出来没有?Java框架的抽象层次跟LangChain基本对齐了,但高级RAG(混合检索、Rerank、查询改写)需要你用更底层的能力自己拼。这也符合Java开发者的习惯——给你积木,你自己搭。
Java类比:就像Spring Data JPA给你Repository接口,MyBatis给你Mapper接口,两个框架都给你RAG的核心抽象(VectorStore/EmbeddingStore),但具体实现细节你需要自己控制。Spring AI的思路是"约定优于配置"(像Spring Boot的auto-configuration),LangChain4j的思路是"显式组合"(像Builder模式)。
Spring AI 2.0:Spring Boot原生RAG方案
依赖配置
Spring AI 2.0的Maven依赖,注意artifactId从milestone到GA有变化:
<span><span leaf=""><</span><span><span leaf="">dependencies</span></span><span leaf="">></span></span><br><span leaf=""> </span><span><span leaf=""><!-- Spring AI核心:OpenAI模型(含Chat和Embedding) --></span></span><br><span leaf=""> </span><span><span leaf=""><</span><span><span leaf="">dependency</span></span><span leaf="">></span></span><br><span leaf=""> </span><span><span leaf=""><</span><span><span leaf="">groupId</span></span><span leaf="">></span></span><span leaf="">org.springframework.ai</span><span><span leaf=""></</span><span><span leaf="">groupId</span></span><span leaf="">></span></span><br><span leaf=""> </span><span><span leaf=""><</span><span><span leaf="">artifactId</span></span><span leaf="">></span></span><span leaf="">spring-ai-starter-model-openai</span><span><span leaf=""></</span><span><span leaf="">artifactId</span></span><span leaf="">></span></span><br><span leaf=""> </span><span><span leaf=""></</span><span><span leaf="">dependency</span></span><span leaf="">></span></span><br><br><span leaf=""> </span><span><span leaf=""><!-- 向量库:选一个就行 --></span></span><br><span leaf=""> </span><span><span leaf=""><</span><span><span leaf="">dependency</span></span><span leaf="">></span></span><br><span leaf=""> </span><span><span leaf=""><</span><span><span leaf="">groupId</span></span><span leaf="">></span></span><span leaf="">org.springframework.ai</span><span><span leaf=""></</span><span><span leaf="">groupId</span></span><span leaf="">></span></span><br><span leaf=""> </span><span><span leaf=""><</span><span><span leaf="">artifactId</span></span><span leaf="">></span></span><span leaf="">spring-ai-starter-vector-store-pgvector</span><span><span leaf=""></</span><span><span leaf="">artifactId</span></span><span leaf="">></span></span><br><span leaf=""> </span><span><span leaf=""></</span><span><span leaf="">dependency</span></span><span leaf="">></span></span><br><br><span leaf=""> </span><span><span leaf=""><!-- 文档读取:PDF --></span></span><br><span leaf=""> </span><span><span leaf=""><</span><span><span leaf="">dependency</span></span><span leaf="">></span></span><br><span leaf=""> </span><span><span leaf=""><</span><span><span leaf="">groupId</span></span><span leaf="">></span></span><span leaf="">org.springframework.ai</span><span><span leaf=""></</span><span><span leaf="">groupId</span></span><span leaf="">></span></span><br><span leaf=""> </span><span><span leaf=""><</span><span><span leaf="">artifactId</span></span><span leaf="">></span></span><span leaf="">spring-ai-pdf-document-reader</span><span><span leaf=""></</span><span><span leaf="">artifactId</span></span><span leaf="">></span></span><br><span leaf=""> </span><span><span leaf=""></</span><span><span leaf="">dependency</span></span><span leaf="">></span></span><br><br><span leaf=""> </span><span><span leaf=""><!-- 文档读取:Tika(Word、HTML等) --></span></span><br><span leaf=""> </span><span><span leaf=""><</span><span><span leaf="">dependency</span></span><span leaf="">></span></span><br><span leaf=""> </span><span><span leaf=""><</span><span><span leaf="">groupId</span></span><span leaf="">></span></span><span leaf="">org.springframework.ai</span><span><span leaf=""></</span><span><span leaf="">groupId</span></span><span leaf="">></span></span><br><span leaf=""> </span><span><span leaf=""><</span><span><span leaf="">artifactId</span></span><span leaf="">></span></span><span leaf="">spring-ai-tika-document-reader</span><span><span leaf=""></</span><span><span leaf="">artifactId</span></span><span leaf="">></span></span><br><span leaf=""> </span><span><span leaf=""></</span><span><span leaf="">dependency</span></span><span leaf="">></span></span><br><span><span leaf=""></</span><span><span leaf="">dependencies</span></span><span leaf="">></span></span><br>
配置文件:
<span><span leaf="">spring:</span></span><br><span leaf=""> </span><span><span leaf="">ai:</span></span><br><span leaf=""> </span><span><span leaf="">openai:</span></span><br><span leaf=""> </span><span><span leaf="">api-key:</span></span><span leaf=""> </span><span><span leaf="">${OPENAI_API_KEY}</span></span><br><span leaf=""> </span><span><span leaf="">chat:</span></span><br><span leaf=""> </span><span><span leaf="">options:</span></span><br><span leaf=""> </span><span><span leaf="">model:</span></span><span leaf=""> </span><span><span leaf="">gpt-4o</span></span><br><span leaf=""> </span><span><span leaf="">embedding:</span></span><br><span leaf=""> </span><span><span leaf="">options:</span></span><br><span leaf=""> </span><span><span leaf="">model:</span></span><span leaf=""> </span><span><span leaf="">text-embedding-3-small</span></span><br><span leaf=""> </span><span><span leaf="">vectorstore:</span></span><br><span leaf=""> </span><span><span leaf="">pgvector:</span></span><br><span leaf=""> </span><span><span leaf="">dimensions:</span></span><span leaf=""> </span><span><span leaf="">1536</span></span><br><span leaf=""> </span><span><span leaf="">distance-type:</span></span><span leaf=""> </span><span><span leaf="">cosine_distance</span></span><br>
阶段1:文档ETL(提取→转换→加载)
Spring AI把文档处理抽象成ETL流水线——跟你在Java里用的Spring Batch一个思路:
<span><span leaf="">@Configuration</span></span><br><span><span leaf="">public</span></span><span leaf=""> </span><span><span><span leaf="">class</span></span><span leaf=""> </span><span><span leaf="">RagConfig</span></span><span leaf=""> </span></span><span leaf="">{</span><br><br><span leaf=""> </span><span><span leaf="">@Bean</span></span><br><span leaf=""> </span><span><span><span leaf="">public</span></span><span leaf=""> DocumentReader </span><span><span leaf="">pdfReader</span></span><span><span leaf="">()</span></span><span leaf=""> </span></span><span leaf="">{</span><br><span leaf=""> </span><span><span leaf="">// 读取PDF文档,返回Document列表</span></span><br><span leaf=""> </span><span><span leaf="">return</span></span><span leaf=""> </span><span><span leaf="">new</span></span><span leaf=""> PagePdfDocumentReader(</span><span><span leaf="">"classpath:company-handbook.pdf"</span></span><span leaf="">);</span><br><span leaf=""> }</span><br><br><span leaf=""> </span><span><span leaf="">@Bean</span></span><br><span leaf=""> </span><span><span><span leaf="">public</span></span><span leaf=""> DocumentTransformer </span><span><span leaf="">documentSplitter</span></span><span><span leaf="">()</span></span><span leaf=""> </span></span><span leaf="">{</span><br><span leaf=""> </span><span><span leaf="">// 递归切分,token大小800,重叠100</span></span><br><span leaf=""> </span><span><span leaf="">// 对应02篇讲的"递归字符切分"策略</span></span><br><span leaf=""> </span><span><span leaf="">return</span></span><span leaf=""> TokenTextSplitter.builder()</span><br><span leaf=""> .chunkSize(</span><span><span leaf="">800</span></span><span leaf="">)</span><br><span leaf=""> .minChunkSizeChars(</span><span><span leaf="">350</span></span><span leaf="">)</span><br><span leaf=""> .minChunkLengthToEmbed(</span><span><span leaf="">5</span></span><span leaf="">)</span><br><span leaf=""> .maxNumChunks(</span><span><span leaf="">10000</span></span><span leaf="">)</span><br><span leaf=""> .keepSeparator(</span><span><span leaf="">true</span></span><span leaf="">)</span><br><span leaf=""> .build();</span><br><span leaf=""> }</span><br><br><span leaf=""> </span><span><span leaf="">@Bean</span></span><br><span leaf=""> </span><span><span><span leaf="">public</span></span><span leaf=""> VectorStore </span><span><span leaf="">vectorStore</span></span><span><span leaf="">(EmbeddingModel embeddingModel, JdbcTemplate jdbcTemplate)</span></span><span leaf=""> </span></span><span leaf="">{</span><br><span leaf=""> </span><span><span leaf="">return</span></span><span leaf=""> PgVectorStore.builder(jdbcTemplate, embeddingModel)</span><br><span leaf=""> .dimensions(</span><span><span leaf="">1536</span></span><span leaf="">)</span><br><span leaf=""> .distanceType(PgVectorStore.PgDistanceType.COSINE_DISTANCE)</span><br><span leaf=""> .table_name(</span><span><span leaf="">"rag_vectors"</span></span><span leaf="">)</span><br><span leaf=""> .build();</span><br><span leaf=""> }</span><br><span leaf="">}</span><br>
ETL执行——把PDF切分后存入向量库:
<span><span leaf="">@Service</span></span><br><span><span leaf="">public</span></span><span leaf=""> </span><span><span><span leaf="">class</span></span><span leaf=""> </span><span><span leaf="">DocumentIngestionService</span></span><span leaf=""> </span></span><span leaf="">{</span><br><br><span leaf=""> </span><span><span leaf="">private</span></span><span leaf=""> </span><span><span leaf="">final</span></span><span leaf=""> DocumentReader documentReader;</span><br><span leaf=""> </span><span><span leaf="">private</span></span><span leaf=""> </span><span><span leaf="">final</span></span><span leaf=""> DocumentTransformer documentTransformer;</span><br><span leaf=""> </span><span><span leaf="">private</span></span><span leaf=""> </span><span><span leaf="">final</span></span><span leaf=""> VectorStore vectorStore;</span><br><br><span leaf=""> </span><span><span><span leaf="">public</span></span><span leaf=""> </span><span><span leaf="">DocumentIngestionService</span></span><span><span leaf="">(DocumentReader documentReader,</span><br><span leaf=""> DocumentTransformer documentTransformer,</span><br><span leaf=""> VectorStore vectorStore)</span></span><span leaf=""> </span></span><span leaf="">{</span><br><span leaf=""> </span><span><span leaf="">this</span></span><span leaf="">.documentReader = documentReader;</span><br><span leaf=""> </span><span><span leaf="">this</span></span><span leaf="">.documentTransformer = documentTransformer;</span><br><span leaf=""> </span><span><span leaf="">this</span></span><span leaf="">.vectorStore = vectorStore;</span><br><span leaf=""> }</span><br><br><span leaf=""> </span><span><span><span leaf="">public</span></span><span leaf=""> </span><span><span leaf="">void</span></span><span leaf=""> </span><span><span leaf="">ingestPdf</span></span><span><span leaf="">(String pdfPath)</span></span><span leaf=""> </span></span><span leaf="">{</span><br><span leaf=""> </span><span><span leaf="">// 1. 读取PDF → Document列表</span></span><br><span leaf=""> List<Document> docs = </span><span><span leaf="">new</span></span><span leaf=""> PagePdfDocumentReader(pdfPath).get();</span><br><br><span leaf=""> </span><span><span leaf="">// 2. 切分文档 → 更小的Document列表</span></span><br><span leaf=""> List<Document> chunks = documentTransformer.apply(docs);</span><br><br><span leaf=""> </span><span><span leaf="">// 3. 批量向量化并存储(VectorStore内部调EmbeddingModel)</span></span><br><span leaf=""> vectorStore.add(chunks);</span><br><br><span leaf=""> System.out.println(</span><span><span leaf="">"导入完成,共"</span></span><span leaf=""> + chunks.size() + </span><span><span leaf="">"个chunk"</span></span><span leaf="">);</span><br><span leaf=""> }</span><br><span leaf="">}</span><br>
Java类比:这段代码的结构跟Spring Batch的ItemReader→ItemProcessor→ItemWriter一模一样。DocumentReader就是ItemReader,DocumentTransformer就是ItemProcessor,VectorStore就是ItemWriter。
阶段2+3:检索+生成
Spring AI的RAG核心是QuestionAnswerAdvisor——一个Advisor,自动在用户提问时检索相关文档并拼入提示:
<span><span leaf="">@RestController</span></span><br><span><span leaf="">@RequestMapping</span></span><span leaf="">(</span><span><span leaf="">"/api/rag"</span></span><span leaf="">)</span><br><span><span leaf="">public</span></span><span leaf=""> </span><span><span><span leaf="">class</span></span><span leaf=""> </span><span><span leaf="">RagController</span></span><span leaf=""> </span></span><span leaf="">{</span><br><br><span leaf=""> </span><span><span leaf="">private</span></span><span leaf=""> </span><span><span leaf="">final</span></span><span leaf=""> ChatClient chatClient;</span><br><br><span leaf=""> </span><span><span><span leaf="">public</span></span><span leaf=""> </span><span><span leaf="">RagController</span></span><span><span leaf="">(ChatClient.Builder builder, VectorStore vectorStore)</span></span><span leaf=""> </span></span><span leaf="">{</span><br><span leaf=""> </span><span><span leaf="">this</span></span><span leaf="">.chatClient = builder</span><br><span leaf=""> .defaultAdvisors(</span><span><span leaf="">new</span></span><span leaf=""> QuestionAnswerAdvisor(vectorStore))</span><br><span leaf=""> .build();</span><br><span leaf=""> }</span><br><br><span leaf=""> </span><span><span leaf="">@PostMapping</span></span><span leaf="">(</span><span><span leaf="">"/chat"</span></span><span leaf="">)</span><br><span leaf=""> </span><span><span><span leaf="">public</span></span><span leaf=""> String </span><span><span leaf="">chat</span></span><span><span leaf="">(@RequestBody String question)</span></span><span leaf=""> </span></span><span leaf="">{</span><br><span leaf=""> </span><span><span leaf="">return</span></span><span leaf=""> chatClient.prompt()</span><br><span leaf=""> .user(question)</span><br><span leaf=""> .call()</span><br><span leaf=""> .content();</span><br><span leaf=""> }</span><br><span leaf="">}</span><br>
就这么几行,RAG就跑起来了。QuestionAnswerAdvisor做了什么?它拦截用户的问题,先调vectorStore做相似检索,再把检索到的文档拼到系统提示里,最后发给LLM。
进阶:自定义检索策略
默认的QuestionAnswerAdvisor只做简单的相似检索。你想做02篇讲的混合检索(BM25+向量),需要自定义SearchRequest:
<span><span leaf="">// 自定义检索:topK=5,相似度阈值0.7</span></span><br><span leaf="">SearchRequest searchRequest = SearchRequest.builder()</span><br><span leaf=""> .query(question)</span><br><span leaf=""> .topK(</span><span><span leaf="">5</span></span><span leaf="">)</span><br><span leaf=""> .similarityThreshold(</span><span><span leaf="">0.7</span></span><span leaf="">)</span><br><span leaf=""> .filterExpression(</span><span><span leaf="">"source == 'company-handbook'"</span></span><span leaf="">) </span><span><span leaf="">// 元数据过滤</span></span><br><span leaf=""> .build();</span><br><br><span><span leaf="">// 手动检索</span></span><br><span leaf="">List<Document> docs = vectorStore.similaritySearch(searchRequest);</span><br><br><span><span leaf="">// 把检索结果拼入提示</span></span><br><span leaf="">String context = docs.stream()</span><br><span leaf=""> .map(Document::getText)</span><br><span leaf=""> .collect(Collectors.joining(</span><span><span leaf="">"\n\n"</span></span><span leaf="">));</span><br><br><span leaf="">String response = chatClient.prompt()</span><br><span leaf=""> .system(</span><span><span leaf="">"基于以下文档回答用户问题,如果文档中没有答案就说'未找到相关内容':\n\n"</span></span><span leaf=""> + context)</span><br><span leaf=""> .user(question)</span><br><span leaf=""> .call()</span><br><span leaf=""> .content();</span><br>
Java类比:这就是Spring里常用的"模板方法模式"——框架给你默认实现,你不满意就覆盖某个步骤。QuestionAnswerAdvisor是默认模板,手动检索+自定义提示是你覆盖了模板。
LangChain4j 1.1.0:更灵活的RAG方案
LangChain4j的RAG抽象更细致,适合需要精确控制每一步的场景。
依赖配置
<span><span leaf=""><</span><span><span leaf="">dependencies</span></span><span leaf="">></span></span><br><span leaf=""> </span><span><span leaf=""><!-- LangChain4j核心 --></span></span><br><span leaf=""> </span><span><span leaf=""><</span><span><span leaf="">dependency</span></span><span leaf="">></span></span><br><span leaf=""> </span><span><span leaf=""><</span><span><span leaf="">groupId</span></span><span leaf="">></span></span><span leaf="">dev.langchain4j</span><span><span leaf=""></</span><span><span leaf="">groupId</span></span><span leaf="">></span></span><br><span leaf=""> </span><span><span leaf=""><</span><span><span leaf="">artifactId</span></span><span leaf="">></span></span><span leaf="">langchain4j</span><span><span leaf=""></</span><span><span leaf="">artifactId</span></span><span leaf="">></span></span><br><span leaf=""> </span><span><span leaf=""><</span><span><span leaf="">version</span></span><span leaf="">></span></span><span leaf="">1.0.0</span><span><span leaf=""></</span><span><span leaf="">version</span></span><span leaf="">></span></span><br><span leaf=""> </span><span><span leaf=""></</span><span><span leaf="">dependency</span></span><span leaf="">></span></span><br><br><span leaf=""> </span><span><span leaf=""><!-- OpenAI集成 --></span></span><br><span leaf=""> </span><span><span leaf=""><</span><span><span leaf="">dependency</span></span><span leaf="">></span></span><br><span leaf=""> </span><span><span leaf=""><</span><span><span leaf="">groupId</span></span><span leaf="">></span></span><span leaf="">dev.langchain4j</span><span><span leaf=""></</span><span><span leaf="">groupId</span></span><span leaf="">></span></span><br><span leaf=""> </span><span><span leaf=""><</span><span><span leaf="">artifactId</span></span><span leaf="">></span></span><span leaf="">langchain4j-open-ai</span><span><span leaf=""></</span><span><span leaf="">artifactId</span></span><span leaf="">></span></span><br><span leaf=""> </span><span><span leaf=""><</span><span><span leaf="">version</span></span><span leaf="">></span></span><span leaf="">1.0.0</span><span><span leaf=""></</span><span><span leaf="">version</span></span><span leaf="">></span></span><br><span leaf=""> </span><span><span leaf=""></</span><span><span leaf="">dependency</span></span><span leaf="">></span></span><br><br><span leaf=""> </span><span><span leaf=""><!-- 向量库:选一个 --></span></span><br><span leaf=""> </span><span><span leaf=""><</span><span><span leaf="">dependency</span></span><span leaf="">></span></span><br><span leaf=""> </span><span><span leaf=""><</span><span><span leaf="">groupId</span></span><span leaf="">></span></span><span leaf="">dev.langchain4j</span><span><span leaf=""></</span><span><span leaf="">groupId</span></span><span leaf="">></span></span><br><span leaf=""> </span><span><span leaf=""><</span><span><span leaf="">artifactId</span></span><span leaf="">></span></span><span leaf="">langchain4j-pgvector</span><span><span leaf=""></</span><span><span leaf="">artifactId</span></span><span leaf="">></span></span><br><span leaf=""> </span><span><span leaf=""><</span><span><span leaf="">version</span></span><span leaf="">></span></span><span leaf="">1.0.0</span><span><span leaf=""></</span><span><span leaf="">version</span></span><span leaf="">></span></span><br><span leaf=""> </span><span><span leaf=""></</span><span><span leaf="">dependency</span></span><span leaf="">></span></span><br><br><span leaf=""> </span><span><span leaf=""><!-- 文档加载器 --></span></span><br><span leaf=""> </span><span><span leaf=""><</span><span><span leaf="">dependency</span></span><span leaf="">></span></span><br><span leaf=""> </span><span><span leaf=""><</span><span><span leaf="">groupId</span></span><span leaf="">></span></span><span leaf="">dev.langchain4j</span><span><span leaf=""></</span><span><span leaf="">groupId</span></span><span leaf="">></span></span><br><span leaf=""> </span><span><span leaf=""><</span><span><span leaf="">artifactId</span></span><span leaf="">></span></span><span leaf="">langchain4j-document-loaders</span><span><span leaf=""></</span><span><span leaf="">artifactId</span></span><span leaf="">></span></span><br><span leaf=""> </span><span><span leaf=""><</span><span><span leaf="">version</span></span><span leaf="">></span></span><span leaf="">1.0.0</span><span><span leaf=""></</span><span><span leaf="">version</span></span><span leaf="">></span></span><br><span leaf=""> </span><span><span leaf=""></</span><span><span leaf="">dependency</span></span><span leaf="">></span></span><br><span><span leaf=""></</span><span><span leaf="">dependencies</span></span><span leaf="">></span></span><br>
文档切分与向量化
<span><span leaf="">// 1. 加载文档</span></span><br><span leaf="">Document document = FileSystemDocumentLoader.loadDocument(</span><span><span leaf="">"company-handbook.pdf"</span></span><span leaf="">);</span><br><br><span><span leaf="">// 2. 切分——对应02篇的递归切分策略</span></span><br><span leaf="">DocumentSplitter splitter = DocumentSplitters.recursive(</span><span><span leaf="">800</span></span><span leaf="">, </span><span><span leaf="">100</span></span><span leaf="">);</span><br><span leaf="">List<TextSegment> segments = splitter.split(document);</span><br><br><span><span leaf="">// 3. 向量化</span></span><br><span leaf="">OpenAiEmbeddingModel embeddingModel = OpenAiEmbeddingModel.builder()</span><br><span leaf=""> .apiKey(System.getenv(</span><span><span leaf="">"OPENAI_API_KEY"</span></span><span leaf="">))</span><br><span leaf=""> .modelName(</span><span><span leaf="">"text-embedding-3-small"</span></span><span leaf="">)</span><br><span leaf=""> .build();</span><br><br><span leaf="">Response<List<Embedding>> embeddings = embeddingModel.embedAll(</span><br><span leaf=""> segments.stream().map(TextSegment::text).toList()</span><br><span leaf="">);</span><br><br><span><span leaf="">// 4. 存入向量库</span></span><br><span leaf="">PgVectorEmbeddingStore embeddingStore = PgVectorEmbeddingStore.builder()</span><br><span leaf=""> .host(</span><span><span leaf="">"localhost"</span></span><span leaf="">)</span><br><span leaf=""> .port(</span><span><span leaf="">5432</span></span><span leaf="">)</span><br><span leaf=""> .database(</span><span><span leaf="">"rag"</span></span><span leaf="">)</span><br><span leaf=""> .table(</span><span><span leaf="">"documents"</span></span><span leaf="">)</span><br><span leaf=""> .dimension(</span><span><span leaf="">1536</span></span><span leaf="">)</span><br><span leaf=""> .build();</span><br><br><span leaf="">embeddingStore.addAll(embeddings.content(), segments);</span><br>
检索+生成:AiServices方式
LangChain4j用AiServices把RAG封装成接口调用——像MyBatis的Mapper代理一样优雅:
<span><span leaf="">// 定义接口(类似MyBatis的Mapper)</span></span><br><span><span><span leaf="">interface</span></span><span leaf=""> </span><span><span leaf="">KnowledgeBaseAssistant</span></span><span leaf=""> </span></span><span leaf="">{</span><br><br><span leaf=""> </span><span><span leaf="">@SystemMessage</span></span><span leaf="">(</span><span><span leaf="">"你是一个企业知识库助手,基于检索到的文档回答用户问题。"</span></span><span leaf="">)</span><br><span leaf=""> </span><span><span leaf="">String </span><span><span leaf="">answer</span></span><span><span leaf="">(@UserMessage String question)</span></span></span><span leaf="">;</span><br><span leaf="">}</span><br><br><span><span leaf="">// 配置RAG增强器</span></span><br><span leaf="">RetrievalAugmentor augmentor = DefaultRetrievalAugmentor.builder()</span><br><span leaf=""> .queryTransformer(QueryTransformer.builder()</span><br><span leaf=""> .queryRouter(QueryRouter.builder()</span><br><span leaf=""> .embeddingStore(embeddingStore)</span><br><span leaf=""> .embeddingModel(embeddingModel)</span><br><span leaf=""> .maxResults(</span><span><span leaf="">5</span></span><span leaf="">)</span><br><span leaf=""> .minScore(</span><span><span leaf="">0.7</span></span><span leaf="">)</span><br><span leaf=""> .build())</span><br><span leaf=""> .build())</span><br><span leaf=""> .build();</span><br><br><span><span leaf="">// 创建代理实例</span></span><br><span leaf="">KnowledgeBaseAssistant assistant = AiServices.builder(KnowledgeBaseAssistant</span><span><span leaf="">.</span><span><span leaf="">class</span></span><span leaf="">)</span><br><span leaf=""> .</span><span><span leaf="">chatLanguageModel</span></span><span leaf="">(</span><span><span leaf="">chatModel</span></span><span leaf="">)</span><br><span leaf=""> .</span><span><span leaf="">retrievalAugmentor</span></span><span leaf="">(</span><span><span leaf="">augmentor</span></span><span leaf="">)</span><br><span leaf=""> .</span><span><span leaf="">build</span></span><span leaf="">()</span></span><span leaf="">;</span><br><br><span><span leaf="">// 使用——就像调用普通Java接口</span></span><br><span leaf="">String answer = assistant.answer(</span><span><span leaf="">"公司的报销流程是什么?"</span></span><span leaf="">);</span><br>
Java类比:AiServices的机制跟MyBatis的@MapperProxy一模一样。你定义接口+注解,框架用动态代理在背后注入RAG逻辑。你写的是接口,框架负责检索、拼提示、调模型、解析结果。
两个框架怎么选?
| 维度
|
Spring AI 2.0
|
LangChain4j 1.1.0
与Spring Boot集成
|
原生(auto-configuration)
|
需手动配置Bean
| |
RAG封装层次
|
Advisor(高层次)
|
AiServices(接口代理)
| |
灵活性
|
中等(Advisor可自定义)
|
高(每步可替换)
| |
向量库支持
|
PgVector/Redis/Chroma等
|
30+种(比Spring AI多)
| |
学习曲线
|
Spring开发者上手快
|
需理解LangChain4j的抽象
| |
社区活跃度
|
Spring生态加持,增长快
|
独立项目,社区活跃
| |
适合场景
|
Spring Boot项目
|
独立Java服务
|
我的建议:
-
你的项目已经用了Spring Boot → Spring AI,无缝集成,配置最少
-
你需要精确控制RAG每一步 → LangChain4j,灵活度更高
-
两个都试一下 → 不冲突,可以同时引入
Spring AI vs LangChain4j:代码对比
同样做一个"文档导入+问答"的完整RAG,两个框架的代码量对比:
<span><span leaf="">// === Spring AI方式 ===</span></span><br><span><span leaf="">// 配置(auto-config自动创建ChatClient和VectorStore)</span></span><br><span><span leaf="">// 只需要写Controller</span></span><br><span><span leaf="">@RestController</span></span><br><span><span leaf="">public</span></span><span leaf=""> </span><span><span><span leaf="">class</span></span><span leaf=""> </span><span><span leaf="">RagController</span></span><span leaf=""> </span></span><span leaf="">{</span><br><span leaf=""> </span><span><span leaf="">private</span></span><span leaf=""> </span><span><span leaf="">final</span></span><span leaf=""> ChatClient chatClient;</span><br><span leaf=""> </span><span><span><span leaf="">public</span></span><span leaf=""> </span><span><span leaf="">RagController</span></span><span><span leaf="">(ChatClient.Builder b, VectorStore vs)</span></span><span leaf=""> </span></span><span leaf="">{</span><br><span leaf=""> </span><span><span leaf="">this</span></span><span leaf="">.chatClient = b.defaultAdvisors(</span><span><span leaf="">new</span></span><span leaf=""> QuestionAnswerAdvisor(vs)).build();</span><br><span leaf=""> }</span><br><span leaf=""> </span><span><span leaf="">@PostMapping</span></span><span leaf="">(</span><span><span leaf="">"/ask"</span></span><span leaf="">)</span><br><span leaf=""> </span><span><span><span leaf="">public</span></span><span leaf=""> String </span><span><span leaf="">ask</span></span><span><span leaf="">(@RequestBody String q)</span></span><span leaf=""> </span></span><span leaf="">{</span><br><span leaf=""> </span><span><span leaf="">return</span></span><span leaf=""> chatClient.prompt().user(q).call().content();</span><br><span leaf=""> }</span><br><span leaf="">}</span><br><br><span><span leaf="">// === LangChain4j方式 ===</span></span><br><span><span leaf="">// 需要手动配置EmbeddingStore、RetrievalAugmentor、AiServices</span></span><br><span><span leaf="">@Configuration</span></span><br><span><span leaf="">public</span></span><span leaf=""> </span><span><span><span leaf="">class</span></span><span leaf=""> </span><span><span leaf="">RagConfig</span></span><span leaf=""> </span></span><span leaf="">{</span><br><span leaf=""> </span><span><span leaf="">@Bean</span></span><br><span leaf=""> </span><span><span><span leaf="">public</span></span><span leaf=""> KnowledgeBaseAssistant </span><span><span leaf="">assistant</span></span><span><span leaf="">(ChatModel model, EmbeddingStore store)</span></span><span leaf=""> </span></span><span leaf="">{</span><br><span leaf=""> RetrievalAugmentor augmentor = DefaultRetrievalAugmentor.builder()</span><br><span leaf=""> </span><span><span leaf="">// ... 配置检索流程</span></span><br><span leaf=""> .build();</span><br><span leaf=""> </span><span><span leaf="">return</span></span><span leaf=""> AiServices.builder(KnowledgeBaseAssistant</span><span><span leaf="">.</span><span><span leaf="">class</span></span><span leaf="">)</span><br><span leaf=""> .</span><span><span leaf="">chatLanguageModel</span></span><span leaf="">(</span><span><span leaf="">model</span></span><span leaf="">)</span><br><span leaf=""> .</span><span><span leaf="">retrievalAugmentor</span></span><span leaf="">(</span><span><span leaf="">augmentor</span></span><span leaf="">)</span><br><span leaf=""> .</span><span><span leaf="">build</span></span><span leaf="">()</span></span><span leaf="">;</span><br><span leaf=""> }</span><br><span leaf="">}</span><br>
Spring AI的代码更短,因为Advisor把RAG逻辑封装了;LangChain4j的代码更多,但你能看到每一步在做什么。这跟Spring Data JPA vs MyBatis的区别是一样的。
我踩过的坑
-
Spring AI版本依赖冲突:1.0 GA跟Spring Boot 4.0.x是绑定的,如果你项目用的是3.2.x或3.3.x,要么升级Spring Boot,要么别用Spring AI。版本不匹配会出现auto-configuration失效的诡异问题。
-
LangChain4j的EmbeddingStore没有BM25:两个框架的向量库都只支持向量相似检索,不支持BM25关键词检索。想做04篇讲的混合检索,需要自己集成Elasticsearch或Lucene,手动做RRF融合。
-
Rerank模型缺失:Spring AI和LangChain4j都没有内置Rerank模型。你需要在检索后手动调Cohere Rerank API或本地部署bge-reranker。Spring AI可以在Advisor的before阶段插入Rerank逻辑。
-
中文切分效果差:TokenTextSplitter基于token切分,中文场景下容易在词语中间断开。建议用递归字符切分,LangChain4j的DocumentSplitters.recursive对中文更友好。
-
LangChain4j的queryRouter配置复杂:DefaultRetrievalAugmentor的Builder嵌套很深(augmentor→queryTransformer→queryRouter→embeddingStore),容易写错层级。建议先跑官方demo再改。
本篇要点
| 要点
|
内容
Python→Java映射
|
TextSplitter→DocumentTransformer/DocumentSplitter,VectorStore→VectorStore/EmbeddingStore
| |
Spring AI核心
|
QuestionAnswerAdvisor(自动检索+拼提示),配置最少
| |
LangChain4j核心
|
AiServices+RetrievalAugmentor(接口代理+检索增强),灵活度高
| |
选型原则
|
Spring Boot项目选Spring AI,独立服务选LangChain4j
| |
共同短板
|
都不内置BM25和Rerank,需自己集成
| |
版本要求
|
Spring AI需Spring Boot 4.0+,LangChain4j无限制
|
下篇预告
下一篇我们把前10篇的理论知识+这篇的Java实战串起来,从0到1搭建一个完整的企业知识库问答系统——包括文档批量导入、混合检索、Rerank、评估和部署,给你一个能直接跑的项目骨架。
你用Spring AI还是LangChain4j做RAG?体验怎么样?评论区聊聊。
觉得有用就点个在看,下一篇是从0到1搭建企业知识库问答系统——RAG端到端实战。
- 原文作者:知识铺
- 原文链接:https://index.zshipu.com/ai002/post/20260822/Java%E5%BC%80%E5%8F%91%E8%80%85%E6%80%8E%E4%B9%88%E5%81%9ARAGSpring-AI%E5%92%8CLangChain4j%E5%8F%8C%E6%A1%86%E6%9E%B6%E5%AF%B9%E6%AF%94/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。
- 免责声明:本页面内容均来源于站内编辑发布,部分信息来源互联网,并不意味着本站赞同其观点或者证实其内容的真实性,如涉及版权等问题,请立即联系客服进行更改或删除,保证您的合法权益。转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。也可以邮件至 sblig@126.com