文章目录
Spring Boot 集成 DeepSeek API 详细步骤
1. 创建API Key
1.访问 DeepSeek控制台 并登录。
2.点击 Create API Key 生成新密钥。
3.复制并保存密钥(需在Spring Boot配置文件中使用)。
2. 创建Spring Boot工程
使用 Spring Initializr 创建项目,选择以下依赖:
Spring Web
Lombok(简化实体类代码)
3. 配置项目依赖
在 pom.xml 中添加必要依赖:
<!-- OkHttp 实现HTTP请求 -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.12.0</version>
</dependency>
<!-- FastJSON 用于JSON解析 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.48</version>
</dependency>
<!-- SSE 事件流支持 -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp-sse</artifactId>
<version>4.12.0</version>
</dependency>
4. 核心代码实现
4.1 定义响应实体类
@Data
public class AiResult {
private Integer code;
private String message;
private String sid;
private String id;
private Long created;
private List<AiResultChoices> choices;
private AiResultUsage usage;
}
@Data
public class AiResultChoices {
private AiResultDelta delta;
private Integer index;
}
@Data
public class AiResultDelta {
private String role;
private String content;
}
@Data
public class AiResultUsage {
private Integer prompt_tokens;
private Integer completion_tokens;
private Integer total_tokens;
}
@Data
public class ContentDto {
private String content;
}
4.2 控制器实现(支持SSE流式响应)
/**
* DeepSeek AI接口请求控制器
* 功能:处理SSE(Server-Sent Events)流式请求,与DeepSeek API交互
* 主要职责:
* 1. 接收前端SSE请求
* 2. 构造DeepSeek API请求
* 3. 处理流式响应并转发给客户端
* 4. 管理连接生命周期和异常处理
*/
@RestController
public class SeekController {
// 日志记录器
private static final Logger log = LoggerFactory.getLogger(SeekController.class);
// 流式结束标识符(符合OpenAI标准)
private static final String DONE_FLAG = "[DONE]";
// HTTP请求超时时间(单位:秒)
private static final int TIMEOUT_SECONDS = 60;
// DeepSeek API端点
private static final String AI_URL = "https://api.deepseek.com/chat/completions";
// JSON媒体类型常量
private static final MediaType JSON_MEDIA_TYPE = MediaType.parse("application/json");
// 从配置文件注入API密钥
@Value("${api.key}")
private String apiKey;
// OkHttp客户端配置(线程安全)
private final OkHttpClient httpClient = new OkHttpClient.Builder()
.connectTimeout(TIMEOUT_SECONDS, TimeUnit.SECONDS) // 连接超时
.readTimeout(TIMEOUT_SECONDS, TimeUnit.SECONDS) // 读取超时
.writeTimeout(TIMEOUT_SECONDS, TimeUnit.SECONDS) // 写入超时
.build();
/**
* SSE请求处理入口
* @param message 用户输入消息
* @param response HTTP响应对象
* @throws IOException 当响应流出现问题时抛出
*/
@GetMapping("/stream")
public void handleSse(@RequestParam String message, HttpServletResponse response) throws IOException {
// 配置SSE响应头
configureSSEResponse(response);
PrintWriter pw = null;
try {
// 获取响应输出流
pw = response.getWriter();
// 创建同步锁(初始计数器为1)
CountDownLatch latch = new CountDownLatch(1);
// 执行SSE请求
executeSSERequest(pw, buildRequest(message), latch);
// 等待异步操作完成(最大等待TIMEOUT_SECONDS秒)
if (!latch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS)) {
log.warn("SSE请求超时");
}
} catch (InterruptedException e) {
// 正确处理线程中断
Thread.currentThread().interrupt();
log.error("请求被中断", e);
response.sendError(503, "服务不可用");
} catch (Exception e) {
log.error("SSE请求异常", e);
response.sendError(500, "服务器内部错误");
} finally {
// 确保关闭输出流
if (pw != null) {
try {
pw.close();
} catch (Exception e) {
log.warn("关闭PrintWriter异常", e);
}
}
}
}
/**
* 配置SSE响应头
* @param response HTTP响应对象
*/
private void configureSSEResponse(HttpServletResponse response) {
response.setContentType("text/event-stream"); // MIME类型
response.setCharacterEncoding("UTF-8"); // 字符编码
response.setHeader("Connection", "keep-alive"); // 保持长连接
response.setHeader("Cache-Control", "no-cache"); // 禁用缓存
}
/**
* 构建DeepSeek API请求
* @param content 用户输入内容
* @return 构造好的Request对象
*/
private Request buildRequest(String content) {
// 构造请求参数
Map<String, Object> params = new HashMap<>();
params.put("model", "deepseek-chat"); // 使用的模型
params.put("stream", true); // 启用流式传输
// 构造消息列表(使用双括号初始化语法)
params.put("messages", Collections.singletonList(
new HashMap<String, String>() {{
put("role", "user"); // 消息角色
put("content", content); // 消息内容
}}
));
// 构建OkHttp请求
return new Request.Builder()
.url(AI_URL) // API地址
.post(RequestBody.create(JSON.toJSONString(params), JSON_MEDIA_TYPE)) // JSON请求体
.addHeader("Authorization", "Bearer " + apiKey) // 认证头
.addHeader("Accept", "text/event-stream") // 接受SSE流
.build();
}
/**
* 执行SSE请求核心方法
* @param pw 响应输出流
* @param request 构造好的API请求
* @param latch 线程同步锁
*/
private void executeSSERequest(PrintWriter pw, Request request, CountDownLatch latch) {
// 创建事件源监听器
RealEventSource eventSource = new RealEventSource(request, new EventSourceListener() {
/**
* 事件处理回调
* @param eventSource 事件源
* @param id 事件ID(未使用)
* @param type 事件类型(未使用)
* @param data 接收到的数据
*/
@Override
public void onEvent(EventSource eventSource, String id, String type, String data) {
try {
// 处理结束标志
if (DONE_FLAG.equals(data)) {
sendData(pw, new ContentDto(DONE_FLAG));
return;
}
// 解析并发送有效内容
String content = parseContent(data);
if (!content.isEmpty()) {
sendData(pw, new ContentDto(content));
}
} catch (Exception e) {
log.error("事件处理异常", e);
}
}
/**
* 失败处理回调
*/
@Override
public void onFailure(EventSource eventSource, Throwable t, Response response) {
log.error("API调用失败: {},响应码:{}",
t.getMessage(),
(response != null ? response.code() : "N/A"));
latch.countDown(); // 释放同步锁
}
/**
* 连接关闭回调
*/
@Override
public void onClosed(EventSource eventSource) {
try {
sendData(pw, new ContentDto(DONE_FLAG)); // 发送结束标志
} finally {
latch.countDown(); // 确保释放同步锁
}
}
});
// 发起异步连接
eventSource.connect(httpClient);
}
/**
* 线程安全的数据发送方法
* @param pw 响应输出流
* @param dto 要发送的数据传输对象
*/
private synchronized void sendData(PrintWriter pw, ContentDto dto) {
try {
// 按照SSE格式发送数据
pw.write("data:" + JSON.toJSONString(dto) + "\n\n");
pw.flush(); // 立即刷新缓冲区
} catch (Exception e) {
log.error("数据发送失败", e);
}
}
/**
* 解析API响应内容
* @param data 原始响应字符串
* @return 解析出的内容文本
*/
private String parseContent(String data) {
try {
// 使用FastJSON反序列化
AiResult result = JSON.parseObject(data, AiResult.class);
// 使用Optional处理可能为空的字段
return Optional.ofNullable(result.getChoices())
.flatMap(list -> list.stream().findFirst()) // 取第一个choice
.map(AiResultChoices::getDelta) // 获取delta对象
.map(AiResultDelta::getContent) // 获取content字段
.orElse(""); // 默认返回空字符串
} catch (Exception e) {
log.error("数据解析异常,原始数据:{}", data, e);
return "";
}
}
}
5. 配置文件说明
在 application.yml 中添加配置:
deepseek:
api-key: your_api_key_here # 替换为实际API Key
6. 测试与验证
6.1启动应用后,通过浏览器或工具发送请求:
GET http://localhost:8080/stream?message=你好,介绍一下你自己