惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Simon Willison's Weblog
Simon Willison's Weblog
P
Privacy International News Feed
www.infosecurity-magazine.com
www.infosecurity-magazine.com
T
Troy Hunt's Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
Attack and Defense Labs
Attack and Defense Labs
S
Secure Thoughts
V2EX - 技术
V2EX - 技术
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
O
OpenAI News
Cloudbric
Cloudbric
Google Online Security Blog
Google Online Security Blog
Schneier on Security
Schneier on Security
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Help Net Security
Help Net Security
Cyberwarzone
Cyberwarzone
G
GRAHAM CLULEY
L
Lohrmann on Cybersecurity
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Spread Privacy
Spread Privacy
NISL@THU
NISL@THU
N
News and Events Feed by Topic
T
Tenable Blog
S
Security @ Cisco Blogs
N
News and Events Feed by Topic
The Hacker News
The Hacker News
C
CXSECURITY Database RSS Feed - CXSecurity.com
宝玉的分享
宝玉的分享
月光博客
月光博客
酷 壳 – CoolShell
酷 壳 – CoolShell
美团技术团队
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Google DeepMind News
Google DeepMind News
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
T
Tailwind CSS Blog
V
Visual Studio Blog
P
Proofpoint News Feed
Webroot Blog
Webroot Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
博客园 - 三生石上(FineUI控件)
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Jina AI
Jina AI
雷峰网
雷峰网
T
The Blog of Author Tim Ferriss
Hugging Face - Blog
Hugging Face - Blog
腾讯CDC
L
LangChain Blog
The Register - Security
The Register - Security
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园 - 聂微东

博客园 - 大山008

若依导出excel时decimal数据千分位格式化 EasyExcel实现百万级数据导入导出 SpringAMQP整合RabbitMQ使用 win11安装redis步骤详解 ftp与sftp工具类 stream流的一些常用用法 子类的toString方法如何打印父类的属性 MySQL常见的几种优化方案 关于若依AsyncFactory的一些思考,记录一下 Vue 路由跳转、路由传参、跳转区别、传值取值 MYSQL中substring_index()用法 mybatis sql 解决 in 参数过多的问题 MySQL之json数据操作 mybatis查询返回map键值对的问题 validate校验,记录一种思路 在vue中用multipart/form-data方式上传文件并同表单同步提交数据 Java微信转发及网络检测 生成带有二维码的海报 java将指定文件夹下面的所有图片压缩到指定大小以内并保存图片 Lock与ReentrantLock、Synchronized
SpringBoot如何引入deepseek-多轮对话
大山008 · 2025-08-05 · via 博客园 - 大山008

直接上代码,部分主要代码

1.发送内容

@Data
@Builder
public class Message {
    private String role;
    private String content;
}

2.请求参数体

@Data
@Builder
public class ChatMessage {
    private String model;
    private List<Message> messages;
    private boolean stream;
    private float temperature;
}

3.访问层

public SseEmitter createConnect(@RequestParam(name = "prompt") String prompt, @RequestParam(name = "temperature") float temperature) throws IOException {
        log.info("prompt {}", prompt);
        log.info("temperature {}", temperature);
        SseEmitter sseEmitter = new SseEmitter(-1L); //创建sse对象 -0表示永不超时
        /**补充自己代码,业务逻辑判断等*/
        deepSeekService.sendMessage(sseEmitter, prompt, apiKey, apiPoint, deepSeekModel, temperature);
        return sseEmitter;
    }

4.业务层

public void sendMessage(SseEmitter emitter, String prompt, String apiKey, String apiHost, String deepModel, float temperature) {
        Long userId = getUserId();
        StringBuffer buffer = new StringBuffer();
        new Thread(() -> {
            OkHttpClient client = new OkHttpClient();
            // 获取或初始化用户的对话历史
            List<Message> messages = getHistory(String.format(REDIS_DEEP_SEEK_USER, userId));
            Message message = Message.builder()
                    .role("user")
                    .content(prompt).build();
            // 添加新的用户消息到对话历史
            messages.add(message);
            //创建请求参数体
            ChatMessage requestBody = ChatMessage.builder()
                    .model(deepModel)
                    .messages(messages)
                    .stream(true)
                    .temperature(temperature)
                    .build();
            // 2. 构建请求
            Request request = new Request.Builder()
                    .url(apiHost)
                    .post(RequestBody.create(JSONObject.toJSONString(requestBody), MediaType.parse("application/json; charset=utf-8")))
                    .header("Authorization", "Bearer " + apiKey)
                    .build();
            try (Response response = client.newCall(request).execute()) {
                log.info("deepSeek返回状态 {}", response.code() + "-" + DeepSeekStatusEnum.fromCode(response.code()).getDescription());
                if (!response.isSuccessful()) {
                    log.error("DeepSeek返回错误信息: {}",response.code() + "-" + DeepSeekStatusEnum.fromCode(response.code()).getDescription());
                    emitter.send(SseEmitter.event().name("error").data("DeepSeek返回错误信息:" + response.code() + "-" + DeepSeekStatusEnum.fromCode(response.code()).getDescription()));
                    emitter.complete();
                    return;
                }
                saveHistory(String.format(REDIS_DEEP_SEEK_USER, userId), messages);
                ResponseBody responseBody = response.body();
                if (responseBody != null) {
                    try (BufferedReader reader = new BufferedReader(new InputStreamReader(responseBody.byteStream()))) {
                        String line;
                        BackDataVo backDataVo = new BackDataVo();
                        while ((line = reader.readLine()) != null) {
                            if (line.startsWith("data: ")) {
                                String jsonStr = line.substring(6).trim();
                                if ("[DONE]".equals(jsonStr)) {
                                    emitter.send(SseEmitter.event()
                                            .data("[DONE]")
                                            .id(UUID.randomUUID().toString()));
                                    break;
                                }
                                JsonNode node = objectMapper.readTree(jsonStr);
                                String content = node.at("/choices/0/delta/content").asText();
                                buffer.append(content);
                                if (!content.isEmpty()) {
                                    backDataVo.setValue(content);
                                    emitter.send(SseEmitter.event()
                                            .data(backDataVo)     //直接赋值content,数据会存在丢失
                                            .id(UUID.randomUUID().toString()));
                                }
                            }
                        }
                        emitter.complete();
                    } catch (IOException e) {
                        handleException(e, emitter);
                    }
                }
            } catch (IOException e) {
                handleException(e, emitter);
            }
        }).start();
        // 异步保存响应,将历史数据保存redis,下次使用
        emitter.onCompletion(() -> {
            List<Message> newHistory = getHistory(String.format(REDIS_DEEP_SEEK_USER, userId));
            if (CollectionUtils.isEmpty(newHistory)) {
                newHistory = new ArrayList<>();
            }
            log.info("对话返回数据:{}", buffer);
            Message message = Message.builder()
                    .role("assistant")
                    .content(buffer.toString()).build();
            newHistory.add(message);
            log.info("历史对话数据:{}",  newHistory);
            saveHistory(String.format(REDIS_DEEP_SEEK_USER, userId), newHistory);
        });
    }

    // 统一异常处理
    private void handleException(Exception e, SseEmitter emitter) {
        try {
            log.error("deepSeek Unexpected error: " + e.getMessage());
            emitter.send(SseEmitter.event().name("error").data("DeepSeek返回错误信息:" + e.getMessage()));
        } catch (IOException ex) {
            log.error("Error sending unexpected error message: " + ex.getMessage());
            emitter.completeWithError(ex);
        } finally {
            emitter.complete();
        }
    }
    private List<Message> getHistory(String key) {
        Object history = redisTemplate.opsForValue().get(key);
        return history != null ? (List<Message>) history : new ArrayList<>();
    }

    private void saveHistory(String key, List<Message> history) {
        redisTemplate.opsForValue().set(key, history);
        redisTemplate.expire(key, 30, TimeUnit.MINUTES); // 每次访问续期
    }

image

 注意:deepseek多轮对话要将之前提问和返回的数据放入请求体中,因此需要保存会话期间的问题和结果

参考:https://blog.csdn.net/weiliang7/article/details/147971898

https://blog.csdn.net/m0_52025266/article/details/147501578