Java工程师快速上手LangChain4j:从环境搭建到RAG实战

发布时间:2026/10/26 14:25:08
Java工程师快速上手LangChain4j:从环境搭建到RAG实战 1. 为什么 Java 工程师现在要关注 LangChain4j1.1 大模型应用开发不是 Python 的专属领地过去一年多我身边不少做 Java 后端的朋友都有一种焦虑大模型应用开发好像天然属于 Python 生态LangChain、LlamaIndex 这些框架清一色是 Python 的Java 工程师想接个大模型能力要么写个 Python 微服务做中转要么直接裸调 HTTP 接口代码又臭又长维护起来非常痛苦。这种局面在 2024 年之后有了明显改观LangChain4j就是专门为 Java 生态打造的大模型应用开发框架它的定位和 Python 版 LangChain 类似但 API 设计更贴合 Java 工程师的直觉和 Spring Boot、Maven、Lombok 这些日常工具链能无缝衔接。我最初接触 LangChain4j 是因为一个内部知识库问答的需求。当时团队评估过三条路一是用 Python 单独搭一套 RAG 服务二是直接调大模型厂商的 HTTP API 自己封装三是用 LangChain4j 在现有 Spring Boot 项目里集成。第一条路要维护两套技术栈第二条路重复造轮子且容易踩坑最后我们选了第三条。实测下来一个中等复杂度的 RAG 问答功能从零到跑通大概两三天代码量比裸调 API 少了将近一半而且后续换模型、加记忆、接向量库都很顺滑。这篇文章面向的是有 Java 基础、想快速上手大模型应用开发的后端工程师不管你是做业务系统想加个智能问答还是想系统学习 RAG、Embedding 这些概念都能从下面这些实操细节里找到可以直接抄作业的部分。我会从环境搭建讲到 RAG 完整链路把每个关键选择背后的理由讲清楚也会分享我自己踩过的坑。1.2 LangChain4j 到底解决了什么问题先说清楚它不是什么。LangChain4j 不是大模型本身它是一层编排框架帮你把「调用大模型」「管理对话记忆」「接入向量数据库」「组装提示词」「解析结构化输出」这些零散动作串成一条清晰的流水线。没有它的时候你调一次 OpenAI 的接口大概要写这些代码构造 HTTP 请求、拼 JSON、处理超时重试、解析返回、管理 token 数量、维护多轮对话的上下文……每加一个功能就要重复一遍。有了 LangChain4j这些都被抽象成了接口和注解。它最核心的几个能力我按使用频率排个序ChatModel 抽象OpenAiChatModel、OllamaChatModel等实现类换模型基本只改一行配置。AI Services用接口加注解的方式声明式地定义 AI 服务类似 MyBatis 的 Mapper写起来非常 Java。Embedding 与向量存储EmbeddingModel配合EmbeddingStoreRAG 的两大基石。内容检索器ContentRetriever、EmbeddingStoreContentRetriever把知识库检索和对话串起来。对话记忆ChatMemory、MessageWindowChatMemory多轮对话不用自己维护 List。提示LangChain4j 的版本迭代比较快API 偶有调整。建议在pom.xml里锁定一个稳定版本不要用LATEST否则某天构建突然失败会很难排查。2. 环境准备与第一个可运行 Demo2.1 JDK、Maven 与依赖版本选择LangChain4j 对 JDK 的要求是JDK 17 及以上这一点要特别注意。很多公司内部项目还停留在 JDK 8如果你直接在主工程里引 LangChain4j 会编译不过。我的建议是单独起一个模块或者新项目JDK 用 17 或 21这两个都是长期支持版本语法上也更舒服比如record、switch表达式、文本块text block在写提示词的时候特别方便。Maven 依赖方面LangChain4j 做了模块化拆分不要一股脑全引。核心的几个坐标如下properties langchain4j.version0.35.0/langchain4j.version /properties dependencies !-- 核心抽象 -- dependency groupIddev.langchain4j/groupId artifactIdlangchain4j/artifactId version${langchain4j.version}/version /dependency !-- OpenAI 兼容模型接入 -- dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-open-ai/artifactId version${langchain4j.version}/version /dependency !-- 内存向量库入门够用 -- dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-embeddings-all-minilm-l6-v2/artifactId version${langchain4j.version}/version /dependency /dependencies这里解释一下为什么入门阶段我推荐用langchain4j-embeddings-all-minilm-l6-v2这个本地 Embedding 模型。它把模型权重打包进了 jar 包启动即用不需要额外部署服务也不需要联网调 Embedding 接口。代价是首次加载会占几百 MB 内存向量维度是 384精度比大厂云端 Embedding 略低但做 Demo 和中小规模知识库完全够用。等你验证完流程再换成云端 Embedding 或者本地部署的 BGE 系列。2.2 配置 API Key 与模型参数的正确姿势接入大模型第一步是拿到 API Key。这里我不展开讲具体厂商只说通用做法永远不要把 Key 硬编码在代码里也不要提交到 Git。用环境变量或者配置中心。Spring Boot 项目里我习惯这样写langchain4j: open-ai: chat-model: base-url: ${LLM_BASE_URL} api-key: ${LLM_API_KEY} model-name: gpt-4o-mini temperature: 0.7 timeout: PT60S max-retries: 2temperature这个参数值得单独说。它控制输出的随机性范围一般 0 到 2。做知识库问答、信息抽取这类要求稳定的场景我一般设 0.1 到 0.3做创意文案、头脑风暴可以设 0.7 到 1.0。新手最容易犯的错是默认值不调结果问答场景里模型老是「自由发挥」答非所问。max-retries设 2 是经验值网络抖动时能自动重试又不至于失败时卡太久。2.3 五行代码跑通第一次对话环境配好之后先别急着上 RAG跑通最基础的对话建立信心OpenAiChatModel model OpenAiChatModel.builder() .baseUrl(System.getenv(LLM_BASE_URL)) .apiKey(System.getenv(LLM_API_KEY)) .modelName(gpt-4o-mini) .temperature(0.3) .build(); String answer model.generate(用一句话解释什么是向量数据库); System.out.println(answer);generate是最简单的同步调用传入字符串返回字符串。实测下来第一次调用如果报连接超时九成是base-url写错了注意结尾不要多加/v1或者少加不同厂商要求不一样以官方文档为准。如果报 401就是 Key 的问题报 429 是限流加个重试或者降并发。注意generate方法内部其实帮你做了「把字符串包装成 UserMessage」这件事。等你需要多轮对话或者系统提示词时就要用ChatRequest和ChatMessage了这是进阶的起点。3. 用 AI Services 把调用写得像 MyBatis Mapper3.1 声明式接口的写法与原理裸调model.generate写多了你会发现每次都要手动拼提示词、解析返回业务代码里混着一堆字符串拼接。LangChain4j 的AI Services就是来解决这个问题的它的设计思路和 MyBatis 的 Mapper 几乎一模一样你只定义接口框架用动态代理生成实现。interface Assistant { SystemMessage(你是一个严谨的 Java 技术助手回答要简洁代码要能编译通过。) String chat(UserMessage String userMessage); } Assistant assistant AiServices.create(Assistant.class, model); String reply assistant.chat(Java 里 HashMap 和 Hashtable 有什么区别);这段代码背后发生了什么AiServices.create用 JDK 动态代理生成了一个Assistant的实现类当你调用chat方法时代理会读取方法上的SystemMessage和UserMessage注解把参数组装成ChatRequest交给ChatModel执行再把返回的AiMessage转成String返回。理解了这个原理你就能明白为什么接口方法名可以随便起真正起作用的是注解。3.2 多轮对话与 ChatMemory 的配合单轮问答用SystemMessage就够了但真实场景几乎都是多轮对话。比如用户先问「LangChain4j 是什么」再问「它和 Spring AI 有什么区别」第二问里的「它」需要模型记得上文。这时候就要引入ChatMemoryAssistant assistant AiServices.builder(Assistant.class) .chatLanguageModel(model) .chatMemory(MessageWindowChatMemory.withMaxMessages(20)) .build();MessageWindowChatMemory是一个滑动窗口记忆withMaxMessages(20)表示最多保留最近 20 条消息用户和 AI 各算一条。为什么是 20 而不是无限因为大模型的上下文窗口是有限的而且上下文越长费用越高、响应越慢。20 条大概能覆盖 10 轮对话对大多数客服、助手场景够用了。如果你的场景需要长期记忆那就要上向量库做「记忆检索」这是另一个话题。这里有个坑我踩过AiServices.builder创建的 Assistant 是有状态的多个用户共用同一个实例会导致记忆串台。正确做法是每个会话比如每个 sessionId创建一个 Assistant 实例或者用MemoryId注解配合ChatMemoryProvider做隔离。生产环境一定要做这个隔离否则 A 用户的隐私可能被 B 用户看到。3.3 结构化输出让模型返回 Java 对象大模型默认返回自然语言但业务代码需要的是对象。LangChain4j 支持让模型直接返回 POJOrecord PersonInfo(String name, int age, String city) {} interface InfoExtractor { UserMessage(从下面这段话里提取人物信息{{it}}) PersonInfo extract(String text); }框架会自动把返回的 JSON 反序列化成PersonInfo。实测下来这个功能对模型的指令遵循能力有要求gpt-4o-mini以上基本稳定小模型偶尔会返回格式不对的 JSON。我的经验是在SystemMessage里明确要求「只返回 JSON不要任何解释」能显著提升成功率。如果还是不稳定可以加一层校验和重试。4. RAG 实战从零搭一个能查文档的问答机器人4.1 RAG 到底在解决什么问题RAG 是 Retrieval-Augmented Generation 的缩写中文叫检索增强生成。为什么需要它因为大模型有两个硬伤一是知识有截止日期训练完之后的新信息它不知道二是不知道你的私有数据比如公司内部文档、产品手册。RAG 的思路很朴素用户提问时先去你的知识库里检索出最相关的几段内容把它们塞进提示词一起发给模型模型基于这些「参考资料」来回答。这就像开卷考试和闭卷考试的区别。闭卷考试纯大模型全靠记忆容易记错或者编造开卷考试RAG允许翻书答案有据可查。所以 RAG 特别适合企业知识库、客服问答、文档助手这类场景。但要注意RAG 不是万能的它解决的是「知识更新」和「私有数据」问题解决不了「推理能力」问题。如果问题需要复杂逻辑推理RAG 帮不上太多忙。4.2 文档加载、切分与 Embedding 的完整链路一个完整的 RAG 链路分两个阶段索引阶段离线把文档变成向量存起来和检索阶段在线用户提问时找相似向量。先看索引阶段。第一步是加载文档。LangChain4j 提供了DocumentLoader支持从文件、URL、类路径加载。我一般用FileSystemDocumentLoader.loadDocuments(path)批量加载一个目录下的 txt、md、pdf。PDF 解析需要额外引langchain4j-document-parser-apache-pdfbox扫描版 PDF 还要 OCR这是另一个坑入门先用 txt 和 md。第二步是切分。文档不能整篇塞进去一是太长超上下文二是检索粒度太粗。用DocumentSplitters.recursive(500, 50)意思是每段目标 500 个字符相邻段重叠 50 个字符。为什么要有重叠因为一句话可能被切断重叠能保证语义连续性。500 这个值不是拍脑袋是经验值太小则语义不完整太大则检索不精准。中文场景我一般调到 300 到 500因为中文信息密度比英文高。ListDocument documents FileSystemDocumentLoader.loadDocuments(/docs); DocumentSplitter splitter DocumentSplitters.recursive(400, 50); ListTextSegment segments splitter.splitAll(documents);第三步是向量化并存储。每个TextSegment通过EmbeddingModel转成一个浮点数组存进EmbeddingStoreEmbeddingModel embeddingModel new AllMiniLmL6V2EmbeddingModel(); EmbeddingStoreTextSegment store new InMemoryEmbeddingStore(); for (TextSegment segment : segments) { Embedding embedding embeddingModel.embed(segment).content(); store.add(embedding, segment); }InMemoryEmbeddingStore把向量存在内存里重启就没了适合 Demo。生产环境要换成持久化的比如PgVectorEmbeddingStore基于 PostgreSQL、MilvusEmbeddingStore、RedisEmbeddingStore。选哪个我的建议是如果团队已经有 PostgreSQL直接用 pgvector 扩展少维护一个中间件如果数据量上千万级别再考虑 Milvus 这类专业向量库。4.3 检索器与 AI Services 的整合索引建好之后检索阶段就简单了。核心是构造一个ContentRetriever然后把它挂到 AI Service 上ContentRetriever retriever EmbeddingStoreContentRetriever.builder() .embeddingStore(store) .embeddingModel(embeddingModel) .maxResults(5) .minScore(0.6) .build(); Assistant assistant AiServices.builder(Assistant.class) .chatLanguageModel(model) .contentRetriever(retriever) .build();maxResults(5)表示每次检索返回最相似的 5 段minScore(0.6)是相似度阈值低于这个分数的直接丢弃。这两个参数是 RAG 效果的关键调节旋钮。maxResults太大提示词里塞一堆无关内容模型容易被干扰太小可能漏掉关键信息。minScore太低会引入噪声太高可能什么都检索不到。我的经验是先用maxResults5, minScore0.6跑起来然后根据实际问答效果微调。整合之后用户提问时框架会自动把问题向量化 → 在 store 里找相似段 → 把相似段拼进提示词 → 调用模型 → 返回答案。整个过程对业务代码透明你只需要调assistant.chat(question)。4.4 RAG 效果调优的几个关键点RAG 跑通容易调好难。我总结了几个最影响效果的点切分策略。固定长度切分是最简单的但对结构化文档不友好。比如一份 API 文档一个接口的说明被切成两半检索出来就不完整。更好的做法是按标题、按段落切LangChain4j 支持自定义DocumentSplitter。如果文档有明确的层级结构按结构切分效果提升很明显。Embedding 模型选择。入门用的 MiniLM 是英文模型中文效果一般。中文场景建议换成 BGE 系列或者云端 Embedding。网上有各种 Embedding 模型排行榜但排行榜是通用评测你的业务场景才是最终标准。我的做法是准备 20 到 30 个真实问题用不同模型跑一遍人工看检索结果选最准的那个。查询改写。用户的问题往往很短、口语化直接拿去检索效果差。可以在检索前加一步「查询改写」让大模型把用户问题改写成更适合检索的形式。比如用户问「那个报错怎么解决」改写成「XXX 错误的解决方法」。这一步能明显提升召回率代价是多一次模型调用。重排序。检索出来的 5 段相关性有高有低。可以用一个 Rerank 模型对它们重新排序把最相关的放前面。LangChain4j 支持ReRankingContentAggregator。这一步对精度提升有帮助但会增加延迟看场景取舍。5. 常见问题排查与避坑经验5.1 依赖冲突与版本不匹配LangChain4j 依赖了 OkHttp、Jackson 等库如果你的项目里已经有这些库的旧版本很容易冲突。典型症状是启动时报NoSuchMethodError或者ClassNotFoundException。排查方法是用mvn dependency:tree看依赖树找到冲突的库用exclusions排除旧版本。我遇到过一次 Jackson 版本冲突模型返回的 JSON 死活解析不了查了半天才发现是项目里另一个库锁了老版本 Jackson。提示新建项目做 LangChain4j 实验时尽量保持依赖干净不要引入一堆无关的 starter。等验证完再往主工程合并能省很多排查时间。5.2 向量检索「答非所问」的排查思路RAG 最常见的抱怨是「检索出来的内容跟问题不相关」。排查按这个顺序来排查项检查方法常见原因切分是否合理打印切分后的 segment段落被切断语义不完整Embedding 是否合适换模型对比检索结果中英文模型用错相似度阈值调低 minScore 看是否召回阈值过高导致漏检查询本身人工改写问题再检索用户问题太口语化向量库数据确认索引阶段是否成功写入写入失败或维度不匹配我遇到最多的是切分问题。有一次一份产品手册按 500 字符切结果每个功能点都被切碎检索出来全是半句话。后来改成按 Markdown 标题切效果立刻好了。所以调 RAG 第一步永远是看切分结果别急着调模型参数。5.3 上下文超限与 Token 成本控制大模型的上下文窗口有限RAG 把检索内容塞进提示词很容易超限。症状是报错「context length exceeded」或者模型开始胡言乱语。控制方法有几个一是限制maxResults别一次塞太多段二是对检索内容做截断三是用TokenCountEstimator估算 token 数超了就动态减少检索段数。成本方面RAG 每次问答至少两次模型调用一次 Embedding一次生成如果加了查询改写和重排序就是四次。Embedding 调用通常比生成便宜很多但量大也不容忽视。我的做法是对 Embedding 结果做缓存相同文本不重复向量化对高频问题做答案缓存命中直接返回。5.4 生产环境部署的几个注意点Demo 跑通到生产上线中间还有不少距离。首先是并发OpenAiChatModel是线程安全的但AiServices创建的 Assistant 如果带记忆就不是必须按会话隔离。其次是超时和降级大模型接口偶尔会慢或者挂要设合理的超时时间并准备降级方案比如返回「服务繁忙请稍后再试」而不是让请求一直挂着。第三是日志和监控把每次问答的问题、检索到的内容、模型返回都记下来出问题时才有据可查这些日志也是后续优化 RAG 的宝贵数据。最后分享一个我自己的体会LangChain4j 这类框架的价值不在于它帮你省了多少行代码而在于它把大模型应用的最佳实践固化成了 API。你按它的方式组织代码自然就避开了很多坑。新手最容易犯的错是绕过框架自己裸调结果在记忆管理、上下文控制、错误重试这些地方反复踩坑。先把框架用熟理解它每个抽象背后的意图再根据业务需要做定制这条路会顺很多。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询