SpringBoot 3.2.6 + Langchain4j 1.0.0-beta3 实战:5分钟搞定通义千问API对接(附完整代码)
SpringBoot 3.2.6 + Langchain4j 1.0.0-beta3 实战:5分钟搞定通义千问API对接(附完整代码)
最近在给一个内部知识库系统增加智能问答功能时,我面临一个选择:是直接调用各家大模型的原始HTTP API,还是找一个更优雅的封装框架?直接调用API虽然直接,但每个厂商的接口规范、参数格式、错误处理都不尽相同,代码很快就会变得臃肿且难以维护。经过一番调研,我发现了Langchain4j这个宝藏库——它就像Java生态里的“大模型中间件”,用统一的接口屏蔽了底层差异。更让我惊喜的是,它与SpringBoot的集成异常丝滑,特别是对接通义千问这类国内主流模型,几乎可以做到开箱即用。
这篇文章,我就以SpringBoot 3.2.6和Langchain4j 1.0.0-beta3为基础,带你走一遍从零开始、5分钟内完成通义千问API对接的完整流程。我会分享我实际配置中遇到的坑和解决方案,并提供可直接运行的代码。无论你是想快速验证一个AI想法,还是为现有Java应用注入智能能力,这篇实战指南都能让你少走弯路。
1. 环境准备与项目初始化
在开始敲代码之前,我们需要先把“舞台”搭好。这里我选择使用SpringBoot 3.2.6,因为它提供了稳定的Web和依赖管理基础。Langchain4j方面,我们使用其最新的1.0.0-beta3版本,这个版本对SpringBoot的支持已经相当成熟。
首先,通过Spring Initializr(start.spring.io)创建一个基础的SpringBoot项目。我习惯使用Maven,当然Gradle也同样适用。在选择依赖时,我们只需要勾选Spring Web即可,其他依赖我们会手动添加以保持清晰。
创建好项目后,打开pom.xml文件,我们需要引入Langchain4j的核心依赖。这里有个关键点:Langchain4j采用了BOM(Bill of Materials)的方式来管理其庞大的子模块版本,这能确保我们引入的各个组件版本兼容,避免令人头疼的依赖冲突。
<properties>
<java.version>17</java.version>
<spring-boot.version>3.2.6</spring-boot.version>
<!-- 定义Langchain4j版本 -->
<langchain4j.version>1.0.0-beta3</langchain4j.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Langchain4j核心启动器,提供基础集成能力 -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-spring-boot-starter</artifactId>
</dependency>
<!-- 专门用于对接阿里云DashScope(通义千问)的启动器 -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-community-dashscope-spring-boot-starter</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<!-- 引入SpringBoot官方BOM -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- 引入Langchain4j官方BOM,这是统一版本的关键 -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-bom</artifactId>
<version>${langchain4j.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
注意:
langchain4j-community-dashscope-spring-boot-starter这个依赖是社区维护的,专门用于对接阿里云的DashScope平台(即通义千问所在平台)。它内部封装了所有必要的HTTP客户端和序列化逻辑,我们无需关心底层细节。
依赖配置完成后,运行mvn clean compile确保一切正常。接下来,我们需要去获取访问大模型的“钥匙”——API Key。
2. 获取通义千问API Key与基础配置
没有API Key,我们的程序就像没有钥匙的车,无法启动。通义千问的API服务主要通过阿里云的“百炼”平台提供。别被名字吓到,申请过程其实非常简单。
- 访问平台:打开浏览器,访问阿里云百炼平台(Model Studio)。如果你没有阿里云账号,需要先注册一个。
- 开通服务:登录后,在控制台找到“模型服务”或“通义千问”相关入口。通常新用户会有一定的免费额度供体验,按照提示开通“DashScope灵积模型服务”即可。
- 创建API Key:在控制台侧边栏找到 “API-KEY管理” 菜单。点击“创建API-KEY”按钮。系统会生成一个以
sk-开头的字符串,这个字符串只会显示一次,请务必立即复制并妥善保存到安全的地方(比如本地的密码管理器或环境变量中)。如果丢失,只能重新创建。
拿到API Key后,我们回到SpringBoot项目进行配置。我不推荐将密钥硬编码在application.yml里然后提交到代码仓库,这有安全风险。更专业的做法是使用环境变量或配置中心。
我们在src/main/resources/application.yml中做如下配置:
server:
port: 8080
spring:
application:
name: langchain4j-demo
# Langchain4j 对接阿里云DashScope配置
langchain4j:
community:
dashscope:
chat-model:
# 关键:从环境变量读取API Key,避免泄露
api-key: ${DASHSCOPE_API_KEY:sk-your-test-key-here-placeholder}
# 指定使用的模型,qwen-max是能力较强的版本,也可用qwen-plus或qwen-turbo(更快)
model-name: qwen-max
# 温度参数,控制输出的随机性。0.0更确定/保守,1.0更随机/有创意。
temperature: 0.7
# 最大输出token数,控制回答长度
max-tokens: 2000
# 开启请求/响应日志,调试时非常有用
log-requests: true
log-responses: true
这里有几个参数值得展开说说:
model-name:通义千问提供了多个模型变体。qwen-max是综合能力最强的版本,适合复杂任务;qwen-turbo响应速度最快,成本也低,适合简单交互;qwen-plus则介于两者之间。你可以根据场景选择。temperature:这是一个核心参数。我把它理解为“想象力开关”。当设置为接近0时(如0.1),模型的回答会非常稳定、保守,适合事实性问答。当设置为较高值(如0.9),回答会更具创造性和多样性,适合写故事、想点子。对于大多数业务场景,0.7是一个不错的平衡点。max-tokens:这限制了模型单次回复的最大长度。设置太小可能导致回答被截断,太大则可能浪费资源。对于对话,1024-2048通常足够。
提示:如何安全地设置
${DASHSCOPE_API_KEY}?在本地运行时,可以在IDE的运行配置中设置环境变量,或者在系统终端执行export DASHSCOPE_API_KEY=your-real-key。在生产环境,则应使用K8s Secret、云厂商的密钥管理服务等。
3. 核心代码:构建你的第一个AI对话服务
配置完成后,Langchain4j的SpringBoot启动器会自动帮我们创建好一个QwenChatModel的Bean,并注入到Spring容器中。我们只需要像使用普通的Spring Bean一样注入它,就可以开始调用了。
下面我们来创建一个简单的REST控制器。我打算设计两个接口:一个用于单次对话,另一个用于模拟多轮对话(带简单的上下文记忆)。
首先,创建src/main/java/com/example/demo/controller/QwenChatController.java:
package com.example.demo.controller;
import dev.langchain4j.community.model.dashscope.QwenChatModel;
import dev.langchain4j.data.message.AiMessage;
import dev.langchain4j.data.message.ChatMessage;
import dev.langchain4j.data.message.SystemMessage;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.memory.ChatMemory;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.output.Response;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
@RestController
@RequestMapping("/api/ai")
@Slf4j
public class QwenChatController {
// 注入Langchain4j自动配置的Qwen模型Bean
@Autowired
private QwenChatModel qwenChatModel;
/**
* 简单单轮对话接口
* @param question 用户问题
* @return AI的回答
*/
@GetMapping("/chat")
public String simpleChat(@RequestParam String question) {
log.info("接收到问题:{}", question);
long start = System.currentTimeMillis();
// 核心调用:一行代码完成与大模型的交互
String answer = qwenChatModel.chat(question);
long cost = System.currentTimeMillis() - start;
log.info("模型响应耗时:{}ms", cost);
return answer;
}
}
启动你的SpringBoot应用,然后用浏览器、Postman或curl测试一下:
curl "http://localhost:8080/api/ai/chat?question=用Java写一个Hello World程序"
你应该能立刻收到通义千问返回的、格式良好的Java代码。是不是简单得有点不可思议?但这只是开始。现实中的对话往往需要上下文,比如你问“它有什么特点?”,模型需要知道“它”指的是上文中提到的某个事物。
接下来,我们实现一个带上下文记忆的聊天接口。Langchain4j提供了ChatMemory组件来管理对话历史。这里我们用MessageWindowChatMemory,它像一个滑动窗口,只保留最近N条消息。
我们在Controller里增加一个方法:
/**
* 带上下文的多轮对话接口
* 使用内存存储对话历史,适合演示,生产环境需考虑持久化
*/
@PostMapping("/chat/with-context")
public String chatWithContext(@RequestBody ChatRequest request) {
// 为每个会话(这里用userId模拟)维护独立的聊天内存
// MessageWindowChatMemory最多保留10轮对话消息
ChatMemory chatMemory = MessageWindowChatMemory.withMaxMessages(10);
// 如果有系统指令(比如设定AI角色),可以添加到内存
if (request.getSystemPrompt() != null && !request.getSystemPrompt().isEmpty()) {
chatMemory.add(SystemMessage.from(request.getSystemPrompt()));
}
// 将用户当前问题作为消息添加
chatMemory.add(UserMessage.from(request.getQuestion()));
// 从内存中获取所有消息作为上下文
List<ChatMessage> messages = new ArrayList<>(chatMemory.messages());
// 调用模型,传入完整的消息历史
Response<AiMessage> response = qwenChatModel.generate(messages);
AiMessage aiMessage = response.content();
// 将AI的回答也存入内存,完成一轮对话闭环
chatMemory.add(aiMessage);
return aiMessage.text();
}
// 简单的请求体封装
@Data
static class ChatRequest {
private String question;
private String systemPrompt; // 可选,例如:“你是一个专业的Java架构师”
}
为了使用@Data注解,你需要在pom.xml中加入Lombok依赖。这个接口通过POST JSON数据的方式工作,你可以指定一个systemPrompt来设定AI的角色,比如“你是一位幽默的科技博主”。模型会基于整个对话历史(内存中的消息)来生成回答,从而实现有上下文的多轮对话。
注意:上面的
MessageWindowChatMemory是存储在内存中的,应用重启后记录会消失。在实际项目中,如果需要持久化会话上下文,你需要实现自定义的ChatMemoryStore,将会话历史存入Redis或数据库。
4. 进阶应用与实战技巧
基本的对话功能实现后,我们可以探索一些更实用的场景。Langchain4j的强大之处在于它提供了丰富的“链”(Chain)和工具(Tool)来构建复杂应用。
场景一:构建一个智能内容摘要服务
假设我们需要从长篇文章中提取核心要点。我们可以利用Langchain4j的PromptTemplate来设计一个更结构化的提示。
首先,创建一个服务类ContentSummaryService.java:
package com.example.demo.service;
import dev.langchain4j.community.model.dashscope.QwenChatModel;
import dev.langchain4j.model.input.Prompt;
import dev.langchain4j.model.input.PromptTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
@Service
public class ContentSummaryService {
@Autowired
private QwenChatModel qwenChatModel;
private final PromptTemplate summaryTemplate = PromptTemplate.from(
"请将以下文本内容总结为不超过{{max_points}}个要点的列表,并用中文输出。\n" +
"文本内容:{{content}}\n" +
"要求:要点清晰、简洁,抓住核心信息。"
);
public String summarize(String content, int maxPoints) {
Map<String, Object> variables = new HashMap<>();
variables.put("content", content);
variables.put("max_points", maxPoints);
Prompt prompt = summaryTemplate.apply(variables);
return qwenChatModel.chat(prompt.text());
}
}
然后在Controller中调用这个服务。这种方式将提示词逻辑封装起来,更清晰也更易复用。
场景二:结构化数据提取 我们经常需要从非结构化的文本(如用户反馈、新闻)中提取出结构化的信息,比如实体、情感、分类等。我们可以通过设计特定的提示词,让模型返回JSON格式的数据,然后在代码中反序列化。
例如,提取用户评论中的产品名和情感:
public class ExtractionService {
// ... 注入模型
public ReviewInfo extractReviewInfo(String comment) {
String prompt = String.format(
"请从以下用户评论中提取信息,并以严格的JSON格式返回,只包含`productName`和`sentiment`(正面、负面、中性)两个字段。\n评论:%s",
comment
);
String jsonResponse = qwenChatModel.chat(prompt);
// 这里需要使用JSON库(如Jackson)将jsonResponse解析为ReviewInfo对象
// 为了简化,假设模型返回了正确格式的JSON
// ObjectMapper mapper = new ObjectMapper();
// return mapper.readValue(jsonResponse, ReviewInfo.class);
log.info("提取的JSON: {}", jsonResponse);
// 实际开发中应添加健壮的解析和异常处理
return new ReviewInfo("示例产品", "正面");
}
@Data
static class ReviewInfo {
private String productName;
private String sentiment;
}
}
性能与配置调优表 在实际使用中,根据不同的业务场景调整模型参数至关重要。下面这个表格总结了我的一些经验:
| 参数 | 推荐范围 | 适用场景 | 对性能/效果的影响 |
|---|---|---|---|
temperature | 0.1 - 0.9 | 0.1-0.3:事实问答、代码生成 0.7-0.9:创意写作、头脑风暴 | 值越低,输出越确定、一致;值越高,输出越多样、有创意。 |
maxTokens | 512 - 4096 | 512:短回复、分类 2048:常规对话、摘要 4096:长文生成、复杂分析 | 限制单次响应长度。设置过小回答会被截断,过大会增加不必要的延迟和成本。 |
topP (核采样) | 0.7 - 0.95 | 通常与temperature配合使用,控制输出词汇的集中度。 | 值越小,候选词集越集中,输出更可控;值越大,候选词集越广,输出更多样。通常保持默认即可。 |
modelName | qwen-turbo/max/plus | turbo:高并发、实时交互plus/max:复杂推理、高质量生成 | turbo响应最快,成本最低,能力适中;max能力最强,响应稍慢,成本较高。 |
错误处理与重试机制
网络请求总有可能失败,或者模型服务暂时不可用。一个健壮的生产级应用必须包含错误处理。Langchain4j的模型调用可能会抛出RuntimeException,我们可以使用Spring的@ControllerAdvice进行全局处理,或者使用重试框架。
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
@Service
public class RobustAIService {
@Autowired
private QwenChatModel qwenChatModel;
// 添加重试机制:最多重试3次,每次间隔2秒
@Retryable(value = { RuntimeException.class }, maxAttempts = 3, backoff = @Backoff(delay = 2000))
public String reliableChat(String question) {
return qwenChatModel.chat(question);
}
}
记得在启动类上添加@EnableRetry注解来启用重试功能。这能有效应对短暂的网络抖动或服务端过载。
5. 部署考量与最佳实践
当你完成开发,准备将应用部署到生产环境时,有几个关键点需要特别注意。
1. API密钥管理: 绝对不要将API密钥提交到代码仓库。除了之前提到的环境变量方式,在Kubernetes中可以使用Secret:
# deployment.yaml 环境变量部分示例
env:
- name: DASHSCOPE_API_KEY
valueFrom:
secretKeyRef:
name: dashscope-secret
key: api-key
在云服务器上,可以使用类似HashiCorp Vault的密钥管理工具。原则就是:代码和配置分离,密钥动态注入。
2. 超时与熔断配置: 大模型API的响应时间可能波动。我们需要在HTTP客户端层面设置合理的超时,并考虑引入熔断器(如Resilience4j)防止一个慢请求拖垮整个服务。虽然Langchain4j starter提供了一些配置,但在高并发场景下可能需要更精细的控制。
3. 成本监控与优化: 大模型调用按Token收费。我们需要关注:
- 缓存:对相同或相似的查询结果进行缓存,可以显著降低成本。可以考虑使用Spring Cache,将“问题”的哈希值作为Key,答案作为Value缓存一段时间。
- 用量日志:记录每次调用的模型、输入/输出Token数,便于后续分析和成本核算。DashScope平台本身也提供了用量监控面板。
- 模型选型:在非关键路径或对响应速度要求极高的场景(如实时对话),使用
qwen-turbo;在对质量要求高的场景(如报告生成),使用qwen-max。混合使用可以平衡成本与效果。
4. 异步与非阻塞调用:
如果你的服务需要同时处理多个AI请求,或者不想阻塞主线程,可以考虑使用异步调用。虽然QwenChatModel本身是同步接口,但你可以将其包装在CompletableFuture或使用Spring的@Async注解中,提升整体吞吐量。
@Service
public class AsyncChatService {
@Async // 需要配置线程池
public CompletableFuture<String> chatAsync(String question) {
return CompletableFuture.completedFuture(qwenChatModel.chat(question));
}
}
踩过几次坑之后,我发现最影响稳定性的往往不是代码逻辑,而是网络、密钥管理和超时设置。建议在项目初期就搭建一个简单的监控看板,至少把API调用成功率、平均响应时间和Token消耗量这几个指标监控起来。
好了,关于SpringBoot整合Langchain4j对接通义千问的核心流程和实战要点就分享到这里。从创建项目到写出第一个对话接口,可能真的用不了5分钟。但要把这个功能做得稳健、高效、可维护,就需要在配置、错误处理和架构设计上多花些心思。我提供的代码只是一个起点,你可以在此基础上,结合Langchain4j文档中关于Tools、RAG(检索增强生成)和Agents的更高级特性,去构建真正复杂而强大的AI应用。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐

所有评论(0)