1、本文将详细介绍Ollama这个开源项目,它能帮你在本地轻松运行LLM。同时,还会一步步教你把Ollama和Spring AI集成起来,让你能在Spring AI项目里用上Ollama的模型。我们会从Ollama的基础概念讲起,再到它的安装、模型下载运行,以及和Spring AI集成的具体步骤
一、Ollama是什么?
Ollama是一个超实用的开源项目,有了它,在自己电脑上运行大语言模型就变得轻松多了。它和Docker有点像,Docker主要是管理项目外部的依赖,像数据库或者JMS;而Ollama专注于大语言模型的运行。它把下载、安装大语言模型,还有和模型交互这些复杂的流程都简化了,支持好多热门的模型,像LLaMA -- 2、Mistral、CodeLLaMA等等。而且,我们还能根据自己的需求微调模型的表现呢。
二、安装Ollama
Ollama提供了三种主要的安装方式,下面来详细说说。
(一)使用安装文件安装
对于Windows和Mac的新手用户来说,这种方式最简单。
- 打开Ollama的官方网站:ollama.com/ 。
- 找到并点击"Download"按钮,网站会自动识别你的操作系统,给出适合的安装文件。
- 下载下来后不要急着安装,首先更改安装目录,因为Ollama默认是安装到C盘,并且中途不可选其他目录,所以我们在安装之前需要先更改安装目录,windows WIN+R 输入CMD进行黑窗口, 执行:OllamaSetup.exe /DIR:D:\Ollama
注:此命令是强制将Ollama安装到D盘Pllama目录下,因为如果你不指定的话,那么ollama会将你C盘干爆,把你电脑干崩,因为大模型非常非常吃硬盘 4 然后双击OllamaSetup.exe安装
三、启动ollama
默认情况下ollama是电脑开机跟随系统自动启动的,在电脑的右下角会有ollama羊驼小图标,表示此时ollama已经启动了,下图所示;如果ollama没有启动,可以在电脑的开始菜单中去点击启动。
配置大模型安装的环境变量,一定要配置,不要干爆你C盘,windows电脑新建环境变量, OllAMA_MODELS=D:\devSoft\Ollama\models 直接上截图不废话
配置完后请记得一定要重启ollama,不重启后续安装的大模型目录不会生效!!!! 点击ollama小图标退出
然后再点击启动
三、安装DeepSeek-R1
打开网页deepseek-r1 复制 ollama run deepseek-r1:8b,在黑窗口执行此命令
可以看到已经可以进行对话了,当然,你电脑配置好,可以选择更高的参数,本机只能跑8b,故而选择8b(1b等于10亿参数)。执行 /bye 则可以退出对话
执行ollama list可以查看当前安装的大模型集合
再次执行ollama run deepseek-r1:8b 则可以进入对话。
通过PowerShell不直观,接下来我们可以安装可视化的界面进行对话。
三、安装Open WebUI
一共分为三步:
- 在Windows上安装Python 3.11(一定是这个版本!)
- 部署Open WebUI
- 打开Open WebUI
- 使用方法
在Windows上安装Python 3.11
开始之前一定要确保你的电脑上有安装Python3.11版本,没有的请安装它。你可以去python官网下载:www.python.org/ftp/python/...
安装完成,执行命令查看是否安装成功,出现以下界面即可
部署Open WebUI
复制一下下面的命令行,粘贴到【PowerShell(管理员)】或【终端(管理员)】里,回车
arduino
pip install open-webui
看下以下界面,即安装成功 重点来了!!!!!要先配置 $env:HF_ENDPOINT="hf-mirror.com" 因为国外镜像源不能访问了,所以一定要配置这个
执行open-webui serve 命令进行安装
打开Open WebUI
如果防火墙提示python要联网,请点击【允许访问】 启动成功后在浏览器打开"http://localhost:8080" 然后创建账号。进入到页面,选择deepseek-R1模型
接下来就可以进行可视化的对话了。
Java调用本地模型
pom:
xml
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.3</version> <!-- 请根据需要选择合适的版本 -->
</dependency>
代码:
arduino
@Data
@AllArgsConstructor
@NoArgsConstructor
public class DeepSeekRequest {
/**
* 消息列表,包含对话中的消息对象
*/
private List<Message> messages;
/**
* 模型名称,指定要使用的模型
*/
private String model;
/**
* 频率惩罚,用于减少重复内容的概率
* 介于 -2.0 和 2.0 之间的数字。如果该值为正,那么新 token 会根据其在已有文本中的出现频率受到相应的惩罚,降低模型重复相同内容的可能性
*/
private double frequency_penalty;
/**
* 最大生成的令牌数
* 介于 1 到 8192 间的整数,限制一次请求中模型生成 completion 的最大 token 数。输入 token 和输出 token 的总长度受模型的上下文长度的限制。
* 如未指定 max_tokens参数,默认使用 4096
*/
private int max_tokens;
/**
* 存在惩罚,用于增加新话题的概率
*/
private double presence_penalty;
/**
* 响应格式,指定返回的响应格式
*/
private ResponseFormat response_format;
/**
* 停止序列,指定生成文本时的停止条件
*/
private Object stop;
/**
* 是否流式返回结果
*/
private boolean stream;
/**
* 流式选项,指定流式返回的选项
* stream 为 true 时,才可设置此参数
*/
private Object stream_options;
/**
* 温度,控制生成文本的随机性
* 介于 0 和 2 之间的数字,值越低,更加准确
*/
private double temperature;
/**
* 核采样参数,控制生成文本的多样性
*/
private double top_p;
/**
* 工具列表,指定可用的工具
*/
private Object tools;
/**
* 工具选择,指定使用的工具
*/
private String tool_choice;
/**
* 是否返回对数概率
*/
private boolean logprobs;
/**
* 对数概率选项,指定返回的对数概率选项
*/
private Object top_logprobs;
/**
* 消息对象,包含单个消息的内容和角色
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public static class Message {
/**
* 消息内容
*/
private String content;
/**
* 消息角色,例如 "system" 或 "user"
*/
private String role;
}
/**
* 响应格式对象,指定返回的响应格式类型
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public static class ResponseFormat {
/**
* 响应格式类型,例如 "text"
*/
private String type;
}
}
arduino
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class DeepSeekResponse {
/**
* API 模型名称,标识使用的模型版本。
*/
private String model;
/**
* 响应创建时间,格式为 ISO 8601 标准的时间戳。
*/
private String createdAt;
/**
* 消息内容,包含角色和具体内容。
*/
private Message message;
/**
* 完成原因,表示请求完成的具体原因。
*/
private String doneReason;
/**
* 是否完成,表示请求是否成功完成。
*/
private boolean done;
/**
* 总持续时间,单位为纳秒,表示整个请求处理的总时间。
*/
private long totalDuration;
/**
* 加载持续时间,单位为纳秒,表示加载阶段的耗时。
*/
private long loadDuration;
/**
* 提示评估次数,表示提示评估的次数。
*/
private int promptEvalCount;
/**
* 提示评估持续时间,单位为纳秒,表示提示评估的总耗时。
*/
private long promptEvalDuration;
/**
* 评估次数,表示总的评估次数。
*/
private int evalCount;
/**
* 评估持续时间,单位为纳秒,表示总的评估耗时。
*/
private long evalDuration;
/**
* 内部静态类,用于封装消息内容。
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class Message {
/**
* 消息的角色,例如 "system" 或 "user"。
*/
private String role;
/**
* 消息的具体内容。
*/
private String content;
}
}
单元测试:
ini
@Test
public void deekSeekTest() throws IOException {
String baseUrl = "http://127.0.0.1:11434/api/chat";
/**
* 创建DeepSeekRequest对象 属性注解均在实体类中
* 官方案例文档地址:https://api-docs.deepseek.com/zh-cn/api/create-chat-completion
*/
DeepSeekRequest requestObject = new DeepSeekRequest();
List<DeepSeekRequest.Message> messages = new ArrayList<>();
// 添加消息 根据自身情况调整
messages.add(new DeepSeekRequest.Message("写一段关于蛇年的祝福语", "user"));
requestObject.setMessages(messages);
// 模型 根据自身情况调整
requestObject.setModel("deepseek-r1:8b");
requestObject.setFrequency_penalty(0);
requestObject.setMax_tokens(2048);
requestObject.setPresence_penalty(0);
requestObject.setResponse_format(new DeepSeekRequest.ResponseFormat("text"));
requestObject.setStop(null);
requestObject.setStream(false);
requestObject.setStream_options(null);
requestObject.setTemperature(1);
requestObject.setTop_p(1);
requestObject.setTools(null);
requestObject.setTool_choice("none");
requestObject.setLogprobs(false);
requestObject.setTop_logprobs(null);
// 使用Gson将请求对象转换为JSON字符串
Gson gson = new Gson();
String jsonBody = gson.toJson(requestObject);
// 创建OkHttpClient实例,并设置超时时间
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(300, java.util.concurrent.TimeUnit.SECONDS)
.readTimeout(300, java.util.concurrent.TimeUnit.SECONDS)
.writeTimeout(300, java.util.concurrent.TimeUnit.SECONDS)
.build();
// 设置请求体的媒体类型为JSON
MediaType mediaType = MediaType.parse("application/json");
// 创建请求体,包含JSON字符串
RequestBody body = RequestBody.create(mediaType, jsonBody);
// 创建HTTP POST请求
Request request = new Request.Builder()
.url(baseUrl)
.method("POST", body)
.addHeader("Content-Type", "application/json")
.addHeader("Accept", "application/json")
.build();
// 发送请求并获取响应
Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
try {
String responseBody = response.body().string();
log.info("responseBody:{}", responseBody);
//将响应体内容转换为DeepSeekResponse对象
DeepSeekResponse deepSeekResponse = JSON.parseObject(responseBody,DeepSeekResponse.class);
log.info(deepSeekResponse.getMessage().getContent());
} catch (IOException e) {
e.printStackTrace();
}
}
}