DeepSeek调用API
目录
1.首次调用API
2.通过代码调用API,并解析数据
(1)非流式输出,一次性返回响应信息(以下是通过对象解析方式处理)
(2)非流式输出,一次性返回响应信息(以下是通过JsonNode节点解析方式处理)
(3)流式输出,逐行逐字返回响应信息
3.通过SSE单向推送数据,流式输出数据到前端进行渲染
(1)技术方案选择
(2)后端实现(Spring Boot + SSE)
(3)前端实现(VUE + JavaScript)
(4)展示效果和待优化内容
一、首次调用API
调用API首先要创建一个API key,通过地址https://platform.deepseek.com/api_keys 进行创建,创建完成后在列表内会展示刚创建的API key。API key 仅在创建时可见可复制,请妥善保存(后续都会以脱敏方式显示)。不要与他人共享你的 API key,或将其暴露在浏览器或其他客户端代码中。
在创建 API key 之后,你可以使用以下样例脚本的来访问 DeepSeek API。样例为非流式输出,您可以将 stream 设置为 true 来使用流式输出。
curl https://api.deepseek.com/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer Your API key" \ -d '{ "model": "deepseek-chat", "messages": [ {"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "你好!"} ], "stream": false }'
二、通过代码调用API,并解析数据
依赖maven
<!-- JSON处理 --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.15.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.15.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.15.0</version> </dependency>
关键参数说明
1、非流式输出,一次性返回响应信息(以下是通过对象解析方式处理)
常量信息
/** * API_KEY */ public static final String API_KEY = "sk-XXXXXXXXXXXXXXXXXXXXXXXXX"; /** * API_URL */ public static final String API_URL = "https://api.deepseek.com/v1/chat/completions";
创建请求对象ChatRequest类
import com.fasterxml.jackson.annotation.JsonProperty; import java.util.List; public class ChatRequest { /** 必填参数 模型名称,如 "deepseek-chat"*/ private String model; /** 对话消息列表*/ private List<Message> messages; /** 生成文本的随机性(0~2)*/ private Double temperature; /** 生成的最大 token 数 */ @JsonProperty("max_tokens") private Integer maxTokens; /** 核采样概率(0~1) */ @JsonProperty("top_p") private Double topP; /** 是否流式输出 */ private Boolean stream; // 无参构造函数和 getter/setter 方法 public ChatRequest() {} public ChatRequest(String model, List<Message> messages, Double temperature, Integer maxTokens, Double topP, Boolean stream) { this.model = model; this.messages = messages; this.temperature = temperature; this.maxTokens = maxTokens; this.topP = topP; this.stream = stream; } public String getModel() { return model; } public void setModel(String model) { this.model = model; } public List<Message> getMessages() { return messages; } public void setMessages(List<Message> messages) { this.messages = messages; } public Double getTemperature() { return temperature; } public void setTemperature(Double temperature) { this.temperature = temperature; } public Integer getMaxTokens() { return maxTokens; } public void setMaxTokens(Integer maxTokens) { this.maxTokens = maxTokens; } public Double getTopP() { return topP; } public void setTopP(Double topP) { this.topP = topP; } public Boolean getStream() { return stream; } public void setStream(Boolean stream) { this.stream = stream; } }
创建返回对象ChatResponse类
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.List; @JsonIgnoreProperties(ignoreUnknown = true) public class ChatResponse { private List<Choice> choices; // 必须有无参构造函数 public ChatResponse() {} // 带 @JsonProperty 注解的构造函数(可选,但推荐) public ChatResponse(@JsonProperty("choices") List<Choice> choices) { this.choices = choices; } // Getter 和 Setter public List<Choice> getChoices() { return choices; } public void setChoices(List<Choice> choices) { this.choices = choices; } }
创建Choice类
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; @JsonIgnoreProperties(ignoreUnknown = true) public class Choice { private Message message; public Choice() {} public Choice(@JsonProperty("message") Message message) { this.message = message; } public Message getMessage() { return message; } public void setMessage(Message message) { this.message = message; } }
创建Message类
@JsonIgnoreProperties(ignoreUnknown = true) public class Message { private String role; private String content; public Message() {} public Message(@JsonProperty("role") String role, @JsonProperty("content") String content) { this.role = role; this.content = content; } public String getRole() { return role; } public void setRole(String role) { this.role = role; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } }
创建模拟调用的客户端DeepSeekChat
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.learn.common.utils.StringUtils; import com.learn.common.constant.Constants; import com.learn.deepseek.pojo.*; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.util.ArrayList; import java.util.List; /** * 非流式输出方式 */ public class DeepSeekChat { // 序列化为 JSON(使用配置好的 ObjectMapper) private static ObjectMapper mapper = new ObjectMapper(); // 创建客户端并发送消息 private static HttpClient client = HttpClient.newHttpClient(); /** * 创建ChatRequest对象 * * @param message 消息 * @return ChatRequest对象 */ private static ChatRequest createChatRequest(String message) { ChatRequest request = new ChatRequest(); request.setModel("deepseek-chat"); request.setTemperature(0.7); request.setMaxTokens(8192); // 设置对话消息 List<Message> messages = new ArrayList<>(); messages.add(new Message("user", message)); request.setMessages(messages); return request; } /** * 发送消息 * * @param chatRequest 请求对象 * @return 响应JSON对象 */ private static String sendMessage(ChatRequest chatRequest) { String result = null; try { // 未知参数处理 mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); // 请求对象转JSON String jsonBody = mapper.writeValueAsString(chatRequest); HttpRequest httpRequest = HttpRequest.newBuilder() .uri(URI.create(Constants.API_URL)) .header("Content-Type", "application/json") .header("Authorization", "Bearer " + Constants.API_KEY) .POST(HttpRequest.BodyPublishers.ofString(jsonBody)) .build(); // 发送消息 HttpResponse<String> response = client.send(httpRequest, HttpResponse.BodyHandlers.ofString()); System.out.println("Status Code: " + response.statusCode()); System.out.println("Response Body: " + response.body()); if (response.statusCode() != 200) { String errorMsg = String.format("请求失败!状态码:%d\n响应内容:%s", response.statusCode(), response.body()); throw new RuntimeException(errorMsg); } result = response.body(); } catch (Exception e) { System.err.println("发生异常:" + e.getMessage()); e.printStackTrace(); } return result; } /** * 解析响应JSON对象 * * @param responseBody 响应JSON对象 */ private static void parseResponseBody(String responseBody) { if(!StringUtils.isEmpty(responseBody)) { try { // JSON解析成对象 ChatResponse chatResponse = mapper.readValue(responseBody, ChatResponse.class); if (chatResponse.getChoices().size() > 0) { String content = chatResponse.getChoices().get(0).getMessage().getContent(); System.out.println("=== 生成结果 ===\n" + content); } else { System.err.println("警告:未收到有效响应内容"); } } catch (JsonProcessingException e) { System.err.println("发生异常:" + e.getMessage()); e.printStackTrace(); } } else { System.err.println("警告:未收到有效响应内容"); } } public static void main(String[] args) { // 1.创建ChatRequest对象,设置对话消息 ChatRequest chatRequest = createChatRequest("你是谁?"); // 2.发送消息 String responseBody = sendMessage(chatRequest); // 3.解析对象 parseResponseBody(responseBody); } }
打印结果:
2.非流式输出,一次性返回响应信息(以下是通过JsonNode节点解析方式处理)
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.learn.common.utils.StringUtils; import com.learn.common.constant.Constants; import com.learn.deepseek.pojo.*; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.util.ArrayList; import java.util.List; /** * 非流式输出方式 */ public class DeepSeekChat { // 序列化为 JSON(使用配置好的 ObjectMapper) private static ObjectMapper mapper = new ObjectMapper(); // 创建客户端并发送消息 private static HttpClient client = HttpClient.newHttpClient(); /** * 创建ChatRequest对象 * * @param message 消息 * @return ChatRequest对象 */ private static ChatRequest createChatRequest(String message) { ChatRequest request = new ChatRequest(); request.setModel("deepseek-chat"); request.setTemperature(0.7); request.setMaxTokens(8192); // 设置对话消息 List<Message> messages = new ArrayList<>(); messages.add(new Message("user", message)); request.setMessages(messages); return request; } /** * 发送消息 * * @param chatRequest 请求对象 * @return 响应JSON对象 */ private static String sendMessage(ChatRequest chatRequest) { String result = null; try { // 未知参数处理 mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); // 请求对象转JSON String jsonBody = mapper.writeValueAsString(chatRequest); HttpRequest httpRequest = HttpRequest.newBuilder() .uri(URI.create(Constants.API_URL)) .header("Content-Type", "application/json") .header("Authorization", "Bearer " + Constants.API_KEY) .POST(HttpRequest.BodyPublishers.ofString(jsonBody)) .build(); // 发送消息 HttpResponse<String> response = client.send(httpRequest, HttpResponse.BodyHandlers.ofString()); System.out.println("Status Code: " + response.statusCode()); System.out.println("Response Body: " + response.body()); if (response.statusCode() != 200) { String errorMsg = String.format("请求失败!状态码:%d\n响应内容:%s", response.statusCode(), response.body()); throw new RuntimeException(errorMsg); } result = response.body(); } catch (Exception e) { System.err.println("发生异常:" + e.getMessage()); e.printStackTrace(); } return result; } /** * 解析响应JSON对象 * * @param responseBody 响应JSON对象 */ private static void parseResponseBody(String responseBody) { if(!StringUtils.isEmpty(responseBody)) { try { // JSON解析成对象 ChatResponse chatResponse = mapper.readValue(responseBody, ChatResponse.class); if (chatResponse.getChoices().size() > 0) { String content = chatResponse.getChoices().get(0).getMessage().getContent(); System.out.println("=== 生成结果 ===\n" + content); } else { System.err.println("警告:未收到有效响应内容"); } } catch (JsonProcessingException e) { System.err.println("发生异常:" + e.getMessage()); e.printStackTrace(); } } else { System.err.println("警告:未收到有效响应内容"); } } public static void main(String[] args) { // 1.创建ChatRequest对象,设置对话消息 ChatRequest chatRequest = createChatRequest("你是谁?"); // 2.发送消息 String responseBody = sendMessage(chatRequest); // 3.解析对象 parseResponseBody(responseBody); } }
打印结果
3、流式输出,逐行逐字返回响应信息
依赖maven
<!-- 客户端 --> <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> <version>4.12.0</version> </dependency>
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.learn.common.constant.Constants; import com.learn.deepseek.pojo.ChatRequest; import com.learn.deepseek.pojo.Message; import okhttp3.*; import org.jetbrains.annotations.NotNull; import java.io.IOException; import java.util.ArrayList; import java.util.List; /** * 流式输出方式 */ public class DeepSeekChatStream { // 序列化为JSON private static ObjectMapper mapper = new ObjectMapper(); // 创建客户端并发送消息 private static OkHttpClient client = new OkHttpClient(); /** * 创建ChatRequest对象 * * @param message 消息 * @return ChatRequest对象 */ private static ChatRequest createChatRequest(String message) { ChatRequest chatRequest = new ChatRequest(); chatRequest.setModel("deepseek-chat"); chatRequest.setTemperature(0.7); chatRequest.setMaxTokens(8192); // 流式输出 chatRequest.setStream(true); // 设置对话消息 List<Message> messages = new ArrayList<>(); messages.add(new Message("user", message)); chatRequest.setMessages(messages); return chatRequest; } /** * 异步发送消息,处理流式响应 * * @param chatRequest 请求对象 * @return 响应JSON对象 */ private static void sendMessage(ChatRequest chatRequest) { try { // 请求对象转JSON String jsonBody = mapper.writeValueAsString(chatRequest); // 创建客户端并发送消息 Request request = new Request.Builder() .url(Constants.API_URL) .header("Authorization", "Bearer " + Constants.API_KEY) .post(RequestBody.create(jsonBody, MediaType.get("application/json"))) .build(); // 异步发送请求并处理流式响应 client.newCall(request).enqueue(new Callback() { @Override public void onFailure(@NotNull Call call, @NotNull IOException e) { System.err.println("请求异常: " + e.getMessage()); } @Override public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { if (!response.isSuccessful()) { System.err.println("请求失败: " + response.code()); return; } // 逐块读取响应流 try (ResponseBody responseBody = response.body()) { if (responseBody != null) { // 使用字符流逐行读取 responseBody.source().request(Long.MAX_VALUE); while (true) { // 逐行读取响应流 String line = responseBody.source().readUtf8Line(); // 流结束 if (line == null) break; // 解析并打印内容(例如:data: {"content": "Hi..."}) processLine(line); } } } } }); } catch (Exception e) { System.err.println("发生异常:" + e.getMessage()); e.printStackTrace(); } } /** * 解析单行数据并打印内容 * * @param line 逐行读取 */ private static void processLine(String line) { if (line.startsWith("data: ")) { String json = line.substring(6).trim(); if (json.equals("[DONE]")) { System.out.println("\n流式传输结束"); return; } try { JsonNode node = mapper.readTree(json); String content = node.path("choices").get(0).path("delta").path("content").asText(); if (!content.isEmpty()) { // 逐字符打印,模拟聊天效果 for (char c : content.toCharArray()) { System.out.print(c); // 控制输出速度 Thread.sleep(50); } } } catch (Exception e) { System.err.println("解析错误: " + e.getMessage()); e.printStackTrace(); } } } public static void main(String[] args) { // 1.创建ChatRequest对象,设置对话消息 ChatRequest chatRequest = createChatRequest("你是谁?"); // 2.异步发送消息,处理流式响应 sendMessage(chatRequest); } }
输出结果
三、通过SSE单向推送数据,流式输出数据到前端进行渲染
要在 Web 页面实现流式输出的逐字响应效果(类似 DeepSeek 的逐字生成),需要 前后端配合,核心思路是:后端通过流式接口获取数据后,将数据分块(chunk)推送到前端,前端逐步渲染。
完整流程图
客户端发起 SSE 请求 → 服务器保持连接 → 分块发送数据 → 客户端实时渲染 → 流结束或断开
1、技术方案选择
2、后端实现(Spring Boot + SSE)
添加依赖
<!-- SpringBoot Web容器 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- JSON处理 --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.15.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.15.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.15.0</version> </dependency> <!-- okhttp3 --> <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> <version>3.14.9</version> </dependency>
创建处理流式调用Controller
package com.learn.web.controller.deepseek; import com.learn.common.core.controller.BaseController; import com.learn.deepseek.service.IStreamService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; /** * 处理流式调用Controller * * @author chengx * @date 2025-03-24 */ @RestController @RequestMapping("/stream") public class StreamController extends BaseController { @Autowired private IStreamService streamService; /** * 创建 SSE 连接端点 */ @GetMapping(value = "/streamData", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public SseEmitter streamData(String message) { // 超时时间 300 秒 SseEmitter emitter = new SseEmitter(300_000L); new Thread(() -> streamService.callStreamApi(message, emitter)).start(); return emitter; } } 处理流式调用Service package com.learn.deepseek.service; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; /** * 处理流式调用Service * * @author chengx * @date 2025-03-24 */ public interface IStreamService { /** * 调用流式API * * @param message 请求消息 * @param emitter sse发射器 */ public void callStreamApi(String message, SseEmitter emitter); }
处理流式调用Service业务层处理
package com.learn.deepseek.service.impl; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.learn.common.constant.DeepSeekConstants; import com.learn.deepseek.pojo.ChatRequest; import com.learn.deepseek.pojo.Message; import com.learn.deepseek.service.IStreamService; import okhttp3.*; import okio.BufferedSource; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; import javax.validation.constraints.NotNull; import java.io.IOException; import java.util.ArrayList; import java.util.List; /** * 处理流式调用Service业务层处理 * * @author chengx * @date 2025-03-24 */ @Service public class StreamServiceImpl implements IStreamService { /** * 序列化为JSON */ private ObjectMapper mapper = new ObjectMapper(); /** * 创建客户端并发送消息 */ private OkHttpClient client = new OkHttpClient(); /** * 创建ChatRequest对象 * * @param message 消息 * @return ChatRequest对象 */ private ChatRequest createChatRequest(String message) { ChatRequest chatRequest = new ChatRequest(); chatRequest.setModel("deepseek-chat"); chatRequest.setTemperature(0.7); chatRequest.setMaxTokens(8192); // 流式输出 chatRequest.setStream(true); // 设置对话消息 List<Message> messages = new ArrayList<>(); messages.add(new Message("user", message)); chatRequest.setMessages(messages); return chatRequest; } /** * 解析单行数据并打印内容 * * @param chunk 逐块读取 * @param emitter sse发射器 */ private void processChunk(String chunk, SseEmitter emitter) { if (chunk.startsWith("data: ")) { String json = chunk.substring(6).trim(); if (json.equals("[DONE]")) { System.out.println("\n流式传输结束"); return; } try { JsonNode node = mapper.readTree(json); String content = node.path("choices").get(0).path("delta").path("content").asText(); if (StringUtils.isNotEmpty(content)) { try { // 推送数据到前端 emitter.send(content); System.out.print(content); // 模拟延迟,控制输出速度 Thread.sleep(100); } catch (InterruptedException e) { System.err.println("请求异常: " + e.getMessage()); e.printStackTrace(); } } } catch (Exception e) { System.err.println("解析错误: " + e.getMessage()); e.printStackTrace(); } } } /** * 调用流式API * * @param message 请求消息 * @param emitter sse发射器 */ @Override public void callStreamApi(String message, SseEmitter emitter) { try { // 1.创建ChatRequest对象,设置对话消息 ChatRequest chatRequest = createChatRequest(message); // 2.请求对象转JSON String jsonBody = mapper.writeValueAsString(chatRequest); RequestBody requestBody = RequestBody.create(MediaType.get("application/json"), jsonBody); // 3.创建客户端并发送消息 Request request = new Request.Builder() .url(DeepSeekConstants.API_URL) .header("Authorization", "Bearer " + DeepSeekConstants.API_KEY) .post(requestBody) .build(); // 4.异步发送请求并处理流式响应 client.newCall(request).enqueue(new Callback() { @Override public void onFailure(@NotNull Call call, @NotNull IOException e) { emitter.completeWithError(e); System.err.println("请求异常: " + e.getMessage()); } @Override public void onResponse(@NotNull Call call, @NotNull Response response) { try (ResponseBody responseBody = response.body()) { if (!response.isSuccessful()) { emitter.completeWithError(new RuntimeException("API 请求失败")); return; } // 5.逐块读取流式响应 BufferedSource source = responseBody.source(); while (!source.exhausted()) { String chunk = source.readUtf8Line(); if(StringUtils.isNotBlank(chunk)) { // 6.解析单行数据并打印内容 processChunk(chunk, emitter); } } // 标记完成 emitter.complete(); } catch (IOException e) { emitter.completeWithError(e); System.err.println("请求异常: " + e.getMessage()); } } }); } catch (Exception e) { emitter.completeWithError(e); System.err.println("发生异常:" + e.getMessage()); } } }
3、前端实现(VUE + JavaScript)
创建stream.vue处理流式输出内容渲染
<template> <div class="app-container"> <div class="output">{{ outputText }}</div> <el-form :model="queryParams" ref="queryForm" size="small" :inline="true"> <el-form-item prop="inputMessage"> <el-input v-model="queryParams.inputMessage" placeholder="给 DeepSeek 发送消息" clearable style="width: 240px" @keyup.enter="startStream" /> </el-form-item> <el-form-item> <el-button type="primary" size="small" @click="startStream" :icon="icon" >发送</el-button> </el-form-item> </el-form> </div> </template> <script> export default { data() { return { // 查询参数 queryParams: { inputMessage: "", }, // 输出文本 outputText: "", // 保存 SSE 连接对象 eventSource: null, // 搜索图标 icon: 'el-icon-search' }; }, methods: { // 发送 startStream() { // 关闭旧连接(如果存在) if (this.eventSource) { this.eventSource.close(); } this.icon = 'el-icon-loading' // 清空输出 this.outputText = ""; const message = this.queryParams.inputMessage const url = "http://localhost:18080/stream/streamData?message=" + message // 创建 EventSource 实例 this.eventSource = new EventSource(url); // 连接打开时触发 this.eventSource.onopen = () => { console.log(this.eventSource.readyState) console.log(this.eventSource.withCredentials) }; // 监听消息事件 this.eventSource.onmessage = (event) => { console.log("收到数据:", event.data) // 逐字显示 this.appendText(event.data); }; // 监听错误(连接发生错误或中断) this.eventSource.onerror = (error) => { console.error("SSE 错误:", error); this.eventSource.close(); // 流数出结束更换按钮图标 this.icon = 'el-icon-search' }; }, // 逐字追加文本(模拟打字机效果) appendText(text) { let index = 0; const addChar = () => { if (index < text.length) { this.outputText += text.charAt(index); index++; addChar(); } }; addChar(); }, }, // 组件销毁时关闭连接 beforeUnmount() { if (this.eventSource) { this.eventSource.close(); } }, }; </script> <style scoped> .output { white-space: pre-wrap; margin-top: 20px; border: 1px solid #ccc; padding: 10px; margin-bottom: 10px; } </style>
4、展示效果和待优化内容
输出结果
待优化内容:
1、输出格式还需要优化,目前版本不能换行
2、输出速度没有控制的话,会出现文字前后交叉错乱情况