# RAG **Repository Path**: Loveanmi/rag ## Basic Information - **Project Name**: RAG - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-04-09 - **Last Updated**: 2026-04-12 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # RuoYi + FastAPI + Qdrant 知识库项目技术方案 ## 1. 项目目标 基于 `RuoYi + JDK 1.8` 构建一个企业知识库问答系统,支持用户上传可读取文档,系统自动完成解析、切片、向量化与检索,并结合大模型输出自然语言答案,且支持流式返回。 系统目标能力: - 支持上传 `txt`、`docx`、`pdf` 等文档 - 支持按知识库管理文档 - 支持文档解析、切片、向量入库 - 支持用户自然语言提问 - 支持基于知识库内容的语义检索 - 支持大模型生成答案并流式输出 - 支持来源引用、会话记录、导入状态追踪 本方案采用以下架构思路: - `RuoYi` 负责业务管理、权限、页面、会话与文件管理 - `FastAPI` 负责 AI 检索与问答服务 - `Qdrant` 负责向量存储与相似度检索 - `MySQL` 负责业务数据存储 - `本地磁盘 / MinIO` 负责原始文件存储 ## 2. 总体架构 ```text +-----------------------+ | Web / RuoYi 前端 | | 上传文档 / 聊天问答 | +-----------+-----------+ | v +-----------------------+ | RuoYi Java 服务 | | JDK 1.8 / SpringBoot | | - 登录认证 | | - 权限控制 | | - 知识库管理 | | - 文件上传 | | - 会话管理 | | - SSE代理 | +-----+------------+----+ | | | v | +------------------+ | | MySQL | | | 业务表 / 日志表 | | +------------------+ | v +-----------------------+ | FastAPI AI 服务 | | - 文档解析 | | - 文本切片 | | - embedding 生成 | | - Qdrant 检索 | | - Prompt 组装 | | - 大模型流式回答 | +-----+------------+----+ | | | v | +------------------+ | | 文件存储 | | | local / MinIO | | +------------------+ | v +-----------------------+ | Qdrant | | 向量 / payload / 检索 | +-----------------------+ ``` ## 3. 架构职责划分 ### 3.1 RuoYi 侧职责 - 用户登录、权限控制、角色管理 - 知识库管理 - 文件上传与文档状态展示 - 对话页面与会话管理 - 文档导入任务状态展示 - 问答日志、引用记录、审计日志 - 代理 AI 服务的 SSE 输出 ### 3.2 FastAPI 侧职责 - 解析 `txt/docx/pdf` - 文本清洗、切片 - 调用 embedding 模型生成向量 - 管理 Qdrant collection - 执行语义检索与 metadata 过滤 - 组装 prompt 并调用大模型 - 以流式方式返回生成结果 ### 3.3 Qdrant 侧职责 - 存储 chunk 向量 - 存储检索 payload - 按知识库、文档、标签过滤 - 返回相似度最高的内容片段 ## 4. 技术选型 ### 4.1 管理端与业务端 - 框架:`RuoYi` - 运行时:`JDK 1.8` - Web:Spring Boot + Spring MVC - 持久层:MyBatis - 业务数据库:`MySQL 5.7 / 8.0` - 流式输出:`SseEmitter` ### 4.2 AI 服务 - 框架:`FastAPI` - 运行:`uvicorn` - 文档解析: - `python-docx` - `pypdf` 或 `pdfplumber` - 向量库客户端:`qdrant-client` - 模型调用:OpenAI 兼容接口 - 流式输出:FastAPI `StreamingResponse` ### 4.3 向量库 - 向量数据库:`Qdrant` - 距离类型:`Cosine` - 存储结构:单 collection + payload 过滤 ### 4.4 文件存储 - 开发环境:本地文件系统 - 生产环境:`MinIO` 或云对象存储 ## 5. 核心业务流程 ### 5.1 文档导入流程 ```text 1. 用户上传文档到 RuoYi 2. RuoYi 保存文件并写入 kb_file 3. RuoYi 创建导入任务 kb_ingest_task 4. RuoYi 调用 FastAPI /api/v1/ingest 5. FastAPI 读取文件并解析文本 6. FastAPI 对文本进行清洗与切片 7. FastAPI 生成每个 chunk 的 embedding 8. FastAPI 批量写入 Qdrant 9. FastAPI 返回导入结果 10. RuoYi 更新文档状态和 chunk 数 ``` ### 5.2 问答流程 ```text 1. 用户在聊天页发起提问 2. RuoYi 创建用户消息记录 3. RuoYi 调用 FastAPI /api/v1/chat/stream 4. FastAPI 将问题转为 embedding 5. FastAPI 在 Qdrant 检索 topK 个 chunk 6. FastAPI 组装上下文 prompt 7. FastAPI 调用大模型流式生成答案 8. RuoYi 代理流式结果给前端 9. RuoYi 保存 assistant 消息和引用信息 ``` ### 5.3 删除文档流程 ```text 1. 管理员删除文档 2. RuoYi 调用 FastAPI 删除该 file_id 对应的向量 3. FastAPI 按 kb_id + file_id 删除 Qdrant points 4. RuoYi 逻辑删除 kb_file 5. 会话历史保留,引用可标记为已失效来源 ``` ## 6. 模块设计 ## 6.1 RuoYi 模块 建议新增 `ruoyi-knowledge` 模块,包含以下子模块。 ### 6.1.1 知识库管理模块 功能: - 新增知识库 - 编辑知识库 - 停用/启用知识库 - 绑定可访问角色或部门 ### 6.1.2 文档管理模块 功能: - 上传文档 - 查看文档状态 - 重新索引 - 删除文档 - 查看导入日志 ### 6.1.3 对话管理模块 功能: - 创建会话 - 会话列表 - 历史消息查看 - 引用来源查看 ### 6.1.4 模型配置模块 功能: - 配置 AI 服务地址 - 配置 embedding 模型 - 配置 chat 模型 - 配置 topK - 配置 chunk size / overlap ### 6.1.5 日志模块 功能: - 导入任务日志 - 问答日志 - 异常日志 - 模型调用日志 ## 6.2 FastAPI 模块 建议目录结构: ```text ai-service/ ├── app/ │ ├── api/ │ │ ├── ingest.py │ │ ├── chat.py │ │ ├── retrieve.py │ │ └── health.py │ ├── core/ │ │ ├── config.py │ │ └── logger.py │ ├── models/ │ │ ├── request.py │ │ └── response.py │ ├── services/ │ │ ├── parser_service.py │ │ ├── chunk_service.py │ │ ├── embedding_service.py │ │ ├── qdrant_service.py │ │ ├── retrieval_service.py │ │ └── llm_service.py │ └── main.py └── requirements.txt ``` 各服务职责: - `parser_service.py`:按文件格式提取纯文本 - `chunk_service.py`:文本切片与 metadata 构造 - `embedding_service.py`:批量生成 embedding - `qdrant_service.py`:collection 管理、upsert、search、delete - `retrieval_service.py`:召回与过滤 - `llm_service.py`:生成 prompt 与流式问答 ## 7. MySQL 表设计 ## 7.1 知识库表 ```sql CREATE TABLE kb_knowledge_base ( id BIGINT PRIMARY KEY AUTO_INCREMENT, kb_code VARCHAR(64) NOT NULL UNIQUE, kb_name VARCHAR(128) NOT NULL, kb_desc VARCHAR(500), status CHAR(1) DEFAULT '0', create_by VARCHAR(64), create_time DATETIME, update_by VARCHAR(64), update_time DATETIME, remark VARCHAR(500) ); ``` ## 7.2 文档表 ```sql CREATE TABLE kb_file ( id BIGINT PRIMARY KEY AUTO_INCREMENT, kb_id BIGINT NOT NULL, file_name VARCHAR(255) NOT NULL, file_ext VARCHAR(32), file_size BIGINT, storage_type VARCHAR(32) DEFAULT 'local', file_path VARCHAR(500) NOT NULL, file_hash VARCHAR(128), parse_status VARCHAR(32) DEFAULT 'PENDING', parse_msg VARCHAR(1000), chunk_count INT DEFAULT 0, version_no INT DEFAULT 1, del_flag CHAR(1) DEFAULT '0', create_by VARCHAR(64), create_time DATETIME, update_by VARCHAR(64), update_time DATETIME ); ``` ## 7.3 导入任务表 ```sql CREATE TABLE kb_ingest_task ( id BIGINT PRIMARY KEY AUTO_INCREMENT, kb_id BIGINT NOT NULL, file_id BIGINT NOT NULL, task_no VARCHAR(64) NOT NULL UNIQUE, status VARCHAR(32) DEFAULT 'PENDING', retry_count INT DEFAULT 0, start_time DATETIME, finish_time DATETIME, error_msg VARCHAR(2000), create_time DATETIME ); ``` ## 7.4 会话表 ```sql CREATE TABLE kb_chat_session ( id BIGINT PRIMARY KEY AUTO_INCREMENT, kb_id BIGINT NOT NULL, session_name VARCHAR(255), user_id BIGINT, status VARCHAR(32) DEFAULT 'ACTIVE', create_time DATETIME, update_time DATETIME ); ``` ## 7.5 消息表 ```sql CREATE TABLE kb_chat_message ( id BIGINT PRIMARY KEY AUTO_INCREMENT, session_id BIGINT NOT NULL, role VARCHAR(32) NOT NULL, content LONGTEXT, model_name VARCHAR(128), prompt_tokens INT DEFAULT 0, completion_tokens INT DEFAULT 0, total_tokens INT DEFAULT 0, latency_ms INT DEFAULT 0, message_status VARCHAR(32) DEFAULT 'SUCCESS', create_time DATETIME ); ``` ## 7.6 引用表 ```sql CREATE TABLE kb_chat_reference ( id BIGINT PRIMARY KEY AUTO_INCREMENT, message_id BIGINT NOT NULL, file_id BIGINT NOT NULL, chunk_id VARCHAR(128) NOT NULL, source_name VARCHAR(255), score DECIMAL(10,6), content_snippet VARCHAR(2000), create_time DATETIME ); ``` ## 7.7 模型配置表 ```sql CREATE TABLE kb_model_config ( id BIGINT PRIMARY KEY AUTO_INCREMENT, config_name VARCHAR(128) NOT NULL, provider VARCHAR(64) NOT NULL, api_base VARCHAR(255), api_key VARCHAR(500), embedding_model VARCHAR(128), chat_model VARCHAR(128), top_k INT DEFAULT 5, chunk_size INT DEFAULT 800, chunk_overlap INT DEFAULT 150, temperature DECIMAL(4,2) DEFAULT 0.20, stream_enabled CHAR(1) DEFAULT '1', status CHAR(1) DEFAULT '0', create_time DATETIME, update_time DATETIME ); ``` ## 8. Qdrant 设计 ## 8.1 Collection 设计 建议第一版使用单 collection: - collection 名称:`knowledge_chunks` 向量配置: - `size`:与 embedding 模型维度一致 - `distance`:`Cosine` 示例: ```json { "collection_name": "knowledge_chunks", "vectors": { "size": 1536, "distance": "Cosine" } } ``` 说明: - 如果使用的 embedding 模型维度不是 `1536`,要同步修改 collection 维度 ## 8.2 Point 结构设计 每个 point 对应一个文档切片: ```json { "id": "1_1001_0001", "vector": [0.12, 0.34, 0.56], "payload": { "kb_id": 1, "file_id": 1001, "chunk_id": "1001_0001", "chunk_index": 1, "file_name": "员工手册.docx", "content": "员工年休假按照累计工作年限确定......", "page_no": 6, "section_name": "休假制度", "doc_type": "docx", "tags": ["制度", "人事"] } } ``` ## 8.3 为什么使用单 collection 第一版不建议每个知识库建一个 collection,原因: - collection 数量过多时维护复杂 - 检索逻辑会分散 - 用 `payload.kb_id` 过滤已经足够满足多知识库隔离 建议统一在一个 collection 内,通过 filter 条件区分: - `kb_id` - `file_id` - `doc_type` - `tags` ## 8.4 检索过滤策略 用户提问时,至少加上: - `kb_id` 等值过滤 如果未来要支持更细颗粒度控制,可以增加: - 标签过滤 - 文档类型过滤 - 指定文档过滤 ## 9. 文档解析与切片策略 ## 9.1 支持格式 第一版建议支持: - `txt` - `docx` - `pdf` 老式 `doc` 可作为第二阶段扩展,避免初期解析复杂度过高。 ## 9.2 文档解析输出 统一输出结构: ```json { "title": "员工手册", "content": "完整文本内容", "meta": { "source_type": "docx" } } ``` ## 9.3 切片策略 推荐参数: - `chunk_size = 800` - `chunk_overlap = 150` 切片优先级: 1. 按标题切 2. 按段落切 3. 按句号、分号切 4. 固定长度兜底 建议保留以下 metadata: - `kb_id` - `file_id` - `chunk_id` - `chunk_index` - `file_name` - `page_no` - `section_name` ## 10. 检索与问答设计 ## 10.1 检索流程 ```text 问题 -> embedding -> Qdrant search -> topK chunks -> prompt -> LLM answer ``` 推荐参数: - `topK = 5` - 检索阶段可先取 `10` - 最终送模型上下文取 `3~5` 条 ## 10.2 问题改写 第一版可选,不强制。 作用: - 将口语化问题改写成更适合检索的表达 - 提取关键实体、制度名称、业务词 例子: - 用户问题:`公司病假最多能休多久` - 检索改写:`病假 医疗期 最长时长 休假制度` ## 10.3 Prompt 设计 系统提示词建议: ```text 你是企业内部知识库问答助手。 请严格基于提供的知识片段回答问题。 如果知识片段不足以支撑结论,请明确回复“根据当前知识库内容无法确定”。 不要编造制度、流程、数字和结论。 请尽量给出简洁准确的答案,并在最后附上来源名称。 ``` 上下文结构建议: ```text 用户问题: {question} 检索资料: 1. 来源:员工手册.docx 内容:... 2. 来源:考勤制度.docx 内容:... 请根据以上资料回答。 ``` ## 10.4 引用来源 回答后应保存引用: - 来源文档名 - chunk_id - 检索分数 - 片段摘要 这样便于: - 前端展示依据 - 后续问题排查 - 用户建立信任 ## 11. SSE 流式输出设计 ## 11.1 总体原则 建议由前端只连接 RuoYi,RuoYi 再代理 FastAPI 的流式输出。 优点: - 权限统一 - Python 服务不直接暴露 - 前端实现更稳定 ## 11.2 事件格式建议 ```text event: status data: {"stage":"retrieving","message":"正在检索知识库"} event: token data: {"content":"根据员工手册规定,"} event: token data: {"content":"累计工作满1年不满10年的员工享有5天年假。"} event: reference data: {"source":"员工手册.docx","chunk_id":"1001_0001","score":0.92} event: done data: {"messageId":9001} ``` ## 11.3 RuoYi 实现建议 使用 `SseEmitter`: - 前端连接 `/knowledge/chat/stream` - 后端在控制器中创建 `SseEmitter` - 调用 Python 服务的流式接口 - 每收到一个 token 就转发给前端 - 流结束后保存消息与引用记录 ## 12. 接口设计 ## 12.1 RuoYi 对前端接口 ### 创建知识库 - `POST /knowledge/base` ### 查询知识库列表 - `GET /knowledge/base/list` ### 上传文档 - `POST /knowledge/file/upload` 表单参数: - `kbId` - `file` ### 文档列表 - `GET /knowledge/file/list?kbId=1` ### 重新索引 - `POST /knowledge/file/reindex/{fileId}` ### 删除文档 - `DELETE /knowledge/file/{fileId}` ### 创建会话 - `POST /knowledge/chat/session` ### 流式问答 - `GET /knowledge/chat/stream?kbId=1&sessionId=100&question=...` ### 查询历史消息 - `GET /knowledge/chat/history/{sessionId}` ## 12.2 RuoYi 对 FastAPI 接口 ### 导入文档 - `POST /api/v1/ingest` 请求示例: ```json { "kb_id": 1, "file_id": 1001, "file_name": "员工手册.docx", "file_path": "/data/upload/员工手册.docx", "chunk_size": 800, "chunk_overlap": 150 } ``` ### 删除文档向量 - `POST /api/v1/index/delete_by_file` ### 检索测试 - `POST /api/v1/retrieve` ### 流式问答 - `POST /api/v1/chat/stream` 请求示例: ```json { "kb_id": 1, "session_id": 100, "question": "员工年假怎么规定的?", "top_k": 5, "stream": true } ``` ### 健康检查 - `GET /api/v1/health` ## 13. 安全与权限设计 ### 13.1 权限设计 建议角色: - 超级管理员 - 知识库管理员 - 普通用户 控制点: - 谁可以创建知识库 - 谁可以上传文档 - 谁可以删除文档 - 谁可以访问指定知识库 - 谁可以查看历史消息 ### 13.2 安全设计 - 限制上传文件后缀与大小 - 文件名进行规范化处理 - AI 服务只开放内网 - API key 只保存于服务端配置 - 问答接口增加频率限制 - 关键操作写审计日志 ## 14. 异常与容错 需要处理以下场景: - 上传成功但解析失败 - embedding 接口失败 - Qdrant upsert 失败 - 检索结果为空 - LLM 流式生成中断 建议处理方式: - 文档状态机清晰化:`PENDING / PROCESSING / SUCCESS / FAILED` - 失败原因落表 - 支持手工重试 - 聊天响应中断时保留已输出内容 ## 15. 部署建议 ## 15.1 最小可用部署 - `Nginx` - `RuoYi Java 服务` - `FastAPI AI 服务` - `MySQL` - `Qdrant` - `Redis`(可选) - `MinIO`(可选) ## 15.2 Docker 组合建议 ```text docker-compose services: - nginx - ruoyi-app - ai-service - mysql - qdrant - redis - minio ``` 开发期可先不接 `Redis` 和 `MinIO`。 ## 16. 开发阶段规划 ## 16.1 第一阶段:MVP 目标:先跑通完整链路 范围: - 知识库管理 - 上传 `txt/docx/pdf` - 文档解析 - 切片与向量入库 - Qdrant 检索 - 单轮问答 - SSE 流式输出 - 引用展示 ## 16.2 第二阶段:增强 - 多轮上下文 - 问题改写 - metadata 高级过滤 - 文档标签 - 检索效果调优 ## 16.3 第三阶段:生产 - OCR 扩展 - Excel 解析 - 重排模型 - 监控报警 - 成本统计 - 多知识库权限继承 ## 17. 推荐开发顺序 1. 建立 MySQL 业务表 2. 部署并初始化 Qdrant 3. 搭建 FastAPI 基础项目 4. 打通文档上传与导入 5. 实现 embedding + Qdrant upsert 6. 实现 Qdrant 检索接口 7. 实现大模型流式问答 8. 在 RuoYi 中接入聊天页面与 SSE 9. 增加日志、失败重试、来源展示 ## 18. 本方案的最终结论 这是一个适合你当前技术栈的落地方案: - `RuoYi + JDK 1.8` 保留现有后台管理能力 - `FastAPI` 承担 AI 检索和问答能力 - `Qdrant` 作为真正的向量数据库承担语义检索 - `MySQL` 负责业务数据 - `SSE` 负责流式用户体验 这样既兼顾现有项目基础,也能让你在实践中真正学到向量库、RAG 和流式问答这几个关键能力。 ## 19. 下一步建议 当前文档已经可以作为项目蓝图使用。建议下一步继续补以下内容中的任意一项: 1. 输出 `MySQL` 完整建表 SQL 文件 2. 输出 `FastAPI + Qdrant` 项目骨架 3. 输出 `RuoYi` 知识库模块 `Controller / Service / Mapper` 设计 4. 输出本地开发环境 `docker-compose.yml`