你不知道的LangChain4j 那些事
2026/4/18 10:11:26 网站建设 项目流程

这是一篇风格化很重的LangChain4j实践教程。我不想给你看一个标准的技术文档模板,我想给你看一个真实的、会踩坑、会骂街、最终又能解决问题的Java开发者的实战记录。

LangChain4j,顾名思义,是LangChain的Java移植版。它的目标很明确:让Java开发者也能像Python开发者一样丝滑地构建大模型应用。目前它已经支持接入OpenAI、Azure、Ollama等超过20家模型提供商,还有内存管理、RAG、函数调用等一整套工具链。

我会从搭建环境开始,中间穿插我踩过的坑和学到的教训,然后重点讲RAG和Tool Calling这两个核心功能,最后聊几句LangChain4j和Spring AI到底怎么选。


1. 环境准备:先别急着写代码,选对版本很重要

LangChain4j的版本迭代挺快的,写这篇文章时最新稳定版是1.0.0-beta1。注意这个beta后缀——很多功能还在打磨,你用的版本可能和我用的不太一样。

如果你是Spring Boot项目,Maven里加上这些:

<dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j</artifactId><version>1.0.0-beta1</version></dependency><dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-open-ai</artifactId><version>1.0.0-beta1</version></dependency>

如果你是纯Java项目不用Spring,也可以,只是多写几行配置代码的事。

API Key别硬编码到代码里,这是血的教训。用环境变量或者配置文件管理。


2. 别用Low-Level API,AI Services才是正解

刚上手时我犯过一个错误:直接用ChatLanguageModel来调大模型。代码大概长这样:

ChatLanguageModelmodel=OpenAiChatModel.builder().apiKey(apiKey).modelName("gpt-3.5-turbo").build();Stringresponse=model.generate("Hello, world!");

能跑,但问题很快就来了:每次都要手动处理对话历史,想加个RAG还得自己拼Prompt,代码越写越乱。后来才发现LangChain4j的设计精髓根本不在这里,而是在AI Services——你只需要定义一个接口,框架帮你生成实现。

publicinterfaceAssistant{Stringchat(Stringmessage);}Assistantassistant=AiServices.builder(Assistant.class).chatLanguageModel(model).chatMemory(MessageWindowChatMemory.withMaxMessages(10)).build();

这叫什么?这叫“声明式AI开发”。你声明你想要什么,框架帮你搞定怎么做到。


3. 第一个坑:对话记忆存MySQL,我踩过的雷

说实话,LangChain4j的官方文档现在确实还不够完善。有个开发者朋友在掘金上吐槽说,他想把对话记录存到MySQL,结果从下午一直折腾到晚上11点半。

问题出在哪?他把模型从阿里百炼换成OpenAI后,所有问题竟然都消失了。这说明什么?LangChain4j对不同模型提供商的适配程度是不一样的,有些SDK还藏着bug。如果你也遇到莫名其妙的持久化问题,不妨先换个模型试试,先确认是框架问题还是模型适配问题。

如果要用ChatMemory持久化到数据库,建议用框架官方支持的ChatMemoryStore实现,别自己瞎折腾。目前比较靠谱的方案是用MongoDB或Redis存对话记录。


4. RAG:从“极简版”到“实战版”的进化

RAG(检索增强生成)是LangChain4j最亮眼的功能之一。说白了就是给大模型配一个“小抄本”,让它回答前先查一下你的私有知识库。

4.1 极简版:三分钟跑通RAG

先跑通再说,别一上来就追求完美。引入langchain4j-easy-rag依赖:

<dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-easy-rag</artifactId><version>1.0.0-beta1</version></dependency>

然后几行代码就能跑起来:

// 1. 加载文档List<Document>documents=FileSystemDocumentLoader.loadDocuments("src/main/resources/docs");// 2. 转换成向量并存到内存EmbeddingStore<TextSegment>embeddingStore=newInMemoryEmbeddingStore<>();EmbeddingStoreIngestor.ingest(documents,embeddingStore);// 3. 构建AI Service并绑定检索器Assistantassistant=AiServices.builder(Assistant.class).chatLanguageModel(model).contentRetriever(EmbeddingStoreContentRetriever.from(embeddingStore)).build();

极简版的特点是“一切皆默认”——内置Embedding模型、内存向量存储、默认分块策略。很适合快速验证想法。

4.2 实战版:换掉默认,优化效果

极简版跑通后,你会发现几个问题:内存存储重启就没了,默认的Embedding模型效果一般,文档分块策略太粗暴。

向量数据库的选择

生产环境别用内存存储。我推荐两个方向:

  • PgVector:如果你的项目已经在用PostgreSQL,这是最省事的选择,直接用现有数据库存向量。
  • MongoDB Atlas:如果你需要的是开箱即用的云服务,MongoDB的Vector Search功能很成熟,而且和LangChain4j有官方集成模块。

以PgVector为例,配置大概这样:

EmbeddingStore<TextSegment>embeddingStore=PgVectorEmbeddingStore.builder().host("localhost").port(5432).database("rag_db").user("postgres").password("password").table("embeddings").dimension(1536)// OpenAI embedding的维度.build();

Embedding模型的选择

默认的Embedding模型效果一般。生产环境建议换成OpenAI的text-embedding-3-small或Cohere的embed模型,中文场景可以试试阿里的通义千问Embedding。


5. Tool Calling:让AI学会调用你的Java方法

如果说RAG是给AI配了本参考书,那Tool Calling就是给了AI一双能干活的手。当用户问“今天天气怎么样”时,AI知道自己答不上来,但可以调用你写的getWeather工具来获取实时数据。

5.1 基础用法

@Tool注解标记你想让AI调用的方法:

publicclassWeatherTools{@Tool("获取指定城市的当前天气")publicStringgetWeather(@P("城市名称")Stringcity){// 这里调用真实的天气APIreturncity+"今天晴朗,温度22°C";}@Tool("计算两个数的和")publicdoublesum(@P("第一个数")doublea,@P("第二个数")doubleb){returna+b;}}

然后在构建AI Service时注册这些工具:

Assistantassistant=AiServices.builder(Assistant.class).chatLanguageModel(model).tools(newWeatherTools()).build();

现在你可以问AI“北京天气怎么样”或者“帮我算一下156加243”,它会自动判断需要调用哪个工具。

5.2 第二个坑:工具调用无限循环

我遇到过一个问题:AI在一个对话里反复调用同一个工具,token烧得飞起。有开发者说他看着AI对话转了半个小时,花了将近100万token。

原因是什么?记忆窗口太小了。LangChain4j默认只保留最近几条对话记录,当工具调用次数多了之后,AI会“忘记”自己刚才已经调用过这个工具了,于是重复调用。

解决方案也很简单:调大MessageWindowChatMemory的容量:

MessageWindowChatMemorychatMemory=MessageWindowChatMemory.withMaxMessages(35);// 默认是10,调到35

这个值需要根据你的实际场景调整,太小容易循环,太大token消耗多。

5.3 多个工具怎么让AI选对?

如果你的工具有很多,关键是把@Tool注解里的描述写清楚。AI是根据你的描述来判断该用哪个工具的。描述要写清楚“这个工具是干什么的”、“什么时候应该用”。写得好不好,直接影响AI选工具的准确率。


6. 流式输出:别把用户晾在那干等

LangChain4j的流式输出确实比Spring AI麻烦一些。Spring AI里call是全量,stream是流式,很直觉。但LangChain4j需要单独用StreamingChatLanguageModel,还要加langchain4j-reactor依赖。

如果用AI Services方式实现流式,代码是这样的:

publicinterfaceStreamingAssistant{TokenStreamchat(Stringmessage);}StreamingAssistantassistant=AiServices.builder(StreamingAssistant.class).streamingChatLanguageModel(streamingModel).chatMemory(chatMemory).build();TokenStreamtokenStream=assistant.chat("讲个笑话");tokenStream.onNext(token->System.out.print(token)).onComplete(response->System.out.println("\n完成")).onError(Throwable::printStackTrace).start();

返回类型用TokenStream,然后通过onNext逐个处理token。

如果你用Spring Boot + WebFlux,可以直接把TokenStream转成Flux<String>,然后通过Server-Sent Events推给前端,实现ChatGPT那种逐字输出的效果。


7. 本地模型部署:Ollama + DeepSeek

不想每次都调OpenAI的付费API?Ollama是你最好的朋友。

Ollama是一个本地大模型运行平台,支持CPU和GPU,能跑DeepSeek、Llama、Mistral等各种开源模型。

安装和运行很简单:

# 安装Ollamacurl-fsSLhttps://ollama.com/install.sh|sh# 拉取DeepSeek-R1模型(14b参数,本地可跑)ollama pull deepseek-r1:14b# 启动服务ollama serve

然后在LangChain4j里这样配置:

ChatLanguageModelmodel=OllamaChatModel.builder().baseUrl("http://localhost:11434").modelName("deepseek-r1:14b").build();

这样就完全本地化了,不用API Key,不用联网,数据安全也更有保障。


8. LangChain4j vs Spring AI,我到底该怎么选?

这个问题我被问过很多次。简单说:

LangChain4j适合你,如果:

  • 你需要丰富的功能(RAG、Agent、工具调用都很成熟)
  • 你不一定要用Spring生态(纯Java、Quarkus都可以)
  • 你需要兼容多种模型提供商

Spring AI适合你,如果:

  • 你已经在用Spring Boot/Cloud全家桶
  • 你追求和Spring生态的无缝集成(依赖注入、配置管理、可观测性)
  • 你能接受功能相对较少但更稳定的状态

我的个人选择:如果是Spring项目,我会用LangChain4j做核心AI逻辑,因为它的功能确实更全;配置文件管理、依赖注入这些基础设施就交给Spring。两者不冲突,可以一起用。


写在最后

LangChain4j现在确实还有很多不完美的地方:官方文档不够详细,某些功能还带着beta标签,不同模型提供商的适配程度参差不齐。但它在快速迭代,而且社区活跃度很高。

如果你是一个想用Java做大模型应用的开发者,LangChain4j是目前最好的选择之一。不要被它的不完美吓到——Python的LangChain一开始也是这样的。技术都是在踩坑中成长的,LangChain4j在成长,我们也是。

希望这篇文章能帮你少踩几个坑。如果你在实践中遇到什么问题,欢迎留言交流。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询