返回顶部
热门问答 更多热门问答
技术文章 更多技术文章

Spring AI DeepSeek R1搭建企业私有化模型工具(直接使用)

[复制链接]
链载Ai 显示全部楼层 发表于 1 小时前 |阅读模式 打印 上一主题 下一主题

本文分享基于Spring AI+DeepSeek R1搭建企业私有化模型工具的案例,效果截图:

可自由切换部署的本地模型,选择deepseek-r1 1.5b参数模型:

选择deepseek-r1 7b参数模型:

涉及到的技术有OLLAMA、DeepSeek R1与Spring AI,OLLAMA部署在Linux操作系统,Spring AI项目本地可以直接跑,也可以部署在服务器上。


01

OLLAMA安装

在Linux系统下,官方推荐的安装OLLAMA命令:

curl -fsSL https://ollama.org.cn/install.sh | sh

实测后发现网速非常慢,推荐使用第二种:

https://github.com/ollama/ollama/releases

选择Assets,下载Windows及对应Linux平台的ollama:

为方便大家使用,我已准备好下载文件:通过网盘分享的文件:

链接: https://pan.baidu.com/s/1MkLCX1K89fWx1UPf4IKbbw?pwd=mbh3

提取码: mbh3

本人Linux操作系统适用ollama-linux-amd64.tgz


将ollama-linux-amd64.tgz文件上传到Linux操作系统/u01/ollama目录下,如果没有此目录,可创建:mkdir -p /u01/ollama


然后进入目录:cd /u01/ollama


安装ollama:

sudo tar -C /usr -xzf ollama-linux-amd64.tgz


安装之后ollama命令出现在在:/usr/bin/ollama文件下


运行ollama:

ollama serve


保持运行状态,另外再打开一个Linux终端查看ollama状态:

表示已运行成功。


接下来需要将ollama加入后台运行,开机启动:

(1)为 Ollama 创建用户和组:

sudo useradd -r -s /bin/false -U -m -d /usr/share/ollama ollama


sudo usermod -a -G ollama $(whoami)


(2)创建OLLAMA服务文件:

sudo vim /etc/systemd/system/ollama.service

文件内容:


[Unit]

Description=Ollama Service

After=network-online.target


[Service]

ExecStart=/usr/bin/ollama serve

User=ollama

Group=ollama

Restart=always

RestartSec=3

Environment="OLLAMA_HOST=0.0.0.0:11434"


[Install]

WantedBy=default.target


其中Environment="OLLAMA_HOST=0.0.0.0:11434"表示指定OLLAMA可以运行的ip为任意ip,默认为127.0.0.1

保存并退出编辑/etc/systemd/system/ollama.service后

给文件授可执行权限:

sudo chmod 777 /etc/systemd/system/ollama.service

(3)后台启动服务ollama

sudo systemctl daemon-reload

设置开机自启:

sudo systemctl enable ollama

启动ollama:

sudo systemctl start ollama

sudo systemctl enable ollama

如果后期修改了/etc/systemd/system/ollama.service配置,需要重新执行以下命令生效:

首先:sudo systemctl daemon-reload

然后:sudo systemctl restart ollama

最后:sudo systemctl enable ollama

02

安装DeepSeek R1模型

第二步,需要借助ollama安装deepseek r1模型。

已知满血版的DeepSeek R1参数是671B。

需要根据您的Linux服务器性能决定安装哪个模型,参数越多的模型,需要的显卡配置要求和内存要求越高。

我的此台Linux服务器是CPU型号,内存16G:

可以安装deepseer-r1 1.5b和deepseek-r1 7b模型,已测试可以。

首先安装deepseek-r1:1.5b模型:

ollama run deepseek-r1:1.5b

会下载r1 1.5b模型一段时间,估计20分钟,1.6G大小左右,成功后出现对话框:

接着安装deepseek-r1:7b模型:

ollama run deepseek-r1:7b

先按Ctrl+D键退出1.5b模型,然后输入:ollama run deepseek-r1:7b

需要下载7b模型,估计要花40分钟~1个小时左右,4.7G大小左右,成功后出现对话框:

注意:在机器性能一定的情况下,模型越大,运行速度越慢,需要用到调优,后期会分享。

如此完成了DeepSeek R1模型的安装。

03

接入Spring AI

接入Spring AI之前,采用的是Spring Boot构建版本,需要用到Spring Boot 3.3.4版本,JDK 17以上,Maven需要3.6.3版本以上,不然无法正常使用Spring AI功能,本部分示例的代码已开源:https://gitee.com/javagongfu/huaxinfang_audio_translate.git

下面分享接入过程:

SpringBoot版本为:

<parent>


<groupId>org.springframework.boot</groupId>


<artifactId>spring-boot-starter-parent</artifactId>


<version>3.3.4</version>


</parent>

其中properties配置为:

<properties>


<maven.compiler.source>17</maven.compiler.source>


<maven.compiler.target>17</maven.compiler.target>


<lombok.version>1.18.30</lombok.version>


<hutool.version>5.8.28</hutool.version>


</properties>

接入Spring Boot核心坐标:

<dependency>


<groupId>org.springframework.boot</groupId>


<artifactId>spring-boot-starter-web</artifactId>


</dependency>


<!--测试依赖-->


<dependency>


<groupId>org.springframework.boot</groupId>


<artifactId>spring-boot-starter-test</artifactId>


<scope>test</scope>


</dependency>

接入lombok坐标:

<dependency>


<groupId>org.projectlombok</groupId>


<artifactId>lombok</artifactId>


<version>${lombok.version}</version>


</dependency>

由于项目使用了SpringBoot自带的Thymeleaf做界面,开箱即用,需引入Thymeleaf坐标:

<dependency>


<groupId>org.springframework.boot</groupId>


<artifactId>spring-boot-starter-thymeleaf</artifactId>


</dependency>

引入Spring AI功能:

<dependency>


<groupId>org.springframework.ai</groupId>


<artifactId>spring-ai-ollama-spring-boot-starter</artifactId>


<version>1.0.0-M5</version>


</dependency>

项目中要使用Java EE,还需引入Java EE坐标:

<dependency>


<groupId>javax.servlet</groupId>


<artifactId>javax.servlet-api</artifactId>


<version>4.0.1</version>


<scope>provided</scope>


</dependency>

logback日志管理工具需要使用:

<dependency>


<groupId>ch.qos.logback</groupId>


<artifactId>logback-classic</artifactId>


<version>1.4.8</version>


</dependency>

再增加依赖管理器:

<dependencyManagement>


<dependencies>


<dependency>


<groupId>org.springframework.ai</groupId>


<artifactId>spring-ai-bom</artifactId>


<version>1.0.0-M5</version>


<type>pom</type>


</dependency>


</dependencies>


</dependencyManagement>

添加Maven快照:

<!--快照版本-->


<repositories>


<repository>


<id>spring-milestones</id>


<name>spring-milestones</name>


<url>https://repo.spring.io/milestone</url>


<releases>


<enabled>true</enabled>


</releases>


<snapshots>


<enabled>true</enabled>


</snapshots>


</repository>


</repositories>

项目Maven打包工具:

<!--打包-->


<build>


<plugins>


<plugin>


<groupId>org.springframework.boot</groupId>


<artifactId>spring-boot-maven-plugin</artifactId>


<configuration>


<includeSystemScope>true</includeSystemScope>


<fork>true</fork>


</configuration>


</plugin>



</plugins>


</build>

写好Maven pom.xml文件后,在IntelliJ IDEA刷新安装一下:

注意:Maven仓库配置需要添加阿里云镜像:


<mirror>


<id>aliyun</id>


<name>Aliyun Maven</name>


<url>https://maven.aliyun.com/repository/public</url>


<mirrorOf>central</mirrorOf>


</mirror>

接着在项目/src/main/resources下新建application.yml和application-dev.yml文件:

application.yml文件内容:

server:


# undertow配置


undertow:


# HTTP post内容的最大大小。当值为-1时,默认值为大小是无限的


max-http-post-size: -1


#以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理


#每块buffer的空间大小,越小的空间被利用越充分


buffer-size: 512


#是否分配的直接内存


direct-buffers:true


threads:


#设置IO线程数,它主要执行非阻塞的任务,它们会负责多个连接,默认设置每个CPU核心一个线程


io:8


#阻塞任务线程池,当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载


worker:256


port:8567


servlet:


context-path: /


spring:


profiles:


# dev默认为开发环境, prod线上环境


active: dev


application:


name: huaxinfang_audio_translate


main:


allow-bean-definition-overriding:true


servlet:


multipart:


max-file-size: 10MB


max-request-size: 10MB


application-dev.yml文件内容:

parameters:


model:"offline"#离线模型为例


hotWords:"{\"自定义\":20,\"热词\":20,\"设置\":30}"


fileUrl:"D:/Audio"


serverIpPort:"ws://X.X.X.X:10095/"



spring:


ai:


ollama:


base-url:http://X.X.X.X:11434


chat:


enabled:true


options:


model: deepseek-r1:1.5b


temperature:0.8


keep_alive: 5m


application-dev.yml其中base-url:

http://X.X.X.X:11434

改为您部署的OLLAMA服务器ip地址,OLLAMA服务的端口号默认是11434,如果您的服务器ip是:192.168.3.1,则配置应该为:

base-url:http://192.168.3.1:11434

可以去服务器ip看11434有没有启动:

netstat -an|grep 11434

表示OLLAMA的11434端口对所有ip可用,如果您使用的是云服务器,还需要在云服务器的安全组放开11434端口,防火墙放开11434端口。

在项目/src/main/java目录下新建com.asr.client包

在包下新建AudioTranslateApplication.java文件,作为SpringBoot启动类,内容为:

packagecom.asr;



importlombok.extern.slf4j.Slf4j;


importorg.springframework.boot.Banner;


importorg.springframework.boot.autoconfigure.SpringBootApplication;


importorg.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;


importorg.springframework.boot.builder.SpringApplicationBuilder;


importorg.springframework.context.annotation.ComponentScan;



@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})


@Slf4j


@ComponentScan(basePackages ="com.asr.client")


public classAudioTranslateApplication {


public static voidmain(String[] args) {


newSpringApplicationBuilder(AudioTranslateApplication.class)


.bannerMode(Banner.Mode.CONSOLE)//控制台打印


.run(args);


log.info("项目初始化完毕!");


}


}

接着新建com.asr.client.config包

在config包下新建WebConfig类,内容为:

packagecom.asr.client.config;



importorg.springframework.context.annotation.Configuration;


importorg.springframework.web.servlet.config.annotation.AsyncSupportConfigurer;


importorg.springframework.web.servlet.config.annotation.WebMvcConfigurer;



/**


●@authorAI技术学院


●@version1.0.1


●@projectdavinci_audio_translate


●@description


●@date2025/3/4 10:54:24


*/


@Configuration


public classWebConfigimplementsWebMvcConfigurer {


@Override


public voidconfigureAsyncSupport(AsyncSupportConfigurer configurer) {


configurer.setDefaultTimeout(3600000);// 1小时超时


}


}

新建com.asr.client.controller包,包下新建OllamaChatController.java文件,内容为:

packagecom.asr.client.controller;



importjakarta.annotation.PostConstruct;


importlombok.extern.slf4j.Slf4j;


importorg.springframework.ai.chat.messages.Message;


importorg.springframework.ai.chat.messages.SystemMessage;


importorg.springframework.ai.chat.messages.UserMessage;


importorg.springframework.ai.chat.model.ChatResponse;


importorg.springframework.ai.chat.prompt.Prompt;


importorg.springframework.ai.ollama.OllamaChatModel;


importorg.springframework.ai.ollama.api.OllamaApi;


importorg.springframework.ai.ollama.api.OllamaOptions;


importorg.springframework.beans.factory.annotation.Autowired;


importorg.springframework.web.bind.annotation.GetMapping;


importorg.springframework.web.bind.annotation.RequestMapping;


importorg.springframework.web.bind.annotation.RequestParam;


importorg.springframework.web.bind.annotation.RestController;


importreactor.core.publisher.Flux;



importjava.util.ArrayList;


importjava.util.List;



/**


*@authorAI技术学院


*@version1.0.1


*@projectdavinci-ocr-python


*@descriptionOllama接口


*@date2025/2/5 11:20:31


*/


@RequestMapping("/deepseek")


@RestController


@Slf4j


public classOllamaChatController {



private finalOllamaChatModelchatModel;


private finalList<Message>chatHistoryList=newArrayList<>();



@Autowired


publicOllamaChatController(OllamaChatModel chatModel) {


this.chatModel= chatModel;


}



@PostConstruct


public voidinit() {


chatHistoryList.add(newSystemMessage("你是一个很好的助手."));


}


/**


@GetMapping("/ai/ollama/generate")


public Map<String,String> generate(@RequestParam(value = "message", defaultValue = "Tell me a joke") String message) {


return Map.of("generation", this.chatModel.call(message));


}


@GetMapping("/ai/ollama/generateStream")


public Flux<ChatResponse>generateStream(@RequestParam(value = "message", defaultValue = "Tell me a joke") String message) {


Prompt prompt = new Prompt(new UserMessage(message));


return this.chatModel.stream(prompt);


}


*/


@GetMapping("/ai/ollama/generate")


publicStringgenerate(String message) {


String call =chatModel.call(message);


returncall;


}



@GetMapping("/ai/generateStream")


publicFlux<ChatResponse>generateStream(@RequestParam(value ="message",defaultValue ="Tell me a joke") String message,@RequestParam(value="model",defaultValue="deepseek-r1:1.5b")String model) {


// chatHistoryList.add(new UserMessage(message));


// Prompt prompt = new Prompt(chatHistoryList, OllamaOptions.create()


// .withModel(model)//使用哪个大模型


// .withTemperature(0.8));


Prompt prompt =newPrompt(newUserMessage(message),OllamaOptions.create()


.withModel(model)//使用哪个大模型


.withTemperature(0.8));


returnchatModel.stream(prompt);


}


}



其中OllamaChatModel是实际的Spring AI模型控制者。

自动注入ChatModel:

@Autowired


publicOllamaChatController(OllamaChatModel chatModel) {


this.chatModel= chatModel;


}


如果模型为同步调用,可以增加一个聊天历史对话框记录:

private finalList<Message>chatHistoryList=newArrayList<>();


并初始化历史消息角色:

@PostConstruct


public voidinit() {


chatHistoryList.add(newSystemMessage("你是一个很好的助手."));


}


消息同步调用:

@GetMapping("/ai/ollama/generate")


publicStringgenerate(String message) {


String call =chatModel.call(message);


returncall;


}


本地运行起来后,可以在PostMan使用:

http://localhost:8567/deepseek/ai/ollama/generate?message=你可以帮我创作一首歌么

查看同步调用结果

在企业实际应用中,应该采用Stream流式打字输出,以下方法还能根据用户传入的模型model参数流式输出结果:

@GetMapping("/ai/generateStream")


publicFlux<ChatResponse>generateStream(@RequestParam(value ="message",defaultValue ="Tell me a joke") String message,@RequestParam(value="model",defaultValue="deepseek-r1:1.5b")String model) {


Prompt prompt =newPrompt(newUserMessage(message),OllamaOptions.create()


.withModel(model)//使用哪个大模型


.withTemperature(0.8));


returnchatModel.stream(prompt);


}


model参数默认为deepseek-r1:1.5b模型,并设置模型选择和温度temperature:

由于项目采用的开箱即用Thymeleaf前端页面,所以在/src/main/resources下新建templates模板文件夹:

在templates模板文件夹中新建deepseek.html采用Thymeleaf引擎:

代码为:

<!DOCTYPEhtml>


<htmllang="en">


<head>


<metacharset="UTF-8">


<title>DeepSeek R1本地模型</title>


</head>


<style>


.terminal-style{


background:#1e1e1e;


color:#00ff00;


font-family:monospace;


padding:20px;


height:400px;


overflow-y:auto;


border-radius:5px;


}


#inputMsg{


padding:10px;


height:50px;


}


#model{


margin-bottom:20px;


}


[data-placeholder]:empty:before{


content:attr(data-placeholder);


color:#888;


pointer-events:none;


display:block;/*用于换行*/


}


</style>


<script>


functionstartStream() {


constmessage =document.getElementById('contentedit').innerText;


consteventSource =newEventSource(`/deepseek/ai/generateStream?message=${encodeURIComponent(message)}&model=${document.getElementById('model').value}`);


constcontainer =document.getElementById('response-container');


container.innerHTML='';


eventSource.onmessage=function(e) {


constresponse =JSON.parse(e.data);


console.log('respose:'+response.result.output.content);


container.innerHTML+= response.result.output.content;


container.scrollTop= container.scrollHeight;//自动滚动


};


eventSource.onerror= () => {


eventSource.close();


// container.innerHTML += "<br>连接已关闭";


};


}


// document.getElementById('contentedit').addEventListener('input', function() {


// document.getElementById('inputMsg').value = this.innerText;


// });


</script>


<body>


<pth:text="${message}"></p>


<divid="output"th:fragment="chat-area">


模型类别:


<selectname="model"id="model">


<optionvalue="deepseek-r1:1.5b"selected>DeepSeek-R1 1.5b</option>


<optionvalue="deepseek-r1:7b">DeepSeek-R1 7b</option>


</select><br>


<divcontenteditable="true"id="contentedit"


style="border:1px solid#ccc;padding:15px"


data-target="inputMsg"data-placeholder="请描述您的问题需求"></div>


<inputtype="hidden"id="inputMsg"/>


<buttononclick="startStream()">发送</button>


<!--查询参数-->


<ath:href="@{/hello}">跳转语音识别使用</a>


<divid="response-container"class="terminal-style"></div>


</div>


</body>


</html>


前端代码不细讲,拷贝进deepseek.html即可:

在com.asr.client.controller包下新建HelloController.java文件,增加:

packagecom.asr.client.controller;



importcn.hutool.core.io.FileUtil;


importcn.hutool.core.lang.UUID;


importcn.hutool.json.JSONObject;


importcn.hutool.json.JSONUtil;


importcom.asr.client.core.exception.ServiceException;


importcom.asr.client.service.RecognitionService;


importlombok.extern.slf4j.Slf4j;


importorg.springframework.beans.factory.annotation.Autowired;


importorg.springframework.beans.factory.annotation.Value;


importorg.springframework.stereotype.Controller;


importorg.springframework.ui.Model;


importorg.springframework.web.bind.annotation.GetMapping;


importorg.springframework.web.bind.annotation.PostMapping;


importorg.springframework.web.multipart.MultipartFile;


importorg.springframework.web.socket.*;


importorg.springframework.web.socket.client.standard.StandardWebSocketClient;



importjava.io.File;


importjava.io.IOException;


importjava.net.URI;


importjava.util.concurrent.CountDownLatch;


importjava.util.concurrent.TimeUnit;



/**


*@authorAI技术学院


*@version1.0.1


*@projectdavinci_audio_translate


*@description接口


*@date2025/2/26 09:19:07


*/


@Controller


@Slf4j


public classHelloController {


@GetMapping("/deepseek")


publicStringdeepseek(Model model) {


model.addAttribute("message","DeepSeek R1本地模型阉割版");


return"deepseek";


}


}


其中@Controller说明是一个页面控制器。

@GetMapping("/deepseek")


publicStringdeepseek(Model model) {


model.addAttribute("message","DeepSeek R1本地模型阉割版");


return"deepseek";


}


方法尾return "deepseek"定位到deepseek.html模板页。

在AudioTranslateApplication运行起来后

本地浏览器输入:http://localhost:8567/deepseek

出现工具页面

先选择1.5b模型,输入描述:

可以看到逐字输出:

再切换成7b模型,输入描述,速度会更慢,模型更大:

响应时间更长:

也是逐字打印:

本代码示例仓库https://gitee.com/javagongfu/huaxinfang_audio_translate.git

还包含阿里达摩院FunASR语音识别使用,有兴趣的可以按照README安装FunASR,接入项目使用,有详细说明。

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

链载AI是专业的生成式人工智能教程平台。提供Stable Diffusion、Midjourney AI绘画教程,Suno AI音乐生成指南,以及Runway、Pika等AI视频制作与动画生成实战案例。从提示词编写到参数调整,手把手助您从入门到精通。
  • 官方手机版

  • 微信公众号

  • 商务合作

  • Powered by Discuz! X3.5 | Copyright © 2025-2025. | 链载Ai
  • 桂ICP备2024021734号 | 营业执照 | |广西笔趣文化传媒有限公司|| QQ