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

Spring也能接入Deepseek?

引言

最近DeepSeek可谓风光无限,AI可谓是目前互联网最火热的几个名词,我也一直在关注他的发展,从以前的人工智障,到chatGPT的高不可攀(价格太贵),再到DeepSeek的横空出世,才看到了AI从概念到应用的真正出路。可以这样说,随着DeepSeek的出现,势必会在接下来的几年让AI变得异常火热。最近接入了coze和DeepSeek两款,接下来简单介绍下Spring AI接入DeepSeek。

本地部署

首先,接入DeepSeek有两种方式,一种是通过DeepSeek官网接入API的方式,另一种是本地部署DeepSeek,通过Ollama接入API接口。这里我选择了本地DeepSeek接入的模式,因为手上正好有个项目可能会用到AI,甲方希望数据安全性,所以需要本地部署DeepSeek。

我们先去下载Ollama,我这里还在测试,所以用的自己电脑,下载好后直接下一步安装,没有任何难度

这里前文已经实践过了大家可以去参考一下

本地部署Deepseek

运行成功后我们可以开始下一步了

Spring 调用本地API

Spring AI包封装了大部分AI调用的工具,适用于很多大模型,这里因为我们用的是本地Ollama,所以也用对应的包。

<dependency>  
    <groupId>org.springframework.ai</groupId>  
    <artifactId>spring-ai-bom</artifactId>  
    <version>1.0.0-SNAPSHOT</version>  
    <type>pom</type>  
    <scope>import</scope>  
</dependency>
<dependency>  
    <groupId>org.springframework.ai</groupId>  
    <artifactId>spring-ai-ollama-spring-boot-starter</artifactId>  
    <version>1.0.0-M5</version>  
</dependency>

引入对应版本包,这里需要注意一点,Spring AI的SpringBoot版本需要3.2.x以上,jdk需要17版本以上,所以我这里将项目从2.3.7.RELEASE升级到了3.3.4。需要升级的时候也遇到不少坑,比如个别包名变了,或者其他三方包冲突,日志冲突等问题,旧的项目升级还是有一定难度的。
增加模型配置

spring:
    ai:  
        ollama:  
            base-url: http://localhost:11434  
            chat:  
                model: deepseek-r1:1.5b  
        openai:  
            chat:  
            options:  
                response-format: json

默认地址是本地Ollama的地址,以后也可以改成远程,配置需要的大模型模板deepseek-r1:1.5b以及反参格式为json
大模型对话一般情况是不支持直接返回json格式的,SpringAI通过在对话增加关键词信息让AI回答以json格式返回,然后再通过框架将返回的内容解析成json,在解析成对象。这里可以不用在配置中指定json格式,也可以在每次对话的时候指定格式。

@Configuration
public class DeepSeekConfig {
    //注入模型,配置文件中的模型,或者可以在方法中指定模型
    @Autowired
    private OllamaChatModel model;

    @Bean
    public ChatClient chatClient() {
        return ChatClient.builder(this.model).defaultAdvisors().build();
    }
}

配置chatChlient类

    @Autowired  
    private ChatClient chatClient;
    @PostMapping("/testChat")
    public DeepSeekResponse testChat(@RequestBody AIChatReq param){
        //直接返回
        return chatClient.prompt(param.getMsg())
                .call()
                .entity(DeepSeekResponse.class);
    }

新增一个测试接口,发起一个对话试试,这里我希望让AI根据我给出的公式和参数,计算出一个结果值,然后结果通过json返回给我。
以下是我的问题:
V=(A+2C+K×H)×H×L ,这是一个开挖土方计算公式,其中V表示基槽土方量,A表示槽底宽度,C表示工作面宽度,H表示基槽深度,L表示基槽长度,如果A=1.5,C=2,H=2,L=3,k=0,V等于多少
启动服务后,通过postman访问一下

看下日志报错  

日志上发现DeepSeek确实回应了我的请求,最后也明确给出了正确结果,为何会转换失败?
跟踪源码,最后发现报错是在BeanOutputConverter的convert方法上

可以看到Spring AI包将AI回答转换成对象的逻辑
首先会根据反参类型格式化提问,就是通过实现FormatProvider接口实现,StructuredOutputConverter继承FormatProvider接口 

BeanOutputConverter实现StructuredOutputConverter接口,同时主要实现的还有MapOutputConverter,ListOutputConverter两个类。
主要作用也很明晰,BeanOutputConverter是将结果转换成对象,MapOutputConverter转换成Map,ListOutputConverter转换成List
我们着重观察下BeanOutputConverter干的活儿
除了实现了上面说的convert方法外,还实现了getFormat方法

 

意思就是会在我们提问前加上上面那段话,希望AI将结果按JSON格式返回,不要包含markdown模块
然后我们再结合convert方法,将结果转换成对象
但是,实际DeepSeek返回结果时,会带上一大段思考过程的内容 

即上面这一段,使用<think>标签包裹的内容
而Spring AI包在转换时会去判断字符串是否以 ``` 字符开头
由于DeepSeek并不是以此返回内容,导致对象转换出错
既然如此,思路就很简单了,我们需要改造BeanOutputConverter的convert方法

@Slf4j
public class DeepSeekBeanOutputConverter<T> extends BeanOutputConverter<T> {

    public DeepSeekBeanOutputConverter(Class<T> clazz) {
        super(clazz);
    }

    public DeepSeekBeanOutputConverter(Class<T> clazz, ObjectMapper objectMapper) {
        super(clazz, objectMapper);
    }

    public DeepSeekBeanOutputConverter(ParameterizedTypeReference<T> typeRef) {
        super(typeRef);
    }

    public DeepSeekBeanOutputConverter(ParameterizedTypeReference<T> typeRef, ObjectMapper objectMapper) {
        super(typeRef, objectMapper);
    }

    @Override
    public T convert(@NonNull String text) {
        if (StrUtil.isBlank(text)) {
            return null;
        }
        log.info("deepseek response: {}", text);
        text = text.substring(text.indexOf("```json"));
        return (T) super.convert(text);
    }
}

 新建一个类DeepSeekBeanOutputConverter继承自BeanOutputConverter,重写convert方法 在转换提取json里面的内容,再通过父类的convert方法实现回答和类的转化

    @PostMapping("/chat")
    public DeepSeekResponse chat(@RequestBody AIChatReq param){
        //直接返回
        DeepSeekBeanOutputConverter deepSeekBeanOutputConverter = new DeepSeekBeanOutputConverter<>(DeepSeekResponse.class);
        return (DeepSeekResponse) chatClient.prompt(param.getMsg())
                .call()
                .entity(deepSeekBeanOutputConverter);
    }

新增一个方法,在我们提交对话时,将BeanOutputConverter替换成DeepSeekBeanOutputConverter的实现

相关文章:

  • ddt 库注解总结
  • Docker迁移/var/lib/docker之后镜像容器丢失问题
  • Go 语言内存池 (`sync.Pool`) 深度解析
  • 【Java项目】基于Spring Boot的网上商城购物系统
  • 一个原教旨的多路径 TCP
  • Day11,Hot100(贪心算法)
  • 一文了解:部署 Deepseek 各版本的硬件要求
  • 突破加速度计的精度与量程瓶颈:HEROS-GAN技术
  • OpenWebUI配置异常的外部模型导致页面无法打开
  • 基于coze+微信小程序的ai对话
  • 【js逆向入门】图灵爬虫练习平台 第八题
  • .gitignore 文件中添加忽略 .pdb 文件
  • Flutter - StatefulWidget (有状态的 Widget) 和 生命周期
  • 算法——递归
  • Mysql表字段字符集未设置导致乱码问题
  • java excel xlsx 增加数据验证
  • JavaWeb登录认证
  • Linux实操——在服务器上直接从百度网盘下载(/上传)文件
  • 【MySQL】InnoDB中的Buffer Pool
  • CNN:卷积网络中设计1×1夹在主要卷积核如3×3前后的作用
  • 专业网站建设哪里有/成人计算机培训机构哪个最好
  • 建设网站网站名/百度竞价课程
  • 有没有做的很炫的科技型网站/seo下拉优化
  • 番禺做网站系统/手机百度ai入口
  • 贵州网站建设 零玖伍壹网络/交换友情链接平台
  • 内衣批发网站建设/百度注册公司地址