本次目标在上一篇登录的基础上,搭建一个基本的模型对话UI界面,可以与
deepseek
完成简单的交流。
- 要完成本次案例需要学习Ollama本地化部署相关的知识
- 项目使用的是
deepseek-r1:32b
模型完成,大家结合自己电脑配置自行下载合适的模型
预期效果
DeepSeek+Ollama本地化部署
安装 Ollama
下载 Ollama
- 访问Ollama 官方下载页面,选择适合你操作系统的安装包。
- 如果无法访问官网,可以尝试从Ollama GitHub Releases下载。
安装 Ollama
- Windows
- 下载安装包后,双击安装文件并按照提示完成安装。
- 如果需要指定安装路径,可以通过命令行安装:
bash
OllamaSetup.exe /DIR="D:\Net_Program\Net_Ollama"
- 安装完成后,退出 Ollama(右键点击任务栏图标并选择退出)。
- macOS
bash
curl https://ollama.ai/install.sh | sh
- Linux
bash
curl -fsSL https://ollama.com/install.sh | sh
systemctl start ollama
验证 Ollama 安装
打开命令行工具,运行以下命令验证 Ollama 是否安装成功:
bash
ollama -v
如果输出版本号,表示安装成功。
下载并运行 DeepSeek R1 模型
选择模型大小根据你的硬件配置选择合适的模型大小:
- 1.5B 模型:适合低配置设备(如 8GB 显存)。
- 7B 模型:适合中等配置设备(如 16GB 显存)。
- 32B 模型:适合高性能设备(如 32GB 显存)。
下载并运行模型
在命令行中运行以下命令下载并运行 DeepSeek R1 模型:
bash
ollama run deepseek-r1:7b
如果需要下载其他大小的模型,可以指定版本号,例如:
bash
ollama run deepseek-r1:1.5b
配置环境变量(可选)
修改模型存储路径
为了避免占用 C 盘空间,可以将模型存储路径修改到其他磁盘:
bash
OLLAMA_MODELS=D:\Net_Program\Net_Ollama\models
多 GPU 支持
如果你有多个 GPU,可以通过以下命令指定使用的 GPU 索引:
bash
CUDA_VISIBLE_DEVICES=0,1
使用 DeepSeek R1 模型
通过命令行交互
运行模型后,Ollama 会进入交互模式,你可以直接输入问题并获取回答。输入/bye
退出交互模式。
通过 HTTP API
调用你可以通过 HTTP API 与模型交互。以下是一个示例:
bash
curl -X POST http://localhost:11434/api/generate -d '{
"model": "deepseek-r1:7b",
"prompt": "请解释量子计算的基本原理"
}'
性能优化
内存管理
根据你的硬件配置,可以调整内存限制:
bash
ollama run deepseek-r1:7b --memory-limit 16GB
上下文长度调整
根据需求调整上下文长度:
bash
ollama run deepseek-r1:7b --context-length 8192
常见问题解决
内存分配错误
如果遇到内存分配错误,可以尝试以下命令:
bash
ollama run deepseek-r1:7b --memory-limit 8GB
模型加载问题
如果模型加载失败,可以尝试重新拉取模型:
bash
ollama pull deepseek-r1:7b
总结
通过以上步骤,你可以在本地成功部署并运行 DeepSeek R1 模型。确保根据你的硬件配置选择合适的模型大小,并根据需要调整性能参数。
DeepSeek-R1模型在Vue3中的使用
小助手界面实现
新建views/ChatMsgStream/index.vue
组件文件来实现智能小助手的UI界面
vue
<template>
<el-row class="chat-window">
<el-col class="chat-title" :span="20">
智能小助手<span v-if="isThinking"><i class="el-icon-loading"></i>(思考中......)</span>
</el-col>
<el-col class="chat-content" :span="24">
<template v-for="(message, index) in messages" :key="index">
<el-collapse v-if="message.role === 'assistant'">
<el-collapse-item
:title="`思考信息${
(message.thinkTime && '-' + message.thinkTime + 's') || ''
}`"
>
{{ message.thinkInfo }}
</el-collapse-item>
</el-collapse>
<div class="chat-message">
<div
v-if="message.content"
class="chat-picture"
:class="{ 'chat-picture-user': message.role === 'user' }"
>
{{ message.role === "user" ? "用户" : "助手" }}
</div>
<v-md-preview :text="message.content"></v-md-preview>
</div>
</template>
</el-col>
<el-col class="chat-input" :span="20">
<el-input
v-model="userInput"
type="textarea"
:rows="4"
placeholder="请输入消息"
></el-input>
</el-col>
<el-col class="chat-btn" :span="4">
<el-button type="primary" plain @click="sendMessage">发送</el-button>
</el-col>
</el-row>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { callDeepSeekAPI } from "@/apis/chatAPIStream";
const messages = ref<
{ role: string; content: string; thinkTime?: number; thinkInfo?: string }[]
>([]);
const userInput = ref<string>("");
let thinkStartTime = 0; // 思考开始时间
let thinkEndTime = 0; // 思考结束时间
let isThinking = ref<boolean>(false); // 是否处于思考状态
const formatDuring = (millisecond: number): number => {
let seconds: number = (millisecond % (1000 * 60)) / 1000;
return seconds;
};
const sendMessage = async () => {
if (!userInput.value.trim()) return;
// 添加用户消息
messages.value.push({ role: "user", content: userInput.value, thinkTime: 0 });
try {
// 调用 DeepSeek API
const stream = await callDeepSeekAPI(messages.value);
const decoder = new TextDecoder("utf-8");
let assistantContent = ""; // 初始化助手内容
const reader = stream.getReader();
messages.value.push({ role: "assistant", content: "", thinkTime: 0, thinkInfo: "" });
// 读取流式数据
while (true) {
const { done, value } = await reader.read();
if (done) break;
// console.log(value, "value");
const chunk = decoder.decode(value, { stream: true });
// console.log(chunk, "chunk");
let _chunkArr = chunk.split("\n").filter(Boolean);
_chunkArr.forEach((item: string) => {
let _content = "";
let {
message: { content },
} = JSON.parse(item);
_content = content;
assistantContent += _content; // 拼接流式数据
});
// 处理 <think> 标签
if (
/<think>(.*?)/gs.test(assistantContent) &&
!/<\/think>/gs.test(assistantContent)
) {
let _thinkInfo = assistantContent.replace(/<think>/gs, "");
if (!thinkStartTime) {
thinkStartTime = Date.now();
}
messages.value[messages.value.length - 1].thinkInfo = _thinkInfo;
isThinking.value = true;
continue;
} else if (/<\/think>/gs.test(assistantContent)) {
assistantContent = assistantContent.replace(/<think>(.*?)<\/think>/gs, "");
isThinking.value = false;
if (!thinkEndTime) {
thinkEndTime = Date.now();
}
messages.value[messages.value.length - 1].thinkTime = formatDuring(
thinkEndTime - thinkStartTime
);
}
// 逐字输出动画
let currentContent = "";
const chars = assistantContent.split("");
chars.forEach((char, i) => {
currentContent += char;
messages.value[messages.value.length - 1].content = currentContent;
});
}
thinkStartTime = 0;
thinkEndTime = 0;
} catch (error) {
console.error("API 调用失败:", error);
} finally {
userInput.value = ""; // 清空输入框
}
};
</script>
<style scoped lang="scss">
.user {
color: blue;
}
.assistant {
color: green;
}
.chat-window {
width: 60%;
padding: 10px;
height: 600px;
margin: 100px auto;
box-shadow: 0 0 10px #6cb4ffcf;
overflow: hidden;
.chat-title {
text-align: center;
font-size: 18px;
font-weight: bold;
margin-bottom: 10px;
height: 30px;
}
.chat-content {
overflow-y: auto;
border: 1px solid #ccc;
padding: 10px;
margin-bottom: 10px;
width: 100%;
height: 436px;
.chat-message {
position: relative;
}
.chat-picture {
width: 35px;
height: 35px;
background: #d44512;
color: #fff;
overflow: hidden;
border-radius: 25px;
font-size: 20px;
line-height: 35px;
text-align: center;
position: absolute;
top: 12px;
left: -6px;
&.chat-picture-user {
background: #0079ff;
}
}
}
.chat-input,
.chat-btn {
height: 94px;
}
.chat-input {
}
.chat-btn {
text-align: center;
button {
width: 100%;
height: 100%;
}
}
}
</style>
小助手接口调用
新建apis/chatAPIStream.ts
来实现与deepseek-r1
接口交互
typescript
export const callDeepSeekAPI = async (messages: any[]): Promise<ReadableStream> => {
const data = {
model: 'deepseek-r1:32b',
messages,
stream: true, // 启用流式响应
};
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.body as ReadableStream; // 返回流式数据
};
小助手配置文件处理
在vite的配置文件vite.config.ts
中增加deepSeek API
代理
typescript
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vite.dev/config/
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'@': '/src', // 确保这里的路径是正确的
},
},
server: {
port: 8080,
proxy: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
},
'/deepseek-server': {
target: 'http://localhost:11434',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/deepseek-server/, ''),
},
},
},
})
至此,成功完成了智能小助手的本地化搭建,如有什么问题欢迎留言讨论~