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 硬件和系统的最低要求
我拿两台机器试过,配置差别挺大,这里给个参考:
| 配置项 | 最低能跑 | 推荐配置 | 说明 |
|---|---|---|---|
| CPU | 4 核 | 8 核以上 | 文档解析和向量化吃 CPU |
| 内存 | 8 GB | 16 GB 以上 | 嵌入模型和大模型如果本地跑,内存需求翻倍 |
| 磁盘 | 40 GB | 100 GB SSD | 镜像、向量数据、文档原文都占空间 |
| 系统 | Ubuntu 20.04 | Ubuntu 22.04 | 其他 Linux 发行版也行,注意 Docker 版本 |
| Docker | 20.10+ | 24.0+ | 低版本 compose 语法可能不兼容 |
| Docker Compose | v2 | v2.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 会增加。目前的做法是删掉旧文档重新上传,但这样会丢失历史记录。更好的做法是做增量更新,只处理变化的文档。这个需要自己写脚本,对比文档哈希值,只重新处理变了的。
我在实际使用中最大的体会是,这套系统的效果,三分靠系统,七分靠文档。文档整理得好,切分策略对,问答质量就高。文档一团糟,再好的模型也白搭。所以部署之前,先花时间把文档理清楚,比后面调参有用得多。