1. 为什么我决定不再给云端 API 打工
1.1 从一次账单惊魂说起
去年年底的一个深夜,我收到了一条扣费提醒,金额不算特别夸张,但足以让我从椅子上弹起来。原因很简单:我写了一个自动化脚本,本来只是每天定时跑几次文档摘要,结果因为一个循环逻辑写错了,它在后台疯狂调用云端大模型接口,一晚上烧掉了我小半个月的咖啡预算。那一刻我意识到一个问题——当你把核心能力完全托管在别人的 API 上时,你不仅在为每一次调用付费,还在为每一个 bug 付费。
这不是个例。我身边做独立开发、做企业内部工具、做知识库问答的朋友,几乎都经历过类似的“账单惊魂”。更别提那些让人抓狂的时刻:接口突然限流、密钥莫名失效、模型版本悄悄下线、上下文长度限制卡死工作流。你明明只是想安安静静做个应用,结果一半精力都花在处理 API 的脾气上。
所以从今年年初开始,我给自己定了一个目标:搭一套本地优先、云端兜底的私有 AI 平台。本地能跑的绝不外传,本地跑不动的再走云端,而且云端也要能随时切换、随时降级。折腾了小半年,最终落地的方案就是标题里说的这套组合——Dify + Ollama + DeepSeek,底层用 Docker 统一编排。
1.2 这套平台到底能做什么
先说清楚它能干什么,免得你看到一半发现不是自己想要的。
这套平台的核心能力有三块。第一块是本地大模型推理,通过 Ollama 在本地跑 DeepSeek 系列模型,日常的问答、摘要、改写、分类这些任务完全不出本机,数据隐私拉满,调用成本为零。第二块是可视化工作流编排,通过 Dify 把模型能力、知识库、工具调用串成可视化流程,不用写一堆胶水代码就能搭出问答机器人、文档处理流水线、内容生成器。第三块是云端兜底与混合路由,当本地模型搞不定复杂推理、或者需要更长上下文时,自动切到云端 DeepSeek API,保证体验不掉线。
适合谁来参考?我觉得三类人最合适。一是独立开发者和小团队,预算有限但需要稳定的 AI 能力;二是企业内部工具建设者,数据敏感、不能随便往外传;三是AI 应用折腾爱好者,喜欢自己掌控整套链路,不想被平台绑死。如果你只是想调个接口做个 demo,那这套方案可能有点重;但如果你打算长期做点东西,这套架构的性价比会随着时间越来越明显。
1.3 为什么是这三个组件
选型这件事我踩过不少坑,最后定下来 Dify + Ollama + DeepSeek,每一个都有明确的理由。
Ollama负责本地推理。它的优势是模型管理极其简单,一条命令就能拉取和运行模型,而且对硬件要求相对友好,消费级显卡甚至纯 CPU 都能跑起来小参数模型。相比自己用 transformers 搭推理服务,Ollama 省掉了我至少一周的工程时间。
Dify负责编排和界面。它是一个开源的大模型应用开发平台,自带工作流、知识库、Agent、API 发布等能力。我试过自己用 FastAPI 写一套,写到知识库检索那块就放弃了——向量化、分块、召回、重排,每一个环节都是坑。Dify 把这些都封装好了,我只需要关注业务逻辑。
DeepSeek负责兜底和增强。本地模型受限于硬件,参数量上不去,复杂推理和长上下文处理会吃力。DeepSeek 的 API 性价比在同类里很能打,而且模型能力足够强,作为云端兜底非常合适。关键是它兼容 OpenAI 的接口格式,接入 Dify 几乎零成本。
Docker是把这三个东西粘在一起的胶水。Dify 官方推荐 Docker Compose 部署,Ollama 也有官方镜像,DeepSeek 走 API 不需要部署。整套环境用 Docker 管理,迁移、备份、升级都方便很多。
2. 整体架构设计与选型背后的逻辑
2.1 三层架构:本地推理层、编排层、兜底层
我把整套平台拆成三层来看,这样思路会清晰很多。
最底层是本地推理层,核心是 Ollama。它跑在宿主机或者独立的容器里,对外暴露一个兼容 OpenAI 格式的接口。这一层负责所有“能本地做就本地做”的任务,比如日常问答、文本分类、简单摘要、关键词提取。模型我一般会准备两个:一个小的(比如 7B 级别)用于快速响应,一个稍大的(比如 14B 或 32B)用于质量要求高的场景。
中间层是编排层,核心是 Dify。Dify 通过配置的方式接入 Ollama 的接口,把本地模型当成一个“模型供应商”来用。同时 Dify 还管理知识库、工作流、应用发布。所有业务逻辑都在这一层,用户看到的应用界面、API 也都由这一层提供。
最上层是兜底层,核心是云端 DeepSeek API。Dify 支持配置多个模型供应商,我可以设置路由规则:简单任务走本地,复杂任务走云端;或者本地超时了自动降级到云端。这一层平时不怎么用,但关键时刻能救命。
三层之间通过标准接口通信,互不绑死。哪天我想换掉 Ollama 换成别的推理引擎,或者换掉 DeepSeek 换成别的云端模型,改动都很小。
2.2 为什么坚持“本地优先”
“本地优先”不是情怀,是实打实的考量。
第一是数据隐私。我处理的一些文档涉及客户信息、内部资料,这些东西发到云端我心里不踏实。本地推理意味着数据从头到尾不出本机,合规风险直接归零。
第二是成本可控。云端 API 按 token 计费,用得越多越贵,而且价格随时可能调整。本地推理的电费是固定的,跑一万次和跑一次的成本差不多。对于高频调用的场景,本地优先能省下大量费用。
第三是可用性。云端服务会限流、会维护、会涨价、会下线。本地模型虽然能力弱一点,但它永远在线,永远听你的。这种确定性对生产环境很重要。
当然,“本地优先”不等于“本地唯一”。硬扛所有任务是不现实的,所以才有“云端兜底”。
2.3 云端兜底的触发策略
兜底不是随便切的,得有策略,不然成本又会失控。我目前用的是三种触发方式。
按任务类型路由。在 Dify 的工作流里,我会根据任务复杂度打标签。比如“提取文档关键词”这种任务直接走本地,“分析合同条款风险”这种走云端。这个判断可以人工配置,也可以用一个小模型先做意图识别。
按本地能力上限路由。本地模型有上下文长度限制,比如 8K 或 32K。如果输入文本超过这个长度,直接切云端。Dify 的工作流里可以加条件判断节点来实现。
按失败降级路由。本地推理偶尔会超时或者报错,这时候自动重试一次,还失败就切云端。这个逻辑在 Dify 里可以用错误处理节点实现,也可以在应用层做。
提示:兜底策略一定要设上限。我见过有人配了自动降级但没设次数限制,结果本地一挂就疯狂调云端,账单又炸了。建议给云端调用加一个每日配额或者频率限制。
2.4 硬件与网络的前置考量
这套方案对硬件有一定要求,提前说清楚免得你白折腾。
本地跑模型,内存是硬门槛。7B 模型量化后大概需要 6-8GB 内存,14B 需要 12-16GB,32B 需要 24GB 以上。显卡不是必须的,但有了会快很多。我用一台 32GB 内存、带 12GB 显存的机器,跑 14B 量化模型比较流畅,32B 就有点吃力了。
网络方面,Docker 镜像拉取和模型下载是两大痛点。Dify 的镜像有好几个 GB,Ollama 的模型动辄几个 GB 到几十 GB。网络不好的话,下载能下到你怀疑人生。我的做法是提前把镜像和模型下载好,做好离线包,需要迁移的时候直接拷贝。
3. 环境搭建:从零到能跑通的完整步骤
3.1 Docker 环境准备与常见坑
Docker 是整套方案的地基,这一步没弄好后面全是问题。
Windows 用户直接装 Docker Desktop 就行,官网下载安装包,一路下一步。装完之后建议做两件事:一是把 Docker 的数据目录改到空间大的盘,默认在 C 盘很容易爆;二是在设置里给 Docker 分配足够的内存,建议至少 8GB,跑 Dify 加 Ollama 的话 16GB 更稳。
Linux 用户用包管理器装就行,装完记得把当前用户加到 docker 组,不然每次都要 sudo。命令是sudo usermod -aG docker $USER,执行完要重新登录才生效。
装完之后验证一下,跑docker --version和docker compose version,两个都有输出就说明没问题。
注意:Windows 上如果遇到 Docker Desktop 启动失败,八成是 WSL2 没配好。可以在 PowerShell 里跑
wsl --update更新一下,然后在 Docker Desktop 设置里确认用的是 WSL2 后端。
3.2 Ollama 部署与模型拉取
Ollama 的部署有两种方式,我推荐用 Docker 跑,方便统一管理。
用 Docker 跑 Ollama 的命令大概是这样:
docker run -d -v ollama:/root/.ollama -p 11434:11434 --name ollama ollama/ollama这条命令做了三件事:把模型数据挂载到名为 ollama 的卷上(这样容器删了模型还在),把 11434 端口暴露出来,给容器起名叫 ollama。
如果你有 NVIDIA 显卡,想让它用上 GPU,需要加--gpus all参数,并且宿主机要装好显卡驱动和 NVIDIA Container Toolkit。命令变成:
docker run -d --gpus all -v ollama:/root/.ollama -p 11434:11434 --name ollama ollama/ollama容器跑起来之后,进容器拉模型:
docker exec -it ollama ollama pull deepseek-r1:7b这里模型选择看你的硬件。7B 是入门款,14B 是甜点,32B 以上就需要好显卡了。DeepSeek 系列有 R1 和 V3 等不同版本,R1 偏推理,V3 偏通用,按需选择。
模型下载慢是高频问题。我的经验是:一是尽量在网络空闲时段下载,二是可以配置镜像加速,三是如果实在慢,找台网络好的机器下载好模型文件,然后拷贝到ollama卷对应的目录里。模型文件一般在/var/lib/docker/volumes/ollama/_data/models/下面。
3.3 Dify 部署与初始化配置
Dify 官方推荐用 Docker Compose 部署,步骤稍微多一点,但一次配好后面很省心。
先把 Dify 的代码拉下来:
git clone https://github.com/langgenius/dify.git cd dify/docker然后复制环境变量文件:
cp .env.example .env这个.env文件很关键,里面配置了数据库密码、端口、各种密钥。默认配置一般能跑,但有几个地方建议改一下:EXPOSE_NGINX_PORT是访问端口,默认 80,如果被占用了改成别的;数据库密码建议改强一点。
改完之后启动:
docker compose up -d第一次启动会拉一堆镜像,耐心等。启动完成后访问http://localhost(或者你改的端口),会看到 Dify 的初始化页面,设置管理员账号密码就进去了。
注意:如果启动后访问报错,先看容器状态
docker compose ps,再看日志docker compose logs -f。常见问题是端口冲突和内存不足。
3.4 把 Ollama 接入 Dify 的关键配置
Dify 和 Ollama 都在 Docker 里跑,这里有个网络问题要注意:容器之间不能直接用 localhost 通信。
如果 Ollama 和 Dify 在同一个 Docker 网络里,可以用容器名访问,地址是http://ollama:11434。如果不在同一个网络,要么把它们加到同一网络,要么用宿主机的 IP。
在 Dify 里配置 Ollama 的路径是:右上角头像 -> 设置 -> 模型供应商 -> 找到 Ollama -> 添加模型。Base URL 填http://ollama:11434(同网络)或者http://宿主机IP:11434(跨网络)。模型名称填你拉取的模型名,比如deepseek-r1:7b。
配置完点保存,Dify 会测试连接。如果报错,八成是网络不通或者地址填错了。可以在 Dify 的容器里curl一下 Ollama 的地址验证。
3.5 云端 DeepSeek 接入与密钥管理
云端接入就简单多了。在 Dify 的模型供应商里找到 DeepSeek(或者用 OpenAI 兼容的方式配置),填入 API Key 就行。
DeepSeek 的 API 兼容 OpenAI 格式,所以如果 Dify 里没有专门的 DeepSeek 供应商,可以选 OpenAI 兼容,Base URL 填 DeepSeek 的接口地址,模型名填deepseek-chat或deepseek-reasoner。
密钥管理是个容易被忽视的点。我见过有人把 API Key 直接写在代码里然后传到公开仓库,结果被人盗刷。正确做法是:密钥只存在 Dify 的配置里,不要写进任何代码;定期轮换密钥;给密钥设置额度上限。
提示:如果遇到
unexpected status 401 unauthorized: incorrect api key provided这类报错,先检查密钥有没有复制完整(前后有没有多余空格),再检查密钥有没有过期或者被禁用,最后检查账户余额是否充足。
4. 核心功能实操:工作流、知识库与混合路由
4.1 用 Dify 工作流搭一个文档问答机器人
工作流是 Dify 最核心的能力,我用它搭了一个文档问答机器人,流程大概是:用户提问 -> 检索知识库 -> 拼接上下文 -> 调用模型 -> 返回答案。
在 Dify 里新建一个“工作流”类型的应用,然后拖节点。开始节点接收用户输入,接一个知识库检索节点,再接一个 LLM 节点,最后接结束节点。知识库检索节点需要提前建好知识库,把文档传进去。
LLM 节点里可以选模型。这里就是混合路由的入口:我可以配一个条件判断,如果检索到的内容长度超过阈值,就用云端模型;否则用本地模型。Dify 的条件分支节点支持这种逻辑。
搭好之后可以点“运行”测试,输入一个问题看返回结果。调试通了就发布,Dify 会自动生成 API 接口,外部程序可以直接调用。
4.2 知识库构建:文档处理与向量化
知识库的质量直接决定问答效果,这块我踩坑最多。
首先是文档格式。Dify 支持 PDF、Word、Markdown、TXT 等常见格式。但 PDF 里的表格和图片处理起来很麻烦,经常提取出一堆乱码。我的经验是:能转成 Markdown 就转,转不了的话尽量用文本型 PDF,扫描件要先做 OCR。
然后是分块策略。Dify 默认按固定长度分块,但这对技术文档不友好,容易把一段完整逻辑切碎。我一般会调整分块大小和重叠长度,技术文档用 500-800 字符一块,重叠 100 字符左右。Dify 里可以配置这些参数。
最后是向量模型。Dify 默认用 OpenAI 的 embedding,但我们可以换成本地的。Ollama 支持 embedding 模型,比如nomic-embed-text。在 Dify 的模型供应商里配置好,然后在知识库设置里选它。
注意:如果遇到
dify unstructured api url is not configured for doc file processing这类报错,说明 Dify 的文档处理依赖一个叫 unstructured 的服务,需要在.env里配置对应的地址,或者改用内置的简单解析器。
4.3 混合路由的配置与调优
混合路由是这套方案的灵魂,配好了能省不少钱,配不好就是灾难。
我的配置思路是按任务分级。把任务分成三档:轻量任务(分类、提取、简单问答)走本地小模型;中等任务(摘要、改写、多轮对话)走本地大模型;重量任务(复杂推理、长文档分析、代码生成)走云端。
在 Dify 里实现这个,可以用工作流开头的“问题分类器”节点,先判断任务类型,再走不同分支。问题分类器本身也可以用一个本地小模型来做,成本几乎为零。
调优的关键是观察和迭代。我会记录每次请求走了哪条路、耗时多少、用户反馈如何。跑一段时间后就能看出哪些任务其实本地就能搞定,哪些必须走云端,然后调整路由规则。
4.4 上下文超长问题的处理
api error: 400 this model's maximum context length is 1048576 tokens这类报错,本质是输入超过了模型能处理的上限。本地模型的上限通常比云端小很多,所以这个问题在混合架构里更常见。
处理思路有几种。一是截断,把超长输入砍到上限以内,但会丢信息。二是摘要压缩,先用模型把长文本压缩成摘要,再喂给主模型。三是分块处理,把长文本切成多块分别处理,最后合并结果。四是直接切云端,如果云端模型支持更长上下文,就路由过去。
我在工作流里一般组合使用:先判断长度,短于本地上限的直接本地处理;超过本地但短于云端上限的,切云端;超过云端上限的,先分块摘要再处理。
5. 常见问题排查与避坑经验
5.1 部署阶段的高频报错速查
部署阶段的问题最集中,我整理了一个速查表。
| 报错信息 | 可能原因 | 解决思路 |
|---|---|---|
| Docker 启动失败 | WSL2 未配置或虚拟化未开启 | 更新 WSL2,BIOS 开启虚拟化 |
| 端口被占用 | 80 或其他端口已被其他程序使用 | 改.env里的端口配置 |
| 镜像拉取超时 | 网络问题 | 配置镜像加速或使用离线包 |
| Dify 页面打不开 | 容器未启动完成或崩溃 | 看docker compose logs |
| Ollama 连接失败 | 容器网络不通或地址错误 | 检查网络配置和 Base URL |
| 模型拉取失败 | 网络问题或模型名错误 | 确认模型名,检查网络 |
5.2 模型运行时的典型故障
模型跑起来之后也会出问题,最常见的是内存不足和推理超时。
内存不足的表现是容器被 OOM Killer 杀掉,日志里能看到Killed字样。解决办法是换更小的模型,或者给 Docker 分配更多内存,或者加 swap。
推理超时的表现是请求一直不返回,最后报 500 错误。这通常是模型太大、硬件太弱导致的。可以换小模型,或者调低推理参数(比如减少 max tokens),或者干脆切云端。
还有一个坑是模型加载慢。第一次调用某个模型时,Ollama 需要把模型加载到内存,可能要几十秒。这不是故障,是正常现象。可以在部署后先手动调用一次预热。
5.3 网络与镜像加速的实战技巧
网络问题是国内用户绕不开的坎,分享几个我实测有效的技巧。
Docker 镜像加速:在 Docker Desktop 的设置里配置 registry mirror,或者修改/etc/docker/daemon.json。具体用哪个镜像源这里不展开,网上能搜到不少可用的。
Ollama 模型下载:如果直连慢,可以试试配置代理环境变量,或者找离线模型包。Ollama 的模型文件格式是 GGUF,理论上可以手动下载 GGUF 文件然后导入,但操作略复杂。
Dify 镜像:Dify 的镜像比较多,建议一次性拉全,做好离线备份。迁移的时候直接拷贝镜像文件,比重新拉快得多。
5.4 数据备份与迁移的注意事项
这套平台跑起来之后会积累不少数据:Dify 的数据库、知识库文件、Ollama 的模型。这些都要定期备份。
Dify 的数据主要在几个 Docker 卷里,数据库、存储、向量库各一个。备份的时候把对应的卷打包就行。命令大概是docker run --rm -v dify_db_data:/data -v $(pwd):/backup alpine tar czf /backup/dify_db.tar.gz /data。
Ollama 的模型卷也要备份,不然重新下载很痛苦。
迁移的时候,把镜像、卷、.env文件一起拷到新机器,然后docker compose up -d就能恢复。注意新机器的 Docker 版本和配置要一致,不然可能出问题。
提示:迁移前先在测试环境验证一遍,别直接在生产环境操作。我吃过这个亏,迁移到一半发现新机器内存不够,服务起不来,又灰溜溜切回去。
6. 这套方案跑了一段时间后的真实体会
6.1 成本与体验的实际对比
跑了小半年,说几个真实数据。
成本方面,本地推理的电费基本可以忽略,主要成本是硬件折旧。云端 API 的费用相比之前纯云端方案下降了大概七成,因为大部分任务都被本地消化了。整体算下来,这套方案的边际成本极低,用得越多越划算。
体验方面,本地模型的响应速度比云端快,因为没有网络往返。但质量确实有差距,复杂任务还是得靠云端。混合路由的好处就是让合适的任务找合适的模型,整体体验反而比纯云端更稳。
6.2 哪些场景适合,哪些不适合
适合的场景:企业内部知识库、文档处理流水线、高频低复杂度的自动化任务、对数据隐私敏感的应用。
不太适合的场景:需要顶级模型能力的复杂推理、对响应质量要求极高的面向用户的产品、硬件资源极其有限的个人开发者。
如果你的场景是后者,可能纯云端方案更合适。这套方案的价值在于“掌控感”和“长期成本”,不是“绝对性能”。
6.3 后续可以继续折腾的方向
这套平台搭好之后还有很多可以扩展的地方。
一是多模型路由,除了 DeepSeek,还可以接入其他云端模型,按任务类型选最合适的。二是缓存层,对高频相同问题做缓存,进一步降低成本。三是监控告警,给平台加上用量监控和异常告警,避免账单失控。四是自动化运维,把备份、升级、健康检查都脚本化。
最后分享一个小技巧:Dify 的工作流支持导入导出,我习惯把调好的工作流导出成文件存起来,换环境的时候直接导入,省得重新拖节点。这个习惯帮我省了不少重复劳动。