第4篇:SpringBoot集成通义千问实战:从零搭建企业级智能客服,支持流式输出+对话记忆

16 阅读6分钟

SpringBoot集成通义千问实战:从零搭建企业级智能客服,支持流式输出+对话记忆

前言

现在Java圈人人都在聊AI,身边很多同行都有同款焦虑:

  • AI全是Python生态,Java开发会不会被淘汰?
  • 想转型AI,从零学算法、学Python,成本太高根本卷不动
  • 公司要求落地AI功能,不知道怎么和现有Java业务系统结合

其实完全没必要焦虑。企业里90%的核心业务系统都是Java栈,AI要真正落地生产、承接高并发、做数据合规,永远绕不开Java生态。我们不用去卷底层算法,只要学会「用Java对接大模型能力,嵌入现有业务」,就已经甩开了80%的同阶段开发者。

今天就带大家从零实现:SpringBoot + 通义千问大模型,搭建支持上下文记忆、流式输出的企业级智能客服。全程Java原生技术栈,代码复制就能跑,看完可以直接落地到公司项目里。

一、前置准备

1. 申请API Key

登录阿里云「百炼平台」申请通义千问API Key,新用户有免费额度,个人开发完全够用:

申请地址:dashscope.console.aliyun.com/

创建API-KEY后复制保存,后面配置文件里要用。

2. 技术栈说明

技术版本说明
JDK1.8兼容绝大多数企业项目
SpringBoot2.7.18稳定版,生产环境常用
通义千问SDK2.14.5官方最新稳定SDK,修复了流式输出bug
Lombok1.18.30简化代码

踩坑提醒:2.14之前版本的SDK流式输出存在断句bug,建议直接用2.14.5及以上版本。


二、项目搭建

1. 核心Maven依赖

<dependencies>
    <!-- SpringBoot Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <version>2.7.18</version>
    </dependency>

    <!-- 通义千问官方SDK -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>dashscope-sdk-java</artifactId>
        <version>2.14.5</version>
    </dependency>

    <!-- Lombok简化代码 -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.30</version>
        <optional>true</optional>
    </dependency>
</dependencies>

2. application.yml配置

server:
  port: 8080

# 通义千问配置
ai:
  qwen:
    api-key: 你的API_KEY
    model: qwen-turbo    # 轻量版,响应快成本低,适合客服场景
    temperature: 0.7     # 创意度:0偏严谨,1偏发散
    max-tokens: 2048     # 单次回复最大长度

三、核心功能实现

1. 统一请求响应实体

// ChatRequest.java  请求实体
@Data
public class ChatRequest {
    private String message;       // 用户提问
    private String sessionId;     // 会话ID,用于上下文记忆
}

// Result.java  统一返回结果
@Data
@AllArgsConstructor
public class Result<T> {
    private Integer code;
    private String msg;
    private T data;

    public static <T> Result<T> success(T data) {
        return new Result<>(200, "success", data);
    }
    public static <T> Result<T> fail(String msg) {
        return new Result<>(500, msg, null);
    }
}

2. 对话记忆管理

用本地缓存维护会话上下文,生产环境可替换为Redis:

@Component
public class ChatContextHolder {
    // key: sessionId  value: 历史消息列表
    private final Map<String, List<Message>> contextMap = new ConcurrentHashMap<>();
    // 单轮对话最多保留10条历史,避免token超限
    private static final int MAX_HISTORY = 10;

    /**
     * 获取会话历史消息
     */
    public List<Message> getHistory(String sessionId) {
        return contextMap.computeIfAbsent(sessionId, k -> new ArrayList<>());
    }

    /**
     * 添加消息到历史
     */
    public void addMessage(String sessionId, Message message) {
        List<Message> history = getHistory(sessionId);
        history.add(message);
        // 超出最大长度则移除最早的消息
        if (history.size() > MAX_HISTORY) {
            history.remove(0);
        }
    }

    /**
     * 清空会话
     */
    public void clearSession(String sessionId) {
        contextMap.remove(sessionId);
    }
}

3. 核心对话服务

@Service
public class QwenChatService {

    @Value("${ai.qwen.api-key}")
    private String apiKey;
    @Value("${ai.qwen.model}")
    private String model;
    @Value("${ai.qwen.temperature}")
    private Float temperature;
    @Value("${ai.qwen.max-tokens}")
    private Integer maxTokens;

    @Autowired
    private ChatContextHolder contextHolder;

    /**
     * 带上下文的对话
     */
    public String chatWithContext(String sessionId, String userMessage) {
        // 1. 初始化客户端
        DashScope dashScope = new DashScope(apiKey);
        
        // 2. 获取历史消息,设置系统人设
        List<Message> messages = contextHolder.getHistory(sessionId);
        // 首次对话加入系统prompt,定义客服人设
        if (messages.isEmpty()) {
            messages.add(Message.builder()
                    .role(Role.SYSTEM)
                    .content("你是专业的企业智能客服,回答严谨简洁,只回答业务相关问题,不知道的就说暂无相关资料。")
                    .build());
        }
        
        // 3. 添加用户当前提问
        Message userMsg = Message.builder().role(Role.USER).content(userMessage).build();
        messages.add(userMsg);

        // 4. 构建请求
        GenerationRequest request = GenerationRequest.builder()
                .model(model)
                .input(MessageInput.builder().messages(messages).build())
                .temperature(temperature)
                .maxTokens(maxTokens)
                .resultFormat(ResultFormat.MESSAGE)
                .build();

        try {
            // 5. 调用大模型
            GenerationResult result = dashScope.call(request);
            String reply = result.getOutput().getChoices().get(0).getMessage().getContent();
            
            // 6. 将AI回复存入上下文
            contextHolder.addMessage(sessionId, 
                Message.builder().role(Role.ASSISTANT).content(reply).build());
            
            return reply;
        } catch (Exception e) {
            e.printStackTrace();
            return "抱歉,系统繁忙,请稍后再试。";
        }
    }
}

4. 流式输出实现(SSE)

企业级客服必备功能,逐字返回体验更好,避免用户长时间等待:

@RestController
@RequestMapping("/api/chat")
public class ChatController {

    @Autowired
    private QwenChatService chatService;

    /**
     * 普通对话接口
     */
    @PostMapping("/send")
    public Result<String> chat(@RequestBody ChatRequest request) {
        String reply = chatService.chatWithContext(request.getSessionId(), request.getMessage());
        return Result.success(reply);
    }

    /**
     * 流式输出接口 SSE
     */
    @GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public SseEmitter streamChat(String sessionId, String message) {
        SseEmitter emitter = new SseEmitter(60000L);
        
        // 异步处理流式返回
        CompletableFuture.runAsync(() -> {
            try {
                DashScope dashScope = new DashScope(apiKey);
                // 构建流式请求
                GenerationRequest request = GenerationRequest.builder()
                        .model(model)
                        .input(MessageInput.builder()
                                .messages(contextHolder.getHistory(sessionId))
                                .build())
                        .incrementalOutput(true) // 开启增量输出
                        .build();

                // 流式回调,逐段返回内容
                dashScope.streamCall(request, new ResultCallback<GenerationResult>() {
                    @Override
                    public void onEvent(GenerationResult result) {
                        try {
                            String text = result.getOutput().getChoices().get(0).getMessage().getContent();
                            if (StrUtil.isNotBlank(text)) {
                                emitter.send(SseEmitter.event().data(text));
                            }
                        } catch (IOException e) {
                            emitter.completeWithError(e);
                        }
                    }
                    @Override
                    public void onComplete() {
                        emitter.complete();
                    }
                    @Override
                    public void onError(Exception e) {
                        emitter.completeWithError(e);
                    }
                });
            } catch (Exception e) {
                emitter.completeWithError(e);
            }
        });
        return emitter;
    }
}

四、接口测试

1. 普通对话

POST请求 http://localhost:8080/api/chat/send 请求体:

{
    "sessionId": "test001",
    "message": "你们公司有哪些产品?"
}

返回示例:

{
    "code": 200,
    "msg": "success",
    "data": "我们公司目前有企业管理系统、智能客服平台、数据中台三大核心产品,请问您想了解哪一款?"
}

同一会话ID继续提问,会携带上下文,实现多轮对话。

2. 流式输出

GET请求 http://localhost:8080/api/chat/stream?sessionId=test001&message=介绍一下智能客服平台 浏览器或前端直接调用,会看到文字逐段输出,和ChatGPT体验一致。


五、企业级落地优化建议

  1. API Key安全:生产环境不要硬编码在配置文件里,建议用Nacos/Apollo配置中心加密存储,配合网关鉴权。
  2. 限流降级:增加接口限流(比如Sentinel),防止恶意刷接口消耗token;大模型故障时自动降级为人工客服入口。
  3. 敏感词过滤:提问和回复都要过敏感词校验,避免违规内容,满足等保合规要求。
  4. 知识库对接:纯大模型回答会胡说八道,生产环境要配合RAG检索增强,基于企业内部文档回答,准确率更高。
  5. 数据统计:记录每轮对话的token消耗、响应时长、用户满意度,方便成本核算和效果优化。

六、写在最后

AI不是来淘汰Java开发的,是来淘汰「只会写CRUD、不肯迭代自己」的开发者的。 不用放弃多年的Java经验去卷纯算法,在现有技术栈上叠加AI落地能力,就是接下来3年最稳的职业护城河。

后续我会持续更新Spring AI、LangChain4j、Java+AI项目落地、一线踩坑经验,陪所有Java同行一起在AI时代站稳脚跟。

原创不易,如果文章对你有帮助,欢迎点赞、转发给身边的同事~