基于SpringBoot的DeepSeek-demo 深度求索-demo 支持流式输出、历史记录

文章目录

    • 文件下载
    • 效果展示
      • Idea
      • Python
      • Postman
    • 使用说明
    • 核心代码
      • controller
      • Service

文件下载

百度网盘 提取码: jsfc

蓝奏云 密码:5kxz

效果展示

Idea

在这里插入图片描述

Python

在这里插入图片描述

Postman

注:postman中只有websocket才有流式效果

在这里插入图片描述

使用说明

  • 修改配置文件

在这里插入图片描述

  • 请求路径为/chat

  • 在请求体中传入对话列表(可以携带对话的历史记录)

    [
        {
          "content": "你是一个乐于助人的助手。",
          "role": "system"
        },
        {
          "content": "记住a=1,b=2",
          "role": "user"
        },
        {
          "content": "好的,我已经记住了:**a = 1**,**b = 2**。如果有其他问题或需要进一步帮助,请告诉我! 😊",
          "role": "assistant"
        },
        {
          "content": "a + b等于什么",
          "role": "user"
        }
    ]
    
    响应结果如下:
    根据我记住的 **a = 1** 和 **b = 2**,**a + b = 1 + 2 = 3**。  
    结果是 **3**!还有其他问题吗? 😊
    

在这里插入图片描述

核心代码

controller

import com.deepseek.deepseekdemo.service.AiChatService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import reactor.core.scheduler.Schedulers;

import java.util.List;
import java.util.Map;

@RestController
public class AiChatController {
    @Autowired
    private AiChatService aiChatService;

    @GetMapping(value = "/chat", produces = "application/stream+json")
    public Flux<String> chat(@RequestBody List<Map<String, String>> messages) {
        Flux<String> responseFlux = aiChatService.chat(messages);

        return Flux.create(sink -> {
            responseFlux.subscribeOn(Schedulers.boundedElastic())
                    .subscribe(
                            sink::next, // 返回流式字符串
                            sink::error, // 返回错误,请在调用端完成错误处理
                            sink::complete // 返回完成事件
                    );
        });
    }
}

Service

import com.deepseek.deepseekdemo.config.DeepSeekConfig;
import com.deepseek.deepseekdemo.dto.AiMessageDto;
import com.deepseek.deepseekdemo.dto.DefaultAiMessageDto;
import com.deepseek.deepseekdemo.dto.StreamingAiMessageDto;
import com.deepseek.deepseekdemo.service.AiChatService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
@Service
public class AiChatServiceA implements AiChatService {

    Map<String, Object> body = new HashMap<>();
    Headers headers;
    AiMessageDto aiMessageDto;

    @Autowired
    private DeepSeekConfig deepSeekConfig;
    @Autowired
    private OkHttpClient httpClient;

    // 初始化
    @PostConstruct
    public void init() {
        body.put("model", deepSeekConfig.getModel());
        body.put("frequency_penalty", deepSeekConfig.getFrequencyPenalty());
        body.put("max_tokens", deepSeekConfig.getMaxTokens());
        body.put("presence_penalty", deepSeekConfig.getPresencePenalty());
        body.put("response_format", deepSeekConfig.getResponseFormat());
        body.put("stop", deepSeekConfig.getStop());

        body.put("stream", deepSeekConfig.getStream());
        if (deepSeekConfig.getStream()) {
            body.put("stream_options", deepSeekConfig.getStreamOptions());
        }

        body.put("temperature", deepSeekConfig.getTemperature());
        body.put("top_p", deepSeekConfig.getTopP());
        body.put("tools", deepSeekConfig.getTools());
        body.put("tool_choice", deepSeekConfig.getToolChoice());

        if (deepSeekConfig.getLogprobs()) {
            body.put("logprobs", deepSeekConfig.getLogprobs());
            body.put("top_logprobs", deepSeekConfig.getTopLogprobs());
        }

        // 构建请求头
        headers = new Headers.Builder().add("Authorization", "Bearer " + deepSeekConfig.getApiKey()).add("Content-Type", "application/json").build();

        if (deepSeekConfig.getStream()) {
            aiMessageDto = new StreamingAiMessageDto();
        } else {
            aiMessageDto = new DefaultAiMessageDto();
        }
    }


    @Override
    public Flux<String> chat(List<Map<String, String>> question) {
        body.put("messages", question);

        // 将请求体转换为 JSON 字符串
        ObjectMapper objectMapper = new ObjectMapper();
        String payload;
        try {
            payload = objectMapper.writeValueAsString(body);
        } catch (JsonProcessingException e) {
            throw new RuntimeException("无法将负载转换为JSON", e);
        }

        // 构建请求
        Request request = new Request.Builder().url(deepSeekConfig.getUrl()).post(RequestBody.create(payload, MediaType.parse("application/json"))).headers(headers).build();

        // 返回 Flux<String> 以流式返回数据
        return Flux.create(emitter -> {
            try (Response response = httpClient.newCall(request).execute()) {
                if (!response.isSuccessful()) {
                    emitter.error(new IOException("请求失败响应码: " + response.code()));
                    return;
                }
                // 处理流式响应
                ResponseBody responseBody = response.body();
                if (responseBody != null) {
                    while (!responseBody.source().exhausted()) {
                        String line = responseBody.source().readUtf8Line();
                        if (line != null && !line.isEmpty()) {
                            try {
                                emitter.next(aiMessageDto.getMessage(line)); // 逐步发送数据
                            } catch (Exception e) {
                                log.error("处理数据时发生错误: ", e);
                            }
                        }
                    }
                    emitter.complete(); // 完成流
                }
            } catch (IOException e) {
                emitter.error(e);
            }
        });
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值