☰
腾讯WeKnora开源知识库本地部署实战:Docker Compose搭建RAG问答系统
2026/10/5 8:46:14 网站建设 项目流程

1. 为什么我盯上了 WeKnora 这套开源知识库

第一次看到 WeKnora 这个项目,是在翻腾讯技术团队的开源仓库列表时。当时我正帮一个做企业内训的朋友找方案,他们的需求很具体:把几十份产品手册、FAQ 文档、历史工单记录整合起来,让新员工能像聊天一样问问题,而不是在文件夹里翻半天。市面上的 SaaS 知识库产品不少,但数据要传到别人服务器上,朋友那边合规过不了,所以必须本地部署。

WeKnora 正好卡在这个点上。它是腾讯微信团队开源的一套 AI 知识库问答系统,核心能力是把文档灌进去,自动切分、向量化、建索引,然后通过对话界面回答问题。整个东西用 Docker Compose 编排,一台普通配置的服务器就能跑起来。我实测下来,从零到能问答,大概花了两个小时,其中大部分时间还是在等镜像下载。

这篇文章我打算把整个部署过程、踩过的坑、参数怎么调、后面怎么扩展,全部摊开讲一遍。适合两类人看:一类是手上有文档资产、想搭内部问答系统但不想碰云服务的开发者;另一类是刚接触 RAG(检索增强生成)这套东西,想找个真实项目练手的技术爱好者。不需要你懂深度学习,但基本的 Linux 命令和 Docker 概念得有,不然下面有些操作会卡住。

先说清楚 WeKnora 到底解决什么问题。传统的关键词搜索,你搜"退款流程",它只能匹配到包含这四个字的文档段落,换个说法"钱怎么退回来"就抓瞎了。WeKnora 走的是语义检索路线,把你的问题转成向量,去向量库里找语义最接近的文档片段,再交给大模型组织成自然语言回答。这套流程就是现在常说的 RAG。它的价值在于,你不需要重新训练模型,只要把文档喂进去,它就能基于你的私有资料回答问题,而且答案有出处,能点开看原文。

2. 部署前的整体设计与选型考量

2.1 这套系统由哪些部件拼起来

WeKnora 不是单一服务,它是一组容器协同工作。拆开看,主要有这么几块:

  • 前端界面:提供对话窗口和文档管理页面,你上传文档、提问都在这里操作。
  • 后端服务:处理文档解析、切分、调用嵌入模型、检索、调用大模型生成回答,是整个系统的大脑。
  • 向量数据库:存文档切片的向量表示,检索时从这里找相似内容。WeKnora 默认支持多种向量库,社区里用得比较多的是腾讯云 VectorDB 的本地替代方案,或者直接用内置的轻量级存储。
  • 关系型数据库:存用户、会话、文档元数据这些结构化信息,通常是 PostgreSQL 或 MySQL。
  • 嵌入模型服务:把文本转成向量。可以用 API 调用远程模型,也可以本地跑一个嵌入模型服务。
  • 大语言模型服务:最终生成回答。同样可以接远程 API,也可以本地部署。

这六块通过 Docker Compose 的 network 连在一起,互相通过服务名通信。理解这个结构很重要,因为后面出问题的时候,你得知道是哪个环节挂了。

2.2 为什么选 Docker Compose 而不是别的

有人会问,为什么不直接装在一台机器上,或者用 K8s。我的判断是这样的:WeKnora 的定位是中小规模知识库,文档量在几千到几万份这个量级,单机 Docker Compose 完全扛得住。K8s 那套东西运维成本太高,为了一个内部问答系统搭一套集群,属于杀鸡用牛刀。直接裸装更不行,Python 依赖、Node 依赖、数据库版本冲突,能把人折腾疯。

Docker Compose 的好处是,所有依赖版本都锁在镜像里,换台机器只要把 compose 文件和配置拷过去,一条命令就能拉起一模一样的环境。这对需要在内网多台机器部署的场景特别友好。而且 WeKnora 官方提供的 compose 文件已经把服务依赖关系、网络、卷挂载都写好了,你只需要改几个环境变量。

不过有个前提,你的机器得能拉取镜像。如果是完全隔离的内网环境,需要提前在有网的机器上把镜像拉下来,导出成 tar 包,再拷进去加载。这个后面会讲具体操作。

2.3 硬件和系统的最低要求

我拿两台机器试过,配置差别挺大,这里给个参考:

配置项最低能跑推荐配置说明
CPU4 核8 核以上文档解析和向量化吃 CPU
内存8 GB16 GB 以上嵌入模型和大模型如果本地跑,内存需求翻倍
磁盘40 GB100 GB SSD镜像、向量数据、文档原文都占空间
系统Ubuntu 20.04Ubuntu 22.04其他 Linux 发行版也行,注意 Docker 版本
Docker20.10+24.0+低版本 compose 语法可能不兼容
Docker Composev2v2.20+v1 已经停止维护,别用了

如果嵌入模型和大模型都走远程 API,那 8 GB 内存的机器就能跑起来。但如果想完全本地化,嵌入模型比如 BGE-M3 本地跑起来大概占 2-3 GB 内存,大模型如果是 7B 量化版本,至少再留 6-8 GB。所以本地全栈方案,16 GB 是起步线。

磁盘方面要注意,Docker 的镜像层和卷会越积越多。我见过有人跑了三个月,磁盘被日志和旧镜像撑爆的。建议单独挂一块数据盘给 Docker 用,或者定期清理。

3. 核心细节解析与实操要点

3.1 镜像拉取与网络问题的处理

国内拉 Docker Hub 的镜像,速度是个绕不开的问题。WeKnora 的 compose 文件里引用的镜像来源比较多,有 Docker Hub 的,也有其他仓库的。我的做法是分两步:先看 compose 文件里列了哪些镜像,然后逐个确认能不能拉下来。

具体操作,先进入项目目录,找到docker-compose.yml,用这个命令把里面引用的镜像列出来:

grep -E "image:" docker-compose.yml | awk '{print $2}' | sort -u

拿到镜像列表后,可以配置镜像加速器。在/etc/docker/daemon.json里加上:

{ "registry-mirrors": [ "https://mirror.ccs.tencentyun.com", "https://docker.mirrors.ustc.edu.cn" ] }

改完重启 Docker:

sudo systemctl daemon-reload sudo systemctl restart docker

注意:镜像加速器地址会变动,用之前先确认当前可用的地址。如果加速器不生效,就老老实实一个个拉,或者找有网的机器导出镜像再传进来。

导出镜像的命令是这样的:

docker save -o weknora-images.tar image1:tag image2:tag

传到内网机器后加载:

docker load -i weknora-images.tar

这一步看着笨,但在隔离环境里是最稳的办法。我试过用代理,但 compose 里有些服务不走代理配置,反而更乱。

3.2 环境变量的配置逻辑

WeKnora 的配置集中在.env文件里。官方一般会给一个.env.example,你需要复制成.env再改。这里面有几个关键变量必须搞清楚,不然服务起不来或者起来了但功能不正常。

数据库连接相关:包括数据库地址、端口、用户名、密码、库名。这些在 compose 文件里通常有对应的服务定义,你改.env里的值,compose 启动时会注入到容器里。要注意的是,如果数据库服务也是 compose 拉起来的,那地址应该填服务名,比如postgres或mysql,而不是localhost。因为容器里的 localhost 指的是容器自己,不是宿主机。

向量库相关:如果用的是内置向量存储,一般不需要额外配置。如果接外部向量库,需要填地址和认证信息。这里有个坑,向量库的维度必须和嵌入模型输出的维度一致。比如 BGE-M3 输出的是 1024 维,那向量库的集合创建时就得指定 1024 维,填错了会报维度不匹配的错误。

模型服务相关:这里分嵌入模型和生成模型两块。如果走远程 API,需要填 API 地址和密钥。如果本地部署,需要填本地服务的地址。我建议第一次部署先用远程 API 把流程跑通,确认系统能正常问答了,再换成本地模型。这样出问题的时候,能快速定位是系统本身的问题还是模型服务的问题。

文件存储相关:上传的文档存哪里,解析后的中间文件存哪里。默认是存在容器内的卷里,如果文档量大,建议挂载到宿主机目录,方便备份和迁移。

配置完.env后,不要急着docker compose up,先用这个命令检查一下 compose 文件语法:

docker compose config

它会把你配置的变量展开,显示最终生效的完整配置。如果某个变量没填,这里会显示为空,一眼就能看出来。

3.3 文档解析与切分策略

文档灌进去之后,WeKnora 会先解析,再切分,再向量化。这三步里,切分策略对最终问答质量影响最大。

解析这一步,系统要处理 PDF、Word、Markdown、纯文本等格式。PDF 是最麻烦的,尤其是扫描件,需要 OCR。WeKnora 对文本型 PDF 支持比较好,扫描件的话得先自己用 OCR 工具转一遍。我试过直接传扫描版 PDF,解析出来是空的,因为里面没有文字层。

切分这一步,系统会把长文档切成一段一段的。切得太碎,上下文丢失,回答不完整;切得太大,检索精度下降,因为一个片段里混了太多主题。WeKnora 默认的切分是按固定长度加重叠,比如每段 500 字,相邻段重叠 50 字。这个参数在配置里可以调。

我的经验是,技术文档按 300-500 字切比较合适,因为技术概念通常集中在一两段里。如果是叙事性的内容,比如案例集,可以切大一点,800-1000 字,保留完整情节。重叠部分的作用是防止一个完整意思被切断,但重叠太多会导致检索结果重复,一般设成片段长度的 10% 左右就行。

还有一个细节,切分的时候最好保留文档的标题层级。比如一个 Markdown 文档,二级标题下的内容应该作为一个切分单元,而不是机械地按字数切。WeKnora 对 Markdown 的支持比较好,能识别标题结构。所以如果你的原始文档是 Word 或 PDF,建议先转成 Markdown 再上传,切分效果会好很多。

3.4 嵌入模型的选择与本地部署

嵌入模型决定了检索的准确度。WeKnora 默认可能用的是某个通用嵌入模型,但你可以换。目前中文场景下,BGE 系列是社区里反馈比较好的,BGE-M3 支持多语言,输出 1024 维向量,对中英文混合的文档很友好。

本地部署 BGE-M3,最简单的办法是用一个专门的嵌入模型服务镜像。compose 文件里加一个服务:

embedding: image: your-embedding-service:latest ports: - "9997:9997" volumes: - ./models:/models environment: - MODEL_NAME=BAAI/bge-m3

然后在 WeKnora 的配置里,把嵌入模型地址指向http://embedding:9997。注意这里用的是服务名,不是 localhost。

提示:嵌入模型服务启动后会加载模型到内存,第一次启动比较慢,要等模型加载完再调接口。可以用curl http://localhost:9997/health检查服务是否就绪。

如果机器内存不够,或者不想本地跑,也可以用远程嵌入 API。但要注意,远程 API 有调用频率限制和费用,文档量大的时候成本不低。而且数据要传到外部,合规上可能有问题。所以能本地跑就本地跑。

3.5 大模型接入的几种方式

生成回答的大模型,接入方式更灵活。WeKnora 支持 OpenAI 兼容的接口,这意味着只要你的模型服务提供这个接口,就能接进来。

几种常见方案:

  • 远程 API:配置最简单,填个地址和密钥就行。缺点是数据出本地,有费用。
  • 本地部署开源模型:比如用 Ollama 跑一个 7B 或 13B 的模型,提供 OpenAI 兼容接口。优点是数据不出本地,缺点是生成速度取决于硬件。
  • 本地部署推理框架:比如用 vLLM 或 TGI 部署,性能比 Ollama 好,但配置复杂一些。

我建议先用远程 API 把系统跑通,确认问答流程没问题,再换本地模型。换的时候只需要改配置里的模型地址和模型名称,其他不用动。

本地模型的选择上,7B 级别的模型在 16 GB 内存的机器上能跑,但生成速度大概每秒几个字,体验一般。如果追求速度,可以用量化版本,比如 4-bit 量化,内存占用减半,速度提升明显,但回答质量会略有下降。这个取舍看你的场景,内部知识库问答对回答质量要求没那么高的话,量化版本够用了。

4. 实操过程与核心环节实现

4.1 从零开始的完整部署流程

假设你拿到了一台干净的 Ubuntu 22.04 机器,下面是我实测走通的完整流程。

第一步,装 Docker 和 Docker Compose。

sudo apt update sudo apt install -y ca-certificates curl gnupg sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod a+r /etc/apt/keyrings/docker.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo $VERSION_CODENAME) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

装完后验证:

docker --version docker compose version

两个命令都能输出版本号,说明装好了。

第二步,获取 WeKnora 源码。

git clone https://github.com/Tencent/WeKnora.git cd WeKnora

如果 git 拉取慢,可以用镜像源,或者直接下载 zip 包。

第三步,配置环境变量。

cp .env.example .env vim .env

重点改这几项:数据库密码、向量库配置、模型服务地址。第一次跑,模型服务可以先填远程 API 的地址和密钥。

第四步,启动服务。

docker compose up -d

-d是后台运行。启动后看日志:

docker compose logs -f

看到所有服务都输出 ready 或 listening 之类的字样,说明启动成功。

第五步,验证。

浏览器打开http://你的机器IP:端口,默认端口看 compose 文件里的映射。能看到登录页或者对话界面,就说明前端起来了。注册一个账号,上传一份测试文档,等它处理完,问一个问题试试。

4.2 文档上传与索引构建的现场记录

我拿一份 50 页的产品手册 PDF 做测试。上传后,系统显示"处理中",大概过了三分钟,状态变成"已完成"。这期间后台在做解析、切分、向量化。

处理完后,我在对话框里问了一个手册里的具体问题,比如"XX 功能的配置步骤是什么"。系统返回了答案,并且下面附了引用来源,点开能看到原文片段。这说明检索和生成链路是通的。

但我也遇到了一个问题:有些问题的回答不完整,只答了一半。排查后发现是切分粒度的问题。手册里有些步骤跨了两页,切分的时候被切断了,检索只召回了前半部分。解决办法是调整切分参数,增大重叠长度,或者把相关章节合并成一个文档再上传。

还有一个现象,问一些手册里没有的问题,系统会强行编一个答案。这是大模型的通病,叫幻觉。WeKnora 的应对方式是在 prompt 里加约束,让模型只根据检索到的内容回答,检索不到就说不知道。但这个约束不是百分百生效,所以关键场景下,答案还是需要人工复核。

4.3 参数调优的实际操作

系统跑起来之后,有几个参数值得调。

检索返回条数:默认可能是返回最相似的 3 条或 5 条片段。条数太少,可能漏掉关键信息;条数太多,会引入无关内容干扰生成。我的经验是,文档主题集中的话,3 条够用;文档主题分散的话,调到 5-8 条。这个在配置里叫top_k之类的名字。

相似度阈值:低于这个阈值的检索结果会被丢弃。设得太高,可能什么都检索不到;设得太低,会召回一堆不相关的内容。一般设在 0.5-0.7 之间,具体看嵌入模型的特性。可以先用默认值跑一批问题,看召回结果的质量,再微调。

生成温度:控制大模型输出的随机性。知识库问答场景,温度设低一点,比如 0.1-0.3,让回答更确定、更贴近原文。设太高的话,模型会自由发挥,容易偏离事实。

调参的时候,建议固定一批测试问题,每次改完参数重新跑一遍,对比回答质量。不要凭感觉调,要有对照。

4.4 数据备份与迁移的实操

知识库跑起来之后,数据就是资产了。备份分两块:数据库和文件存储。

数据库备份,如果用 PostgreSQL:

docker compose exec postgres pg_dump -U 用户名 库名 > backup.sql

文件存储备份,找到 compose 文件里挂载的卷,直接打包:

tar -czf files-backup.tar.gz /path/to/volume

迁移的时候,在新机器上先把服务停掉,恢复数据库和文件,再启动。注意数据库版本要一致,不然恢复可能失败。

提示:备份最好做成定时任务,用 cron 每天跑一次。备份文件存到另一台机器或者对象存储上,别跟原数据放一块,不然机器挂了全没了。

5. 常见问题与排查技巧实录

5.1 服务起不来的排查思路

docker compose up之后,如果某个服务一直重启,先用这个命令看状态:

docker compose ps

状态显示Restarting或Exit的,就是有问题的。然后看它的日志:

docker compose logs 服务名

常见的几类错误:

错误现象可能原因解决办法
数据库连接被拒绝数据库还没启动完,或密码不对等数据库就绪,检查 .env 里的密码
端口已被占用宿主机上已有服务占用端口改 compose 里的端口映射
镜像拉取失败网络问题或镜像不存在配置加速器或手动导入镜像
内存不足被 kill容器内存超限增加机器内存或限制容器内存
卷挂载权限错误宿主机目录权限不对chmod 或 chown 调整权限

我遇到过一次,后端服务一直重启,日志显示连不上向量库。查了半天发现是向量库服务启动比后端慢,后端启动时连不上就退出了。解决办法是在 compose 里给后端加depends_on和健康检查,等向量库就绪再启动后端。

5.2 问答质量差的优化方向

系统能跑,但回答质量不行,这是最常见的问题。排查方向按优先级排:

第一,看检索结果。很多系统有调试模式,能看到检索到了哪些片段。如果检索到的片段跟问题不相关,那问题出在检索环节。可能是嵌入模型不适合你的文档语言,或者切分粒度不对,或者相似度阈值设得不好。

第二,看 prompt。如果检索结果是对的,但生成的回答不对,那是 prompt 的问题。可以调整 prompt 模板,明确告诉模型"只根据以下内容回答"、"如果内容中没有答案,回答不知道"。

第三,看模型能力。如果 prompt 没问题,检索也没问题,但回答还是不行,那可能是模型本身能力不够。换一个更大的模型,或者换一个在中文问答上表现更好的模型。

第四,看文档质量。如果原始文档本身结构混乱、错别字多、信息重复,那再好的系统也救不了。这种情况得先整理文档,把过时的、重复的内容清理掉,再重新上传。

5.3 性能瓶颈的定位与处理

文档量大了之后,可能会感觉变慢。慢在哪,得定位。

上传文档慢,是解析和向量化慢。这两个都是 CPU 密集型操作。如果 CPU 核数少,可以限制同时处理的文档数量,排队处理。

问答慢,分两段:检索慢和生成慢。检索慢通常是向量库的问题,数据量大到一定程度,需要建索引。生成慢是大模型的问题,本地模型受限于硬件,远程 API 受限于网络和对方限流。

我实测下来,一万份文档以内,检索延迟在几百毫秒级别,感知不明显。超过这个量级,就得考虑向量库的索引优化了。WeKnora 支持的向量库一般都有索引配置,建好索引后检索速度会快很多。

5.4 几个容易忽略的细节

时区问题:容器默认可能是 UTC 时间,日志时间跟本地对不上。在 compose 里加TZ=Asia/Shanghai环境变量可以解决。

日志膨胀:Docker 容器的日志默认不限制大小,跑久了能把磁盘写满。在 daemon.json 里配置日志轮转:

{ "log-driver": "json-file", "log-opts": { "max-size": "100m", "max-file": "3" } }

会话清理:用户跟系统的对话记录会一直存着,时间长了数据库会变大。可以配置定期清理,或者只保留最近 N 天的记录。

模型更新:如果换了嵌入模型,之前建的向量索引就失效了,因为维度或语义空间变了。换模型后必须重新处理所有文档,这个要有心理准备。

6. 后续扩展的一些想法

这套系统跑通之后,能扩展的方向不少。比如接企业现有的账号体系,用 OIDC 做单点登录,这样员工不用单独注册账号。WeKnora 本身支持 OIDC 配置,填几个参数就能接上。

再比如,把问答入口嵌到现有的办公工具里。WeKnora 提供 API,可以自己写个前端调它的接口,做成一个聊天窗口挂在内部系统里。这样用户不用专门打开知识库页面,在平时用的工具里就能问。

还有一个方向是加多轮对话能力。现在的问答基本是一问一答,如果用户追问,系统不一定能理解上下文。这个需要在会话管理上做文章,把历史对话也作为上下文传给模型。WeKnora 的架构支持这个,但需要改一些代码。

文档更新也是个实际问题。产品手册会改版,FAQ 会增加。目前的做法是删掉旧文档重新上传,但这样会丢失历史记录。更好的做法是做增量更新,只处理变化的文档。这个需要自己写脚本,对比文档哈希值,只重新处理变了的。

我在实际使用中最大的体会是,这套系统的效果,三分靠系统,七分靠文档。文档整理得好,切分策略对,问答质量就高。文档一团糟,再好的模型也白搭。所以部署之前,先花时间把文档理清楚,比后面调参有用得多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询