文章目录
- 文件下载
- 效果展示
- 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);
}
});
}
}