# RAG 数据流程 > 本文档基于 v7.0.0 架构,详细梳理 RAG 系统从用户查询到回答生成的完整数据流。 > 涵盖意图分析、混合检索、云端重排序、MMR 去重、上下文构建、LLM 生成和引用溯源等核心环节。 --- ## 一、概述 ### 1.1 检索管线总览 ``` 用户查询 │ ▼ ┌─────────────────────────────────────────────────────────────────────────┐ │ 1. 意图分析 (intent_analyzer) │ │ 问题改写 · 指代消解 · 是否需要检索 · 重复提问检测 │ │ 模型:qwen-turbo │ └─────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────┐ │ 2. 混合检索 (engine.search_knowledge) │ │ ┌──────────────┐ ┌──────────────┐ │ │ │ 向量检索 │ │ BM25 检索 │ │ │ │ (ChromaDB) │ │ (关键词匹配) │ │ │ └──────┬───────┘ └──────┬───────┘ │ │ └────────┬──────────┘ │ │ ▼ │ │ RRF 融合 (Reciprocal Rank Fusion) │ └─────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────┐ │ 3. 云端 Rerank (DashScope API) │ │ 模型:qwen3-rerank │ └─────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────┐ │ 4. MMR 去重 (文本 Jaccard 模式) │ │ MMR_USE_EMBEDDING=false │ └─────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────┐ │ 5. 上下文扩展 + 图片选择 │ │ 上下文增强 · 图文关联补充 · 懒加载 VLM 描述 │ └─────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────┐ │ 6. LLM 生成 (AgenticRAG 引擎) │ │ 模型:qwen3.6-flash(主 LLM)/ qwen-vl-plus(VLM) │ │ 流式输出 · 引用标注 │ └─────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────┐ │ 7. 返回结果 │ │ { answer, sources, images } │ └─────────────────────────────────────────────────────────────────────────┘ ``` ### 1.2 模型配置一览 | 用途 | 模型 | 说明 | |------|------|------| | 主 LLM | qwen3.6-flash | 回答生成、上下文理解 | | 意图分析 | qwen-turbo | 轻量快速,用于问题改写与意图判断 | | VLM | qwen-vl-plus | 图片理解与描述生成 | | 云端重排序 | qwen3-rerank | DashScope API 调用,替代本地 BGE-reranker | ### 1.3 v7.0.0 架构变更要点 - **Reranker**:从本地 BGE-reranker 切换为云端 DashScope API(qwen3-rerank) - **MMR 去重**:使用文本相似度模式(`MMR_USE_EMBEDDING=false`),基于 Jaccard 系数 - **Agentic 引擎**:拆分为多个子模块(agentic_search / agentic_answer / agentic_citation 等) - **Graph RAG**:模块已清空,不再使用 --- ## 二、请求入口 ### 2.1 API 路由 **入口文件**:`api/chat_routes.py` 用户通过 API 发送查询请求,由 `generate()` 函数统一调度: ``` POST /rag 或 POST /chat Body: { "query": "用户问题", "kb_name": "知识库名称" } ``` `generate()` 的职责: 1. 调用意图分析模块,获取改写后的查询与检索决策 2. 若需要检索,调用混合检索管线 3. 执行图片选择与上下文构建 4. 调用 LLM 生成回答(流式输出) 5. 组装最终响应(回答 + 引用来源 + 图片) ### 2.2 请求数据结构 ```python # 请求 { "query": "蓄水以来逐年发电量", "kb_name": "public_kb", "history": [...] # 可选:对话历史 } # 响应 { "type": "finish", "answer": "完整回答文本", "sources": [...], # 引用来源列表 "images": [...] # 精选图片列表 } ``` --- ## 三、意图分析 **入口文件**:`core/intent_analyzer.py` **使用模型**:qwen-turbo(轻量快速) ### 3.1 核心功能 意图分析器在检索前对用户查询进行预处理,输出结构化决策: ```python @dataclass class IntentAnalysis: rewritten_query: str # 改写后的查询(指代消解、省略补全) use_context: bool # 是否使用历史上下文 need_retrieval: bool # 是否需要检索知识库 ``` ### 3.2 处理逻辑 | 功能 | 说明 | |------|------| | 问题改写 | 指代消解("它" → 具体实体)、省略补全(补全缺失主语) | | 上下文判断 | 根据对话历史决定是否需要前文信息 | | 检索决策 | 判断是否需要查询知识库(闲聊类问题可跳过) | | 重复提问检测 | 用户重复提问相同问题时,强制设置 `need_retrieval=true` | ### 3.3 重复提问规则 当用户重复提问相同或相似问题时,意图分析器必须设置 `need_retrieval = true`。原因:用户可能对之前的回答不满意,或之前的回答包含错误信息,需要重新检索以获取更准确的结果。 --- ## 四、检索阶段 **入口文件**:`core/engine.py` **核心函数**:`search_knowledge()` ### 4.1 查询缓存检查 检索前先检查缓存,命中则直接返回历史结果,避免重复计算: ```python cached = cache.get_query_result(query, kb_name) if cached: return cached ``` ### 4.2 向量检索 使用 embedding 模型将查询编码为向量,在 ChromaDB 中执行 ANN 检索: ```python query_vector = embedding_model.encode(query) text_results = collection.query( query_embeddings=[query_vector], n_results=100 # recall_k,召回候选数量 ) ``` **图片独立召回**:同时对图片切片进行独立检索,保证图片有足够的召回机会: ```python image_results = collection.query( query_embeddings=[query_vector], where={'chunk_type': {'$in': ['image', 'chart', 'table']}}, n_results=5 ) ``` ### 4.3 BM25 关键词检索 基于 BM25 算法执行关键词匹配检索,弥补向量检索在精确匹配上的不足: ```python bm25_results = bm25_index.search(query, top_k=100) ``` ### 4.4 RRF 融合 使用 Reciprocal Rank Fusion 算法将向量检索和 BM25 检索的结果进行融合排序: ```python fused_results = reciprocal_rank_fusion( [text_results, image_results, bm25_results], weights=[VECTOR_WEIGHT, IMAGE_WEIGHT, BM25_WEIGHT] ) ``` **融合参数**: | 参数 | 默认值 | 说明 | |------|--------|------| | `recall_k` | 100 | 各通道召回候选数量 | | `VECTOR_WEIGHT` | 0.6 | 向量检索权重 | | `BM25_WEIGHT` | 0.4 | BM25 检索权重 | --- ## 五、重排序 ### 5.1 云端 Rerank **v7.0.0 变更**:从本地 BGE-reranker 切换为云端 DashScope API。 **调用模型**:qwen3-rerank RRF 融合后的候选结果通过云端 Reranker 进行精排,基于查询与文档的语义相关性重新打分排序: ```python reranked = rerank_results( query, fused_results, model="qwen3-rerank", # DashScope API top_k=15 ) ``` ### 5.2 与旧版的区别 | 对比项 | 旧版(本地 BGE-reranker) | v7.0.0(云端 qwen3-rerank) | |--------|--------------------------|---------------------------| | 部署方式 | 本地模型加载 | DashScope API 远程调用 | | 资源占用 | 需要 GPU 显存 | 无本地资源消耗 | | 模型能力 | BGE-reranker(较小) | qwen3-rerank(更强) | | 延迟 | 低(本地推理) | 中等(网络往返) | --- ## 六、MMR 去重 ### 6.1 配置 v7.0.0 采用文本相似度模式进行 MMR(Maximal Marginal Relevance)去重: ``` MMR_USE_EMBEDDING = false ``` ### 6.2 工作原理 使用文本 Jaccard 相似度(而非 embedding 向量余弦相似度)来衡量候选文档之间的重复程度: ```python def _apply_mmr(query, candidates, top_k=30, lambda_param=0.7): """ MMR 去重:在相关性和多样性之间取得平衡 lambda_param=0.7: 70% 权重给相关性,30% 权重给多样性 相似度度量:文本 Jaccard 系数(基于词集合交集/并集) """ selected = [] for candidate in candidates: if not selected: selected.append(candidate) continue # Jaccard 相似度(文本模式) max_sim = max( jaccard_similarity(candidate.tokens, s.tokens) for s in selected ) mmr_score = lambda_param * relevance - (1 - lambda_param) * max_sim if mmr_score > threshold: selected.append(candidate) return selected[:top_k] ``` ### 6.3 选择文本模式的原因 - **速度更快**:无需计算 embedding 向量之间的余弦相似度 - **效果直观**:Jaccard 系数直接反映文本内容的重叠程度 - **避免向量偏差**:embedding 模型可能对格式化内容(如图片描述)产生不准确的相似度 --- ## 七、上下文构建 ### 7.1 检索结果处理 **入口文件**:`api/chat_routes.py` 将检索结果转换为 LLM 可用的上下文列表: ```python contexts = [] for result in search_results: meta = result['metadata'] if meta['chunk_type'] in ('image', 'chart'): # 图片切片:使用完整描述(而非轻量描述) doc = meta.get('full_description', result['document']) else: doc = result['document'] contexts.append({'doc': doc, 'meta': meta}) ``` ### 7.2 懒加载增强 对没有 VLM 描述的图片切片,按需调用 VLM 生成更精准的语义描述: ```python enhance_retrieved_chunks(contexts, query, kb_name) # 对缺少 VLM 描述的图片 → 调用 qwen-vl-plus 生成描述 ``` ### 7.3 图片选择 **核心函数**:`select_images()` 从检索结果中筛选与查询最相关的图片: **步骤一:意图检测** | 查询类型 | 参数调整 | |----------|----------| | 精确图号查询("图2.3") | `MAX_IMAGES=2, MIN_SCORE=5.0` | | 弱图片意图("发电量图") | `MAX_IMAGES=1` | | 普通查询 | `MAX_IMAGES=2` | **步骤二:提取图表引用** 从 top 5 文本块中提取 "见图2.3"、"如表2.2" 等引用,建立图号与来源文件的映射: ```python referenced_figures = {'2.3': {'source_file': 'xxx.pdf'}, ...} ``` **步骤三:图片相关性打分** `score_image_relevance()` 打分规则: | 匹配项 | 加分 | |--------|------| | 图号精确匹配(查询中有"图2.3") | +10 分 | | 表号精确匹配 | +10 分 | | 关键词匹配("发电量"等) | +2 分/个 | | 字符重叠 | +0.2 分/字符 | | 章节匹配 | +1.5 分 | | 图片类型(chart > image) | +2 / +1 分 | | 向量相似度 | +2 分(最高) | | 引用匹配(需章节相关) | +8 分 | **步骤四:图文关联补充** 遍历 top 5 文本块中引用的图表编号,查找对应的图片切片并补充到结果中。 **步骤五:返回 top N 图片** ```python scored_images.sort(key=lambda x: x['score'], reverse=True) return scored_images[:MAX_IMAGES] ``` ### 7.4 构建 LLM Prompt ```python # 文本上下文 context_text = "\n\n".join([ctx['doc'] for ctx in contexts[:5]]) # 图片信息 if selected_images: image_info = "【可用图片】\n" + 图片描述列表 # 最终上下文 enhanced_context = context_text + image_info ``` --- ## 八、LLM 生成 ### 8.1 AgenticRAG 引擎 v7.0.0 将 Agentic 引擎拆分为独立的子模块,各司其职: | 子模块 | 职责 | |--------|------| | `agentic_search.py` | 检索调度:管理多轮检索、查询分解 | | `agentic_answer.py` | 回答生成:基于上下文生成最终回答 | | `agentic_citation.py` | 引用标注:在回答中插入来源引用标记 | | `agentic_context.py` | 上下文管理:上下文窗口控制、截断策略 | | `agentic_query.py` | 查询处理:查询改写、多查询生成 | | `agentic_media.py` | 多媒体处理:图片理解、表格解析 | | `agentic_quality.py` | 质量控制:回答质量评估、幻觉检测 | | `agentic_meta.py` | 元数据管理:知识库信息、检索统计 | ### 8.2 流式生成 使用 SSE(Server-Sent Events)实现流式输出: ```python for token in engine.generate_answer_stream(query, enhanced_context): yield token # 逐 token 推送给前端 ``` **主 LLM 模型**:qwen3.6-flash **VLM 模型**:qwen-vl-plus(处理图片理解任务) ### 8.3 回答结构 ```python { "type": "finish", "answer": "根据蓄水以来的统计数据,三峡电站逐年发电量呈现波动上升趋势...", "sources": [ { "source": "三峡公报_2022.pdf", "page": 12, "section": "综述 > 2.3 发电", "chunk_id": "三峡公报_text_24" } ], "images": [ { "id": "ab77281e7913.jpg", "url": "/images/ab77281e7913.jpg", "type": "chart", "source": "三峡公报_2022.pdf", "page": 12, "description": "图2.3 柱状图:2003-2022年逐年发电量" } ] } ``` --- ## 九、引用溯源 ### 9.1 引用标注机制 `agentic_citation.py` 负责在回答中插入引用标记,将回答内容与知识库来源关联: ``` 根据统计数据[1],2022年三峡电站年度发电量为787.90亿千瓦时[2]。 [1] 来源:三峡公报_2022.pdf,第12页,综述 > 2.3 发电 [2] 来源:三峡公报_2022.pdf,第15页,表2.1 ``` ### 9.2 引用数据来源 每个引用标记对应检索结果中的一个切片,包含: | 字段 | 说明 | |------|------| | `source` | 源文件名 | | `page` | 页码 | | `section` | 章节路径 | | `chunk_id` | 切片唯一标识 | | `chunk_type` | 类型(text / table / image / chart) | ### 9.3 前端引用跳转 前端解析回答中的引用标记(如 `[1]`),渲染为可点击的链接,点击后跳转到对应的来源文件或页面。 --- ## 十、文档入库流程(补充参考) > 入库流程为检索提供数据基础,以下简要说明关键环节。 ### 10.1 文档解析 **入口文件**:`parsers/mineru_parser.py` **核心函数**:`parse_with_mineru()` MinerU 解析 PDF/Word/Excel 文件,输出结构化内容: ``` .data/mineru_temp/{file_hash}/ ├── auto/ │ ├── {doc_name}.md # Markdown 内容 │ ├── {doc_name}_content_list.json # 结构化内容列表(核心) │ └── images/ # 提取的图片文件 ``` **content_list.json 中的条目类型**: | item_type | 处理方式 | 关键字段 | |-----------|----------|----------| | `text` | 文本块 | content, section_path, text_level | | `table` | 表格 | content, table_html, image_path | | `image` | 图片 | content(=caption), image_path, context_before/after | | `chart` | 图表 | content(=caption), image_path, context_before/after | ### 10.2 MinerUChunk 数据结构 ```python @dataclass class MinerUChunk: content: str # 文本内容 chunk_type: str # 类型: text, table, image, chart page_start: int = 1 # 起始页码 page_end: int = 1 # 结束页码 text_level: int = 0 # 标题级别 (0=正文, 1=h1, 2=h2...) title: str = "" # 标题文本 section_path: str = "" # 章节路径 bbox: Optional[List[float]] = None # 边界框 [x0, y0, x1, y1] source_file: str = "" # 源文件名 table_html: Optional[str] = None # 表格 HTML image_path: Optional[str] = None # 图片路径 images: Optional[List[Dict]] = None # 关联图片列表 context_before: str = "" # 图片前的文本上下文 context_after: str = "" # 图片后的文本上下文 ``` ### 10.3 切片入库 **入口文件**:`knowledge/manager.py` **核心函数**:`add_file_to_kb()` ``` MinerUChunk 列表 │ ├── 文本块 → 计算 embedding → 存入 ChromaDB │ ├── 表格块 → 生成语义增强摘要 → 存入 ChromaDB │ └── 图片块 → VLM 缓存检查 → 生成描述 → 存入 ChromaDB ``` **图片描述策略**: 1. 优先使用 VLM 缓存描述(语义更丰富,由 qwen-vl-plus 生成) 2. 若无缓存,生成轻量描述(基于文件名 + 章节路径 + 上下文) 轻量描述示例: ``` 图表:图2.3,位于「综述 > 2.3发电」,第12页 前文:受长江流域性严重枯水影响,2022年三峡电站年度发电量为787.90亿千瓦时... 后文:2.4航运 三峡船闸和葛洲坝船闸实行统一调度... ``` VLM 描述示例(更精准): ``` 图2.3 柱状图 主要内容描述:该柱状图展示了2003年至2022年每年的发电量(单位:亿千瓦时)。 发电量在2003年为86.07亿千瓦时,随后逐年波动上升,至2020年达到峰值1118.02亿千瓦时... ``` ### 10.4 ChromaDB 存储结构 | 字段 | 类型 | 说明 | |------|------|------| | `ids` | str | 切片唯一标识,如 `doc.pdf_text_24` | | `embeddings` | List[float] | 向量表示 | | `documents` | str | 切片内容(文本/描述/摘要) | | `metadatas` | dict | 元数据(见下表) | **图片切片 metadata 示例**: ```python { 'source': '三峡公报_2022.pdf', 'page': 12, 'chunk_type': 'chart', 'section': '综述 > 2.3发电', 'figure_number': '2.3', 'image_path': 'ab77281e7913.jpg', 'has_vlm_desc': True, 'preview': '图2.3 柱状图 主要内容...' } ``` --- ## 十一、关键文件索引 | 文件 | 职责 | 关键函数/类 | |------|------|-------------| | `api/chat_routes.py` | API 路由与请求调度 | `generate()`, `select_images()`, `score_image_relevance()` | | `core/intent_analyzer.py` | 意图分析 | `analyze()`, `IntentAnalysis` | | `core/engine.py` | 检索引擎核心 | `search_knowledge()`, `reciprocal_rank_fusion()`, `rerank_results()` | | `core/agentic_search.py` | 检索调度 | 多轮检索、查询分解 | | `core/agentic_answer.py` | 回答生成 | 基于上下文生成最终回答 | | `core/agentic_citation.py` | 引用标注 | 来源引用标记插入 | | `core/agentic_context.py` | 上下文管理 | 上下文窗口控制、截断 | | `core/agentic_query.py` | 查询处理 | 查询改写、多查询生成 | | `core/agentic_media.py` | 多媒体处理 | 图片理解、表格解析 | | `core/agentic_quality.py` | 质量控制 | 回答质量评估、幻觉检测 | | `core/agentic_meta.py` | 元数据管理 | 知识库信息、检索统计 | | `parsers/mineru_parser.py` | 文档解析 | `parse_with_mineru()`, `MinerUChunk` | | `knowledge/manager.py` | 知识库管理 | `add_file_to_kb()`, `generate_lightweight_image_description()` | | `knowledge/lazy_enhance.py` | 懒加载增强 | `lazy_vlm_description()`, `enhance_retrieved_chunks()` |