作者:lushen
mbp pro:
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 # 启动服务
ollama-v # 输出ollama version is0.5.7
出现上述则表示安装成功,可浏览器访问http://localhost:11434/验证。
以 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 后台标识
win 后台标识见任务栏托盘区
运行交互式对话测试:
请用Python写一个快速排序算法
当看到完整代码输出,说明模型已成功加载。
硬件要求建议:
最低配置:16GB 内存 + 8GB 显存
推荐配置:32GB 内存 + 16GB 显存(RTX 3060 级别)
(1) 下载地址chatboxai.app
(2) 配置本地模型
进入设置页面
选择 ollama api (本地部署)
配置本机地址,默认http://127.0.0.1:11434
至此即可开启问答模式。
安装地址Page Assist - 本地 AI 模型的 Web UI
安装后简单配置即可开启问答模式,功能丰富,可以参考官方引导
本插件支持本地知识库建设,因本次使用 Dify 建设,在此不赘述。
参考文档地址Docker Compose 部署
(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"]}
(1) 访问http://localhost/(默认 80 端口) 进入 dify
(2) 首次进入初始化设置账号密码
(3) 点击 Dify 平台右上角头像 → 设置 → 模型供应商,选择 Ollama,轻点“添加模型”。
在配置 url 时,因为是 docker 服务,http://localhost:11434 存在无法访问的情况,可以尝试http://host.docker.internal:11434。
(4) 至此,可以开始创建应用,在主页选择 全部 -> 创建空白应用 -> 填入应用信息即可
主页选择 知识库 -> 创建知识库 -> 上传知识 -> 等待处理完成
进入聊天应用,选择刚才创建的知识库,即可开始带有私域知识的沟通。
(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)----------------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)
翻译结果:
"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 执行效率能满足特定业务场景要求。