ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Ollama+DeepSeek+Docker+Dify本地RAG知识库部署

Ollama+DeepSeek+Docker+Dify本地RAG知识库部署

简介:面向希望在本地搭建个人知识库的开发者与 AI 爱好者,这份文档以 ollama、deepseek、docker 与 Dify 四件工具的协同为主线,梳理从零搭建自用问答系统的完整路径,适合具备基础命令行操作能力、想低成本跑通本地大模型检索方案的中级学习者。压缩包内为 1 个 doc 文档,约 5.19MB,以图文混排方式记录各环节的关键界面与参数,涵盖模型下载与环境变量设置、docker-desktop 安装、Dify 容器化启动、知识库分词配置以及聊天应用接入 ollama 端口等内容。文档对易错处保留了提示式说明,例如模型体积与下载耗时的取舍、命令行窗口需保持运行以维持模型服务、容器内访问宿主机端口的地址写法等,便于读者对照排查。目前已有 3400 人学习,可作为搭建私有知识库时的手边参照。

1. 从一台 16G 内存的 Windows 开始:ollama + deepseek + Docker + Dify 这套组合到底能干什么

很多人第一次把 deepseek 跑在自己电脑上,是在命令行里敲一句ollama run,问两句“你好”,然后就关掉了。这其实只走完了半程——真正让本地大模型有用的是后半截:Dify 把 ollama 提供的本地模型接口,和你硬盘里那堆 PDF、Word、Markdown 接起来,让模型只基于你自己的资料回答。这套 ollama + deepseek + Docker + Dify 的组合,本质是把“模型推理”和“知识检索”拆成两个独立进程,再用 HTTP 把它们缝在一起。它适合手上有几十份内部文档、又不想把资料传到外部接口的从业者;也适合想搞懂 RAG 到底怎么落地的新手,因为整条链路都在自己机器上,每一层都能打开看。网上流传的这类部署课程资源不少,但大多只给命令不讲链路,照着敲完一旦连不上就卡死,这篇就按我实际拆过的顺序,把每一步的判断依据和翻车点摊开讲。

2. 装 ollama 并让模型离开 C 盘:Windows 安装、环境变量与 deepseek-r1 拉取

ollama 在这套架构里扮演的是“模型运行时”,它负责把权重加载进显存、对外暴露一个 HTTP 接口、把推理结果吐回来。把它当作本地版的模型服务端来理解,后面所有配置就顺了:Dify 是客户端,ollama 是服务端,两者通过 11434 端口对话。这一章先把服务端装稳、把模型放到正确的盘上,不然后面容器一跑,C 盘直接告急。

2.1 安装包落地与第一次版本校验

Windows 上的安装没什么花活,官网下载对应安装包,双击一路下一步,装完托盘里会多一个羊驼图标,表示后台服务已经起来了。真正的坑在“装完之后怎么确认它是真的可用”,而不是“图标亮了就算成功”。装完先别急着拉模型,用两条命令把客户端和服务端的状态摸清楚。

# 打开 PowerShell,确认 ollama 已经进了 PATH ollama --version # 列出当前已下载的模型,刚装完应该是一张空表 ollama list # 查看服务端监听状态,确认 11434 端口有人守着 Get-NetTCPConnection -LocalPort 11434 -ErrorAction SilentlyContinue

ollama --version验证的是命令行客户端能不能被找到,如果这条报“不是内部或外部命令”,说明安装时 PATH 没写进去,重装或者手动把安装目录加进系统变量都行。ollama list走的是客户端到服务端的本地调用,它能正常返回空表,说明后台服务已经跑起来了——这比看托盘图标靠谱得多。第三条是个很多人会忽略的动作:11434 是 ollama 的默认端口,后面 Dify 要连的就是它,这里先确认端口处于监听状态,等于提前排掉一类“容器连不上”的问题。

需要提醒的是,Windows 托盘启动的 ollama 和你手动ollama serve启动的是同一个东西,别重复启动,否则会撞端口。如果托盘已经常驻,就不要再开一个命令行窗口跑 serve。

2.2 用 OLLAMA_MODELS 把权重挪出系统盘

deepseek-r1 的 1.5B 版本体积还算克制,但只要你顺手拉了 7B、8B,或者以后想加一个 embedding 模型,几十 GB 的权重默认全塞在C:\Users\<用户名>\.ollama\models下,系统盘很快就红。我的习惯是装机第一步就把模型目录挪走,而不是等满了再搬——搬一次要停服务、要移动大文件、要改环境变量,中途断电就是一堆半截文件。

正确顺序是“先停服务、再搬文件、最后设变量”,三步顺序错了会出现“设了变量但 ollama 还在读老路径”的玄学现象。原因是 ollama 启动时读一次环境变量,运行期间不会重读;你在服务还活着的时候改系统变量,它当然无感。

# 1. 先把托盘里的 Ollama 完全退出,确认没有残留进程 Get-Process ollama* -ErrorAction SilentlyContinue | Stop-Process -Force # 2. 建新目录,把已有模型整体搬过去 New-Item -ItemType Directory -Force -Path D:\ollama\models | Out-Null Move-Item "$env:USERPROFILE\.ollama\models\*" "D:\ollama\models\" -Force # 3. 写入用户级环境变量,重开终端后生效 setx OLLAMA_MODELS "D:\ollama\models" setx OLLAMA_HOST "0.0.0.0:11434"

OLLAMA_MODELS指向的是模型权重的根目录,搬完之后ollama list还能列出原来那些模型,说明迁移成功;如果列表变空了,八成是搬到了子目录里,多套了一层models。OLLAMA_HOST设成0.0.0.0:11434而不是默认的127.0.0.1:11434,是为了让 Docker 容器能通过宿主机地址访问到它——只监听回环地址的话,容器那一侧是打不通的,这个点在第四章会再撞一次。

除了这两个,还有几个值得一并设掉的变量:

变量名建议值作用与取舍
OLLAMA_MODELSD:\ollama\models权重落盘位置,越大越该挪出系统盘
OLLAMA_HOST0.0.0.0:11434监听地址,容器访问必需
OLLAMA_KEEP_ALIVE30m模型在显存里的驻留时长,默认 5 分钟,调大避免频繁冷加载
OLLAMA_NUM_PARALLEL1并发请求数,显存吃紧就保持 1

OLLAMA_KEEP_ALIVE这个参数在知识库场景里很实用。RAG 的典型交互是“先检索、再生成”,用户点一次发送,模型可能被调用两次,如果间隔超过默认的 5 分钟,第二次又得重新加载权重,体感上就是“第一次回答快、隔一会儿再问就卡住十几秒”。设成 30 分钟能明显改善这种抖动,代价是显存占用不被释放。

2.3 拉取 deepseek-r1 并跑通第一次推理

模型选择上,1.5B 适合先跑通链路,不适合做正式的知识库问答。参数量决定了它对长上下文和中文语义的把握能力,1.5B 在“根据资料回答问题”这种任务上很容易出现两种毛病:要么无视检索到的内容自己编,要么把资料里的原句原样抄一遍不回答问题。我的建议是先用 1.5B 把所有配置打通,确认链路没问题,再换成更大的版本压测效果。

# 拉取 1.5B 版本,第一次会下载权重,耗时取决于带宽 ollama pull deepseek-r1:1.5b # 进交互模式做一次冒烟测试 ollama run deepseek-r1:1.5b # 不进交互,直接用 API 发一条,方便脚本化验证 curl http://127.0.0.1:11434/api/generate -d "{\"model\":\"deepseek-r1:1.5b\",\"prompt\":\"用一句话解释什么是向量检索\",\"stream\":false}"

ollama pull只下载不加载,可以在不占显存的情况下把模型备好;ollama run既下载又进交互,适合人肉验证。第三条 curl 是我更推荐的方式,因为它验证的是 HTTP 接口而不是命令行封装,后面 Dify 走的就是这条路——命令行能通不代表接口能通,尤其是改过OLLAMA_HOST之后。

规格怎么挑,给一张按经验值的对照表,实际占用会随量化版本和上下文长度浮动:

模型规格大致内存/显存需求适用场景
deepseek-r1:1.5b4GB 以内跑通链路、低配机器验证
deepseek-r1:7b/8b8~12GB个人知识库日常问答的主力档
deepseek-r1:14b16GB 以上对回答质量有要求、机器够硬
deepseek-r1:32b24GB 以上多文档交叉推理,普通办公机不建议

拉完之后记得看一眼ollama ps,它会告诉你模型是否已经加载、占了多少显存。这个命令在排查“为什么回答很慢”时比看任务管理器准,因为模型可能被换出到内存里,任务管理器上看不出区别。

3. 用 Docker Desktop 把 Dify 拉起来:compose 编排、镜像加速与端口自检

Dify 在这套组合里的角色是“应用层”:它管知识库的切分和索引、管对话应用的编排、管模型供应商的接入。它自己不跑模型,所有推理都转发给 ollama。官方提供的是 Docker Compose 编排文件,一条命令拉起一组容器(api、worker、web、db、redis、nginx 等),所以这一章的重点不是“怎么点下一步”,而是“容器起来之后怎么确认每一层都活着”。

3.1 Docker Desktop 安装与 WSL2 后端确认

Windows 上跑 Dify,Docker Desktop 的后端必须走 WSL2,不能是旧的 Hyper-V 模式,否则容器网络和文件挂载都会出各种奇怪问题。安装包下载完双击,安装向导里那个“Use WSL 2 instead of Hyper-V”的选项要保持勾选。装完重启一次,托盘出现鲸鱼图标,右下角状态是 Running 才算法。

# 确认 WSL 版本与已安装的发行版 wsl --status wsl --list --verbose # 确认 Docker 引擎可用,能跑通说明后端没问题 docker version docker info --format "{{.ServerVersion}} | {{.OSType}} | {{.Architecture}}"

wsl --status里如果显示默认版本是 1,用wsl --set-default-version 2改掉再重启 Docker Desktop。docker info那条格式化输出能一次性看到服务端版本、系统类型和架构,比docker version的一屏输出更好读。这三条都过了,再往下走,不然 compose 阶段报的错会把你带偏到镜像问题上,实际是虚拟化层没通。

还有一个容易被忽略的点:Docker Desktop 默认会随开机启动,如果你同时装了别的虚拟化软件(某些安卓模拟器、虚拟机),它们会抢 WSL2 的资源,表现是 Docker 时好时坏。遇到这种间歇性抽风,先关掉其它虚拟化软件再试。

3.2 镜像加速与 .env 里的端口设置

Dify 的 compose 文件要拉十几个镜像,国内网络环境下不加加速基本卡在 pull 阶段。加速配置在 Docker Desktop 的 Settings → Docker Engine 里,编辑那个 JSON,往registry-mirrors数组里填你自己可用的加速地址,保存后 Docker 会自动重启生效。

{ "registry-mirrors": [ "https://<你的加速地址一>", "https://<你的加速地址二>" ], "features": { "buildkit": true } }

填多个地址是有讲究的:Docker 会按顺序尝试,第一个不通自动切下一个,单个地址偶尔抽风不至于让整条 pull 挂死。改完一定要点 Apply & Restart,只在编辑器里保存是不生效的。验证方式是docker info输出里能看到Registry Mirrors那一段列出的地址。

然后是端口。Dify 默认通过 nginx 容器把 Web 界面暴露在宿主机 80 端口,配置文件在dify/docker/.env里,键名是EXPOSE_NGINX_PORT。如果你机器上 80 已经被 IIS、Apache 或者别的服务占了,compose 起的时候 nginx 容器会直接起不来,报端口绑定失败。

# 进到 Dify 源码解压后的 docker 目录 cd D:\dify\docker # 看一眼端口和关键配置,不修改也能跑,但心里要有数 grep -E "EXPOSE_NGINX_PORT|EXPOSE_NGINX_SSL_PORT" .env

端口冲突的判断方法很简单:netstat -ano | findstr :80看有没有别的进程占着。占了就改.env里的EXPOSE_NGINX_PORT,比如改成 8080,那访问地址就变成http://127.0.0.1:8080/signin。这里要提醒一句,改完端口后如果之前已经起过容器,需要docker compose down再up -d,光restart不会重新读端口映射。

3.3 compose 启动与容器状态自检

启动命令有新旧两种写法,docker-compose(v1,带横杠)和docker compose(v2,空格)。新版 Docker Desktop 自带的是 v2,老的教程里写的是 v1,两条都能用,但别在一台机器上混着来,容易因为 compose 文件版本差异出问题。

# 在 dify/docker 目录下执行,-d 表示后台运行 docker compose up -d # 看容器列表和健康状态,重点关注 State 和 Health 两列 docker compose ps # 跟着 api 容器的日志走,初始化数据库失败会在这里暴露 docker compose logs -f api

up -d第一次执行会拉镜像、建数据表、初始化管理员,耗时可能十几分钟,中途别 Ctrl+C。docker compose ps是判断“到底起没起来”的第一现场:正常状态下 api、worker、web、db、redis、nginx 应该是 Up 或 Up (healthy);如果有容器反复重启,State 那一列会显示 Restarting,这时候去看它的日志,八成是数据库连不上或者环境变量缺失。

docker compose logs -f api里最值得关注的是数据库迁移那一段。如果看到连接 db 超时的报错,先确认 db 容器是不是 healthy,再确认.env里的数据库密码和 compose 文件里的一致。这两处对不上是最常见的初始化失败原因,而且报错信息往往指向“认证失败”而不是“密码错了”,容易误判。

全部容器 Up 之后,浏览器访问http://127.0.0.1/signin(或你改过的端口),第一次会要求设置管理员账号,设完就进主界面了。这一步过了,说明应用层已经就绪,接下来才是真正把模型接进来。

4. Dify 接上 ollama 与知识库:模型供应商、分段索引与聊天应用

这一章是整套方案的核心。前面两章装的是“零件”,这一章把它们装成一台能用的机器。链路上有三个关键节点:Dify 怎么找到 ollama(网络地址)、知识库怎么把文档变成可检索的向量(切分与索引)、聊天应用怎么把检索结果喂给模型(上下文注入与参数)。三个节点任何一个配错,表现都是“能聊天但答不准”,很难一眼看出问题在哪。

4.1 配置 Ollama 供应商:host.docker.internal 与 11434

这是新手翻车率最高的一步。在 Dify 的模型供应商设置里选 Ollama,填的 Base URL 如果照抄教程写成http://127.0.0.1:11434,大概率连不上。原因不复杂:Dify 的 api 容器跑在自己的网络命名空间里,容器内的127.0.0.1指向容器自己,不是你的 Windows 宿主机。ollama 装在宿主机上,容器当然找不到。

Docker Desktop 为此提供了一个特殊域名host.docker.internal,容器内解析它会得到宿主机的地址。所以正确的填法是:

Base URL: http://host.docker.internal:11434 模型名称: deepseek-r1:1.5b 模型类型: LLM

填完之后点“保存”,Dify 会立刻发一次请求做连通性校验,成功的话下方会出现模型列表,失败会直接报错。这一步能过,说明网络链路通了;如果报连接超时,按顺序排查三件事:ollama 服务是否在跑、OLLAMA_HOST是否设成了0.0.0.0、宿主机防火墙有没有拦 11434。

模型名称这一栏要和ollama list里的名字完全一致,包括 tag。deepseek-r1:1.5b和deepseek-r1在 ollama 里是两个不同的引用,写错了保存时会提示模型不存在。这一点在同时装了多个规格的机器上特别容易搞混。

4.2 建知识库:分段策略与 Embedding 模型

知识库的本质是把文档切成小段、每段转成一个向量存起来,提问时先算问题向量、再找最相近的几段。这里有两个决策点:切多粗、用什么模型转向量。

切分上,Dify 提供通用分段和父子分段两种模式。通用分段就是按固定长度切,配置项是分段长度和重叠长度;父子分段会保留段落层级,检索时命中子段、返回父段,适合结构清晰的手册类文档。我的经验是:技术文档、产品手册用父子分段,问答对、FAQ 用通用分段,分段长度 512 字符、重叠 50 字符起步,文档里句子特别长的再往上调。

# 拉一个中文效果稳定的向量模型,Ollama 直接提供 ollama pull bge-m3 # 确认拉下来了,记住名字,等下在 Dify 里要原样填 ollama list | grep -i bge

bge-m3是多语言向量模型,中文语义检索表现稳,体积也在可接受范围。拉下来之后回到 Dify 的 Ollama 供应商页面,再添加一个模型,类型选Text Embedding,名称填bge-m3,Base URL 和刚才一样。添加成功后,知识库创建时就能在索引方式里选“高质量(向量检索)”。

索引方式那两个选项值得说清楚:选“高质量”会调用 embedding 模型把每段转成向量,检索时走向量相似度,语义匹配能力强,代价是要额外跑一个模型、建索引慢;“经济”模式只做关键词倒排,不调模型,快但只能命中字面相同的词,问“怎么配置端口”可能检索不到写着“修改监听地址”的段落。做个人知识库没有理由省这一步,选高质量。

文档上传后点“下一步”,确认分段预览没问题再保存,然后等索引完成。索引时间取决于文档数量和机器性能,几十页 PDF 通常几分钟内能跑完,状态从“排队中”变成“可用”才算真正入库。中途刷新页面看到进度卡住不动,先去看 worker 容器的日志,索引任务是它跑的。

4.3 聊天应用里挂知识库并调参数

知识库建好只是“有货”,还得让聊天应用去取货。在 Dify 里创建一个空白应用,类型选聊天助手,进编排界面后,在“上下文”那一栏点添加,选中刚建好的知识库。这一步做了之后,每次提问都会先触发一次检索,命中的片段作为上下文拼进 prompt 再发给模型。

模型参数这块,我一般在 RAG 场景下调成偏保守的一组:

参数建议值调整理由
temperature0.3~0.5越低越贴近检索内容,越高越发散,知识库问答不需要发散
top_p0.9与温度配合,控制采样范围
最大 token2048回答够用即可,太长会拖慢首字延迟
上下文长度4096 起要能装下检索回来的几个片段加问题本身

检索侧还有一组参数在知识库的“召回设置”里:Top K 控制返回几个片段,score 阈值控制低于多少分直接丢弃。Top K 设 3~5 比较合适,设太大反而会稀释重点,模型面对一堆弱相关片段更容易跑偏;阈值一开始可以设 0.5 左右,跑几条真实问题看命中情况再微调。

配好之后在预览窗口做两件事:一是问一个知识库里明确写了答案的问题,看回答有没有引用来源、引用的是不是正确的那一段;二是问一个知识库里完全没有的问题,看模型是老实说“资料中没有”还是硬编。第二个测试比第一个重要,很多“RAG 效果不好”其实是模型在无检索结果时不肯认输。

5. 避坑清单:五个把新手卡在半路的真实现场

这一章记录的是我在不同机器上重复遇到过的五类问题,每一条都按“现象 → 原因 → 解决”写。它们的共同点是:报错信息指向的地方,往往不是真正出问题的地方,照着报错去搜会绕很远。

5.1 现象:compose 卡在 pull 阶段,进度条不动或反复重试

原因基本是镜像拉取通道不畅,跟 Dify 本身无关,跟 compose 文件版本也无关。Docker Hub 的默认源在部分网络环境下会超时,表现是某几个镜像拉了一半停住,docker compose ps里对应容器一直是 Created 状态。

解决分两步:先在 Docker Engine 配置里加registry-mirrors并重启引擎,再执行docker compose pull单独把镜像拉完,最后才up -d。把拉镜像和起容器拆成两步的好处是,卡住的时候能明确知道是网络问题还是配置问题。如果只有某一个镜像死活拉不下来,可以单独docker pull <镜像名>看具体报错,比在一堆并行拉取里找线索快得多。

5.2 现象:Dify 里填127.0.0.1:11434报连接失败,但浏览器能打开 ollama

原因就是容器网络隔离。容器内的回环地址指向容器自身,宿主机上的服务对它不可见。这不是配置错误,是网络模型决定的。

解决办法是把地址换成http://host.docker.internal:11434。这个域名在 Docker Desktop 的 Windows 和 macOS 后端下默认可用,Linux 上需要在 compose 里给服务加extra_hosts: - "host.docker.internal:host-gateway"。另外别忘了宿主机侧的OLLAMA_HOST必须是0.0.0.0:11434,只监听127.0.0.1的话,即使域名解析对了也连不上,因为服务根本没在那个网卡上等连接。

5.3 现象:设了OLLAMA_MODELS,但 ollama 还在往 C 盘写

原因是环境变量在服务启动时只读一次。你在托盘图标还亮着的时候改系统变量,运行中的进程不会重读,新拉的模型照样落到老路径。

解决顺序是死的:先退出托盘里的 Ollama、确认进程清空、再设变量、再重启。验证方法是拉一个小模型,然后看新目录下有没有多出blobs和manifests两个子目录。还有个小概率情况是变量设到了系统级但当前终端是管理员权限开的,用户级变量读不到,重开一个普通终端再试。

5.4 现象:知识库建好了,提问却完全不引用资料,回复像是凭空生成

原因可能是三个层面的:分段切得太碎导致语义不完整、索引方式选成了关键词模式、或者检索阈值设得太高导致一条都没召回。

排查从知识库的“召回测试”入口开始,直接输入你的问题,看它返回哪些片段、相似度分数是多少。如果一条都不返回,把 score 阈值往下调;如果返回的片段明显不相关,说明分段有问题,回去调小分段长度或者换成父子分段;如果片段相关但回答还是不引用,那就是应用侧没挂上知识库,回去检查“上下文”那一栏是不是空的。这三层按顺序排,比一上来就换模型有效得多。

5.5 现象:1.5B 模型在知识库问答里答非所问,换了提示词也没用

原因是模型容量不够。1.5B 参数量在“阅读多段检索内容并归纳回答”这个任务上能力有限,典型表现是忽略检索内容自顾自答、或者把几个片段拼接起来答非所问。这不是参数没调好,是模型本身撑不住。

解决办法是换规格。同样的配置、同样的知识库,换成 7B 或 8B 版本,回答质量会有肉眼可见的提升。1.5B 的正确用法是当“链路验证器”——所有配置用它跑通,确认网络、索引、上下文注入都没问题,再换大模型压效果。这个顺序别反过来,否则模型效果差和配置错会混在一起,根本分不清是哪个的问题。

6. 上线前的三条自检:从 curl 探活到召回抽查

配置全部跑通之后,我一般不会直接开始用,而是走一遍固定的自检流程。原因很简单:这套链路有四五个进程,任何一层半死不活的表现都是“能回答但答得怪”,靠体感判断不出来。

第一条是分层探活。宿主机侧和容器侧各发一次请求,两边结果对比,能立刻定位问题在网络层还是应用层。

# 宿主机视角:ollama 自己通不通 curl -s http://127.0.0.1:11434/api/tags | head -c 200 # 容器视角:从 Dify 的 api 容器里访问宿主机,模拟真实调用路径 docker compose exec api python -c "import urllib.request; print(urllib.request.urlopen('http://host.docker.internal:11434/api/tags', timeout=5).read().decode()[:200])"

第一条返回模型清单,说明 ollama 服务本身没问题;第二条如果在容器里也能返回同样的清单,说明host.docker.internal解析和端口映射都通了。第二条失败而第一条成功,问题一定在容器网络或OLLAMA_HOST的监听地址上,不用再怀疑模型和配置。

第二条是看模型驻留状态。ollama ps会显示当前加载了哪个模型、占用多少、还剩多久被卸载。知识库问答场景下,如果你发现每次提问前模型都要重新加载,把OLLAMA_KEEP_ALIVE调大;如果显示同时加载了两个模型(一个对话一个向量),而显存又吃紧,考虑把向量模型换小一号。

第三条是召回抽查,也是最能体现效果的一步。从知识库里挑 10 个问题,覆盖三种类型:答案明确的、需要跨段落拼的、知识库里根本没有的。前两类看召回片段是否正确、回答是否引用了来源,第三类看模型会不会硬编。我习惯建一张简单的表记录结果:

问题类型抽查条数观察点不合格时的动作
答案明确4引用片段是否命中原文调分段长度或降阈值
跨段落3是否召回了多个相关片段提高 Top K
无答案3是否承认资料中没有补提示词约束

这张表跑一轮大概十几分钟,但能提前发现八成以上的效果问题。血泪经验是:不要用“随便问几句感觉还行”来代替这一步,感觉这东西在 RAG 上极不可靠,同一套配置换个问法结论就反过来了。

从那以后我每次部署完这类本地知识库,都会强制走一遍这三条自检,哪怕只是换了个模型版本。因为链路的每一层都在动,昨天通的今天未必通,而排错的成本远高于自检的成本。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进