langchain4j+SpringBoot+DashScope(灵积)整合

创建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": "英文"}'

相关推荐
ZhengEnCi3 小时前
SpringBoot 配置文件完全指南-从入门到精通
java·spring boot
L7ink3 小时前
解放双手!Moni:一款用 AI 帮你自动记账、分析消费的智能助手
spring boot·openai
摇滚侠3 小时前
Spring Boot 3零基础教程,依赖管理机制,笔记06
spring boot·笔记·后端
贾维思基5 小时前
产品经理哭晕,我用AI+MCP把他需求秒了!
ai编程
深度学习机器6 小时前
AI Agent上下文工程设计指南|附实用工具推荐
langchain·llm·agent
paopaokaka_luck6 小时前
基于SpringBoot+Vue的少儿编程培训机构管理系(WebSocket及时通讯、协同过滤算法、Echarts图形化分析)
java·vue.js·spring boot·后端·spring
用户4099322502126 小时前
PostgreSQL 查询慢?是不是忘了优化 GROUP BY、ORDER BY 和窗口函数?
后端·ai编程·trae
陈小桔7 小时前
Springboot之常用注解
java·spring boot·后端
阿挥的编程日记8 小时前
基于SpringBoot的高校(学生综合)服务平台的设计与实现
java·spring boot·后端·spring·mybatis