# RAG API curl 测试手册 > 基于 `后端对接规范.md`,所有 curl 命令均在生产模式 (`APP_ENV=prod`) 下验证通过。 > > 测试日期:2026-06-04 | 生产服务器:`47.116.16.222` | 服务地址:`http://127.0.0.1:5001` > > 当前部署模型:`qwen-plus`(DashScope)| 嵌入模型:`bge-base-zh-v1.5`(本地 CPU)| Rerank:`qwen3-rerank`(DashScope 云端 API) ## 目录 - [1. 基础信息](#1-基础信息) - [2. 健康检查](#2-健康检查) - [3. 问答接口](#3-问答接口) - [4. 检索接口](#4-检索接口) - [5. 向量库管理](#5-向量库管理) - [6. 文档管理](#6-文档管理) - [7. 切片管理](#7-切片管理) - [8. 同步服务](#8-同步服务) - [9. 反馈系统](#9-反馈系统) - [10. FAQ 管理](#10-faq-管理) - [11. 出题系统](#11-出题系统) - [12. 图片服务](#12-图片服务) - [13. 报告服务](#13-报告服务) - [14. 知识库路由](#14-知识库路由) - [15. 异步任务查询](#15-异步任务查询) - [附录. 已知问题](#附录-已知问题) --- ## 1. 基础信息 ### 认证方式 生产模式 (`APP_ENV=prod`) 下无需认证 Header,直接放行。用户信息使用默认值: ```json {"user_id": "backend-caller", "username": "后端调用", "role": "user", "department": ""} ``` ### 中文编码注意 Windows bash 中 curl 传递中文 JSON 需使用文件方式: ```bash # 方式一:使用临时文件(推荐) echo '{"query":"中文内容"}' > /tmp/test.json curl -s -X POST http://localhost:5001/search -H "Content-Type: application/json" -d @/tmp/test.json # 方式二:英文可直接传递 curl -s -X POST http://localhost:5001/search -H "Content-Type: application/json" -d '{"query":"hello"}' ``` ### 通用响应格式 所有接口返回 JSON,成功时 HTTP 200,错误时返回对应 HTTP 状态码。 --- ## 2. 健康检查 ### GET /health 检查服务运行状态。 ```bash curl -s http://localhost:5001/health ``` **响应示例**: ```json { "bm25_index": "动态按需加载", "knowledge_base": "多向量库模式 (按集合提供服务)", "mode": "Agentic RAG", "status": "ok" } ``` **验证结果**:✅ 通过 --- ## 3. 问答接口 ### POST /rag 知识库问答(SSE 流式返回)。 ```bash echo '{"message":"三峡工程","chat_history":[]}' > /tmp/rag.json curl -s -X POST http://localhost:5001/rag \ -H "Content-Type: application/json" \ -d @/tmp/rag.json ``` **请求体**: | 字段 | 类型 | 必需 | 说明 | |------|------|------|------| | `message` | string | ✅ | 用户消息 | | `chat_history` | array | ✅(生产模式) | 对话历史 | | `collections` | string[] | ❌ | 知识库列表,默认 `["public_kb"]` | | `session_id` | string | ❌ | 会话 ID | **chat_history 格式**: ```json [ {"role": "user", "content": "之前的问题"}, {"role": "assistant", "content": "之前的回答"} ] ``` **SSE 事件序列**: ``` data: {"type": "start", "message": "正在检索知识库..."} data: {"type": "sources", "sources": [...]} data: {"type": "chunk", "content": "每个token"} data: {"type": "finish", "answer": "完整回答[ref:chunk_id]", "sources": [...], "citations": [...]} ``` **finish 事件中 citations 结构**(引用溯源,用于前端跳转定位): ```json { "citations": [ { "chunk_id": "3.docx_33", "chunk_index": 33, "source": "3.docx", "collection": "public_kb", "doc_type": "word", "section": "第五篇 投入管理 > 一、零售终端建设投入标准", "preview": "内容摘要...", "content": "切片内容(截断 300 字)", "chunk_type": "text", "page": null, "page_end": null } ] } ``` | citations 字段 | 说明 | |------|------| | `chunk_id` | 切片唯一标识,格式 `{文件名}_{序号}` | | `chunk_index` | 全局切片序号(从 chunk_id 提取),用于文档预览跳转 | | `source` | 来源文件名 | | `collection` | 所属向量库名称,用于定位文档所在知识库 | | `doc_type` | 文档类型:`pdf`、`word`、`excel` | | `section` | 章节路径(已清洗,过滤掉正文内容) | | `page` / `page_end` | 页码(仅 PDF) | | `bbox` / `bbox_mode` | 坐标定位(仅 PDF) | | `section_chunk_id` | 章节内段落序号(仅 Word) | **带 collections 参数**: ```bash echo '{"message":"三峡工程","chat_history":[],"collections":["public_kb"]}' > /tmp/rag.json curl -s -X POST http://localhost:5001/rag \ -H "Content-Type: application/json" \ -d @/tmp/rag.json ``` > **⚠️ 重要**:指定知识库必须使用 `collections` 数组参数(如 `["dept_tech"]`),而非 `collection` 单数参数。 > 使用单数 `collection` 参数会被忽略,导致默认检索 `public_kb`。 **多向量库同时查询**: ```bash # 同时检索多个知识库 echo '{"message":"信息安全","collections":["dept_tech","public_kb"],"chat_history":[]}' > /tmp/rag.json curl -s -X POST http://localhost:5001/rag \ -H "Content-Type: application/json" \ -d @/tmp/rag.json ``` **验证结果**:✅ 通过 --- ### POST /chat 普通聊天模式(非知识库)。 ```bash echo '{"message":"你好","chat_history":[]}' > /tmp/chat.json curl -s -X POST http://localhost:5001/chat \ -H "Content-Type: application/json" \ -d @/tmp/chat.json ``` **请求体**: | 字段 | 类型 | 必需 | 说明 | |------|------|------|------| | `message` | string | ✅ | 用户消息 | | `chat_history` | array | ❌ | 对话历史 | **响应示例**: ```json { "answer": "你好!很高兴见到你~", "mode": "chat", "sources": [], "web_searched": false } ``` **验证结果**:✅ 通过 --- ## 4. 检索接口 ### POST /search 混合检索(向量 + BM25),供 Dify 工作流调用。 ```bash echo '{"query":"三峡工程","top_k":3}' > /tmp/search.json curl -s -X POST http://localhost:5001/search \ -H "Content-Type: application/json" \ -d @/tmp/search.json ``` **请求体**: | 字段 | 类型 | 必需 | 说明 | |------|------|------|------| | `query` | string | ✅ | 查询文本 | | `top_k` | int | ❌ | 返回数量,默认 5 | | `collections` | string[] | ❌ | 知识库列表,默认 `["public_kb"]` | **响应示例**: ```json { "contexts": ["综述\n三峡工程是国之重器..."], "metadatas": [ { "_collection": "public_kb", "chunk_id": "三峡公报_1-15页.pdf_text_8", "chunk_type": "text", "collection": "public_kb", "doc_type": "pdf", "page": 5, "page_end": 6, "section": "三峡工程公报 > 综述", "source": "三峡公报_1-15页.pdf", "status": "active", "version": "v1" } ], "scores": [0.9975] } ``` **scores 说明**:相似度分数,范围 0-1,越高越相关(已从距离转换)。 **验证结果**:✅ 通过 --- ## 5. 向量库管理 ### GET /collections 获取向量库列表。 ```bash curl -s http://localhost:5001/collections ``` **响应示例**: ```json { "collections": [ { "created_at": "2026-06-02T21:35:43.839864", "department": "", "description": "所有人可访问的公开文档", "display_name": "公开知识库", "document_count": 824, "name": "public_kb" }, { "created_at": "2026-06-04T06:15:52.556940", "department": "", "description": "", "display_name": "dept_1_kb", "document_count": 800, "name": "dept_1_kb" } ], "total": 9 } ``` **验证结果**:✅ 通过 --- ### POST /collections 创建新向量库。 ```bash echo '{"name":"test_kb","display_name":"测试库","department":"测试部","description":"测试描述"}' > /tmp/create.json curl -s -X POST http://localhost:5001/collections \ -H "Content-Type: application/json" \ -d @/tmp/create.json ``` **请求体**: | 字段 | 类型 | 必需 | 说明 | |------|------|------|------| | `name` | string | ✅ | 向量库名称(字母、数字、下划线、连字符) | | `display_name` | string | ❌ | 显示名称 | | `department` | string | ❌ | 所属部门 | | `description` | string | ❌ | 描述 | **响应示例**: ```json { "message": "向量库 'test_kb' 创建成功", "name": "test_kb", "success": true } ``` **验证结果**:✅ 通过 --- ### PUT /collections/\ 修改向量库信息。 ```bash echo '{"display_name":"新名称","description":"新描述"}' > /tmp/update.json curl -s -X PUT http://localhost:5001/collections/test_kb \ -H "Content-Type: application/json" \ -d @/tmp/update.json ``` **请求体**: | 字段 | 类型 | 必需 | 说明 | |------|------|------|------| | `display_name` | string | ❌ | 显示名称 | | `description` | string | ❌ | 描述 | **验证结果**:✅ 通过 --- ### DELETE /collections/\ 删除向量库。 ```bash curl -s -X DELETE http://localhost:5001/collections/test_kb ``` **查询参数**: | 参数 | 类型 | 必需 | 说明 | |------|------|------|------| | `delete_documents` | boolean | ❌ | 是否删除文档源文件,默认 false | **带参数示例**: ```bash curl -s -X DELETE "http://localhost:5001/collections/test_kb?delete_documents=true" ``` **验证结果**:✅ 通过 --- ### GET /collections/\/documents 获取向量库内的文档列表。 ```bash curl -s "http://localhost:5001/collections/public_kb/documents" ``` **响应示例**: ```json { "collection": "public_kb", "documents": [ { "chunks": 105, "source": "1.docx" }, { "chunks": 39, "source": "三峡公报_1-15页.pdf" } ], "total": 9 } ``` **验证结果**:✅ 通过 --- ### GET /collections/\/chunks 获取向量库切片列表。 ```bash curl -s "http://localhost:5001/collections/public_kb/chunks?limit=2&offset=0" ``` **查询参数**: | 参数 | 类型 | 必需 | 说明 | |------|------|------|------| | `document_id` | string | ❌ | 过滤指定文档的切片 | | `limit` | int | ❌ | 返回数量,默认 100 | | `offset` | int | ❌ | 偏移量,默认 0 | **响应示例**: ```json { "collection": "public_kb", "chunks": [ { "id": "三峡公报_1-15页.pdf_text_0", "document": "三峡公报_1-15页.pdf", "content": "切片内容...", "metadata": { "chunk_id": "三峡公报_1-15页.pdf_text_0", "chunk_type": "text", "collection": "public_kb", "page": 1, "source": "三峡公报_1-15页.pdf", "status": "active", "version": "v1" }, "score": null } ], "total": 150 } ``` **验证结果**:✅ 通过 --- ### GET /collections/\/documents/\/versions 获取文档版本历史。 ```bash # URL 编码文件名中的特殊字符 curl -s "http://localhost:5001/collections/public_kb/documents/%E4%B8%89%E5%B3%A1%E5%85%AC%E6%8A%A5_1-15%E9%A1%B5.pdf/versions?limit=5" ``` **查询参数**: | 参数 | 类型 | 必需 | 说明 | |------|------|------|------| | `limit` | int | ❌ | 返回数量,默认 10 | **响应示例**: ```json { "success": true, "document_id": "三峡公报_1-15页.pdf", "collection": "public_kb", "versions": [ { "version": "v1", "status": "active", "effective_date": "2026-04-14T21:03:06", "deprecated_date": null, "chunk_count": 120 } ], "total": 1 } ``` **验证结果**:✅ 通过 --- ### POST /collections/\/update-image-descriptions 更新图片切片描述(重新生成)。 ```bash curl -s -X POST "http://localhost:5001/collections/public_kb/update-image-descriptions" ``` **响应示例**: ```json { "success": true, "message": "图片描述更新完成", "updated": 5 } ``` **验证结果**:✅ 通过 --- ## 6. 文档管理 ### GET /documents/list 获取文档列表。 ```bash curl -s "http://localhost:5001/documents/list?collection=public_kb" ``` **查询参数**: | 参数 | 类型 | 必需 | 说明 | |------|------|------|------| | `collection` | string | ❌ | 知识库名称(也可用 `kb_name`),默认返回全部 | > **注意**:该接口不支持分页,返回指定知识库的全部文档列表。 **响应示例**: ```json { "documents": [ { "collection": "public_kb", "filename": "三峡公报_1-15页.pdf", "last_modified": "2026-04-14T21:03:06", "path": "public_kb/三峡公报_1-15页.pdf", "size": 2773520 } ], "total": 8 } ``` **验证结果**:✅ 通过 --- ### GET /documents/\/status 获取文档处理状态。 ```bash # URL 编码路径中的斜杠 curl -s "http://localhost:5001/documents/public_kb%2Ftest.txt/status" ``` **响应示例**: ```json { "success": true, "status": "active", "chunk_count": 105, "last_processed": null } ``` **验证结果**:✅ 通过 --- ### POST /documents/upload 上传单个文件。 ```bash echo "测试文档内容" > /tmp/test.txt curl -s -X POST http://localhost:5001/documents/upload \ -F "file=@/tmp/test.txt" \ -F "collection=public_kb" ``` **表单字段**: | 字段 | 类型 | 必需 | 说明 | |------|------|------|------| | `file` | file | ✅ | 上传文件 | | `collection` | string | ✅ | 目标知识库(也可用 `kb_name`) | **响应示例**: ```json { "data": { "file": { "collection": "public_kb", "filename": "test.txt", "path": "public_kb/test.txt", "size": 18, "replaced": false }, "sync_status": "已保存,向量化任务已启动", "task_id": "a1b2c3d4e5f6" }, "message": "文件上传成功,已保存,向量化任务已启动", "status": "success", "status_code": 2002, "success": true } ``` > **异步说明**:文件保存为同步操作,向量化在后台线程异步执行。响应中的 `task_id` 可用于轮询向量化进度(`GET /tasks/`)。 > > **同名文件处理**:上传同名文件时,旧版本的切片会被自动清理后覆盖(`replaced: true`),不会生成时间戳后缀文件。 **验证结果**:✅ 通过 --- ### POST /documents/batch-upload 批量上传文件。 ```bash echo "文件1内容" > /tmp/file1.txt echo "文件2内容" > /tmp/file2.txt curl -s -X POST http://localhost:5001/documents/batch-upload \ -F "files=@/tmp/file1.txt" \ -F "files=@/tmp/file2.txt" \ -F "collection=public_kb" ``` **响应示例**: ```json { "data": { "results": [ {"filename": "file1.txt", "path": "public_kb/file1.txt", "status": "success", "replaced": false}, {"filename": "file2.txt", "path": "public_kb/file2.txt", "status": "success", "replaced": false} ], "success_count": 2, "total": 2, "task_id": "b2c3d4e5f6a1" }, "message": "批量上传完成,成功 2/2 个文件", "status": "success", "status_code": 2003, "success": true } ``` > **异步说明**:批量上传成功后自动触发后台向量化任务。`task_id` 可用于轮询向量化进度(`GET /tasks/`)。若无成功上传的文件则不返回 `task_id`。 > > **同名文件处理**:与单文件上传相同,批量上传中遇到同名文件也会自动覆盖旧版本(`replaced: true`)。 **验证结果**:✅ 通过 --- ### PUT /documents/\ 更新/替换文档。 ```bash echo "更新后的文档内容" > /tmp/updated.txt curl -s -X PUT "http://localhost:5001/documents/public_kb%2Ftest.txt" \ -F "file=@/tmp/updated.txt" ``` **表单字段**: | 字段 | 类型 | 必需 | 说明 | |------|------|------|------| | `file` | file | ✅ | 替换的文件 | **响应示例**: ```json { "success": true, "message": "文件已更新" } ``` **验证结果**:✅ 通过 --- ### DELETE /documents/\ 删除文档。 ```bash # URL 编码路径中的斜杠 curl -s -X DELETE "http://localhost:5001/documents/public_kb%2Ftest.txt" ``` **响应示例**: ```json { "message": "文档已删除", "success": true } ``` **验证结果**:✅ 通过 --- ### POST /collections/\/documents/\/deprecate 废弃文档版本。 ```bash curl -s -X POST "http://localhost:5001/collections/public_kb/documents/test.txt/deprecate" ``` **验证结果**:✅ 通过(接口存在) --- ### POST /collections/\/documents/\/restore 恢复文档版本。 ```bash curl -s -X POST "http://localhost:5001/collections/public_kb/documents/test.txt/restore" ``` **验证结果**:✅ 通过(接口存在) --- ### 🛠️ DEV GET /documents/\/preview > **开发环境自用接口,后端组无需调用。** > 仅供 dev-ui 内部前端实现引用溯源跳转使用。后端组可根据 `/rag` 返回的 `citations` 中的 `chunk_index`、`page`、`bbox` 等字段自行实现文档定位。 文档预览(支持按切片序号跳转),用于引用溯源点击后定位到文档中的具体位置。 ```bash # 跳转到指定切片(前后各取 2 个上下文切片) curl -s "http://localhost:5001/documents/public_kb%2F1.docx/preview?chunk_index=33&context=2" # 不指定 chunk_index,返回前 5 个切片作为概览 curl -s "http://localhost:5001/documents/public_kb%2F1.docx/preview" ``` **查询参数**: | 参数 | 类型 | 必需 | 说明 | |------|------|------|------| | `chunk_index` | int | ✅ | 目标切片序号(来自 citations 的 `chunk_index` 字段) | | `context` | int | ✅ | 上下文切片数,默认 2(前后各取 2 个) | **响应示例**: ```json { "success": true, "collection": "public_kb", "source": "1.docx", "total_chunks": 105, "target_index": 33, "chunks": [ {"id": "1.docx_31", "content": "...", "metadata": {...}, "is_target": false}, {"id": "1.docx_32", "content": "...", "metadata": {...}, "is_target": false}, {"id": "1.docx_33", "content": "...", "metadata": {...}, "is_target": true}, {"id": "1.docx_34", "content": "...", "metadata": {...}, "is_target": false}, {"id": "1.docx_35", "content": "...", "metadata": {...}, "is_target": false} ] } ``` > **说明**:`is_target: true` 的切片即为引用定位目标,前端可高亮显示并滚动到该位置。 --- ## 7. 切片管理 ### GET /documents/\/chunks 获取文档切片列表。 ```bash # URL 编码路径 curl -s "http://localhost:5001/documents/dept_hr%2F%E4%BA%BA%E5%91%98%E5%90%8D%E5%86%8C.txt/chunks" ``` > **注意**:该接口不支持分页或过滤参数,返回指定文档的全部切片。 **响应示例**: ```json { "chunks": [ { "document": "# 智启科技有限公司人员名册\n\n**更新日期**:2024年6月...", "id": "人员名册.txt_text_0", "metadata": { "chunk_id": "人员名册.txt_text_0", "chunk_index": 0, "chunk_type": "text", "collection": "dept_hr", "doc_type": "other", "page": 1, "page_end": 1, "preview": "# 智启科技有限公司人员名册...", "section": "人员名册", "source": "人员名册.txt", "has_table": false, "status": "active", "version": "v1" }, "status": "active", "version": "v1" } ], "collection": "dept_hr", "document_id": "dept_hr/人员名册.txt", "success": true, "total": 4 } ``` **切片字段说明**: | 字段 | 说明 | |------|------| | `id` / `chunk_id` | 切片唯一标识,格式:`{文件名}_{类型}_{序号}` | | `document` | **切片完整内容**(重要:用于增删改查) | | `chunk_type` | 切片类型:`text`(文本)、`table`(表格)、`image`(图片) | | `source` | 来源文件名 | | `page` / `page_end` | 起止页码 | | `section` | 所属章节路径 | | `preview` | 内容预览(截断版,用于列表展示) | | `has_table` | 是否包含表格 | **验证结果**:✅ 通过 --- ### POST /chunks 新增切片。 ```bash echo '{"collection":"public_kb","content":"测试切片内容","metadata":{"section":"测试章节"}}' > /tmp/chunk.json curl -s -X POST http://localhost:5001/chunks \ -H "Content-Type: application/json" \ -d @/tmp/chunk.json ``` **请求体**: | 字段 | 类型 | 必需 | 说明 | |------|------|------|------| | `collection` | string | ✅ | 向量库名称 | | `content` | string | ✅ | 切片内容 | | `metadata` | object | ❌ | 元数据 | **验证结果**:✅ 通过 --- ### PUT /chunks/\ 修改切片。 ```bash echo '{"collection":"dept_hr","content":"更新后的内容"}' > /tmp/update_chunk.json curl -s -X PUT "http://localhost:5001/chunks/人员名册.txt_text_0" \ -H "Content-Type: application/json" \ -d @/tmp/update_chunk.json ``` **请求体**: | 字段 | 类型 | 必需 | 说明 | |------|------|------|------| | `collection` | string | ✅ | 向量库名称 | | `content` | string | ❌ | 新内容 | | `metadata` | object | ❌ | 新元数据 | **验证结果**:✅ 通过 --- ### DELETE /chunks/\ 删除切片。 ```bash curl -s -X DELETE "http://localhost:5001/chunks/人员名册.txt_text_0?collection=dept_hr" ``` **查询参数**: | 参数 | 类型 | 必需 | 说明 | |------|------|------|------| | `collection` | string | ✅ | 向量库名称 | **验证结果**:✅ 通过 --- ## 8. 同步服务 ### POST /sync 触发文档同步(异步任务)。 ```bash curl -s -X POST http://localhost:5001/sync \ -H "Content-Type: application/json" ``` **响应示例**: ```json { "data": { "task_id": "c3d4e5f6a1b2", "message": "同步任务已启动,通过 GET /tasks/c3d4e5f6a1b2 查询进度" }, "message": "同步任务已启动", "status": "success", "status_code": 2010, "success": true } ``` > **⚠️ 异步变更**:此接口已从同步改为异步。不再直接返回同步结果,而是返回 `task_id`。后端需通过 `GET /tasks/` 轮询任务状态,直到 `status` 为 `completed` 或 `failed`。 > > **冲突检测**:如果已有同步任务正在运行,返回 HTTP 409: > ```json > {"error": "TASK_RUNNING", "message": "同步任务正在执行中 (task_id: xxx),请等待完成"} > ``` **验证结果**:✅ 通过 --- ### GET /sync/status 获取同步状态。 ```bash curl -s http://localhost:5001/sync/status ``` **响应示例**: ```json { "documents_tracked": 0, "enabled": true, "last_sync": null, "monitoring": false } ``` **验证结果**:✅ 通过 --- ### GET /sync/history 获取同步历史。 ```bash curl -s http://localhost:5001/sync/history ``` **响应示例**: ```json { "history": [] } ``` **验证结果**:✅ 通过 --- ### GET /sync/changes 获取待同步变更。 ```bash curl -s http://localhost:5001/sync/changes ``` **响应示例**: ```json { "changes": [] } ``` **验证结果**:✅ 通过 --- ### POST /sync/start 启动文件监控。 ```bash curl -s -X POST http://localhost:5001/sync/start ``` **响应示例**: ```json { "message": "文件监控已启动", "status": "success", "status_code": 2010 } ``` **验证结果**:✅ 通过 --- ### POST /sync/stop 停止文件监控。 ```bash curl -s -X POST http://localhost:5001/sync/stop ``` **响应示例**: ```json { "message": "文件监控已停止", "status": "success", "status_code": 2010 } ``` **验证结果**:✅ 通过 --- ## 9. 反馈系统 ### POST /feedback 提交反馈。 ```bash echo '{"session_id":"test-session","query":"测试问题","answer":"测试回答","rating":1,"reason":"测试原因"}' > /tmp/feedback.json curl -s -X POST http://localhost:5001/feedback \ -H "Content-Type: application/json" \ -d @/tmp/feedback.json ``` **请求体**: | 字段 | 类型 | 必需 | 说明 | |------|------|------|------| | `session_id` | string | ✅ | 会话 ID | | `query` | string | ✅ | 用户问题 | | `answer` | string | ❌ | 系统回答(可为空) | | `rating` | int | ✅ | 评分(1=赞,-1=踩) | | `reason` | string | ❌ | 原因 | **响应示例**: ```json { "faq_suggested": true, "feedback_id": 7, "success": true, "suggestion_id": 6 } ``` **验证结果**:✅ 通过 --- ### GET /feedback/list 获取反馈列表。 ```bash curl -s "http://localhost:5001/feedback/list?limit=5" ``` **查询参数**: | 参数 | 类型 | 必需 | 说明 | |------|------|------|------| | `limit` | int | ❌ | 返回数量,默认 100 | | `rating` | int | ❌ | 按评分过滤(1=赞,-1=踩) | | `user_id` | string | ❌ | 按用户 ID 过滤 | | `start_date` | string | ❌ | 起始日期 | | `end_date` | string | ❌ | 结束日期 | > **注意**:该接口不支持 `page`/`page_size` 分页,使用 `limit` 控制返回数量。 **响应示例**: ```json { "feedbacks": [ { "answer": "测试回答", "created_at": "2026-05-04T01:45:35", "id": 7, "query": "测试问题", "rating": 1, "reason": "测试原因", "session_id": "test-session", "sources": [], "user_id": "" } ], "success": true, "total": 7 } ``` **验证结果**:✅ 通过 --- ### GET /feedback/stats 获取反馈统计。 ```bash curl -s http://localhost:5001/feedback/stats ``` **响应示例**: ```json { "stats": { "avg_rating": 1.0, "negative_count": 0, "positive_count": 8, "satisfaction_rate": 100.0, "total_feedback": 8 }, "success": true } ``` **验证结果**:✅ 通过 --- ### GET /feedback/bad-cases 获取差评案例列表。 ```bash curl -s http://localhost:5001/feedback/bad-cases ``` **响应示例**: ```json { "bad_cases": [], "blacklisted_sources": [], "success": true, "suggestions": [ "补充到知识库(针对知识盲区)", "添加到 Query Rewrite 规则(针对表达歧义)" ] } ``` **验证结果**:✅ 通过 --- ### GET /feedback/blacklist 获取切片黑名单。 ```bash curl -s http://localhost:5001/feedback/blacklist ``` **响应示例**: ```json { "blacklist": [], "count": 0, "success": true, "usage": "在检索时过滤这些来源以提升回答质量" } ``` **验证结果**:✅ 通过 --- ## 10. FAQ 管理 ### GET /faq 获取 FAQ 列表。 ```bash curl -s "http://localhost:5001/faq?limit=5" ``` **查询参数**: | 参数 | 类型 | 必需 | 说明 | |------|------|------|------| | `status` | string | ❌ | 按状态过滤(如 `approved`、`draft`) | | `limit` | int | ❌ | 返回数量,默认 50 | **响应示例**: ```json { "faqs": [ { "answer": "根据参考资料...", "avg_rating": 1.0, "created_at": "2026-04-19T11:27:15", "frequency": 1, "id": 1, "question": "C类吸烟区域", "source_documents": null, "status": "approved", "updated_at": "2026-04-19T11:27:15" } ], "success": true, "total": 3 } ``` **验证结果**:✅ 通过 --- ### POST /faq 创建新 FAQ。 ```bash curl -s -X POST http://localhost:5001/faq \ -H "Content-Type: application/json" \ -d "{\"question\":\"FAQ question\",\"answer\":\"FAQ answer\"}" ``` **请求体**: | 字段 | 类型 | 必需 | 说明 | |------|------|------|------| | `question` | string | ✅ | 问题内容 | | `answer` | string | ✅ | 答案内容 | **响应示例**: ```json { "faq_id": 7, "message": "FAQ已创建,请通过 /faq//approve 接口确认后生效", "status": "draft", "success": true } ``` **验证结果**:✅ 通过 --- ### PUT /faq/\ 修改 FAQ。 ```bash curl -s -X PUT "http://localhost:5001/faq/7" \ -H "Content-Type: application/json" \ -d "{\"question\":\"updated question\",\"answer\":\"updated answer\"}" ``` **请求体**: | 字段 | 类型 | 必需 | 说明 | |------|------|------|------| | `question` | string | ❌ | 新问题内容 | | `answer` | string | ❌ | 新答案内容 | **响应示例**: ```json { "message": "FAQ更新成功", "success": true, "sync_status": "skipped" } ``` **验证结果**:✅ 通过 --- ### DELETE /faq/\ 删除 FAQ。 ```bash curl -s -X DELETE "http://localhost:5001/faq/7" ``` **响应示例**: ```json { "message": "FAQ删除成功", "success": true } ``` **验证结果**:✅ 通过 --- ### GET /faq/suggestions 获取 FAQ 建议列表。 ```bash curl -s "http://localhost:5001/faq/suggestions?status=pending&limit=5" ``` **查询参数**: | 参数 | 类型 | 必需 | 说明 | |------|------|------|------| | `status` | string | ❌ | 按状态过滤,默认 `pending` | | `limit` | int | ❌ | 返回数量,默认 50 | **响应示例**: ```json { "success": true, "suggestions": [ { "answer": "", "avg_rating": 1.0, "created_at": "2026-04-29T20:49:38", "frequency": 2, "id": 5, "query": "test", "status": "pending" } ], "total": 3 } ``` **验证结果**:✅ 通过 --- ### POST /faq/suggestions/\/approve 批准 FAQ 建议(需 admin 角色)。 ```bash curl -s -X POST "http://localhost:5001/faq/suggestions/6/approve" \ -H "Content-Type: application/json" \ -d '{}' ``` **请求体**(可选,用于修改答案): ```json {"answer": "管理员修改后的答案"} ``` **注意**:必须传递请求体(至少空对象 `{}`),否则返回 400 错误。 **验证结果**:✅ 通过 --- ### POST /faq/suggestions/\/reject 拒绝 FAQ 建议(需 admin 角色)。 ```bash curl -s -X POST "http://localhost:5001/faq/suggestions/6/reject" \ -H "Content-Type: application/json" \ -d '{}' ``` **验证结果**:✅ 通过 --- ## 11. 出题系统 ### 出题架构说明(v2) v2 重构后采用 **4 阶段出题架构**,确保题目质量和多样性: | 阶段 | 说明 | 改进点 | |------|------|--------| | Phase 1 | 文档结构分析 | 按章节分组,统计内容量 | | Phase 2 | 知识点规划 | 全局唯一,避免跨章节重复 | | Phase 3 | 精准出题 | 每个知识点单独检索,确保上下文充足 | | Phase 4 | 质量校验 | 去重 + 数量校正,零重复率 | **支持的题型**: | 题型 key | 中文名 | answer 格式 | |----------|--------|-------------| | `single_choice` | 单选题 | 单个选项字母(如 "B") | | `multiple_choice` | 多选题 | 选项字母列表(如 ["A","B"]) | | `true_false` | 判断题 | 布尔值或字符串(如 true/"true") | | `fill_blank` | 填空题 | 嵌套列表(如 [["答案1"],["答案2"]]) | | `subjective` | 简答题 | 参考答案字符串 | --- ### GET /exam/health 出题服务健康检查。 ```bash curl -s http://localhost:5001/exam/health ``` **响应示例**: ```json { "service": "exam-api", "status": "ok", "version": "2.0" } ``` **验证结果**:✅ 通过 --- ### POST /exam/generate 生成考题(指定题型和数量)。 **特点**: - 需要手动指定 `question_types`(每种题型的数量) - 适合有明确需求的场景(如"出 5 道单选题") **入参校验**: - `question_types` 中每个题型必须属于: `single_choice`, `multiple_choice`, `true_false`, `fill_blank`, `subjective` - 每种题型数量必须为非负整数 - `difficulty` 必须为 1-5 的整数 - **总题数上限 20 道**(所有题型数量之和不能超过 20) - 校验失败返回 HTTP 400,error_code 为 `INVALID_PARAMS` ```bash curl -s -X POST http://localhost:5001/exam/generate \ -H "Content-Type: application/json" \ -d '{"file_path":"public_kb/1.docx","collection":"public_kb","question_types":{"single_choice":5,"true_false":3,"fill_blank":2}}' ``` **请求体**: | 字段 | 类型 | 必需 | 说明 | |------|------|------|------| | `file_path` | string | ✅ | 文档路径(格式:`collection/filename`) | | `collection` | string 或 string[] | ✅ | 知识库名称,支持数组(按优先级排序) | | `question_types` | object | ✅ | 题型数量配置 | | `difficulty` | int | ❌ | 难度等级(1-5),默认 3 | | `options` | object | ❌ | 附加选项(如 `max_source_chunks`) | | `request_id` | string | ❌ | 请求 ID(幂等性) | | `exclude_stems` | string[] | ❌ | 已有题目题干列表(跨调用去重,最多 100 条) | > **注意**:生产模式(`DEV_MODE=false`)下无需传 `Authorization` header,直接放行。开发模式下可传 `Authorization: Bearer mock-token-admin` 模拟管理员身份。 **question_types 配置示例**: ```json { "single_choice": 5, "multiple_choice": 2, "true_false": 3, "fill_blank": 2, "subjective": 1 } ``` **响应示例**: ```json { "data": { "task_id": "d4e5f6a1b2c3", "message": "出题任务已启动 (10题),通过 GET /tasks/d4e5f6a1b2c3 查询结果" }, "message": "出题任务已启动", "status": "success", "status_code": 2020, "success": true } ``` > **⚠️ 异步变更**:此接口已从同步改为异步。响应仅返回 `task_id`,后端需通过 `GET /tasks/` 轮询任务状态。任务完成后,`result` 字段包含完整出题结果(格式见下方说明)。 **轮询结果(GET /tasks/\ 完成后的 result 字段)**: ```json { "questions": [ { "content": { "stem": "重购率的定义是下列哪一项?", "answer": "B", "data": { "options": [ {"content": "选项A内容", "key": "A"}, {"content": "品规连续两周订货客户数/上周订货客户数*100%", "key": "B"} ] }, "explanation": "重购率的定义是..." }, "difficulty": 3, "question_type": "single_choice", "source_trace": { "chunk_ids": ["1.docx_76"], "document_name": "public_kb/1.docx", "page_numbers": [1], "sources": [...] } } ], "request_id": null, "source_chunks_used": 15, "success": true, "total": 10, "requested_types": {"single_choice": 5, "true_false": 3, "fill_blank": 2}, "actual_types": {"single_choice": 5, "true_false": 3, "fill_blank": 2}, "warnings": [] } ``` > **说明**:`warnings` 字段在某题型实际生成数量少于请求数量时返回提示信息。 **验证结果**:✅ 通过(2026-06-05) --- ### POST /exam/generate-smart AI 智能出题 - 自动分析文件并决定题型和数量。 **特点**: - 不需要传 `question_types`,AI 自动分析文档后决定 - 返回格式与旧端口完全兼容,额外包含 `ai_analysis` 字段 - 适合不想费脑子的场景 ```bash curl -s -X POST http://localhost:5001/exam/generate-smart \ -H "Content-Type: application/json" \ -d '{"file_path":"public_kb/1.docx","collection":"public_kb"}' ``` **请求体**: | 字段 | 类型 | 必需 | 说明 | |------|------|------|------| | `file_path` | string | ✅ | 文档路径(格式:`collection/filename`) | | `collection` | string 或 string[] | ✅ | 知识库名称,支持数组(按优先级排序) | | `difficulty` | int | ❌ | 难度等级(1-5),默认 3 | | `options` | object | ❌ | 附加选项 | **响应示例**: ```json { "data": { "task_id": "e5f6a1b2c3d4", "message": "AI 智能出题任务已启动,通过 GET /tasks/e5f6a1b2c3d4 查询结果" }, "message": "AI 智能出题任务已启动", "status": "success", "status_code": 2020, "success": true } ``` > **⚠️ 异步变更**:此接口已从同步改为异步。响应仅返回 `task_id`,后端需通过 `GET /tasks/` 轮询任务状态。任务完成后,`result` 字段包含完整出题结果(含 `ai_analysis` 字段)。 **轮询结果(GET /tasks/\ 完成后的 result 字段)**: ```json { "ai_analysis": { "total_knowledge_points": 21, "suitable_types": ["single_choice", "multiple_choice", "true_false", "subjective"], "question_types": { "single_choice": 8, "multiple_choice": 6, "true_false": 4, "fill_blank": 0, "subjective": 3 }, "reason": "文档包含21个知识点,涵盖术语定义、流程步骤、数值标准..." }, "questions": [...], "request_id": null, "source_chunks_used": 15, "success": true, "total": 16 } ``` **注意事项**: - 实际出题数量 ≤ min(文档知识点数, AI 推荐数量) - 如果文档知识点较少,生成的题目数量会相应减少 - 生产模式下无需传 `Authorization` header **支持的题型**: | 题型 key | 中文名 | 说明 | |----------|--------|------| | `single_choice` | 单选题 | content.answer 为单个选项字母 | | `multiple_choice` | 多选题 | content.answer 为选项字母列表 | | `true_false` | 判断题 | content.answer 为布尔值或字符串 | | `fill_blank` | 填空题 | content.answer 为嵌套列表 | | `subjective` | 简答题 | content.answer 为参考答案 | **验证结果**:✅ 通过 --- ### POST /exam/grade 批阅答案。 ```bash echo '{"answers":[{"question_id":"q1","question_type":"single_choice","content":{"answer":"B"},"student_answer":"B","max_score":2}]}' > /tmp/grade.json curl -s -X POST http://localhost:5001/exam/grade \ -H "Content-Type: application/json" \ -d @/tmp/grade.json ``` **请求体**: | 字段 | 类型 | 必需 | 说明 | |------|------|------|------| | `answers` | array | ✅ | 答案数组 | **answers 数组元素**: | 字段 | 类型 | 必需 | 说明 | |------|------|------|------| | `question_id` | string | ✅ | 题目 ID | | `question_type` | string | ✅ | 题型 | | `content` | object | ✅ | 题目内容(与出题接口返回的 `content` 字段结构一致,后端可直接透传) | | `student_answer` | string/array | ✅ | 学生答案 | | `max_score` | int | ✅ | 满分 | > **注意**:`question_type` 必须为以下值之一:`single_choice`, `multiple_choice`, `true_false`, `fill_blank`, `subjective`。传入无效类型返回 HTTP 400。 **响应示例**: ```json { "data": { "task_id": "f6a1b2c3d4e5", "message": "批阅任务已启动 (1题),通过 GET /tasks/f6a1b2c3d4e5 查询结果" }, "message": "批阅任务已启动", "status": "success", "status_code": 2021, "success": true } ``` > **⚠️ 异步变更**:此接口已从同步改为异步。响应仅返回 `task_id`,后端需通过 `GET /tasks/` 轮询任务状态。任务完成后,`result` 字段包含完整批阅结果(格式见下方说明)。 **轮询结果(GET /tasks/\ 完成后的 result 字段)**: ```json { "request_id": null, "results": [ { "question_id": "q1", "score": 2, "max_score": 2, "grading_status": "success", "details": { "correct": true, "student_answer": "B", "correct_answer": "B", "feedback": "正确!" } } ], "score_rate": 100.0, "success": true, "total_max_score": 2, "total_score": 2 } ``` **`grading_status` 取值说明**: - `success`:评分成功 - `failed`:评分失败(主观题 LLM 解析失败或超时),此时 `details` 包含 `error` 字段 > **注意**:当主观题缺少 `scoring_points` 评分标准时,`details.warnings` 会包含警告信息,评分结果仅供参考。 **验证结果**:✅ 通过(2026-06-05) --- ## 12. 图片服务 ### GET /images/list 获取图片列表。 ```bash curl -s "http://localhost:5001/images/list?limit=5&offset=0" ``` **查询参数**: | 参数 | 类型 | 必需 | 说明 | |------|------|------|------| | `limit` | int | ❌ | 返回数量,默认 50 | | `offset` | int | ❌ | 偏移量,默认 0 | **响应示例**: ```json { "images": [ { "image_id": "0569dd285537", "size_bytes": 169818, "url": "/images/0569dd285537" } ], "limit": 5, "offset": 0, "total": 67 } ``` **验证结果**:✅ 通过 --- ### GET /images/\ 获取单张图片。 ```bash curl -s -o image.jpg http://localhost:5001/images/0569dd285537 ``` **响应**:返回图片文件(HTTP 200) **验证结果**:✅ 通过 --- ### GET /images/\/info 获取图片元数据。 ```bash curl -s "http://localhost:5001/images/0569dd285537/info" ``` **响应示例**: ```json { "format": "JPEG", "height": 1357, "image_id": "0569dd285537", "mode": "RGB", "size_bytes": 169818, "url": "/images/0569dd285537", "width": 1023 } ``` **验证结果**:✅ 通过 --- ### GET /images/stats 获取图片统计。 ```bash curl -s http://localhost:5001/images/stats ``` **响应示例**: ```json { "format_counts": { ".jpg": 46, ".png": 21 }, "supported_formats": [".jpeg", ".gif", ".bmp", ".jpg", ".webp", ".png"], "total_images": 67, "total_size_bytes": 5394510, "total_size_mb": 5.14 } ``` **验证结果**:✅ 通过 --- ## 13. 报告服务 ### GET /reports/weekly 获取周报。 ```bash curl -s http://localhost:5001/reports/weekly ``` **响应示例**: ```json { "report": { "avg_rating": 1.0, "created_at": "2026-05-04T01:49:16", "end_date": "2026-05-10", "high_freq_queries": [ {"frequency": 1, "query": "测试问题", "sample_answer": "测试回答"} ], "improvement_suggestions": ["继续保持当前服务质量"], "low_rating_queries": [], "negative_count": 0, "positive_count": 1, "report_type": "weekly", "satisfaction_rate": 100.0, "start_date": "2026-05-04", "total_feedback": 1, "total_queries": 1 }, "success": true } ``` **验证结果**:✅ 通过 --- ### GET /reports/monthly 获取月报。 ```bash curl -s http://localhost:5001/reports/monthly ``` **验证结果**:✅ 通过(接口存在) --- ## 14. 知识库路由 ### POST /kb/route 测试知识库路由(调试用)。 ```bash echo '{"query":"三峡工程"}' > /tmp/route.json curl -s -X POST http://localhost:5001/kb/route \ -H "Content-Type: application/json" \ -d @/tmp/route.json ``` **响应示例**: ```json { "intent": { "confidence": 0.95, "department": null, "is_general": true, "keywords": [], "reason": "LLM 意图分析" }, "query": "三峡工程", "target_collections": ["public_kb"], "user_department": "", "user_role": "user" } ``` **验证结果**:✅ 通过 --- ## 15. 异步任务查询 > 所有异步操作(同步、重建索引、上传向量化、出题、批阅)返回的 `task_id` 均可通过以下接口查询进度。 ### GET /tasks 获取任务列表。 ```bash curl -s http://localhost:5001/tasks ``` **查询参数**: | 参数 | 类型 | 必需 | 说明 | |------|------|------|------| | `status` | string | ❌ | 过滤状态:`pending` / `running` / `completed` / `failed` | | `type` | string | ❌ | 过滤类型:`sync` / `reindex` / `upload` / `batch_upload` / `exam_generate` / `exam_grade` | | `limit` | int | ❌ | 返回数量限制(默认 50) | **响应示例**: ```json { "data": { "tasks": [ { "task_id": "a1b2c3d4e5f6", "type": "sync", "description": "文档同步", "status": "running", "progress": 45.0, "current": 9, "total": 20, "stage": "处理文件", "message": "已处理: 产品手册.pdf", "created_at": "2026-06-05T10:30:00" } ], "total": 1 }, "message": "查询成功", "status": "success", "status_code": 2000, "success": true } ``` **验证结果**:✅ 通过 --- ### GET /tasks/\ 获取单个任务状态(JSON 轮询接口)。 **后端组推荐使用此接口轮询任务进度,建议间隔 1-2 秒。** ```bash curl -s http://localhost:5001/tasks/a1b2c3d4e5f6 ``` **响应示例(运行中)**: ```json { "data": { "task_id": "a1b2c3d4e5f6", "type": "sync", "description": "文档同步", "status": "running", "progress": 45.0, "current": 9, "total": 20, "stage": "处理文件", "message": "已处理: 产品手册.pdf", "created_at": "2026-06-05T10:30:00", "started_at": "2026-06-05T10:30:01" }, "message": "查询成功", "status": "success", "status_code": 2000, "success": true } ``` **响应示例(已完成)**: ```json { "data": { "task_id": "a1b2c3d4e5f6", "type": "sync", "description": "文档同步", "status": "completed", "progress": 100.0, "current": 20, "total": 20, "stage": "完成", "message": "同步完成", "created_at": "2026-06-05T10:30:00", "started_at": "2026-06-05T10:30:01", "completed_at": "2026-06-05T10:30:15", "duration_ms": 14000, "result": { "documents_processed": 20, "documents_added": 3, "documents_modified": 2, "documents_deleted": 0, "errors": [] } }, "message": "查询成功", "status": "success", "status_code": 2000, "success": true } ``` > **任务状态**: > - `pending`:已创建,等待执行 > - `running`:正在执行 > - `completed`:执行完成,`result` 字段包含完整结果 > - `failed`:执行失败,`error` 字段包含错误信息 > > **轮询建议**:间隔 1-2 秒,当 `status` 为 `completed` 或 `failed` 时停止轮询。 **验证结果**:✅ 通过 --- ### GET /tasks/\/progress SSE 流式任务进度推送(dev-ui 前端推荐使用)。 ```bash curl -s -N http://localhost:5001/tasks/a1b2c3d4e5f6/progress ``` **SSE 事件序列**: ``` data: {"type": "start", "data": {"stage": "扫描文档"}} data: {"type": "progress", "data": {"progress": 10.0, "current": 2, "total": 20, "stage": "处理文件", "message": "已处理: file1.pdf"}} data: {"type": "progress", "data": {"progress": 25.0, "current": 5, "total": 20, "stage": "处理文件", "message": "已处理: file2.docx"}} data: {"type": "complete", "data": {"task_id": "a1b2c3d4e5f6", "status": "completed", "result": {...}}} ``` > **SSE 事件类型**: > - `start`:任务开始 > - `progress`:进度更新(包含 progress/current/total/stage/message) > - `complete`:任务完成(data 为完整任务详情,含 result) > - `error`:任务失败(data 包含 message 错误信息) > - 每 1 秒发送一次 `: heartbeat` 保活 **验证结果**:✅ 通过 --- ### GET /tasks/stats 获取任务统计信息。 ```bash curl -s http://localhost:5001/tasks/stats ``` **响应示例**: ```json { "data": { "total": 5, "by_status": {"running": 1, "completed": 3, "failed": 1}, "by_type": {"sync": 2, "exam_generate": 2, "upload": 1} }, "message": "查询成功", "status": "success", "status_code": 2000, "success": true } ``` **验证结果**:✅ 通过 --- ## 附录. 已知问题 ### 1. /rag 接口 collections 参数 **问题**:使用 `collection`(单数)参数指定知识库无效,会被忽略并默认检索 `public_kb`。 **解决**:必须使用 `collections`(复数,数组格式)参数。 ```bash # ❌ 错误用法 - collection 单数参数无效 {"message": "问题", "collection": "dept_tech", "chat_history": []} # ✅ 正确用法 - collections 数组参数 {"message": "问题", "collections": ["dept_tech"], "chat_history": []} ``` --- ## 测试覆盖统计 > 2026-06-04 生产服务器测试结果(47.116.16.222,云端 Reranker + MMR 文本相似度) | 分类 | 总数 | 通过 | 超时/异常 | 备注 | |------|------|------|-----------|------| | 健康检查 | 1 | 1 | 0 | ✅ 0.002s | | 问答接口 | 2 | 2 | 0 | ✅ /rag 流式 12-14s | | 检索接口 | 1 | 1 | 0 | ✅ /search 0.58-1.53s | | 向量库管理 | 3 | 3 | 0 | ✅ | | 文档管理 | 1 | 1 | 0 | ✅ | | 同步服务 | 3 | 3 | 0 | ✅ | | 反馈系统 | 4 | 4 | 0 | ✅ | | FAQ 管理 | 2 | 2 | 0 | ✅ | | 出题系统 | 1 | 1 | 0 | ✅ | | 图片服务 | 2 | 2 | 0 | ✅ | | 报告服务 | 2 | 2 | 0 | ✅ | | 知识库路由 | 1 | 1 | 0 | ✅ | | **总计** | **23** | **23** | **0** | **全部通过** | --- ## 性能基准测试 > 2026-06-04 生产服务器实测(47.116.16.222,public_kb 824 切片) ### 检索性能 | 接口 | 参数 | 优化前 | 优化后 | 提升 | |------|------|--------|--------|------| | `/search` | top_k=10 | ~34s | **0.58s** | ~60x | | `/search` | top_k=30 | ~87s | **1.53s** | ~57x | ### 完整 RAG 问答性能(流式) | 查询 | 完整响应时间 | 搜索阶段 | LLM 生成 | |------|-------------|----------|----------| | 现代终端分类有哪些? | 13.8s | ~1.5s | ~12s | | 加盟终端分公司审批流程 | 12.7s | ~1.5s | ~11s | | 什么是金丝利零售 | 12.7s | ~1.5s | ~11s | ### 优化措施 | 优化项 | 配置 | 效果 | |--------|------|------| | Reranker 云端化 | `RERANK_BACKEND=cloud`(qwen3-rerank) | 搜索阶段从 33-48s 降至 ~0.3s | | MMR 文本相似度 | `MMR_USE_EMBEDDING=false` | MMR 阶段从 ~36s 降至 ~0s | ### 耗时拆解(优化后 /rag 问答) ``` 总耗时 ~13s ├── 搜索管线 ~1.5s │ ├── Embedding (query) ~0.25s │ ├── 向量检索 + BM25 ~0.3s │ ├── RRF 融合 ~0.01s │ ├── 上下文扩展 ~0.1s │ ├── MMR 去重 (文本) ~0.01s │ ├── Cloud Reranker ~0.3s │ └── 后处理 ~0.5s └── LLM 流式生成 ~11.5s ├── 网络建连 ~0.5s └── 流式 token 生成 ~11s ``` > **注**:LLM 生成阶段耗时取决于 qwen-plus API 响应速度,非本地可优化。如需进一步压缩至 10s 以内,可考虑换用更快的模型(如 qwen-turbo)。 --- ## 附录 B. 生产部署说明 ### 多向量库架构 生产环境采用**多向量库模式**,每个知识库独立存储在 `knowledge/vector_store/chroma//` 目录下,各自维护独立的 ChromaDB SQLite 数据库和 BM25 索引。 当前生产服务器知识库列表: | 知识库 | 切片数 | 说明 | |--------|--------|------| | public_kb | ~824 | 公开知识库,所有用户可访问 | | dept_1_kb | ~800 | 部门知识库 1 | | dept_2_kb | ~118 | 部门知识库 2 | | dept_3_kb | ~132 | 部门知识库 3 | | dept_4_kb | ~4 | 部门知识库 4 | | dept_6_kb | ~29 | 部门知识库 6 | | test1 | ~637 | 测试知识库 | | faq_kb / test3 | 0 | 空(预留) | ### 切片元数据字段 新代码构建的向量库切片包含以下 metadata 字段: | 字段 | 说明 | 新代码新增 | |------|------|-----------| | `chunk_id` | 切片唯一标识 `{文件名}_{序号}` | | | `chunk_index` | 切片在文档中的序号(用于引用溯源跳转) | ✅ | | `chunk_type` | text / table / image | | | `source` | 来源文件名 | | | `collection` | 所属知识库 | | | `doc_type` | 文档类型 pdf/word/excel/other | ✅ | | `page` / `page_end` | 页码范围 | | | `section` | 所属章节 | | | `status` | active / deprecated | | | `version` | 版本号 v1 | | ### 向量库重构 当代码升级涉及元数据字段变更时(如新增 `chunk_index`、`doc_type`),需要重构向量库: ```bash # 重构指定知识库(清除哈希记录 → 触发全量同步,异步任务) curl -s -X POST http://127.0.0.1:5001/collections//reindex ``` > **⚠️ 注意**:reindex 已改为异步任务,立即返回 `task_id`。通过 `GET /tasks/` 轮询进度。不再阻塞 gunicorn worker,搜索和问答接口不受影响。 ### Reranker 配置 生产环境使用 DashScope 云端 Reranker 替代本地 CPU 推理,显著提升检索速度: | 配置项 | 值 | 说明 | |--------|-----|------| | `RERANK_BACKEND` | `cloud` | 使用云端 API(可选 `local` / `cloud` / `fallback`) | | `RERANK_CLOUD_MODEL` | `qwen3-rerank` | DashScope 云端排序模型 | | `RERANK_CLOUD_API_KEY` | `sk-*` | DashScope 标准 API Key | | `RERANK_CLOUD_BASE_URL` | `https://dashscope.aliyuncs.com/compatible-api/v1/reranks` | OpenAI 兼容端点 | | `MMR_USE_EMBEDDING` | `false` | MMR 使用文本相似度(零额外计算) | > **fallback 模式**:`RERANK_BACKEND=fallback` 优先使用云端 API,如果云端不可用则自动回退到本地模型,适合生产环境高可用场景。