# RagPro_4
**Repository Path**: ipoter/rag-pro_4
## Basic Information
- **Project Name**: RagPro_4
- **Description**: 国内 IT 教育头部企业 RAG 教学项目
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 34
- **Created**: 2026-10-21
- **Last Updated**: 2026-10-21
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# EduRAG 智能学科答疑系统
EduRAG 是一套面向学科答疑场景的智能问答系统。系统采用「本地问答库 + RAG 知识库」双层架构:优先通过 Redis 缓存与 BM25 从 MySQL FAQ 库中快速命中标准答案;当本地问答未命中或置信度不足时,自动降级到 RAG 链路,结合意图分类、检索策略路由、混合检索与重排序,调用大语言模型生成答案,从而兼顾**回答速度、准确性与知识覆盖面**。
系统同时提供命令行与 Web 两种入口:命令行入口为 `main.py`,Web / API 入口为 `api/main.py`。两种入口复用同一套 `IntegratedQASystem` 业务逻辑。
> **项目状态**
>
> | 模块 | 状态 | 说明 |
> | --- | --- | --- |
> | 本地问答(Redis + MySQL + BM25) | ✅ 已完成 | 支持缓存命中、相似问题匹配与阈值降级 |
> | RAG 知识库问答 | ✅ 已完成 | 意图分类、策略路由、混合检索、重排、流式生成 |
> | QA + RAG 一体化 | ✅ 已完成 | `IntegratedQASystem`,命令行与 API 共用 |
> | FastAPI 服务化 | ✅ 已完成 | RESTful API、SSE 流式接口、健康检查、统一异常响应 |
> | Web 前端页面 | ✅ 已完成 | 流式对话、来源标注、历史会话、Markdown、深色模式 |
---
## 目录
- [核心特性](#核心特性)
- [系统架构](#系统架构)
- [技术栈](#技术栈)
- [目录结构](#目录结构)
- [快速开始](#快速开始)
- [配置说明](#配置说明)
- [API 接口](#api-接口)
- [Web 前端](#web-前端)
- [部署](#部署)
- [开发路线图](#开发路线图)
- [常见问题](#常见问题)
---
## 核心特性
### 1. 双层问答路由,快慢结合
- **第一层:本地问答库**
- 用户问题先查 Redis 精确缓存,命中即返回。
- 未命中时使用 BM25 在 MySQL `faq` 表中检索最相似的预设问题。
- 相似度得分达到阈值(默认 `8.0`)时返回标准答案,并回写缓存。
- **第二层:RAG 知识库**
- 本地问答未命中或低分时自动降级到 RAG。
- RAG 答案同样支持 Redis 缓存,避免重复推理。
- **异常兜底**:RAG 初始化或生成失败时返回人工客服提示,不会导致整个服务不可用。
### 2. 面向业务的检索增强策略
RAG 链路不是简单的「向量检索 + 拼接」,而是包含多层决策:
1. **意图分类**:基于微调后的 BERT 模型,将问题划分为「通用知识」与「专业咨询」。
- 通用知识:不检索知识库,直接由大模型回答。
- 专业咨询:进入检索链路。
2. **检索策略路由**:由大模型在四种策略中选择最合适的一种。
- 直接检索(Direct)
- 假设性文档嵌入检索(HyDE)
- 子查询拆分检索(Sub-query)
- 回溯问题简化检索(Backtracking)
3. **混合检索与重排**:
- BGE-M3 稠密向量 + 稀疏向量混合检索(Milvus `WeightedRanker`)。
- BGE Reranker 对候选文档进行精排。
- 父文档—子文档切分:以子块检索、以父块作为上下文,兼顾召回精度与上下文完整性。
- 命中子块在父文档中高亮定位,提升答案可解释性。
4. **自动降级**:高级策略未检索到结果时,回退到直接检索。
### 3. 流式输出与会话上下文
- 支持逐字流式生成答案,降低首字等待时间;API 通过 SSE 推送增量内容。
- 会话历史按 `session_id` 存储在 Redis 中,默认保留最近 5 轮对话。
- Web 端可以在多个会话之间切换、恢复历史并清空当前上下文。
- 流式异常时不会缓存不完整答案。
### 4. 多格式文档接入
文档加载器工厂支持:
| 格式 | Loader | 说明 |
| --- | --- | --- |
| `.txt` | `UTF8TextLoader` | 自动尝试多种编码 |
| `.md` | `FastMarkdownLoader` | Markdown 结构化加载 |
| `.pdf` | `OCRPDFLoader` | 文本提取 + OCR 兜底 |
| `.docx` | `OCRDocxLoader` | 段落 + 图片 OCR |
| `.pptx` | `OCRPPTLoader` | 幻灯片文本 + 图片 OCR |
| `.jpg/.jpeg/.png/.bmp` | `OCRImageLoader` | RapidOCR 识别 |
配合语义切分器 `BgeSemanticSplitter`,按语义边界切分文档。
### 5. 服务化与 Web 界面
- FastAPI 提供同步问答、SSE 流式问答、健康检查、会话历史查询与清空接口。
- 所有非流式业务接口统一返回 `{code, message, data}`;参数错误、依赖异常与未处理异常都由全局异常处理器转换为标准 JSON。
- 前端由 FastAPI 直接托管,无需单独构建流程;支持流式渲染、来源徽标、Markdown、代码高亮、复制答案、停止生成与深色模式。
- 应用启动时初始化 `IntegratedQASystem`;依赖不可用时仍可降级启动,由 `/api/health` 报告依赖状态。
---
## 系统架构
```mermaid
flowchart TD
U[用户] --> CLI[CLI 入口 main.py]
U --> WEB[Web 前端 static/]
WEB --> API[FastAPI api/app.py
REST + SSE]
CLI --> IQ[IntegratedQASystem]
API --> IQ
IQ --> QA[本地问答层]
QA --> RC[(Redis 缓存)]
QA --> MY[(MySQL faq 表)]
QA -->|"命中 / 分数 ≥ 阈值"| ANS[返回答案]
QA -->|"未命中 / 低分"| RAG[RAGPipeline]
RAG --> CLS[BERT 意图分类]
CLS -->|通用知识| LLM[大语言模型
DeepSeek]
CLS -->|专业咨询| SS[检索策略选择]
SS --> VS[(Milvus
BGE-M3 混合检索 + 重排)]
VS --> LLM
LLM --> ANS
```
### 一次问答的完整流程
1. 接收用户问题;Web 端复用本地会话 ID,服务端在未传 `session_id` 时自动生成。
2. 查询 Redis 精确缓存;命中则直接返回。
3. 使用 BM25 在 MySQL FAQ 库中检索相似问题;得分达到阈值则返回标准答案。
4. 查询 RAG 答案缓存;命中则直接返回。
5. 进入 RAG 链路:
- BERT 判断问题类型;
- 通用知识直接调用大模型;
- 专业咨询选择检索策略,执行混合检索与重排;
- 组装上下文并调用大模型生成答案。
6. 输出答案(命令行直接打印,API 同步返回或通过 SSE 推送增量),写入会话历史与答案缓存。
7. 任一步骤异常时返回人工客服兜底提示。
---
## 技术栈
| 层次 | 技术 |
| --- | --- |
| 语言与配置 | Python 3.12+、Pydantic Settings、python-dotenv |
| 本地问答 | Redis、MySQL、`rank-bm25`、`jieba` |
| RAG 框架 | LangChain |
| 向量数据库 | Milvus 2.4(etcd + MinIO) |
| 嵌入与重排 | BGE-M3、BGE Reranker v2-m3 |
| 意图分类 | HuggingFace Transformers + 微调 BERT |
| 大模型 | DeepSeek(OpenAI 兼容协议) |
| 文档处理 | PyMuPDF、python-docx、python-pptx、RapidOCR |
| 服务化 | FastAPI、Uvicorn、SSE |
| 前端 | 单页 HTML、原生 JavaScript、Tailwind CSS(CDN)、marked、DOMPurify、highlight.js |
---
## 目录结构
```text
RagPro/
├── main.py # QA + RAG 一体化业务入口(CLI 与 API 共用)
├── api/
│ ├── app.py # FastAPI 实例、生命周期、CORS、静态文件挂载
│ ├── schemas.py # Pydantic 请求 / 响应模型
│ ├── exceptions.py # 统一错误码与全局异常处理
│ ├── dependencies.py # IntegratedQASystem 单例依赖
│ └── routers/
│ ├── chat.py # 同步问答、SSE 流式问答
│ ├── health.py # 健康检查
│ └── sessions.py # 会话历史查询与清空
├── static/
│ ├── index.html # Web 对话页面
│ ├── app.js # 会话管理、SSE 解析、Markdown 渲染
│ └── styles.css # 消息气泡、徽标、Markdown、深色模式样式
├── base/
│ ├── config.py # Pydantic Settings 配置中心
│ └── logger.py # 日志配置
├── qa_system/
│ ├── qa_search.py # BM25 相似问题检索
│ ├── qa_cache.py # Redis 缓存、会话历史
│ ├── qa_db.py # MySQL 连接池与 FAQ 读写
│ ├── qa_utils.py # jieba 分词、停用词与文本预处理
│ ├── res/ # 停用词表等资源文件
│ ├── data/ # 本地 FAQ 数据(已 gitignore)
│ └── main.py # 独立 QA 命令行程序
├── rag_system/
│ ├── main.py # 独立 RAG 命令行程序
│ ├── core/
│ │ ├── rag_pipeline.py # RAG 主流程、缓存、流式生成
│ │ ├── query_classifier.py # BERT 意图分类推理
│ │ ├── strategy_selector.py # 检索策略选择
│ │ ├── vector_store.py # Milvus 混合检索与重排
│ │ ├── document_processor.py # 文档加载、切分、父块组装
│ │ ├── prompts_manager.py # Prompt 模板管理
│ │ └── train_pipeline.py # 意图分类模型离线训练与评估
│ ├── document_loaders/ # 多格式文档加载器与 OCR
│ ├── text_splitters/ # 语义切分器
│ ├── data/ # 本地数据(已 gitignore)
│ ├── docs/ # 知识库原始文档(已 gitignore)
│ └── models/ # 本地模型权重(已 gitignore)
├── docker/
│ └── docker-compose.yml # Milvus / MySQL / Redis 等基础服务
├── logs/ # 运行日志
├── requirements.txt
├── config.ini # 旧版配置样例(当前代码不读取)
└── .env # 本地环境变量(已 gitignore)
```
> `api/` 只负责参数校验、会话管理、SSE 封装与响应格式;问答流程仍由 `main.py` 中的 `IntegratedQASystem.query()` 完成。`static/` 由 FastAPI 直接托管,无独立前端构建产物。
---
## 快速开始
### 1. 环境要求
- Python 3.12+
- Docker / Docker Compose(用于启动 MySQL、Redis、Milvus)
- 可用的 DeepSeek API Key
- 本地模型权重(见 [准备模型](#5-准备模型与知识库))
### 2. 启动基础服务
项目已提供 `docker/docker-compose.yml`,包含 MySQL、Redis、Milvus(etcd + MinIO)以及 Milvus 可视化管理工具 Attu:
```bash
cd docker
docker compose up -d
```
启动后可访问:
| 服务 | 地址 |
| --- | --- |
| Milvus | `localhost:19530` |
| Attu(Milvus 管理界面) | http://localhost:3000 |
| MinIO 控制台 | http://localhost:9001 |
| MySQL | `localhost:3306` |
| Redis | `localhost:6379` |
> `docker-compose.yml` 中的 MySQL root 密码与 Redis 密码仅为演示默认值(`123456` / `1234`),生产环境务必修改,并与 `.env` 保持一致。
### 3. 创建虚拟环境并安装依赖
```bash
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
```
> 首次安装 `torch`、`sentence-transformers`、`FlagEmbedding` 等依赖体积较大,请耐心等待。
### 4. 配置环境变量
在项目根目录创建 `.env`(该文件已被 `.gitignore` 忽略):
```dotenv
# ---------- MySQL ----------
MYSQL_HOST=localhost
MYSQL_PORT=3306
MYSQL_USER=root
MYSQL_PASSWORD=123456
MYSQL_DATABASE=itcast
# ---------- Redis ----------
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=1234
REDIS_DB=0
# ---------- Milvus ----------
MILVUS_HOST=localhost
MILVUS_PORT=19530
MILVUS_DATABASE_NAME=itcast
MILVUS_COLLECTION_NAME=edurag
# ---------- 大模型(DeepSeek,使用 OpenAI 兼容协议)----------
OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxx
LLM_MODEL_PROVIDER=openai
LLM_BASE_URL=https://api.deepseek.com
LLM_QA_MODEL_NAME=deepseek-flash
LLM_RS_MODEL_NAME=deepseek-v4-pro
# ---------- 业务参数 ----------
QA_MIN_SCORE_THRESHOLD=8.0
RETRIEVAL_K=8
CANDIDATE_M=2
RAG_CACHE_ENABLED=true
```
> 由于使用 OpenAI 兼容协议,DeepSeek 的 Key 通过 `OPENAI_API_KEY` 传入。环境变量优先级高于代码默认值;完整配置项见 [base/config.py](base/config.py)。
### 5. 准备模型与知识库
模型权重不纳入 Git,请将以下目录放到 `rag_system/models/`:
```text
rag_system/models/
├── BAAI/
│ ├── bge-m3/ # 稠密 + 稀疏嵌入模型
│ └── bge-reranker-v2-m3/ # 重排序模型
├── bert-intent-recognition-model/ # 微调后的意图分类模型
└── google-bert/
└── bert-base-chinese/ # BERT 基座(训练/调试用)
```
将业务文档放入 `rag_system/docs/`,然后执行一次知识库构建。该命令会加载文档、按父子结构切分、生成 BGE-M3 向量并写入 Milvus:
```bash
python -m rag_system.core.vector_store
```
> 向量主键由「文件路径 + 内容摘要」确定性生成,重复执行会覆盖更新同一条记录,不会产生重复数据。更换嵌入模型或调整切分参数后,建议删除并重建 Milvus 集合。
### 6. 初始化 FAQ 问答库
本地问答依赖 MySQL 中的 `faq` 表:
```sql
CREATE TABLE IF NOT EXISTS faq (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
subject_name VARCHAR(20) NOT NULL,
question VARCHAR(1000) NOT NULL,
answer VARCHAR(10000) NOT NULL
);
```
也可以通过项目内置的 `MySQLClient` 建表并批量导入 CSV:
```python
from qa_system.qa_db import MySQLClient
client = MySQLClient()
client.create_table()
# CSV 需包含三列:学科名称、问题、答案
client.insert_data('path/to/faq.csv')
```
### 7. 启动系统
#### Web / API 服务
```bash
uvicorn api.app:app --reload --port 8000
```
启动后可访问:
| 入口 | 地址 |
| --- | --- |
| Web 对话页面 | http://127.0.0.1:8000 |
| Swagger 文档 | http://127.0.0.1:8000/docs |
| OpenAPI 描述 | http://127.0.0.1:8000/openapi.json |
| 健康检查 | http://127.0.0.1:8000/api/health |
启动说明:
- 应用启动时会初始化 `IntegratedQASystem`,连接 MySQL 与 Redis;但不会加载 BGE-M3、Reranker、BERT 等大模型。
- 如果 `IntegratedQASystem` 初始化失败,FastAPI 仍会启动,健康检查显示 `degraded`,问答接口返回 `503` / `2001`。
- 如果只是 Milvus 不可用,本地问答仍可正常使用;RAG 请求会返回人工客服兜底提示。
- 第一次触发 RAG 时会加载模型,可能出现明显延迟,后续查询会显著加快。
#### 命令行
```bash
python main.py
```
按提示输入问题;输入 `exit` 或 `quit` 退出。
命令行会在答案下方标注来源:
- `[本地问答库]`:命中 Redis / BM25 / MySQL 标准答案
- `[RAG知识库]`:由 RAG 链路生成
- `[Python程序]`:无效问题或系统异常时的兜底提示
---
## 配置说明
配置集中在 [base/config.py](base/config.py),通过环境变量或 `.env` 覆盖。常用配置项如下:
| 配置项 | 默认值 | 说明 |
| --- | --- | --- |
| `MYSQL_HOST` / `MYSQL_PORT` | `localhost` / `3306` | MySQL 连接地址 |
| `MYSQL_USER` / `MYSQL_PASSWORD` | `root` / 空 | MySQL 账号 |
| `MYSQL_DATABASE` | `itcast` | 问答库数据库名 |
| `REDIS_HOST` / `REDIS_PORT` | `localhost` / `6379` | Redis 连接地址 |
| `REDIS_PASSWORD` | 空 | Redis 密码 |
| `MILVUS_HOST` / `MILVUS_PORT` | `localhost` / `19530` | Milvus 连接地址 |
| `MILVUS_COLLECTION_NAME` | `edurag` | 向量集合名 |
| `OPENAI_API_KEY` | 空 | 大模型 API Key |
| `LLM_BASE_URL` | `https://api.deepseek.com` | OpenAI 兼容接口地址 |
| `LLM_QA_MODEL_NAME` | `deepseek-flash` | 问答模型 |
| `LLM_RS_MODEL_NAME` | `deepseek-v4-pro` | 策略选择等推理模型 |
| `LLM_TEMPERATURE` / `LLM_MAX_TOKENS` | `0.1` / `3200` | 大模型生成参数 |
| `QA_MIN_SCORE_THRESHOLD` | `8.0` | BM25 命中阈值,低于该值降级到 RAG |
| `RETRIEVAL_K` | `8` | 每次检索返回的候选数量 |
| `CANDIDATE_M` | `2` | 最终送入大模型的上下文文档数量 |
| `PARENT_CHUNK_SIZE` / `CHILD_CHUNK_SIZE` | `1200` / `300` | 父/子文档切分大小 |
| `RAG_CACHE_ENABLED` | `true` | 是否启用 RAG 答案缓存 |
| `QAS_CACHE_TTL` / `RAG_CACHE_TTL` | 7 天 / 1 天 | 答案缓存有效期(秒) |
| `HISTORY_MAX_TURNS` | `5` | 会话保留的最大轮数 |
| `CONTACT_TEL` | `400-888-6666` | 异常兜底时展示的客服电话 |
| `LOG_FILE` | `logs/app.log` | 日志文件路径 |
| `APP_NAME` / `APP_VERSION` | `EduRAG 智能学科答疑系统` / `1.0.0` | 服务名称与版本 |
| `API_PREFIX` | `/api/v1` | 业务接口前缀 |
| `CORS_ORIGINS` | `["*"]` | 允许跨域的来源列表 |
| `STATIC_DIR` | `static/` | 前端静态资源目录 |
| `HEALTH_PROBE_TIMEOUT` | `2.0` | 健康检查单个依赖的超时时间(秒) |
> 根目录的 `config.ini` 是旧版配置样例,当前代码不再读取,保留仅作参考。
---
## API 接口
FastAPI 应用入口为 `api/main.py`,业务逻辑复用 `IntegratedQASystem`。除健康检查外,业务接口统一使用 `/api/v1` 前缀。
### 服务结构
```text
api/
├── __init__.py
├── app.py # FastAPI 实例、生命周期、CORS、静态文件挂载
├── schemas.py # Pydantic 请求 / 响应模型
├── exceptions.py # 统一错误码与全局异常处理
├── dependencies.py # IntegratedQASystem 单例与依赖注入
└── routers/
├── chat.py # 同步问答、SSE 流式问答
├── health.py # 健康检查
└── sessions.py # 会话历史
```
启动:
```bash
uvicorn api.app:app --reload --port 8000
```
### 接口一览
| 方法 | 路径 | 说明 | 返回类型 |
| --- | --- | --- | --- |
| `GET` | `/api/health` | 服务健康状态与依赖探测 | JSON |
| `POST` | `/api/v1/chat` | 同步问答,一次性返回完整答案 | JSON |
| `POST` | `/api/v1/chat/stream` | 流式问答,SSE 逐块推送 | `text/event-stream` |
| `GET` | `/api/v1/sessions/{session_id}/history` | 获取会话历史 | JSON |
| `DELETE` | `/api/v1/sessions/{session_id}/history` | 清空会话历史 | JSON |
| `GET` | `/` | Web 前端页面 | HTML |
| `GET` | `/static/*` | 前端静态资源 | 静态文件 |
| `GET` | `/docs` | Swagger UI | HTML |
### 统一响应结构
业务接口(健康检查、同步问答、会话历史查询 / 清空)统一使用以下结构:
```json
{
"code": 0,
"message": "ok",
"data": {}
}
```
`source` 使用稳定的机器可读编码,而不是直接返回中文文案:
| 编码 | 对应现有枚举 | 前端展示 |
| --- | --- | --- |
| `qa` | `AnswerSource.SOURCE_QA` | 本地问答库 |
| `rag` | `AnswerSource.SOURCE_RAG` | RAG 知识库 |
| `system` | `AnswerSource.SOURCE_NONE` | 系统提示 / 人工客服 |
### 同步问答
`POST /api/v1/chat`
请求:
```json
{
"question": "AI 大模型课程的学费是多少?",
"session_id": "optional-session-id"
}
```
- `question`:必填,非空字符串,去除首尾空白后长度为 `1~2000`。
- `session_id`:可选,格式为 `^[A-Za-z0-9_-]{1,64}$`;不传或传空字符串时由服务端生成 32 位十六进制 ID,并通过响应返回。
响应:
```json
{
"code": 0,
"message": "ok",
"data": {
"answer": "AI 大模型课程的学费为……",
"source": "qa",
"session_id": "8f1c2d3e4a5b6c7d",
"latency_ms": 12.3,
"cached": true
}
}
```
`cached` 的含义:
- `true`:命中 Redis 精确问答缓存,或命中 RAG 答案缓存。
- `false`:通过 BM25 相似问题命中、实时调用大模型生成,或返回系统兜底提示。
### 流式问答(SSE)
`POST /api/v1/chat/stream`
请求体与同步问答相同。接口返回 `text/event-stream`,事件格式如下:
```text
event: meta
data: {"session_id":"8f1c2d3e4a5b6c7d","source":"rag"}
event: delta
data: {"content":"AI"}
event: delta
data: {"content":" 大模型课程"}
event: done
data: {"latency_ms":1234,"cached":false}
```
事件说明:
| 事件 | 时机 | 内容 |
| --- | --- | --- |
| `meta` | 检索 / 初始化完成、开始输出前 | 会话 ID、答案来源 |
| `delta` | 每生成一个文本块 | 增量内容 |
| `done` | 正常结束 | 总耗时、是否命中缓存 |
| `error` | 发生异常 | 错误码与兜底提示 |
异常事件示例:
```text
event: error
data: {"code":5000,"message":"服务内部错误,请稍后重试"}
```
实现说明:
- 响应头包含 `Cache-Control: no-cache` 与 `X-Accel-Buffering: no`,避免代理或浏览器缓存流式内容。
- 浏览器原生 `EventSource` 只支持 GET,而本接口为 POST,因此前端使用 `fetch` 读取 `ReadableStream` 并解析 SSE。
- 缓存答案会被合并为一次 `delta` 推送,避免逐字符产生大量事件。
- 客户端提前断开时,服务端会关闭底层同步生成器,确保 RAG 生成器的 `finally` 执行,已生成内容仍会写入会话历史。
### 会话历史
`GET /api/v1/sessions/{session_id}/history`
```json
{
"code": 0,
"message": "ok",
"data": {
"session_id": "8f1c2d3e4a5b6c7d",
"messages": [
{"role": "user", "content": "AI 大模型课程的学费是多少?"},
{"role": "assistant", "content": "AI 大模型课程的学费为……"}
]
}
}
```
- 会话不存在或没有历史时返回空数组,不视为错误。
- `session_id` 长度限制为 `1~64`,允许字母、数字、下划线和连字符。
`DELETE /api/v1/sessions/{session_id}/history`
```json
{
"code": 0,
"message": "ok",
"data": {
"session_id": "8f1c2d3e4a5b6c7d",
"deleted": 1
}
}
```
`deleted` 为 Redis 中实际删除的键数量。
### 健康检查
`GET /api/health`
```json
{
"code": 0,
"message": "ok",
"data": {
"status": "up",
"version": "1.0.0",
"uptime_seconds": 3600.12,
"dependencies": {
"mysql": "up",
"redis": "up",
"milvus": "up"
}
}
}
```
- `status`:所有依赖均为 `up` 时为 `up`,否则为 `degraded`;健康检查接口本身仍返回 HTTP `200`。
- `dependencies`:每个依赖为 `up` 或 `down`。
- 探测方式:MySQL 执行 `SELECT 1`,Redis 执行 `PING`,Milvus 调用 `list_collections`。
- 探测不会实例化 `VectorStore`,也不会触发 BGE-M3、Reranker、BERT 模型加载。
- 三个依赖串行探测,单个依赖超时由 `HEALTH_PROBE_TIMEOUT` 控制,默认 2 秒。
### 错误码
| code | HTTP 状态 | 含义 |
| --- | --- | --- |
| `0` | `200` | 成功 |
| `1001` | `400` | 请求参数不合法 |
| `1002` | `404` | 资源或路由不存在 |
| `2001` | `503` | 依赖不可用(问答系统未初始化或上游 5xx) |
| `3001` | `429` | 预留:请求过于频繁 |
| `4001` | `401` | 预留:未认证 |
| `5000` | `500` | 服务内部错误 |
参数校验失败时,`data` 会包含字段位置、错误信息和错误类型:
```json
{
"code": 1001,
"message": "请求参数不合法",
"data": [
{
"loc": ["body", "question"],
"msg": "String should have at least 1 character",
"type": "string_too_short"
}
]
}
```
### API 层实现要点
- **复用业务逻辑**:API 层只负责参数校验、会话管理与响应封装,问答仍由 `IntegratedQASystem.query()` 完成。
- **同步路由 + 线程池**:Redis、MySQL、Milvus、BM25 与模型推理目前均为同步阻塞操作,路由使用同步 `def`,由 FastAPI 自动放入线程池执行,避免阻塞事件循环。
- **RAG 延迟初始化**:只有真正触发降级时才加载 BGE-M3、Reranker 与 BERT;初始化使用双重检查锁,避免并发请求重复加载模型。
- **降级启动**:`IntegratedQASystem` 初始化失败时应用仍可启动,`/api/health` 会显示 `degraded`,问答接口返回 `503` / `2001`。
- **统一异常处理**:参数校验、404、401、429、5xx 与未捕获异常都转换为标准 JSON,未捕获异常只记录日志,不向客户端暴露堆栈。
- **CORS 与静态资源**:默认允许任意来源(`CORS_ORIGINS=["*"]`,不携带凭证);FastAPI 直接挂载 `static/` 并返回首页。
- **SSE 代理配置**:使用 Nginx 时需关闭响应缓冲(`proxy_buffering off;`),并适当延长读取超时。
---
## Web 前端
前端定位为**单页对话式答疑界面**,由 FastAPI 直接托管,无需单独构建流程。
### 页面功能
- 对话框式交互,支持多轮会话。
- SSE 流式渲染,逐字展示答案。
- 答案来源徽标:`本地问答库` / `RAG 知识库` / `系统提示`。
- 新建会话、清空上下文、切换历史会话。
- 停止生成:流式过程中可中断请求。
- Markdown 渲染、代码块高亮、复制答案。
- 加载状态、异常提示与系统通知。
- 响应式布局,适配桌面与移动端。
- 深色 / 浅色主题切换,主题偏好保存在浏览器本地。
### 技术实现
- 原生 HTML + CSS + JavaScript(IIFE),无打包步骤。
- Tailwind CSS 通过 CDN 引入,使用 `darkMode: 'class'` 支持深色模式。
- `marked@12.0.2` 渲染 Markdown,`DOMPurify@3.1.6` 净化 HTML,`highlight.js@11.9.0` 高亮代码块。
- 使用 `fetch` + `ReadableStream` 消费 POST SSE;通过 `AbortController` 实现停止生成。
- 会话列表与当前会话 ID 保存在 `localStorage`;对话历史仍由 Redis 按 `session_id` 存储,切换会话时通过历史接口恢复。
### 目录与托管
```text
static/
├── index.html
├── app.js
└── styles.css
```
FastAPI 中的挂载方式:
```python
from fastapi.staticfiles import StaticFiles
app.mount('/static', StaticFiles(directory='static'), name='static')
```
访问 `http://127.0.0.1:8000/` 即可使用对话页面。
### 当前限制
- 会话列表保存在浏览器 `localStorage` 中,是**单浏览器**的会话索引,不是服务端统一会话列表;换浏览器后历史仍可通过已知 `session_id` 访问,但列表不会自动出现。
- 历史接口只存储 `role` 与 `content`,因此恢复历史消息时不显示当时的来源徽标。
- 前端依赖 CDN 资源,离线环境需改为本地静态文件;生产环境建议使用本地构建或固定 CDN 版本。
- 首次触发 RAG 时模型加载较慢,前端会显示加载动画,期间可能暂时收不到 `meta` 事件。
---
## 部署
> 当前仓库提供 `docker/docker-compose.yml` 用于启动基础服务,但尚未提供应用侧 `Dockerfile` 与生产启动脚本。
### 开发环境
```bash
uvicorn api.app:app --reload --host 127.0.0.1 --port 8000
```
### 生产环境建议
```bash
uvicorn api.app:app --host 0.0.0.0 --port 8000 --workers 1
```
注意事项:
- **建议单 Worker 运行**:BGE-M3、Reranker、BERT 等模型会占用较多内存,多进程会重复加载模型;如需横向扩展,应将模型推理拆分为独立服务。
- **基础服务外置**:MySQL、Redis、Milvus 使用独立容器或托管服务,应用容器只负责 API 与前端。
- **反向代理**:Nginx 负责 TLS、静态资源与 SSE 转发;SSE 需关闭缓冲,并延长读取超时。
- **健康检查**:容器编排使用 `/api/health` 作为存活/就绪探针;依赖不可用时接口仍返回 `200`,请根据 `status` 字段判断是否就绪。
- **密钥管理**:`.env` 不进入镜像,通过环境变量或密钥管理服务注入。
- **日志与监控**:保留 `logs/app.log`;API 层建议增加请求 ID、耗时与错误率指标。
- **CORS**:生产环境应将 `CORS_ORIGINS` 从 `["*"]` 收紧为实际前端域名。
---
## 开发路线图
- [x] Redis + MySQL + BM25 本地问答
- [x] RAG 意图分类与检索策略路由
- [x] BGE-M3 混合检索与 BGE Reranker 重排
- [x] 父文档—子文档切分与命中高亮
- [x] QA → RAG 自动降级与异常兜底
- [x] 答案缓存、会话历史与流式输出
- [x] 多格式文档加载与 OCR
- [x] FastAPI 应用骨架(`api/main.py`、Routers、Schemas)
- [x] 同步问答接口 `POST /api/v1/chat`
- [x] SSE 流式接口 `POST /api/v1/chat/stream`
- [x] 健康检查与会话历史接口
- [x] Web 对话前端
- [ ] RAG 模型预热与启动自检
- [ ] Dockerfile 与一键部署脚本
- [ ] 单元测试与接口测试
- [ ] 鉴权、限流与可观测性
- [ ] 服务端会话列表与跨设备同步
---
## 常见问题
### 1. 启动时报 Redis / MySQL 连接失败
确认基础服务已启动:
```bash
docker ps
```
并检查 `.env` 中的地址、端口、密码是否与 `docker-compose.yml` 一致。
FastAPI 启动时如果 `IntegratedQASystem` 初始化失败,应用仍会以降级模式启动。此时可以访问 `/api/health` 查看各依赖状态,或查看 `logs/app.log` 中的初始化异常。
### 2. Milvus 连接失败
Milvus 依赖 etcd 与 MinIO,首次启动需要一定时间。可通过以下方式确认:
```bash
docker compose -f docker/docker-compose.yml ps
```
并访问 Attu(http://localhost:3000)查看 Milvus 是否就绪。
### 3. 第一次专业问题响应很慢
RAG 采用延迟初始化:只有在本地问答未命中时才会加载 BGE-M3、Reranker 与 BERT 模型。首次加载耗时属于正常现象,后续查询会显著加快。
- 命令行启动和 FastAPI 启动都不会等待模型加载。
- 健康检查也不会触发模型加载。
- SSE 连接在模型初始化期间可能暂时没有 `meta` 事件,前端会保持加载动画。
### 4. 调用大模型返回 401 或鉴权失败
- 检查 `OPENAI_API_KEY` 是否为有效的 DeepSeek Key。
- 确认 `LLM_BASE_URL` 为 `https://api.deepseek.com`。
- 确认 `LLM_MODEL_PROVIDER=openai`(使用 OpenAI 兼容协议)。
### 5. 提示找不到模型或向量维度不匹配
- 确认 `rag_system/models/` 下的模型目录完整。
- 模型权重已被 `.gitignore` 忽略,从 Git 拉取代码后需要单独下载。
- 更换嵌入模型后,需要重建 Milvus 集合,否则会出现维度不匹配。
### 6. 修改知识库后答案没有更新
答案会被写入 Redis 缓存。知识库或 Prompt 更新后,可分别调用 `RedisClient.for_qas().clear_cache()` 与 `RedisClient.for_rag().clear_cache()` 按业务前缀清理缓存,或在 Redis 中删除对应键。
### 7. 多进程部署后内存占用过高
每个进程都会独立加载 BGE-M3、Reranker 与 BERT。建议 API 服务使用单 Worker;需要扩容时将模型推理拆分为独立服务。
### 8. 前端可以打开,但问答接口返回 503
说明 `IntegratedQASystem` 初始化失败,通常由 MySQL 或 Redis 不可用导致。先检查 `/api/health` 中的依赖状态,再查看 `logs/app.log` 中的初始化异常。若仅 Milvus 不可用,本地问答仍可用,RAG 请求会返回人工客服兜底提示。
### 9. SSE 在 Nginx 后变成一次性返回
确认反向代理关闭了响应缓冲:
```nginx
location /api/v1/chat/stream {
proxy_buffering off;
proxy_read_timeout 300s;
proxy_pass http://127.0.0.1:8000;
}
```
API 已经设置 `X-Accel-Buffering: no`,但部分代理仍需显式关闭缓冲。
### 10. 切换会话后历史为空
历史记录按 `session_id` 隔离。新建会话会生成新的 ID;如果使用自定义 ID,请确保请求中的 `session_id` 与写入时一致。未知会话会返回空历史,而不是报错。
---
## 作者
**骆昊**
本项目用于智能答疑系统的教学与实践,涵盖 BM25 本地问答、RAG 检索增强、重排序、意图识别与服务化改造等完整链路。