# 检索增强知识库
**Repository Path**: agent-dev-team/sonar-kb
## Basic Information
- **Project Name**: 检索增强知识库
- **Description**: 本地可部署的RAG知识库系统,覆盖9环节检索增强全流程,可配置、可观测。
- **Primary Language**: Unknown
- **License**: MulanPSL-2.0
- **Default Branch**: master
- **Homepage**: http://sonar-kb.zzrkyd.cn/
- **GVP Project**: No
## Statistics
- **Stars**: 1
- **Forks**: 0
- **Created**: 2026-06-22
- **Last Updated**: 2026-08-21
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# SonarKB 企业级检索增强知识库(RAG)
本地 / 私有化部署的企业级 RAG 知识库系统,强调全流程可控、可观测。支持 PDF / Markdown / Word / TXT,提供 FAISS + BM25 混合检索、多分块策略、重排序与可计量 Token 消耗。每一个 RAG 步骤都清晰可见、精确可控。
架构形态:**纯 Web,无 Electron、无桌面安装包**。后端 `backend/`(FastAPI)与三个网页 `frontend/`(消费者)、`admin/`(管理后台)、`website/`(营销站)相互独立,仅通过 HTTP + SSE 通信。三个网页打包进**同一个 Docker 容器**,由**同一个 Nginx** 托管静态资源并**反向代理 `/api` 到后端容器**;后端位于 Docker 内部网络,不暴露公网端口。
---
## 一、项目背景
企业知识分散在大量 PDF / Word / 网页文档中,员工检索困难、答案不可溯源。通用大模型对话无法直接接入私有文档,且存在数据出域合规风险。
SonarKB 面向**企业内部私有化部署**场景,将企业文档构建为可检索知识库,员工用自然语言提问即可获得带原文出处的答案。系统所有环节(解析 → 分块 → 向量化 → 检索 → 重排 → 生成)均在自有环境内完成,数据不出域、过程可观测。
---
## 二、项目目标
- **全流程可控可观测**:RAG 九环节(文件解析 → 文本分块 → 向量化 → 索引构建 → 查询改写 → 混合检索 → 重排序 → 推理生成)可视化、每步可配置,Token 消耗可计量。
- **高召回检索质量**:FAISS + BM25 双引擎混合检索,权重 / Top-K 可调;支持 4 种分块策略(auto / hierarchical / sliding_window / llm_semantic)与 LLM / 本地模型重排序。
- **企业级权限**:JWT 无状态鉴权 + 文件级 ACL(用户 / 用户组 / 公开,读 / 写 / 删),企业共享单一知识库。
- **私有化与易部署**:纯 Web 架构,三个网页合并为单一 Nginx 容器,前端直连后端,单条 `docker compose` 命令拉起全部服务,无外部 SaaS 依赖(仅可选调用外部 LLM)。
---
## 三、架构
### 3.1 服务与容器拓扑(部署态)
采用**三页一后端**架构:消费者端、管理后台、营销官网合并为**同一个 `web` 容器**,由单一 Nginx 托管静态资源并**反向代理 `/api` 到后端容器**;后端 `backend` 容器运行 FastAPI,位于 Docker 内部网络,不暴露公网端口。数据层为 MySQL 8,全部 Docker 容器化。
```mermaid
%%{init: {'themeVariables': {'fontSize': '15px', 'fontFamily': 'system-ui, -apple-system, sans-serif', 'primaryTextColor': '#ffffff', 'lineColor': '#94a3b8'}}}%%
flowchart TB
subgraph Browser["用户浏览器"]
direction LR
U1["访客
访问 /(宣传站)"]
U2["员工 / 消费者
访问 /app/"]
U3["管理员
访问 /admin/"]
end
subgraph WebContainer["Docker 容器: web :80 → 宿主机 :8080"]
direction TB
NG["Nginx
静态资源托管 + /api 反向代理"]
subgraph NginxStatic["Nginx 静态目录 /usr/share/nginx/html"]
direction LR
PP["/
website 构建产物
宣传官网 (Vue3+TS)"]
FE["/app/
frontend 构建产物
消费者端 (Vue3+TS)"]
AD["/admin/
admin 构建产物
管理后台 (Vue3+TS)"]
end
NG --> PP
NG --> FE
NG --> AD
end
subgraph BackendContainer["Docker 容器: backend :8000(内部网络,不暴露公网)"]
direction TB
BE_GW["FastAPI 网关
JWT 鉴权 / SSE 流式 / lifespan"]
subgraph BE_Modules["业务模块"]
direction LR
BE_RAG["RAG 引擎
索引 / 混合检索 / 重排 / 生成"]
BE_Auth["鉴权与权限
注册登录 / 用户组 / 文件级 ACL"]
BE_File["文件管理
上传 / 解析 / 分块 / 状态"]
BE_Conf["配置管理
模型池 / 系统参数"]
end
BE_GW --> BE_RAG
BE_GW --> BE_Auth
BE_GW --> BE_File
BE_GW --> BE_Conf
end
subgraph DBContainer["Docker 容器: db :3306(内部网络)"]
direction TB
DB[("MySQL 8 数据库
User / Permission
File / ChatSession")]
end
LLM["外部 LLM 服务
OpenAI / 阿里云百炼 / DeepSeek / 自定义协议"]
%% 浏览器 → Nginx(静态资源 + API 都走 Nginx,同源免 CORS)
U1 -->|"HTTP 静态资源 /"| NG
U2 -->|"HTTP + SSE /app/ 与 /api"| NG
U3 -->|"HTTP + SSE /admin/ 与 /api"| NG
%% Nginx → 后端(Docker 内部网络,通过容器名 DNS 解析)
NG -->|"proxy_pass /api
Docker 网络: backend:8000"| BE_GW
%% 后端 → 数据库 & LLM
BE_Auth -->|SQLAlchemy| DB
BE_File -->|SQLAlchemy| DB
BE_Conf -->|SQLAlchemy| DB
BE_RAG -->|向量化 / 推理| LLM
classDef browserNode fill:#7c2d12,stroke:#fb923c,color:#fff,stroke-width:2px
classDef nginxNode fill:#0e7490,stroke:#22d3ee,color:#fff,stroke-width:2px
classDef staticNode fill:#155e75,stroke:#67e8f9,color:#fff,stroke-width:1.5px
classDef beNode fill:#1e3a5f,stroke:#38bdf8,color:#fff,stroke-width:2px
classDef beModule fill:#1e40af,stroke:#93c5fd,color:#fff,stroke-width:1.5px
classDef dbNode fill:#374151,stroke:#d1d5db,color:#fff,stroke-width:2px
classDef extNode fill:#4c1d95,stroke:#a78bfa,color:#fff,stroke-width:2px
class U1,U2,U3 browserNode
class NG nginxNode
class FE,AD,PP staticNode
class BE_GW beNode
class BE_RAG,BE_Auth,BE_File,BE_Conf beModule
class DB dbNode
class LLM extNode
style Browser fill:transparent,stroke:#fb923c,stroke-width:1.5px,color:#ffffff
style WebContainer fill:transparent,stroke:#22d3ee,stroke-width:2px,color:#ffffff
style NginxStatic fill:transparent,stroke:#67e8f9,stroke-width:1px,color:#ffffff
style BackendContainer fill:transparent,stroke:#38bdf8,stroke-width:2px,color:#ffffff
style BE_Modules fill:transparent,stroke:#7dd3fc,stroke-width:1px,color:#ffffff
style DBContainer fill:transparent,stroke:#d1d5db,stroke-width:2px,color:#ffffff
```
**架构要点**:
- **三个网页合并为一个 `web` 容器**:Nginx 按路径分发静态产物——`/` 给宣传官网(落地页)、`/app/` 给消费者端、`/admin/` 给管理后台,减少容器数量和运维复杂度
- **Nginx 同时托管静态资源和反向代理 API**:`/`、`/app/`、`/admin/` 返回对应静态文件,`/api`、`/docs`、`/openapi.json` 通过 `proxy_pass` 转发到后端容器;SSE 流式响应关闭缓冲(`proxy_buffering off`),保证问答/检索进度实时推送
- **后端单容器**:FastAPI 网关统一处理 JWT / SSE,业务模块涵盖 RAG 引擎、鉴权权限、文件管理、配置管理,通过 SQLAlchemy 访问 MySQL
- **Docker 内部网络**:`backend` 和 `db` 容器不暴露公网端口,仅在 Docker 自定义网络内通信;Nginx 通过容器名 `backend:8000` 解析并转发请求,更安全
- **同源免 CORS**:前端页面与 API 都经 Nginx 同一域名端口访问,浏览器视为同源,无需配置 `CORS_ORIGINS`
- **MySQL 仅内部网络**:`db` 容器不暴露公网端口,仅 `backend` 可访问,数据通过 Docker Volume 持久化
### 3.2 Nginx 静态托管与 API 反向代理
`web` 容器内的 Nginx **同时负责静态文件分发和 API 反向代理**:
**静态资源路由**(按路径返回对应站点构建产物):
- `/` → website(宣传官网,纯静态,不依赖后端)
- `/app/` → frontend(消费者端 SPA,base `/app/`,`try_files` 兜底 `index.html`)
- `/admin/` → admin(管理后台 SPA,base `/admin/`)
**API 反向代理**(转发到后端容器,Docker 内部网络通过容器名 DNS 解析):
- `/api` · `/docs` · `/openapi.json` · `/redoc` → `backend:8000`
- SSE 端点关闭缓冲(`proxy_buffering off`、`proxy_cache off`、`chunked_transfer_encoding off`),保证流式响应实时推送
- 后端容器不暴露公网端口,仅 Nginx 可通过 Docker 网络访问
### 3.3 鉴权与权限
- **登录**:QQ 邮箱验证码 / 微信 OAuth,登录即自动注册。
- **无状态 JWT**(HS256,7 天);SSE 用 `?token=` 查询参数。中间件白名单放行 `/api/auth`、`/api/health`、`/docs`、`/openapi.json`、`/redoc` 与 `OPTIONS`。
- **后台接口**:`require_admin` 守护 `/api/admin/*`。
- **文件级 ACL**:`backend/core/permissions.py` 提供 `can_read` / `can_delete` / `get_readable_doc_ids`;权限条目含 `principal_type`(user|group|public)、`principal_id`、`access`(read|write|delete)。
- **企业共享知识库**:所有请求使用同一工作空间 `DATA_ROOT/shared`,文件级权限由 `core/permissions` 控制,数据不按用户隔离。
### 3.4 技术栈
- **网页**:Vue 3 + Pinia + Vue Router(hash 模式)+ Vite;UI 组件 `exploria-ui`;包管理器 **pnpm**。
- **后端**:FastAPI + `uvicorn[standard]`;检索 `faiss-cpu` / `rank-bm25` / `jieba`;解析 `pdfplumber` / `python-docx` / `Pillow`;鉴权 `pyjwt` + `sqlalchemy` + `aiomysql`;可选重排(torch + transformers)/ 本地 LLM(llama-cpp-python)。
- **依赖管理**:Python 用 **UV**(`backend/pyproject.toml`);Node/TS 用 **pnpm**(禁 npm/yarn)。
- **部署**:Docker + docker compose;CI 构建并推送 `web`(三站合一 Nginx)与 `backend` 镜像。
---
## 四、启动方式
### (a) 开发环境启动
无需 Docker,四个进程各自运行(端口已固定,被占用则启动失败):
```bash
# 1) 后端(FastAPI,固定 127.0.0.1:8001)
cd backend; uv sync; uv run python server.py
# 2) 消费者网页(另开终端,固定 http://localhost:7001)
cd frontend; pnpm install; pnpm dev
# 3) 管理后台网页(另开终端,固定 http://localhost:7002)
cd admin; pnpm install; pnpm dev
# 4) 官网(可选,另开终端,固定 http://localhost:7000)
cd website; pnpm install; pnpm dev
```
> 端口均为固定模式(strictPort),被占用时直接报错退出,不会自动加一。
> 网页开发时由 Vite dev server 把 `/api` 代理到 `http://localhost:8001`;未启动后端则仅网页壳可用。前端与后端完全分离,经 HTTP + SSE 通信(零 IPC)。
### (b) 部署环境启动(Docker Compose)
三个网页合并为 `web` 容器(单一 Nginx 托管静态资源,宿主机 :8080),前端 API 直连后端容器:
```bash
cd deploy
cp .env.example .env # 填入 5 个密钥(见下方「注意事项」)
docker compose up -d --build
```
启动后访问:
| 入口 | 地址 |
|---|---|
| 宣传官网 | `http://<服务器IP>:8080/` |
| 消费者端 | `http://<服务器IP>:8080/app/` |
| 管理后台 | `http://<服务器IP>:8080/admin/` |
| API 文档(经 Nginx 代理) | `http://<服务器IP>:8080/docs` |
- 首个注册且邮箱匹配 `SUPERADMIN_EMAIL` 的用户自动成为超级管理员。
- 生产环境用预构建镜像时,改用 `docker compose -f docker-compose.prod.yml pull && up -d`(镜像由 CI 推送,见 `cicd/README.md`)。
- `web` 容器多阶段构建:依次编译 frontend / admin / website,将 dist 拷贝到 Nginx 镜像的不同路径下。
---
## 五、注意事项
- **密钥(5 项,在 `deploy/.env`,已被 `.gitignore` 忽略、勿提交)**:`JWT_SECRET`、`MYSQL_ROOT_PASSWORD`、`MYSQL_PASSWORD`、`SMTP_AUTH_CODE`、`WECHAT_APPSECRET`(可选,留空不启用微信登录)。
- **非密钥配置**:已内联在 `deploy/docker-compose.yml` / `deploy/docker-compose.prod.yml` 的 `environment:` 块(含 `SUPERADMIN_EMAIL`、`CORS_ORIGINS`、数据库名 / 用户、`SMTP_HOST/PORT`、`JWT_ALGORITHM`、过期时长、`DATA_ROOT` 等),一般无需修改。
- **pnpm 锁文件**:`frontend/` 与 `admin/` 当前仅有 `package-lock.json`,Dockerfile 靠 `package.json` 解析可构建但不冻结依赖;在本机各执行一次 `pnpm install` 生成 `pnpm-lock.yaml` 以对齐 pnpm 工具链。
- **`backend/static/` 为可选兼容**:默认由 `web` 容器统一托管网页静态产物。如需单包简化部署,仍可将 dist 放进 `backend/static/`(见 `backend/core/static_hosting.py`)。
- **外部 LLM**:用户各自在模型池条目中填 API Key,平台不承担费用;默认文本模型 `qwen-flash`。
- **技术深读文档**:检索评测指标与报告解读见 `eval/README.md`;PDF 解析器流程见 `docs/PDF解析器流程说明.md`;模型池与多模态轮询见 `docs/模型轮询与多模态调用.md`;PowerShell 脚本约束见 `docs/PowerShell错题本.md`。
> 根目录 `.venv/` 为孤立本地 Python 虚拟环境(后端使用 `backend/` 内自建环境),已被 gitignore,不进入仓库。
---
## 六、检索评测
`eval/`目录提供检索量化评测,衡量「查询重写」与「关键词检索(BM25)」两个特性对召回质量的贡献。
- 指标:recall@k(前k条覆盖相关段落比例)、nDCG@k(按排序位置加权)、MRR(首个相关结果排名倒数),默认k=5、10。
- A/B组合:对同一测试集跑4种开关——baseline(纯向量)、+rewrite(向量+重写)、+keyword(混合向量+BM25)、full(生产默认,重写+关键词)。
- 前置条件:需先在某知识库workspace完成文档上传与索引构建(chunks.json+vector_dbs+bm25_dbs)。
```bash
uv run python eval/run_eval.py # 一键评测(无测试集时自动生成弱监督种子集)
uv run python eval/run_eval.py --gen-seed # 仅生成种子测试集
uv run python eval/run_eval.py --check # 离线冒烟(不调API)
uv run python eval/run_eval.py --self-test # 指标公式单元测试
```
测试集为eval/testset.json(query→相关chunk_id列表,可参考testset.example.json人工标注);评测报告输出到eval/reports/(含指标表与内联SVG图表)。Windows下也可双击eval/run_eval.ps1一键运行。
> 详细设计、指标定义与报告解读见eval/README.md。
## 七、CI/CD
流水线由Gitee Webhook触发Jenkins,自动构建并部署全部服务到网关:8080。
1. 拉取代码:git checkout master。
2. 类型检查:cicd/Dockerfile.check镜像内跑pyright(backend/)与vue-tsc --noEmit(frontend/admin/website)。
3. 构建服务镜像:cicd/scripts/build-web.sh经docker compose构建web(三站合一Nginx)与backend镜像并推送镜像仓库。
4. 部署全部服务:scp deploy/docker-compose.prod.yml到服务器,docker compose pull && up -d拉起全部容器,静态资源统一入口http://host:8080/,后端API直连;构建通知邮件经cicd/send-mail.py发送。
关键文件:cicd/Jenkinsfile、cicd/Dockerfile.check、cicd/scripts/build-web.sh、cicd/send-mail.py。
> 完整凭据、环境变量与排查见cicd/README.md。