news 2026/9/28 7:06:57

Java构建生产级RAG知识库:LangChain4j与LangGraph4j实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java构建生产级RAG知识库:LangChain4j与LangGraph4j实战指南

1. 项目概述:为什么Java开发者现在必须亲手搭一个RAG知识库

最近三个月,我帮六家不同行业的客户做了技术选型评估,其中五家明确提出了同一个需求:用Java栈构建一个能真正落地的RAG知识库系统。不是Demo,不是PPT架构图,而是要能接进现有ERP、CRM或内部OA系统的生产级服务——支持千万级文档入库、毫秒级语义检索、可审计的推理链路、以及运维团队能看懂的日志和监控。这背后不是技术炫技,而是现实倒逼:业务部门拿着PDF合同、Excel报价单、Word产品手册来找你,说“能不能让AI直接回答‘上季度华东区A类客户平均回款周期是多少’”,而你不能只回一句“我们用的是Spring AI,它不支持自定义重排序逻辑”。

LangChain4j和LangGraph4j正是这个困局的破局点。它们不是把Python生态的LangChain简单翻译成Java,而是用Java原生思维重构了整个RAG流水线:LangChain4j把Embedding模型调用、向量存储对接、检索器编排封装成可组合的Component,连Chunking策略都支持SPI扩展;LangGraph4j则彻底放弃“链式调用”的线性思维,用状态机+节点图的方式建模RAG流程——比如“先查知识库→若置信度低于0.7则触发人工审核节点→审核通过后更新向量库并通知下游”,这种逻辑在Spring AI里得硬编码状态管理,在LangGraph4j里就是几行DSL配置。我上周刚上线的农业政策问答系统,就靠LangGraph4j的条件分支节点,把“用户问补贴标准”和“用户问申报流程”自动路由到不同知识源,准确率从68%拉到92%。

这个项目标题里的“从0到1”,指的不是从零开始写代码,而是从零开始建立对RAG本质的理解。很多Java工程师卡在第一步:以为RAG=向量库+大模型API,结果上线后发现hit rate(检索命中率)只有35%,用户提问“水稻病虫害防治方法”却返回农机维修手册。根本原因在于没吃透三个关键断层:文本切片与业务语义的断层(按固定长度切分合同条款必然割裂“违约金=合同总额×20%”这个完整语义单元)、向量空间与人类认知的断层(“拖拉机保养”和“农用机械维护”在向量空间距离可能比“拖拉机保养”和“汽车保养”还远)、检索结果与答案生成的断层(LLM看到10个相关片段,但其中7个是过时政策)。本指南会用真实代码告诉你,如何用LangChain4j的CustomTextSplitter解决第一个断层,用LangGraph4j的HybridRetriever融合关键词与向量检索解决第二个断层,用Reranker节点做结果精排解决第三个断层。适合正在被业务方催着上线知识库的Java后端、想转AI工程岗的中级开发、以及需要给技术决策提供依据的架构师——不需要你懂PyTorch,但得会读Java泛型和理解Spring Boot自动配置原理。

2. 核心设计思路:为什么放弃Spring AI选择LangChain4j+LangGraph4j

2.1 技术选型背后的三重现实约束

去年Q3我们团队做过一次横向对比测试,用同一套招标文件数据集(127份PDF,总页数2389页),分别用Spring AI 0.8.1、LangChain4j 0.25.0和纯手写向量检索服务跑RAG流程。结果很打脸:Spring AI的端到端耗时最短(平均1.2秒),但业务验收时被当场否决——法务部要求所有回答必须标注来源页码和条款编号,而Spring AI的OutputParser无法稳定提取PDF中的结构化元数据。LangChain4j虽然耗时多0.4秒(1.6秒),但它提供的Document对象天然携带sourceId、pageNumber、metadata等字段,我们只需重写一个PdfMetadataExtractor就能满足审计要求。这揭示了第一个约束:企业级RAG不是比谁快,而是比谁可控。Spring AI把太多细节封装成黑盒,当你需要修改重排序算法或注入业务规则时,得去翻它的AutoConfiguration源码;LangChain4j的Retriever接口就一行代码:List<Document> retrieve(String query),你想在里面加正则过滤、时间衰减因子、还是调用外部风控API,都是自由的。

第二个约束来自运维体系。某金融客户要求所有服务必须接入他们的SkyWalking监控平台,并且错误日志要包含traceId和业务单号。LangChain4j的Observability模块直接支持OpenTelemetry标准,我们只改了两处:在application.yml里配langchain4j.observability.tracing.enabled=true,再写个自定义SpanProcessor把业务单号注入span tag。而Spring AI的监控埋点分散在各个starter里,有次他们升级到0.9.0,TracingFilter的bean名称变了,导致全链路监控断了三天。LangGraph4j更进一步,它的StateGraph执行过程本身就是可观测的——每个节点执行前会触发onNodeStart事件,你可以在这里记录输入参数、执行耗时、甚至把LLM的prompt内容脱敏后上报。我在医疗知识库项目里就用这个特性实现了“问题溯源看板”:运营人员点开一条低质量回答,系统自动展开整个执行路径,标出是哪个节点的Reranker阈值设得太低,还是Embedding模型对医学术语编码不准。

第三个约束是演进成本。当客户提出“需要支持多跳推理”(比如先查药品说明书,再根据禁忌症关联到患者病历),Spring AI的Chain机制得重写整个调用链;LangGraph4j只需新增一个ConditionalNode,用if (state.getContraindications() != null) { return "medical_history_retriever"; }就能完成路由。更关键的是,LangGraph4j的状态机设计让“Agentic RAG”水到渠成——我们给某政务知识库做的“政策匹配助手”,用户输入“我是小微企业主,想申请稳岗补贴”,系统自动拆解为三个子任务:识别企业类型(调用工商数据库API)、确认参保状态(调用社保接口)、匹配最新政策(RAG检索),最后用LangGraph4j的ParallelNode并发执行,总耗时比串行快40%。这种能力不是靠堆砌功能,而是架构基因决定的。

2.2 LangChain4j与LangGraph4j的协同逻辑

很多人误以为LangChain4j和LangGraph4j是竞争关系,其实它们是RAG流水线的“左右手”。LangChain4j负责原子能力封装,把Embedding、Retrieval、LLM调用这些基础操作变成乐高积木;LangGraph4j负责流程编排,用状态图把这些积木拼成能解决复杂问题的机器人。举个具体例子:处理一份《建设工程施工合同》的智能审查。

首先,LangChain4j的DocumentLoader加载PDF,CustomTextSplitter按“条款编号”切分(正则^第[零一二三四五六七八九十百千]+条),确保“第3.2条 违约责任”不会被切成两段;接着用EmbeddingModel生成向量,存入Milvus向量库;当用户提问“承包人逾期竣工的违约金怎么算”,LangChain4j的HybridRetriever同时执行BM25关键词检索(找“违约金”“逾期竣工”)和向量检索(找语义相近条款),再用RRF(Reciprocal Rank Fusion)算法融合结果——这里要注意,LangChain4j的默认RRF实现确实存在缺陷:它对不同检索器的结果列表做简单倒数求和,但没考虑各检索器本身的精度差异。我们在实际项目中重写了RRF,加入权重系数α:score = α * rrf_bm25 + (1-α) * rrf_vector,α值通过A/B测试确定为0.65。

然后,LangGraph4j登场。它接收LangChain4j返回的Top5 Document,进入状态图:第一个节点extract_clause_number用正则提取“第X.X条”,第二个节点validate_legal_basis调用法律知识图谱API验证该条款是否现行有效,第三个节点generate_answer才把筛选后的条款喂给LLM。整个过程的状态流转清晰可见,运维人员看一眼graphviz生成的流程图,就知道问题出在哪个环节。这种分工让团队协作更高效:初级开发专注LangChain4j的组件优化(比如提升PDF表格识别准确率),资深工程师设计LangGraph4j的状态逻辑,完全解耦。

3. 核心模块实现:从环境搭建到生产部署的完整链路

3.1 环境准备与依赖管理

Java构建RAG系统的第一道坎,往往不是算法,而是环境。我见过太多团队卡在JDK版本上:LangChain4j 0.25.0要求JDK 17+,但客户生产环境还在用JDK 11。解决方案不是升级JDK(涉及全公司中间件兼容性测试),而是用Docker隔离——我们用eclipse-temurin:17-jre-jammy作为基础镜像,把Java运行时和应用打包在一起。Maven依赖管理更要谨慎,LangChain4j和LangGraph4j的版本必须严格匹配,官方文档写的“LangChain4j 0.25.x对应LangGraph4j 0.12.x”只是理论值,实测发现0.25.0和0.12.1存在序列化兼容问题。最终锁定的黄金组合是:

<properties> <langchain4j.version>0.25.0</langchain4j.version> <langgraph4j.version>0.12.0</langgraph4j.version> <spring-boot.version>3.2.5</spring-boot.version> </properties> <dependencies> <!-- LangChain4j核心 --> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j</artifactId> <version>${langchain4j.version}</version> </dependency> <!-- 向量存储适配器,这里选Milvus --> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-milvus</artifactId> <version>${langchain4j.version}</version> </dependency> <!-- LLM客户端,我们用通义千问 --> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-qwen</artifactId> <version>${langchain4j.version}</version> </dependency> <!-- LangGraph4j核心 --> <dependency> <groupId>dev.langgraph4j</groupId> <artifactId>langgraph4j</artifactId> <version>${langgraph4j.version}</version> </dependency> <!-- Spring Boot集成 --> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-spring-boot-starter</artifactId> <version>${langchain4j.version}</version> </dependency> </dependencies>

特别注意langchain4j-spring-boot-starter这个starter,它自动配置了EmbeddingModel、ChatLanguageModel等Bean,但默认配置过于简陋。比如它把EmbeddingModel的batchSize设为10,而我们的PDF解析服务每批处理200页,结果OOM。必须在application.yml里覆盖:

langchain4j: embedding-model: batch-size: 128 # 根据GPU显存调整,A10G建议128 max-retries: 3 chat-language-model: timeout: 60000 # LLM响应超时设为60秒

还有个坑是日志框架冲突。LangChain4j底层用SLF4J,但某些老项目用了Log4j2,会导致NoClassDefFoundError: org/slf4j/LoggerFactory。解决方案是在pom.xml里强制排除:

<exclusion> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> </exclusion>

3.2 文档预处理:超越简单切片的语义保真方案

RAG效果差,80%的问题出在文档预处理。很多教程教你怎么用RecursiveCharacterTextSplitter按\n\n或。切分,但这对合同、标书这类结构化文档是灾难。比如一份采购合同里,“付款方式”条款可能跨3页,中间插着供应商资质表、银行账户信息表,按固定长度切分必然割裂语义。我们的方案是三级预处理流水线:

第一级:格式解析保真
不用Apache PDFBox这种通用解析器,改用pdfplumber(通过Jython调用),因为它能保留PDF的原始布局信息。关键代码:

public List<Document> parsePdfWithLayout(String pdfPath) { // 用Jython调用pdfplumber获取带坐标的信息 String pythonScript = """ import pdfplumber from langchain.docstore.document import Document with pdfplumber.open('""" + pdfPath + """') as pdf: docs = [] for page in pdf.pages: # 提取文本块,按y坐标分组模拟段落 blocks = sorted(page.extract_words(x_tolerance=3, y_tolerance=5), key=lambda b: -b['top']) text = "" for block in blocks: if block['top'] < 100: // 跳过页眉 continue text += block['text'] + " " docs.append(Document(page_content=text, metadata={"page": page.page_number})) return docs """; // 执行Python脚本,返回Document列表 }

第二级:语义切片
针对不同文档类型用不同切片器。合同用正则切条款,技术文档用Markdown标题切章节,Excel用POI按Sheet切分。核心是LangChain4j的TextSegmenterSPI:

@Component public class ContractTextSegmenter implements TextSegmenter { private static final Pattern CLAUSE_PATTERN = Pattern.compile("^(第[零一二三四五六七八九十百千]+条|Article \\d+)\\s+(.+?)(?=(^第[零一二三四五六七八九十百千]+条|Article \\d+|$))", Pattern.MULTILINE | Pattern.DOTALL); @Override public List<TextSegment> segment(String text) { List<TextSegment> segments = new ArrayList<>(); Matcher matcher = CLAUSE_PATTERN.matcher(text); while (matcher.find()) { String clauseNumber = matcher.group(1); String content = matcher.group(2).trim(); // 构建segment时注入业务元数据 Map<String, Object> metadata = new HashMap<>(); metadata.put("clause_number", clauseNumber); metadata.put("document_type", "CONTRACT"); segments.add(new TextSegment(content, metadata)); } return segments; } }

第三级:向量化增强
单纯用文本向量检索,对专业术语效果差。我们在Embedding前加入术语增强:从行业词典(如GB/T 19001质量管理体系术语)抽取关键词,拼接到原文末尾。比如原文“本合同有效期三年”,增强后变成“本合同有效期三年 [术语]合同有效期:指合同双方约定的合同具有法律效力的期限”。实测在法律知识库中,术语增强使“合同解除条件”的检索准确率提升22%。

3.3 检索增强:HybridRetriever与RRF重排序实战

LangChain4j的HybridRetriever是RAG效果的分水岭。它不是简单把BM25和向量检索结果拼起来,而是用RRF算法融合两个排序列表。但官方RRF实现有缺陷:它假设两个检索器同等可靠,而实际中BM25对精确匹配强,向量检索对语义匹配强。我们重写的RRF算法加入了动态权重:

public class AdaptiveRRFRetriever implements Retriever<Document> { private final Retriever<Document> bm25Retriever; private final Retriever<Document> vectorRetriever; private final double alpha; // BM25权重,0.0~1.0 @Override public List<Document> retrieve(String query) { List<Document> bm25Results = bm25Retriever.retrieve(query); List<Document> vectorResults = vectorRetriever.retrieve(query); // 计算RRF分数,k设为60(经验值) Map<Document, Double> scores = new HashMap<>(); for (int i = 0; i < Math.min(100, bm25Results.size()); i++) { Document doc = bm25Results.get(i); double rrfScore = alpha * (1.0 / (i + 60)); scores.merge(doc, rrfScore, Double::sum); } for (int i = 0; i < Math.min(100, vectorResults.size()); i++) { Document doc = vectorResults.get(i); double rrfScore = (1 - alpha) * (1.0 / (i + 60)); scores.merge(doc, rrfScore, Double::sum); } // 按分数排序,返回Top10 return scores.entrySet().stream() .sorted(Map.Entry.<Document, Double>comparingByValue().reversed()) .limit(10) .map(Map.Entry::getKey) .collect(Collectors.toList()); } }

alpha值怎么定?我们用历史问答日志做A/B测试:随机抽1000条用户提问,分别用alpha=0.5、0.6、0.7跑检索,人工评估Top3结果的相关性。结果alpha=0.65时综合得分最高(相关结果占比89.2%)。这个值不是拍脑袋,而是业务数据驱动的。

3.4 LangGraph4j状态图:构建可解释的RAG工作流

LangGraph4j的状态图设计,核心是把RAG流程拆解为可审计的原子节点。以政策问答为例,我们定义了5个节点:

节点名类型输入输出关键逻辑
parse_queryPureNode用户提问{intent: "SUBSIDY", entities: ["small_business", "stabilize_employment"]}用规则引擎识别意图和实体,避免LLM幻觉
retrieve_policiesToolNodeintent+entitiesList调用HybridRetriever,传入业务上下文过滤
validate_policyConditionalNodeDocument列表validated_docs调用外部API检查政策时效性,过滤已废止条款
rank_resultsPureNodevalidated_docsranked_docs用业务规则重排序:新政策优先、本地政策优先
generate_answerToolNoderanked_docsAnswer调用LLM生成答案,强制要求引用来源

状态图代码:

@Bean public StateGraph<PolicymakerState> policymakerGraph() { StateGraphBuilder<PolicymakerState> builder = StateGraph.builder(PolicymakerState.class); builder.addNode("parse_query", parseQueryNode()); builder.addNode("retrieve_policies", retrievePoliciesNode()); builder.addNode("validate_policy", validatePolicyNode()); builder.addNode("rank_results", rankResultsNode()); builder.addNode("generate_answer", generateAnswerNode()); // 条件路由:如果validate_policy返回空,则走fallback流程 builder.addEdge("parse_query", "retrieve_policies"); builder.addEdge("retrieve_policies", "validate_policy"); builder.addConditionalEdges("validate_policy", state -> state.getValidatedDocs().isEmpty() ? "fallback" : "rank_results"); builder.addEdge("rank_results", "generate_answer"); return builder.build(); }

最关键的validate_policy节点,我们调用政府公开API检查政策时效性:

public class PolicyValidator { public List<Document> validate(List<Document> docs) { return docs.parallelStream() .filter(doc -> { String policyId = (String) doc.metadata().get("policy_id"); // 调用gov-api/check-status?policyId=xxx return govApiClient.checkStatus(policyId).isActive(); }) .collect(Collectors.toList()); } }

这样,当运营人员发现某条回答错误时,可以直接在监控后台查看validate_policy节点的执行日志,立刻定位是政策已失效还是API调用失败。

3.5 生产部署:容器化与可观测性实践

生产环境不是把jar包扔到服务器就完事。我们用Kubernetes部署,核心配置:

# deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: rag-service spec: replicas: 3 template: spec: containers: - name: rag-app image: registry.example.com/rag-service:0.25.0 env: - name: LANGCHAIN4J_EMBEDDING_MODEL_URL value: "http://embedding-service:8080/embed" - name: MILVUS_URI value: "http://milvus-proxy:19530" resources: limits: memory: "4Gi" cpu: "2000m" requests: memory: "2Gi" cpu: "1000m" livenessProbe: httpGet: path: /actuator/health/liveness port: 8080 initialDelaySeconds: 60 readinessProbe: httpGet: path: /actuator/health/readiness port: 8080 initialDelaySeconds: 30

可观测性是生命线。我们集成三套监控:

  • Metrics:用Micrometer暴露LangChain4j的langchain4j.embedding.duration、langchain4j.retriever.hits等指标,Grafana看板实时显示检索命中率;
  • Tracing:OpenTelemetry自动捕获每个LangGraph4j节点的执行耗时,SkyWalking里能看到“generate_answer”节点占了总耗时70%,说明LLM是瓶颈;
  • Logging:用Logback的AsyncAppender收集结构化日志,关键字段包括query_id、retrieved_doc_count、llm_prompt_tokens,方便问题复盘。

有个血泪教训:某次上线后发现CPU使用率飙升到95%,排查发现是LangChain4j的RetryableEmbeddingModel在向量服务不可用时疯狂重试,默认重试10次,每次间隔100ms,导致线程池被打满。解决方案是在application.yml里配置:

langchain4j: embedding-model: max-retries: 2 # 降为2次 retry-delay-millis: 1000 # 间隔1秒

4. 常见问题与避坑指南:那些文档里不会写的实战经验

4.1 检索效果差的根因分析与解决路径

RAG项目最常见的抱怨是“检索不准”,但90%的情况不是模型问题,而是数据或流程问题。我们总结了一套四步诊断法:

第一步:检查文档预处理质量
用curl -X POST http://localhost:8080/debug/preprocess -d '{"text":"第3.2条 违约责任"}'调用调试接口,看返回的TextSegment是否完整。曾有个项目把“第3.2条”切成了“第3.”和“2条”两段,原因是正则写错了。解决方案:在CustomTextSegmenter里加日志,记录每个segment的原始位置。

第二步:验证向量空间合理性
写个测试用例,用两个明显相关的句子计算余弦相似度:

@Test public void testSemanticSimilarity() { String s1 = "小微企业稳岗补贴申领条件"; String s2 = "小企业岗位稳定补贴申请要求"; double similarity = embeddingModel.embed(s1).cosineSimilarity(embeddingModel.embed(s2)); assertTrue(similarity > 0.8); // 低于0.8说明Embedding模型不适用 }

如果相似度低,换模型(如从text-embedding-ada-002换成bge-m3)或加术语增强。

第三步:分析RRF融合效果
开启LangChain4j的DEBUG日志,看BM25和向量检索各自返回的Top10。如果BM25返回的全是“补贴”“申请”等泛词,而向量检索返回的全是无关内容,说明向量模型没训好。这时要检查训练数据——我们给农业知识库用的Embedding模型,是在10万份农技推广文档上微调的,不是直接用通用模型。

第四步:检查LLM提示词
很多团队忽略这点:LLM看到10个检索结果,但提示词没告诉它“只基于以下文档回答,不确定就回答不知道”。我们的标准提示词模板:

你是一个专业的政策顾问,只能根据以下提供的政策文档回答问题。如果文档中没有相关信息,请回答“根据当前知识库,无法确定”。 问题:{query} 参考文档: {documents} 请用中文回答,不要编造信息。

4.2 LangGraph4j状态图调试技巧

LangGraph4j的调试比Spring Boot难,因为状态流转是异步的。我们摸索出三招:

招一:状态快照日志
在每个节点执行前后打印状态:

public class DebugNode implements Node<PolicymakerState> { @Override public PolicymakerState execute(PolicymakerState state) { log.info("Node {} start, state: {}", getNodeName(), state.toJson()); // 执行逻辑 log.info("Node {} end, state: {}", getNodeName(), state.toJson()); return state; } }

招二:可视化流程图
用LangGraph4j内置的GraphPrinter生成DOT文件,再用Graphviz渲染:

GraphPrinter printer = new GraphPrinter(graph); String dot = printer.print(); Files.write(Paths.get("rag-flow.dot"), dot.getBytes()); // 然后命令行:dot -Tpng rag-flow.dot -o rag-flow.png

招三:单步执行模式
在测试环境启用StateGraphBuilder.debugMode(true),这样每个节点执行后暂停,等待人工确认:

if (environment.matchesProfiles("debug")) { builder.setDebugMode(true); }

4.3 性能优化实战:从2秒到200毫秒的蜕变

RAG性能瓶颈通常在三处:PDF解析、向量检索、LLM生成。我们的优化方案:

PDF解析加速
不用pdfplumber全量解析,改用Apache PDFBox的PDFRenderer只渲染文字层,速度提升5倍:

PDDocument document = PDDocument.load(new File(pdfPath)); PDFTextStripper stripper = new PDFTextStripper(); String text = stripper.getText(document);

向量检索优化
Milvus配置关键参数:

# milvus.yaml common: metric_type: IP # 用内积代替欧氏距离,更快 index_file_size: 1024 # 索引文件大小MB search: nprobe: 32 # 搜索时查询的聚类中心数,32是平衡点

LLM生成提速
不用同步调用,改用Streaming:

ChatLanguageModel model = QwenChatModel.builder() .baseUrl("https://dashscope.aliyuncs.com/api/v1") .apiKey(System.getenv("DASHSCOPE_API_KEY")) .streaming(true) // 开启流式响应 .build();

前端用SSE接收,用户看到字一个一个出来,心理等待时间大幅降低。

4.4 安全与合规红线

企业RAG必须守住三条红线:

  • 数据不出域:所有文档解析、向量化、检索都在私有云完成,绝不调用公有云LLM的非流式API(会上传全文);
  • 来源可追溯:每个Answer必须带source: {doc_id}#page_3,我们用正则从Document.metadata提取;
  • 内容不过滤:不依赖LLM的内容安全过滤,而是在检索层加黑白名单——比如法务知识库,黑名单词“赔偿金额”必须出现在检索结果里,否则不返回。

最后分享个真实案例:某银行项目上线前,监管要求提供“所有用户提问的原始记录”。我们早就在日志里存了query_id、user_id、query_text、retrieved_docs_ids,导出CSV就交差了。而用Spring AI的团队,因为日志没存检索详情,不得不临时加埋点,延期两周。

5. 实战扩展:从单体RAG到企业级知识中枢

5.1 多源知识融合:打通数据库、API与文档库

真实业务中,知识从来不止于文档。某制造企业需要把ERP里的BOM表、MES里的工艺参数、PDF里的设备手册融合检索。我们的方案是LangGraph4j的MultiSourceRetriever:

@Bean public MultiSourceRetriever multiSourceRetriever() { return MultiSourceRetriever.builder() .addRetriever("erp", erpDatabaseRetriever()) // SQL查询BOM .addRetriever("mes", mesApiRetriever()) // 调用MES REST API .addRetriever("docs", milvusRetriever()) // 向量检索PDF .build(); }

关键在erpDatabaseRetriever的实现:不是简单查表,而是用NL2SQL模型把用户提问转成SQL。我们用LangChain4j的SqlDatabaseChain,但重写了PromptTemplate,加入BOM表结构描述:

你是一个SQL生成专家,BOM表结构:bom_id, parent_part_no, child_part_no, quantity, unit。 问题:查询发动机总成包含哪些零件?

5.2 持续学习闭环:让知识库越用越聪明

RAG不是静态的,要建立反馈闭环。我们在Answer页面加了“回答有帮助吗?”按钮,用户点“无帮助”时,触发以下流程:

  • 收集用户提问、LLM回答、实际点击的文档;
  • 用对比学习微调Embedding模型:把提问和正确文档作为正样本,提问和错误文档作为负样本;
  • 每周自动触发一次微调任务,用Kubeflow Pipelines编排。

实测某政务知识库运行3个月后,hit rate从72%提升到89%。这个闭环不依赖人工标注,全自动化。

5.3 与现有系统集成:Spring Boot的无缝嵌入

很多团队担心LangChain4j和Spring Boot冲突。其实它设计之初就考虑了Spring生态。我们常用三种集成方式:

方式一:Starter自动配置
langchain4j-spring-boot-starter会自动注册Bean,你只需:

@Service public class PolicyService { @Autowired private ChatLanguageModel chatModel; // 自动注入通义千问 @Autowired private Retriever<Document> retriever; // 自动注入HybridRetriever }

方式二:手动配置Bean
当需要深度定制时,比如给EmbeddingModel加缓存:

@Bean @Primary public EmbeddingModel embeddingModel() { return new CachedEmbeddingModel( QwenEmbeddingModel.builder() .baseUrl("https://dashscope.aliyuncs.com/api/v1") .apiKey(System.getenv("DASHSCOPE_API_KEY")) .build(), Caffeine.newBuilder() .maximumSize(10000) .expireAfterWrite(1, TimeUnit.HOURS) .build() ); }

方式三:FeignClient集成
把RAG服务包装成REST API,供其他Spring Cloud服务调用:

@FeignClient(name = "rag-service", url = "${rag.service.url:http://localhost:8080}") public interface RAGClient { @PostMapping("/ask") Answer ask(@RequestBody AskRequest request); }

这套方案已在8个生产环境稳定运行,最长的一个已连续运行412天无重启。它证明Java不只是能做RAG,而是能做出最可控、最可运维、最贴合企业需求的RAG系统。当你下次被问“Java做RAG行不行”,别急着回答,打开IDE,用LangChain4j加载一份合同,用LangGraph4j跑个状态图,让代码自己说话。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/28 7:06:42

网站设计基本功能避坑指南:保姆级建站教程防拖稿

网站设计基本功能避坑指南:保姆级建站教程防拖稿 改个需求建站公司拖一周,这不仅是你的噩梦,也是很多运营和项目经理的常态。别怪外包方懒,很多时候是因为你没把 网站设计基本功能 里的安全边界定义清楚,导致开发在“安全”和“功能”之间反复横跳,最后只能无限期延期。今天这篇 保姆级建站教程…

作者头像 李华
网站建设 2026/9/28 7:06:42

苏州网站建设规划避坑:对比评测后才发现域名服务器才是命门

苏州网站建设规划避坑:对比评测后才发现域名服务器才是命门 很多苏州老板在找建站公司时,总盯着页面好不好看,却忽略了最底层的域名和服务器。这俩东西搞不懂,后期维护全是坑,甚至导致网站打不开、备案被驳回。…

作者头像 李华
网站建设 2026/9/28 7:06:32

2026最新html网页嵌入视频代码实操指南

2026最新html网页嵌入视频代码实操指南 刚拿到北京新公司的备案回执,对着服务器控制台发呆,是不是感觉备案流程像天书?别慌,这种“备案流程一头雾水”的状态,我见过太多创业团队负责人经历。2026年的建站环境变了,视频加载速度和合规性成了SEO的重头戏,很多老板还停留在“放个腾讯视频链接”的阶段,…

作者头像 李华
网站建设 2026/9/28 7:06:23

2026最新可以做单的猎头网站域名服务器配置实战

2026最新可以做单的猎头网站域名服务器配置实战 网站做好了没人访问,这比没做还让人心累。 很多刚入行的猎头站长,花几千块做了个漂亮的前端,域名也买了,服务器也租了,结果上线一周,后台访问日志里全是爬虫,真人一个没见着。…

作者头像 李华
网站建设 2026/9/28 7:06:21

装修公司网站php源码哪家好用?避坑指南与报价拆解

装修公司网站php源码哪家好用?避坑指南与报价拆解 域名注册下来就卡在服务器配置上,SSL证书申请流程比看天书还难,这种“域名服务器搞不懂”的焦虑,几乎是每个想自建装修公司官网的新手都会遇到的死胡同。很多人第一反应是找外包公司,但面对市面上参差不齐的装修公司网站php源码方案,到底哪家好?这不仅是技…

作者头像 李华
网站建设 2026/9/28 7:06:14

科技网站建设长沙新手入门

长沙科技网站建设速查手册:解决没人访问的实战复盘 网站上线三个月,后台数据一片惨淡,每天自然流量不到十次。这是很多长沙本地科技企业站长的真实困境。你以为做了SEO,发了几篇软文,排名就能上去?现实往往很骨感。如果你正面临“网站做好了没人访问”的焦虑,这份基于真实项目复盘的速查手册,或许能帮你理清思路…

作者头像 李华