当前位置: 首页 > article >正文

Springboot快速接入Deepseek

一、必要依赖

pom.xml

<!-- SpringBoot Actuator -->
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- SpringBoot Webflux-->
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
二、api相关配置
deepseek:
  api:
    url: https://api.deepseek.com
    key: ${API keys}

API keysdeepseek开放平台(https://platform.deepseek.com/api_keys)注册登录后获得。调用api还需要充值
在这里插入图片描述
在这里插入图片描述

三、关键代码逻辑

deepseek模型

enum DEEPSEEK_MODEL {
  V3("deepseek-chat"), 
  R1("deepseek-reasoner");
  
  private String name;

  DEEPSEEK_MODEL(String name) {
      this.name = name;
  }
}

请求参数DeepSeekRequest

@Data
public class DeepSeekRequest {
    private String model;
    private List<Message> messages;
    private Boolean stream;

    @Data
    public static class Message {
        private String role;
        private String content;

        public Message(String role, String content) {
            this.role = role;
            this.content = content;
        }
    }
}

返回参数DeepSeekStreamResponse

@Data
public class DeepSeekStreamResponse {
    private String id;
    private String object;
    private String created;
    private String model;
    private String system_fingerprint;
    private List<Choice> choices;
    private Usage usage;

    @Data
    public static class Choice {
        private Integer index;
        private Message delta;
        private String logprobs;
        private String finish_reason;

        @Data
        public static class Message {
            private String role;
            private String content;
            private String reasoning_content;
        }
    }

    @Data
    public static class Usage {
        private Integer prompt_tokens;
        private Integer completion_tokens;
        private Integer total_tokens;
        private PromptTokensDetails prompt_tokens_details;
        private Integer prompt_cache_hit_tokens;
        private Integer prompt_cache_miss_tokens;

        @Data
        public static class PromptTokensDetails {
            private Integer cached_tokens;
        }
    }
}

DeepSeekService服务

import com.alibaba.fastjson2.JSON;
import com.left.domain.DEEPSEEK_MODEL;
import com.left.domain.request.DeepSeekRequest;
import com.left.domain.response.DeepSeekStreamResponse;
import io.netty.channel.ChannelOption;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Flux;
import reactor.netty.http.client.HttpClient;
import reactor.netty.resources.ConnectionProvider;

import javax.annotation.PostConstruct;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;

@Slf4j
@Service
public class DeepSeekService {
    @Value("${deepseek.api.url}")
    private String apiUrl;
    @Value("${deepseek.api.key}")
    private String apiKey;
    private WebClient webClient;

    @PostConstruct
    private void initClient() {
        log.info("初始化WebClient...");

        HttpClient httpClient = HttpClient.create(ConnectionProvider
	        .builder("deepseek-pool")
	        .maxConnections(100)
	        .pendingAcquireTimeout(Duration.ofSeconds(120))
	        .build())
        .compress(true)
        .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 2 * 60 * 1000)
        .responseTimeout(Duration.ofSeconds(120));

        this.webClient = WebClient.builder()
                .codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(-1))
                .baseUrl(apiUrl)
                .clientConnector(new ReactorClientHttpConnector(httpClient))
                .defaultHeader(HttpHeaders.AUTHORIZATION, "Bearer " + apiKey)
                .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                .build();
    }

    public Flux<String> callDeepSeek(String message) {
        log.info("调用deepseek接口开始");
        long start = System.currentTimeMillis();
        try {
            DeepSeekRequest request = new DeepSeekRequest();
            request.setModel(DEEPSEEK_MODEL.R1.name);  //r1模型
            request.setStream(true); // 流式
            List<DeepSeekRequest.Message> list = new ArrayList<>();
            //提示库,可根据自己需要修改
            list.add(new DeepSeekRequest.Message("system", "你是一位大模型提示词生成专家,请根据用户的需求编写一个智能助手的提示词,来指导大模型进行内容生成,要求:\n" +
                    "1. 以 Markdown 格式输出\n" +
                    "2. 贴合用户需求,描述智能助手的定位、能力、知识储备\n" +
                    "3. 提示词应清晰、精确、易于理解,在保持质量的同时,尽可能简洁\n" +
                    "4. 只输出提示词,不要输出多余解释"));
            list.add(new DeepSeekRequest.Message("user", message));

            request.setMessages(list);

            return webClient.post()
          		.uri("/v1/chat/completions")
                  .bodyValue(request)
                  .retrieve()
                  .bodyToFlux(String.class)
                  .filter(resp -> !resp.startsWith("[DONE]"))
                  .doOnNext(resp -> {
                      DeepSeekStreamResponse response = JSON.parseObject(resp, DeepSeekStreamResponse.class);
                      DeepSeekStreamResponse.Choice choice = response.getChoices().get(0);
                      DeepSeekStreamResponse.Choice.Message message = choice.getDelta();
                      if (message != null) {
                          log.info("deepseek接口返回:{}", message.getContent() != null ? message.getContent() : message.getReasoning_content());
                      }
                  }).map(resp -> {
                      DeepSeekStreamResponse response = JSON.parseObject(resp, DeepSeekStreamResponse.class);
                      DeepSeekStreamResponse.Choice choice = response.getChoices().get(0);
                      DeepSeekStreamResponse.Choice.Message message = choice.getDelta();
                      if (message == null) {
                          return "";
                      }
                      return message.getReasoning_content() != null ? message.getReasoning_content() : message.getContent();
                  })
                  .onErrorResume(e -> Flux.just("[ERROR] " + e.getMessage()))
                  .doFinally(e -> {
                      log.info("调用deepseek接口结束,耗时:{}ms", System.currentTimeMillis() - start);
                  });
        } catch (Exception e) {
            log.error("调用deepseek接口异常", e);
        }
        return Flux.just("");
    }
}

调用

@PostMapping("/call")
public Flux<String> call(String message) {
    return deepSeekService.callDeepSeek(message);
}
四、返回的原始数据
// 1、通用数据
{
	"id": "c973449d-6fe7-4eaf-8869-c3db3f9fe747",
	"object": "chat.completion.chunk",
	"created": 1741004759,
	"model": "deepseek-chat",
	"system_fingerprint": "fp_3a5770e1b4_prod0225",
	"choices": [{
			"index": 0,
			"delta": {
				"content": "详细信息"
			},
			"logprobs": null,
			"finish_reason": null
		}]
}
//2、带role标识的数据
{
	"id": "c973449d-6fe7-4eaf-8869-c3db3f9fe747",
	"object": "chat.completion.chunk",
	"created": 1741004759,
	"model": "deepseek-chat",
	"system_fingerprint": "fp_3a5770e1b4_prod0225",
	"choices": [{
			"index": 0,
			"delta": {
				"role": "assistant",
				"content": ""
			},
			"logprobs": null,
			"finish_reason": null
		}]
}
//3、结束帧
{
	"id": "c973449d-6fe7-4eaf-8869-c3db3f9fe747",
	"object": "chat.completion.chunk",
	"created": 1741004759,
	"model": "deepseek-chat",
	"system_fingerprint": "fp_3a5770e1b4_prod0225",
	"choices": [{
			"index": 0,
			"delta": {
				"content": ""
			},
			"logprobs": null,
			"finish_reason": "stop"
		}],
	"usage": {
		"prompt_tokens": 130,
		"completion_tokens": 451,
		"total_tokens": 581,
		"prompt_tokens_details": {
			"cached_tokens": 128
		},
		"prompt_cache_hit_tokens": 128,
		"prompt_cache_miss_tokens": 2
	}
}

其他的api可访问https://api-docs.deepseek.com/zh-cn/进行查看


http://www.kler.cn/a/572455.html

相关文章:

  • 椭圆函数3D双重周期性交互式演示工具
  • 【hot100】102二叉树的层序遍历
  • 生态安全相关
  • Linux搜索---find
  • 【后端开发面试题】每日 3 题(六)
  • 使用ffmpeg读取mp4文件解码失败
  • Android+SpringBoot的老年人健康饮食小程序平台
  • 基于 Spring Boot 的企业级快速启动模板 —— spring-quick
  • 看视频学习方法总结
  • 知识图谱科研文献推荐系统vue+django+Neo4j的知识图谱
  • 【HarmonyOS Next】自定义Tabs
  • [杂学笔记]面向对象特性、右值引用与移动语义、push_back与emplace_back的区别、读写锁与智能指针对锁的管理、访问网站的全过程
  • 大数据学习(52)-MySQL数据库基本操作
  • QT实现单个控制点在曲线上的贝塞尔曲线
  • c#窗体按键点击事件
  • GenBI 可视化选谁:Python Matplotlib?HTML ?Tableau?
  • 计算机毕业设计SpringBoot+Vue.js电商平台(源码+文档+PPT+讲解)
  • UE身体发光设置覆层材质
  • 高压电路试题(二)
  • sqli-labs靶场通关攻略