最近在折腾企业内部知识库方案时,被 WeKnora 这个名字吸引住了。这个由腾讯团队开源的框架,用 Go 语言把RAG(检索增强生成)、Agent(智能体)和Wiki(知识库)三件事揉成了一个整体,并且主打私有化部署。和市面上很多“只解决问答”的开源项目不太一样,WeKnora 给人的第一感觉是它更像一个企业知识管理系统的底座:既能把散落的文档变成可检索、可问答的知识资产,又能让 Agent 基于这些知识去执行具体任务,同时用 Wiki 的协作方式把内容持续维护下去。
这篇文章我想按照自己的理解,把 WeKnora 的定位、Go 技术选型的理由、RAG 链路、Agent 设计、本地部署和排坑经验完整拆一遍。适合正在做企业知识库、团队 Wiki 升级、私有化 AI 问答系统,或者单纯想看看 Go 在 AI 应用层能怎么发光发热的读者。项目本身的文档还在快速迭代中,我写的内容更多是通用方法和原理层面的梳理,具体配置请以你拿到的版本和官方仓库为准。
1. WeKnora 是什么:一份被低估的「三合一」工程方案
1.1 拆解 RAG、Agent、Wiki 三块拼图
先解决一个最核心的问题:为什么要把 RAG、Agent、Wiki 放在同一个项目里?传统的知识管理系统,比如 Confluence、Notion、语雀,本质上是文档存储和协同工具。它们的搜索能力有,但距离“直接回答问题”还很远。传统的 RAG 项目,比如基于 LangChain 做的文档问答,解决了“我有一堆资料,想问某个具体问题”的需求,却很难回答“帮我查过去一个月所有线上故障并总结规律”这种需要多步操作的问题。而 Agent 单独做,又容易出现没有知识底座、全靠模型幻觉撑场面的问题。
WeKnora 的三合一思路,本质上是把知识管理的生命周期串起来:
- Wiki 负责沉淀和结构:文档不再是散落的文件,而是有分类、有标签、有页面关系的知识节点;
- RAG 负责理解和检索:把 Wiki 里的内容向量化、索引化,让大模型在回答时能引用真实资料;
- Agent 负责执行和联动:当用户的问题不是一个简单的检索问题,而是需要拆解成多个子任务时,Agent 可以调用检索工具、汇总多份文档、甚至触发后续动作。
这种设计在逻辑上很顺,实际工程上却比单一环节难做很多。因为三者对数据格式、权限模型和并发能力的要求都不一样。我看到 WeKnora 直接用 Go 做这件事时,反而觉得这是一个合理的取舍,后面第二部分会详细说。
1.2 什么样的人适合上手
如果你把这套框架当成“本地知识库 + AI 问答”来用,它能干的事包括:对内部 PDF、Markdown、Word 文档做语义检索,给出带来源的回答,建立团队 Wiki 页面并支持多人维护,再通过 Agent 做一些自动化汇总、信息抽取、报表生成一类的事情。
我觉得下面几类人最容易从中受益:
- 企业内部需要私有化知识库的团队,特别是在意数据不出内网、要自己控制模型的团队;
- 已经用了 LangChain、LlamaIndex 做 RAG 但觉得“文档问答太单薄”,想往 Agent 方向延展的开发者;
- 需要给技术团队、运营团队、客服团队做内部问答系统的后端工程师;
- 正在对比 Dify、RagFlow、WeKnora 这类开源项目的人。需要提醒的是,WeKnora 和 Obsidian 这类个人知识库不是一个物种:Obsidian 是给个人用的 Markdown 笔记工具,WeKnora 是给团队用的服务端系统,它强调的是权限、多人协作、可部署和可检索。
2. Go 技术底座:为什么这个项目选择了 Go
2.1 Go 在企业知识系统里的独特优势
现在大部分 RAG 和 Agent 项目都是 Python 写的,因为大模型生态里的 SDK、框架、工具大部分优先支持 Python。那 WeKnora 为什么反着来?我的理解是:企业知识管理系统的瓶颈,根本不是“算法有多新”,而是“能不能稳定跑起来、好不好部署、并发了扛不扛得住”。
Go 有一个非常大的优势:最终编译产物是单个二进制文件。这意味着你在服务器上部署一个知识库服务时,不用先装 Python 3.10、不用折腾 C 依赖、不用管理一堆虚拟环境。拷一个二进制,再加上配置文件就能启动。对企业私有化部署来说,这是巨大的维护成本节省。尤其很多企业服务器是不联网的,或者环境比较老旧,Python 项目的依赖安装非常痛苦,Go 项目则干净得多。
另一个优势是内存占用和并发模型。RAG 流程本身不是一个 CPU 密集型计算任务,它的大部分耗时在网络 IO 上:调 embedding 模型、查向量库、调 LLM 接口、读取文档内容。这种 IO 密集场景恰好是 Go 的强项。goroutine 创建成本很低,几万甚至几十万个并发任务在一个进程里调度都没有压力。配合 channel 和 context 机制,可以很优雅地做超时控制、熔断和请求取消。
有人可能会问,那真正的模型推理性能怎么办?向量模型和大模型的推理通常是通过独立服务提供的,比如本地的 Ollama、vLLM,或者远程的模型 API。WeKnora 作为控制层和业务层,通过 HTTP 或者其他协议去调用这些推理服务,而不是自己在进程内跑模型推理。所以它不需要 Python 那种深度学习计算生态,反而更适合用 Go 这种高并发、低开销的语言做编排。
2.2 并发处理与部署形态的工程收益
从标题里的热搜词能看出,大家很关心 “AI Agent 怎么扛并发”“RAG 项目怎么选型”。在真实企业环境里,知识库系统往往不只是给一个人用的,而是几十人甚至上百人同时访问。每个人提问时,后端要同时经历文档检索、重排、构造 prompt、调用大模型、流式返回答案几个阶段。如果每个请求都独占一个重量级线程,高并发下很容易把服务拖垮。
Go 的 goroutine 可以用很轻量的方式处理这种请求。举个直观例子:假设有 200 个用户同时问“某某系统的使用手册是什么”,每个请求需要先看权限,再去向量库召回,最后调用大模型。如果用阻塞线程模型,200 个线程就可能耗尽内存;而用 goroutine,每个请求的栈空间很小,实际占用的资源可能连 1GB 都不到。再配合连接池复用模型服务的 HTTP 连接,整体吞吐量会非常可观。
部署形态上,Go 服务也更容易嵌入到现有运维体系里。它可以用 Docker 镜像跑,也可以直接部署为 systemd 服务,暴露一个 HTTP 端口供前端调用。在做 Kubernetes 部署时,镜像可以做得非常小,拉取速度快,启动时间短。对运维来说,“一个 Go 服务接着所有模块”比“一个 Python 主服务加一堆 worker 进程”要省心太多。
3. RAG 链路深度拆解:从文档入库到回答生成
3.1 文档解析与切片:决定上限的第一道工序
RAG 做得不好,很多时候不是大模型的问题,而是文档解析和切片这两步太粗糙。WeKnora 这类框架也一样,你喂进去的是 PDF 也好、Word 也好、Markdown 也好,系统都要先把它变成纯文本,再按一定粒度切块,最后交给 embedding 模型变成向量。这个流程的第一个坑,就是 PDF 内容根本不是一个“正常段落”,而是扫描图片或带复杂排版的文件。
我自己在部署时遇到过一个很典型的场景:团队把一堆合同扫描件放进了知识库,结果搜索“违约责任”怎么都搜不到。后来发现那些 PDF 全是图片,系统拿到的是空文本文件,要么解析失败,要么得到乱码。所以,如果你遇到“WeKnora 解析失败”或者“文档入库了但搜索不到”,第一反应应该是检查文档本身是不是可复制文字的 PDF,而不是急着调向量模型。
切片参数也非常关键。切太大会导致每段里塞进太多无关内容,检索时向量不但不聚焦,还会浪费 LLM 的上下文窗口;切太小会导致语义被切断,一个完整的知识点被拆得七零八落。比较常见的做法是围绕 512 token 左右的块大小,配合 64 到 128 token 的重叠。这里我不写死数值,因为不同语言、不同业务场景差异很大:代码文档可以切短一点,合同和制度类文档可以切长一点。但无论如何,重叠部分都是必须的。没有重叠,一个段落刚好被拦腰截断时,前后两句语义就都丢了。
3.2 向量检索、关键词召回与重排
切片之后是 embedding。WeKnora 这类项目一般支持多种 embedding 模型接入,本地可以用 bge-m3、m3e 这一类,也可以接云端的模型 API。选 embedding 模型时,不要只盯着榜单分数,要看它在你的业务文本上的表现。企业内部文档里到处都是专有名词和缩写,通用模型未必认识“某某项目代号”和“某某系统简称”。所以上线前一定要拿真实问题样本做检索测试。
但向量检索不是万能的。我见过不少团队只配了向量召回,然后发现 RAG hit rate 不高。原因往往是用户的问题里有精确的产品型号、工单编号、代码函数名,这类信息不适合用语义相似度匹配。这时候必须有关键词检索,也就是 BM25 一类的传统搜索。用老百姓的话解释:向量搜索像是“意思差不多的找出来”,关键词搜索像是“长得一模一样的找出来”,两者缺一不可。
有了两路结果之后,还要做融合和重排。最简单的融合是 RRF(Reciprocal Rank Fusion),把两个搜索结果的排名做一个加权合并。如果想要更高精度,再加一个 rerank 模型,也就是 cross-encoder,让它站在“问题-候选文本”这对组合的角度重新打分。这套组合拳打完之后,才能决定哪些内容真正进入给大模型的上下文。只做向量召回不重排,就好比把图书馆里一堆“看起来相关的书”全堆到你面前,让你自己翻,大模型会非常容易走神。
3.3 索引背后的本体与关系:RAG 不只是“切块加向量”
如果你处理的知识域比较固定,比如技术故障库、产品需求库、客户案例库,页面之间往往存在明确的关系。热搜词里的“ontology RAG”“GraphRAG”“本体”指的就是把实体和关系结构化地建模,而不是让大模型对着散落的文本片段硬猜。
举个简单的场景:企业 Wiki 里有“项目A”“服务器B”“负责人C”三个页面,A 部署在 B 上,C 负责 A。如果不做本体关联,用户问“项目A挂在哪台机器上,现在谁负责”,系统只是把这些页面全部召回,但回答时大模型未必知道这三者之间的具体关系。如果利用 Wiki 页面的标签、分类、超链接、属性字段建立知识图谱,那系统在检索时就可以直接按照“项目A -> 部署服务器 -> 服务器B”和“项目A -> 负责人 -> C”的路径去找,答案的准确率会明显更高。
所以我不太建议把 WeKnora 的 Wiki 当成一个单纯的 Markdown 编辑器看。页面边栏的标签、父子页面结构、内部链接,其实都在为后面的检索和 Agent 决策积累结构信息。你在录入知识时越有规范,后面问答就越准。这比上了系统之后疯狂调 prompt 管用得多。
4. Agent 能力拆解:让知识库从“答问题”变成“干活”
4.1 Agent 在企业场景里到底负责什么
如果 RAG 解决的是“基于资料的问与答”,Agent 解决的则是“基于资料的多步任务”。我举一个很有代表性的企业场景:运营团队每天要写日报,需要从很多页面里找出昨天的数据、异常事件和处理进展。普通 RAG 的做法是问一句“昨天的数据是多少”,它给你一段引用。但现实的问题是“把昨天所有关联内容捞出来,按重要程度归纳成一份报告”,这需要拆解成很多步:
- 识别要查询的系统;
- 检索对应时间范围的文章、工单、故障记录;
- 提取关键指标和结论;
- 按固定模板汇总输出。
WeKnora 的 Agent 设计思路和现在主流的智能体框架类似:模型先理解用户意图,然后把任务拆成步骤,每一步调用预先注册好的工具,比如“搜索 Wiki 页面”“读取指定文档”“调用数据库查询”等。工具执行完以后,模型会结合返回结果决定下一步动作,最终汇总回答。这个过程通常被叫做 ReAct 或者 Plan-and-Execute,本质上是让模型在一个循环里交替进行思考、行动、观察。
4.2 工具调用、沙箱与权限边界
Agent 能力强的地方同时也是风险大的地方。如果 Agent 什么都能做,一个内部员工问“帮我把所有文档权限改成公开”,系统真的要执行吗?所以,企业级 Agent 必须有清晰的边界。我总结下来有三道防线:
- 工具白名单:不是所有能力都向 Agent 开放。一般开始时只开放只读类工具,比如搜索知识库、读取页面、统计页面数量。写操作、删除操作、权限修改,要么完全关闭,要么需要人工确认。
- 沙箱执行环境:如果 Agent 要执行代码或脚本,比如生成一个 CSV 或画一张图表,建议在隔离的容器或沙箱里跑,限制网络、限制文件系统访问。这样即使模型生成了有问题的代码,也不会影响宿主机器。
- 人机确认节点:对重要操作,Agent 不应该一步到位执行。比较稳的模式是“Agent 生成计划 -> 用户确认计划 -> 逐步执行”。这和 Git 提交代码要 review 是一个道理。
另外在配置 Agent 时要重点关注“最大迭代次数”。模型在复杂任务里可能反复尝试同一个动作,或者陷入死循环。不设置上限,一批请求就可能把模型服务和数据库连接池全部打满。我习惯先把最大迭代次数设置为 5 到 8,等流程稳定了再逐步放开。
4.3 Agent 和 RAG 怎么协作才不翻车
很多人会把 Agent 和 RAG 当两条平行路线选,但在 WeKnora 的定位里,它们必须协作。最经典的协作方式是“Agentic RAG”:Agent 不是一上来就调大模型回答问题,而是先判断“我现在需不需要更多资料”。如果需要,就去调用检索工具;如果检索到的资料不够,就改写关键词语义重新检索;如果足够,再开始生成答案。
这种模式下,检索工具返回的结果必须附带上来源和置信度信息。原因很简单:Agent 是多步流程,它一旦在某一步相信了错误信息,后面每一步都会基于错误继续推导,最后输出一个看起来头头是道、实际完全跑偏的结论。这比普通 RAG 的幻觉更隐蔽,也更危险。所以我在设计检索工具时,总会尽量让返回结果包含“文档 ID、页面标题、匹配段落、分数”,并且要求在 prompt 里明确告诉模型:找不到就承认找不到,不要编造上下文。
现在很多团队在纠结“直接用大模型原生调用工具,还是用 RAG 知识库”。我的经验是,在企业内部,RAG 知识库本身就是最重要的一个工具。你要让 Agent 具备“懂业务”的能力,靠的不是把大模型越调越大,而是让它知道该去问哪里、该引谁的结论。WeKnora 把这两者放一起,本质上就是在做这件事。
5. 本地部署实操:从零把 WeKnora 跑起来
5.1 Docker Compose 快速启动
目前最稳妥的起步方式是用 Docker Compose 拉起整套环境。假设你已经装好了 Docker,并且对 Linux 命令基本熟悉,第一步是获取项目代码:
git clone https://github.com/tencent/weknora.git cd weknora docker compose up -d首次启动会拉取镜像、初始化数据库、启动后端服务和前端页面。启动完成后打开本机的 HTTP 端口,应该能看到登录页和管理界面。如果项目仓库里有.env.example,建议先复制一份出来:
cp .env.example .env然后根据你的实际环境修改配置。需要注意,不同分支和版本的配置项名可能有差异,所以不要背参数名,而是去看项目文档里的环境变量说明。
我用这种方式部署的经验是:如果是在一台干净的 Linux 服务器上,整个过程通常能在十几分钟内跑通。但先别急着上传业务文档,建议先用一两份测试文档,确认“入库 -> 检索 -> 问答 -> 引用来源”这一整条链路是通的,再往里面灌正式数据。否则一旦后期排查问题,你会分不清是文档处理的问题,还是系统配置的问题。
5.2 Windows 11 下的部署细节
很多个人开发者和企业内部试用机器是 Windows 11。在 Windows 下,我建议优先用 WSL2 + Docker Desktop,而不是直接在 Windows 命令行里硬跑。原因主要是两个:一是大多数开源系统的部署脚本和文档默认基于 Linux 写,WSL2 能减少很多路径和权限的坑;二是 Docker Desktop 在 Windows 上默认需要启用 WSL 后端,文件挂载和性能表现更稳。安装完 WSL2 后,基本就是:
wsl --install然后在 Microsoft Store 里装一个 Ubuntu,并在 Ubuntu 里安装 Docker Engine,或者直接在 Docker Desktop 的设置里把 WSL 集成打开。之后所有操作都在 Ubuntu 终端里执行,部署命令和 Linux 服务器上完全一致。
Windows 下还要注意几个细节:项目所在的绝对路径尽量不要有中文和空格,否则容器挂载时偶尔会出现编码问题。如果机器内存不足 16GB,建议把 Docker Desktop 给的内存调低一些,避免本机直接卡死。如果你用 Ollama 跑本地模型,需要让容器能访问到宿主机的 Ollama 服务,通常可以把 Ollama 服务监听地址设为0.0.0.0,或者用 WSL2 内访问 Windows 宿主机的 IP。这一步很多人会漏,最后表现为“知识库系统起来了,但问答一直超时”。
5.3 接模型服务的关键配置
WeKnora 本身不提供大模型和向量模型,它需要对接外部模型服务。如果你本地已经装了 Ollama,可以用 OpenAI 兼容接口方式接入,伪配置大概长这样:
llm: provider: openai-compatible base_url: http://localhost:11434/v1 api_key: ollama model: qwen2.5:7b embedding: provider: openai-compatible base_url: http://localhost:11434/v1 api_key: ollama model: bge-m3上面的api_key在本地 Ollama 场景下并不真正校验,填一个占位符就行。但如果你接的是云端服务,base_url、api_key、model三要素就必须和实际账号一致。这里特别提醒:embedding 模型和 LLM 模型不一定要用同一个服务商。完全可以用本地 Ollama 跑 embedding,再通过 API 调用外部 LLM,只要网络允许。
接口接好以后,我建议用一道“最小验证”来判断配置是否成功:先在管理后台手动新建一个空白页面,写上一句特征明确的话,比如“项目X的登录超时时间是 300 秒”,然后提问“项目X的登录超时时间是多少”。如果回答能准确引用这句话,说明入库和问答链路没问题。如果回答不出来,你先别急着调整提示词,先看检索阶段到底返回了哪些内容,再判断问题出在 embedding 还是 LLM 生成。
6. 常见问题排查与踩坑实录
6.1 文档解析失败与乱码
“WeKnora 解析失败”是很多人遇到的头号问题。我按照经验把常见原因和解决办法整理成了一个排查表:
| 现象 | 大概率原因 | 处理方法 |
|---|---|---|
| PDF 入库后搜索不到内容 | PDF 是扫描件,没有文字层 | 先 OCR 成可复制文本,再重新入库 |
| Word 文档解析后排版乱 | 表格、文本框、页眉页脚干扰 | 转成 Markdown 或纯文本,清洗后再导入 |
| 中文出现乱码 | 文件编码不是 UTF-8 | 统一转为 UTF-8,避免 GBK 等旧编码 |
| 超大 PDF 解析过程卡死 | 内存不足或解析服务超时 | 拆分文件,或调大解析服务超时时间 |
| 带密码/加密文档无法解析 | 系统不内置解密逻辑 | 先解密,再上传普通文件 |
如果你同时上传了一大批文档,建议不要一次性全量导入。先传 5 份不同类型的文档,观察每种格式的解析成功率,再分批继续。解析质量差会直接拉低后续检索的准确率,这一步值得多花时间。
6.2 召回效果差的排查顺序
很多团队问“为什么 RAG hit rate 上不去”。我的排查顺序是固定的,按优先级来:
- 先检查文档解析后的文本是否干净,有乱码吗?有缺失吗?
- 再检查切片大小是否合适,重点看被切断的语义多不多;
- 然后用一条测试问题手工调检索接口,看返回结果里到底有没有正确答案;
- 如果没有正确答案,看是 embedding 模型不认业务词汇,还是关键词搜索没有命中;
- 如果结果里有正确答案但排名靠后,就检查重排模型是不是没有对接,或者 top_k 取值太小;
- 最后才是调整 prompt 和引用格式。
我见过最典型的情况是:团队花了很多时间调 prompt,最后发现文档解析根本没成功,系统从头到尾就检索不到任何有效内容。所以排查要自底向上,不要一上来就怀疑大模型。RAG 系统就像一条水管,出水量小,首先要看是不是进水口堵了,而不是出水口接歪了。
6.3 Agent 幻觉和死循环怎么防
Agent 模式会产生一种非常隐蔽的错误:它会主动“脑补”信息来源。比如让它根据知识库写一份报告,它可能把某篇文章里的数据自动扩展到另一个页面,还写得像真实来源一样。这比直接问答的幻觉更危险。
我的做法是强制要求 Agent 在每一步引用里带上页面 ID 或文档链接,并设置一个规则:如果没有检索到相关内容,必须明确说“知识库中没有找到”。技术上,这可以通过在工具返回结构里加入source_verified字段,然后让 prompt 只允许使用标记为verified的内容。同时要控制推理温度,不要把温度调太高,事实型场景下 0.1 到 0.3 比较合适。
死循环问题则主要通过两个手段控制:一个是最大迭代次数,这个前面说过;另一个是给 Agent 的工具调用加“去重”机制。如果同一个工具在短时间被反复调用且参数几乎一样,就说明它已经陷入重复行为,这时候应该终止循环,并把“当前方案不可行”的反馈交给用户处理。
6.4 部署层面的几个小坑
部署 WeKnora 时,我还遇到过几个非常实际的问题。第一个是数据库初始化失败。项目如果默认使用 PostgreSQL 或 SQLite 之类的存储,首次启动时需要注意目录权限,容器里的工作目录没有写权限会直接报错。第二个是端口占用。默认端口如果和本机已有服务冲突,容器会启动失败,或者能启动但浏览器打不开,这时候要去 Docker 日志里看端口绑定报错。第三个是模型服务地址写错。在容器内访问宿主机时,不能用localhost而要用host.docker.internal,这是一个很经典的 Docker 网络问题。
还有一个小提醒:如果你接的是云端模型 API,一定要在请求超时和失败重试上做好配置。企业内部文档量大的时候,一次 Agent 多轮调用会产生很多次模型请求,如果网络不稳定,单个请求失败就可能让整个任务中断。把重试次数设到 2 到 3 次,并把超时时间设置在 60 秒以上,是比较稳妥的做法。
7. 一点个人体会:这套框架还能怎么用
最后分享一些我自己的使用感受。WeKnora 这类项目最有价值的地方,不是某一个单独模块有多强,而是它把知识库的组织、检索和智能体执行串在了一条生产链路上。我以前用过的很多方案是拼积木式:知识库用一个系统,RAG 服务自己写,Agent 又是另一套,最后还要写胶水代码。WeKnora 至少给了你一个统一模型,让权限、页面结构、工具注册和模型调用能在同一个系统里被管理。
如果你团队里已经有固定的文档规范、页面结构和标签体系,那上手会很快。如果本身文件管理就很乱,我建议不要一次把所有东西都倒进去。先把最常用、最核心的 50 个页面整理好,跑通典型的问答和 Agent 场景,再逐步扩大知识库范围。知识管理的本质不是把文件全塞进去,而是让系统知道什么内容该被谁在什么场景下看到。
后续如果时间允许,我准备从三个方向再深入试试:第一,把 WeKnora 的 Wiki 页面和版本管理工具联动,让发布流程自动更新知识库;第二,用本地向量模型加私有化大模型,完全脱离外部 API 跑通全链路,这对数据敏感团队非常友好;第三,测试在多租户场景下的权限隔离能力,看看不同部门的知识是不是能真正做到各看各的。等有结果了,我再写一篇实操记录出来。