langchain4j+SpringBoot+DashScope(灵积)整合
创建Springboot项目
引入包
<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配置
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-chattemperature: 0.8top-p: 0.9max-retries: 3timeout: 60senable-search: false # 是否启用联网搜索# 嵌入模型配置(用于文本向量化)embedding-model:model-name: "text-embedding-v1"max-retries: 3
# 应用配置
app:ai:max-chat-history: 10default-temperature: 0.7
# 开发环境配置
logging:level:dev.langchain4j: DEBUGcom.example: DEBUGlangchain4j:dashscope:chat-model:timeout: 120s
核心配置类
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;/*** 普通聊天模型*/@Beanpublic ChatLanguageModel chatLanguageModel() {return QwenChatModel.builder().apiKey(apiKey).modelName(chatModelName).temperature(temperature).topP(topP).maxRetries(maxRetries).timeout(Duration.ofSeconds(60)).enableSearch(false).build();}/*** 流式聊天模型(用于实时输出)*/@Beanpublic StreamingChatLanguageModel streamingChatLanguageModel() {return QwenStreamingChatModel.builder().apiKey(apiKey).modelName(chatModelName).temperature(temperature).topP(topP).maxRetries(maxRetries).timeout(Duration.ofSeconds(60)).build();}/*** 嵌入模型(用于文本向量化)*/@Beanpublic EmbeddingModel embeddingModel() {return QwenEmbeddingModel.builder().apiKey(apiKey).modelName(embeddingModelName).maxRetries(maxRetries).timeout(Duration.ofSeconds(30)).build();}
}
服务层实现
基础聊天服务
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;}@PostConstructpublic 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参数版本);}
}
流式聊天服务
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);}}
}
记忆聊天服务
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);}
}
控制器层
基础聊天控制器
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 {@Autowiredprivate ChatService chatService;@Autowiredprivate StreamingChatService streamingChatService;@Autowiredprivate 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);}// 请求对象定义@Datapublic static class ChatRequest {@NotBlank(message = "消息不能为空")private String message;}@Datapublic static class MemoryChatRequest {@NotBlank(message = "会话ID不能为空")private String sessionId;@NotBlank(message = "消息不能为空")private String message;}@Datapublic static class TranslateRequest {@NotBlank(message = "文本不能为空")private String text;private String sourceLang = "中文";private String targetLang = "英文";}// 响应对象@Datapublic 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;}}
}
全局异常处理
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”: “英文”}’