☰
本地优先云端兜底:Ollama+DeepSeek+Dify构建私有AI平台全攻略
2026/10/4 20:30:10 网站建设 项目流程

上个月整理云账单的时候,看见 AI API 那几行数字确实肉疼。内部工具、知识库问答、日志总结,每个任务单看都不大,但一天几千次请求,一个月下来就是上千元的开销。更麻烦的是,有些内部数据根本不适合往外送。于是我把整套架构翻了过来,搭建了一个「本地优先、云端兜底」的私有 AI 平台:Ollama 在本地跑 DeepSeek 开源模型,Dify 负责应用编排、知识库和工作流,云端 DeepSeek API 只作为本地模型搞不定时的备用通道。这篇文章就是完整的搭建复盘,从架构方案、环境部署、模型接入,到知识库流水线和各类报错排查,一步步都写清楚。

这套方案适合谁?如果你有一张普通显卡,想在公司内网搭一个私有知识库,或者正在被大模型 API 账单拖着走,这篇文章会正好切中你的需求。哪怕你的机器没有独立显卡,也可以用 CPU 跑小尺寸模型,只是速度会慢一些。

1. 别再给API打工:整套方案的选型与架构拆解

1.1 先算账:API 账单是怎么一步步吃钱的

很多时候我们觉得单个 API 调用不贵,但用量一旦起来,账单就会变得很真实。我按一个比较常见的内部场景算一笔账:

  • 团队内部工具有 3000 次调用/天;
  • 平均每次请求:输入 4000 token,输出 800 token;
  • 每天消耗:输入 1200 万 token,输出 240 万 token;
  • 按一个中等定价估算:输入 2 元/百万 token,输出 3 元/百万 token;
  • 单日成本:2 × 1200 / 100 + 3 × 240 / 100 = 24 元 + 7.2 元 = 31.2 元;
  • 一个月 30 天就是 936 元。

这还只算了对话模型的费用,如果再加上 embedding、文档解析、图片识别或者失败重试,一个月破千是很容易的事情。更关键的是,API 账单是纯消费型的,用一次算一次,模型涨价、限流、接口波动都会直接影响业务。这些钱累积起来,足够买一块不错的显卡了。

费用之外还有两个刚性问题:一是数据隐私,很多内部数据根本不适合经过外部 API;二是服务依赖,外部服务的限流、断连、模型版本下线,都不是我们能控制的。所以我想要的是一个数据不出内网,也能跑得很顺的 AI 底座。

1.2 本地优先、云端兜底的架构模式

这套平台的核心路由逻辑很直接:所有用户请求先进 Dify,默认交给本地 Ollama 里的 DeepSeek 模型;只有本地模型接不住的时候,才切换到云端 API。

什么算“接不住”?我梳理了三种情况:

  • 本地显存不足导致模型加载失败或直接崩溃;
  • 请求对推理能力要求特别高,比如复杂代码设计、长篇深度分析;
  • 用户主动在界面里选择“更强模型”通道。

请求链路就是两条:

  • 请求 -> Dify -> Ollama 本地模型;
  • 请求 -> Dify -> DeepSeek API 云端模型。

这种架构的好处是用户感知不到后端切换,体验仍然是同一个界面、同一套工作流。数据、日志、对话记录都留在本地,只有真正需要云端的少量请求才出内网。成本也从“按 token 付费”变成了“按硬件折旧付费”,只要机器开着,边际成本几乎为零。

1.3 为什么偏偏是 Dify、Ollama、DeepSeek 这三个

先解释一下为什么不能靠纯代码解决。我早期用过 LangChain 自己搭,工作流、知识库、会话管理、权限全部要手写,维护成本很高。Dify 是开源 LLM 应用平台,自带可视化工作流、知识库管理、Agent 编排、模型供应商管理、日志监控,还提供后端 API 方便二次开发,很适合做私有平台的底座。

Ollama 的价值在于把本地模型部署变成一件“简单到离谱”的事。一条命令就能拉模型、起服务,原生支持 REST API,DeepSeek 等模型的量化版本也能直接拉。Dify 官方集成里就有 Ollama 供应商,填个地址就能用,不需要自己写推理服务。

为什么选 DeepSeek?一是在中文任务上表现扎实,二是 Ollama 里可以直接拉 DeepSeek 的蒸馏版本,比如 deepseek-r1:7b、deepseek-r1:14b,分别对应不同显存档位。三是它的云端 API 也是使用同一个团队的模型,本地模型和云端模型的指令习惯、输出风格接近,切换兜底时用户几乎无感。这三个工具各管一层:Ollama 管本地算力,Dify 管应用编排,DeepSeek 管模型能力,组合起来就是一套完整的私有 AI 平台。

2. 从零部署:硬件评估、Ollama 离线安装与 Dify 上手指南

2.1 硬件评估与模型选择:先别急着装

部署之前先看硬件。Ollama 的模型文件会加载到显存里,显存大小直接决定了你能跑多大的模型。我按自己的经验和社区常见配置整理了一张表:

显卡显存推荐模型量化大小实际体验
8GBdeepseek-r1:7b约 4.7GB流畅,适合日常问答和文档总结
16GBdeepseek-r1:14b约 9GB良好,复杂问题的推理能力明显提升
24GBdeepseek-r1:32b约 19GB较好,可以承担较长上下文的深度分析
32GB+更大参数模型视显存而定可挑战更强推理,但性价比需要考虑

如果只有 CPU 没有独立显卡,也能跑,但 7B 模型 Q4 量化在 CPU 上大概只有 5-10 token/s,属于“能跑但急人”的状态。有显卡的话,同样模型可以到 40-80 token/s,体验完全不一样。内存方面,建议至少 16GB,因为 Dify 本身要跑 MySQL、Redis、Sandbox 等组件,模型也要占用一定内存。

有些朋友上来就想直接部署 70B 大模型,如果显存不够,硬上量化版本会导致速度极慢甚至加载不进显存。我的建议是先跑 7B,把系统全链路打通,再根据实际效果决定要不要升级更大的模型。

2.2 Ollama 安装与下载慢问题:离线安装方案

Ollama 的安装本身不复杂,但很多人卡在第一步:下载太慢或者安装包拉不下来。这里我分享一套“离线安装”的思路,尤其适合内网环境或者下载不稳定的情况。

第一步,在一台网络条件好的机器上下载 Ollama 安装包。无论是 Windows 安装包还是 Linux 的 tgz 离线包,都先下好,然后通过 U 盘或者局域网传到目标机器。

第二步,Linux 目标机的离线安装,命令如下:

tar -C /usr/local -xzf ollama-linux-amd64.tgz

解压之后直接启动服务:

ollama serve

为了开机自启和崩溃后自动拉起,建议配置 systemd 服务。创建一个服务文件,内容大致是把 ollama 的二进制路径、启动用户和启动参数写好,然后 enable 这个服务。Windows 用户直接装原版安装包,简单很多。

第三步,修改模型存储路径。默认模型会放在当前用户目录下,如果系统盘空间有限,建议改到数据盘。Linux 可以先设置环境变量再启动服务:

export OLLAMA_MODELS=/data/ollama

Windows 则在系统环境变量里新增 OLLAMA_MODELS,指向一个足够大的目录。这个步骤一定要在拉取模型之前完成,否则模型已经下到默认目录了,迁移起来还要多一步。

第四步,拉取模型。比如:

ollama pull deepseek-r1:7b

如果网络实在不给力,另辟蹊径的方案是:在另一台已经拉好模型的机器上,把整个 OLLAMA_MODELS 目录复制过来。模型目录里包含 manifests 和 blobs,整体拷贝后重新执行 ollama list 就能看到模型。这里要注意 Linux 下的权限,如果 Ollama 以 ollama 用户运行,复制完模型目录后要执行 chown 把归属改对。

最后验证一下:

ollama list curl http://localhost:11434/api/tags

第二条命令如果返回一串 JSON,说明服务已经启动了。

2.3 安装 Dify:Docker Compose 部署与初始化

Dify 推荐用 Docker Compose 部署,因为组件多:nginx、api、worker、web、db、redis、sandbox、ssrf_proxy 等。如果你没有 Docker,先装好 Docker 和 Docker Compose。

有网络的机器上,先把 Dify 仓库拉下来:

git clone https://github.com/langgenius/dify.git

离线部署时,把这个仓库打包带到内网机器上解压。进入 dify/docker 目录,复制环境变量文件:

cd dify/docker cp .env.example .env

默认情况下,Dify 会监听宿主机的 80 端口。如果端口被占用,编辑 .env 把映射端口改掉,比如改成 8080:80。然后:

docker compose up -d

这里会遇到一个实际问题:镜像下载特别慢。如果网络条件不好,我建议在有网络环境机器上先把容器镜像拉好,然后用 docker save 导出、docker load 导入目标机器。或者用 docker compose pull 先拉镜像,再一台台导入到内网。这样比在目标机器上干等要快很多。

初始化完成后,浏览器访问 http://目标机IP:8080/install,按提示设置管理员账号。如果看到的是空白页或 502,大概率是服务还没完全启动,等一两分钟再刷新,或者 docker compose ps 看各容器状态。

升级和迁移也要提醒一句。Dify 的数据都在 Docker volume 里,包括 postgres 数据、redis 数据、上传文件等。升级前先用 docker compose down 停掉服务,再备份 volume。迁移时把volume内容整体复制到新机器,保持 .env 配置一致。版本跨度大时要谨慎,建议先备份,再按官方升级路径逐步走。

2.4 网络与安全基础:Dify 和 Ollama 要不要对外暴露

我强烈建议这套平台不要直接暴露到公网。内网使用就好,如果团队成员需要远程访问,走公司已有的内部网络接入方式,不要在公网上裸奔。Dify 管理后台默认没有特别复杂的访问控制,暴露到公网会很容易被扫到。

Ollama 默认只监听 127.0.0.1,也就是说只有本机能访问。Dify 如果跑在 Docker 里访问宿主机上的 Ollama,需要让 Ollama 监听所有网卡:

export OLLAMA_HOST=0.0.0.0:11434

但这样一来,局域网内其他机器也都能访问到 Ollama 的服务。如果只想让 Dify 访问,防火墙里就不要对 11434 端口开放公网访问,只允许内网网段访问即可。

3. 模型接入与兜底路由:把 Ollama 和 DeepSeek API 接进 Dify

3.1 在 Dify 里添加 Ollama 模型供应商:本地模型接入

Dify 安装好之后,进入管理后台,在“设置 -> 模型供应商”里找到 Ollama,点击添加模型。这一步有几个参数必须填对。

第一个是模型名称,必须是 ollama list 里显示的完整名称带 tag,比如 deepseek-r1:7b。手填容易错,我建议从命令行输出里直接复制。

第二个是 Base URL。这里最容易踩坑。Dify 如果是 Docker 方式部署,容器里的 localhost 指的不是宿主机,而是容器本身,所以填 localhost:11434 是连不通的,必须填宿主机 IP。Linux 上我一般填内网地址,比如 https://192.168.1.100:11434,但注意协议:Ollama 默认是 http 不是 https。如果你填了 https://,Dify 会认为你在走 SSL 加密通道,而实际服务是明文 http,测试时就会报 SSL 错误。

第三个是模型类型,选 LLM。如果你要用本地 embedding,还需要额外添加一个类型为 Embedding 的模型,比如 nomic-embed-text 或 bge-m3。

第四个是上下文长度参数。不同模型支持的上下文不一样,如果你在 Dify 里填了一个很大的值(比如 131072),而实际模型只有 8k 上下文,后续请求一旦超出模型限制就会报错。我的建议是先查模型的实际上下文能力,7B 模型填 8192 到 16384 更稳妥,32B 模型可以填大一些。

添加完成后点“测试”。如果报错“an error occurred during credentials validation”,不要慌,常见原因我放在第 5 节排查表里详细写。

3.2 接入云端 DeepSeek API 作为兜底模型

本地模型再强,也有接不住的高难度请求。这时候云端 API 兜底就发挥作用了。我选择的是 DeepSeek 的开放平台 API,因为和本地使用的 DeepSeek 系列模型风格一致,切换时用户几乎感知不到差异。

操作上,先去 DeepSeek 开放平台申请一个 API Key,然后把 Key 填进 Dify。在“设置 -> 模型供应商”里选择 DeepSeek 供应商,填上 Key。如果你发现 Dify 里没有单独的 DeepSeek 供应商,也可以用“OpenAI-API-compatible”的方式来接:

  • Base URL:https://api.deepseek.com/v1;
  • API Key:DeepSeek 的 Key;
  • 模型名:deepseek-chat 或 deepseek-reasoner。

这里有个典型的 Dify 日志报错,关键词是 “llm-deepseek: no api key for provider route "deepseek-official"; store deeps...”。出现这个报错的意思是,应用选择的模型供应商是“DeepSeek 官方”,但你并没有在 Dify 的 DeepSeek 供应商里保存 Key,可能只是在 OpenAI 兼容供应商里填了 Key。解决方案很简单:要么在对应供应商里把 Key 存好,要么在应用里改选你实际配置的那个供应商。这类“密钥路由”问题,多数时候是供应商选错或 Key 没存到位。

3.3 用工作流实现“本地优先、云端兜底”的路由策略

Dify 默认的普通聊天应用只能指定一个模型。要实现“先本地、失败再云端”的路由,需要在工作流里做条件分支。我分享一套可以直接照抄的方案。

第一步,新建一个工作流类型应用。

第二步,添加“开始”节点,定义一个输入变量 sys.query,也就是用户问题。

第三步,添加第一个 LLM 节点。模型选择 Ollama 供应商里的本地模型,比如 deepseek-r1:7b。Prompt 里引用 {{sys.query}}。

第四步,添加“条件分支”节点。判断第一个 LLM 节点的输出是否为空,或者是否包含“error”“timeout”等错误关键词。本地模型 OOM、超时、连接失败时,输出通常会为空或异常,这时就走分支的“失败”侧。

第五步,在失败分支里再放一个 LLM 节点,模型选择云端 DeepSeek API,生成最终答案。

第六步,添加“结束”节点,把第一分支的本地结果或第二分支的云端结果统一输出。

这样用户提交同一段需求时,默认由本地模型处理,本地崩了自动切到云端。我实测下来,切换过程会有 1-3 秒额外等待,但比整个服务不可用要强太多。

在实际使用中,我还经常用到“变量聚合器”。工作流里经常要拼接多段内容,比如知识库检索结果、系统提示、上下文历史等,如果直接在提示词里拼,节点一多就会很难维护。用变量聚合器把多个文本合并成一个上下文变量,再在 LLM 节点里引用,整个流程会清爽很多。

上下文超长的问题也在这里解决。比如请求拼接后超过了模型的上下文上限,可以在变量聚合器之后做一层截断,或者把知识库召回的 Top K 从 5 降为 3,降低进入提示词的文本量。

3.4 二次开发接口:把工作流暴露给业务系统

Dify 应用发布之后,在“访问 API”页面可以看到 API 调用地址和密钥。业务系统可以用 HTTP 请求直接调用这个应用,参考如下调用方式:

curl -X POST 'http://你的Dify地址/v1/chat-messages' \ -H 'Authorization: Bearer 你的API密钥' \ -H 'Content-Type: application/json' \ -d '{ "inputs": {}, "query": "6月生产故障的根因是什么?", "response_mode": "blocking", "conversation_id": "", "user": "internal-user" }'

这种方式很适合接进 OA、客服、运维工单等系统。如果后续需要更深度定制,比如改模型路由逻辑、增加自定义工具,Dify 也有插件机制和后端接口,不需要自己重写一套。

4. 知识库流水线实战:让私有 AI 真正回答自家问题

4.1 文档入库与切片参数设置:为什么不能整篇塞进去

私有 AI 平台要做知识库问答,第一步是把文档灌进去。Dify 的知识库支持上传 PDF、Markdown、网页链接等。上传之后会做文本提取和清洗,然后进行分块。

分块看起来简单,其实直接决定答案质量。我常用的参数是:

  • 块大小:500-800 token;
  • 块重叠:50-100 token;
  • 检索 Top K:3-5;
  • 召回 Score 阈值:0.4-0.6。

为什么要分块而不是把整篇文档作为一条记录?因为大模型的输入有上下文上限,而且知识库检索靠的是语义相似度,分块越合理,越容易在小范围内找到与问题对应的内容。想象一个 PDF 有 100 页,如果整本作为一个 chunk,检索时系统很难定位到具体答案所在;如果按 500 token 切块,用户问“某模块的异常处理逻辑”,系统就能在相近的语义块里找到相关内容。

重叠的作用是防止信息刚好被切刀劈开。比如一个概念从第 700 token 开始,到第 800 token 结束,如果块与块之间没有重叠,这个概念可能在两个块里都被截断。加上重叠之后,召回时命中率更高。

隐私方面,知识库的 embedding 模型我也用本地的。在 Dify 的 Ollama 供应商里添加一个 Embedding 模型,比如 nomic-embed-text,然后在知识库的检索设置里选择这个本地 embedding 模型,文档内容就不会因为 embedding 计算而传给外部 API。这一点对于“私有”二字非常关键。

4.2 检索增强工作流(RAG Pipeline)完整配置

有了知识库之后,我把问答应用升级为一个标准的 RAG 工作流。场景还是内部知识库:用户问“上周生产故障复盘文档里提到的根因是什么”,系统要先检索文档,再把相关段落交给 LLM 生成答案。

具体配置步骤如下。

第一步,在工作流里添加“知识检索”节点。

第二步,选择刚才创建的数据集,设置检索参数。Top K 我一般设成 3,Score 阈值设成 0.5。阈值太低会混入不相关的内容,太高可能导致检索不到结果,0.5 是一个比较均衡的起点。

第三步,把知识检索结果通过“变量聚合器”拼接成一个上下文变量,比如叫 context。这一步很重要,因为检索节点返回的往往是一个列表,不能直接塞进 LLM 提示词。

第四步,在 LLM 节点的 System Prompt 里写清楚约束。我习惯用这个模板:

你是一个企业内部助手。请仅依据以下材料回答问题。 如果材料中没有相关信息,请明确说“资料库中暂时没有找到相关信息”,不要编造。 材料: {{context}} 用户问题: {{sys.query}}

第五步,连接到“结束”节点,把答案输出。

这套流水线跑起来之后,我再也不担心模型“一本正经地胡说八道”,因为提示词里限定了必须基于材料回答。加上 Dify 的引用来源功能,用户能看到答案来自哪一段文档,可信度提升非常明显。

4.3 从“问答机器人”升级到“私有 Agent”

知识库问答只是第一步。Dify 里还可以创建 Agent 应用,给 Agent 挂上工具,比如内网 SQL 查询、运维脚本、内部 API 等。模型本身不直接执行工具,而是先看懂用户意图,决定调用哪个工具,再把工具结果整合成最终答案。

这个阶段,本地模型的工具调用能力很关键。7B 模型也能做简单的工具选择,但复杂场景偶尔会选错工具或参数。我的策略是:普通问题走本地模型,涉及的步骤多、逻辑链长的请求走云端兜底,或者干脆在 Agent 节点里配置两个模型并让工作流具备失败切换能力。

给 Agent 配置工具时必须限制权限。私有平台里的工具往往能触碰敏感数据,我一般给每个工具加白名单参数,并且在 Dify 日志里持续观察调用记录,防止 Agent 被诱导执行不合理的操作。

5. 避坑实录:连接失败、上下文超长、下载慢与显存不足

5.1 Dify 报错“an error occurred during credentials validation”怎么查

这个报错几乎每个人都会遇到,特别是第一次连 Ollama 的时候。我把它排查过程整理成一张表:

现象原因解决方法
测试模型时提示 SSL 错误Base URL 填了 https 而 Ollama 是 http改成 http://
Dify 容器里连不上宿主机填了 localhostLinux 填宿主机局域网 IP,macOS/Windows 填 host.docker.internal
Ollama 只监听 127.0.0.1,外部访问被拒OLLAMA_HOST 没设置监听设置 OLLAMA_HOST=0.0.0.0:11434 并重启服务
提示模型名不存在模型名手填错误从 ollama list 输出中复制完整名称
提示连接被重置Ollama 服务没启动检查进程状态,确认服务处于监听状态

排查顺序建议:先确认 Ollama 服务在机器上能访问,再确认 Dify 容器到宿主机网络通不通,最后检查协议和模型名。Denied 之前花最多时间在“协议”上,填成 https 是低级的坑。

5.2 “no api key for provider route”密钥配置问题

报错日志里会看到类似 “llm-deepseek: no api key for provider route "deepseek-official"" 的信息。这表示 Dify 在调用 DeepSeek 模型时,没找到对应供应商的 API Key。

我之前一度以为只要在“OpenAI 兼容”里填过 Key 就没问题,结果 Dify 的应用层认的是具体的模型供应商。最后是把 DeepSeek 官方供应商单独配置好,在模型选择时也明确选择了那个供应商,才彻底解决。总结经验就是:Dify 的密钥是按“供应商”隔离的,不是填一次到处都能用。

5.3 上下文超长报错:400 maximum context length

工作流跑起来之后,最常见的高频报错是:

API error: 400 This model's maximum context length is 1048576 tokens. However...

这里报的 1048576 是模型支持的上下文窗口上限。实际发生的原因是,用户问题、历史消息、知识库检索内容、系统提示词合起来超过了模型当前实例能处理的范围,或者是 Dify 侧配置的上下文窗口和真实模型能力不一致。

我的处理思路是四步走:

  1. 在 Dify 模型配置里把“上下文大小”写成模型实际支持的值,不要盲目填最大值;
  2. 把单次请求的 max_tokens 调低一些,比如 1024,减少输出侧的压力;
  3. 知识库分块大小调成 500 token 左右,减少单次召回的拼接量;
  4. 减少历史对话轮数,或者做消息压缩,只保留最近几轮。

本地小模型的显存和上下文都有限,没有必要硬撑大窗口。把「上下文大小」填成 8192 或 16384,跑起来反而更稳定。

5.4 Ollama 下载慢、卡在 pulling 的离线方案

Ollama 拉大模型卡住是很多人都遇到过的情形。我的应对方式是:

  • 在下载环境好的机器上先拉好模型,把 OLLAMA_MODELS 目录整体拷贝到目标机器;
  • 如果中途中断,先用 ollama rm 把失败的标签清掉,再重新尝试拉取;
  • 下载前确认磁盘剩余空间足够,模型文件展开之后比下载体积还要大不少;
  • Linux 下复制模型目录后要修正属主,否则 Ollama 读不到。

大模型动辄几个 GB,反复重试非常浪费时间。离线拷贝虽然看起来原始,实际是内网环境里最高效的办法。

5.5 显存不够、OOM 和并发问题

本地模型跑着跑着突然退出,查看 Ollama 日志发现 CUDA out of memory,这是显存不足的典型表现。我分享几个有效的缓解手段:

  • 同时只加载一个模型,设置环境变量 OLLAMA_MAX_LOADED_MODELS=1;
  • 降低请求并发,Dify 模型节点不要设置过高的并发数;
  • 调整模型上下文窗口,把 num_ctx 降低,显存占用也会跟着降;
  • 换上更小的量化版本或者更小参数的模型,比如从 14B 降到 7B;
  • 如果同时配置了 embedding 模型和 chat 模型,Ollama 可能为了做知识库检索而同时加载两个模型,显存会突然飙升。这种情况建议把 embedding 模型换成极小尺寸,或者错峰使用。

顺便说一句,如果后续要对接编码助手场景,比如在 IDE 里把 codex 或 Continue 这类工具接进来,也可以把接口指向本地 Ollama 或 DeepSeek API。编码场景我会优先用 deepseek-coder 或 qwen2.5-coder 系列,8GB 显存环境下 6.7B 或 7B 模型已经能承担不少补全和解释类任务,但目标仍是“本地优先、云端兜底”的大原则。

我个人跑下来最满意的不是账单降了,而是终于敢把内部数据接进去了。最开始也担心 7B 模型会不会太笨,实际用下来,日常请求九成都能被本地模型接住,剩下的一成靠云端兜底,这个配比已经很够用。最后再分享一个细节:Dify 和 Ollama 最好分机或者给足内存,否则并发一上来,就算显存够,内存不够也会卡。希望这套「本地优先、云端兜底」的方案,能让你也少给 API 打工。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询