你手上有一堆内部文档,想让它们能问答,又不想把内容传到别人服务器。思路是 Ollama 在本机把模型跑起来,Dify 负责切分、检索和编排。这篇按动作顺序讲:先确认模型接口通不通,再配模型、选嵌入与重排、调切分参数、最后做一轮可用性验证和排查。踩到的坑大多是接口地址、模型名和上下文长度对不上。
你手上有一堆内部文档,想让它们能问答,又不想把内容传到别人服务器上。一个常见做法是:用 Ollama 在本机把模型跑成一个可调用的服务,用 Dify 负责文档切分、向量检索、提示编排和对话界面。数据不出内网,模型也不用按调用量付费。
这篇按动作顺序讲:先确认模型接口通不通,再配模型、选嵌入与重排、调切分参数,最后做一轮可用性验证和排查。踩到的坑大多是三件事对不上:接口地址、模型名、上下文长度。先把这三样对齐,后面就顺了。
先想清楚:为什么是 Ollama 加 Dify 这套组合
这两个组件各管一段。Ollama 把模型跑成本地可调用的服务,你不用关心推理细节;Dify 管的是文档切分、向量检索、提示词编排和前端对话。你只需要把两者用一条 HTTP 接口连起来,剩下的都是配置。
| 判断维度 | 适合这套组合 | 不适合,换思路 |
|---|---|---|
| 文档量 | 中等规模,单机能装下向量库 | 上百万文档、要分布式检索 |
| 问答形态 | 以检索原文为主、有出处可查 | 要复杂多轮推理、多工具联动 |
| 数据要求 | 文档必须留在内网 | 无此要求,可接受公有云 |
| 并发 | 几人到几十人内部用 | 高并发、多租户隔离 |
| 扩容 | 单机或少量机器 | 需要弹性伸缩、跨机调度 |
需要极高并发或者要跨机器弹性扩容的场景,更适合看 vLLM 一类专门的推理框架,Dify 在这里主要是编排层。选型前先到 /models/deploy 按模型和量化档位估算一遍显存占用,别等装完才发现跑不动。
装之前先把三件事定下来
很多人失败不是技术不行,是装之前没想清楚要跑什么。先把模型、嵌入模型、部署方式这三件事定下来,后面每一步都有依据。
- 跑哪个生成模型、量化到什么档位:模型越大对显存越敏感,量化越低省显存但效果会打折,先定一个能跑起来的档位
- 嵌入模型用哪个:中文检索效果很大程度取决于它,可到 /embeddings 对比不同嵌入模型的适用面
- 部署方式:Docker 和宿主机混装最容易出网络问题,先把端口规划写下来,谁连谁心里有数
- 把机器配置、显存占用、预期文档量记成一页纸,后面排查时拿来对照
部署方式这一项特别容易被忽略。如果 Dify 跑在容器里、Ollama 跑在宿主机上,两边的网络视角完全不同,地址怎么写、端口通不通,是后面最常见的卡点,提前规划能省很多时间。
Ollama 这一侧:把服务跑成能对外调用的接口
装好之后,先确认服务真的在监听、能列出已下载的模型,再谈对接。这一步没验证就去配 Dify,等于闭着眼睛调接口。具体命令和路径可以到 /guides/ollama-getting-started 对照着做。
- 确认服务在跑看进程和服务端口,能列出本地已有模型列表,说明服务本身没问题
- 拉模型优先选带中文能力的模型;下载慢或中断就换镜像源重试,别在一个源上硬等
- 放开监听地址默认只监听本机,Dify 跑在容器里会连不上,需要改成能被外部访问的地址,做法见 /guides/ollama-local-api-address
- 量一个速度基线有条件就让 Ollama 用 GPU;用一条短请求量出首字延迟和生成速度,到 /tools/speed-test 记下基线,后面改动前后有对比
速度基线这一步看着多余,其实很值。等你换模型、改量化档位、调召回条数之后,能立刻看出是变快了还是变慢了,而不是凭感觉。
Dify 这一侧:模型接不上的排查顺序
Dify 里的流程是:在模型供应商里选 Ollama 类型,填服务地址和模型名,保存后点一下测试。报连接失败的时候,别乱改配置,按固定顺序排查最快。
| 报错表现 | 先查什么 | 再查什么 |
|---|---|---|
| 连接被拒 | 容器能不能访问到宿主机 | 服务有没有真的在监听该端口 |
| 超时 | 地址写的是不是可达地址 | 防火墙或端口有没有拦 |
| 模型不存在 | 模型名是否和服务里列出的一致 | 有没有多写或漏写标签后缀 |
| 能连但很慢 | 是不是退回 CPU 跑 | 显存是否被其他进程占满 |
最容易犯的错是地址写 localhost。容器里的 localhost 指的是容器自己,不是宿主机,所以要用宿主机的可达地址或者容器网络别名。模型名也要和服务里列出的完全一致,多一个标签后缀就找不到。
嵌入与重排:决定检索效果的两块
知识库的检索质量主要由嵌入模型决定。中文语料要挑在中文上训练过的嵌入模型,选型可以到 /embeddings 对比。嵌入模型换一次,就要把全部文档重新向量化一遍,所以别中途随便换。
文档多、提问比较绕的时候,可以加重排模型:先用向量粗召回一批,再用重排精挑出真正相关的几条,能明显减少答非所问。重排模型的选型思路见 /guides/ollama-rerank-model-pick。
- 生成模型和嵌入模型可以不是同一个,分开配是常规做法,别强行用同一个
- 嵌入模型偏重语义匹配,重排模型偏重相关性排序,两者职责不同
- 文档量小、提问直接时,不加重排也能用,先跑通再优化
- 重排会增加一次计算,机器吃力时先只上嵌入,看效果够不够
切分与检索参数:调这几个就够了
切分块大小按文档类型走:条款式文档语句独立、适合切小一点;叙述式文档上下文连在一起、适合切大一些。先定一个值,再回看检索结果调整,不要一次改好几个参数。
| 参数 | 作用 | 怎么定 |
|---|---|---|
| 块大小 | 每段被切多长 | 条款式切小,叙述式切大 |
| 重叠长度 | 相邻块保留多少重合 | 给一点,避免一句话被切断两边都搜不到 |
| 召回条数 | 每次取回几段原文 | 先给小值,不够再加,多了容易被无关内容带偏 |
| 上下文长度 | 拼给模型的文本总长 | 卡在模型窗口内,超出会被截断 |
召回条数不是越大越好。条数多了,模型容易被无关内容带偏,答案反而更差。上下文长度也要卡在模型窗口之内,超出的部分会被直接截断。相关概念可以到 /ai-dict/category/basics 补一下。
怎么判断这套东西真的能用
跑通不等于能用。准备一组带标准答案的问题集,故意包含两类:文档里能查到答案的,和文档里根本没有的。看它答不出来的时候会不会硬编。
- 答案能不能落到原文:回答里的关键信息要能在引用片段里找到
- 引用片段对不对:检索出来的段落是不是真的和问题相关
- 响应要等多久:单次问答的等待时间是否在可接受范围
- 并发会不会卡:同时进来几个人,响应是否明显变慢
- 边界问题表现:文档里没有的内容,是老实说不知道还是编一个
如果发现编造答案,先查检索层,别急着怪模型。大部分情况下是检索压根没拿到原文,模型只能靠训练记忆硬答。把检索修好,编造问题往往自己就消失了。验证结果记成一份对照表,换模型或改切分参数后重跑一遍。
常见卡点:按这个顺序排除
遇到问题按层次排除,比东改一处西改一处高效得多。从接口层往下,一层一层确认,哪层不通就停在哪层解决。
- 先看接口层模型调不通和答案不好是两回事,先确认能正常调用再谈效果
- 再看检索层到 /tools/verify 一类在线检测工具确认服务状态,检索结果为空多半是嵌入或切分的问题
- 然后看上下文层报错里出现长度超限,就下调召回条数或块大小,把拼给模型的文本压回窗口内
- 最后看资源层显存满了会退回 CPU 跑,速度断崖式下跌,排查思路见 /guides/vram-optimization-guide
常见问题
本地知识库问答答不准,是模型不行还是检索不行?
多数情况先怀疑检索。去 Dify 里看命中的文档片段,如果片段本身跟问题无关,换生成模型也没用,该调块大小、召回条数或换嵌入模型;如果片段是对的、模型还是答偏,再考虑换更强的生成模型。
Ollama 和 Dify 都装在本机,为什么还是连不上?
常见原因是 Dify 跑在容器里,而 Ollama 只监听本机地址,容器里的本机不是你的本机。要放开 Ollama 的监听地址,并在 Dify 里填容器能访问到的地址,同时确认端口没有被防火墙挡。
文档要更新,是不是每次都得重新做一遍?
只更新改动部分,Dify 侧重新解析对应文档即可。但要注意:如果换了嵌入模型,必须全量重新向量化,否则新旧向量的语义空间不一致,检索结果会乱。
这套方案适合多大文档量、多少人用?
中小团队、文档以内部资料和手册为主、同时在线人数不多的场景比较合适。文档量很大或并发要求高的时候,瓶颈通常在检索和推理资源上,得先算资源再定方案。
不想让文档出内网,这套做法能满足吗?
模型推理和文档处理都在自己的机器上,链路本身不外发。但要注意别在流程里接入外部模型接口做兜底,也别把日志和备份传到外部存储,否则等于绕开了隔离。