把知识库做成可接入业务的底座:Knowledge Worker 当前进度与目标
记录 Knowledge Worker 在律师行业内部试运行期间的当前进度,包括文档摄取、混合检索、流式问答和 MCP 接入。
最近在做一套叫 Knowledge Worker 的知识库系统。目前它正在律师行业内部试运行,可以完成文档上传、索引、检索和带引用的问答,但离可以直接交给不同团队长期使用,还有不少细节要继续验证。
我没有把它限定成某个行业的问答产品。现在更关心的是另一件事:能不能把知识处理做成一块独立能力,通过 API 或 MCP 接进已有业务,同时保留清楚的数据边界、来源和运维手段。
从一条完整链路开始
很多知识库演示会从聊天框开始,实际接入时,聊天只占整个链路的一部分。文档怎么进来,格式是否统一,处理失败后从哪里重试,答案引用了哪一段原文,知识库之间会不会串数据,这些问题更早出现,也更影响系统能否长期使用。
Knowledge Worker 当前支持文本、Markdown、CSV、HTML、PDF、DOC 和 DOCX。文档上传后进入异步任务,由 Worker 负责解析、分块、生成向量并写入索引。管理端可以查看每个任务的阶段、分块进度和错误信息,失败任务也有明确的重试入口。
问答链路已经覆盖查询改写、检索、证据筛选、答案生成和引用返回。回答可以流式输出,引用会带上文档、版本和分块位置,继续展开还能查看对应原文。内部试用时,我会优先看引用是否可靠,而不只看回答读起来是否流畅。
模块边界要允许替换
项目主体用 Rust 编写,代码分成两层。modules/knowledge 保存知识库领域模型、摄取服务、文本处理逻辑,以及文档存储、向量存储、Embedding 和回答生成等接口;crates/knowledge_worker 负责 HTTP API、任务执行、数据库、健康检查、指标和运维命令。管理端是单独的 React 应用,只通过接口使用这些能力。
这样的拆分主要服务于替换。解析、向量化、检索和回答生成不会绑在一个处理函数里,模型节点、向量数据库或文档存储发生变化时,业务对象和任务流程不需要一起重写。现在的实现使用 Qdrant 保存向量索引,业务数据可以放在 SQLite 或 PostgreSQL,模型侧既能连接 Ollama,也能连接兼容 OpenAI 协议的服务。
模块化还有一个更实际的作用:系统不必强迫接入方采用同一套界面。已有后台可以直接调用 HTTP API,Agent 可以通过只读 MCP 工具查询知识库,内置管理端则用于开发、调试和内部操作。三种入口使用的是同一套知识处理能力。
目前使用的技术栈
后端使用 Rust 2024 edition,HTTP 服务基于 Axum 和 Tokio,配置与接口数据通过 Serde 做强类型解析。领域数据使用 Toasty,可以按部署环境连接 SQLite 或 PostgreSQL;向量索引放在 Qdrant,文档解析则分别处理 PDF、Word、HTML、Markdown、文本和 CSV。模型请求统一经过 Reqwest,可以连接 Ollama 或兼容 OpenAI 协议的服务。
管理端使用 React、TypeScript 和 Vite,负责知识库、文档任务、会话和问答操作。系统对业务方提供 JSON HTTP API,对 Agent 提供 stdio MCP,运行状态通过健康检查、结构化日志和 Prometheus 指标暴露。每个组件负责一项清楚的工作,后续替换模型、数据库或前端时不必同时改动整套系统。
检索不能只依赖一种相似度
当前检索同时使用语义向量和 BM25 关键词结果,再通过排名融合得到候选内容。语义检索适合处理表达不同但意思相近的问题,关键词检索对编号、专有名词和精确字段更敏感,两边各自解决一部分遗漏。
用户的问题还会经过查询改写,一次问法可以扩展成几条检索表达,再把结果合并。这个步骤不能无限放大,改写数量、候选数量和最终证据数量都由配置明确限制,否则召回内容越多,噪声也会跟着进入回答。
检索结果最终要回到原文。每条引用保存文档 ID、版本、分块位置和分数,管理端可以从答案跳到引用片段,再加载文档全文并定位对应内容。旧索引找不到源文件时,界面会明确说明只能查看当时保存的切片,不会把缺失的来源伪装成完整文档。
模型节点是运行资源,不是写死的地址
内部环境里的模型服务可能分布在不同机器上,单个节点也会临时离线。Knowledge Worker 把模型节点集中配置,健康检查会记录每个节点的状态,向量化、查询改写和回答生成只选择最近确认在线的节点。多个节点同时可用时,调度器会参考正在处理的请求数量分配任务。
这种设计既能接本地 Ollama,也能接带 API Key 的兼容服务。协议、模型接口和鉴权信息都要显式配置,系统不会根据域名或 URL 路径猜测服务类型。配置不完整、向量维度不一致或索引参数冲突时,Worker 会在启动或请求阶段直接报错。
模型服务可以更换,已经进入业务系统的数据约束不能跟着变。对接方需要知道一次请求用了哪个知识库、返回了哪些证据、失败发生在哪个环节,这些信息比隐藏差异更有用。
权限留在正确的系统里
Knowledge Worker 负责租户隔离,但不维护企业里的用户、部门和角色关系。上游业务系统先完成用户认证和知识库授权,再把已经允许访问的 knowledgeBaseId 和 tenantId 传给 Worker。Worker 会再次校验知识库归属,租户不匹配时不返回资源。
MCP 的边界更窄。每个 MCP 进程启动时固定一个租户和一组知识库白名单,只提供知识库、文档、任务、搜索和原文读取等只读工具。用户权限发生变化后,上游需要更新白名单并重新加载 MCP,模型不能通过临时参数扩大自己的访问范围。
我倾向于把这类边界写得明确。不同公司的权限模型差异很大,知识库服务自行维护一套简化用户体系,最后往往会和真实业务权限冲突。Worker 只接受经过授权的范围,并在自己的数据层继续执行租户校验,职责会更稳定。
运维能力要和问答一起完成
一套知识库开始保存业务资料后,健康检查、指标和备份就不再是附加功能。当前服务会检查数据库、Qdrant 和模型节点,暴露 Prometheus 指标,也提供数据库、原始文档和 Qdrant 快照的一体化备份与恢复命令。
恢复操作会校验备份清单和当前索引配置,并要求 Worker 停止运行,避免一边写入一边恢复。文档删除和知识库删除也会同时清理数据库记录、原始文件和向量数据。资源清理失败会作为错误返回,不会只删掉界面上的记录。
这些能力平时不显眼,却决定了系统遇到节点故障、误操作或迁移时有没有明确的处理路径。内部试用阶段正适合把这些路径跑通,因为等资料规模上来以后再补,代价会高很多。
哪些场景适合接入
通用知识底座不等于所有资料都应该放进去。它更适合信息已经有一定规模、需要反复查询,而且回答必须能追到来源的场景。
这次先放到律师行业内部试运行,现阶段主要看专业资料能否稳定导入,提问后能否找到有效段落,连续问答能否保留上下文,以及引用能否回到对应原文。试运行还没有积累到足以公开评估效果的数据,所以这里不写准确率、节省时间或业务结果。
企业制度、操作手册和内部流程可以按知识库隔离,员工提问后直接查看引用条款;项目文档、接口说明和故障记录可以提供给研发助手,在回答旁保留原始上下文;专业资料库可以把不同来源和版本分开管理,避免旧文件与新规则混在一起。已有 AI 助手也可以只接 MCP,不必重新做一套知识库界面。
如果资料很少、内容长期不更新,普通目录和全文搜索可能已经够用。需要跨部门权限审批、公开分享或复杂用户管理时,也应该由现有业务系统负责,Knowledge Worker 提供知识处理和查询接口。明确哪些事情不由它处理,可以让接入范围更容易估算。
当前进度和下一阶段目标
目前已经跑通的主线包括知识库管理、文档摄取、混合检索、流式问答、引用回看、会话保存、任务重试、节点健康检查、指标、备份恢复和只读 MCP 接入。最近一轮工作补上了持久会话、文档预览、回答流式输出,以及旧文档索引的兼容处理,内部使用时已经能完成从上传资料到连续问答的完整流程。
下一阶段会把注意力放在检索质量和接入稳定性上。检索评估需要覆盖更多真实问题,记录召回结果和引用是否正确;任务、模型节点和外部依赖的异常路径还要继续验证;API 与 MCP 的契约则要保持严格,让上游在字段或配置错误时尽早得到清楚的错误信息。
长期目标是让这套系统保持独立:它可以部署在受信任的内网,接本地模型,也可以使用兼容服务;可以配合现有后台,也能成为 Agent 的只读知识来源。业务界面会变化,模型也会继续换,文档、检索、权限边界和来源追踪应该留在一套稳定的基础能力里。