返回个人主页

企业知识库 Demo

查看源码
纯 Python · 本地单机可跑 · 无需 GPU

一套把自己讲得清楚的企业知识库

完全自建、可解释、可迁移的 RAG 知识库。目标不是「装上就能用」,而是把企业知识库的每个环节都亲手跑一遍—— 从文档解析、中文切分、混合召回、交叉编码器精排,到答案里的每一句都能点回原文。

750 MB内存占用(7.6GB 笔记本实测)
0PyTorch / Docker / GPU 依赖
2 段检索架构:广召回 + 精排
100%答案逐句标注来源

界面

提问,然后自己核对答案

每个结论后面都跟着 [编号]。点一下编号,直接展开那段原文——并且标出它为什么被选中: 重排分、余弦相似度、以及向量路与关键词路各自的排名。

知识库问答界面:提问「一线城市差旅住宿标准是多少」,回答带 [1][2][3] 引用,下方展开显示每个来源片段的原文与重排分、余弦相似度、两路召回排名
提问 → 混合检索 → Cross-Encoder 重排 → 带编号的回答。下方是命中的原文片段, 每条都标着 重排分 / 余弦 / 向量# / 关键词#——检索为什么选中它,一眼可见。
知识库初始界面:左侧是已入库的四个文档与分块数,顶部显示向量模型、重排模型与大模型的就绪状态
左侧是文档库(4 个示例文档、12 个分块)与模型就绪状态;顶部实时显示向量模型、重排模型与大模型的加载情况。

架构

两段式检索:先保证不漏,再保证不脏

第一阶段广召回,第二阶段精排。这不是可选的优化项,而是精度能否达标的分水岭——下一节有实测数据。

第一阶段 · 广召回

尽量不漏掉任何相关片段

向量检索(FAISS) 抓语义相近:换个说法也能找到
BM25 关键词检索 抓专有名词与编号:ISO27001、HT-2024-087
↓  RRF 倒数排名融合,取前 20 条候选  ↓
第二阶段 · 精排

让无关内容进不了上下文

Cross-Encoder 重排 BAAI/bge-reranker-base,逐条精算相关性
重排分门槛过滤 阈值 0.5,经实测标定(安全区 0.0 ~ 0.5)
↓  只把过了门槛的片段交给大模型  ↓
生成

带编号的回答

强制标注来源 系统提示词要求每个结论必须带编号,资料不足必须说「无法回答」
可点回原文 分块带 source / page / section:PDF 到页码,Word 到章节

实测数据

只有「向量 + 关键词」,是会出错的

用 10 个真相关问题 + 8 个无关问题做过标定(4 份示例文档,中文 BGE 模型)。下面这四行是最关键的证据。

问题类型余弦相似度重排分
出差住酒店能报多少钱相关0.71302.63
ISO27001 是什么认证相关0.45371.85
北京到上海高铁多久无关0.6370-0.043
Python 怎么读文件无关0.4154-7.27
看第三行。无关问题的余弦相似度(0.637)比真相关问题(0.454)还高。 原因是文档里有「高铁乘坐二等座」,BM25 把「高铁」当成稀有词给了极高分数,硬把它顶了上来—— 这是关键词路污染语义路的典型表现。

于是纯阈值方案必然失败:余弦阈值扫描后,相关区间与无关区间是重叠的。

区间相关问题的余弦无关问题的余弦
扫描结果0.45 ~ 0.780.30 ~ 0.64
结论两段重叠 → 阈值取 0.45 还是 0.64,都必然在「漏拦假阳性」和「误杀真相关」之间二选一

加上 Cross-Encoder 重排后,两段区间完全不重叠:

相关(10 个问题 × 全部候选)无关(4 个问题 × 全部候选)
重排分0.43 ~ 4.91-10.19 ~ -0.043
阈值扫描确认安全区为 0.0 ~ 0.5,默认取上边界 0.5——在零误杀的前提下精度最高。 标定结果:阈值 0.0 / 0.5 时,相关问题零引用(误杀)与无关问题漏拦都是 0;阈值调到 1.0 就出现 1 次误杀。
这就是本项目的核心论点:「调阈值」这类看起来是工程细节的问题,其实是架构问题—— 该加一层模型,而不是继续调参数。如果你的知识库方案里没有重排环节, 它在「用户问了个不相关的问题」时几乎一定会给出编造的答案。

能力

它具体做了什么

中

中文优化

BGE 中文向量模型 + BGE 中文重排模型,针对中文语义与专有名词调优,不是直接套英文模型。

▤

多格式解析

PDF(含页码溯源)、Word(含章节溯源)、Excel(含工作表溯源)、Markdown、TXT、CSV。

⌕

两段式检索

向量 + BM25 混合召回,Cross-Encoder 精排,融合用 RRF——只看排名不看分数,比加权求和稳。

[1]

答案可溯源

每个结论强制标注来源编号,点击展开原文。企业知识库最大的风险不是答不出,而是答错了还看不出来。

▣

零隐藏状态

存储就是「一个 FAISS 索引文件 + 一个 JSON」。删掉目录即彻底重置,没有藏在框架里的状态。

↘

优雅降级

重排模型不可用时自动退回余弦门槛,服务不中断。也可用 KB_RERANK=0 主动关闭。

✓

短文档不丢

切分阈值只用于过滤页眉页脚碎片,每份文档保证至少保留一块——曾有一份 36 字符摘要被静默丢弃,已修。

◎

没有 Key 也能跑

内置 mock 大模型,可以先验证「检索 → 重排 → 组 prompt → 流式输出 → 引用渲染」整条链路。

选型

为什么不用 PyTorch / ChromaDB / LangChain

这三件事都实际装过、试过。结论记录在此——你在企业里选型时会遇到同样的坑。

01 不用 sentence-transformers

它会拉入 PyTorch(约 2GB 磁盘、运行时数 GB 内存),本机 7.6G 内存 + C 盘只剩 14G,跑起来非常勉强。

改用 fastembed(ONNX Runtime)加载 BAAI/bge-small-zh-v1.5:磁盘约 90MB、内存约 200MB,中文效果不变。

02 不用 ChromaDB

  • 1.5.9 的 Rust 内核在 Windows 上建库直接报 unable to open database file(数据库文件已创建却打不开)
  • 降级到 0.5.x 需要现场编译 chroma-hnswlib,本机没有 C++ 编译器
  • 更关键的是:它内置的 ONNX 嵌入函数硬编码英文模型 all-MiniLM-L6-v2,下载路径写死、不读环境变量,中文检索效果差

改用 FAISS:官方提供 Windows 预编译 wheel,IndexFlatIP + 向量归一化即余弦相似度,存储完全显式、便于审计。

03 不用 LangChain

解析 → 切分 → 检索 → 生成,每一步都在 app/ 里显式可见。企业知识库最终一定要按自己的文档特点调切分与检索, 被框架抽象掉反而更难改。

#原本的假设实测结果最终决定
1用 ChromaDB 做向量库Rust 内核在 Windows 建库即报错;降级需现场编译 C++ 扩展换 FAISS
2用 ChromaDB 内置嵌入默认函数硬编码英文模型,中文效果差换 fastembed
3用 sentence-transformers拉入 PyTorch(约 2GB 磁盘、数 GB 内存)全程 ONNX
4只靠余弦阈值挡无关问题无关问题得分(0.637)高于真相关问题(0.454),两段区间重叠引入 Cross-Encoder 重排
5切分阈值过滤碎片一份 36 字符摘要在纯阈值逻辑下被静默丢弃每份文档保证至少一块
6引用里展示「相关度」实际展示的是 RRF 融合分(量级 0.016,跨查询不可比),毫无解释力区分余弦 / 重排分 / 融合分
第 4 条是最值得记住的一条——它说明「调阈值」看起来是工程细节,实则是架构问题:该加一层模型,而不是继续调参数。

细节

中文切分的三个细节

按段落 → 句子切

而不是固定字数切——固定字数会把一句话拦腰截断,检索时两头都匹配不上。

⇄

块间重叠 80 字符

避免答案恰好落在切口上,导致两边的块都只拿到半句话。

▦

表格单独成块

Excel / Word 表格里的数字脱离表头就没有意义,所以整表保留,不切。

技术栈与运行

怎么把它跑起来

后端

PythonFastAPIuvicornhttpx

检索

FAISSfastembed (ONNX)BM25RRF 融合Cross-Encoder 重排

文档解析

pypdfpython-docxopenpyxl

前端

原生 HTML / CSS / JS无构建流式渲染
# 安装依赖(首次会下载约 90MB 向量模型到 models/)
pip install -r requirements.txt

# 启动服务
python -m uvicorn app.main:app --host 127.0.0.1 --port 8000

# 打开界面,左侧上传文档,右侧提问
start http://127.0.0.1:8000

# 端到端自检(在现有数据上巡检)
python verify.py
没有大模型 Key 也能先跑通全链路。仓库自带 mock 模型服务, 可以先验证「检索 → 重排 → 组 prompt → 流式输出 → 引用渲染」这条通路, 再换成任何 OpenAI 兼容接口(DeepSeek、通义千问、智谱、vLLM、企业内网模型网关)。

可迁移性

从单机 Demo 到企业环境要改什么

代码层面的改动很少:kb.py 换存储实现(接口只有五个函数)、retrieval.py 换检索后端、main.py 加鉴权中间件。解析、切分、融合、提示词可直接复用。

项目现在(单机 Demo)企业环境
向量检索FAISS 本地文件Milvus / Qdrant / Elasticsearch
部署单进程 uvicornDocker + 反向代理 + systemd / K8s
模型本地 ONNX 小模型内网模型服务或 GPU 推理集群
鉴权无SSO / LDAP,按部门做文档权限过滤
文档来源手工上传对接 Confluence / 共享盘 / OA,定时增量同步
已知边界:FAISS 是单机索引,多人并发写入需要自行加锁;扫描版 PDF 不做 OCR, 入库时会提示「未解析出文本」,企业环境需另行接入 OCR。