LangChain4j源码到使用 java大模型开发必备的工具包
本教程共包含 6 个核心示例,从基础到高级,循序渐进地介绍 LangChain4j 框架的使用:
📦 基础部分
|
|
|
|
|---|---|---|
| 示例01 |
|
|
| 示例02 |
|
|
| 示例03 |
|
|
| 示例04 |
|
|
| 05-01 |
|
|
| 05-02 |
|
|
| 05-03 |
|
|
| 05-04 |
|
|
| 05-05 |
|
|
| 05-06 |
|
|
| 示例06 |
|
|
为什么需要 LangChain4j?
LangChain4j 官方文档:https://docs.langchain4j.dev/
LangChain4j 解决了什么问题?
-
用接口+注解定义 AI 服务,告别字符串拼接 -
自动类型转换,支持 String、枚举、boolean、POJO -
内置记忆管理,轻松实现多轮对话 -
简化工具调用,AI 自动决定何时调用
环境准备
Maven 依赖
<properties>
<langchain4j.version>1.11.0</langchain4j.version>
</properties>
<dependencies>
<!-- LangChain4j Core -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j</artifactId>
<version>${langchain4j.version}</version>
</dependency>
<!-- OpenAI 兼容模块(阿里百炼、DeepSeek 等都兼容) -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-open-ai</artifactId>
<version>${langchain4j.version}</version>
</dependency>
</dependencies>
模型配置
本教程使用阿里百炼(DashScope)的通义千问模型,通过 OpenAI 兼容 API 接入:
publicclassDashScopeProperties{
// API Key - 请替换为您自己的 Key
publicstaticfinal String API_KEY = "your-api-key";
// 阿里百炼 OpenAI 兼容 API URL
publicstaticfinal String BASE_URL = "https://dashscope.aliyuncs.com/compatible-mode/v1";
// 模型名称
publicstaticfinal String DEFAULT_MODEL_NAME = "qwen-plus";
publicstaticfinal String QWEN_MAX = "qwen-max";
publicstaticfinal String QWEN_PLUS = "qwen-plus";
publicstaticfinal String QWEN_TURBO = "qwen-turbo";
}
示例 01:Hello World
最简单的示例,使用 ChatModel 进行对话。
代码实现
publicclassHelloWorld{
publicstaticvoidmain(String[] args){
// 创建 ChatModel
ChatModel model = OpenAiChatModel.builder()
.baseUrl(BASE_URL)
.apiKey(API_KEY)
.modelName(DEFAULT_MODEL_NAME)
.build();
// 发送消息
String answer = model.chat("你好,请用中文说 Hello World");
System.out.println(answer);
}
}
设计重点
ChatModel 是最基础的模型接口:
-
调用 chat()方法发送消息 -
返回字符串类型的响应 -
每次调用都是独立的,模型不记住之前的对话
爱码说:这是最原始的调用方式,适合单轮问答场景。
示例 02:模型参数配置
大模型支持多种参数,最常用的是 temperature 和 maxTokens。
代码实现
publicclassModelParametersExample{
publicstaticvoidmain(String[] args){
ChatModel model = OpenAiChatModel.builder()
.baseUrl(BASE_URL)
.apiKey(API_KEY)
.modelName(QWEN_PLUS)
.temperature(0.3) // 温度参数
.maxTokens(500) // 最大输出长度
.build();
String response = model.chat("用三行话解释如何画一幅美丽的画");
System.out.println(response);
}
}
设计重点
temperature(温度):
-
值范围 0~1,默认约 0.7 -
值越小,输出越确定、一致性强 -
值越大,输出越随机、创意性强
maxTokens(最大令牌数):
-
限制输出长度,控制成本 -
一个中文字约等于 1-2 个 token
爱码说:temperature 不是越低越好,要根据场景选择。翻译场景需要确定性,而创意场景需要随机性。
示例 03:流式输出
等待大模型完整响应再显示,用户体验很差。流式输出(Streaming) 实现打字机效果。
代码实现
publicclassStreamingExample{
publicstaticvoidmain(String[] args){
StreamingChatModel model = OpenAiStreamingChatModel.builder()
.baseUrl(BASE_URL)
.apiKey(API_KEY)
.modelName(DEFAULT_MODEL_NAME)
.build();
String prompt = "写一首关于程序员和空指针异常的短诗,最多10行";
CompletableFuture<ChatResponse> future = new CompletableFuture<>();
model.chat(prompt, new StreamingChatResponseHandler() {
@Override
publicvoidonPartialResponse(String partialResponse){
// 逐块输出
System.out.print(partialResponse);
}
@Override
publicvoidonCompleteResponse(ChatResponse completeResponse){
System.out.println("\n--- 完成 ---");
future.complete(completeResponse);
}
@Override
publicvoidonError(Throwable error){
future.completeExceptionally(error);
}
});
future.join();
}
}
设计重点
StreamingChatModel 与 ChatModel 的区别:
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
StreamingChatResponseHandler 三个回调方法:
-
onPartialResponse()– 每收到一块数据就调用 -
onCompleteResponse()– 全部完成后调用 -
onError()– 发生错误时调用
爱码说:Web 应用中可以配合 SSE(Server-Sent Events)实现真正的打字机效果。
示例 04:大模型没有记忆
这是一个非常重要的概念:大模型本身是没有记忆的!
代码验证
publicclassMemoryExample{
publicstaticvoidmain(String[] args){
ChatModel model = createChatModel();
// 第一次对话
String answer1 = model.chat("你好!我叫小明。");
System.out.println("AI: " + answer1);
// 第二次对话
String answer2 = model.chat("我叫什么名字?");
System.out.println("AI: " + answer2);
// AI 回答:抱歉,我不知道您的名字...
}
}
设计重点
为什么模型记不住?
-
HTTP 是无状态的,每次调用都是独立的请求 -
模型只看到当前这条消息,看不到之前的对话
如何实现记忆?
-
每次请求时,把之前的对话历史一起发送 -
LangChain4j 提供了 ChatMemory 来管理对话历史
爱码说:这就像你每次打电话都要重新自我介绍,因为对方不记得你是谁。解决方法就是——每次都说”我是上次跟您聊过的xxx”。
示例 05:AI Service 入门
直接用 ChatModel 有什么问题?
-
手动拼接 prompt 字符串 -
返回值只能是自己解析 -
记忆管理需要手动处理
AiServices 提供了更优雅的方式:用接口定义 AI 服务。
05-01:简单 AI Service
// 定义接口
publicinterfaceSimpleAssistant{
String chat(String message);
}
// 使用
SimpleAssistant assistant = AiServices.create(SimpleAssistant.class, model);
String answer = assistant.chat("请翻译: 'Hello, how are you?'");
就这么简单!不需要实现类,LangChain4j 会自动生成代理。
05-02:系统消息(设定角色)
publicinterfaceChefAssistant{
@SystemMessage("你是一位专业的厨师。你友好、礼貌且简洁。")
String answer(String question);
}
ChefAssistant chef = AiServices.create(ChefAssistant.class, model);
chef.answer("烤鸡需要多长时间?");
// AI 会以厨师的角色回答
@SystemMessage 注解设定 AI 的角色和行为规范。
05-03:变量模板
publicinterfaceTextUtilsAssistant{
@SystemMessage("你是一位专业的{{language}}翻译")
@UserMessage("翻译以下文本: {{text}}")
String translate(@V("text") String text, @V("language") String language);
}
TextUtilsAssistant utils = AiServices.create(TextUtilsAssistant.class, model);
utils.translate("Hello", "中文");
注解说明:
-
@V("变量名")– 方法参数绑定到模板变量 -
{{变量名}}– 模板中的占位符 -
{{it}}– 特殊变量,代表第一个参数
05-04:返回枚举和布尔值
// 情感枚举
publicenum Sentiment {
POSITIVE, NEUTRAL, NEGATIVE
}
publicinterfaceSentimentAnalyzer{
@UserMessage("分析以下文本的情感: {{it}}")
Sentiment analyzeSentimentOf(String text);
@UserMessage("以下文本是否是正面情感?")
booleanisPositive(String text);
}
SentimentAnalyzer analyzer = AiServices.create(SentimentAnalyzer.class, model);
Sentiment s = analyzer.analyzeSentimentOf("这个产品非常好用!"); // POSITIVE
boolean p = analyzer.isPositive("这个产品太差了!"); // false
重点:AI Service 自动将 AI 响应转换为目标类型!
05-05:返回 POJO(结构化数据提取)
publicclassPerson{
@Description("人的名字")
private String firstName;
@Description("人的姓氏")
private String lastName;
@Description("出生日期")
private LocalDate birthDate;
// getter/setter...
}
publicinterfacePersonExtractor{
@UserMessage("从以下文本中提取人物信息: {{it}}")
Person extractPersonFrom(String text);
}
PersonExtractor extractor = AiServices.create(PersonExtractor.class, model);
String text = "1968年,国庆节的余韵中,一个名叫小明的孩子降生了。这个新生儿姓张...";
Person person = extractor.extractPersonFrom(text);
// Person{firstName="小明", lastName="张", birthDate=1968-10-01}
@Description 注解帮助 AI 理解每个字段的含义,提高提取准确性。
爱码说:这在 RAG、知识图谱构建中非常有用。从非结构化文本提取结构化数据,只需要定义一个 POJO!
示例 05-06:对话记忆
单用户记忆
publicinterfaceAssistantWithMemory{
String chat(String message);
static AssistantWithMemory create(ChatModel model){
ChatMemory memory = MessageWindowChatMemory.withMaxMessages(10);
return AiServices.builder(AssistantWithMemory.class)
.chatModel(model)
.chatMemory(memory)
.build();
}
}
// 使用
AssistantWithMemory assistant = AssistantWithMemory.create(model);
assistant.chat("你好!我叫小明。");
assistant.chat("我叫什么名字?"); // AI 会回答:你叫小明
MessageWindowChatMemory:
-
自动保存最近 N 条消息 -
每次调用时自动带上历史消息
多用户记忆
publicinterfaceAssistantWithMultiUserMemory{
String chat(@MemoryId int userId, @UserMessage String message);
static AssistantWithMultiUserMemory create(ChatModel model){
return AiServices.builder(AssistantWithMultiUserMemory.class)
.chatModel(model)
.chatMemoryProvider(id -> MessageWindowChatMemory.withMaxMessages(10))
.build();
}
}
// 使用
AssistantWithMultiUserMemory assistant = AssistantWithMultiUserMemory.create(model);
assistant.chat(1, "你好,我叫小明");
assistant.chat(2, "你好,我叫小红");
assistant.chat(1, "我叫什么名字?"); // AI 回答:你叫小明
assistant.chat(2, "我叫什么名字?"); // AI 回答:你叫小红
@MemoryId 注解标识用户 ID,每个用户有独立的对话记忆。
爱码说:这在多用户聊天场景中非常重要。用户 A 的对话不应该影响用户 B。
示例 06:工具调用(Function Calling)
工具调用是大模型的高级能力:AI 可以调用你提供的函数来完成任务。
定义工具
publicclassCalculator{
@Tool("计算字符串的长度")
intstringLength(String s){
return s.length();
}
@Tool("计算两个数的和")
intadd(int a, int b){
return a + b;
}
@Tool("计算一个数的平方根")
doublesqrt(int x){
return Math.sqrt(x);
}
}
@Tool 注解标记可被 AI 调用的方法,描述帮助 AI 理解何时调用。
注册工具并使用
interfaceAssistant{
String chat(String userMessage);
}
Assistant assistant = AiServices.builder(Assistant.class)
.chatModel(model)
.tools(newCalculator()) // 注册工具
.chatMemory(MessageWindowChatMemory.withMaxMessages(10))
.build();
String question = "请计算: 'hello' 和 'world' 两个单词的字母数量之和的平方根是多少?";
String answer = assistant.chat(question);
AI 的执行过程
用户: 'hello' 和 'world' 两个单词的字母数量之和的平方根是多少?
AI 自动执行:
1. stringLength('hello') -> 5
2. stringLength('world') -> 5
3. add(5, 5) -> 10
4. sqrt(10) -> 3.162...
最终回答: 'hello' 有 5 个字母,'world' 也有 5 个字母,它们之和是 10,
10 的平方根约等于 3.162。
AI 会自动决定:
-
是否需要调用工具 -
调用哪个工具 -
传递什么参数 -
如何组合结果
爱码说:这就是 Agent 的雏形!AI 不只是回答问题,还能”动手做事”。查询天气、调用数据库、执行计算…都可以封装成工具。
总结
核心组件对比
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
AiServices 支持的返回类型
-
String– 原始文本 -
boolean/Boolean– 布尔判断 -
枚举类型 – 分类任务 -
List<String>– 列表 -
POJO – 结构化数据提取
最佳实践
-
简单场景:直接用 ChatModel -
需要类型转换:使用 AiServices -
多轮对话:配置 ChatMemory -
多用户场景:使用 @MemoryId + chatMemoryProvider -
需要外部能力:定义 @Tool 工具
思路,比结论重要
LangChain4j 解决了什么问题?让 Java 开发者用熟悉的方式(接口、注解、POJO)与大模型交互。
不需要手动拼接字符串,不需要手动解析 JSON,不需要手动管理对话历史。定义好接口,剩下的交给框架。
点赞+关注 后台回复 langchain4j 获取源码
夜雨聆风
