
1. 为什么我要从“API 打工人”变成“本地优先”的架构1.1 一个让我彻底醒悟的账单夜晚去年冬天的一个晚上我盯着后台的 API 调用账单发呆。那个月我做了三个小工具一个帮团队整理会议纪要的助手、一个给运营同事写文案的机器人、还有一个我自己用来做代码审查的小助手。三个东西加起来一个月烧掉了将近四百块。钱不算多但问题在于——我完全不知道这些钱花在了哪里哪个工具在偷偷调用哪个提示词写得又臭又长导致 token 爆炸。更让我难受的是另一件事。有天晚上我在家调试一个知识库问答的流程网络突然抽风整个应用直接瘫了。我坐在电脑前看着屏幕上那个红色的报错突然意识到一个很荒谬的事实我花了一堆时间搭的东西命脉居然攥在别人手里。网络一断我连自己写的提示词都跑不起来。那一刻我下定决心要搞一套“本地优先、云端兜底”的私有 AI 平台。本地能跑的就本地跑本地跑不动的、或者需要更强推理能力的再走云端。核心诉求就三条数据不出内网、断网也能用、成本可控。1.2 为什么是 Dify Ollama DeepSeek 这个组合选型这件事我折腾了差不多两周试过不少方案最后落在这三个东西上是有明确理由的。Dify解决的是“编排”问题。我不想每次都写一堆 Python 脚本去拼接提示词、管理上下文、处理知识库检索。Dify 提供了一套可视化的流程编排能力工作流、知识库、变量聚合器这些概念让我可以把一个复杂的 AI 应用拆成一个个节点像搭积木一样拼起来。而且它支持本地部署数据完全在自己手里。Ollama解决的是“本地模型运行”问题。它的价值在于把模型下载、加载、推理这一整套东西封装得极其简单。一条ollama run命令就能跑起来一个模型还自带一个兼容 OpenAI 格式的 API 接口。对于我这种不想折腾 CUDA 版本、不想手动编译推理框架的人来说它就是最省心的选择。DeepSeek解决的是“兜底”问题。本地模型再强遇到复杂推理、长上下文、需要高质量代码生成的时候还是力不从心。DeepSeek 的 API 价格便宜推理质量在同类里属于第一梯队作为云端兜底非常合适。而且它的接口兼容 OpenAI 格式接入成本极低。这三个东西组合起来形成了一条清晰的链路Dify 做大脑负责调度Ollama 做本地肌肉负责日常推理DeepSeek 做外援负责攻坚。下面我把整套搭建过程、踩过的坑、以及一些只有实际跑过才知道的细节完整地分享出来。2. 整体架构设计与核心思路拆解2.1 三层架构调度层、推理层、兜底层整套平台的架构我画过好几版最后稳定下来的是一种三层结构。最上面是调度层由 Dify 承担。Dify 里配置了多个模型供应商包括本地的 Ollama 和云端的 DeepSeek。工作流里通过条件判断来决定走哪条路。比如一个简单的文本分类任务直接走本地模型一个需要深度推理的代码审查任务走云端。中间是推理层由 Ollama 承担。我在本地跑了两三个模型一个通用对话模型、一个嵌入模型用于知识库检索。Ollama 的 API 默认监听在11434端口Dify 通过这个端口和它通信。最下面是兜底层由 DeepSeek API 承担。当本地模型返回的结果置信度低、或者任务本身被标记为“高复杂度”时Dify 会把请求转发到 DeepSeek。这个架构的核心思路是按需分配。不是所有任务都需要最强的模型就像不是所有邮件都需要叫顺丰加急。日常的、简单的、涉及敏感数据的任务本地消化复杂的、需要高质量输出的任务云端处理。2.2 为什么“本地优先”不是“本地唯一”这里我要特别说明一个我踩过的认知坑。一开始我想的是“全部本地化”结果发现根本不现实。首先是硬件限制。我手头的机器是一台 32G 内存、带一张 12G 显存显卡的工作站。跑一个 7B 参数的模型还算流畅但跑 32B 以上的模型就非常吃力推理速度慢到无法接受。而很多复杂任务小模型确实做不好。其次是能力边界。本地小模型在代码生成、复杂逻辑推理、长文档理解这些方面和云端大模型有明显差距。我试过让本地模型做代码审查它经常漏掉一些微妙的逻辑问题而 DeepSeek 能准确指出来。所以“本地优先”的真正含义是在满足需求的前提下优先用本地。判断标准包括数据敏感度、任务复杂度、响应速度要求、以及当前本地模型的负载情况。这个判断逻辑我后来在 Dify 的工作流里用条件节点实现了。2.3 数据流向与安全边界这套架构里数据流向是这样的用户请求进入 DifyDify 根据路由规则决定走本地还是云端。走本地时数据只在 Dify 容器和 Ollama 服务之间流转不出内网。走云端时只有经过脱敏处理的请求内容会发送到 DeepSeek 的 API。这里有个关键设计敏感数据永远不走云端。我在 Dify 的工作流里加了一个前置节点用来识别请求中是否包含敏感信息比如内部项目名称、客户数据等。如果包含强制走本地即使本地模型能力不足也不外发。这个判断逻辑我用的是关键词匹配加正则表达式虽然简单但足够覆盖大部分场景。更复杂的方案可以用本地模型做一次分类但那样会增加延迟我权衡后选择了轻量方案。3. 环境准备与核心组件部署实操3.1 Docker 环境搭建从安装到避坑整套平台的部署我全部用 Docker 完成这样环境隔离干净迁移也方便。但 Docker 的安装本身就有不少坑我一个个说。Windows 环境下我推荐用 Docker Desktop。安装过程本身不复杂但有几个点要注意。第一安装前确保 WSL2 已经启用否则 Docker Desktop 启动会报错。第二安装完成后在设置里把 Docker 的镜像存储位置改到非系统盘否则 C 盘很快就会被镜像撑爆。我一开始没改结果 C 盘红了排查了半天才发现是 Docker 的镜像和容器数据占了三十多个 G。Linux 环境下用官方的安装脚本最省事。但要注意安装完成后需要把当前用户加入 docker 组否则每次执行 docker 命令都要加 sudo。命令是sudo usermod -aG docker $USER执行完要重新登录才生效。安装完成后验证一下docker --version docker compose version两个命令都能正常输出版本号说明环境没问题。注意如果你在公司内网环境Docker 拉取镜像可能会很慢甚至失败。这时候需要配置镜像加速器。具体配置方法是在 Docker 的配置文件中添加 registry-mirrors 字段。配置完成后重启 Docker 服务。3.2 Ollama 部署模型存储路径与离线安装Ollama 的安装本身很简单官网下载安装包一路下一步就行。但有两个问题几乎所有人都会遇到模型下载太慢和模型存储路径默认在系统盘。先说存储路径。Ollama 默认把模型存在用户目录下的.ollama文件夹里。一个 7B 模型大概 4 到 5 个 G几个模型下来就是几十个 G。如果你的系统盘空间紧张一定要改路径。Linux 下修改路径编辑 Ollama 的服务配置文件添加环境变量OLLAMA_MODELS指向新的目录。Windows 下则是设置系统环境变量。改完之后重启 Ollama 服务之前下载的模型需要手动迁移过去。再说下载慢的问题。Ollama 的模型仓库在境外下载速度确实不稳定。我的做法是能离线安装就离线安装。Ollama 支持从本地文件导入模型你可以先在其他网络环境好的机器上下载好模型文件然后拷贝过来导入。导入的命令是ollama create my-model -f Modelfile其中 Modelfile 里指定了模型文件的路径。这种方式适合批量部署也适合内网环境。验证 Ollama 是否正常运行ollama list curl http://localhost:11434/api/tags第一个命令列出已下载的模型第二个命令测试 API 接口是否可用。两个都正常说明 Ollama 部署成功。3.3 Dify 本地部署从拉取到初始化Dify 的本地部署官方推荐用 Docker Compose。流程是克隆代码仓库、进入 docker 目录、复制环境变量文件、启动容器。git clone https://github.com/langgenius/dify.git cd dify/docker cp .env.example .env docker compose up -d启动完成后访问http://localhost:3000就能看到 Dify 的初始化页面。第一次访问需要设置管理员账号和密码。这里有几个坑我要重点说。第一个坑是端口冲突。Dify 默认用 3000 端口如果你机器上已经有其他服务占用了这个端口启动会失败。解决办法是修改.env文件里的端口映射配置。第二个坑是 SSL 错误。很多人第一次访问 Dify 时会遇到浏览器提示 SSL 证书错误。这是因为 Dify 默认配置了 HTTPS 重定向但本地环境没有有效的证书。解决办法是在.env里把 HTTPS 相关的配置关掉或者用http://而不是https://访问。第三个坑是凭据验证失败。在 Dify 里配置模型供应商时如果 API 地址或密钥填错会提示an error occurred during credentials validation。这个错误的排查思路是先用 curl 直接测试 API 地址是否可达再检查密钥是否正确最后确认 Dify 容器能否访问到那个地址。如果是 Ollama要注意 Dify 容器里的localhost指的是容器本身不是宿主机。需要用宿主机的内网 IP 或者 Docker 的特殊域名。提示Dify 容器访问宿主机的 Ollama地址应该填http://host.docker.internal:11434Windows/Mac或者宿主机的内网 IPLinux。这个细节坑了我整整一个下午。4. Dify 工作流编排与模型路由实现4.1 模型供应商配置本地与云端并存Dify 支持配置多个模型供应商这是实现“本地优先、云端兜底”的基础。在 Dify 的设置里进入模型供应商页面添加两个供应商。一个是 Ollama填入本地 Ollama 的 API 地址比如http://host.docker.internal:11434。添加完成后Dify 会自动拉取 Ollama 里已下载的模型列表。另一个是 DeepSeek填入 API 密钥选择对应的模型。配置完成后在创建应用时就可以选择用哪个模型。但真正的灵活性在于工作流里的模型节点——你可以在同一个工作流的不同节点使用不同的模型。这里有个细节Ollama 的模型名称和 Dify 里显示的名称可能不完全一致。如果遇到模型找不到的错误去 Ollama 里用ollama list确认准确的模型名称然后在 Dify 里手动填写。4.2 条件路由什么任务走本地什么任务走云端这是整套平台最核心的逻辑。我在 Dify 的工作流里设计了一个路由判断节点根据几个维度来决定走哪条路。第一个维度是数据敏感度。工作流开头有一个代码节点用正则表达式检查用户输入是否包含敏感关键词。如果命中直接走本地并且记录一条日志。第二个维度是任务类型。我在工作流里预设了几种任务类型简单问答、文本分类、代码生成、长文档分析。简单问答和文本分类走本地代码生成和长文档分析走云端。第三个维度是本地负载。这个稍微复杂一点我写了一个简单的检查逻辑通过 Ollama 的 API 获取当前是否有模型正在加载。如果本地正忙就把请求路由到云端避免排队。这三个维度的判断结果通过 Dify 的条件分支节点组合起来。最终形成一棵决策树敏感数据一律本地非敏感数据看任务类型任务类型不明确的看本地负载。4.3 变量聚合器让多路结果统一输出Dify 的变量聚合器是我用得最多的功能之一。它的作用是把多个分支的输出合并成一个统一的输出。在这个场景里本地模型和云端模型的返回格式可能不完全一样。本地模型可能返回纯文本云端模型可能返回带 markdown 格式的文本。变量聚合器可以把它们统一成一种格式再传给后续的节点。使用变量聚合器的步骤是在工作流里添加一个变量聚合器节点把本地分支和云端分支的输出都连到它上面然后设置一个输出变量名。后续节点引用这个变量名就能拿到统一的结果。这里有个坑变量聚合器要求所有输入变量的类型一致。如果本地返回的是字符串云端返回的是 JSON 对象聚合器会报错。解决办法是在聚合之前先用代码节点把格式统一。5. 知识库流水线与本地嵌入模型5.1 为什么知识库也要本地化知识库是这套平台里数据最敏感的部分。我往里放的是团队内部的文档、项目资料、会议记录。这些东西如果走云端的嵌入接口等于把内部资料全部上传了。所以知识库的嵌入模型必须本地跑。Ollama 支持嵌入模型我选了一个轻量级的嵌入模型跑在本地。Dify 的知识库配置里嵌入模型选择 Ollama 提供的那个。这样文档切片、向量化、检索全部在本地完成。5.2 知识库流水线的搭建步骤搭建知识库的流程是创建知识库、上传文档、配置分段规则、选择嵌入模型、等待处理完成。分段规则这里我要多说一句。默认的自动分段有时候会把一个完整的逻辑段落切碎导致检索时召回的内容不完整。我的做法是手动调整分段标识符用文档里实际存在的标题层级作为分隔。比如 markdown 文档用##作为分隔符这样每个二级标题下的内容会成为一个独立的块。嵌入模型选择本地 Ollama 的嵌入模型后处理速度取决于本地硬件。我的机器上一份五十页的文档大概需要两三分钟处理完。如果文档量大建议分批上传避免一次性把内存打满。5.3 检索效果调优的实操经验知识库搭好之后检索效果不一定理想。我遇到过几个典型问题。问题一召回内容不相关。原因是分段太粗一个块里包含了好几个主题。解决办法是细化分段或者调整检索时的相似度阈值。问题二召回内容不完整。原因是分段太细一个完整的答案被切成了好几块。解决办法是增大分段长度或者启用 Dify 的“父子分段”模式让检索时返回更完整的上下文。问题三检索速度慢。原因是向量数量太多检索时计算量大。解决办法是定期清理不再需要的文档或者升级本地硬件。我最后稳定下来的配置是分段长度 500 字符重叠 50 字符相似度阈值 0.6召回数量 5。这套参数在我的场景下效果比较均衡。6. 常见问题排查与避坑实录6.1 模型连接类问题速查问题现象可能原因排查方法解决方案Dify 提示凭据验证失败API 地址错误或密钥无效用 curl 测试 API 地址检查地址是否可达密钥是否正确Ollama 模型列表为空Dify 容器无法访问宿主机在容器内 ping 宿主机 IP改用 host.docker.internal 或内网 IP模型调用超时本地模型加载中或硬件不足查看 Ollama 日志等待加载完成或换更小的模型云端 API 返回 400上下文超长检查请求的 token 数截断输入或换用支持长上下文的模型6.2 上下文超长的处理策略this models maximum context length is 1048576 tokens这个报错我遇到过好几次。虽然 1048576 这个数字看起来很大但当你把知识库检索结果、对话历史、系统提示词全部拼在一起时很容易就超了。我的处理策略是三层截断。第一层限制知识库召回数量从默认的 10 降到 5。第二层限制对话历史长度只保留最近 5 轮。第三层在代码节点里做一个 token 估算如果超过阈值就自动截断最前面的内容。Token 估算我用的是简单的字符数除以 4 的方法虽然不精确但足够用来做阈值判断。6.3 离线安装插件的正确姿势Dify 的插件系统很强大但离线安装插件是个麻烦事。官方市场里的插件需要联网下载内网环境用不了。我的做法是在有网络的机器上先把插件下载下来然后拷贝到内网机器上手动安装。Dify 的插件安装包是一个压缩文件放到指定目录后重启 Dify 就能识别。具体路径是 Dify 的plugins目录。把插件包解压后放进去然后在 Dify 的插件管理页面里刷新就能看到新插件。注意离线安装插件时要确保插件的版本和 Dify 的版本兼容。版本不匹配会导致插件加载失败甚至影响 Dify 的正常运行。6.4 模型存储路径迁移的完整流程如果你一开始没改 Ollama 的模型存储路径后来想迁移流程是这样的。第一步停止 Ollama 服务。第二步把原路径下的models文件夹整体拷贝到新路径。第三步修改环境变量OLLAMA_MODELS指向新路径。第四步重启 Ollama 服务。第五步用ollama list确认模型还在。这个过程看起来简单但有一个坑如果新路径的权限不对Ollama 会启动失败。Linux 下要确保 Ollama 的运行用户对新路径有读写权限。7. 我在这套平台上跑出来的实际效果7.1 成本对比从月均四百到月均不到五十搭好这套平台之后我统计了一个月的使用数据。本地模型处理了大约 70% 的请求云端只处理了 30%。云端 API 的费用从之前的月均四百降到了不到五十。更重要的是那 70% 的本地请求里包含了所有涉及内部数据的任务。这部分数据以前是要上传到云端的现在完全不出内网。对于我这种对数据敏感的人来说这个价值比省钱更重要。7.2 响应速度本地模型的优势与短板本地模型的响应速度分两种情况。模型已经加载到内存时响应非常快基本在 1 到 2 秒内返回。但如果模型没加载第一次请求需要等模型加载可能要十几秒。我的优化方案是让常用的模型常驻内存。Ollama 支持设置模型保持加载的时间我把它设成了 30 分钟。这样在活跃使用时段模型基本不会卸载。云端模型的响应速度取决于网络一般在 3 到 5 秒。虽然比本地慢一点但胜在稳定不会因为本地硬件负载而波动。7.3 一个让我惊喜的意外收获搭这套平台的过程中我意外发现了一个很有用的场景本地模型做预处理云端模型做精加工。具体来说用户输入一段很长的文本我先用本地模型做一次摘要和关键信息提取然后把摘要和提取结果发给云端模型做深度分析。这样既减少了发送到云端的数据量又提高了云端模型的处理质量。这个模式后来成了我处理长文档分析任务的标准流程。本地模型就像一个初级助理先把材料整理好云端模型就像一个资深专家在整理好的材料上做深度工作。8. 后续可以继续折腾的方向这套平台目前运行稳定但还有几个方向我打算继续折腾。第一个方向是模型量化。本地模型占用的显存比较大如果能用量化版本可以在同样的硬件上跑更大的模型。Ollama 支持多种量化格式我打算试试 Q4 和 Q5 的量化版本看看效果和速度的平衡点在哪里。第二个方向是多模型并行。目前本地只跑了一个通用模型和一个嵌入模型。如果硬件允许我想再跑一个专门用于代码的模型这样代码相关的任务可以在本地完成不用走云端。第三个方向是自动化路由优化。目前的路由规则是我手动配置的未来想根据历史数据自动调整。比如统计哪些任务本地模型处理得好哪些处理得差然后动态调整路由阈值。这套平台从想法到落地前后花了大概三周时间其中大部分时间花在踩坑和调优上。但跑通之后那种“数据在自己手里、断网也能用”的踏实感让我觉得这些时间花得值。如果你也在被 API 账单和网络依赖困扰不妨试试这个思路。