DeepSeek本地部署详细指南!从 Ollama 到个人知识库应用

通过对DeepSeek本地部署详细讲解,希望每个人都能拥有专属 AI 助手,安全高效,开启智能化知识管理新体验。
首页 新闻资讯 行业资讯 DeepSeek本地部署详细指南!从 Ollama 到个人知识库应用

作者:lushen

一、系统介绍

mbp pro:

35a63a388c3fafc684b76669265bb095ec11cd.webp

二、Ollama 安装与配置

1. 跨平台安装指南

Ollama 作为本地运行大模型的利器,支持三大主流操作系统:

# macOS一键安装
# Windows用户
访问官网 https://ollama.com/download 下载安装包

# Linux安装(Ubuntu/Debian为例)
curl-fsSL https://ollama.com/install.sh|sudo bash
sudo usermod-aG ollama $USER# 添加用户权限
sudo systemctl start ollama    # 启动服务

2. 服务验证

ollama-v

# 输出ollama version is0.5.7

出现上述则表示安装成功,可浏览器访问http://localhost:11434/验证。

三、Deepseek 模型部署

1. 模型下载与加载

以 deepseek r1 模型为例:

(1) 访问https://ollama.com/library/deepseek-r1,默认为 7b 模型,如需其他模型,可以在当前页搜索所需模型

(2) 模型详情页复制安装命令ollama run deepseek-r1 

(3) 安装完成后在终端执行:

ollama run deepseek-r1
# 执行后
pulling manifest
pulling 96c415656d37...100%▕██████████████▏4.7GBpulling 369ca498f347...100%▕██████████████▏387Bpulling 6e4c38e1172f...100%▕██████████████▏1.1KBpulling f4d24e9138dd...100%▕██████████████▏148Bpulling 40fb844194b2...100%▕██████████████▏487Bverifying sha256 digest
writing manifest
success>>>Send amessage(/?forhelp)>>>`>>>当看到上述提示,即可开始模型对话。
  • mac 后台标识

28ac8c9452f1839c56a809bbc00290df2261c0.webp

  • win 后台标识见任务栏托盘区

2. 模型验证测试

运行交互式对话测试:

请用Python写一个快速排序算法

当看到完整代码输出,说明模型已成功加载。

硬件要求建议:

  • 最低配置:16GB 内存 + 8GB 显存

  • 推荐配置:32GB 内存 + 16GB 显存(RTX 3060 级别)

四、安装交互 ui

1. chatbox

(1) 下载地址chatboxai.app

(2) 配置本地模型

  • 进入设置页面

04773ff95307c9b18772892a24540a31636c19.webp

  • 选择 ollama api (本地部署)

  • 配置本机地址,默认http://127.0.0.1:11434

c10eb0f526f527898d13241b56d3cda825930c.webp


至此即可开启问答模式。

2. Page Assist 浏览器插件 

  • 安装地址Page Assist - 本地 AI 模型的 Web UI

  • 安装后简单配置即可开启问答模式,功能丰富,可以参考官方引导

  • 本插件支持本地知识库建设,因本次使用 Dify 建设,在此不赘述。

e27466831cfecc0804944852df8c63862319d3.webp

五、Dify 知识库搭建

参考文档地址Docker Compose 部署

1. 环境准备

(1) 拉取源代码,准备环境

# mac os
# 克隆 Dify 源代码至本地环境。
git clone https://github.com/langgenius/dify.git

# 进入 Dify 源代码的 Docker 目录
cd dify/docker

# 复制环境配置文件
cp.env.example.env

(2) 启动 Docker 容器(需要先安装 D ocker)

docker compose up-d
# 如果版本是 Docker ComposeV1,使用以下命令:
docker-compose up-d

# 正常返回[+]Running74/9✔ db Pulled834.2s
 ✔ sandbox Pulled1120.7s
 ✔ weaviate Pulled526.5s
 ✔ web Pulled174.0s
 ✔ redis Pulled893.7s
 ✔ api Pulled2919.8s
 ✔ worker Pulled2919.8s
 ✔ ssrf_proxy Pulled494.0s
 ✔ nginx Pulled184.7s[+]Running11/11✔ Network docker_default             Created0.0s
 ✔ Network docker_ssrf_proxy_network  Created0.0s
 ✔ Container docker-db-1Started1.1s
 ✔ Container docker-web-1Started1.1s
 ✔ Container docker-redis-1Started1.1s
 ✔ Container docker-sandbox-1Started1.1s
 ✔ Container docker-weaviate-1Started1.1s
 ✔ Container docker-ssrf_proxy-1Started1.1s
 ✔ Container docker-api-1Started0.7s
 ✔ Container docker-worker-1Started0.7s
 ✔ Container docker-nginx-1Started0.8s

在此阶段可能会遇到下列失败的情况,可以尝试切换源解决我当时的条件。

  • 修改配置后重启 docker

  • 办公网环境下

docker compose up-d[+]Running9/9✘ web Error        context canceled14.9s
 ✘ redis Error      context canceled14.9s
 ✘ db Error         context canceled14.9s
 ✘ nginx Error      context canceled14.9s
 ✘ ssrf_proxy Error context canceled14.9s
 ✘ sandbox Error    Head "https://registry-1.do...14.9s
 ✘ api Error        context canceled14.9s
 ✘ worker Error     context canceled14.9s
 ✘ weaviate Error   context canceled14.9s
Error response from daemon:Head"https://registry-1.docker.io/v2/langgenius/dify-sandbox/manifests/0.2.10":Get"https://auth.docker.io/token?scope=repository%3Alanggenius%2Fdify-sandbox%3Apull&service=registry.docker.io":EOF

解决方法:

  • 右上角齿轮图标进入设置 -> Docker engine,在配置中添加

  • 写入以下内容 ocker)

{// ..."registry-mirrors":["https://docker.hpcloud.cloud","https://docker.m.daocloud.io","https://docker.unsee.tech","https://docker.1panel.live","http://mirrors.ustc.edu.cn","https://docker.chenby.cn","http://mirror.azure.cn","https://dockerpull.org","https://dockerhub.icu","https://hub.rat.dev"]}

2. Dify 创建聊天

(1) 访问http://localhost/(默认 80 端口) 进入 dify

(2) 首次进入初始化设置账号密码

(3) 点击 Dify 平台右上角头像 → 设置 → 模型供应商,选择 Ollama,轻点“添加模型”。

947a74f94ceb532d6c33263f754c4d59999729.webp

f2902e739df8c02f7d03004a3dd15b542a10a3.webp

在配置 url 时,因为是 docker 服务,http://localhost:11434 存在无法访问的情况,可以尝试http://host.docker.internal:11434。

(4) 至此,可以开始创建应用,在主页选择 全部 -> 创建空白应用 -> 填入应用信息即可

b29fc240303e48762c28417181b3c004a1276c.webp

3. Dify 知识库创建

主页选择 知识库 -> 创建知识库 -> 上传知识 -> 等待处理完成

e43365065b1a3d0c95794895c9548b27807677.webp

进入聊天应用,选择刚才创建的知识库,即可开始带有私域知识的沟通。

83c473f979379005d4f2410092e70c6b62bd1e.webp

六、应用测试

1. 翻译场景

(1) 本地客户端具有部分国际化测试文件需要执行翻译,格式示例如下,多层嵌套的 json 格式,value 为string类型。需要利用大模型对整个 json 文件进行翻译,将中文翻译为英文后按原格式返回

// zh.json{"window":{"willUnload":{"title":"确认刷新当前页面吗?","message":"系统可能不会保存您做的更改","unload_bt":"重新加载","cancel_bt":"取消"}}}ocker)

(2) 实际应用测试,以deepseek-r1:7b/14b模型做测试。得到结果如下

(3) 执行脚本trans.js 

constfs=require("fs");constaxios=require("axios");// 1. 读取本地JSON文件constreadJsonFile=(filePath)=>{returnnewPromise((resolve,reject)=>{fs.readFile(filePath,"utf8",(err,data)=>{if(err){reject(err);}else{resolve(JSON.parse(data));}});});};constMODEL="deepseek-r1:14b";// 2. 调用本地大模型接口进行翻译consttranslateText=async(text,key)=>{letresponse;try{console.time(`run worker${key}`);response=awaitaxios.post("http://localhost:11434/api/generate",{// model: 'deepseek-r1:7b',model:MODEL,prompt:`有部分客户端国际化的配置文件,内容为json格式,需要翻译,要求按步骤进行翻译:
      1. 将中文翻译为英文
      2. 保持原有json格式不变,将value替换成翻译后的文本
      3. 你始终以合法的JSON格式响应,返回结果格式如: {"key1":"翻译后的文本1","key2":"翻译后的文本2"},直接返回结果,不需要符号包裹
      配置文件
      """${JSON.stringify(text)}"""`,stream:false,});console.timeEnd(`run worker${key}`);constsplitText="</think>";conststartIndex=response.data.response.indexOf(splitText);constresult=response.data.response.slice(startIndex+splitText.length).trim().replace(/<<+|>>+/g,"");// console.log('response.data.response:', response.data.response, JSON.parse(result), result)returnJSON.parse(result);// 假设接口返回的翻译结果在response.data.translatedText中}catch(error){console.error("翻译出错:",key);returntranslateText(text,key);// 如果翻译失败,返回原文}};// 3. 并行翻译逻辑(手动控制并发)consttranslateJson=async(jsonData,concurrency=5)=>{constentries=Object.entries(jsonData);consttranslatedData={};letcurrentIndex=0;// 当前处理的任务索引// 定义工作线程:每个线程不断处理下一个任务constworker=async()=>{while(currentIndex<entries.length){constindex=currentIndex++;if(index>=entries.length)break;// 所有任务已完成const[key,value]=entries[index];try{translatedData[key]=awaittranslateText(value,key);}catch(error){translatedData[key]=value;// 保留原文}}};// 启动指定数量的工作线程constworkers=Array(concurrency).fill(null).map(worker);awaitPromise.all(workers);// 等待所有线程完成constresult={};// 保持原有顺序entries.forEach(([key,value])=>{result[key]=translatedData[key]||value;});returnresult;};// 4. 将翻译后的内容生成新的文件constwriteTranslatedJson=(filePath,data)=>{returnnewPromise((resolve,reject)=>{fs.writeFile(filePath,JSON.stringify(data,null,2),"utf8",(err)=>{if(err){reject(err);}else{resolve();}});});};functioncompareObjectsWithPath(obj1,obj2,path=""){// 类型不同时直接返回路径if(typeofobj1!==typeofobj2){return{success:false,path:path||"root"};}// 处理可遍历对象(对象或数组)if(typeofobj1==="object"&&obj1!==null&&obj2!==null){constisArr1=Array.isArray(obj1);constisArr2=Array.isArray(obj2);// 数组类型不一致if(isArr1!==isArr2){return{success:false,path:path||"root"};}if(isArr1){// 数组长度不同if(obj1.length!==obj2.length){return{success:false,path:path||"root"};}// 递归检查数组元素for(leti=0;i<obj1.length;i++){constcurrentPath=`${path}[${i}]`;constresult=compareObjectsWithPath(obj1[i],obj2[i],currentPath);if(!result.success)returnresult;}return{success:true};}else{// 检查是否为纯对象(字面量对象)constisPlainObj1=isPlainObject(obj1);constisPlainObj2=isPlainObject(obj2);if(isPlainObj1!==isPlainObj2){return{success:false,path:path||"root"};}// 非纯对象(如 Date、RegExp)需检查是否均为字符串if(!isPlainObj1){returntypeofobj1==="string"&&typeofobj2==="string"?{success:true}:{success:false,path:path||"root"};}// 合并所有 key 并检查数量constkeys1=Object.keys(obj1);constkeys2=Object.keys(obj2);constallKeys=newSet([...keys1,...keys2]);if(allKeys.size!==keys1.length||allKeys.size!==keys2.length){return{success:false,path:path||"root"};}// 递归检查每个属性for(constkeyofallKeys){constcurrentPath=path?`${path}.${key}`:key;if(!keys1.includes(key)||!keys2.includes(key)){return{success:false,path:currentPath};}constresult=compareObjectsWithPath(obj1[key],obj2[key],currentPath);if(!result.success)returnresult;}return{success:true};}}else{// 基本类型:检查是否均为字符串returntypeofobj1==="string"&&typeofobj2==="string"?{success:true}:{success:false,path:path||"root"};}}// 判断是否为纯对象(字面量对象)functionisPlainObject(value){returnObject.prototype.toString.call(value)==="[object Object]";}// 主函数constmain=async()=>{console.time("run main");constinputFilePath="./locales/zh.json";// 输入的JSON文件路径constoutputFilePath=`output_${MODEL}.json`;// 输出的JSON文件路径try{// 读取JSON文件constjsonData=awaitreadJsonFile(inputFilePath);// 翻译JSON内容consttranslatedData=awaittranslateJson(jsonData);// 将翻译后的内容写入新文件awaitwriteTranslatedJson(outputFilePath,translatedData);console.log("翻译完成,结果是否存在遗漏项:",compareObjectsWithPath(jsonData,translatedData));console.log("翻译完成,结果已写入:",outputFilePath);}catch(error){console.error("处理过程中出错:",error);}console.timeEnd("run main");};// 执行主函数main();

7b:

run worker window:1:16.909(m:ss.mmm)翻译出错:window
run worker contextMenu:1:19.915(m:ss.mmm)翻译出错:contextMenu
run worker autoUpdater:1:24.182(m:ss.mmm)run worker menu:1:54.272(m:ss.mmm)run worker openWindowWarn:2:08.219(m:ss.mmm)翻译出错:openWindowWarn
run worker contextMenu:54.257s翻译出错:contextMenu
run worker createPreloadFileWarn:1:05.595(m:ss.mmm)翻译出错:createPreloadFileWarn
run worker window:1:13.320(m:ss.mmm)翻译出错:window
run worker openWindowWarn:42.933s
run worker renderer:1:06.620(m:ss.mmm)run worker contextMenu:58.129s
run worker createPreloadFileWarn:51.205s
run worker window:1:10.067(m:ss.mmm)翻译出错:window
run worker window:17.583s翻译出错:window
run worker window:16.479s翻译出错:window
run worker window:53.783s
翻译完成,结果是否存在遗漏项:{success:false,path:'menu'}翻译完成,结果已写入:output_deepseek-r1:7b.json
run main:5:08.166(m:ss.mmm)![img_1.png](img_1.png)----------------run worker openWindowWarn:27.835s翻译出错:openWindowWarn
run worker window:47.317s翻译出错:window
run worker contextMenu:1:00.365(m:ss.mmm)翻译出错:contextMenu
run worker openWindowWarn:42.320s
run worker window:1:00.580(m:ss.mmm)翻译出错:window
run worker menu:2:01.575(m:ss.mmm)翻译出错:menu
run worker contextMenu:1:05.158(m:ss.mmm)run worker autoUpdater:2:08.553(m:ss.mmm)run worker createPreloadFileWarn:1:41.123(m:ss.mmm)run worker window:1:28.518(m:ss.mmm)翻译出错:window
run worker renderer:1:46.725(m:ss.mmm)run worker menu:1:54.031(m:ss.mmm)翻译出错:menu
run worker window:57.867s
run worker menu:1:16.267(m:ss.mmm)翻译完成,结果是否存在遗漏项:{success:false,path:'menu'}翻译完成,结果已写入:output_deepseek-r1:7b.json
run main:5:11.880(m:ss.mmm)![img_2.png](img_2.png)

翻译结果:

"window":{"willUnload":{"title":"What should you confirm before refreshing the current page?","message":"the system might not save your changes","unload_bt":"Reload","cancel_bt":"Cancel"}},

14b:

run worker window:2:15.983(m:ss.mmm)run worker contextMenu:2:17.554(m:ss.mmm)run worker autoUpdater:3:02.960(m:ss.mmm)run worker menu:4:06.753(m:ss.mmm)run worker openWindowWarn:4:14.074(m:ss.mmm)run worker createPreloadFileWarn:2:04.443(m:ss.mmm)run worker renderer:2:21.099(m:ss.mmm)翻译完成,结果是否存在遗漏项:{success:true}翻译完成,结果已写入:output_deepseek-r1:14b.json
run main:4:38.673(m:ss.mmm)------------------------run worker autoUpdater:1:34.068(m:ss.mmm)run worker openWindowWarn:1:57.715(m:ss.mmm)run worker window:2:09.907(m:ss.mmm)run worker contextMenu:2:14.214(m:ss.mmm)run worker renderer:1:38.631(m:ss.mmm)run worker createPreloadFileWarn:2:24.484(m:ss.mmm)run worker menu:4:16.409(m:ss.mmm)翻译出错:menu
run worker menu:2:00.482(m:ss.mmm)翻译完成,结果是否存在遗漏项:{success:true}翻译完成,结果已写入:output_deepseek-r1:14b.json
run main:6:16.900(m:ss.mmm)

翻译结果:

"window":{"willUnload":{"title":"Confirm to refresh the current page?","message":"The system may not save your changes.","unload_bt":"Reload","cancel_bt":"Cancel"}},

(4) 整体体验下来,14b 模型在翻译工作上比 7b 模型更为准确,一次性翻译成功率高。7B 模型翻译结果噪声多,返回结果可序列化效果差。翻译结果远远不如 14b。

结论

14b 在 macos 执行效率能满足特定业务场景要求。

29    2025-02-11 12:15:57    DeepSeek Ollama AI