# RagPro **Repository Path**: freeed/rag-pro ## Basic Information - **Project Name**: RagPro - **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-22 - **Last Updated**: 2026-10-22 ## 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 检索增强、重排序、意图识别与服务化改造等完整链路。