起因
团队内部文档散在飞书、Confluence、几个 Git 仓库里,新人问问题永远是同一批。我想搭一个能自己部署、数据不出内网的知识库问答系统。
试过几个方案:Dify 功能全但偏重,RAGFlow 解析能力强但依赖多。后来看到 WeKnora,腾讯微信团队开源,定位就是文档理解 + 语义检索 + 问答,代码量不算大,适合拿来改。
这篇记录我从零部署到跑通的全过程。包括 Docker Compose 起服务、配模型、传文档、调检索参数。踩到的坑我都写清楚,没验证的地方我会标出来。
环境准备
我的机器:Ubuntu 22.04,16 核 32G 内存,一张 4090(24G 显存)。
WeKnora 官方仓库在 GitHub 上,直接 clone:
git clone https://github.com/Tencent/WeKnora.git
cd WeKnora
仓库根目录有 docker-compose.yml 和 .env.example。我实际用的版本是主分支某次提交,没有打 tag。这一点我建议你 clone 后先 git log -1 记一下 commit hash,因为主分支变动挺快。
复制环境变量文件:
cp .env.example .env
.env 里需要关注的几类配置:
| 配置项 | 作用 | 我的取值 |
|---|---|---|
| 数据库相关 | PostgreSQL / 向量库连接 | 用 compose 默认 |
| 模型 API 地址 | LLM 和 Embedding 的 endpoint | 指向本地 vLLM |
| 模型名称 | 具体调用的模型 | 见下文 |
| 存储路径 | 上传文档落盘位置 | 默认卷 |
具体变量名我就不逐字抄了,因为不同 commit 之间命名改过,你以自己 clone 到的 .env.example 为准。我踩的第一个坑就是照着某篇博客的变量名改,结果那个版本根本没有这个字段,服务起不来。
起服务
docker compose up -d
第一次拉镜像比较慢,主要是几个基础镜像体积大。起来之后:
docker compose ps
正常的话能看到数据库、后端、前端几个容器都是 running。前端默认映射到某个端口,我在 .env 里改成了 8080,浏览器打开 http://localhost:8080 能看到界面。
如果容器反复重启,先看日志:
docker compose logs -f backend
我遇到过一次后端起不来,日志里是连不上数据库。原因是我本地 5432 端口已经被另一个项目的 PostgreSQL 占了,compose 的端口映射冲突。改掉宿主机映射端口就好。这个坑很常见,建议起服务前先 ss -lntp | grep 5432 看一眼。
配置模型
WeKnora 的问答链路需要两类模型:
- LLM:负责最终生成回答
- Embedding:负责把文档切片和 query 转成向量
我用本地 vLLM 起了一个 OpenAI 兼容接口的服务。LLM 用 Qwen2.5-7B-Instruct,Embedding 用 bge-m3。
# 起 LLM
python -m vllm.entrypoints.openai.api_server \
--model Qwen/Qwen2.5-7B-Instruct \
--port 8000
# 起 Embedding
python -m vllm.entrypoints.openai.api_server \
--model BAAI/bge-m3 \
--port 8001
然后在 WeKnora 界面或者 .env 里把 endpoint 填进去。填的时候注意:vLLM 的 OpenAI 兼容接口路径是 /v1,base url 要写到 /v1 这一层。
验证模型通不通,最直接的办法是 curl:
curl http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "Qwen/Qwen2.5-7B-Instruct",
"messages": [{"role": "user", "content": "你好"}]
}'
能返回 JSON 就说明 LLM 侧没问题。Embedding 同理,打 /v1/embeddings。
这一步我卡了挺久。一开始 Embedding 一直报错,后来发现是 bge-m3 的输出维度和我之前用的模型不一样,而在建知识库的时候维度是写死在索引里的。如果你中途换 Embedding 模型,已经建好的知识库索引大概率要重建,因为维度对不上。这一点我是踩了才反应过来。
上传文档与解析
界面上建知识库,然后上传文档。我传了一堆 Markdown 和 PDF。
解析环节是这类系统最容易出问题的地方。WeKnora 支持多种格式,但 PDF 的解析质量取决于它内部用的解析器。我传的几份带表格的 PDF,解析出来的文本表格结构基本丢了,变成一堆挤在一起的文字。
Markdown 和纯文本解析得很干净,切片也合理。
所以我的实际做法是:能把源文档转成 Markdown 的,先转再传。 PDF 里如果是扫描件,那还得先 OCR,WeKnora 这块我没测,不确定它内部有没有集成 OCR。这一点我没有验证。
切片参数可以在界面上调,主要是 chunk size 和 overlap。我用的是一组比较常见的值:
| 参数 | 取值 | 说明 |
|---|---|---|
| chunk size | 512 | 字符数,不是 token |
| overlap | 50 | 相邻切片重叠 |
这两个值我调过几轮,512/50 对我的文档效果还行。但这跟文档类型强相关,代码文档和技术手册的最优值不一样,你得自己试。
检索与问答调参
WeKnora 走的是 RAG 常规流程:query 向量化 → 向量检索 top-k → 拼上下文 → LLM 生成。
界面上能调的参数主要是 top-k 和相似度阈值。我一开始 top-k 设 3,发现回答经常漏信息,因为相关内容被切在好几个 chunk 里,只取 3 个不够。后来调到 8,回答完整度明显好了,但噪声也进来了一些。
最后我稳定在 top-k=5,阈值 0.5 左右。这个阈值是余弦相似度还是别的度量,我没去翻源码确认,不同向量库的默认度量可能不一样,你调的时候最好先确认一下。
一个实际观察:bge-m3 对中文技术文档的召回还不错,但同一个问题换个问法,召回结果差异挺大。所以我在实际用的时候,会引导同事尽量把问题问具体一点。
完整验证脚本
想确认整条链路通不通,可以绕过前端直接打后端接口。以下是我用的 curl(接口路径以你部署版本的文档为准,我这里的路径在不同 commit 间改过):
# 先登录拿 token(如果开了鉴权)
curl -X POST http://localhost:8080/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"your_password"}'
# 用 token 提问
curl -X POST http://localhost:8080/api/v1/chat \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <your_token>" \
-d '{
"knowledge_base_id": "your_kb_id",
"question": "部署流程是什么?"
}'
返回里应该能看到生成的回答和引用的文档片段。如果引用的片段是空的但回答正常,说明检索没生效,LLM 在硬答——这是 RAG 系统最坑的失败模式,看着有回答,其实是幻觉。
所以一定要看返回里的引用来源,不要只看回答文本。
踩坑清单
整理一下我实际遇到的问题:
| 问题 | 原因 | 解决 |
|---|---|---|
| 后端容器反复重启 | 宿主机端口冲突 | 改映射端口 |
| Embedding 报维度错误 | 换了模型,索引维度不匹配 | 重建知识库 |
| PDF 表格解析乱 | 解析器对复杂版面支持有限 | 先转 Markdown |
| 回答有内容但无引用 | 检索阈值过高或 top-k 太小 | 调大 top-k 降阈值 |
| 照抄博客变量名失败 | 版本间配置字段改名 | 以本地 .env.example 为准 |
结论
WeKnora 能用,部署门槛不高,Docker Compose 一把起。适合想自己掌控数据、又不想从零写 RAG 链路的团队。
它的强项在文档解析和检索这套流程的封装,弱项是文档格式兼容性——PDF 复杂版面会丢结构,这个得靠预处理补。
几个建议:
- 模型用本地部署,数据不出内网,这也是选它的主要原因之一
- Embedding 模型定了就别随便换,换一次要重建索引
- 上线前一定要验证引用来源,别被"看起来对"的回答骗了
- 主分支变动快,部署时记下 commit hash,方便回滚
最后再强调一次:文中涉及的具体参数名、接口路径、模型版本,请以你实际 clone 到的版本为准。我写的是我这次部署时的状态,不确定的地方已经标出来了,没有验证的我没有编。