基于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); } }); } }