我把 WeKnora 完整部署到了本地,这套腾讯微信团队开源的 AI 知识库问答系统,最后配合 Ollama 跑的本地大模型组成了一套完全离线的问答系统。整个过程花了两个晚上,中间踩了不少坑:依赖容器起不来、模型服务连不上、知识图谱生成失败……这篇实录就是想把 WeKnora 本地部署的完整链路拆开来讲,包括选型理由、环境准备、部署步骤、模型接入、图谱生成,以及维护避坑。
如果你正在为内部知识库找一套开源方案,又不想把数据送出去调云上的接口,想把文档喂给本地大模型做问答,那这篇内容应该对你有用。我会尽量把细节写透,包括我在 Docker Compose 里改过的配置、Ollama 模型服务的对接方式、以及最后跑通问答的完整验证过程。
1. 为什么我选了 WeKnora 而不是 Dify 或 FastGPT:从需求倒推方案
先说结论:我没有一开始就选 WeKnora,而是先列了自己的需求清单,再拿市面上的开源知识库方案逐个对比,最后才定下它。
1.1 我真正想要的知识库是什么样的
我的场景很明确:团队内部有大量产品文档、技术方案、会议纪要,散落在 Word、PDF、Markdown、Excel 各种格式里。平时查一个历史决策要翻半天聊天记录和共享盘,非常痛苦。所以我想搭一套内部问答系统,上传文档后直接问问题,由 AI 给出答案,并且答案要能追溯到原文。
这里有一个硬性条件:数据不能出内网。公司对内部资料外发管得比较严,云上的接口基本都不可用,因此必须本地化。也就是说,问答模型要跑在本地,文档向量化也要在本地完成,整条链路不依赖任何外部服务。
另外一个软性需求是我比较贪心的:不只是简单的"向量检索 + 拼接文档 + 让模型总结",而是希望系统能理解文档之间的关系。比如多个文档里提到同一个人物、同一个项目代号、同一个故障指标,最好能自动构建出一张知识图谱,让提问"跟 A 项目相关的所有风险点"时,检索结果不只是关键词命中,而是能够顺着关系把相关片段捞出来。
1.2 主流开源知识库问答系统的横向对比
我把当时重点看的四个方案放到一张表里,列一下各自的特点:
| 方案 | 开发团队 | 知识图谱 | 混合检索 | UI 类型 | 我的评价 |
|---|---|---|---|---|---|
| Dify | LangGenius | 无 | 有 | 应用编排型 | 好用但偏"搭建 AI 应用",知识库只是其中一个模块,跟我要的"文档知识库"定位不完全一样 |
| FastGPT | 环界云 | 无 | 部分 | 应用编排型 | 流程编排灵活,适合做客服机器人,但知识库管理不算突出 |
| RAGFlow | Infinity | 有 | 有 | 知识库管理型 | 文档解析很强,图谱也有,但我实际部署时被组件复杂度劝退过 |
| WeKnora | 腾讯微信 | 有 | 有 | 知识库管理型 | 最符合"本地知识库问答系统"的定位,一键生成图谱是亮点 |
需要说明的是,这个对比不代表谁好谁坏,而是基于我的场景。Dify 和 FastGPT 更适合做"从知识库出发构建 AI 应用",比如带工作流、带多轮对话的智能客服;而我要的是"把文档喂进去,把问答跑起来",WeKnora 的定位最贴合。
最终让我下决心的三个点是:
- 一键生成知识图谱,这是 WeKnora 区别于普通 ChatPDF 工具的核心功能,我后面会专门讲。
- 内置多个文档解析器,对中文文档的格式兼容做得不错。
- 腾讯微信团队维护,社区活跃度还可以,Issues 响应速度比预期快。
选型这件事,我的建议永远是:不要看谁功能多,要看谁匹配你的场景最自然。你如果是想快速做一个带业务逻辑的对话机器人,Dify 可能更顺手;你如果像我一样,核心需求是"内部文档库问答",WeKnora 值得多花一些时间。
2. 部署前必须想清楚的三件事:硬件、模型与组件依赖
部署 WeKnora 之前,我原本以为它就是"拉一个镜像、起一个容器"的事。真正开始以后才发现,这个系统对硬件、模型和周边组件都有一定要求,顺序搞错了会浪费很多时间。
2.1 硬件配置:没有大显存显卡也能跑
我用来部署的机器配置很普通,并不是一台专门的 AI 服务器:
- CPU:8 核
- 内存:32 GB
- 显卡:一张 8 GB 显存的旧卡,支持 CUDA
- 磁盘:系统盘之外单独留了 200 GB
先说结论:如果你像我一样有一张 8 GB 显存的卡,跑 7B 量级的量化模型是够用的。如果完全没有 GPU,纯 CPU 推理也能跑,但问答速度会明显变慢,7B 模型在 CPU 上回答一个问题可能要等十几秒甚至更久,体验上会比较着急。
内存方面,WeKnora 全家桶本身就会吃掉不少。我完整部署完之后观察下来,Elasticsearch、Milvus、MySQL、Redis、MinIO 这些基础组件加在一起,常驻内存大约 8~12 GB,详见后面第 6 章的实测数据。所以如果你机器只有 16 GB 内存,会比较紧张,建议至少 16 GB,32 GB 更从容。
磁盘空间也是容易忽略的。模型文件、文档对象存储、向量索引、知识图谱数据都会占空间。我拉了两个大模型加一个 Embedding 模型,光模型文件就占了几十 GB。磁盘建议按 100 GB 起步来留。
2.2 大模型选型:Ollama 拉模型之前的默认配置检查
本地大模型我选的是 Ollama,原因很简单:安装方便,模型管理简单,而且提供了一个 OpenAI 兼容的接口,WeKnora 可以直接用这个接口,不需要额外写模型服务代码。
我实际拉了两个模型:
- 基础问答模型:qwen2.5:7b-instruct,主要用于生成回答和抽取知识图谱实体关系
- Embedding 模型:bge-m3,用于文档向量化
如果你的显存有 16 GB 以上,可以考虑 qwen2.5:14b,回答质量会更好一些。8 GB 显存就老老实实用 7B 量化版,配合 WebUI 用下来,日常问答的准确率已经可以接受。
关于 Ollama 的默认配置,这里有个特别容易被忽略的点:Ollama 默认只监听 127.0.0.1,也就是说只有本机能访问。但 WeKnora 跑在 Docker 容器里,它要访问宿主机的 Ollama 服务,就必须让 Ollama 监听所有网卡。
在 Linux 上我是这样改的:
# 编辑 ollama 服务的环境变量 sudo systemctl edit ollama然后在打开的编辑文件里加上:
[Service] Environment="OLLAMA_HOST=0.0.0.0:11434"保存后重启服务:
sudo systemctl restart ollama改完之后用下面的命令验证一下是否监听正常:
curl http://宿主机IP:11434/api/tags如果你是在 Windows 上部署,同样的逻辑,在 Ollama 的启动环境变量里加上 OLLAMA_HOST=0.0.0.0:11434 即可。这一步没做对,后面 WeKnora 填模型服务地址时,容器内是永远连不通的。
2.3 WeKnora 用到的几个基础设施组件,以及每个组件干什么
第一次打开 WeKnora 的 docker-compose 文件时,我愣了一下——里面不止一个服务,而是十几个容器。要说完全不慌是假的,但搞清楚每个组件是干什么的之后,整个系统的设计思路就清晰了。
我当时梳理出来的核心组件如下:
- Elasticsearch:负责关键词全文检索,回答时会把问题里的关键词在原始文档里做精确匹配。
- Milvus:负责向量检索,把问题和文档都变成向量,用余弦相似度来找"语义上最接近"的内容。
- MySQL:保存用户、知识库元数据、文档列表、问答日志等结构化数据。
- Redis:做缓存和异步任务的消息队列,知识库解析和索引的异步任务都会经过它。
- MinIO:对象存储,存放上传的原始文档、生成的图片和解析后的临时文件。
- 图谱存储与索引服务:支撑知识图谱实体、关系的存储和查询,这一块是 WeKnora 的特色。
- WeKnora 自身的 API 服务、任务调度服务和前端页面:分别对应后端接口、异步任务处理和用户界面。
这些组件为什么一个都不能少?因为 WeKnora 的检索策略是"混合检索":Elasticsearch 负责关键词召回,Milvus 负责语义向量召回,两者把结果汇合后再经过重排序模型(Rerank)统一打分,最终给大模型一个最优的上下文片段。如果要砍掉任何一块,检索质量都会明显下降。
理解这个组件关系还有一个好处:排查问题的时候,你可以很快判断是哪个环节出的问题。比如上传文档后一直显示"解析中",大概率是任务队列或者 MinIO 的问题;搜索时能搜到小部分相关内容,但语义相关度不高,问题多半在 Embedding 模型的选择;如果问答引用片段文不对题,可能要检查 Rerank 模型是否生效。
3. Docker Compose 部署实录:从拉镜像到服务全部起来
这一章是流水账式的实操记录。WeKnora 官方提供了 Docker Compose 的部署方式,我的建议是优先用这个方式,比源码部署省心很多,尤其是你要同时管理一堆中间件的时候。
3.1 配置文件改动:我只动了这几个地方
拉取项目之后,进入部署目录,先看 .env 文件和 docker-compose.yml。默认配置能跑,但至少三个地方我做了调整。
第一个是端口映射。我的宿主机上已经有本地的 MySQL 和 Redis 在跑,默认映射的 3306、6379 端口肯定会冲突。我把 compose 文件里宿主机侧端口改成了不常用的高位端口,比如 13306、16379,容器内部的端口不动。这样既不影响 WeKnora 内部通信,也不影响宿主机上已有的服务。
第二个是增加 host.docker.internal 支持。前面提到 Ollama 跑在宿主机上,WeKnora 容器要访问它,在 Linux 下默认没有 Docker Desktop 那样自动生成的 host.docker.internal 域名,需要对每个 WeKnora 相关服务添加额外的主机映射。我在 compose 文件的对应服务下加了一段:
extra_hosts: - "host.docker.internal:host-gateway"这样容器里就能通过 host.docker.internal 这个域名访问宿主机服务了。这是很多人在 Linux 环境下部署 WeKnora 时最容易卡住的地方。
第三个是持久化目录挂载。默认的 compose 文件通常会用数据卷,但为了直观备份,我把 MySQL、MinIO、Elasticsearch 的数据目录映射到了宿主机的一个特定文件夹下,后续要做备份或者排查缓存问题会方便得多。
3.2 启动顺序与服务健康检查:哪些报错可以忽略
确认配置没问题后,执行:
docker compose up -d第一次启动会拉取大量镜像,没有配镜像加速器的话可能要等很久。我是提前在 /etc/docker/daemon.json 里配置了加速地址(如果用的是国内云服务器,一般厂商会自带加速),这一步卡壳的话整个部署体验会非常差。
容器全部启动之后,不要急着打开浏览器。第一次启动时中间件之间是乱序的,Elasticsearch 启动最慢,可能需要一两分钟才能被其他服务连上。这个阶段我看到的报错基本都是:
connection refused connect: connection refused - trying to connect to elasticsearch第一次看到这个报错别慌,它不是配置错误,是依赖服务没就绪。正确的观察方式是单独看 WeKnora 后端服务的日志:
docker compose logs -f weknora-server等到日志里不再刷连接报错,并且能看到类似"应用启动成功"的提示,前端页面才能正常访问。我在浏览器里访问前端地址后,看到登录页面正常渲染,才敢说部署这一步基本过了。
3.3 我踩过的坑:容器间网络、端口占用、minio 桶权限
这一节是我最想写的部分,因为里面每一个坑都花了我不少时间排查。
第一个坑是容器间网络不通。我把服务全部起来后,后端日志一直报 MinIO 连接超时。后来发现是我在自定义网络配置时,改了 compose 文件里的网络声明,导致部分旧容器还挂在默认网络上,新容器在我自定义网络上,两个网络互相隔离。解决办法是把所有服务放到同一个网络里,或者干脆清掉 docker compose down 之后再重新 up,让全部容器按同一次配置创建。
第二个坑是端口占用。除了宿主机侧的端口冲突之外,我还遇到了容器内部端口被占用的情况。有一次我同时起了两个 WeKnora 项目目录,两套 compose 都往同一个自定义网络上注册了同名服务,导致服务名解析混乱。这个只能手动停掉一套,问题瞬间消失。
第三个坑是 MinIO 桶权限。WeKnora 初始化时会自动创建需要的桶和访问密钥,但如果 .env 文件里改了默认的 Access Key / Secret Key,或者重新创建了 MinIO 数据卷,老桶的权限信息就对不上了。表现就是前端能登录,但上传文档时一直转圈,最终失败。排查方法很简单:打开 MinIO 控制台,检查对应桶是否存在,检查后端配置的密钥和桶里的访问策略是否一致。我后来干脆把 MinIO 的数据卷全部清掉,让系统重新初始化,这个问题就再没出现过。
还有一个不算坑但值得提的事:WeKnora 的镜像体积不小,十几个容器加一块,光镜像可能就占了十几 GB。如果你磁盘不够,一定要提前清理。我当时就是临时清了 Docker 缓存才顺利拉完。
4. 接入 Ollama 本地大模型:让问答系统长脑子的关键步骤
部署只是骨架,接入模型才算是给系统长上脑子。这一章是比较容易让人困惑的地方,因为 WeKnora 需要配置的不是一个模型,而是三类模型服务:基础对话模型、Embedding 模型、Rerank 重排序模型。
4.1 在 WeKnora 里配置基础模型服务
登录后台之后,找到模型服务或模型管理相关的配置页面。不同版本的 WeKnora UI 叫法不太一样,有的叫"模型服务",有的叫"基础模型配置",但逻辑是统一的。
我配置基础模型服务时,服务类型选了 OpenAI 兼容类型,因为 Ollama 默认提供了 /v1 这个 OpenAI 兼容接口,这样 WeKnora 不用为 Ollama 做特殊适配。具体参数如下:
- API 地址:http://host.docker.internal:11434/v1
- API Key:随便填一个本地字符串,这里不做真实鉴权,但不能留空
- 模型名称:qwen2.5:7b-instruct
保存后先点测试连接。如果提示失败,优先检查前面提到的 OLLAMA_HOST 是否监听 0.0.0.0,以及容器里的 host.docker.internal 映射是否生效。
一个我实测下来很有效的排查命令是在 WeKnora 容器内直接 curl 宿主机的 Ollama 接口:
docker compose exec weknora-server curl http://host.docker.internal:11434/v1/models如果这条命令通,说明网络层面没问题,问题多半出在配置信息上;如果这条命令不通,说明容器到宿主机之间没通。
4.2 Embedding 模型的选择:为什么我最终用了 bge-m3
Embedding 模型负责把文档切成片段后变成向量,系统依赖这些向量做语义检索。选择这个模型时,我重点考虑了三方面:中文效果、切分长度、本地是否好跑。
我最初试过 text2vec-large-chinese,中文效果不错,但对长文本的支持不够理想,文档切太碎会导致检索上下文丢失。后来换成 bge-m3,它支持最长 8192 token 的输入,对长文档更友好,而且是多语言模型,中英文混合的技术文档也能处理得更好。向量维度是 1024,在 8 GB 显存上跑完全没问题。
在 Ollama 上拉取这个模型很简单:
ollama pull bge-m3之后回到 WeKnora 的模型配置页面,把 Embedding 模型的地址同样指向 Ollama 的 OpenAI 兼容接口,模型名填 bge-m3。如果你选的 Embedding 模型不支持通过 OpenAI 兼容接口调用,那就要额外部署一个 Embedding 服务组件,官方文档里对这种部署方式有单独说明,但通常没必要。
选 Embedding 模型时我的一个经验是:不要只盯着榜单分数。你还要考虑它跟你的文档切分策略是否匹配,是否支持长文本,以及和你的 Rerank 模型是否搭配协调。如果 Embedding 向量是 1024 维,但你的 Rerank 模型期望的是另一套打分逻辑,最终效果未必好。
4.3 实测:知识库问答从报错到跑通的完整链路
把基础模型和 Embedding 模型都配置好之后,我建了一个测试知识库,上传了几篇产品文档,等待系统完成解析和向量化,然后开始提问。
第一个问题刚发出去就报错了。错误信息大致是模型不存在或模型名配置错误。我当时的排查顺序是:
- 先看 Ollama 本机是否真的有这个模型:
ollama list - 再到 WeKnora 的模型配置里确认模型名完全一致,包括版本号。
- 再到容器内 curl Ollama 接口,确认接口确实返回了该模型信息。
最终发现是我在 WeKnora 里填模型名时少写了一个标签版本号,系统默认去找完全匹配的模型名,就报错了。改对之后重新测试,问答流程立刻通了。
跑通之后的效果让我觉得前面的折腾都值了。这里放两个我实际测试的问答对,供大家感受一下输出质量:
问:我们产品的故障切换机制是如何工作的?
答:根据《XX 产品说明文档》第 3 章内容,故障切换由主备节点的健康检查触发,当主节点连续三次心跳超时后,备用节点会接管虚拟 IP,整个过程预计在 30 秒内完成。详细流程见文档附录。
问:A 项目之前遗留了哪些未关闭的风险?
答:知识库中与 A 项目相关的两个文档提到了风险点:一是接口兼容性测试未完成(来源:A 项目周报),二是部署脚本在高并发场景下存在超时隐患(来源:A 项目技术评审纪要)。目前未查到已关闭的记录。
第一条是纯向量检索就能回答的问题,第二条带上了"两个文档里都有提到"的跨文档信息,明显是混合检索和知识图谱在后端起了作用。
5. 知识图谱与混合检索:WeKnora 区别于普通 ChatPDF 的地方
跑通问答只是及格线。我选 WeKnora 的一个核心原因就是它有一键生成知识图谱和混合检索能力,这一章我想单独展开,因为这是它区别于普通 ChatPDF 工具的真正的分水岭。
5.1 一键生成知识图谱的实际体验
在 WeKnora 的知识库管理页面里,上传完文档并完成解析后,会有一个"图谱生成"或者"构建图谱"的操作入口。点击之后,系统会调用大模型对文档内容进行实体抽取和关系识别,把人物、组织、项目代号、技术概念、指标项等实体,以及它们之间的关系自动整理出来。
这个过程不是实时的,它走的是异步任务。任务执行期间,页面上能看到进度,但层级比较深,我一开始没找到入口,后来在任务列表里才看到生成状态。
生成完的图谱可以从图谱展示页面直观查看。我第一次看到系统从一个文档集里抽出了"产品 A — 依赖 — 服务 B — 负责人 — 张三"这样的关系链时,确实觉得这个功能挺有用的。
但这里我也要如实说一个局限:知识图谱的质量上限,基本取决于你本地大模型的能力。用 7B 级别的模型做实体关系抽取,能抽出一些明显的关系,但复杂语义和隐含关系经常抽不完整,或者抽出的关系不够精确。如果你的机器能跑 14B 甚至更大的模型,图谱质量会明显上一个台阶。所以我对图谱功能的态度是:别指望它一开始就完美,把它当做一个辅助筛选和跨文档联动的工具,效果就已经很好了。
5.2 混合检索调参:召回结果不理想时先调哪里
混合检索是 WeKnora 的检索策略核心,简单说就是关键词检索和向量检索双路并行,各自召回一批候选片段,再经过 Rerank 模型把两条路的候选统一打分,最后把得分最高的一批片段交给大模型生成答案。
部署完成后我有一段时间觉得回答效果不稳定:有的问题能命中,换一种问法就答非所问。后来我发现问题主要出在参数配置上,而不是模型上。
在 WeKnora 的检索配置里,有几个参数值得关注:
- 两路召回的 top_k:决定各自返回多少候选片段。
- score 阈值:太低会有不少无关片段混进来,太高又有可能召回不足。
- Rerank 开关:确认是否真的启用了重排序模型。
我的调参经验是:当回答效果不好的时候,不要直接去调大模型的 prompt,先去看"系统实际检索到了什么片段"。WeKnora 在回答详情里一般会显示引用的原文片段,这时候你一眼就能判断是不是检索阶段出了问题。
如果检索引用的片段明显不对,优先把 top_k 调大,先让候选池变大,再看 Rerank 是否把正确片段排到了前面。如果正确片段本身都没进入候选池,那就是召回环节的问题,可能出在 Embedding 模型对中文语义理解不够,或者关键词分词和你的文档写法的匹配度不高。
这里我想强调一个反直觉的经验:回答效果不好时,大部分人第一反应是去换大模型,但实际上多数问题出在检索链路。大模型只是"拿着你给的材料回答问题",你给的材料不对,再强的模型也答不对。所以遇到效果问题,优先调检索,其次再考虑换模型。
6. 日常使用与维护建议:数据备份、动态更新与资源监控
部署完成和跑通问答只是第一步。系统真正进入日常使用之后,还会遇到一系列维护层面的问题。这章是我在持续使用过程里总结的实操经验,包括文档更新、备份策略、资源占用观察。
6.1 文档更新后索引不同步怎么办
知识库最常遇到的问题之一,就是文档更新后问答结果仍然是旧内容。我第一次遇到这个问题时,直接在页面里把旧文档删除,再重新上传新文档,结果问答还是引用旧文档内容。排查了一圈才发现,文档虽然删了,但向量索引和关键词索引可能没有被同步清理干净。
正确的操作方式是:
- 上传新版本前,先在知识库里把旧文件移除。
- 确认系统完成了索引清理任务后,再上传新文件。
- 如果更新后依旧引用旧内容,可以手动触发重建索引或任务重跑。
这个问题的根因,通常在于异步任务的执行顺序。删除和重新上传如果连续操作太快,系统可能会因为任务并发导致索引状态不一致。我的建议是:文档更新时,给后台任务一点时间,不要频繁连续操作。
6.2 资源占用观察:一套服务到底吃了多少内存
对于本地部署的系统来说,了解常驻资源占用很重要,尤其是你要决定跑在什么规格的机器上。我在部署完成后做过一次记录,实测数据大致如下,不同的版本和数据量会略有浮动:
| 组件 | 内存占用(约) | 说明 |
|---|---|---|
| Elasticsearch | 2~4 GB | 受 JVM 堆大小影响,可调整 |
| Milvus Standalone | 1.5~3 GB | 向量检索核心 |
| MySQL | 0.5~1 GB | 数据量小的话占用不高 |
| Redis | 0.3~0.5 GB | 缓存与异步队列 |
| MinIO | 0.3~0.8 GB | 对象存储 |
| WeKnora 后端与调度服务 | 2~4 GB | 多进程常驻 |
| 前端 Nginx | 0.2~0.5 GB | 流量不大时很低 |
合计下来,我这台机器的常驻内存大约 10~12 GB。如果算上 Ollama 加载模型占用的显存和内存,16 GB 内存的机器确实会比较紧张,32 GB 会更从容。
如果你的机器内存实在紧张,有几个可调整的方向:
- 修改 Elasticsearch 的 JVM 堆大小。我当时在 compose 配置里加了 ES_JAVA_OPTS,把堆从默认值压到了 1~2 GB,效果很明显。
- 暂时关闭知识图谱相关组件。不用图谱功能时,可以把图谱调度任务停掉,但这样也就失去了 WeKnora 的灵魂功能,属于万不得已的选择。
- 减少查询并发。这个系统本来就不是为高并发设计的,团队内部使用场景下限制同时在线人数即可。
6.3 我的本地化部署建议
最后简单给几条随着使用积累下来的建议。
一是数据备份要有节奏。WeKnora 的数据分散在 MySQL、MinIO、Elasticsearch、Milvus 以及图谱存储里,备份时不要只盯着一处。我目前的习惯是每周停掉相关容器后,统一拷贝宿主机挂载的数据目录。因为数据库引擎运行状态下直接拷贝文件,可能会拿到不一致的数据。
二是升级前先看 release notes。WeKnora 迭代速度不快,但每次升级涉及多个组件的版本同步,版本跨度大了容易出现兼容问题。我的做法是升级前记录当前镜像版本 tag,升级后如果出现异常,可以快速回退。
三是不要忽视日志文件大小。容器跑久了,日志文件会越滚越大,如果磁盘本来就紧张,记得配置日志轮转。我在部署目录里后来加了 logrotate 规则,定期清理容器日志,这个问题才不再操心。
部署本地知识库问答系统,不是一条简单的向上命令就能解决的事。你需要理顺组件依赖、模型接入、索引调度和数据备份这一整条链路。而我个人的体会是:一旦这套链路贯通,日常查文档的效率提升是肉眼可见的。希望这篇 WeKnora 部署实录能帮你把踩坑的时间省下来,直接跑到系统真正为你工作的那一天。