diff --git a/docs/RAG引用跳转-前端实现说明.md b/docs/RAG引用跳转-前端实现说明.md index 5137833..69c3468 100644 --- a/docs/RAG引用跳转-前端实现说明.md +++ b/docs/RAG引用跳转-前端实现说明.md @@ -71,7 +71,7 @@ SSE 流结束时,`finish` 事件的 `citations` 字段是一个数组,每个 ### 三、文档预览接口 ``` -GET /api/documents/{collection}/{source}/preview?chunk_index={N}&context={K} +GET /documents/{collection}/{source}/preview?chunk_index={N}&context={K} ``` **参数:** @@ -94,7 +94,7 @@ GET /api/documents/{collection}/{source}/preview?chunk_index={N}&context={K} "target_index": 5, // 请求的 chunk_index "chunks": [ { - "id": "uuid-xxx", + "id": "三峡公报.pdf_3", // 切片 ID(文件名_序号格式,序号 = chunk_index) "document": "完整切片正文...", // 完整内容(不截断) "metadata": { "chunk_index": 3, @@ -104,12 +104,16 @@ GET /api/documents/{collection}/{source}/preview?chunk_index={N}&context={K} "doc_type": "pdf", ... }, + "status": "active", // 文档状态 + "version": "v1", // 版本号 "is_target": false // 是否为目标切片 }, { - "id": "uuid-yyy", + "id": "三峡公报.pdf_5", "document": "目标切片的完整正文...", "metadata": { "chunk_index": 5, "page": 3, ... }, + "status": "active", + "version": "v1", "is_target": true // ← 这是用户点击的那个切片 }, // ... 上下文切片 @@ -126,7 +130,7 @@ GET /api/documents/{collection}/{source}/preview?chunk_index={N}&context={K} ``` 1. 从 finish 事件取 answer(回答全文)和 citations(引用数组) 2. 用正则 /\[ref:([^\]]+)\]/g 从 answer 中按出现顺序提取所有 chunk_id -3. 与 citations 数组匹配,建立 chunk_id → 显示序号 [1] [2] ... 的映射 +3. 以 chunk_id 为键从 citations 数组中查找对应的引用信息(注意:是按 chunk_id 匹配,不是按数组下标),建立 chunk_id → 显示序号 [1] [2] ... 的映射 4. 渲染回答时,将 [ref:xxx] 替换为可点击的上标元素 ``` diff --git a/scripts/fix_image_paths.py b/scripts/fix_image_paths.py index 6afe5fa..1e70a91 100644 --- a/scripts/fix_image_paths.py +++ b/scripts/fix_image_paths.py @@ -17,86 +17,109 @@ import sys sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) import chromadb +from config import CHROMA_DB_PATH def fix_image_paths(dry_run: bool = False): """ 修复向量库中的图片路径格式 + 多向量库模式下,遍历 chroma/ 下的每个知识库子目录, + 分别创建 PersistentClient 处理各自的集合。 + Args: dry_run: 仅预览,不实际修改 """ - chroma_path = "knowledge/vector_store/chroma" - client = chromadb.PersistentClient(path=chroma_path) + # 扫描知识库子目录(每个子目录是一个独立的向量库) + kb_dirs = [] + if os.path.exists(CHROMA_DB_PATH): + for item in os.listdir(CHROMA_DB_PATH): + item_path = os.path.join(CHROMA_DB_PATH, item) + if os.path.isdir(item_path) and not item.startswith('.'): + if os.path.exists(os.path.join(item_path, 'chroma.sqlite3')): + kb_dirs.append(item) - # 获取所有集合 - collections = client.list_collections() - print(f"找到 {len(collections)} 个集合") + if not kb_dirs: + print("未找到任何知识库子目录") + return - for collection in collections: - print(f"\n处理集合: {collection.name}") + print(f"发现 {len(kb_dirs)} 个知识库: {kb_dirs}\n") - # 获取所有图片类型的切片 - try: - results = collection.get( - where={"chunk_type": "image"}, - include=["metadatas", "documents", "embeddings"] - ) + for kb_name in kb_dirs: + kb_path = os.path.join(CHROMA_DB_PATH, kb_name) + print(f"处理知识库: {kb_name}") + client = chromadb.PersistentClient(path=kb_path) - if not results["ids"]: - print(f" 无图片类型切片") - continue + # 获取所有集合 + collections = client.list_collections() + print(f" 找到 {len(collections)} 个集合") - print(f" 找到 {len(results['ids'])} 个图片切片") + for collection in collections: + print(f"\n 处理集合: {collection.name}") - # 检查需要修复的路径 - needs_fix = [] - for i, (id, meta) in enumerate(zip(results["ids"], results["metadatas"])): - image_path = meta.get("image_path", "") - if image_path and ("/" in image_path or "\\" in image_path): - # 路径包含目录,需要修复 - new_path = os.path.basename(image_path) - needs_fix.append({ - "id": id, - "old_path": image_path, - "new_path": new_path, - "index": i - }) + # 获取所有图片类型的切片 + try: + results = collection.get( + where={"chunk_type": "image"}, + include=["metadatas", "documents", "embeddings"] + ) - if not needs_fix: - print(f" 所有路径格式正确,无需修复") - continue + if not results["ids"]: + print(f" 无图片类型切片") + continue - print(f" 需要修复 {len(needs_fix)} 条记录:") - for item in needs_fix[:5]: # 只显示前5条 - print(f" {item['old_path']} -> {item['new_path']}") - if len(needs_fix) > 5: - print(f" ... 还有 {len(needs_fix) - 5} 条") + print(f" 找到 {len(results['ids'])} 个图片切片") - if dry_run: - print(f" [DRY-RUN] 跳过实际修改") - continue + # 检查需要修复的路径 + needs_fix = [] + for i, (id, meta) in enumerate(zip(results["ids"], results["metadatas"])): + image_path = meta.get("image_path", "") + if image_path and ("/" in image_path or "\\" in image_path): + # 路径包含目录,需要修复 + new_path = os.path.basename(image_path) + needs_fix.append({ + "id": id, + "old_path": image_path, + "new_path": new_path, + "index": i + }) - # 执行修复 - ids_to_update = [] - metadatas_to_update = [] + if not needs_fix: + print(f" 所有路径格式正确,无需修复") + continue - for item in needs_fix: - # 更新元数据 - new_meta = results["metadatas"][item["index"]].copy() - new_meta["image_path"] = item["new_path"] - ids_to_update.append(item["id"]) - metadatas_to_update.append(new_meta) + print(f" 需要修复 {len(needs_fix)} 条记录:") + for item in needs_fix[:5]: # 只显示前5条 + print(f" {item['old_path']} -> {item['new_path']}") + if len(needs_fix) > 5: + print(f" ... 还有 {len(needs_fix) - 5} 条") - # 批量更新 - collection.update( - ids=ids_to_update, - metadatas=metadatas_to_update - ) - print(f" ✓ 已修复 {len(ids_to_update)} 条记录") + if dry_run: + print(f" [DRY-RUN] 跳过实际修改") + continue - except Exception as e: - print(f" 处理集合 {collection.name} 时出错: {e}") + # 执行修复 + ids_to_update = [] + metadatas_to_update = [] + + for item in needs_fix: + # 更新元数据 + new_meta = results["metadatas"][item["index"]].copy() + new_meta["image_path"] = item["new_path"] + ids_to_update.append(item["id"]) + metadatas_to_update.append(new_meta) + + # 批量更新 + collection.update( + ids=ids_to_update, + metadatas=metadatas_to_update + ) + print(f" ✓ 已修复 {len(ids_to_update)} 条记录") + + except Exception as e: + print(f" 处理集合 {collection.name} 时出错: {e}") + + print() def main():