创建Springboot项目

引入包
bash
<project>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
</parent>
<properties>
<java.version>11</java.version>
<langchain4j.version>0.29.1</langchain4j.version>
</properties>
<dependencies>
<!-- Spring Boot Starters -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!-- LangChain4j Core -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j</artifactId>
<version>${langchain4j.version}</version>
</dependency>
<!-- LangChain4j DashScope Integration -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-dashscope</artifactId>
<version>${langchain4j.version}</version>
</dependency>
<!-- LangChain4j Spring Boot Starter -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-spring-boot-starter</artifactId>
<version>${langchain4j.version}</version>
</dependency>
<!-- Tools -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
application.yml配置
bash
spring:
application:
name: springboot-dashscope-demo
# DashScope 配置
langchain4j:
dashscope:
# 从阿里云DashScope控制台获取
api-key: ${DASHSCOPE_API_KEY:your-dashscope-api-key-here}
# 聊天模型配置
chat-model:
model-name: "qwen-plus" # 可选: qwen-turbo, qwen-plus, qwen-max, qwen-7b-chat, qwen-14b-chat
temperature: 0.8
top-p: 0.9
max-retries: 3
timeout: 60s
enable-search: false # 是否启用联网搜索
# 嵌入模型配置(用于文本向量化)
embedding-model:
model-name: "text-embedding-v1"
max-retries: 3
# 应用配置
app:
ai:
max-chat-history: 10
default-temperature: 0.7
# 开发环境配置
logging:
level:
dev.langchain4j: DEBUG
com.example: DEBUG
langchain4j:
dashscope:
chat-model:
timeout: 120s
核心配置类
bash
package com.example.config;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.chat.StreamingChatLanguageModel;
import dev.langchain4j.model.dashscope.QwenChatModel;
import dev.langchain4j.model.dashscope.QwenStreamingChatModel;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.model.dashscope.QwenEmbeddingModel;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.time.Duration;
@Configuration
public class DashScopeConfig {
@Value("${langchain4j.dashscope.api-key}")
private String apiKey;
@Value("${langchain4j.dashscope.chat-model.model-name:qwen-plus}")
private String chatModelName;
@Value("${langchain4j.dashscope.chat-model.temperature:0.8}")
private Double temperature;
@Value("${langchain4j.dashscope.chat-model.top-p:0.9}")
private Double topP;
@Value("${langchain4j.dashscope.chat-model.max-retries:3}")
private Integer maxRetries;
@Value("${langchain4j.dashscope.embedding-model.model-name:text-embedding-v1}")
private String embeddingModelName;
/**
* 普通聊天模型
*/
@Bean
public ChatLanguageModel chatLanguageModel() {
return QwenChatModel.builder()
.apiKey(apiKey)
.modelName(chatModelName)
.temperature(temperature)
.topP(topP)
.maxRetries(maxRetries)
.timeout(Duration.ofSeconds(60))
.enableSearch(false)
.build();
}
/**
* 流式聊天模型(用于实时输出)
*/
@Bean
public StreamingChatLanguageModel streamingChatLanguageModel() {
return QwenStreamingChatModel.builder()
.apiKey(apiKey)
.modelName(chatModelName)
.temperature(temperature)
.topP(topP)
.maxRetries(maxRetries)
.timeout(Duration.ofSeconds(60))
.build();
}
/**
* 嵌入模型(用于文本向量化)
*/
@Bean
public EmbeddingModel embeddingModel() {
return QwenEmbeddingModel.builder()
.apiKey(apiKey)
.modelName(embeddingModelName)
.maxRetries(maxRetries)
.timeout(Duration.ofSeconds(30))
.build();
}
}
服务层实现
基础聊天服务
bash
package com.example.service;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.List;
@Slf4j
@Service
public class ChatService {
private final ChatLanguageModel chatLanguageModel;
private Assistant assistant;
// AI服务接口定义
interface Assistant {
@SystemMessage("""
你是一个专业的AI助手,基于通义千问模型。
请用中文回答用户的问题,回答要准确、专业、友好。
如果遇到不知道的问题,请诚实地告知。
""")
String chat(@UserMessage String message);
@SystemMessage("你是一个专业的翻译专家,精通多国语言。")
String translate(@UserMessage String text, String sourceLang, String targetLang);
@SystemMessage("你是一个代码专家,擅长多种编程语言。")
String explainCode(@UserMessage String code);
@SystemMessage("你是一个创意写作助手,擅长各种文体写作。")
String writeContent(@UserMessage String topic, String style, String length);
}
public ChatService(ChatLanguageModel chatLanguageModel) {
this.chatLanguageModel = chatLanguageModel;
}
@PostConstruct
public void init() {
this.assistant = AiServices.create(Assistant.class, chatLanguageModel);
}
/**
* 普通聊天
*/
public String chat(String message) {
log.info("用户提问: {}", message);
long startTime = System.currentTimeMillis();
try {
String response = assistant.chat(message);
long endTime = System.currentTimeMillis();
log.info("AI回复完成,耗时: {}ms", endTime - startTime);
return response;
} catch (Exception e) {
log.error("AI服务调用失败", e);
throw new RuntimeException("AI服务暂时不可用,请稍后重试");
}
}
/**
* 翻译功能
*/
public String translate(String text, String sourceLang, String targetLang) {
log.info("翻译请求: {} [{} -> {}]", text, sourceLang, targetLang);
return assistant.translate(text, sourceLang, targetLang);
}
/**
* 代码解释
*/
public String explainCode(String code) {
log.info("代码解释请求: {}", code.substring(0, Math.min(code.length(), 100)) + "...");
return assistant.explainCode(code);
}
/**
* 内容创作
*/
public String writeContent(String topic, String style, String length) {
String prompt = String.format("主题: %s, 风格: %s, 长度: %s", topic, style, length);
return assistant.writeContent(prompt, style, length);
}
/**
* 获取支持的模型列表
*/
public List<String> getSupportedModels() {
return List.of(
"qwen-turbo", // 速度最快,成本最低
"qwen-plus", // 平衡性能与成本
"qwen-max", // 能力最强
"qwen-7b-chat", // 7B参数版本
"qwen-14b-chat" // 14B参数版本
);
}
}
流式聊天服务
bash
package com.example.service;
import dev.langchain4j.model.chat.StreamingChatLanguageModel;
import dev.langchain4j.model.output.Response;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.TokenStream;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import java.util.function.Consumer;
@Slf4j
@Service
public class StreamingChatService {
private final StreamingChatLanguageModel streamingModel;
private StreamingAssistant assistant;
interface StreamingAssistant {
@SystemMessage("你是一个有用的AI助手,请用流式方式回复用户。")
TokenStream chat(@SystemMessage String message);
}
public StreamingChatService(StreamingChatLanguageModel streamingModel) {
this.streamingModel = streamingModel;
this.assistant = AiServices.create(StreamingAssistant.class, streamingModel);
}
/**
* 流式聊天
*/
public Flux<String> streamChat(String message) {
return Flux.create(sink -> {
try {
assistant.chat(message)
.onNext(sink::next)
.onComplete(sink::complete)
.onError(sink::error)
.start();
} catch (Exception e) {
sink.error(e);
}
});
}
/**
* 带回调的流式聊天
*/
public void streamChatWithCallback(String message, Consumer<String> onNext, Runnable onComplete, Consumer<Throwable> onError) {
try {
assistant.chat(message)
.onNext(onNext::accept)
.onComplete(onComplete::run)
.onError(onError::accept)
.start();
} catch (Exception e) {
onError.accept(e);
}
}
}
记忆聊天服务
bash
package com.example.service;
import dev.langchain4j.memory.ChatMemory;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.service.MemoryId;
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
@Service
public class MemoryChatService {
private final Map<String, ChatWithMemory> assistants = new ConcurrentHashMap<>();
private final ChatLanguageModel chatLanguageModel;
@Value("${app.ai.max-chat-history:10}")
private int maxChatHistory;
interface ChatWithMemory {
@SystemMessage("你是一个友好的AI助手,能够记住对话历史。")
String chat(@MemoryId String sessionId, @UserMessage String userMessage);
}
public MemoryChatService(ChatLanguageModel chatLanguageModel) {
this.chatLanguageModel = chatLanguageModel;
}
/**
* 带记忆的聊天
*/
public String chatWithMemory(String sessionId, String message) {
ChatWithMemory assistant = assistants.computeIfAbsent(sessionId, id -> {
ChatMemory chatMemory = MessageWindowChatMemory.withMaxMessages(maxChatHistory);
return AiServices.builder(ChatWithMemory.class)
.chatLanguageModel(chatLanguageModel)
.chatMemory(chatMemory)
.build();
});
return assistant.chat(sessionId, message);
}
/**
* 生成新的会话ID
*/
public String createNewSession() {
return UUID.randomUUID().toString();
}
/**
* 清除会话记忆
*/
public void clearSessionMemory(String sessionId) {
assistants.remove(sessionId);
}
}
控制器层
基础聊天控制器
bash
package com.example.controller;
import com.example.service.ChatService;
import com.example.service.MemoryChatService;
import com.example.service.StreamingChatService;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import javax.validation.constraints.NotBlank;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/ai")
@Validated
public class AIController {
@Autowired
private ChatService chatService;
@Autowired
private StreamingChatService streamingChatService;
@Autowired
private MemoryChatService memoryChatService;
/**
* 普通聊天
*/
@PostMapping("/chat")
public ApiResponse<String> chat(@RequestBody @Validated ChatRequest request) {
try {
String response = chatService.chat(request.getMessage());
return ApiResponse.success(response);
} catch (Exception e) {
return ApiResponse.error(e.getMessage());
}
}
/**
* 流式聊天
*/
@PostMapping(value = "/chat/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> streamChat(@RequestBody @Validated ChatRequest request) {
return streamingChatService.streamChat(request.getMessage());
}
/**
* 带记忆的聊天
*/
@PostMapping("/chat/with-memory")
public ApiResponse<String> chatWithMemory(@RequestBody @Validated MemoryChatRequest request) {
try {
String response = memoryChatService.chatWithMemory(request.getSessionId(), request.getMessage());
return ApiResponse.success(response);
} catch (Exception e) {
return ApiResponse.error(e.getMessage());
}
}
/**
* 创建新会话
*/
@PostMapping("/session/new")
public ApiResponse<Map<String, String>> createNewSession() {
String sessionId = memoryChatService.createNewSession();
return ApiResponse.success(Map.of("sessionId", sessionId));
}
/**
* 翻译功能
*/
@PostMapping("/translate")
public ApiResponse<String> translate(@RequestBody @Validated TranslateRequest request) {
try {
String result = chatService.translate(
request.getText(),
request.getSourceLang(),
request.getTargetLang()
);
return ApiResponse.success(result);
} catch (Exception e) {
return ApiResponse.error(e.getMessage());
}
}
/**
* 获取支持的模型列表
*/
@GetMapping("/models")
public ApiResponse<List<String>> getSupportedModels() {
List<String> models = chatService.getSupportedModels();
return ApiResponse.success(models);
}
// 请求对象定义
@Data
public static class ChatRequest {
@NotBlank(message = "消息不能为空")
private String message;
}
@Data
public static class MemoryChatRequest {
@NotBlank(message = "会话ID不能为空")
private String sessionId;
@NotBlank(message = "消息不能为空")
private String message;
}
@Data
public static class TranslateRequest {
@NotBlank(message = "文本不能为空")
private String text;
private String sourceLang = "中文";
private String targetLang = "英文";
}
// 响应对象
@Data
public static class ApiResponse<T> {
private boolean success;
private String message;
private T data;
public static <T> ApiResponse<T> success(T data) {
ApiResponse<T> response = new ApiResponse<>();
response.setSuccess(true);
response.setMessage("成功");
response.setData(data);
return response;
}
public static <T> ApiResponse<T> error(String message) {
ApiResponse<T> response = new ApiResponse<>();
response.setSuccess(false);
response.setMessage(message);
return response;
}
}
}
全局异常处理
bash
package com.example.handler;
import com.example.controller.AIController;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.validation.ConstraintViolationException;
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 参数校验异常
*/
@ExceptionHandler(BindException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public AIController.ApiResponse<?> handleBindException(BindException e) {
String message = e.getBindingResult().getFieldError().getDefaultMessage();
return AIController.ApiResponse.error(message);
}
/**
* 参数校验异常
*/
@ExceptionHandler(ConstraintViolationException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public AIController.ApiResponse<?> handleConstraintViolationException(ConstraintViolationException e) {
return AIController.ApiResponse.error(e.getMessage());
}
/**
* 业务异常
*/
@ExceptionHandler(RuntimeException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public AIController.ApiResponse<?> handleRuntimeException(RuntimeException e) {
log.error("业务异常", e);
return AIController.ApiResponse.error(e.getMessage());
}
/**
* 其他异常
*/
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public AIController.ApiResponse<?> handleException(Exception e) {
log.error("系统异常", e);
return AIController.ApiResponse.error("系统繁忙,请稍后重试");
}
}
测试使用
普通聊天
curl -X POST http://localhost:8080/api/ai/chat
-H "Content-Type: application/json"
-d '{"message": "你好,介绍一下Spring Boot"}'
创建带记忆的会话
curl -X POST http://localhost:8080/api/ai/session/new
带记忆的聊天(使用上一步返回的sessionId)
curl -X POST http://localhost:8080/api/ai/chat/with-memory
-H "Content-Type: application/json"
-d '{"sessionId": "your-session-id", "message": "记住我喜欢编程"}'
流式聊天
curl -X POST http://localhost:8080/api/ai/chat/stream
-H "Content-Type: application/json"
-d '{"message": "讲一个故事"}'
--no-buffer
翻译
curl -X POST http://localhost:8080/api/ai/translate
-H "Content-Type: application/json"
-d '{"text": "这是一个测试", "sourceLang": "中文", "targetLang": "英文"}'