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

推荐订阅源

腾讯CDC
Schneier on Security
Schneier on Security
B
Blog RSS Feed
aimingoo的专栏
aimingoo的专栏
P
Proofpoint News Feed
A
About on SuperTechFans
Recorded Future
Recorded Future
Recent Announcements
Recent Announcements
Microsoft Security Blog
Microsoft Security Blog
L
LangChain Blog
Hugging Face - Blog
Hugging Face - Blog
The GitHub Blog
The GitHub Blog
Google DeepMind News
Google DeepMind News
T
Tailwind CSS Blog
Vercel News
Vercel News
H
Hackread – Cybersecurity News, Data Breaches, AI and More
MyScale Blog
MyScale Blog
V2EX - 技术
V2EX - 技术
N
Netflix TechBlog - Medium
F
Fortinet All Blogs
V
Visual Studio Blog
Martin Fowler
Martin Fowler
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
博客园 - Franky
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
T
The Exploit Database - CXSecurity.com
F
Full Disclosure
Scott Helme
Scott Helme
H
Heimdal Security Blog
博客园 - 叶小钗
Google DeepMind News
Google DeepMind News
Cyberwarzone
Cyberwarzone
Application and Cybersecurity Blog
Application and Cybersecurity Blog
V
Vulnerabilities – Threatpost
Blog — PlanetScale
Blog — PlanetScale
Security Latest
Security Latest
WordPress大学
WordPress大学
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
T
Troy Hunt's Blog
S
SegmentFault 最新的问题
Forbes - Security
Forbes - Security
Jina AI
Jina AI
S
Securelist
小众软件
小众软件
Simon Willison's Weblog
Simon Willison's Weblog
J
Java Code Geeks
AWS News Blog
AWS News Blog
N
News and Events Feed by Topic
博客园 - 三生石上(FineUI控件)
量子位

博客园 - 大山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