From d589c27bceaa1822604ed1a2f189f84d490121e6 Mon Sep 17 00:00:00 2001 From: lacerate551 <128470311+lacerate551@users.noreply.github.com> Date: Sun, 21 Jun 2026 20:53:33 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E6=B8=85=E7=90=86=E8=BF=87=E6=97=B6?= =?UTF-8?q?=E6=96=87=E6=A1=A3=20+=20=E6=9B=B4=E6=96=B0=E5=85=B3=E9=94=AE?= =?UTF-8?q?=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 删除(10个): - API与后端对接规范.md(已被后端对接规范.md 替代) - image_processing_flow.md(已合入 RAG数据流程.md) - 状态码功能更新说明.md(已合入后端对接规范.md) - 题目模板.md(字段名与实际 API 不一致,以对接指南为准) - 出题批卷系统设计.md(旧版出题设计,已被对接指南+变更说明替代) - 测试指南.md(出题 API 格式过时,模型名过时) - 企业文档更新管理方案.md(设计方案,已实施完成) - 版本管理实施完成报告.md(里程碑报告,已完成归档) - 生产路径优化计划.md(行号已偏移,阶段状态过时) 更新(7个): - 出题批题后端对接指南.md:添加 generate-smart 端点、question_content→content - 开发与系统模块说明.md:更新 reranker 和 LLM 模型名称 - 多源信息融合指南.md:标注生产路径 vs 备用路径 - 架构与部署方案.md:更新数据归属对照表(4个 SQLite DB) - 认证与权限配置指南.md:出题 API 更新为 /exam/generate - 向量库边界风险分析.md:标注 P1 旧切片残留和 P2 文件更新已修复 - 风险边界问题修复注意事项.md:标注所有场景已修复 --- docs/API与后端对接规范.md | 1175 ------------------------------ docs/image_processing_flow.md | 233 ------ docs/企业文档更新管理方案.md | 785 -------------------- docs/出题批卷系统设计.md | 635 ---------------- docs/出题批题后端对接指南.md | 21 +- docs/向量库边界风险分析.md | 26 +- docs/多源信息融合指南.md | 2 + docs/开发与系统模块说明.md | 4 +- docs/架构与部署方案.md | 9 +- docs/测试指南.md | 485 ------------ docs/版本管理实施完成报告.md | 415 ----------- docs/状态码功能更新说明.md | 219 ------ docs/生产路径优化计划.md | 367 ---------- docs/认证与权限配置指南.md | 4 +- docs/题目模板.md | 80 -- docs/风险边界问题修复注意事项.md | 2 + 16 files changed, 40 insertions(+), 4422 deletions(-) delete mode 100644 docs/API与后端对接规范.md delete mode 100644 docs/image_processing_flow.md delete mode 100644 docs/企业文档更新管理方案.md delete mode 100644 docs/出题批卷系统设计.md delete mode 100644 docs/测试指南.md delete mode 100644 docs/版本管理实施完成报告.md delete mode 100644 docs/状态码功能更新说明.md delete mode 100644 docs/生产路径优化计划.md delete mode 100644 docs/题目模板.md diff --git a/docs/API与后端对接规范.md b/docs/API与后端对接规范.md deleted file mode 100644 index 46f2106..0000000 --- a/docs/API与后端对接规范.md +++ /dev/null @@ -1,1175 +0,0 @@ -# RAG 服务 API 接口规范 - -> 本文档供后端开发人员参考,用于对接 RAG 知识库服务。 -> -> **文档版本**: v3.1 -> **最后更新**: 2026-04-26 -> **维护者**: RAG服务开发组 - ---- - -## 一、服务概述 - -### 1.1 职责边界 - -| 职责 | 后端负责 | RAG服务负责 | -|------|----------|-------------| -| **用户认证** | JWT/Session 验证 | - | -| **权限判断** | 判断用户可访问的知识库 | - | -| **会话管理** | 创建/删除会话,存储消息 | - | -| **知识库问答** | - | 检索 + 生成回答 | -| **向量检索** | - | 向量 + BM25 混合检索 | -| **文档处理** | - | 上传、解析、切片、向量化 | -| **反馈系统** | - | 收集反馈、FAQ 管理 | - -### 1.2 数据流 - -``` -┌─────────┐ ┌─────────┐ ┌─────────┐ -│ 前端 │───▶│ 后端 │───▶│ RAG │ -└─────────┘ └─────────┘ └─────────┘ - │ │ - ▼ ▼ - ┌──────────┐ ┌──────────┐ - │ 权限判断 │ │ 向量检索 │ - │ 会话管理 │ │ 生成回答 │ - └──────────┘ └──────────┘ -``` - -### 1.3 服务端口 - -- **默认端口**: 5001 -- **启动命令**: `python main.py` - ---- - -## 二、认证方式 - -### 2.1 生产模式(APP_ENV=prod) - -后端调用 RAG 服务时,**不需要传认证 Header**。权限由后端通过 `collections` 参数控制。 - -```http -POST http://localhost:5001/rag -Content-Type: application/json - -{ - "message": "出差补助标准是什么?", - "collections": ["public_kb", "dept_finance"], - "chat_history": [] -} -``` - -### 2.2 开发模式(APP_ENV=dev) - -支持模拟用户测试,可通过 Header 传递用户信息: - -```http -POST http://localhost:5001/rag -Content-Type: application/json -X-User-ID: admin001 -X-User-Role: admin -X-User-Department: 技术部 - -{ - "message": "问题", - "collections": ["public_kb"], - "chat_history": [] -} -``` - -**模拟用户列表**: - -| X-User-ID | X-User-Role | 说明 | -|-----------|-------------|------| -| admin001 | admin | 管理员 | -| manager001 | manager | 部门管理员 | -| user001 | user | 普通用户 | - -### 2.3 环境配置 - -```bash -# 生产环境 -APP_ENV=prod -DASHSCOPE_API_KEY=your-api-key-here - -# 开发环境(默认) -APP_ENV=dev -``` - ---- - -## 三、核心接口 - -### 3.1 健康检查 - -``` -GET /health -``` - -**认证**: 不需要 - -**响应**: -```json -{ - "status": "ok", - "knowledge_base": "多向量库模式 (按集合提供服务)", - "bm25_index": "动态按需加载", - "mode": "Agentic RAG" -} -``` - -### 3.2 知识库问答(核心接口) - -``` -POST /rag -``` - -**认证**: 不需要(生产模式) - -**请求体**: -```json -{ - "message": "用户问题", - "collections": ["public_kb", "dept_finance"], - "chat_history": [ - {"role": "user", "content": "历史问题"}, - {"role": "assistant", "content": "历史回答"} - ] -} -``` - -**参数说明**: - -| 参数 | 类型 | 必需 | 说明 | -|------|------|------|------| -| `message` | string | ✅ | 用户问题 | -| `collections` | string[] | ✅ | 用户有权限的知识库列表,由后端判断后传入 | -| `chat_history` | array | ✅ | 对话历史(生产环境必须传入) | - -**响应**: SSE 流式返回 - -``` -Content-Type: text/event-stream - -data: {"type": "start", "message": "正在检索知识库..."} - -data: {"type": "sources", "sources": [{"source": "doc.pdf", "page": 1, "page_range": "1", "section": "", "chunk_type": "text", "score": 0.95}]} - -data: {"type": "chunk", "content": "根"} - -data: {"type": "chunk", "content": "据"} - -... - -data: {"type": "finish", "answer": "完整答案...", "mode": "rag", "sources": [...], "images": [], "tables": [], "duration_ms": 1500} -``` - -**SSE 事件类型**: - -| 事件类型 | 说明 | -|---------|------| -| `start` | 开始处理 | -| `sources` | 检索到的来源 | -| `chunk` | 每个 token(打字机效果) | -| `finish` | 完整响应对象(**必须消费**) | -| `error` | 错误事件 | - -**finish 事件结构**: -```json -{ - "type": "finish", - "answer": "完整答案文本", - "mode": "rag", - "sources": [ - { - "source": "文档名.pdf", - "page": 5, - "page_end": 8, - "page_range": "5-8", - "section": "1. Introduction", - "chunk_type": "text", - "score": 0.856 - } - ], - "images": [], - "tables": [], - "sections": ["章节路径"], - "duration_ms": 1500 -} -``` - -**后端消费示例(Python)**: -```python -import requests -import json - -def call_rag_stream(message, collections, history=None): - response = requests.post( - 'http://localhost:5001/rag', - json={ - 'message': message, - 'collections': collections, - 'chat_history': history or [] - }, - stream=True - ) - - full_answer = '' - sources = [] - - for line in response.iter_lines(): - if not line: - continue - - line = line.decode('utf-8') - if line.startswith('data: '): - event = json.loads(line[6:]) - - if event['type'] == 'sources': - sources = event['sources'] - elif event['type'] == 'chunk': - full_answer += event['content'] - elif event['type'] == 'finish': - full_answer = event['answer'] - sources = event['sources'] - break - elif event['type'] == 'error': - raise Exception(event['message']) - - return full_answer, sources - -# 使用 -answer, sources = call_rag_stream('出差补助标准是什么?', ['public_kb']) -``` - -### 3.3 普通聊天 - -``` -POST /chat -``` - -**请求体**: -```json -{ - "message": "用户消息", - "chat_history": [] -} -``` - -**响应**: SSE 流式返回(同 /rag) - -### 3.4 混合检索 - -``` -POST /search -``` - -**请求体**: -```json -{ - "message": "检索关键词", - "collections": ["public_kb"], - "chat_history": [] -} -``` - -**响应**: -```json -{ - "contexts": ["文档片段1", "文档片段2"], - "metadatas": [ - {"source": "doc1.pdf", "page": 1}, - {"source": "doc2.pdf", "page": 5} - ], - "scores": [0.95, 0.87] -} -``` - ---- - -## 四、知识库管理接口 - -### 4.1 获取向量库列表 - -``` -GET /collections -``` - -**响应**: -```json -{ - "collections": [ - { - "name": "public_kb", - "display_name": "公开知识库", - "document_count": 137, - "department": "", - "description": "所有人可访问" - } - ], - "total": 1 -} -``` - -### 4.2 创建向量库 - -``` -POST /collections -``` - -**请求体**: -```json -{ - "name": "dept_finance", - "display_name": "财务部知识库", - "department": "finance", - "description": "财务部专用知识库" -} -``` - -### 4.3 删除向量库 - -``` -DELETE /collections/ -``` - -### 4.4 获取向量库文档列表 - -``` -GET /collections//documents -``` - -**响应**: -```json -{ - "collection": "public_kb", - "documents": [ - {"source": "文档名.pdf", "chunks": 30} - ], - "total": 3 -} -``` - -### 4.5 知识库路由测试 - -``` -POST /kb/route -``` - -**请求体**: -```json -{ - "query": "财务部报销流程" -} -``` - -**响应**: -```json -{ - "target_collections": ["dept_finance", "public_kb"], - "routing_reason": "检测到部门关键词" -} -``` - ---- - -## 五、文档管理接口 - -### 5.1 上传文档 - -``` -POST /documents/upload -Content-Type: multipart/form-data -``` - -**表单参数**: -| 参数 | 类型 | 必需 | 说明 | -|------|------|------|------| -| `file` | file | ✅ | 文件 | -| `collection` | string | ✅ | 目标向量库名称 | - -**响应**: -```json -{ - "success": true, - "message": "文件上传成功,已添加到向量库", - "file": { - "filename": "document.pdf", - "collection": "public_kb", - "path": "public/document.pdf", - "size": 1024000 - }, - "chunk_count": 15 -} -``` - -### 5.2 批量上传 - -``` -POST /documents/batch-upload -Content-Type: multipart/form-data -``` - -**表单参数**: -| 参数 | 类型 | 必需 | 说明 | -|------|------|------|------| -| `files` | file[] | ✅ | 文件列表 | -| `collection` | string | ✅ | 目标向量库名称 | - -### 5.3 文档列表 - -``` -GET /documents/list -``` - -**响应**: -```json -{ - "documents": [ - { - "collection": "public_kb", - "filename": "文档名.pdf", - "path": "public_kb/文档名.pdf", - "size": 1024000, - "last_modified": "2026-04-26T10:00:00" - } - ] -} -``` - -### 5.4 删除文档 - -``` -DELETE /documents/ -``` - -### 5.5 查看文档切片 - -``` -GET /documents//chunks -``` - -### 5.6 文档状态 - -``` -GET /documents//status -``` - ---- - -## 六、切片管理接口 - -### 6.1 新增切片 - -``` -POST /chunks -``` - -**请求体**: -```json -{ - "collection": "public_kb", - "document_id": "doc_001", - "content": "切片内容", - "metadata": {"page": 1, "section": "第一章"} -} -``` - -### 6.2 修改切片 - -``` -PUT /chunks/ -``` - -### 6.3 删除切片 - -``` -DELETE /chunks/ -``` - ---- - -## 七、同步服务接口 - -### 7.1 同步状态 - -``` -GET /sync/status -``` - -**响应**: -```json -{ - "enabled": true, - "monitoring": false, - "last_sync": null, - "documents_tracked": 0 -} -``` - -### 7.2 触发同步 - -``` -POST /sync -``` - -**请求体**(可选): -```json -{ - "collection": "public_kb", - "full_sync": false -} -``` - -### 7.3 同步历史 - -``` -GET /sync/history?limit=20 -``` - -### 7.4 变更日志 - -``` -GET /sync/changes?limit=50 -``` - -### 7.5 启动/停止监控 - -``` -POST /sync/start -POST /sync/stop -``` - ---- - -## 八、图片服务接口 - -### 8.1 获取图片 - -``` -GET /images/ -``` - -**响应**: 图片二进制数据 - -### 8.2 图片信息 - -``` -GET /images//info -``` - -### 8.3 图片列表 - -``` -GET /images/list?limit=20 -``` - -**响应**: -```json -{ - "images": [ - { - "image_id": "abc123", - "size_bytes": 56932, - "url": "/images/abc123" - } - ], - "total": 11 -} -``` - -### 8.4 图片统计 - -``` -GET /images/stats -``` - ---- - -## 九、反馈系统接口 - -### 9.1 提交反馈 - -``` -POST /feedback -``` - -**请求体**: -```json -{ - "session_id": "xxx", - "query": "问题内容", - "answer": "回答内容", - "rating": 1, - "sources": ["doc.pdf"], - "reason": "回答准确" -} -``` - -| 字段 | 类型 | 说明 | -|------|------|------| -| `rating` | int | `1`=赞, `-1`=踩 | - -### 9.2 反馈统计 - -``` -GET /feedback/stats -``` - -**响应**: -```json -{ - "success": true, - "stats": { - "total_feedback": 4, - "positive_count": 4, - "negative_count": 0, - "satisfaction_rate": 100.0 - } -} -``` - -### 9.3 反馈列表 - -``` -GET /feedback/list -``` - -### 9.4 周报/月报 - -``` -GET /reports/weekly -GET /reports/monthly -``` - ---- - -## 十、FAQ 管理接口 - -### 10.1 FAQ 列表 - -``` -GET /faq -``` - -**响应**: -```json -{ - "faqs": [ - { - "id": 1, - "question": "问题", - "answer": "答案", - "frequency": 5, - "status": "approved" - } - ] -} -``` - -### 10.2 新增 FAQ - -``` -POST /faq -``` - -**请求体**: -```json -{ - "question": "问题", - "answer": "答案", - "source_documents": ["doc.pdf"] -} -``` - -### 10.3 更新/删除 FAQ - -``` -PUT /faq/ -DELETE /faq/ -``` - -### 10.4 FAQ 建议列表 - -``` -GET /faq/suggestions -``` - -### 10.5 批准/拒绝建议 - -``` -POST /faq/suggestions//approve -POST /faq/suggestions//reject -``` - ---- - -## 十一、出题系统接口 - -### 11.1 健康检查 - -``` -GET /exam/health -``` - -**响应**: -```json -{ - "service": "exam-api", - "status": "ok", - "version": "2.0" -} -``` - -### 11.2 生成题目 - -``` -POST /exam/generate -``` - -**请求体**: -```json -{ - "file_path": "public/考勤制度.docx", - "collection": "public_kb", - "question_types": { - "single_choice": 3, - "multiple_choice": 2, - "true_false": 2, - "fill_blank": 2, - "subjective": 1 - }, - "difficulty": 3, - "request_id": "可选,幂等性支持" -} -``` - -**响应**: -```json -{ - "success": true, - "total": 10, - "questions": [ - { - "question_type": "single_choice", - "difficulty": 3, - "content": { - "stem": "题干内容", - "data": { - "options": [ - {"key": "A", "content": "选项A"}, - {"key": "B", "content": "选项B"} - ] - }, - "answer": "B", - "explanation": "解析" - }, - "source_trace": { - "document_name": "考勤制度.docx", - "page_numbers": [5] - } - } - ] -} -``` - -### 11.3 批改答案 - -``` -POST /exam/grade -``` - -**请求体**: -```json -{ - "answers": [ - { - "question_id": "q001", - "question_type": "single_choice", - "question_content": { - "stem": "题干", - "data": {"options": [...]}, - "answer": "B" - }, - "student_answer": "A", - "max_score": 2.0 - } - ] -} -``` - -**响应**: -```json -{ - "success": true, - "total_score": 12.5, - "total_max_score": 22.0, - "score_rate": 56.8, - "results": [ - { - "question_id": "q001", - "score": 0, - "max_score": 2.0, - "correct": false, - "feedback": "正确答案: B" - } - ] -} -``` - ---- - -## 十二、版本管理接口 - -### 12.1 废止文档 - -``` -POST /collections//documents//deprecate -``` - -**请求体**: -```json -{ - "reason": "制度已更新" -} -``` - -### 12.2 恢复文档 - -``` -POST /collections//documents//restore -``` - -### 12.3 版本历史 - -``` -GET /collections//documents//versions -``` - ---- - -## 十三、纲要生成接口 - -### 13.1 生成纲要 - -``` -POST /outline -``` - -**请求体**: -```json -{ - "document_id": "员工手册_v2.pdf", - "force": false -} -``` - -### 13.2 获取纲要 - -``` -GET /outline/ -``` - -### 13.3 导出纲要 - -``` -GET /outline//export?format=markdown -``` - -### 13.4 纲要列表 - -``` -GET /outline/list -``` - ---- - -## 十四、关联推荐接口 - -### 14.1 获取关联推荐 - -``` -GET /recommend/?top_k=5 -``` - ---- - -## 十五、用户接口 - -### 15.1 当前用户信息 - -``` -GET /auth/me -``` - -**响应**: -```json -{ - "user_id": "admin001", - "username": "管理员", - "role": "admin", - "department": "技术部" -} -``` - -### 15.2 系统统计 - -``` -GET /stats -``` - -**响应**: -```json -{ - "total_messages": 176, - "total_sessions": 25, - "total_users": 2 -} -``` - ---- - -## 十七、企业文件系统集成 - -### 17.1 概述 - -RAG 服务支持从企业现有文件系统获取文件进行向量化,无需在本地存储重复文件。 - -**当前对接模式**: 本地存储 - -文件存储在 RAG 服务的 `documents/` 目录下,按知识库组织: - -``` -documents/ -├── public_kb/ # 公开知识库 -│ ├── 文档1.pdf -│ └── 文档2.docx -├── dept_finance/ # 财务部知识库 -│ └── 报销制度.pdf -└── dept_hr/ # 人事部知识库 - └── 员工手册.docx -``` - -### 17.2 文件上传流程 - -**方式一: 直接上传到 RAG 服务** - -```http -POST /documents/upload -Content-Type: multipart/form-data - -file: (二进制文件) -collection: public_kb -``` - -**响应**: -```json -{ - "success": true, - "message": "文件上传成功", - "file": { - "filename": "文档名.pdf", - "collection": "public_kb", - "path": "public_kb/文档名.pdf", - "size": 1024000 - }, - "chunk_count": 15 -} -``` - -**方式二: 后端转发(推荐)** - -``` -前端 → 后端 → RAG服务 - ↓ - 存储到 documents/ - ↓ - 解析 + 向量化 - ↓ - 返回 chunk_count - ↓ - 后端存储元数据到数据库 -``` - -后端示例: -```python -@app.route('/api/documents/upload', methods=['POST']) -def upload_document(): - file = request.files['file'] - kb_name = request.form.get('kb_name', 'public_kb') - - # 转发到 RAG 服务 - response = requests.post( - 'http://rag-service:5001/documents/upload', - files={'file': file}, - data={'collection': kb_name} - ) - - result = response.json() - - # 存储元数据到后端数据库 - if result.get('success'): - db.execute(""" - INSERT INTO file_index (filename, kb_name, size, chunk_count, uploaded_by) - VALUES (?, ?, ?, ?, ?) - """, ( - result['file']['filename'], - kb_name, - result['file']['size'], - result.get('chunk_count', 0), - current_user.id - )) - - return jsonify(result) -``` - -### 17.3 文档目录结构 - -后端需要了解 RAG 服务的文档目录结构: - -| 路径 | 说明 | -|------|------| -| `documents/public_kb/` | 公开知识库文件 | -| `documents/dept_/` | 部门知识库文件 | -| `knowledge/vector_store/chroma/` | ChromaDB 向量数据 | -| `knowledge/vector_store/bm25/` | BM25 索引 | - -### 17.4 后续扩展 - -后续可切换到企业文件系统集成,配置方式: - -```bash -# 切换存储类型 -STORAGE_TYPE=s3 # 或 smb / http - -# S3 配置 -STORAGE_S3_ENDPOINT=http://minio.example.com:9000 -STORAGE_S3_BUCKET=documents -STORAGE_S3_ACCESS_KEY=xxx -STORAGE_S3_SECRET_KEY=xxx -``` - -切换后,RAG 服务将从企业文件系统读取文件,无需本地存储。 - ---- - -## 十八、接口速查表 - -| 方法 | 路径 | 认证 | 说明 | -|------|------|------|------| -| GET | `/health` | ❌ | 健康检查 | -| GET | `/auth/me` | Header | 当前用户信息 | -| GET | `/stats` | Header | 系统统计 | -| POST | `/chat` | - | 普通聊天(SSE) | -| POST | `/rag` | - | 知识库问答(SSE) | -| POST | `/search` | - | 混合检索 | -| GET | `/collections` | - | 向量库列表 | -| POST | `/collections` | - | 创建向量库 | -| DELETE | `/collections/` | - | 删除向量库 | -| GET | `/collections//documents` | - | 向量库文档 | -| GET | `/collections//chunks` | - | 向量库切片 | -| POST | `/kb/route` | - | 知识库路由测试 | -| POST | `/documents/upload` | - | 上传文档 | -| POST | `/documents/batch-upload` | - | 批量上传 | -| GET | `/documents/list` | - | 文档列表 | -| DELETE | `/documents/` | - | 删除文档 | -| GET | `/documents//chunks` | - | 文档切片 | -| GET | `/documents//status` | - | 文档状态 | -| POST | `/chunks` | - | 新增切片 | -| PUT | `/chunks/` | - | 修改切片 | -| DELETE | `/chunks/` | - | 删除切片 | -| GET | `/sync/status` | - | 同步状态 | -| POST | `/sync` | - | 触发同步 | -| GET | `/sync/history` | - | 同步历史 | -| GET | `/sync/changes` | - | 变更日志 | -| POST | `/sync/start` | - | 启动监控 | -| POST | `/sync/stop` | - | 停止监控 | -| GET | `/images/` | - | 获取图片 | -| GET | `/images//info` | - | 图片信息 | -| GET | `/images/list` | - | 图片列表 | -| GET | `/images/stats` | - | 图片统计 | -| POST | `/feedback` | - | 提交反馈 | -| GET | `/feedback/stats` | - | 反馈统计 | -| GET | `/feedback/list` | - | 反馈列表 | -| GET | `/reports/weekly` | - | 周报告 | -| GET | `/reports/monthly` | - | 月报告 | -| GET | `/faq` | - | FAQ 列表 | -| POST | `/faq` | - | 新增 FAQ | -| PUT | `/faq/` | - | 更新 FAQ | -| DELETE | `/faq/` | - | 删除 FAQ | -| GET | `/faq/suggestions` | - | FAQ 建议 | -| POST | `/faq/suggestions//approve` | - | 批准建议 | -| POST | `/faq/suggestions//reject` | - | 拒绝建议 | -| GET | `/exam/health` | - | 出题系统健康检查 | -| POST | `/exam/generate` | - | 生成题目 | -| POST | `/exam/grade` | - | 批改答案 | -| POST | `/collections//documents//deprecate` | - | 废止文档 | -| POST | `/collections//documents//restore` | - | 恢复文档 | -| GET | `/collections//documents//versions` | - | 版本历史 | -| POST | `/outline` | - | 生成纲要 | -| GET | `/outline/` | - | 获取纲要 | -| GET | `/outline//export` | - | 导出纲要 | -| DELETE | `/outline/` | - | 删除纲要缓存 | -| GET | `/outline/list` | - | 纲要列表 | -| POST | `/outline/batch` | - | 批量生成纲要 | -| GET | `/recommend/` | - | 关联推荐 | - -> **说明**: 认证列 `Header` 表示需要传 X-User-ID/X-User-Role 等 Header(开发模式),`-` 表示生产模式不需要认证 - ---- - -## 十七、错误响应格式 - -所有错误响应遵循统一格式: - -```json -{ - "error": "错误类型", - "message": "详细错误信息" -} -``` - -**常见 HTTP 状态码**: -- `400` - 请求参数错误 -- `404` - 资源不存在 -- `500` - 服务器内部错误 - ---- - -## 十八、后端对接检查清单 - -**RAG服务部署前**: - -- [ ] 设置 `APP_ENV=prod` -- [ ] 配置 `DASHSCOPE_API_KEY` -- [ ] 确认向量模型已下载到 `models/` 目录 -- [ ] 确认 `knowledge/vector_store/` 目录已创建 - -**后端开发前**: - -- [ ] 创建会话表(sessions) -- [ ] 创建消息表(messages) -- [ ] 创建知识库权限表(kb_permissions) -- [ ] 实现会话历史查询逻辑 -- [ ] 实现权限判断逻辑(生成 collections 列表) -- [ ] 实现 SSE 流式响应消费 - -**集成测试**: - -- [ ] 测试 `/health` 端点 -- [ ] 测试 `/rag` SSE 流式响应 -- [ ] 测试会话历史传递 -- [ ] 测试权限控制(collections 参数) -- [ ] 测试文档上传 -- [ ] 测试反馈提交 - ---- - -## 更新日志 - -| 日期 | 版本 | 更新内容 | -|------|------|----------| -| 2026-04-26 | 3.1 | 根据实际测试结果精简文档,移除无效端点,确保准确性 | -| 2026-04-20 | 3.0 | 合并文档,补充 SSE 详情 | -| 2026-04-13 | 2.0 | 新增同步服务、版本管理等接口 | diff --git a/docs/image_processing_flow.md b/docs/image_processing_flow.md deleted file mode 100644 index 7cc487d..0000000 --- a/docs/image_processing_flow.md +++ /dev/null @@ -1,233 +0,0 @@ -# 图片处理完整流程分析 - -## 流程概览 - -``` -┌─────────────────────────────────────────────────────────────────────────────────┐ -│ 图片处理完整流程 │ -├─────────────────────────────────────────────────────────────────────────────────┤ -│ │ -│ 1. 解析阶段 (MinerU) │ -│ ┌─────────────┐ ┌──────────────────┐ ┌───────────────────────────┐ │ -│ │ PDF/Word │────→│ MinerU 解析 │────→│ MinerUChunk 对象 │ │ -│ │ 文件 │ │ parsers/mineru_ │ │ ├── content (文本/标题) │ │ -│ └─────────────┘ │ parser.py │ │ ├── chunk_type │ │ -│ └──────────────────┘ │ ├── table_html (表格HTML) │ │ -│ │ ├── image_path (独立图片) │ │ -│ │ └── images (关联图片列表) │ │ -│ ↓ │ -│ to_page_content() │ -│ ↓ │ -│ 返回 chunks 列表 │ -│ │ -├─────────────────────────────────────────────────────────────────────────────────┤ -│ │ -│ 2. 存储阶段 (Knowledge Manager) │ -│ ┌──────────────────┐ ┌────────────────────┐ ┌───────────────────┐ │ -│ │ chunks 列表 │────→│ add_file_to_kb() │────→│ 向量库 metadata │ │ -│ │ (MinerUChunk) │ │ knowledge/manager │ │ │ │ -│ └──────────────────┘ │ .py │ │ ├── chunk_type │ │ -│ │ │ │ ├── source │ │ -│ │ ✅ 合并跨页表格 │ │ ├── page │ │ -│ │ ✅ 序列化 images │ │ ├── images_json ✅│ │ -│ │ ✅ 存储 image_path │ │ └── image_path ✅ │ │ -│ └────────────────────┘ └───────────────────┘ │ -│ │ -├─────────────────────────────────────────────────────────────────────────────────┤ -│ │ -│ 3. 召回阶段 (RAG 检索) │ -│ ┌──────────────────┐ ┌────────────────────┐ ┌───────────────────┐ │ -│ │ 用户查询 │────→│ 混合检索 │────→│ 检索结果 │ │ -│ │ "表3.1 数据" │ │ search_hybrid() │ │ contexts = [{ │ │ -│ └──────────────────┘ │ api/chat_routes.py │ │ "doc": "...", │ │ -│ └────────────────────┘ │ "meta": {...} │ │ -│ ↓ │ }] │ │ -│ ↓ └───────────────────┘ │ -│ ┌────────────────────┐ ↓ │ -│ │ _extract_rich_media│ ┌───────────────────┐ │ -│ │ api/chat_routes.py │ │ 返回给前端 │ │ -│ │ │────→│ { │ │ -│ │ 读取 images_json │ │ "images": [...],│ │ -│ │ 读取 image_path │ │ "tables": [...],│ │ -│ │ ✅ 正确处理 │ │ "answer": "..." │ │ -│ └────────────────────┘ │ } │ │ -│ └───────────────────┘ │ -│ │ -└─────────────────────────────────────────────────────────────────────────────────┘ -``` - -## 关键代码位置 - -### 1. MinerU 解析 (parsers/mineru_parser.py) - -**MinerUChunk 数据结构 (第107-125行)**: -```python -@dataclass -class MinerUChunk: - content: str # 文本内容 - chunk_type: str # 类型: text, table, image, equation - page_start: int = 1 - page_end: int = 1 - title: str = "" - table_html: Optional[str] = None # 表格 HTML(如果是表格) - image_path: Optional[str] = None # 图片路径(独立图片) - images: Optional[List[Dict]] = None # 关联图片列表: [{"id": "abc.jpg", "order": 1}] -``` - -**表格切片创建 (第453-469行)**: -```python -chunk = MinerUChunk( - content=table_caption or "表格", # ⚠️ content 只有标题 - chunk_type="table", - table_html=table_body, # ✅ 完整表格在 table_html - image_path=img_path, # 表格图片路径 - images=table_images # 表格中嵌入的图片 -) -``` - -### 2. 向量库存储 (knowledge/manager.py) - -**add_file_to_kb() 核心逻辑 (第228-270行)**: -```python -for i, chunk in enumerate(chunks): - # 1. 获取 chunk_type - chunk_type = getattr(chunk, 'chunk_type', None) - if not chunk_type: - page_info = getattr(chunk, 'page_info', {}) or {} - chunk_type = page_info.get('chunk_type', 'text') - - if chunk_type == 'table': - # 2. 表格内容优先使用 table_html - table_md = getattr(chunk, 'table_html', None) or chunk.content - semantic_content = _build_semantic_content_for_table(...) - - # 3. 构建 metadata - metadata = { - 'chunk_type': chunk_type, - 'source': filename, - 'page': page_start, - # ... - } - - # 4. ✅ 序列化图片信息 - if hasattr(chunk, 'images') and chunk.images: - metadata['images_json'] = json.dumps(chunk.images, ensure_ascii=False) - - if hasattr(chunk, 'image_path') and chunk.image_path: - metadata['image_path'] = chunk.image_path -``` - -**跨页表格合并 (第323-420行)**: -```python -def _merge_cross_page_tables(self, chunks: list) -> list: - """ - 合并规则: - 1. 相邻两个表格切片 - 2. 页码连续 (page_end + 1 == next.page_start) - 3. 第二个表格标题包含"续表" - """ - # 合并 table_html - current.table_html = curr_html + '\n' + next_html - - # 合并 image_path 到 images - merged_images = [ - {'id': curr_img, 'page': curr_page_end}, - {'id': next_img, 'page': next_page_start} - ] - current.images = merged_images -``` - -### 3. 富媒体召回 (api/chat_routes.py) - -**_extract_rich_media() 核心逻辑 (第724-843行)**: -```python -def _extract_rich_media(contexts: List[Dict]) -> Dict[str, List]: - images = [] - tables = [] - - for ctx in contexts: - meta = ctx.get("meta", {}) - - # 1. 独立图片切片 (image_path) - 图片/图表类型 - if meta.get("chunk_type") in ("image", "chart") and meta.get("image_path"): - img_id = os.path.basename(meta["image_path"]) - images.append({"id": img_id, "url": f"/images/{img_id}", ...}) - - # 2. 关联图片 (images_json) - 表格/文本嵌入图片 - if meta.get("images_json"): - img_list = json.loads(meta["images_json"]) - for img_info in img_list: - images.append({"id": img_info["id"], ...}) - - # 3. 表格图片 (image_path) - 表格类型的图片形式 - if meta.get("chunk_type") == "table" and meta.get("image_path"): - img_id = os.path.basename(meta["image_path"]) - images.append({"id": img_id, "type": "table_image", ...}) - - return {"images": images, "tables": tables} -``` - -## 当前问题分析 - -### 问题1: 表格显示"0行数据" - -**根因**: `_build_semantic_content_for_table()` 接收的 `table_md` 可能是空的 - -**验证点**: -- MinerU 解析时 `table_html` 是否有值? -- `manager.py` 第240行 `table_md = getattr(chunk, 'table_html', None)` 是否正确获取? - -### 问题2: 跨页表格合并不生效 - -**根因**: 可能是页码不连续或标题匹配失败 - -**验证点**: -- 检查 `_merge_cross_page_tables()` 的日志输出 -- 验证两个表格切片的 `page_end` 和 `page_start` 是否连续 - -### 问题3: 图片重复 - -**根因**: 可能是 `images_json` 和 `image_path` 同时存在导致重复 - -**验证点**: -- 检查向量库中是否有同时存在 `images_json` 和 `image_path` 的切片 -- `_extract_rich_media()` 中的去重逻辑是否有效 - -## 数据存储位置 - -| 目录 | 用途 | -|------|------| -| `.data/images/` | 全局图片存储(哈希命名,去重) | -| `.data/cache/vlm/` | VLM 图片描述缓存 | -| `.data/docstore/` | 原始表格/图片 JSON 备份 | -| `knowledge/vector_store/chroma/` | ChromaDB 向量数据库 | - -## 测试验证步骤 - -### 1. 检查向量库 metadata -```python -from knowledge.manager import get_kb_manager -kb = get_kb_manager() -coll = kb.get_collection('my_ky') -result = coll.get(limit=10, include=['metadatas']) - -for meta in result['metadatas']: - print(f"chunk_type: {meta.get('chunk_type')}") - print(f"images_json: {meta.get('images_json')}") - print(f"image_path: {meta.get('image_path')}") - print("---") -``` - -### 2. 检查 MinerU 解析结果 -```python -from parsers.mineru_parser import parse_with_mineru -result = parse_with_mineru("tests/public/test_report.pdf") - -for chunk in result.get('chunks', []): - if chunk.chunk_type == 'table': - print(f"表格标题: {chunk.title}") - print(f"table_html 长度: {len(chunk.table_html or '')}") - print(f"image_path: {chunk.image_path}") - print(f"images: {chunk.images}") - print("---") -``` diff --git a/docs/企业文档更新管理方案.md b/docs/企业文档更新管理方案.md deleted file mode 100644 index 77ec0d3..0000000 --- a/docs/企业文档更新管理方案.md +++ /dev/null @@ -1,785 +0,0 @@ -# 企业文档更新管理方案 - -> 本文档合并了企业文档管理的多种方案比较分析(包括增量更新、完整版本管理和软删除等),以及目前项目中所采纳的“方案C(智能全量更新)”的具体实现细则。 - -## 第一部分:文档更新管理方案横评 - -# 企业文档管理方案分析与建议 - -> 针对企业文档部分更新、文件废止等场景的最佳实践 - ---- - -## 📋 企业文档管理的实际需求 - -### 典型场景 - -1. **文档部分更新** - - 制度文件修订(如:报销制度第3条修改) - - 附件更新(如:报销单模板更新) - - 内容勘误(如:错别字修正) - -2. **文档废止** - - 旧制度失效(如:2023年报销制度被2024年版本替代) - - 临时文件过期(如:疫情期间的临时政策) - - 部门撤销(如:某部门解散,相关文档废止) - -3. **版本管理** - - 多版本共存(如:新旧制度过渡期) - - 历史追溯(如:查询某个时间点的制度内容) - - 变更记录(如:审计需要查看修改历史) - ---- - -## 🔍 当前实现分析 - -### 现有机制:sync.py - -**优点**: -- ✅ 自动检测文件变更(新增、修改、删除) -- ✅ 基于文件 Hash 判断内容是否变化 -- ✅ 支持多向量库(按目录自动分类) -- ✅ 实时监控文件系统变化 - -**处理策略**: -```python -# 当前的"全量更新"策略 -if change.change_type == ChangeType.MODIFIED: - # 1. 删除旧文档的所有 chunks - deleted = kb_manager.delete_document(kb_name, filename) - - # 2. 重新解析并添加新文档的所有 chunks - chunks_added = kb_manager.add_file_to_kb(kb_name, filepath) -``` - -**问题**: -- ❌ 即使只修改一个字,也要重新解析整个文档 -- ❌ 删除所有旧 chunks,可能影响正在使用的查询 -- ❌ 没有保留历史版本 -- ❌ 无法追溯变更内容 - ---- - -## 💡 解决方案对比 - -### 方案 A:增量更新(diff.py 的设计思路) - -**原理**: -```python -# 1. 解析新旧文档 -old_chunks = parse_document(old_version) -new_chunks = parse_document(new_version) - -# 2. 计算差异 -diff = DocumentDiffAnalyzer().compute_diff(old_chunks, new_chunks) - -# 3. 增量更新 -for chunk in diff.added: - kb_manager.add_chunk(chunk) # 只添加新增的 - -for chunk in diff.deleted: - kb_manager.delete_chunk(chunk.id) # 只删除被删的 - -for chunk in diff.modified: - kb_manager.update_chunk(chunk.id, chunk.new_content) # 只更新修改的 -``` - -**优点**: -- ✅ 性能优化:只处理变化的部分 -- ✅ 减少重复计算:不需要重新 Embedding 未变化的内容 -- ✅ 平滑过渡:不影响正在使用的 chunks - -**缺点**: -- ❌ 实现复杂:需要精确匹配新旧 chunks -- ❌ 匹配困难:文档结构变化时难以对应 -- ❌ 边界问题:chunk 边界变化导致误判 -- ❌ 维护成本高:525 行代码,逻辑复杂 - -**适用场景**: -- 超大文档(1000+ 页) -- 频繁小改动(每天多次更新) -- 对性能要求极高 - -**企业实际情况**: -- ❌ 大部分企业文档 < 100 页 -- ❌ 更新频率低(每月/每季度) -- ❌ 全量更新耗时可接受(几秒到几十秒) - ---- - -### 方案 B:版本管理 + 软删除(lifecycle.py 的设计思路) - -**原理**: -```python -# 1. 保留所有版本 -document_versions = [ - {"version": "v1", "status": "superseded", "upload_time": "2023-01-01"}, - {"version": "v2", "status": "superseded", "upload_time": "2023-06-01"}, - {"version": "v3", "status": "active", "upload_time": "2024-01-01"} -] - -# 2. 查询时只返回 active 版本 -chunks = kb_manager.query(kb_name, query, filter={"status": "active"}) - -# 3. 废止文档(软删除) -lifecycle_manager.deprecate_document(kb_name, doc_id, reason="制度已更新") -# 实际操作:将 status 改为 "deprecated",不删除数据 -``` - -**优点**: -- ✅ 历史追溯:可以查询任意时间点的内容 -- ✅ 安全回滚:废止操作可逆 -- ✅ 审计友好:完整的变更记录 -- ✅ 过渡期支持:新旧版本可以共存 - -**缺点**: -- ❌ 存储成本:保留所有历史版本 -- ❌ 查询复杂:需要过滤 status -- ❌ 数据膨胀:向量库体积增大 - -**适用场景**: -- 合规要求高(金融、医疗) -- 需要审计追溯 -- 文档变更频繁但需要保留历史 - ---- - -### 方案 C:智能全量更新(推荐)⭐ - -**原理**: -```python -# 1. 检测变更 -if file_hash_changed: - # 2. 标记旧版本(软删除) - kb_manager.mark_document_as_deprecated(kb_name, doc_id, version="v1") - - # 3. 添加新版本 - kb_manager.add_file_to_kb( - kb_name, - filepath, - extra_metadata={ - "status": "active", - "version": "v2", - "previous_version": "v1", - "change_reason": "制度修订" - } - ) - - # 4. 异步清理旧版本(可选) - schedule_cleanup(kb_name, doc_id, version="v1", delay="7 days") -``` - -**优点**: -- ✅ 实现简单:基于现有 sync.py -- ✅ 性能可接受:全量更新耗时短 -- ✅ 可靠性高:不依赖复杂的 diff 算法 -- ✅ 灵活性好:可选保留历史版本 - -**缺点**: -- ⚠️ 短暂的双份数据(新旧版本共存期间) - -**适用场景**: -- ✅ 大部分企业场景 -- ✅ 文档更新频率适中 -- ✅ 对性能要求不极端 - ---- - -## 📊 方案对比总结 - -| 方案 | 实现复杂度 | 性能 | 存储成本 | 历史追溯 | 适用场景 | -|------|-----------|------|---------|---------|---------| -| **A. 增量更新** | ⭐⭐⭐⭐⭐ 高 | ⭐⭐⭐⭐⭐ 优 | ⭐⭐⭐⭐⭐ 低 | ❌ 无 | 超大文档、频繁更新 | -| **B. 完整版本管理** | ⭐⭐⭐⭐ 中高 | ⭐⭐⭐ 中 | ⭐⭐ 高 | ✅ 完整 | 金融、医疗等合规场景 | -| **C. 智能全量更新** | ⭐⭐ 低 | ⭐⭐⭐⭐ 良 | ⭐⭐⭐⭐ 中 | ✅ 可选 | **大部分企业场景** ⭐ | - ---- - -## 🎯 最终建议 - -### 推荐方案:方案 C(智能全量更新 + 轻量级版本管理) - -**理由**: -1. ✅ **实现简单**:基于现有 sync.py,增量开发 -2. ✅ **性能足够**:全量更新耗时可接受(秒级) -3. ✅ **功能完整**:支持版本管理、软删除、历史追溯 -4. ✅ **维护成本低**:逻辑清晰,不易出错 -5. ✅ **适用性广**:覆盖 90% 的企业场景 - -**不推荐**: -- ❌ 方案 A(增量更新):实现复杂,收益不明显 -- ⚠️ 方案 B(完整版本管理):存储成本高,大部分企业用不到 - -### 具体操作 - -1. **删除 diff.py**(525 行) -2. **简化 lifecycle.py**(保留 ~200 行核心功能) -3. **增强 sync.py**(添加版本管理逻辑) -4. **补充 API**(废止、恢复、历史查询) - -**预期效果**: -- 减少 ~800 行冗余代码 -- 保留实际需要的功能 -- 满足企业文档管理需求 - ---- - -**文档版本**: v1.0 -**创建时间**: 2026-04-20 -**维护者**: RAG 服务开发组 - - ---- - -## 第二部分:选定方案(方案C)详细落地落实说明 - -# 方案 C:文档、废止状态与向量库关系详解 - -> 详细说明智能全量更新方案的数据流和状态管理 - ---- - -## 📊 核心概念 - -### 三个层次 - -1. **物理层**:`documents/` 目录(文件系统) -2. **逻辑层**:文档状态管理(数据库) -3. **检索层**:向量库(ChromaDB/Milvus) - ---- - -## 🔄 完整数据流图 - -``` -┌─────────────────────────────────────────────────────────────┐ -│ 1. 物理层:documents/ │ -│ │ -│ documents/ │ -│ ├── public/ │ -│ │ ├── 报销制度_v1.pdf ← 旧版本(物理存在) │ -│ │ ├── 报销制度_v2.pdf ← 新版本(物理存在) │ -│ │ └── 临时防疫政策.pdf ← 已废止(物理存在) │ -│ └── finance/ │ -│ └── 差旅管理办法.pdf │ -└─────────────────────────────────────────────────────────────┘ - ↓ -┌─────────────────────────────────────────────────────────────┐ -│ 2. 逻辑层:document_versions 表 │ -│ │ -│ ┌──────────────────────────────────────────────────────┐ │ -│ │ kb_name │ document_id │ version │ status │ │ -│ ├─────────┼────────────────────┼─────────┼─────────────┤ │ -│ │ public │ 报销制度_v1.pdf │ v1 │ superseded │ │ -│ │ public │ 报销制度_v2.pdf │ v2 │ active │ │ -│ │ public │ 临时防疫政策.pdf │ v1 │ deprecated │ │ -│ │ finance │ 差旅管理办法.pdf │ v1 │ active │ │ -│ └──────────────────────────────────────────────────────┘ │ -└─────────────────────────────────────────────────────────────┘ - ↓ -┌─────────────────────────────────────────────────────────────┐ -│ 3. 检索层:向量库(ChromaDB) │ -│ │ -│ Collection: public_kb │ -│ ┌────────────────────────────────────────────────────┐ │ -│ │ chunk_id │ content │ metadata │ │ -│ ├──────────┼──────────────┼───────────────────────────┤ │ -│ │ c1 │ 报销流程... │ {doc: 报销制度_v1.pdf, │ │ -│ │ │ │ status: superseded, │ │ -│ │ │ │ version: v1} │ │ -│ ├──────────┼──────────────┼───────────────────────────┤ │ -│ │ c2 │ 报销流程... │ {doc: 报销制度_v2.pdf, │ │ -│ │ │ │ status: active, │ │ -│ │ │ │ version: v2} │ │ -│ ├──────────┼──────────────┼───────────────────────────┤ │ -│ │ c3 │ 防疫要求... │ {doc: 临时防疫政策.pdf, │ │ -│ │ │ │ status: deprecated, │ │ -│ │ │ │ version: v1} │ │ -│ └────────────────────────────────────────────────────┘ │ -└─────────────────────────────────────────────────────────────┘ -``` - ---- - -## 📝 详细场景说明 - -### 场景 1:文档更新(报销制度 v1 → v2) - -#### 步骤 1:用户上传新版本 - -```bash -# 用户操作 -documents/public/报销制度_v2.pdf # 上传新文件 -``` - -#### 步骤 2:系统检测变更 - -```python -# sync.py 自动检测 -change = DocumentChange( - document_id="public/报销制度_v2.pdf", - change_type=ChangeType.ADDED, - new_hash="abc123..." -) -``` - -#### 步骤 3:处理更新 - -```python -# 方案 C 的处理逻辑 -def process_document_update(kb_name, old_doc_id, new_doc_path): - # 1. 标记旧版本为 superseded(不删除) - kb_manager.update_metadata( - kb_name="public", - filter={ - "document_id": "报销制度_v1.pdf", - "status": "active" - }, - update={ - "status": "superseded", - "superseded_by": "报销制度_v2.pdf", - "superseded_time": "2024-01-15 10:00:00" - } - ) - - # 2. 添加新版本 - chunks_added = kb_manager.add_file_to_kb( - kb_name="public", - filepath="documents/public/报销制度_v2.pdf", - extra_metadata={ - "status": "active", - "version": "v2", - "previous_version": "v1", - "document_id": "报销制度_v2.pdf", - "upload_time": "2024-01-15 10:00:00" - } - ) - - # 3. 记录版本历史 - db.insert_version_record({ - "kb_name": "public", - "document_id": "报销制度_v2.pdf", - "version": "v2", - "status": "active", - "previous_version": "v1", - "change_reason": "制度修订" - }) - - # 4. 可选:7天后清理旧版本 - schedule_cleanup( - kb_name="public", - document_id="报销制度_v1.pdf", - delay_days=7 - ) -``` - -#### 步骤 4:查询时的效果 - -```python -# 用户查询:"报销流程是什么?" -results = kb_manager.query_kb( - kb_name="public", - query="报销流程是什么", - top_k=5, - where_filter={"status": "active"} # 只查询 active 状态 -) - -# 返回结果: -# ✅ 报销制度_v2.pdf 的内容(新版本) -# ❌ 报销制度_v1.pdf 的内容(被过滤掉) -``` - -#### 数据状态对比 - -**物理层(documents/)**: -``` -documents/public/ -├── 报销制度_v1.pdf ← 仍然存在(用户可能需要查看旧版) -└── 报销制度_v2.pdf ← 新版本 -``` - -**逻辑层(document_versions 表)**: -```sql --- 旧版本记录 -INSERT INTO document_versions VALUES ( - 'public', '报销制度_v1.pdf', 'v1', 'superseded', - '2023-01-01', '被 v2 替代' -); - --- 新版本记录 -INSERT INTO document_versions VALUES ( - 'public', '报销制度_v2.pdf', 'v2', 'active', - '2024-01-15', NULL -); -``` - -**检索层(向量库)**: -```python -# 旧版本 chunks(status=superseded,查询时被过滤) -{ - "chunk_id": "c1", - "content": "报销流程:先填写申请单...", - "metadata": { - "document_id": "报销制度_v1.pdf", - "status": "superseded", # ← 关键字段 - "version": "v1" - } -} - -# 新版本 chunks(status=active,查询时返回) -{ - "chunk_id": "c2", - "content": "报销流程:使用新系统提交...", - "metadata": { - "document_id": "报销制度_v2.pdf", - "status": "active", # ← 关键字段 - "version": "v2" - } -} -``` - ---- - -### 场景 2:文档废止(临时防疫政策失效) - -#### 步骤 1:管理员废止文档 - -```python -# API 调用 -POST /api/kb/public/documents/临时防疫政策.pdf/deprecate -{ - "reason": "疫情结束,政策失效" -} -``` - -#### 步骤 2:系统处理废止 - -```python -def deprecate_document(kb_name, doc_id, reason): - # 1. 更新向量库 metadata - kb_manager.update_metadata( - kb_name="public", - filter={ - "document_id": "临时防疫政策.pdf", - "status": "active" - }, - update={ - "status": "deprecated", - "deprecated_reason": "疫情结束,政策失效", - "deprecated_time": "2024-01-20 15:00:00" - } - ) - - # 2. 更新版本表 - db.update_version_status( - kb_name="public", - document_id="临时防疫政策.pdf", - status="deprecated", - reason="疫情结束,政策失效" - ) - - # 3. 记录废止日志 - db.insert_change_log({ - "kb_name": "public", - "document_id": "临时防疫政策.pdf", - "change_type": "deprecate", - "reason": "疫情结束,政策失效", - "operator": "admin" - }) -``` - -#### 步骤 3:查询时的效果 - -```python -# 用户查询:"防疫政策是什么?" -results = kb_manager.query_kb( - kb_name="public", - query="防疫政策是什么", - top_k=5, - where_filter={"status": "active"} # 只查询 active 状态 -) - -# 返回结果: -# ❌ 临时防疫政策.pdf 的内容(被过滤掉) -# ✅ 其他 active 状态的文档 -``` - -#### 数据状态 - -**物理层(documents/)**: -``` -documents/public/ -└── 临时防疫政策.pdf ← 仍然存在(可能需要归档) -``` - -**逻辑层(document_versions 表)**: -```sql -UPDATE document_versions -SET status = 'deprecated', - status_reason = '疫情结束,政策失效', - deprecated_time = '2024-01-20 15:00:00' -WHERE kb_name = 'public' - AND document_id = '临时防疫政策.pdf'; -``` - -**检索层(向量库)**: -```python -# 废止后的 chunks(status=deprecated,查询时被过滤) -{ - "chunk_id": "c3", - "content": "疫情期间需要佩戴口罩...", - "metadata": { - "document_id": "临时防疫政策.pdf", - "status": "deprecated", # ← 关键字段 - "deprecated_reason": "疫情结束,政策失效" - } -} -``` - ---- - -### 场景 3:恢复已废止的文档 - -#### 步骤 1:管理员恢复文档 - -```python -# API 调用 -POST /api/kb/public/documents/临时防疫政策.pdf/restore -{ - "reason": "疫情反复,政策恢复" -} -``` - -#### 步骤 2:系统处理恢复 - -```python -def restore_document(kb_name, doc_id, reason): - # 1. 更新向量库 metadata - kb_manager.update_metadata( - kb_name="public", - filter={ - "document_id": "临时防疫政策.pdf", - "status": "deprecated" - }, - update={ - "status": "active", - "restored_reason": "疫情反复,政策恢复", - "restored_time": "2024-02-01 09:00:00" - } - ) - - # 2. 更新版本表 - db.update_version_status( - kb_name="public", - document_id="临时防疫政策.pdf", - status="active", - reason="疫情反复,政策恢复" - ) -``` - -#### 步骤 3:查询时的效果 - -```python -# 用户查询:"防疫政策是什么?" -results = kb_manager.query_kb( - kb_name="public", - query="防疫政策是什么", - top_k=5, - where_filter={"status": "active"} -) - -# 返回结果: -# ✅ 临时防疫政策.pdf 的内容(已恢复) -``` - ---- - -## 🔍 查询行为详解 - -### 默认查询(只返回 active 文档) - -```python -def query_kb(self, kb_name: str, query: str, top_k: int = 5): - """默认查询:只返回生效的文档""" - results = self.collection.query( - query_texts=[query], - n_results=top_k, - where={ - "status": "active" # ← 自动过滤 - } - ) - return results -``` - -**效果**: -- ✅ 返回:报销制度_v2.pdf(active) -- ❌ 过滤:报销制度_v1.pdf(superseded) -- ❌ 过滤:临时防疫政策.pdf(deprecated) - -### 历史查询(包含所有版本) - -```python -def query_kb_with_history(self, kb_name: str, query: str, top_k: int = 5): - """历史查询:包含所有版本""" - results = self.collection.query( - query_texts=[query], - n_results=top_k, - where={ - "status": {"$in": ["active", "superseded", "deprecated"]} - } - ) - return results -``` - -**效果**: -- ✅ 返回:报销制度_v2.pdf(active) -- ✅ 返回:报销制度_v1.pdf(superseded) -- ✅ 返回:临时防疫政策.pdf(deprecated) - -### 特定版本查询 - -```python -def query_specific_version(self, kb_name: str, query: str, version: str): - """查询特定版本""" - results = self.collection.query( - query_texts=[query], - n_results=5, - where={ - "version": version # 指定版本 - } - ) - return results -``` - ---- - -## 📊 数据清理策略 - -### 自动清理(可选) - -```python -def schedule_cleanup(kb_name: str, doc_id: str, delay_days: int = 7): - """ - 定期清理 superseded 版本 - - 策略: - 1. 保留最近 7 天的 superseded 版本(防止误操作) - 2. 7 天后自动删除向量库中的 chunks - 3. 保留版本记录(document_versions 表) - """ - # 7 天后执行 - schedule_task( - task=lambda: kb_manager.delete_chunks( - kb_name=kb_name, - filter={ - "document_id": doc_id, - "status": "superseded" - } - ), - delay=timedelta(days=delay_days) - ) -``` - -### 手动清理 - -```python -# API 端点 -POST /api/kb/public/cleanup -{ - "strategy": "superseded", # 清理 superseded 版本 - "older_than_days": 30 # 超过 30 天的 -} -``` - ---- - -## 🎯 关键优势 - -### 1. 物理层与逻辑层分离 - -**物理层(documents/)**: -- 文件可以保留(用户可能需要下载旧版) -- 文件可以删除(不影响向量库) -- 灵活管理 - -**逻辑层(向量库)**: -- 通过 metadata 控制可见性 -- 不需要物理删除 -- 支持快速恢复 - -### 2. 查询时自动过滤 - -```python -# 用户无感知,系统自动过滤废止文档 -results = query_kb(kb_name, query) # 只返回 active 文档 -``` - -### 3. 历史可追溯 - -```python -# 管理员可以查询历史版本 -history = get_document_history(kb_name, doc_id) -# 返回:v1 (superseded), v2 (active) -``` - -### 4. 操作可逆 - -```python -# 废止操作可以恢复 -deprecate_document(kb_name, doc_id) # 废止 -restore_document(kb_name, doc_id) # 恢复 -``` - ---- - -## 📈 存储成本分析 - -### 短期(7天内) - -``` -向量库大小 = active 文档 + superseded 文档(7天内) -存储成本 = 1.2x ~ 1.5x(相比只保留 active) -``` - -### 长期(7天后自动清理) - -``` -向量库大小 = active 文档 -存储成本 = 1.0x(与只保留 active 相同) -``` - -### 版本记录(永久保留) - -``` -document_versions 表大小 = 每个版本 ~1KB -100 个文档 × 平均 3 个版本 = 300KB(可忽略) -``` - ---- - -## ✅ 总结 - -### 方案 C 的核心特点 - -1. **物理层**:documents/ 目录可以保留或删除文件,不影响向量库 -2. **逻辑层**:通过 status 字段控制文档可见性 -3. **检索层**:查询时自动过滤非 active 文档 -4. **历史追溯**:保留版本记录,支持审计 -5. **操作可逆**:废止/恢复操作不删除数据 -6. **自动清理**:定期清理旧版本,控制存储成本 - -### 与现有方案的区别 - -| 方面 | 当前方案 | 方案 C | -|------|---------|--------| -| 文档更新 | 删除旧 chunks,添加新 chunks | 标记旧 chunks 为 superseded,添加新 chunks | -| 文档废止 | 删除 chunks | 标记 chunks 为 deprecated | -| 历史追溯 | ❌ 无法查询旧版本 | ✅ 可以查询任意版本 | -| 操作可逆 | ❌ 删除后无法恢复 | ✅ 废止后可以恢复 | -| 存储成本 | 低 | 中(短期略高,长期相同) | - ---- - -**文档版本**: v1.0 -**创建时间**: 2026-04-20 -**维护者**: RAG 服务开发组 diff --git a/docs/出题批卷系统设计.md b/docs/出题批卷系统设计.md deleted file mode 100644 index 35e37c1..0000000 --- a/docs/出题批卷系统设计.md +++ /dev/null @@ -1,635 +0,0 @@ -# 出题批卷系统设计 - -> **文档类型**: 系统设计文档 -> **创建日期**: 2026-04-10 -> **最后更新**: 2026-06-04 -> **状态**: 已实施 - ---- - -## 一、系统概述 - -### 1.1 背景 - -出题批卷系统是 RAG 知识库系统的扩展模块,支持: -- **按文件出题**:根据指定文档自动生成题目 -- **智能批卷**:支持选择题、填空题、简答题的自动批改 -- **溯源追踪**:每道题可追溯到来源文件和知识片段 - -### 1.2 模块结构 - -``` -exam_pkg/ # 考试系统 -├── generator.py # 出题逻辑(按文件/按主题生成题目) -├── grader.py # 批卷逻辑(选择题/填空题/简答题批改) -├── manager.py # 试卷管理与协调逻辑 -├── api.py # Flask Blueprint (exam_bp) -└── local_db.py # 本地题库 (SQLite) -``` - -**认证模块**: `auth/gateway.py` - 网关认证 - ---- - -## 二、出题系统设计 - -### 2.1 按文件出题接口 - -**接口路径**:`POST /exam/generate-by-file` - -**请求参数**: -```json -{ - "file_path": "public/产品手册.pdf", - "collection": "public_kb", - "choice_count": 5, - "blank_count": 2, - "short_answer_count": 2, - "difficulty": 3, - "choice_score": 2, - "blank_score": 3 -} -``` - -| 参数 | 类型 | 必填 | 默认值 | 说明 | -|------|------|------|--------|------| -| `file_path` | string | ✅ | - | 文件路径 | -| `collection` | string | ✅ | - | 向量库名称 | -| `choice_count` | int | ❌ | 3 | 选择题数量 | -| `blank_count` | int | ❌ | 2 | 填空题数量 | -| `short_answer_count` | int | ❌ | 2 | 简答题数量 | -| `difficulty` | int | ❌ | 3 | 难度等级 (1-5) | -| `choice_score` | int | ❌ | 2 | 每道选择题分值 | -| `blank_score` | int | ❌ | 3 | 每道填空题分值 | - -**返回结果**: -```json -{ - "exam_id": "uuid-xxxx-xxxx", - "source_file": { - "path": "public/产品手册.pdf", - "collection": "public_kb" - }, - "choice_questions": [ - { - "id": "q_choice_001", - "content": "根据保密制度,公司最高机密的处理原则是什么?", - "options": ["A. 可向客户透露", "B. 严禁外传", "C. 部门内共享", "D. 仅领导知晓"], - "answer": "B", - "analysis": "根据保密制度第1条规定...", - "knowledge_points": ["保密制度", "信息安全"], - "difficulty": 2, - "score": 2, - "source_file": "public/产品手册.pdf", - "source_snippet": "该题依据的知识片段..." - } - ], - "blank_questions": [...], - "short_answer_questions": [...], - "total_count": 9, - "total_score": 22, - "generated_at": "2026-04-10T14:00:00" -} -``` - -### 2.2 试卷状态流程 - -``` -生成试卷 → draft (草稿) - ↓ -提交审核 → pending_review (待审核) - ↓ -管理员审核 → approved (通过) / rejected (驳回) - ↓ -学生答题 → 批阅 → 生成报告 -``` - -**状态说明**: -| 状态 | 说明 | 可见范围 | -|------|------|----------| -| `draft` | 草稿,刚生成尚未提交审核 | 创建者可见 | -| `pending_review` | 待审核,已提交等待管理员审核 | 管理员可见 | -| `approved` | 已通过,可用于学生答题 | 所有用户可见 | -| `rejected` | 已驳回,不可使用 | 创建者可见 | - ---- - -## 三、题目格式规范 - -### 3.1 选择题 - -```json -{ - "id": "q_choice_001", - "content": "根据保密制度,公司最高机密的处理原则是什么?", - "options": [ - "A. 可向客户透露", - "B. 严禁外传", - "C. 部门内共享", - "D. 仅领导知晓" - ], - "answer": "B", - "analysis": "根据保密制度第1条规定,公司最高机密严禁外传,仅限特定人员知晓。", - "knowledge_points": ["保密制度", "信息安全"], - "difficulty": 2, - "score": 2, - "source_file": "public/产品手册.pdf", - "source_snippet": "原文相关片段..." -} -``` - -**字段说明**: - -| 字段 | 类型 | 必填 | 说明 | -|------|------|------|------| -| `id` | string | ✅ | 题目唯一标识 | -| `content` | string | ✅ | 题干内容 | -| `options` | array | ✅ | 选项列表,格式为 `["A. 选项内容", ...]` | -| `answer` | string | ✅ | 正确答案,单个字母(如 "A", "B") | -| `analysis` | string | ✅ | 答案解析 | -| `knowledge_points` | array | ❌ | 知识点标签 | -| `difficulty` | int | ❌ | 难度等级 1-5,默认 3 | -| `score` | int | ✅ | 题目分值 | -| `source_file` | string | ❌ | 来源文件路径 | -| `source_snippet` | string | ❌ | 来源文本片段 | - -### 3.2 填空题 - -```json -{ - "id": "q_blank_001", - "content": "公司财务报表应在每季度结束后______天内提交。", - "answer": "15", - "analysis": "根据财务管理制度第5条规定,季度报表需在季后15天内提交。", - "knowledge_points": ["财务管理"], - "difficulty": 3, - "score": 3 -} -``` - -**字段说明**: - -| 字段 | 类型 | 必填 | 说明 | -|------|------|------|------| -| `id` | string | ✅ | 题目唯一标识 | -| `content` | string | ✅ | 题干内容,空缺处用 `______` 表示 | -| `answer` | string | ✅ | 正确答案 | -| `analysis` | string | ✅ | 答案解析 | -| `knowledge_points` | array | ❌ | 知识点标签 | -| `difficulty` | int | ❌ | 难度等级 1-5 | -| `score` | int | ✅ | 题目分值 | - -### 3.3 简答题 - -```json -{ - "id": "q_short_001", - "content": "简述公司数据安全的三道防线。", - "reference_answer": { - "points": [ - {"point": "技术防线(防火墙、加密、访问控制等)", "score": 3}, - {"point": "制度防线(安全规定、审批流程、应急预案)", "score": 3}, - {"point": "人员防线(安全培训、意识教育、考核机制)", "score": 4} - ], - "total_score": 10 - }, - "analysis": "评分要点说明...", - "knowledge_points": ["数据安全"], - "difficulty": 4, - "score": 10 -} -``` - -**字段说明**: - -| 字段 | 类型 | 必填 | 说明 | -|------|------|------|------| -| `id` | string | ✅ | 题目唯一标识 | -| `content` | string | ✅ | 题干内容 | -| `reference_answer` | object | ✅ | 参考答案,包含评分要点 | -| `reference_answer.points` | array | ✅ | 得分点列表 | -| `reference_answer.points[].point` | string | ✅ | 得分点描述 | -| `reference_answer.points[].score` | int | ✅ | 该得分点分值 | -| `analysis` | string | ❌ | 整体解析 | -| `knowledge_points` | array | ❌ | 知识点标签 | -| `difficulty` | int | ❌ | 难度等级 1-5 | -| `score` | int | ✅ | 题目总分值 | - ---- - -## 四、批卷系统设计 - -### 4.1 批卷输入格式 - -**接口路径**:`POST /exam/grade-from-mysql` - -**当前格式(完整字段)**: -```json -{ - "exam_id": "uuid-xxxx-xxxx", - "student_id": "STU_2023001", - "student_name": "张三", - "answers": [ - { - "question_id": "q_choice_001", - "question_type": "choice", - "question_content": "根据保密制度,公司最高机密的处理原则是什么?", - "options": ["A. 可向客户透露", "B. 严禁外传", "C. 部门内共享", "D. 仅领导知晓"], - "correct_answer": "B", - "max_score": 2, - "student_answer": "B" - }, - { - "question_id": "q_blank_001", - "question_type": "blank", - "question_content": "公司财务报表应在每季度结束后______天内提交。", - "correct_answer": "15", - "max_score": 3, - "student_answer": "10" - }, - { - "question_id": "q_short_001", - "question_type": "short_answer", - "question_content": "简述公司数据安全的三道防线。", - "correct_answer": "{\"points\":[{\"point\":\"技术防线\",\"score\":3},{\"point\":\"制度防线\",\"score\":3},{\"point\":\"人员防线\",\"score\":4}]}", - "max_score": 10, - "student_answer": "第一道是技术防护,包括防火墙和加密;第二道是制度管理;第三道是员工培训。" - } - ] -} -``` - -**优化后格式(最小字段)**: -```json -{ - "exam_id": "uuid", - "student_id": "STU_001", - "student_name": "张三", - "answers": [ - { - "question_id": "q_choice_001", - "question_type": "choice", - "student_answer": "B" - }, - { - "question_id": "q_blank_001", - "question_type": "blank", - "student_answer": "15" - }, - { - "question_id": "q_short_001", - "question_type": "short_answer", - "student_answer": "第一道是技术防护..." - } - ] -} -``` - -### 4.2 批卷输出格式 - -```json -{ - "report_id": "report-uuid-xxxx", - "exam_id": "uuid-xxxx-xxxx", - "student_id": "STU_2023001", - "student_name": "张三", - "total_score": 12, - "max_score": 15, - "score_rate": 80.0, - "graded_at": "2026-04-12T14:30:00", - "results": [ - { - "question_id": "q_choice_001", - "question_type": "choice", - "correct": true, - "score": 2, - "max_score": 2, - "student_answer": "B", - "correct_answer": "B", - "feedback": "回答正确!" - }, - { - "question_id": "q_blank_001", - "question_type": "blank", - "correct": false, - "score": 0, - "max_score": 3, - "student_answer": "10", - "correct_answer": "15", - "feedback": "正确答案是15天,请复习财务管理制度。" - }, - { - "question_id": "q_short_001", - "question_type": "short_answer", - "score": 8, - "max_score": 10, - "student_answer": "第一道是技术防护...", - "score_details": [ - {"point": "技术防线", "earned": 3, "max": 3}, - {"point": "制度防线", "earned": 2, "max": 3}, - {"point": "人员防线", "earned": 3, "max": 4} - ], - "feedback": "整体回答较好,制度防线描述不够具体。", - "highlights": ["技术防线表述准确"], - "shortcomings": ["制度防线未具体说明"], - "suggestions": ["建议补充具体的制度名称"] - } - ], - "summary": { - "strengths": ["选择题掌握较好", "简答题要点覆盖全面"], - "weaknesses": ["填空题记忆不准确"], - "recommendations": ["重点复习财务管理制度第3章"] - } -} -``` - -### 4.3 批改流程 - -``` -┌─────────────────────────────────────────────────────────────────────┐ -│ 批量批改流程 │ -├─────────────────────────────────────────────────────────────────────┤ -│ │ -│ 1. 前端传入 answers (最小字段) │ -│ └─ 只有 question_id + question_type + student_answer │ -│ │ -│ 2. 后端查询题目详情 │ -│ └─ 从数据库/缓存获取 correct_answer, max_score, content │ -│ │ -│ 3. 按题型分组 │ -│ ├─ choice 组 → 批量调用 Dify 代码执行节点 │ -│ └─ blank/short_answer 组 → 批量调用 Dify LLM 节点 │ -│ │ -│ 4. 合并结果返回 │ -│ └─ 统一格式返回所有批改结果 │ -│ │ -└─────────────────────────────────────────────────────────────────────┘ -``` - ---- - -## 五、数据库设计 - -### 5.1 题目表 (questions) - -```sql -CREATE TABLE questions ( - id VARCHAR(64) PRIMARY KEY, -- 题目ID(UUID) - question_type ENUM('choice', 'blank', 'short_answer') NOT NULL, - content TEXT NOT NULL, -- 题干内容 - options JSON, -- 选择题选项(JSON数组) - correct_answer TEXT NOT NULL, -- 正确答案 - analysis TEXT, -- 解析 - knowledge_points JSON, -- 知识点(JSON数组) - difficulty TINYINT DEFAULT 3, -- 难度(1-5) - score INT NOT NULL, -- 分值 - - -- 溯源字段(核心) - source_file VARCHAR(255) NOT NULL, -- 来源文件路径 - source_collection VARCHAR(64) NOT NULL, -- 来源向量库 - source_snippet TEXT, -- 来源知识片段 - source_hash VARCHAR(64), -- 文件哈希(用于检测文件变更) - - -- 审核状态 - status ENUM('pending', 'approved', 'rejected') DEFAULT 'pending', - reviewed_by VARCHAR(64), - reviewed_at DATETIME, - - -- 元数据 - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - created_by VARCHAR(64), - updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - - INDEX idx_source_file (source_file), - INDEX idx_source_collection (source_collection), - INDEX idx_question_type (question_type), - INDEX idx_status (status) -); -``` - -### 5.2 试卷表 (exams) - -```sql -CREATE TABLE exams ( - id VARCHAR(64) PRIMARY KEY, -- 试卷ID - name VARCHAR(255) NOT NULL, -- 试卷名称 - description TEXT, -- 描述 - total_score INT NOT NULL, -- 总分 - total_count INT NOT NULL, -- 题目总数 - duration INT DEFAULT 60, -- 考试时长(分钟) - - -- 状态 - status ENUM('draft', 'pending', 'published', 'archived') DEFAULT 'draft', - published_at DATETIME, - - -- 元数据 - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - created_by VARCHAR(64), - updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - - INDEX idx_status (status) -); -``` - -### 5.3 试卷题目关联表 (exam_questions) - -```sql -CREATE TABLE exam_questions ( - exam_id VARCHAR(64) NOT NULL, - question_id VARCHAR(64) NOT NULL, - question_order INT NOT NULL, -- 题目顺序 - - PRIMARY KEY (exam_id, question_id), - FOREIGN KEY (exam_id) REFERENCES exams(id) ON DELETE CASCADE, - FOREIGN KEY (question_id) REFERENCES questions(id) ON DELETE CASCADE, - - INDEX idx_exam_id (exam_id), - INDEX idx_question_id (question_id) -); -``` - -### 5.4 学生答卷表 (student_answers) - -```sql -CREATE TABLE student_answers ( - id VARCHAR(64) PRIMARY KEY, - exam_id VARCHAR(64) NOT NULL, - student_id VARCHAR(64) NOT NULL, - student_name VARCHAR(100), - - question_id VARCHAR(64) NOT NULL, - question_type ENUM('choice', 'blank', 'short_answer') NOT NULL, - student_answer TEXT NOT NULL, -- 学生答案 - - -- 批阅结果 - score INT DEFAULT 0, - max_score INT NOT NULL, - feedback TEXT, - score_details JSON, -- 评分详情(JSON) - - -- 元数据 - submitted_at DATETIME DEFAULT CURRENT_TIMESTAMP, - graded_at DATETIME, - - FOREIGN KEY (exam_id) REFERENCES exams(id) ON DELETE CASCADE, - FOREIGN KEY (question_id) REFERENCES questions(id) ON DELETE CASCADE, - - INDEX idx_exam_student (exam_id, student_id), - INDEX idx_student_id (student_id) -); -``` - -### 5.5 批阅报告表 (grade_reports) - -```sql -CREATE TABLE grade_reports ( - id VARCHAR(64) PRIMARY KEY, - exam_id VARCHAR(64) NOT NULL, - student_id VARCHAR(64) NOT NULL, - student_name VARCHAR(100), - - total_score INT NOT NULL, - max_score INT NOT NULL, - score_rate DECIMAL(5,2), - - -- 整卷分析(可选) - analysis JSON, -- AI生成的整卷分析 - - graded_at DATETIME DEFAULT CURRENT_TIMESTAMP, - - FOREIGN KEY (exam_id) REFERENCES exams(id) ON DELETE CASCADE, - - INDEX idx_exam_id (exam_id), - INDEX idx_student_id (student_id) -); -``` - ---- - -## 六、文件修改联动 - -### 6.1 触发条件 - -当文件被修改或删除时,通过 `source_file` 字段查找受影响的题目。 - -### 6.2 联动逻辑 - -```sql --- 查找受影响的题目 -SELECT id, source_file, source_hash -FROM questions -WHERE source_file = 'public/产品手册.pdf'; - --- 如果文件哈希变更,标记题目需要重新审核 -UPDATE questions -SET status = 'pending', - source_hash = 'new_hash_value' -WHERE source_file = 'public/产品手册.pdf'; -``` - -### 6.3 联动接口 - -**接口路径**:`POST /exam/check-file-changes` - -**请求参数**: -```json -{ - "file_path": "public/产品手册.pdf", - "new_hash": "新的文件哈希" -} -``` - -**返回结果**: -```json -{ - "success": true, - "file_path": "public/产品手册.pdf", - "affected_questions": ["q_uuid_001", "q_uuid_002", ...], - "count": 15, - "recommendation": "建议重新生成该文件的题目" -} -``` - ---- - -## 七、API 接口汇总 - -### 7.1 出题接口 - -| 接口 | 方法 | 说明 | -|------|------|------| -| `/exam/generate` | POST | 按主题生成试卷 | -| `/exam/generate-by-file` | POST | 按文件生成题目 | -| `/exam/list` | GET | 获取试卷列表 | -| `/exam/` | GET | 获取试卷详情 | -| `/exam/` | PUT | 更新试卷 | -| `/exam/` | DELETE | 删除试卷 | -| `/exam//submit` | POST | 提交审核 | -| `/exam//review` | POST | 审核试卷(仅管理员) | -| `/exam/by-file` | GET | 查询文件关联的题目 | - -### 7.2 批卷接口 - -| 接口 | 方法 | 说明 | -|------|------|------| -| `/exam/grade-from-mysql` | POST | 基于传入题目批卷 | -| `/exam//grade` | POST | 批阅试卷 | -| `/exam/report/` | GET | 获取批阅报告 | -| `/exam/report/list` | GET | 批阅报告列表 | - -### 7.3 题库接口 - -| 接口 | 方法 | 说明 | -|------|------|------| -| `/exam/questions/search` | GET | 搜索题目 | - -### 7.4 联动接口 - -| 接口 | 方法 | 说明 | -|------|------|------| -| `/exam/check-file-changes` | POST | 检查文件变更影响的题目 | - ---- - -## 八、错误处理 - -### 8.1 错误响应格式 - -```json -{ - "error": "错误类型", - "message": "详细错误信息", - "details": {} -} -``` - -### 8.2 常见错误码 - -| HTTP状态码 | 错误类型 | 说明 | -|-----------|---------|------| -| 400 | bad_request | 请求参数格式错误 | -| 401 | unauthorized | 未认证 | -| 403 | forbidden | 权限不足 | -| 404 | not_found | 资源不存在 | -| 500 | internal_error | 服务器内部错误 | - ---- - -## 九、注意事项 - -1. **题目ID生成**:使用UUID,确保全局唯一 -2. **文件哈希**:用于检测文件变更,建议使用MD5或SHA256 -3. **批量批卷性能**:简答题批卷耗时,建议使用异步处理或并发 -4. **错误处理**:批卷失败时返回默认结果,不影响整体流程 -5. **认证方式**:出题系统使用 JWT Bearer Token 认证 - ---- - -## 十、变更记录 - -| 日期 | 版本 | 变更内容 | -|------|------|---------| -| 2026-06-04 | 2.1 | 更新模块结构:移除已删除的 analysis.py、question_hook.py,新增 generator.py、grader.py | -| 2026-04-13 | 2.0 | 合并出题批卷功能改造计划、批卷工作流优化计划、批卷接口规范 | -| 2026-04-12 | 1.2 | 新增最小字段输入格式,优化批量批改流程 | -| 2026-04-10 | 1.0 | 初始版本:按文件出题功能设计 | diff --git a/docs/出题批题后端对接指南.md b/docs/出题批题后端对接指南.md index 0b672eb..5ac2337 100644 --- a/docs/出题批题后端对接指南.md +++ b/docs/出题批题后端对接指南.md @@ -4,9 +4,12 @@ | 接口 | 方法 | 功能 | 超时建议 | |------|------|------|----------| -| `/exam/generate` | POST | 生成题目 | 120秒 | +| `/exam/generate` | POST | 生成题目(手动指定题型数量) | 120秒 | +| `/exam/generate-smart` | POST | 生成题目(AI 自动分析文档结构出题) | 120秒 | | `/exam/grade` | POST | 批阅答案 | 60秒 | +> **2026-06-05 变更**:`/exam/grade` 请求字段 `question_content` 已重命名为 `content`(破坏性变更)。详见 [出题批阅接口变更说明(2026-06-05)](出题批阅接口变更说明(2026-06-05).md)。 + --- ## 二、出题接口 @@ -175,7 +178,7 @@ Content-Type: application/json |------|------|------|------|------| | `question_id` | string | ✅ | 题目ID | 后端数据库 | | `question_type` | string | ✅ | 题型 | 后端数据库 | -| `question_content` | object | ✅ | 题目内容(含正确答案) | 后端数据库 | +| `content` | object | ✅ | 题目内容(含正确答案) | 后端数据库 | | `student_answer` | any | ✅ | 学生答案 | 学生提交 | | `max_score` | number | ✅ | 满分 | 后端数据库 | @@ -188,7 +191,7 @@ Content-Type: application/json { "question_id": "q-001", "question_type": "single_choice", - "question_content": { + "content": { "stem": "根据公司规定,员工薪资由哪几部分组成?", "data": { "options": [ @@ -206,7 +209,7 @@ Content-Type: application/json { "question_id": "q-002", "question_type": "multiple_choice", - "question_content": { + "content": { "stem": "以下哪些属于绩效奖金的评定因素?", "data": { "options": [ @@ -224,7 +227,7 @@ Content-Type: application/json { "question_id": "q-003", "question_type": "true_false", - "question_content": { + "content": { "stem": "公司规定员工每月绩效奖金上限为工资的20%。", "answer": "F" }, @@ -234,7 +237,7 @@ Content-Type: application/json { "question_id": "q-004", "question_type": "fill_blank", - "question_content": { + "content": { "stem": "员工薪资由___、___和___三部分组成。", "data": {"blank_count": 3}, "answer": [["基本工资"], ["绩效奖金", "绩效"], ["津贴补贴", "补贴"]] @@ -245,7 +248,7 @@ Content-Type: application/json { "question_id": "q-005", "question_type": "subjective", - "question_content": { + "content": { "stem": "请简述公司薪酬制度的核心原则。", "data": { "scoring_points": [ @@ -397,7 +400,7 @@ Content-Type: application/json 后端从数据库查询: - question_id - question_type - - question_content (含正确答案) + - content (含正确答案,原 question_content 已弃用) - score (满分) │ ▼ @@ -434,7 +437,7 @@ Content-Type: application/json |------|------|------| | question_id | 后端数据库 | 题目唯一标识 | | question_type | 后端数据库 | 题型 | -| question_content | 后端数据库 | 题目内容(含正确答案) | +| content | 后端数据库 | 题目内容(含正确答案,原 question_content 已弃用) | | student_answer | 学生提交 | 学生作答 | | **max_score** | **后端数据库** | 满分(决定得分上限) | diff --git a/docs/向量库边界风险分析.md b/docs/向量库边界风险分析.md index 8bc3cbe..95684b8 100644 --- a/docs/向量库边界风险分析.md +++ b/docs/向量库边界风险分析.md @@ -42,6 +42,8 @@ doc_path = docstore_dir / f"{doc_id}.json" # doc_id = chunk_id = "filename_N" ### P1:同名文件重复上传 — 旧切片残留 + 搜索结果重复 +> **✅ 已修复**(2026-06-04):上传接口现在自动替换同名文件,旧切片自动标记为 `superseded`。详见 [风险边界问题修复注意事项.md](风险边界问题修复注意事项.md)。 + **位置**:`api/document_routes.py` 第 246-250 行 ```python @@ -119,6 +121,8 @@ for item in all_items: ### P2:文件无原地更新机制 +> **✅ 已修复**(2026-06-04):上传接口新增自动替换机制(`replaced=true`),同名文件自动替换旧版本。 + **场景**:用户上传 `制度.pdf` v1 后发现内容有误,修改后想替换。当前系统没有 "更新文件" 接口,只能删除后重新上传。如果用户不知道要先删除,就会触发 P1 的重复问题。 **修复方向**:upload 接口增加 "如果同名文件已存在则替换" 选项(先 delete_document 再 add_file_to_kb),或提供独立的 "更新文档" API。 @@ -151,14 +155,14 @@ chunk_index = int(str(chunk_id_raw).rsplit('_', 1)[-1]) ### 风险总结 -| 等级 | 风险 | 核心原因 | 触发条件 | -|------|------|----------|----------| -| **P0** | RRF 融合吞结果 | 去重 key 缺少 collection | 多库有同名文件 | -| **P0** | DocStore 覆盖 | 存储路径缺少 collection | 多库有同名含表格/图片文件 | -| **P1** | 旧切片残留 | 重复上传只改名不替换 | 同名文件二次上传 | -| **P1** | _collection 回退错误 | 硬编码 collections[0] | 单库路径 + 多 collection | -| **P1** | search_multiple 去重 | 去重 key 缺少 collection | 直接调用低层 API | -| **P2** | citation 字段名不一致 | `collection` vs `_collection` | 非标准查询路径 | -| **P2** | 无文件更新机制 | 设计缺失 | 用户需要替换文档 | -| **P2** | 元数据不同步 | JSON 文件可能损坏 | 手动操作或异常退出 | -| **P3** | 文件名含下划线 | 无问题(rsplit 兼容) | — | +| 等级 | 风险 | 核心原因 | 触发条件 | 状态 | +|------|------|----------|----------|------| +| **P0** | RRF 融合吞结果 | 去重 key 缺少 collection | 多库有同名文件 | 未修复 | +| **P0** | DocStore 覆盖 | 存储路径缺少 collection | 多库有同名含表格/图片文件 | 未修复 | +| **P1** | 旧切片残留 | 重复上传只改名不替换 | 同名文件二次上传 | ✅ 已修复 | +| **P1** | _collection 回退错误 | 硬编码 collections[0] | 单库路径 + 多 collection | 未修复 | +| **P1** | search_multiple 去重 | 去重 key 缺少 collection | 直接调用低层 API | 未修复 | +| **P2** | citation 字段名不一致 | `collection` vs `_collection` | 非标准查询路径 | 未修复 | +| **P2** | 无文件更新机制 | 设计缺失 | 用户需要替换文档 | ✅ 已修复 | +| **P2** | 元数据不同步 | JSON 文件可能损坏 | 手动操作或异常退出 | 未修复 | +| **P3** | 文件名含下划线 | 无问题(rsplit 兼容) | — | 无需修复 | diff --git a/docs/多源信息融合指南.md b/docs/多源信息融合指南.md index ce845ba..a553f43 100644 --- a/docs/多源信息融合指南.md +++ b/docs/多源信息融合指南.md @@ -1,5 +1,7 @@ # 多源信息融合设计指南 +> **⚠️ 路径说明**:本文档描述的 `AgenticRAG.process()` 多源融合路径是**备用路径**(需启用网络搜索)。生产环境的 `/rag` 问答接口使用 `chat_routes.py` 的轻量编排路径(详见 [RAG数据流程.md](RAG数据流程.md)),不经过 `AgenticRAG.process()`。两条路径的区别见 [Agentic_RAG完整指南.md](Agentic_RAG完整指南.md)。 + ## 一、问题背景 当 Agentic RAG 同时使用知识库和网络搜索时,会遇到以下情况: diff --git a/docs/开发与系统模块说明.md b/docs/开发与系统模块说明.md index 58edfa4..8126078 100644 --- a/docs/开发与系统模块说明.md +++ b/docs/开发与系统模块说明.md @@ -73,8 +73,8 @@ | 文档解析 | MinerU 3.0+ | PDF/DOCX/PPTX/图片统一解析 | | 向量检索 | ChromaDB + BGE-base-zh | 本地向量数据库 + 嵌入模型 | | 关键词检索 | BM25 + jieba | 中文分词 + 倒排索引 | -| 重排序 | BGE-reranker-base | CrossEncoder 精排 | -| 大模型 | Qwen (通义千问) | 问答生成、查询改写、意图分析 | +| 重排序 | qwen3-rerank(云端)/ BGE-reranker-base(本地,图片二次评分) | CrossEncoder 精排 | +| 大模型 | deepseek-v4-flash / Qwen (通义千问) | 问答生成、查询改写、意图分析 | | 数据库 | SQLite | 会话管理、知识管理 | --- diff --git a/docs/架构与部署方案.md b/docs/架构与部署方案.md index 4fbfdfe..db38020 100644 --- a/docs/架构与部署方案.md +++ b/docs/架构与部署方案.md @@ -75,13 +75,14 @@ | 数据类型 | 存储位置 | 管理方 | 说明 | |----------|----------|--------|------| -| 向量数据 | ChromaDB | RAG 组 | 文档 embedding | -| 文档哈希 | SQLite | RAG 组 | 同步状态检测 | +| 向量数据 | ChromaDB | RAG 组 | 文档 embedding(每个知识库独立实例) | +| 文档哈希 | SQLite (knowledge.db) | RAG 组 | 同步状态检测 | | 原始文档 | 文件系统 | RAG 组 | documents/ 目录 | +| 反馈记录 | SQLite (feedback.db) | RAG 组 | 用户反馈、黑名单 | +| 会话数据 | SQLite (session.db) | RAG 组 | 会话管理 | +| 出题数据 | SQLite (exam.db) | RAG 组 | 题目/批阅 | | 用户账户 | MySQL/PG | 后端组 | 账号密码信息 | -| 会话历史 | MySQL/PG | 后端组 | 对话记录 | | 审计日志 | MySQL/PG | 后端组 | 操作日志 | -| 反馈记录 | MySQL/PG | 后端组 | 用户反馈 | | 题库数据 | MySQL/PG | 后端组 | 题目/试卷 | --- diff --git a/docs/测试指南.md b/docs/测试指南.md deleted file mode 100644 index 984d85a..0000000 --- a/docs/测试指南.md +++ /dev/null @@ -1,485 +0,0 @@ -# 测试指南 - -> **文档类型**: 测试指南 -> **创建日期**: 2026-04-05 -> **最后更新**: 2026-06-04 -> **文档总数**: 21个测试文档 - ---- - -## 一、测试文档清单 - -### 1.1 文档目录结构 - -``` -documents/ -├── public/ # 公开文档 - 所有人可见(包括未登录用户) -│ ├── 公司简介.txt -│ ├── 产品手册.pdf -│ ├── 产品手册.txt -│ ├── 员工手册.txt -│ ├── 组织架构.xlsx -│ ├── 组织架构说明.txt -│ └── 常见问题.txt -│ -├── internal/ # 内部文档 - 登录用户可见 -│ ├── 差旅管理办法.txt -│ ├── 请假制度.docx -│ ├── 信息安全管理制度.pdf -│ ├── 项目管理制度.xlsx -│ └── 会议纪要_2024Q1.txt -│ -├── confidential/ # 机密文档 - 管理层及以上可见 -│ ├── 财务报表_2024.pdf -│ ├── 薪酬制度.docx -│ ├── 合同台账.xlsx -│ ├── 战略规划.txt -│ └── 人员名册.txt -│ -└── secret/ # 绝密文档 - 仅管理员可见 - ├── 董事会决议.pdf - ├── 并购方案.docx - ├── 股权结构.xlsx - └── 核心技术机密.txt -``` - -### 1.2 文档格式分布 - -| 格式 | 数量 | 测试目的 | -|------|------|---------| -| TXT | 10个 | 测试纯文本解析、编码识别 | -| PDF | 4个 | 测试PDF解析、表格提取、中文字体 | -| DOCX | 3个 | 测试Word解析、标题样式、表格处理 | -| XLSX | 4个 | 测试Excel解析、多工作表、单元格数据 | - -### 1.3 权限级别 - -| 目录 | 权限级别 | 可见角色 | -|------|---------|---------| -| public | public | 所有人(含未登录用户) | -| internal | internal | user, manager, admin | -| confidential | confidential | manager, admin | -| secret | secret | admin only | - ---- - -## 二、测试环境准备 - -### 2.1 环境检查 - -```bash -# 检查 Python 版本 -python --version # 需要 Python 3.8+ - -# 检查依赖安装 -pip list | grep -E "chromadb|sentence-transformers|flask|jieba" - -# 检查模型文件 -ls models/bge-base-zh-v1.5/ -``` - -### 2.2 配置检查 - -确保 `config.py` 配置正确: -```python -# API配置 -DASHSCOPE_API_KEY = "your-api-key" -DASHSCOPE_BASE_URL = "https://dashscope.aliyuncs.com/compatible-mode/v1" -DASHSCOPE_MODEL = "qwen3.6-flash" # 主 LLM(文本生成 / RAG 对话) -INTENT_MODEL = "qwen-turbo" # 意图分析模型(轻量、确定性高) -``` - -> **注意**: Graph RAG(Neo4j)功能已废弃,相关配置(NEO4J_URI、USE_GRAPH_RAG 等)已移除。 - ---- - -## 三、测试执行流程 - -### 3.1 第一阶段:索引构建测试 - -#### 测试 1.1:向量索引构建 - -**测试步骤**: -```bash -# 清除旧索引 -rm -rf chroma_db/ - -# 重建向量索引 -python scripts/rebuild_multi_kb.py -``` - -**预期结果**: -- 控制台显示文档加载进度 -- 显示各格式文档解析数量 -- 显示向量构建进度 -- 生成 `chroma_db/` 目录 - -**验证方法**: -```python -import chromadb -client = chromadb.PersistentClient(path="./chroma_db") -collection = client.get_collection("knowledge_base") -print(f"向量数量: {collection.count()}") -``` - -#### 测试 1.2:BM25 索引构建 - -**测试步骤**: -```bash -# BM25索引会随向量索引一起构建 -# 检查索引文件 -ls -la bm25_index.pkl -``` - -**预期结果**: -- 生成 `bm25_index.pkl` 文件 -- 文件大小约 1-5 MB - ---- - -### 3.2 第二阶段:权限控制测试 - -#### 测试 2.1:未登录用户权限 - -**测试步骤**: -```bash -# 不带 Token 访问 -curl -X POST http://localhost:5001/rag \ - -H "Content-Type: application/json" \ - -d '{"message": "公司的产品有哪些?"}' -``` - -**预期结果**: -- 只返回 public 目录下的内容 -- 不返回 internal、confidential、secret 内容 - -#### 测试 2.2:user 角色权限 - -**测试步骤**: -```bash -# 使用 mock token 登录 -curl -X POST http://localhost:5001/rag \ - -H "Content-Type: application/json" \ - -H "Authorization: Bearer mock-token-testuser" \ - -d '{"message": "差旅费标准是多少?"}' -``` - -**预期结果**: -- 返回 public + internal 目录内容 -- 不返回 confidential、secret 内容 - -#### 测试 2.3:manager 角色权限 - -**测试步骤**: -```bash -curl -X POST http://localhost:5001/rag \ - -H "Authorization: Bearer mock-token-manager" \ - -H "Content-Type: application/json" \ - -d '{"message": "2024年财务报表显示净利润是多少?"}' -``` - -**预期结果**: -- 返回 public + internal + confidential 内容 -- 正确回答财务相关问题 -- 不返回 secret 目录内容 - -#### 测试 2.4:admin 角色权限 - -**测试步骤**: -```bash -curl -X POST http://localhost:5001/rag \ - -H "Authorization: Bearer mock-token-admin" \ - -H "Content-Type: application/json" \ - -d '{"message": "董事会决议的并购方案是什么?"}' -``` - -**预期结果**: -- 返回所有目录内容 -- 正确回答涉及绝密信息的问题 - ---- - -### 3.3 第三阶段:检索质量测试 - -#### 测试 3.1:向量语义检索 - -| 测试问题 | 预期命中文档 | 预期答案关键点 | -|---------|------------|--------------| -| 公司有哪些产品? | 公司简介.txt、产品手册.pdf | 智能数据分析平台、AI知识图谱平台、RAG系统 | -| 请假需要提前几天申请? | 请假制度.docx | 1天以内直属上级、3天内部门负责人、7天以上总经理 | -| 年假有几天? | 请假制度.docx | 1年5天、5年7天、10年10天、20年15天 | - -**测试命令**: -```bash -curl -X POST http://localhost:5001/search \ - -H "Content-Type: application/json" \ - -H "Authorization: Bearer mock-token-admin" \ - -d '{"query": "公司有哪些产品?", "top_k": 5}' -``` - -#### 测试 3.2:BM25 关键词检索 - -| 测试关键词 | 预期命中文档 | -|-----------|------------| -| 差旅补助 500元 | 差旅管理办法.txt | -| 年假 15天 | 请假制度.docx | -| 薪酬 P5 35万 | 薪酬制度.docx | - -#### 测试 3.3:混合检索 + Rerank - -**测试步骤**: -```bash -curl -X POST http://localhost:5001/search \ - -H "Content-Type: application/json" \ - -H "Authorization: Bearer mock-token-admin" \ - -d '{"query": "出差住宿标准是多少?", "top_k": 10}' -``` - -**预期结果**: -- 返回结果包含 rerank_score -- 结果排序比纯向量检索更准确 - ---- - -### 3.4 第四阶段:Agentic RAG 测试 - -#### 测试 4.1:简单问题直接回答 - -**测试问题**: -```bash -curl -X POST http://localhost:5001/rag \ - -H "Authorization: Bearer mock-token-admin" \ - -H "Content-Type: application/json" \ - -d '{"message": "公司的请假制度是什么?"}' -``` - -**预期结果**: -- Agent 决策为 "answer" -- 直接返回检索结果 -- 无需多轮检索 - -#### 测试 4.2:查询改写 - -**测试问题**: -```bash -curl -X POST http://localhost:5001/rag \ - -H "Authorization: Bearer mock-token-admin" \ - -H "Content-Type: application/json" \ - -d '{"message": "我想了解关于报销的事情"}' -``` - -**预期结果**: -- Agent 决策为 "rewrite" -- 查询被改写为更具体的表述 - -#### 测试 4.3:问题分解 - -**测试问题**: -```bash -curl -X POST http://localhost:5001/rag \ - -H "Authorization: Bearer mock-token-admin" \ - -H "Content-Type: application/json" \ - -d '{"message": "请假和报销的流程分别是什么?"}' -``` - -**预期结果**: -- Agent 决策为 "decompose" -- 问题被分解为多个子问题 -- 分别检索后合并回答 - -#### 测试 4.4:多源融合 - -**测试问题**: -```bash -curl -X POST http://localhost:5001/rag \ - -H "Authorization: Bearer mock-token-admin" \ - -H "Content-Type: application/json" \ - -d '{"message": "技术部的组织架构和职责分工是怎样的?"}' -``` - -**预期结果**: -- 同时触发向量检索和 BM25 关键词检索 -- 返回结果经过 Rerank 重排序并标注来源 - ---- - -### 3.5 第五阶段:出题系统测试 - -#### 测试 5.1:试卷生成 - -**测试步骤**: -```bash -curl -X POST http://localhost:5001/exam/generate \ - -H "Authorization: Bearer mock-token-admin" \ - -H "Content-Type: application/json" \ - -d '{"topic": "公司制度基础", "choice_count": 5, "name": "公司制度测试"}' -``` - -**预期结果**: -- 返回 exam_id -- 试卷状态为 "draft" -- 包含选择题、填空题、简答题 - -#### 测试 5.2:试卷审核 - -**测试步骤**: -```bash -curl -X POST http://localhost:5001/exam//review \ - -H "Authorization: Bearer mock-token-admin" \ - -H "Content-Type: application/json" \ - -d '{"action": "approve"}' -``` - -**预期结果**: -- 返回 success: true -- 试卷状态变为 "approved" - -#### 测试 5.3:试卷批阅 - -**测试步骤**: -```bash -curl -X POST http://localhost:5001/exam//grade \ - -H "Authorization: Bearer mock-token-admin" \ - -H "Content-Type: application/json" \ - -d '{"student_name": "测试学生", "answers": {"choice_1": "A", "choice_2": "B"}}' -``` - -**预期结果**: -- 返回批阅报告 -- 包含每题得分和总分 - ---- - -### 3.6 第六阶段:API 接口测试 - -#### 测试 6.1:认证接口 - -```bash -# 获取用户信息 -curl http://localhost:5001/auth/me \ - -H "Authorization: Bearer mock-token-admin" -``` - -#### 测试 6.2:会话管理 - -```bash -# 创建会话并对话 -curl -X POST http://localhost:5001/chat \ - -H "Authorization: Bearer mock-token-admin" \ - -H "Content-Type: application/json" \ - -d '{"message": "你好", "session_id": "test-001"}' - -# 获取会话列表 -curl http://localhost:5001/sessions \ - -H "Authorization: Bearer mock-token-admin" - -# 获取会话历史 -curl http://localhost:5001/history/test-001 \ - -H "Authorization: Bearer mock-token-admin" -``` - -#### 测试 6.3:健康检查 - -```bash -curl http://localhost:5001/health -``` - -**预期结果**: -```json -{ - "status": "ok", - "knowledge_base": "多向量库模式 (按集合提供服务)", - "bm25_index": "动态按需加载", - "mode": "Agentic RAG" -} -``` - ---- - -## 四、测试报告模板 - -### 4.1 测试执行摘要 - -| 项目 | 内容 | -|------|------| -| 测试日期 | YYYY-MM-DD | -| 测试人员 | | -| 测试环境 | | -| 文档数量 | 21个 | -| 发现问题数量 | | - -### 4.2 测试结果统计 - -| 测试类型 | 用例数 | 通过 | 失败 | 阻塞 | -|----------|--------|------|------|------| -| 索引构建测试 | 2 | | | | -| 权限控制测试 | 4 | | | | -| 检索质量测试 | 3 | | | | -| Agentic RAG测试 | 4 | | | | -| 出题系统测试 | 3 | | | | -| API接口测试 | 3 | | | | -| **总计** | **19** | | | | - -### 4.3 问题列表 - -| 编号 | 测试用例 | 问题描述 | 严重程度 | 状态 | -|------|----------|----------|----------|------| -| BUG-001 | | | 高/中/低 | 待修复 | - ---- - -## 五、快速测试命令 - -```bash -# 一键索引重建 -python scripts/rebuild_multi_kb.py - -# 启动服务 -python main.py - -# 快速测试 -curl http://localhost:5001/health -``` - ---- - -## 六、测试文档生成 - -测试文档通过脚本 `generate_test_docs.py` 自动生成,支持以下格式: - -- **TXT**: 直接文本写入 -- **DOCX**: 使用 `python-docx` 库生成 -- **XLSX**: 使用 `openpyxl` 库生成 -- **PDF**: 使用 `reportlab` 库生成 - -运行命令: -```bash -python generate_test_docs.py -``` - -依赖安装: -```bash -pip install python-docx openpyxl reportlab -``` - ---- - -## 七、文本量统计 - -| 目录 | 文件数 | 总字符数 | 总词数(估计) | -|------|--------|---------|-------------| -| public | 7 | ~35,000 | ~15,000 | -| internal | 5 | ~25,000 | ~10,000 | -| confidential | 5 | ~20,000 | ~8,000 | -| secret | 4 | ~15,000 | ~6,000 | -| **合计** | **21** | **~95,000** | **~39,000** | - ---- - -## 八、变更记录 - -| 日期 | 版本 | 变更内容 | -|------|------|---------| -| 2026-06-04 | 3.0 | 移除 Graph RAG(Neo4j)相关内容,更新模型配置和章节编号 | -| 2026-04-13 | 2.0 | 合并测试文档清单和测试执行流程 | -| 2026-04-05 | 1.0 | 初始版本 | diff --git a/docs/版本管理实施完成报告.md b/docs/版本管理实施完成报告.md deleted file mode 100644 index 0757d8b..0000000 --- a/docs/版本管理实施完成报告.md +++ /dev/null @@ -1,415 +0,0 @@ -# 企业文档版本管理方案实施完成报告 - -## ✅ 实施完成 - -所有计划的功能已成功实现,代码已提交。 - ---- - -## 📊 实施总结 - -### 已完成的 Phase - -| Phase | 任务 | 状态 | 说明 | -|-------|------|------|------| -| Phase 1 | 清理冗余代码 | ✅ 完成 | 删除 diff.py,简化 lifecycle.py | -| Phase 2 | 增强 sync.py | ✅ 完成 | 添加版本管理逻辑 | -| Phase 3 | 增强 manager.py | ✅ 完成 | 添加状态标记和查询过滤 | -| Phase 4 | 添加 API 端点 | ✅ 完成 | 废止/恢复/版本历史 API | -| Phase 5 | 验证数据库 | ✅ 完成 | 添加性能优化索引 | -| Phase 6 | 创建清理机制 | ✅ 完成 | 自动清理旧版本 | - ---- - -## 📝 代码变更清单 - -### 删除的文件(2个) - -1. **knowledge/diff.py** (525行) - - 原因:完全未使用 - - 影响:无 - -2. **knowledge/lifecycle.py** (618行) - - 原因:大部分功能未使用 - - 替代:knowledge/document_versions.py - -### 新增的文件(2个) - -1. **knowledge/document_versions.py** (300行) - - 功能:文档版本查询(简化版) - - 保留:get_document_history, get_active_version, create_version_record, log_version_change - -2. **knowledge/cleanup.py** (250行) - - 功能:自动清理 superseded/deprecated 版本 - - 可选:定时任务调度 - -### 修改的文件(4个) - -1. **knowledge/sync.py** - - 修改:process_change 方法 - - 新增:_get_current_version, _generate_version_id, _record_version_change - - 功能:文档更新时标记旧版本为 superseded,生成新版本号 - -2. **knowledge/manager.py** - - 新增:mark_document_as_superseded 方法 - - 修改:search_single 方法(添加 include_deprecated 参数) - - 功能:状态标记和查询过滤 - -3. **api/kb_routes.py** - - 新增:3个 API 端点 - - POST /collections//documents//deprecate - - POST /collections//documents//restore - - GET /collections//documents//versions - -4. **data/db.py** - - 新增:2个性能优化索引 - - idx_document_versions_status - - idx_version_change_logs_document - ---- - -## 🎯 功能实现 - -### 1. 文档更新(版本管理) - -**流程**: -``` -文档修改 → 检测变更 → 标记旧版本为 superseded → 添加新版本 → 记录变更日志 -``` - -**代码位置**: -- `knowledge/sync.py` - process_change 方法 - -**效果**: -- 旧版本:status = "superseded",查询时被过滤 -- 新版本:status = "active",查询时返回 -- 版本号:自动递增(v1 → v2 → v3) - -### 2. 文档废止(软删除) - -**API**: -```bash -POST /api/kb/collections/public_kb/documents/报销制度.pdf/deprecate -{ - "reason": "制度已废止" -} -``` - -**代码位置**: -- `knowledge/manager.py` - deprecate_document 方法 -- `api/kb_routes.py` - deprecate_document 端点 - -**效果**: -- 文档状态:status = "deprecated" -- 查询行为:不返回该文档 -- 可恢复:调用 restore API - -### 3. 文档恢复 - -**API**: -```bash -POST /api/kb/collections/public_kb/documents/报销制度.pdf/restore -``` - -**代码位置**: -- `knowledge/manager.py` - restore_document 方法 -- `api/kb_routes.py` - restore_document 端点 - -**效果**: -- 文档状态:status = "active" -- 查询行为:恢复返回 - -### 4. 版本历史查询 - -**API**: -```bash -GET /api/kb/collections/public_kb/documents/报销制度.pdf/versions?limit=10 -``` - -**代码位置**: -- `knowledge/document_versions.py` - get_document_history 方法 -- `api/kb_routes.py` - get_document_versions 端点 - -**返回**: -```json -{ - "success": true, - "versions": [ - { - "version": "v2", - "status": "active", - "created_at": "2024-01-15T10:00:00", - "chunk_count": 20 - }, - { - "version": "v1", - "status": "superseded", - "created_at": "2023-01-01T10:00:00", - "chunk_count": 15 - } - ] -} -``` - -### 5. 查询过滤 - -**代码位置**: -- `knowledge/manager.py` - search_single 方法 - -**默认行为**: -```python -# 只返回 active 状态的文档 -result = search_single(kb_name, query_vector, query_text, include_deprecated=False) -``` - -**包含废止文档**: -```python -# 返回所有状态的文档(包括 deprecated 和 superseded) -result = search_single(kb_name, query_vector, query_text, include_deprecated=True) -``` - -### 6. 自动清理 - -**代码位置**: -- `knowledge/cleanup.py` - -**使用方式**: -```python -from knowledge.cleanup import cleanup_superseded_versions - -# 清理超过 7 天的 superseded 版本 -cleaned = cleanup_superseded_versions(days_to_keep=7) -``` - -**定时任务**(可选): -```python -from knowledge.cleanup import start_cleanup_scheduler - -# 每天凌晨 3 点自动清理 -start_cleanup_scheduler( - superseded_days=7, - deprecated_days=30, - schedule_time="03:00" -) -``` - ---- - -## 📈 代码统计 - -### 代码量变化 - -| 指标 | 变化 | -|------|------| -| 删除代码 | -1143 行(diff.py + lifecycle.py) | -| 新增代码 | +550 行(document_versions.py + cleanup.py + 修改) | -| **净减少** | **-593 行** | - -### 功能完整性 - -| 功能 | 状态 | -|------|------| -| 文档版本管理 | ✅ 已实现 | -| 软删除和恢复 | ✅ 已实现 | -| 历史追溯 | ✅ 已实现 | -| 查询自动过滤 | ✅ 已实现 | -| 自动清理 | ✅ 已实现(可选) | - ---- - -## 🧪 测试建议 - -### 1. 单元测试 - -创建 `tests/test_version_management.py`: - -```python -def test_document_update_creates_version(): - """测试文档更新时创建新版本""" - # 1. 上传文档 v1 - # 2. 修改文档上传 v2 - # 3. 验证 v1 状态为 superseded - # 4. 验证 v2 状态为 active - # 5. 查询只返回 v2 - -def test_deprecate_and_restore(): - """测试废止和恢复""" - # 1. 废止文档 - # 2. 验证查询不返回该文档 - # 3. 恢复文档 - # 4. 验证查询返回该文档 - -def test_version_history(): - """测试版本历史查询""" - # 1. 创建多个版本 - # 2. 查询版本历史 - # 3. 验证返回所有版本记录 -``` - -### 2. 集成测试 - -```bash -# 1. 上传文档 -curl -X POST http://localhost:5001/api/kb/public/upload \ - -F "file=@报销制度_v1.pdf" - -# 2. 查询文档(应返回 v1) -curl http://localhost:5001/api/rag \ - -d '{"query": "报销流程", "kb_name": "public"}' - -# 3. 上传新版本 -curl -X POST http://localhost:5001/api/kb/public/upload \ - -F "file=@报销制度_v2.pdf" - -# 4. 查询文档(应只返回 v2) -curl http://localhost:5001/api/rag \ - -d '{"query": "报销流程", "kb_name": "public"}' - -# 5. 查询版本历史 -curl http://localhost:5001/api/kb/collections/public_kb/documents/报销制度.pdf/versions - -# 6. 废止文档 -curl -X POST http://localhost:5001/api/kb/collections/public_kb/documents/报销制度.pdf/deprecate \ - -d '{"reason": "制度已废止"}' - -# 7. 查询文档(应不返回) -curl http://localhost:5001/api/rag \ - -d '{"query": "报销流程", "kb_name": "public"}' - -# 8. 恢复文档 -curl -X POST http://localhost:5001/api/kb/collections/public_kb/documents/报销制度.pdf/restore - -# 9. 查询文档(应返回) -curl http://localhost:5001/api/rag \ - -d '{"query": "报销流程", "kb_name": "public"}' -``` - -### 3. 性能测试 - -```python -import time - -# 测试查询性能(带状态过滤) -start = time.time() -result = kb_manager.search_single(kb_name, query_vector, query_text) -print(f"无过滤: {time.time() - start:.3f}s") - -start = time.time() -result = kb_manager.search_single(kb_name, query_vector, query_text, include_deprecated=False) -print(f"带过滤: {time.time() - start:.3f}s") - -# 预期:性能差异 < 10% -``` - ---- - -## ⚠️ 注意事项 - -### 1. 数据库迁移 - -如果数据库已存在,需要运行索引创建: - -```python -from data.db import get_connection - -with get_connection("knowledge") as conn: - conn.execute(''' - CREATE INDEX IF NOT EXISTS idx_document_versions_status - ON document_versions(document_id, collection, status) - ''') - conn.execute(''' - CREATE INDEX IF NOT EXISTS idx_version_change_logs_document - ON version_change_logs(document_id, collection, created_at DESC) - ''') - conn.commit() -``` - -### 2. 现有文档处理 - -现有文档的 metadata 中可能没有 `status` 字段,需要批量更新: - -```python -from knowledge.manager import get_kb_manager - -kb_manager = get_kb_manager() -kb_names = kb_manager.list_collections() - -for kb_name in kb_names: - collection = kb_manager.get_collection(kb_name) - result = collection.get() - - # 更新所有没有 status 字段的 chunks - updated_metadatas = [] - ids_to_update = [] - - for i, meta in enumerate(result['metadatas']): - if 'status' not in meta: - meta['status'] = 'active' - meta['version'] = 'v1' - updated_metadatas.append(meta) - ids_to_update.append(result['ids'][i]) - - if ids_to_update: - collection.update(ids=ids_to_update, metadatas=updated_metadatas) - print(f"更新 {kb_name}: {len(ids_to_update)} chunks") -``` - -### 3. 清理策略 - -建议的清理策略: -- **superseded 版本**:保留 7 天(防止误操作) -- **deprecated 版本**:保留 30 天(审计需求) -- **定时执行**:每天凌晨 3 点(低峰期) - ---- - -## 📚 相关文档 - -- [企业文档更新管理方案](docs/企业文档更新管理方案.md) - ---- - -## 🎉 总结 - -### 实施成果 - -1. ✅ **代码质量提升** - - 删除 ~1000 行冗余代码 - - 降低维护成本 60% - - 提高代码可读性 - -2. ✅ **功能完整性** - - 支持文档版本管理 - - 支持软删除和恢复 - - 支持历史追溯 - - 查询自动过滤废止文档 - -3. ✅ **性能影响** - - 查询性能:无明显影响(< 5%) - - 存储成本:短期略增(1.2x),长期持平(自动清理) - - 更新性能:略有提升(标记 vs 删除+重建) - -### 下一步 - -1. **测试验证** - - 运行单元测试 - - 执行集成测试 - - 验证性能影响 - -2. **部署准备** - - 更新现有文档的 metadata - - 创建数据库索引 - - 配置清理任务(可选) - -3. **文档更新** - - 更新 API 文档 - - 更新用户手册 - - 更新部署指南 - ---- - -**实施完成时间**: 2026-04-20 -**实施者**: Claude Code -**代码审查**: 已完成 -**测试状态**: 已实施 -**部署状态**: 已部署 diff --git a/docs/状态码功能更新说明.md b/docs/状态码功能更新说明.md deleted file mode 100644 index 04a9663..0000000 --- a/docs/状态码功能更新说明.md +++ /dev/null @@ -1,219 +0,0 @@ -# 状态码功能更新说明 - -> **更新日期**: 2026-04-30 -> **改动范围**: 长时间运行端口的响应格式增强 - ---- - -## 一、更新概述 - -为方便后端判断 RAG 服务处理状态,以下端口增加了统一的 `status_code` 字段: - -| 端口 | 改动文件 | -|------|----------| -| `/documents/upload` | `api/document_routes.py` | -| `/documents/batch-upload` | `api/document_routes.py` | -| `/sync` | `api/sync_routes.py` | -| `/exam/generate` | `exam_pkg/api.py` | -| `/exam/grade` | `exam_pkg/api.py` | - ---- - -## 二、状态码速查表 - -### 处理中 (10xx) - -| 状态码 | 常量名 | 说明 | -|--------|--------|------| -| 1000 | PROCESSING | 通用处理中 | -| 1010 | SYNC_RUNNING | 同步进行中 | -| 1020 | EXAM_GENERATING | 出题生成中 | -| 1021 | EXAM_GRADING | 批阅进行中 | - -### 成功 (20xx) - -| 状态码 | 常量名 | 说明 | -|--------|--------|------| -| 2000 | SUCCESS | 通用成功 | -| 2002 | UPLOAD_SUCCESS | 文件上传成功 | -| 2003 | BATCH_UPLOAD_SUCCESS | 批量上传成功 | -| 2010 | SYNC_SUCCESS | 同步完成 | -| 2020 | EXAM_SUCCESS | 出题成功 | -| 2021 | GRADE_SUCCESS | 批阅完成 | - -### 客户端错误 (40xx) - -| 状态码 | 常量名 | 说明 | -|--------|--------|------| -| 4000 | BAD_REQUEST | 请求参数错误 | -| 4004 | NO_FILE | 没有上传文件 | -| 4005 | NO_FILE_SELECTED | 没有选择文件 | -| 4006 | NO_COLLECTION | 未指定向量库 | -| 4007 | UNSUPPORTED_FORMAT | 不支持的文件格式 | -| 4008 | FILE_TOO_LARGE | 文件过大 | - -### 服务端错误 (50xx) - -| 状态码 | 常量名 | 说明 | -|--------|--------|------| -| 5000 | INTERNAL_ERROR | 内部错误 | -| 5010 | SYNC_ERROR | 同步失败 | -| 5020 | EXAM_ERROR | 出题失败 | -| 5021 | GRADE_ERROR | 批阅失败 | - ---- - -## 三、各端口改动对照 - -### 3.1 文档上传 `/documents/upload` - -| 场景 | 旧响应 | 新响应 | -|------|--------|--------| -| 成功 | `{"success": true, ...}` | `{"status": "success", "status_code": 2002, ...}` | -| 无文件 | `{"error": "没有上传文件"}` | `{"status": "failed", "status_code": 4004, ...}` | -| 格式错误 | `{"error": "不支持的文件类型"}` | `{"status": "failed", "status_code": 4007, ...}` | -| 文件过大 | `{"error": "文件大小超过限制"}` | `{"status": "failed", "status_code": 4008, ...}` | - -### 3.2 批量上传 `/documents/batch-upload` - -| 场景 | 旧响应 | 新响应 | -|------|--------|--------| -| 成功 | `{"success": true, ...}` | `{"status": "success", "status_code": 2003, ...}` | - -### 3.3 同步服务 `/sync` - -| 场景 | 旧响应 | 新响应 | -|------|--------|--------| -| 成功 | `{"success": true, ...}` | `{"status": "success", "status_code": 2010, ...}` | -| 服务不可用 | `{"error": "同步服务未启用"}` | `{"status": "failed", "status_code": 5000, ...}` | -| 同步失败 | `{"error": "xxx"}` | `{"status": "failed", "status_code": 5010, ...}` | - -### 3.4 出题 `/exam/generate` - -| 场景 | 旧响应 | 新响应 | -|------|--------|--------| -| 成功 | `{"success": true, ...}` | `{"status": "success", "status_code": 2020, ...}` | -| 参数缺失 | `{"error": "缺少参数"}` | `{"status": "failed", "status_code": 4000, ...}` | -| 出题失败 | `{"error": "xxx"}` | `{"status": "failed", "status_code": 5020, ...}` | - -### 3.5 批阅 `/exam/grade` - -| 场景 | 旧响应 | 新响应 | -|------|--------|--------| -| 成功 | `{"success": true, ...}` | `{"status": "success", "status_code": 2021, ...}` | -| 批阅失败 | `{"error": "xxx"}` | `{"status": "failed", "status_code": 5021, ...}` | - ---- - -## 四、响应格式示例 - -### 成功响应 - -```json -{ - "status": "success", - "status_code": 2002, - "message": "文件上传成功", - "success": true, - "data": { - "file": { - "filename": "document.pdf", - "collection": "public_kb", - "path": "public_kb/document.pdf", - "size": 102400 - } - } -} -``` - -### 错误响应 - -```json -{ - "status": "failed", - "status_code": 4007, - "message": "不支持的文件格式: .exe,支持: pdf, docx, doc, xlsx, txt", - "success": false, - "error": { - "error": "UNSUPPORTED_FORMAT", - "error_code": 4007, - "details": {} - } -} -``` - ---- - -## 五、后端对接代码示例 - -### Python 示例 - -```python -def handle_rag_response(response): - """处理 RAG 服务响应""" - data = response.json() - status_code = data.get("status_code", 0) - - # 判断处理状态 - if 2000 <= status_code < 3000: - # 成功 - return data.get("data", data) - - elif 4000 <= status_code < 5000: - # 客户端错误 - raise ClientError(data.get("message"), status_code) - - elif 5000 <= status_code < 6000: - # 服务端错误 - raise ServerError(data.get("message"), status_code) - - # 兼容旧格式 - if data.get("success"): - return data - raise Exception(data.get("error", "未知错误")) -``` - -### JavaScript 示例 - -```javascript -function handleRagResponse(data) { - const statusCode = data.status_code || 0; - - if (statusCode >= 2000 && statusCode < 3000) { - // 成功 - return data.data || data; - } else if (statusCode >= 4000 && statusCode < 5000) { - // 客户端错误 - throw new ClientError(data.message, statusCode); - } else if (statusCode >= 5000 && statusCode < 6000) { - // 服务端错误 - throw new ServerError(data.message, statusCode); - } - - // 兼容旧格式 - if (data.success) return data; - throw new Error(data.error || "未知错误"); -} -``` - ---- - -## 六、向后兼容说明 - -所有响应保留了 `success` 字段: -- 成功时 `success: true` -- 失败时 `success: false` - -旧代码仍可通过 `success` 字段判断,无需立即修改。 - ---- - -## 七、文件变更清单 - -| 文件 | 变更类型 | -|------|----------| -| `core/status_codes.py` | 新增 | -| `api/response_utils.py` | 新增 | -| `api/document_routes.py` | 修改 | -| `api/sync_routes.py` | 修改 | -| `exam_pkg/api.py` | 修改 | diff --git a/docs/生产路径优化计划.md b/docs/生产路径优化计划.md deleted file mode 100644 index 4e07e5f..0000000 --- a/docs/生产路径优化计划.md +++ /dev/null @@ -1,367 +0,0 @@ -## 生产 RAG 路径优化计划 - -> 原则:小步快跑,每阶段独立可测,打完 commit 验证质量后再推进下一阶段。 -> 如果某阶段导致回答质量下降,直接 `git revert` 回退到上一个 commit。 - ---- - -### 实施进度总览(更新于 2026-06) - -| 阶段 | 内容 | 状态 | 说明 | -|------|------|------|------| -| Phase 0 | 基线建立 + 评估基础设施 | **部分实施** | `scripts/eval_e2e.py` 已创建,但 `data/eval_dataset.json` 和 `baseline.json` 尚未生成 | -| Phase 1 | Rerank 分数传递与上下文过滤 | **已实施** | `RERANK_CONTEXT_MIN_SCORE` 已配置,`_order_text_contexts_for_prompt` 已接受 `min_score` 参数 | -| Phase 2 | Token 预算控制 | **已实施** | `CONTEXT_MAX_CHARS=8000` / `CONTEXT_SOFT_LIMIT=6000` 已配置,`_build_context_with_budget()` 已实现 | -| Phase 3 | 上下文扩展精细化 | **已实施** | `EXPANSION_SCORE_THRESHOLD=0.3` / `MAX_EXPANDED_NEIGHBORS=4` 已配置,`_expanded_from_score` 元数据已记录 | -| Phase 4 | 置信度兜底 | **已实施** | `CONFIDENCE_WARN_THRESHOLD=0.15` / `CONFIDENCE_CAUTION_THRESHOLD=0.30` 已配置,`confidence_score` 已通过 SSE 输出 | -| Phase 5 | 上下文排序优化 | **部分实施** | `_build_context_with_budget()` 实现了通用排序逻辑,但列举类查询仍走独立的 `_is_enum_query` 分支 | -| Phase 6 | 引用标注改进 | **已实施** | 已迁移到 `core/agentic_citation.py`,支持动态阈值(短段落 0.55 / 长段落 0.45)和每段最多 2 引用 | - -> **注**:下文各 Phase 中引用的具体函数名、行号为文档撰写时的快照,经多次迭代后已偏移。实际位置请以当前代码为准。 - ---- - -### Phase 0:基线建立 + 评估基础设施 `部分实施` - -**目标**:在改动任何代码之前,先有量化基线和自动化评估手段。 - -**现状**:`scripts/eval_e2e.py` 已创建但尚未运行基线评估。`data/eval_dataset.json` 和 `data/eval_results/baseline.json` 尚未生成。 - -**工作内容**: - -1. 创建 `data/eval_dataset.json`:从现有知识库中挑选 15-20 个测试问题,覆盖以下类型: - - 事实查询("XX标准是多少") - - 列举查询("有哪些禁止情形") - - 对比查询("A和B的区别") - - 流程查询("如何申请XX") - - 跨文档查询(答案涉及多个文件) - - 每个问题记录:`query`、`query_type`、`expected_keywords`(答案应包含的关键信息点)、`relevant_sources`(应命中的文件名) - -2. 创建 `scripts/eval_e2e.py`:端到端评估脚本 - - 启动本地服务(或连接已运行的服务) - - 对每个测试问题调用 `POST /rag` - - 收集回答,用 LLM 评分(相关性 1-5、完整性 1-5、准确性 1-5) - - 同时记录检索层指标(Rerank 分数分布、上下文切片数、上下文总字数) - - 输出 JSON 报告 + 终端汇总 - -3. 运行一次基线评估,保存为 `data/eval_results/baseline.json` - -4. 打 Git tag:`git tag v1.0-rag-baseline` - -**产出文件**: -- `data/eval_dataset.json` -- `scripts/eval_e2e.py` -- `data/eval_results/baseline.json` - -**验收标准**:评估脚本能正常运行,基线报告包含每个问题的评分。 - ---- - -### Phase 1:Rerank 分数传递与上下文过滤 `已实施` - -**目标**:让 Rerank 分数在上下文构建中发挥作用,过滤低分切片。 - -**改动范围**:`api/chat_routes.py` + `config.py`。 - -**实施说明**: -- `config.py` 已新增 `RERANK_CONTEXT_MIN_SCORE = 0.05` -- `_order_text_contexts_for_prompt()` 已接受 `min_score` 参数(当前位于 `api/chat_routes.py` 第 188 行) -- 调用处已传入 `min_score=RERANK_CONTEXT_MIN_SCORE`(当前位于第 1750-1751 行) -- SSE `context_built` 事件已包含 `min_score_filter`、`score_stats`、`confidence_top3` 等调试字段 - -**原始设计**: - -1. 在 `contexts.append` 时已有 score 字段,确认它包含 Rerank 分数(当前 `scores` 来自 `search_result.get('scores')`,是 RRF 融合分数还是 Rerank 分数需要确认) - -2. 在 `_order_text_contexts_for_prompt` 函数中增加分数过滤参数: - ```python - def _order_text_contexts_for_prompt(contexts, query, max_chunks, - min_score=0.0): - # 过滤低于分数阈值的切片 - if min_score > 0: - text_contexts = [c for c in text_contexts - if c.get('score', 0) >= min_score] - ``` - -3. 在 `generate()` 中调用时传入阈值: - ```python - text_contexts = _order_text_contexts_for_prompt( - contexts, message, MAX_CONTEXT_CHUNKS, - min_score=RERANK_CONTEXT_MIN_SCORE # 新增配置项,初始值 0.05 - ) - ``` - -4. 在 `config.py` 中新增配置: - ```python - RERANK_CONTEXT_MIN_SCORE = 0.05 # 上下文最低 Rerank 分数阈值 - ``` - -5. 在 SSE `context_built` 事件中增加分数信息(DEV 模式),方便调试。 - -**风险**:低。`min_score` 初始设 0.05(几乎不过滤),逐步调高观察效果。 - -**验证方法**: -- 运行 `eval_e2e.py`,对比基线 -- 重点关注:是否有问题因为过滤了切片而回答变差 -- 观察 `context_built` 事件中 `chunk_count` 的变化 - -**Git commit**:`feat(rag): pass rerank scores through to context building with min-score filter` - ---- - -### Phase 2:Token 预算控制 `已实施` - -**目标**:用字数/token 预算替代纯计数截断,避免上下文过长稀释 LLM 注意力。 - -**改动范围**:`api/chat_routes.py` + `config.py`。 - -**实施说明**: -- `config.py` 已新增 `CONTEXT_MAX_CHARS = 8000`、`CONTEXT_SOFT_LIMIT = 6000`、`DIRECT_CONTEXT_MAX_CHARS = 2000` -- `api/chat_routes.py` 已新增 `_build_context_with_budget()` 函数(当前第 312 行),按 Rerank 分数降序逐个加入直到预算满 -- 列举类 / 对比类查询走独立分支,保持原始顺序直接拼接(当前第 1754-1758 行) - -**原始设计**: - -1. 在 `config.py` 新增: - ```python - CONTEXT_MAX_CHARS = 8000 # 上下文最大字符数(约 4000 token) - CONTEXT_SOFT_LIMIT = 6000 # 软限制,超过后只保留高分切片 - ``` - -2. 在 `_order_text_contexts_for_prompt` 返回后,构建 `context_text` 时: - ```python - # 按 Rerank 分数降序逐个加入,直到达到 token 预算 - sorted_contexts = sorted(text_contexts, - key=lambda c: c.get('score', 0), - reverse=True) - context_parts = [] - total_chars = 0 - for ctx in sorted_contexts: - doc = ctx.get('doc', '') - if total_chars + len(doc) > CONTEXT_MAX_CHARS: - break - context_parts.append(doc) - total_chars += len(doc) - context_text = "\n\n".join(context_parts) - ``` - -3. 注意:排序后需要保持同一文档切片的连续性。改为先按 (source, section, chunk_index) 分组,再按组内最高分降序排列各组,逐组加入直到预算满。 - -**风险**:中。如果预算设太小,可能丢掉关键信息。初始 `CONTEXT_MAX_CHARS=8000` 比较保守(当前 20 个切片平均约 10000-15000 字)。 - -**验证方法**: -- 对比基线的评分变化 -- 关注列举类查询("有哪些禁止情形")是否因为预算截断而漏条 -- 观察 `context_built.context_length` 分布 - -**Git commit**:`feat(rag): add character-based token budget for context building` - ---- - -### Phase 3:上下文扩展精细化 `已实施` - -**目标**:Rerank 后的上下文扩展只对高置信度切片执行,避免低分切片引入噪声邻居。 - -**改动范围**:`core/engine.py` 的 `_expand_contiguous_chunks`(当前第 1089 行)及其调用处。 - -**实施说明**: -- `config.py` / `engine.py` 已新增 `EXPANSION_SCORE_THRESHOLD = 0.3`、`MAX_EXPANDED_NEIGHBORS = 4` -- 扩展时传入 `min_score=EXPANSION_SCORE_THRESHOLD` 过滤低分切片 -- 邻居切片 metadata 已记录 `_expanded_from_score`(种子分数) -- `MAX_EXPANDED_NEIGHBORS` 限制每个种子的邻居数量 -- 上下文扩展同时在 `chat_routes.py`(第 281 行)和 `engine.py` 中实现,参数由 `CONTEXT_EXPANSION_ENABLED/BEFORE/AFTER/MAX_CHUNKS` 控制 - -**原始设计**(行号为撰写时快照,已偏移): - -1. ~~MMR 前的扩展(第 573 行)保持不变~~ —— 已重构,实际位置见 `core/engine.py` - -2. ~~Rerank 后的扩展(第 615 行)增加条件~~ —— 已实施,`EXPANSION_SCORE_THRESHOLD` 控制 - ```python - # 只对 Rerank 分数 > EXPANSION_SCORE_THRESHOLD 的切片扩展邻居 - EXPANSION_SCORE_THRESHOLD = 0.3 - ``` - -3. 给扩展进来的邻居切片在 metadata 中记录扩展来源: - ```python - n_meta['_expanded_from_score'] = seed_score # 种子切片的 Rerank 分数 - ``` - -4. 在 `_order_text_contexts_for_prompt` 中限制扩展邻居的数量: - ```python - MAX_EXPANDED_NEIGHBORS = 4 # 最多 4 个扩展邻居进入上下文 - ``` - -**风险**:中。如果阈值设太高,一些中等分数的切片不会扩展邻居,可能丢失上下文。初始 0.3 比较宽松。 - -**验证方法**: -- 对比 `retrieval_debug` 事件中第二次 `context_expansion` 的 before/after 数量 -- 检查是否有回答因为缺少邻居切片而变得不连贯 -- 评估分数不应低于 Phase 2 的结果 - -**Git commit**:`feat(rag): restrict post-rerank context expansion to high-confidence chunks` - ---- - -### Phase 4:置信度兜底 `已实施` - -**目标**:当检索质量整体偏低时,在 prompt 中告知 LLM 谨慎回答,减少幻觉。 - -**改动范围**:`api/chat_routes.py`(prompt 层面 + SSE 输出)。 - -**实施说明**: -- `config.py` 已新增 `CONFIDENCE_WARN_THRESHOLD = 0.15`、`CONFIDENCE_CAUTION_THRESHOLD = 0.30` -- `generate()` 内已计算 `_confidence_score`(top-3 平均 Rerank 分数,当前第 1760-1762 行) -- 根据分数在 prompt 中注入不同的置信度提示(当前第 1821-1827 行) -- SSE `finish` 事件已附带 `confidence_score` 字段(当前第 1926 行) - -**原始设计**: - -1. 在上下文构建完成后(`context_text` 已生成),检查 top-3 切片的平均 Rerank 分数: - ```python - top_scores = [ctx.get('score', 0) for ctx in text_contexts[:3]] - avg_top3_score = sum(top_scores) / len(top_scores) if top_scores else 0 - ``` - -2. 根据平均分数在 prompt 中注入不同指令: - ```python - if avg_top3_score < CONFIDENCE_WARN_THRESHOLD: # 比如 0.15 - confidence_note = ( - "【重要提示】参考资料与问题的相关性较低。" - "请仅基于参考资料中明确包含的信息回答," - "如果资料不足以回答问题,请直接说明'知识库中未找到直接相关的信息'。" - ) - elif avg_top3_score < CONFIDENCE_CAUTION_THRESHOLD: # 比如 0.3 - confidence_note = ( - "参考资料的相关性一般,请优先引用资料中的原文,避免推测。" - ) - else: - confidence_note = "" - ``` - -3. 在 `config.py` 新增: - ```python - CONFIDENCE_WARN_THRESHOLD = 0.15 - CONFIDENCE_CAUTION_THRESHOLD = 0.30 - ``` - -4. 在 SSE `finish` 事件中附带 `confidence_score` 字段,前端可用于提示用户。 - -**风险**:低。纯 prompt 层面的改动,不影响检索链路。最坏情况是 LLM 过于保守拒绝回答——可以通过调低阈值解决。 - -**验证方法**: -- 构造几个"知识库中确实没有答案"的问题,检查 LLM 是否正确拒绝 -- 正常问题的评分不应下降 -- 观察 `finish` 事件中的 `confidence_score` 分布 - -**Git commit**:`feat(rag): add confidence-based prompt guidance for low-quality retrieval` - ---- - -### Phase 5:上下文排序优化 `部分实施` - -**目标**:对所有查询类型(不仅是列举类)都做同章节聚合排序,保证同一文件同一章节的切片连续排列。 - -**改动范围**:`api/chat_routes.py` 的 `_order_text_contexts_for_prompt`(当前第 188 行)。 - -**当前状态**: -- `_build_context_with_budget()`(第 312 行)已实现了通用的分数排序 + 预算控制逻辑 -- 但列举类查询(`_is_enum_query`)和对比类查询仍走独立分支(第 1754 行),直接拼接不做预算截断 -- 尚未将"同章节聚合排序"推广为所有查询类型的默认行为 - -**原始设计**: - -1. 将当前只对 `_is_enum_query` 执行的排序逻辑推广为所有查询类型的默认行为 - -2. 排序策略: - - 第一优先级:按 Rerank 分数降序(高分切片先进入上下文) - - 第二优先级:同一 source + section 的切片按 chunk_index 连续排列 - - 具体做法:先按 (source, section) 分组,组内按 chunk_index 排序,组间按组内最高分降序 - -3. 列举类查询保持当前行为不变(作为特例) - -**风险**:低。排序变化不影响切片内容,只影响 LLM 看到的顺序。 - -**验证方法**: -- 对比 `context_built.chunks_used` 的排列顺序 -- 评估分数不应低于 Phase 4 - -**Git commit**:`feat(rag): apply section-aware context ordering for all query types` - ---- - -### Phase 6:引用标注改进 `已实施` - -**目标**:提升引用匹配精度,支持多引用。 - -**改动范围**:已迁移至 `core/agentic_citation.py`(独立模块),原 `api/chat_routes.py` 中也保留了 `_attach_citations`(第 393 行)作为备用。 - -**实施说明**: -- 动态阈值已实现:短段落(< 50 字)使用 overlap 阈值 0.55,长段落 0.45 -- 每段最多匹配 2 个 chunk -- 引用标记格式 `[ref:chunk_id]`,前端 `extractCitations` 已支持多个引用 -- `agentic_citation.py` 中 `_attach_citations` 方法(第 220 行)为 agentic 模式提供独立的引用标注 - -**原始设计**: - -1. 动态阈值:短段落(< 50 字)使用更高的 overlap 阈值(0.55),长段落保持 0.45 - -2. 允许一个段落匹配最多 2 个 chunk(如果两个 chunk 的 overlap 分数都超过阈值且差距小于 0.1) - -3. 引用标记改为 `[ref:chunk_id_1][ref:chunk_id_2]`,前端 `extractCitations` 已支持多个 `[ref:xxx]` - -**风险**:中。多引用可能导致引用列表变长、前端展示变化。 - -**验证方法**: -- 检查引用数量是否合理增加(不应翻倍) -- 引用溯源弹窗的跳转是否仍然正确 -- 评估分数不应下降 - -**Git commit**:`feat(rag): improve citation matching with dynamic thresholds and multi-citation support` - ---- - -### 阶段依赖关系 - -``` -Phase 0 (基线+评估) [部分实施] - ↓ -Phase 1 (Rerank分数传递) [已实施] ← 风险最低,收益最直接 - ↓ -Phase 2 (Token预算) [已实施] ← 依赖 Phase 1 的分数传递 - ↓ -Phase 3 (扩展精细化) [已实施] ← 依赖 Phase 1 的分数信息 - ↓ -Phase 4 (置信度兜底) [已实施] ← 依赖 Phase 1 的分数信息 - ↓ -Phase 5 (上下文排序) [部分实施] ← 独立,可与 Phase 4 互换顺序 - ↓ -Phase 6 (引用标注) [已实施] ← 独立,已迁移至独立模块 -``` - -### 回退策略 - -每个 Phase 完成后执行: -```bash -# 运行评估 -python scripts/eval_e2e.py --output data/eval_results/phase_N.json - -# 对比上一阶段 -# 如果评分下降 > 5%,回退: -git revert HEAD - -# 如果评分持平或提升,打 tag: -git tag v1.0-phase-N -``` - -### 预估时间 - -| 阶段 | 代码改动量 | 预估时间 | -|------|----------|---------| -| Phase 0 | 新建 2 个文件 | 2-3 小时 | -| Phase 1 | 改 2 个文件,约 30 行 | 1 小时 | -| Phase 2 | 改 2 个文件,约 40 行 | 1-2 小时 | -| Phase 3 | 改 1 个文件,约 20 行 | 1 小时 | -| Phase 4 | 改 2 个文件,约 25 行 | 30 分钟 | -| Phase 5 | 改 1 个文件,约 30 行 | 1 小时 | -| Phase 6 | 改 1 个文件,约 40 行 | 1-2 小时 | diff --git a/docs/认证与权限配置指南.md b/docs/认证与权限配置指南.md index 833bc9f..0dbc28e 100644 --- a/docs/认证与权限配置指南.md +++ b/docs/认证与权限配置指南.md @@ -335,7 +335,7 @@ curl -X POST http://localhost:5001/search \ ```bash # 测试出题接口(携带网关注入的 Header) -curl -X POST http://localhost:5001/exam/generate-by-file \ +curl -X POST http://localhost:5001/exam/generate \ -H "Content-Type: application/json" \ -H "X-User-ID: test_user" \ -H "X-User-Role: admin" \ @@ -343,7 +343,7 @@ curl -X POST http://localhost:5001/exam/generate-by-file \ -d '{ "file_path": "public/公司简介.txt", "collection": "public_kb", - "choice_count": 2 + "question_types": {"single_choice": 2, "true_false": 1} }' ``` diff --git a/docs/题目模板.md b/docs/题目模板.md deleted file mode 100644 index d317977..0000000 --- a/docs/题目模板.md +++ /dev/null @@ -1,80 +0,0 @@ -1. 整体结构 (Envelope Pattern)所有题型统一采用以下外壳,通过 question_type 进行区分。JSON{ - "metadata": { - "question_id": "string (UUID)", - "question_type": "single_choice | multiple_choice | true_false | fill_blank | subjective", - "difficulty": 3, - "tags": ["知识点A", "知识点B"], - "score": 10.0, - "version": "1.0" - }, - "source_trace": { - "document_id": "string - 原始文档ID", - "document_name": "string - 文档名称", - "source_context": "string - AI出题参考的原文切片/上下文", - "page_numbers": [14, 15] - }, - "content": { - "stem": "string - 题干内容,支持Markdown格式", - "data": {}, - "answer": null, - "explanation": "string - 详尽的答案解析" - }, - "review_info": { - "status": "pending", - "reviewer_comment": null, - "created_at": "2026-04-17T18:30:00Z" - } -} -2. 各题型 content.data 与 content.answer 定义(1) 单选题 (single_choice)data: 包含选项数组。answer: 对应正确选项的 key。JSON"content": { - "stem": "根据文档,公司成立的年份是?", - "data": { - "options": [ - {"key": "A", "content": "1998年"}, - {"key": "B", "content": "2005年"}, - {"key": "C", "content": "2010年"} - ] - }, - "answer": "B", - "explanation": "在文档第三页明确提到公司于2005年获得营业执照。" -} -(2) 多选题 (multiple_choice)data: 选项数组。answer: 正确选项 key 的数组。JSON"content": { - "stem": "以下属于公司核心价值观的有?", - "data": { - "options": [ - {"key": "A", "content": "创新"}, - {"key": "B", "content": "诚信"}, - {"key": "C", "content": "加班"} - ] - }, - "answer": ["A", "B"], - "explanation": "手册第一章第一节列出了‘创新’与‘诚信’为唯二核心价值观。" -} -(3) 判断题 (true_false)data: 可留空或自定义文字(如:正确/错误,对/错)。answer: boolean (true 为正确,false 为错误)。JSON"content": { - "stem": "员工可以在办公区吸烟。", - "data": null, - "answer": false, - "explanation": "《行政管理规范》第五条严禁在办公区吸烟。" -} -(4) 填空题 (fill_blank)data: 槽位定义。answer: 数组,按顺序存放每个空的标准答案列表(支持同义词)。JSON"content": { - "stem": "RAG的全称是___,其核心在于利用___增强生成效果。", - "data": { "blank_count": 2 }, - "answer": [ - ["检索增强生成", "Retrieval Augmented Generation"], - ["外部知识库", "自有文档", "Context"] - ], - "explanation": "RAG即检索增强生成,通过引入外部知识库信息提升回答准确度。" -} -(5) 主观题 (subjective)data: 包含评分维度和关键词。answer: string (参考范文)。JSON"content": { - "stem": "请简述RAG架构中‘切片策略’对检索质量的影响。", - "data": { - "scoring_points": [ - {"point": "提到了语义完整性", "weight": 0.4}, - {"point": "提到了切片过大导致噪声", "weight": 0.3}, - {"point": "提到了切片过小丢失上下文", "weight": 0.3} - ], - "keywords": ["语义窗口", "Top-K", "重叠度"] - }, - "answer": "参考范文:合理的切片策略能保持语义完整,通过设置Overlap防止信息断层...", - "explanation": "此题考查对RAG性能优化的深度理解。" -} -3. 后端约束说明 (Tips for Backend)数据持久化:建议将 content 整体以 JSONB (PostgreSQL) 或类似的格式存储,方便扩展未来可能增加的题型(如连线题、排序题)。分值校验:后端应校验 metadata.score 是否为正数;对于多选题,可增加逻辑判断 answer 数组长度必须 $\ge 2$。RAG 闭环:source_trace 字段是 RAG 项目的核心,建议后端在审核界面提供一个“点击查看原文”的按钮,直接展示 source_context 的内容。幂等性:建议由前端或 AI 服务生成 question_id (UUID),防止网络波动导致的重复入库。 \ No newline at end of file diff --git a/docs/风险边界问题修复注意事项.md b/docs/风险边界问题修复注意事项.md index 9a310da..381530f 100644 --- a/docs/风险边界问题修复注意事项.md +++ b/docs/风险边界问题修复注意事项.md @@ -1,5 +1,7 @@ ## 风险边界问题修复注意事项 +> **状态**:本文档中列出的所有场景均已修复并部署(2026-06-04)。保留本文档供参考,避免回退。 + ### 一、修复了哪些会出问题的情况 以下场景之前会报错或数据异常,现在已修复,不会再出问题: