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

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

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


系统介绍

  1. mbp pro


一、Ollama 安装与配置


1.1 跨平台安装指南

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

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

#Linux安装(Ubuntu/Debian为例)
curl-fsSLhttps://ollama.com/install.sh|sudobash
sudousermod-aGollama$USER#添加用户权限
sudosystemctlstartollama#启动服务

1.2 服务验证

ollama-v

#输出ollamaversionis0.5.7

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

二、Deepseek 模型部署

2.1 模型下载与加载

以 deepseek r1 模型为例:

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

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

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

ollamarundeepseek-r1
#执行后
pullingmanifest
pulling96c415656d37...100%▕██████████████▏4.7GB
pulling369ca498f347...100%▕██████████████▏387B
pulling6e4c38e1172f...100%▕██████████████▏1.1KB
pullingf4d24e9138dd...100%▕██████████████▏148B
pulling40fb844194b2...100%▕██████████████▏487B
verifyingsha256digest
writingmanifest
success
>>>Sendamessage(/?forhelp)
>>>`
>>>当看到上述提示,即可开始模型对话。
  • mac 后台标识

  • win 后台标识

    见任务栏托盘区

2.2 模型验证测试

运行交互式对话测试:

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

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

硬件要求建议:

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

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

三、安装交互 ui

3.1 chatbox

  1. 下载地址chatboxai.app

  2. 配置本地模型

  • 进入设置页面

  • 选择 ollama api (本地部署)

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


至此即可开启问答模式

3.2 Page Assist 浏览器插件

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

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

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

四、Dify 知识库搭建

参考文档地址Docker Compose 部署

4.1 环境准备

1.拉取源代码,准备环境

#macos
#克隆 Dify 源代码至本地环境。
gitclonehttps://github.com/langgenius/dify.git

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

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

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

dockercomposeup-d
#如果版本是 Docker Compose V1,使用以下命令:
docker-composeup-d

#正常返回
[+]Running74/9
✔dbPulled834.2s
✔sandboxPulled1120.7s
✔weaviatePulled526.5s
✔webPulled174.0s
✔redisPulled893.7s
✔apiPulled2919.8s
✔workerPulled2919.8s
✔ssrf_proxyPulled494.0s
✔nginxPulled184.7s
[+]Running11/11
✔Networkdocker_defaultCreated0.0s
✔Networkdocker_ssrf_proxy_networkCreated0.0s
✔Containerdocker-db-1Started1.1s
✔Containerdocker-web-1Started1.1s
✔Containerdocker-redis-1Started1.1s
✔Containerdocker-sandbox-1Started1.1s
✔Containerdocker-weaviate-1Started1.1s
✔Containerdocker-ssrf_proxy-1Started1.1s
✔Containerdocker-api-1Started0.7s
✔Containerdocker-worker-1Started0.7s
✔Containerdocker-nginx-1Started0.8s

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

  1. 修改配置后重启 docker

  2. 办公网环境下
dockercomposeup-d

[+]Running9/9
✘webErrorcontextcanceled14.9s
✘redisErrorcontextcanceled14.9s
✘dbErrorcontextcanceled14.9s
✘nginxErrorcontextcanceled14.9s
✘ssrf_proxyErrorcontextcanceled14.9s
✘sandboxErrorHead"https://registry-1.do...14.9s
✘apiErrorcontextcanceled14.9s
✘workerErrorcontextcanceled14.9s
✘weaviateErrorcontextcanceled14.9s
Errorresponsefromdaemon: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"
]
}

4.2 Dify 创建聊天

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

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

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

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

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

4.3 Dify 知识库创建

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

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

五、应用测试

5.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(`runworker${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(`runworker${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)==="[objectObject]";
}

//主函数
constmain=async()=>{
console.time("runmain");

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("runmain");
};

//执行主函数
main();

7b


runworkerwindow:1:16.909(m:ss.mmm)
翻译出错:window
runworkercontextMenu:1:19.915(m:ss.mmm)
翻译出错:contextMenu
runworkerautoUpdater:1:24.182(m:ss.mmm)
runworkermenu:1:54.272(m:ss.mmm)
runworkeropenWindowWarn:2:08.219(m:ss.mmm)
翻译出错penWindowWarn
runworkercontextMenu:54.257s
翻译出错:contextMenu
runworkercreatePreloadFileWarn:1:05.595(m:ss.mmm)
翻译出错:createPreloadFileWarn
runworkerwindow:1:13.320(m:ss.mmm)
翻译出错:window
runworkeropenWindowWarn:42.933s
runworkerrenderer:1:06.620(m:ss.mmm)
runworkercontextMenu:58.129s
runworkercreatePreloadFileWarn:51.205s
runworkerwindow:1:10.067(m:ss.mmm)
翻译出错:window
runworkerwindow:17.583s
翻译出错:window
runworkerwindow:16.479s
翻译出错:window
runworkerwindow:53.783s
翻译完成,结果是否存在遗漏项:{ success:false,path:'menu'}
翻译完成,结果已写入utput_deepseek-r1:7b.json
runmain:5:08.166(m:ss.mmm)
![img_1.png](img_1.png)

----------------
runworkeropenWindowWarn:27.835s
翻译出错penWindowWarn
runworkerwindow:47.317s
翻译出错:window
runworkercontextMenu:1:00.365(m:ss.mmm)
翻译出错:contextMenu
runworkeropenWindowWarn:42.320s
runworkerwindow:1:00.580(m:ss.mmm)
翻译出错:window
runworkermenu:2:01.575(m:ss.mmm)
翻译出错:menu
runworkercontextMenu:1:05.158(m:ss.mmm)
runworkerautoUpdater:2:08.553(m:ss.mmm)
runworkercreatePreloadFileWarn:1:41.123(m:ss.mmm)
runworkerwindow:1:28.518(m:ss.mmm)
翻译出错:window
runworkerrenderer:1:46.725(m:ss.mmm)
runworkermenu:1:54.031(m:ss.mmm)
翻译出错:menu
runworkerwindow:57.867s
runworkermenu:1:16.267(m:ss.mmm)
翻译完成,结果是否存在遗漏项:{ success:false,path:'menu'}
翻译完成,结果已写入utput_deepseek-r1:7b.json
runmain:5:11.880(m:ss.mmm)
![img_2.png](img_2.png)

翻译结果

"window":{
"willUnload":{
"title":"Whatshouldyouconfirmbeforerefreshingthecurrentpage?",
"message":"thesystemmightnotsaveyourchanges",
"unload_bt":"Reload",
"cancel_bt":"Cancel"
}
},

14b

runworkerwindow:2:15.983(m:ss.mmm)
runworkercontextMenu:2:17.554(m:ss.mmm)
runworkerautoUpdater:3:02.960(m:ss.mmm)
runworkermenu:4:06.753(m:ss.mmm)
runworkeropenWindowWarn:4:14.074(m:ss.mmm)
runworkercreatePreloadFileWarn:2:04.443(m:ss.mmm)
runworkerrenderer:2:21.099(m:ss.mmm)
翻译完成,结果是否存在遗漏项:{ success:true}
翻译完成,结果已写入utput_deepseek-r1:14b.json
runmain:4:38.673(m:ss.mmm)


------------------------

runworkerautoUpdater:1:34.068(m:ss.mmm)
runworkeropenWindowWarn:1:57.715(m:ss.mmm)
runworkerwindow:2:09.907(m:ss.mmm)
runworkercontextMenu:2:14.214(m:ss.mmm)
runworkerrenderer:1:38.631(m:ss.mmm)
runworkercreatePreloadFileWarn:2:24.484(m:ss.mmm)
runworkermenu:4:16.409(m:ss.mmm)
翻译出错:menu
runworkermenu:2:00.482(m:ss.mmm)
翻译完成,结果是否存在遗漏项:{ success:true}
翻译完成,结果已写入utput_deepseek-r1:14b.json
runmain:6:16.900(m:ss.mmm)

翻译结果


"window":{
"willUnload":{
"title":"Confirmtorefreshthecurrentpage?",
"message":"Thesystemmaynotsaveyourchanges.",
"unload_bt":"Reload",
"cancel_bt":"Cancel"
}
},

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

结论

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

回复

使用道具 举报

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

本版积分规则

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

  • 微信公众号

  • 商务合作

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