From 8268071fdcf3121dbf31c91ebbc4f4e475f27163 Mon Sep 17 00:00:00 2001 From: lacerate551 <128470311+lacerate551@users.noreply.github.com> Date: Sun, 21 Jun 2026 23:11:46 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E6=A8=A1=E5=9E=8B=E5=88=87=E6=8D=A2?= =?UTF-8?q?=E8=87=B3=20mimo-v2.5=20+=20=E6=96=87=E6=A1=A3=E5=85=A8?= =?UTF-8?q?=E9=9D=A2=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - config.py: INTENT_MODEL 从 deepseek-v4-flash 切换至 mimo-v2.5 - config.py: get_intent_client() 从百炼 API 切换至 mimo API - RAG系统完整指南.md: v4.0→v4.1,新增图片检索子系统、P0安全网、 救援管线、五层缓存架构文档;替换所有旧模型名和API地址 - RAG数据流程.md: 完全重写,匹配实际代码逻辑 - curl测试手册.md: 更新模型名和Reranker配置 - MinerU模型部署指南.md: VLM_MODEL 更新为 mimo-v2.5 - 开发与系统模块说明.md: API地址和模型名更新 - 测试指南.md: API地址和模型名更新 --- config.py | 11 +- docs/MinerU模型部署指南.md | 2 +- docs/RAG数据流程.md | 549 ++++++++++++++++++++----------------- docs/RAG系统完整指南.md | 173 +++++++++--- docs/curl测试手册.md | 14 +- docs/开发与系统模块说明.md | 8 +- docs/测试指南.md | 6 +- 7 files changed, 466 insertions(+), 297 deletions(-) diff --git a/config.py b/config.py index dc693fc..c172567 100644 --- a/config.py +++ b/config.py @@ -20,7 +20,7 @@ DASHSCOPE_API_KEY = os.getenv("DASHSCOPE_API_KEY", "") DASHSCOPE_BASE_URL = os.getenv("DASHSCOPE_BASE_URL", "https://token-plan-cn.xiaomimimo.com/v1") DASHSCOPE_MODEL = os.getenv("DASHSCOPE_MODEL", "mimo-v2.5") # 文本生成模型 RAG_CHAT_MODEL = os.getenv("RAG_CHAT_MODEL", "mimo-v2.5") # RAG 对话模型 -INTENT_MODEL = os.getenv("INTENT_MODEL", "deepseek-v4-flash") # 意图分析模型(百炼快速模型) +INTENT_MODEL = os.getenv("INTENT_MODEL", "mimo-v2.5") # 意图分析模型(百炼额度用尽,切回 mimo) VLM_MODEL = os.getenv("VLM_MODEL", "mimo-v2.5") # 视觉语言模型(图片描述) # 百炼 API(阿里云 DashScope,用于意图分析等轻量任务) @@ -295,11 +295,12 @@ def get_llm_client(): _intent_client = None def get_intent_client(): - """获取意图分析专用 LLM 客户端(百炼快速模型)""" + """获取意图分析专用 LLM 客户端""" global _intent_client if _intent_client is None: - if not BAILIAN_API_KEY: - raise ValueError("BAILIAN_API_KEY 未配置,请在 .env 中设置") + # 百炼额度用尽,意图分析也使用 mimo API + if not DASHSCOPE_API_KEY: + raise ValueError("DASHSCOPE_API_KEY 未配置,请在 .env 中设置") from openai import OpenAI - _intent_client = OpenAI(api_key=BAILIAN_API_KEY, base_url=BAILIAN_BASE_URL) + _intent_client = OpenAI(api_key=DASHSCOPE_API_KEY, base_url=DASHSCOPE_BASE_URL) return _intent_client diff --git a/docs/MinerU模型部署指南.md b/docs/MinerU模型部署指南.md index f60b306..4df97ea 100644 --- a/docs/MinerU模型部署指南.md +++ b/docs/MinerU模型部署指南.md @@ -364,7 +364,7 @@ DASHSCOPE_BASE_URL= DASHSCOPE_MODEL=mimo-v2.5 RAG_CHAT_MODEL=mimo-v2.5 INTENT_MODEL=mimo-v2.5 -VLM_MODEL=qwen-vl-plus +VLM_MODEL=mimo-v2.5 # MinerU 在线 API(可选,设置后无需本地模型) MINERU_API_TOKEN= diff --git a/docs/RAG数据流程.md b/docs/RAG数据流程.md index d362e65..0bc9afd 100644 --- a/docs/RAG数据流程.md +++ b/docs/RAG数据流程.md @@ -1,7 +1,7 @@ # RAG 数据流程 -> 本文档基于 v7.0.0 架构,详细梳理 RAG 系统从用户查询到回答生成的完整数据流。 -> 涵盖意图分析、混合检索、云端重排序、MMR 去重、上下文构建、LLM 生成和引用溯源等核心环节。 +> 本文档基于当前代码实际逻辑梳理,详细记录 RAG 系统从用户查询到回答生成的完整数据流。 +> 涵盖意图分析、混合检索、重排序、救援管道、上下文构建、图片选择与注入、LLM 生成和引用溯源等核心环节。 --- @@ -16,7 +16,7 @@ ┌─────────────────────────────────────────────────────────────────────────┐ │ 1. 意图分析 (intent_analyzer) │ │ 问题改写 · 指代消解 · 是否需要检索 · 重复提问检测 │ -│ 模型:qwen-turbo │ +│ 模型:mimo-v2.5 │ └─────────────────────────────────────────────────────────────────────────┘ │ ▼ @@ -29,31 +29,37 @@ │ └────────┬──────────┘ │ │ ▼ │ │ RRF 融合 (Reciprocal Rank Fusion) │ +│ │ │ +│ 查询缓存检查 (命中则跳过 3-7 步) │ └─────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────┐ -│ 3. 云端 Rerank (DashScope API) │ -│ 模型:qwen3-rerank │ +│ 3. 云端 Rerank │ +│ 模型:xop3qwen8breranker (讯飞云) │ +│ 结果缓存:rerank_cache (TTL=1h, 版本失效自动清除) │ └─────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────┐ -│ 4. MMR 去重 (文本 Jaccard 模式) │ -│ MMR_USE_EMBEDDING=false │ +│ 4. 救援管道 (chat_routes.py) │ +│ BM25 分歧救援 · 词法匹配救援 · 章节聚类救援 │ +│ 目的:挽救被 CrossEncoder 低估但实际相关的切片 │ └─────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────┐ -│ 5. 上下文扩展 + 图片选择 │ -│ 上下文增强 · 图文关联补充 · 懒加载 VLM 描述 │ +│ 5. 上下文构建 + 图片选择 │ +│ _order_text_contexts_for_prompt · _build_context_with_budget │ +│ select_images · VLM 相关性筛选 · CrossEncoder 精排 │ +│ 懒加载 VLM 描述 · 图片描述注入 · P0 安全网 │ └─────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────┐ -│ 6. LLM 生成 (AgenticRAG 引擎) │ -│ 模型:qwen3.6-flash(主 LLM)/ qwen-vl-plus(VLM) │ -│ 流式输出 · 引用标注 │ +│ 6. LLM 生成 │ +│ 模型:mimo-v2.5 │ +│ 流式输出 · 置信度指令 · 引用标注 · 图片后置过滤 │ └─────────────────────────────────────────────────────────────────────────┘ │ ▼ @@ -65,19 +71,26 @@ ### 1.2 模型配置一览 -| 用途 | 模型 | 说明 | -|------|------|------| -| 主 LLM | qwen3.6-flash | 回答生成、上下文理解 | -| 意图分析 | qwen-turbo | 轻量快速,用于问题改写与意图判断 | -| VLM | qwen-vl-plus | 图片理解与描述生成 | -| 云端重排序 | qwen3-rerank | DashScope API 调用,替代本地 BGE-reranker | +| 用途 | 模型 | API 端点 | 说明 | +|------|------|----------|------| +| 主 LLM | mimo-v2.5 | xiaomimimo.com | 回答生成、上下文理解 | +| 意图分析 | mimo-v2.5 | xiaomimimo.com | 问题改写与意图判断 | +| VLM | mimo-v2.5 | xiaomimimo.com | 图片理解与描述生成 | +| 云端重排序 | xop3qwen8breranker | 讯飞云 Maas API | 替代本地 BGE-reranker | +| 向量编码 | bge-base-zh-v1.5 | 本地模型 | 查询与文档 embedding | -### 1.3 v7.0.0 架构变更要点 +> 模型可通过环境变量覆盖:`DASHSCOPE_MODEL`、`INTENT_MODEL`、`VLM_MODEL`、`RERANK_CLOUD_MODEL` -- **Reranker**:从本地 BGE-reranker 切换为云端 DashScope API(qwen3-rerank) -- **MMR 去重**:使用文本相似度模式(`MMR_USE_EMBEDDING=false`),基于 Jaccard 系数 -- **Agentic 引擎**:拆分为多个子模块(agentic_search / agentic_answer / agentic_citation 等) -- **Graph RAG**:模块已清空,不再使用 +### 1.3 关键配置参数 + +| 参数 | 默认值 | 说明 | +|------|--------|------| +| `RERANK_CONTEXT_MIN_SCORE` | 0.05 | Rerank 分数低于此值的切片不送入 LLM | +| `MAX_CONTEXT_CHUNKS` | 20 | 送给 LLM 的最大文本切片数 | +| `CONTEXT_MAX_CHARS` | 8000 | 上下文最大字符数(约 4000 token) | +| `CONTEXT_SOFT_LIMIT` | 6000 | 软限制,超过后只接受高分切片组 | +| `CONFIDENCE_WARN_THRESHOLD` | 0.15 | top-3 均分低于此值时,提示 LLM 谨慎回答 | +| `CONFIDENCE_CAUTION_THRESHOLD` | 0.30 | top-3 均分低于此值时,提示 LLM 优先引用原文 | --- @@ -87,38 +100,41 @@ **入口文件**:`api/chat_routes.py` -用户通过 API 发送查询请求,由 `generate()` 函数统一调度: - ``` -POST /rag 或 POST /chat -Body: { "query": "用户问题", "kb_name": "知识库名称" } +POST /chat — 普通聊天(不检索知识库,直接 LLM 回答) +POST /rag — 知识库问答(检索 + LLM 生成) +POST /search — 纯检索(返回切片,不生成回答) ``` -`generate()` 的职责: +`/rag` 的核心函数 `rag()` 的职责: 1. 调用意图分析模块,获取改写后的查询与检索决策 -2. 若需要检索,调用混合检索管线 -3. 执行图片选择与上下文构建 -4. 调用 LLM 生成回答(流式输出) -5. 组装最终响应(回答 + 引用来源 + 图片) +2. 若需要检索,调用混合检索管线(`search_hybrid()`) +3. 执行救援管道(BM25 分歧 / 词法匹配 / 章节聚类) +4. 执行图片选择(`select_images()`) +5. 构建 LLM 上下文(排序 + 预算截断 + 图片注入) +6. 调用 LLM 生成回答(流式输出) +7. 后置图片过滤(`_filter_images_by_answer()`) +8. 组装最终响应(回答 + 引用来源 + 图片) ### 2.2 请求数据结构 ```python # 请求 { - "query": "蓄水以来逐年发电量", - "kb_name": "public_kb", + "message": "用户问题", "history": [...] # 可选:对话历史 } -# 响应 -{ - "type": "finish", - "answer": "完整回答文本", - "sources": [...], # 引用来源列表 - "images": [...] # 精选图片列表 -} +# SSE 响应事件流 +data: {"type": "intent_result", "data": {...}} +data: {"type": "chunks_retrieved", "data": {...}} +data: {"type": "images_selected", "data": {...}} +data: {"type": "context_built", "data": {...}} +data: {"type": "chunk", "content": "部分回答"} +data: {"type": "chunk", "content": "部分回答"} +... +data: {"type": "finish", "answer": "完整回答", "sources": [...], "images": [...]} ``` --- @@ -126,7 +142,7 @@ Body: { "query": "用户问题", "kb_name": "知识库名称" } ## 三、意图分析 **入口文件**:`core/intent_analyzer.py` -**使用模型**:qwen-turbo(轻量快速) +**使用模型**:mimo-v2.5 ### 3.1 核心功能 @@ -138,6 +154,8 @@ class IntentAnalysis: rewritten_query: str # 改写后的查询(指代消解、省略补全) use_context: bool # 是否使用历史上下文 need_retrieval: bool # 是否需要检索知识库 + intent: str # 意图类型:factual/comparison/reasoning/instruction + sub_queries: list # 子查询列表(对比/复杂查询拆分) ``` ### 3.2 处理逻辑 @@ -147,7 +165,9 @@ class IntentAnalysis: | 问题改写 | 指代消解("它" → 具体实体)、省略补全(补全缺失主语) | | 上下文判断 | 根据对话历史决定是否需要前文信息 | | 检索决策 | 判断是否需要查询知识库(闲聊类问题可跳过) | -| 重复提问检测 | 用户重复提问相同问题时,强制设置 `need_retrieval=true` | +| 意图分类 | 识别查询意图(事实查询/对比分析/推理/操作指导) | +| 子查询拆分 | 对比类查询拆分为多个子查询并行检索 | +| 语义缓存 | 相似度 ≥ 0.92 时复用历史意图分析结果 | ### 3.3 重复提问规则 @@ -170,9 +190,16 @@ if cached: return cached ``` +缓存层级(5 层): +1. `query_cache` — 精确查询结果缓存 +2. `embedding_cache` — embedding 向量缓存 +3. `rerank_cache` — rerank 分数缓存(TTL=1h,知识库版本变更自动失效) +4. `semantic_cache` — 语义相似查询缓存(阈值 0.92) +5. `intent_exact_cache` — 意图分析精确缓存 + ### 4.2 向量检索 -使用 embedding 模型将查询编码为向量,在 ChromaDB 中执行 ANN 检索: +使用本地 bge-base-zh-v1.5 模型将查询编码为向量,在 ChromaDB 中执行 ANN 检索: ```python query_vector = embedding_model.encode(query) @@ -211,13 +238,9 @@ fused_results = reciprocal_rank_fusion( ) ``` -**融合参数**: +### 4.5 子查询并行检索 -| 参数 | 默认值 | 说明 | -|------|--------|------| -| `recall_k` | 100 | 各通道召回候选数量 | -| `VECTOR_WEIGHT` | 0.6 | 向量检索权重 | -| `BM25_WEIGHT` | 0.4 | BM25 检索权重 | +意图分析器拆分的子查询(如对比类查询)会并行检索,结果合并后再 rerank。 --- @@ -225,204 +248,233 @@ fused_results = reciprocal_rank_fusion( ### 5.1 云端 Rerank -**v7.0.0 变更**:从本地 BGE-reranker 切换为云端 DashScope API。 - -**调用模型**:qwen3-rerank +**调用模型**:xop3qwen8breranker(讯飞云 Maas API) RRF 融合后的候选结果通过云端 Reranker 进行精排,基于查询与文档的语义相关性重新打分排序: ```python -reranked = rerank_results( - query, - fused_results, - model="qwen3-rerank", # DashScope API - top_k=15 -) +reranked = rerank_results(query, fused_results, top_k=15) ``` -### 5.2 与旧版的区别 +**重要特征**:CrossEncoder 对不同类型切片的评分分布差异显著: -| 对比项 | 旧版(本地 BGE-reranker) | v7.0.0(云端 qwen3-rerank) | -|--------|--------------------------|---------------------------| -| 部署方式 | 本地模型加载 | DashScope API 远程调用 | -| 资源占用 | 需要 GPU 显存 | 无本地资源消耗 | -| 模型能力 | BGE-reranker(较小) | qwen3-rerank(更强) | -| 延迟 | 低(本地推理) | 中等(网络往返) | +| 切片类型 | 典型分数范围 | 说明 | +|----------|------------|------| +| 文本切片 | 0.3 ~ 0.99 | 正常分布 | +| 表格切片 | 0.1 ~ 0.5 | 偏低,有专用救援逻辑 | +| 图片/图表切片 | 0.002 ~ 0.08 | 系统性偏低,需要宽松阈值 | + +### 5.2 Rerank 缓存 + +- 每个 (query, doc_id) 对的 rerank 分数独立缓存 +- TTL = 1 小时 +- 知识库版本号变更时自动失效 --- -## 六、MMR 去重 +## 六、救援管道 -### 6.1 配置 +**入口文件**:`api/chat_routes.py` +**目的**:挽救被 CrossEncoder 低估但实际相关的切片 -v7.0.0 采用文本相似度模式进行 MMR(Maximal Marginal Relevance)去重: +在 `_order_text_contexts_for_prompt` 之前执行,三个救援管道按顺序运行: -``` -MMR_USE_EMBEDDING = false -``` +### 6.1 BM25 分歧救援 `_rescue_bm25_divergence` -### 6.2 工作原理 +**触发条件**:BM25 排名 top-3 但 CrossEngineer 评分低于 min_score 的切片 -使用文本 Jaccard 相似度(而非 embedding 向量余弦相似度)来衡量候选文档之间的重复程度: +**逻辑**:BM25 是关键词精确匹配的强信号,如果 BM25 认为相关但 CE 误判,提升分数至保底值。 ```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] +# 配置 +BM25_DIVERGENCE_RESCUE_ENABLED = True +BM25_DIVERGENCE_MAX_RANK = 3 # 仅救援 BM25 rank <= 3 的切片 ``` -### 6.3 选择文本模式的原因 +### 6.2 词法匹配救援 `_rescue_lexical_match` -- **速度更快**:无需计算 embedding 向量之间的余弦相似度 -- **效果直观**:Jaccard 系数直接反映文本内容的重叠程度 -- **避免向量偏差**:embedding 模型可能对格式化内容(如图片描述)产生不准确的相似度 +**触发条件**:切片文本精确包含查询关键词(bigram 匹配率 > 35%)但 CE 评分低 + +**逻辑**:当切片文本直接包含查询中的关键词组合时,说明语义相关,CE 可能因为表述差异低估。 + +### 6.3 章节聚类救援 `_rescue_section_cluster` + +**触发条件**:同一 section 内所有切片都被 min_score 过滤("全灭 section") + +**逻辑**:如果同一章节的切片全部被过滤,但其他章节有切片通过,说明 CE 对该章节整体低估。为全灭 section 的切片分配保底分数(0.06)。 + +```python +# 配置 +SECTION_CLUSTER_RESCUE_ENABLED = True +CLUSTER_RESCUE_FLOOR = 0.06 # 略高于 RERANK_CONTEXT_MIN_SCORE=0.05 +``` + +### 6.4 表格救援 `_rescue_table_chunks` + +**触发条件**:查询涉及表格但上下文中没有表格数据(表格被预算截断) + +**逻辑**:从被截断的切片中补回表格数据。 --- ## 七、上下文构建 -### 7.1 检索结果处理 +### 7.1 切片排序与过滤 `_order_text_contexts_for_prompt` -**入口文件**:`api/chat_routes.py` +**核心逻辑**: -将检索结果转换为 LLM 可用的上下文列表: +1. **文本切片 + 表格切片** → 进入 `text_contexts` +2. **图片/图表切片** → 进入 `chart_contexts`(独立处理) +3. **min_score 过滤**: + - `text_contexts`:使用 `RERANK_CONTEXT_MIN_SCORE`(0.05) + - 表格保护:同 section 内如有切片通过阈值,table 切片保底分数为 `min_score * 0.3` + - `chart_contexts`:使用宽松阈值 `min_score * 0.5`(0.025) + - 原因:CrossEncoder 对图片描述评分系统性偏低 + - 实际内容相关性由 `select_images` 独立评分保证 +4. **合并**:`text_contexts + chart_contexts[:3]` -```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 预算构建 `_build_context_with_budget` -### 7.2 懒加载增强 +按字符预算构建上下文文本: -对没有 VLM 描述的图片切片,按需调用 VLM 生成更精准的语义描述: +1. 按 (source, section) 分组 +2. 组内按 chunk_index 排序(保持原文连续性) +3. 组间按组内最高 Rerank 分数降序 +4. 逐组加入直到达到 `CONTEXT_MAX_CHARS`(8000) +5. 超过 `CONTEXT_SOFT_LIMIT`(6000)后收紧准入 -```python -enhance_retrieved_chunks(contexts, query, kb_name) -# 对缺少 VLM 描述的图片 → 调用 qwen-vl-plus 生成描述 -``` +**特殊处理**:表格切片不受预算截断(结构化关键内容) -### 7.3 图片选择 - -**核心函数**:`select_images()` +### 7.3 图片选择 `select_images()` 从检索结果中筛选与查询最相关的图片: -**步骤一:意图检测** +**步骤一:动态预算** -| 查询类型 | 参数调整 | -|----------|----------| -| 精确图号查询("图2.3") | `MAX_IMAGES=2, MIN_SCORE=5.0` | -| 弱图片意图("发电量图") | `MAX_IMAGES=1` | -| 普通查询 | `MAX_IMAGES=2` | +| 查询类型 | MAX_IMAGES | MIN_SCORE | 检测方式 | +|----------|------------|-----------|----------| +| 精确图号查询("图2.3") | 2 | 5.0 | 正则匹配 `图\s*(\d+\.?\d*)` | +| 有图片数据(检索含 image/chart) | 5 | 2.0 | 检查检索结果 chunk_type | +| 文本引用图表 | 3 | 2.0 | 提取 top-5 文本中的图号/表号 | +| 普通查询 | 2 | 3.0 | 默认 | **步骤二:提取图表引用** -从 top 5 文本块中提取 "见图2.3"、"如表2.2" 等引用,建立图号与来源文件的映射: +从 top 5 文本块中提取 "见图2.3"、"如表2.2" 等引用,建立图号与来源文件的映射。 -```python -referenced_figures = {'2.3': {'source_file': 'xxx.pdf'}, ...} -``` - -**步骤三:图片相关性打分** - -`score_image_relevance()` 打分规则: +**步骤三:图片相关性打分 `score_image_relevance()`** | 匹配项 | 加分 | |--------|------| | 图号精确匹配(查询中有"图2.3") | +10 分 | | 表号精确匹配 | +10 分 | -| 关键词匹配("发电量"等) | +2 分/个 | -| 字符重叠 | +0.2 分/字符 | -| 章节匹配 | +1.5 分 | -| 图片类型(chart > image) | +2 / +1 分 | +| 关键词匹配(jieba 分词) | +2 分/个,上限 8 分 | +| 字符重叠 | +0.2 分/字符,上限 3 分 | +| 章节匹配 | +1.5 分/关键词 | +| 图片类型(chart) | +2 分 | +| 图片类型(image) | +1 分 | | 向量相似度 | +2 分(最高) | -| 引用匹配(需章节相关) | +8 分 | +| 引用匹配 + 章节相关 | +8 分 + 5 分 | +| VLM 相关性 < 0.3 | -3 分(VLM 描述与查询不相关) | +| VLM 相关性 ≥ 0.5 | +2 分 | +| 章节不相关 | -5 分(除非被文本引用) | -**步骤四:图文关联补充** +**步骤四:VLM 相关性筛选** -遍历 top 5 文本块中引用的图表编号,查找对应的图片切片并补充到结果中。 +对有 VLM 描述的图片,使用关键词重叠率判断描述与查询的相关性: +- 重叠率 < 0.3:降分 -3(描述与查询不相关) +- 重叠率 ≥ 0.5:加分 +2 -**步骤五:返回 top N 图片** +**步骤五:CrossEncoder 精排** + +对 `select_images` 候选用 CE 做语义精排: +- CE < 0:直接剔除(语义不相关) +- CE 0~2:保留但不加分(弱相关) +- CE > 2:加分 `min((ce - 2) / 3, 1) * 5` + +**步骤六:表格嵌入图片补充** + +处理 `images_json` 字段的表格切片,补充表格中嵌入的图片。 + +**步骤七:文本引用图片补充** + +从 top 5 文本块中提取 `referenced_images` 字段,补充未选中的关联图片。 + +**步骤八:返回 top N** ```python scored_images.sort(key=lambda x: x['score'], reverse=True) return scored_images[:MAX_IMAGES] ``` -### 7.4 构建 LLM Prompt +### 7.4 图片描述注入 -```python -# 文本上下文 -context_text = "\n\n".join([ctx['doc'] for ctx in contexts[:5]]) +**核心逻辑**:将 `select_images` 选中的图片的 `full_description` 注入到 LLM context 中。 -# 图片信息 -if selected_images: - image_info = "【可用图片】\n" + 图片描述列表 - -# 最终上下文 -enhanced_context = context_text + image_info ``` +context_text(文本切片) + + +【相关图片信息】 +【图片1】VLM 完整描述(来源:xxx 第N页) +【图片2】VLM 完整描述(来源:xxx 第N页) + + +【回答要求】回答时请简要介绍每张图片的内容和用途。 +``` + +**P0 安全网**(防止图片描述被过滤遗漏): + +1. 若 `selected_images` 非空但 `【相关图片信息】` 未生成 → 补注入所有选中图片描述 +2. 若已有 `【相关图片信息】` 但遗漏部分图片 → 追加遗漏的描述 +3. DEV 模式日志:检查所有选中图片的 ID 是否出现在 context 中 + +### 7.5 置信度指令 + +根据 top-3 切片的平均 Rerank 分数注入不同的回答指令: + +| 置信度范围 | 指令 | +|-----------|------| +| < 0.15 | "参考资料与问题的相关性较低,仅基于明确信息回答,不足则说明" | +| 0.15 ~ 0.30 | "参考资料的相关性一般,优先引用资料原文,避免推测" | +| ≥ 0.30 | 无额外指令 | --- ## 八、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 流式生成 +### 8.1 流式生成 使用 SSE(Server-Sent Events)实现流式输出: ```python -for token in engine.generate_answer_stream(query, enhanced_context): +for token in engine.generate_answer_stream(query, enhanced_context, history): yield token # 逐 token 推送给前端 ``` -**主 LLM 模型**:qwen3.6-flash -**VLM 模型**:qwen-vl-plus(处理图片理解任务) +**LLM 模型**:mimo-v2.5 -### 8.3 回答结构 +### 8.2 System Prompt + +``` +你是一个严谨的知识库问答助手。 +你必须且只能根据用户提供的【参考资料】回答问题。 +如果参考资料中有答案,必须引用对应内容回答,并在回答末尾标注引用编号。 +如果参考资料中确实没有相关信息,简短说明即可,不要编造或补充资料外的内容。 +禁止使用参考资料以外的知识进行补充或推测。 +【重要-表格处理规则】当用户询问表格时,必须将 Markdown 表格原样输出... +``` + +### 8.3 图片后置过滤 `_filter_images_by_answer` + +LLM 生成回答后,根据回答内容对图片做最终过滤: + +1. **候选 ≤ 1 张**:直接返回 +2. **无图意图早退**:回答和查询都不含图片引用词 → 返回空 +3. **图号精确豁免**:图片描述包含回答引用的具体图号/表号 → 无条件保留 +4. **主题一致性**:合并查询+回答关键词,图片描述重叠 ≥ 阈值才保留 + - 子章节惩罚:图片子章节与主要检索章节不一致时,阈值 +2 +5. **兜底**:过滤后为空且有图片意图 → 保留分数最高的 1 张 + +### 8.4 回答结构 ```python { @@ -443,7 +495,8 @@ for token in engine.generate_answer_stream(query, enhanced_context): "type": "chart", "source": "三峡公报_2022.pdf", "page": 12, - "description": "图2.3 柱状图:2003-2022年逐年发电量" + "description": "图2.3 柱状图:2003-2022年逐年发电量", + "full_description": "...完整 VLM 描述..." } ] } @@ -455,13 +508,10 @@ for token in engine.generate_answer_stream(query, enhanced_context): ### 9.1 引用标注机制 -`agentic_citation.py` 负责在回答中插入引用标记,将回答内容与知识库来源关联: +`_attach_citations()` 负责在回答中插入引用标记,将回答内容与知识库来源关联: ``` -根据统计数据[1],2022年三峡电站年度发电量为787.90亿千瓦时[2]。 - -[1] 来源:三峡公报_2022.pdf,第12页,综述 > 2.3 发电 -[2] 来源:三峡公报_2022.pdf,第15页,表2.1 +根据统计数据[ref:三峡公报_text_24],2022年三峡电站年度发电量为787.90亿千瓦时[ref:三峡公报_table_5]。 ``` ### 9.2 引用数据来源 @@ -476,10 +526,6 @@ for token in engine.generate_answer_stream(query, enhanced_context): | `chunk_id` | 切片唯一标识 | | `chunk_type` | 类型(text / table / image / chart) | -### 9.3 前端引用跳转 - -前端解析回答中的引用标记(如 `[1]`),渲染为可点击的链接,点击后跳转到对应的来源文件或页面。 - --- ## 十、文档入库流程(补充参考) @@ -510,28 +556,7 @@ MinerU 解析 PDF/Word/Excel 文件,输出结构化内容: | `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 切片入库 +### 10.2 切片入库 **入口文件**:`knowledge/manager.py` **核心函数**:`add_file_to_kb()` @@ -547,63 +572,95 @@ MinerUChunk 列表 ``` **图片描述策略**: -1. 优先使用 VLM 缓存描述(语义更丰富,由 qwen-vl-plus 生成) +1. 优先使用 VLM 缓存描述(由 mimo-v2.5 生成,语义更丰富) 2. 若无缓存,生成轻量描述(基于文件名 + 章节路径 + 上下文) +3. VLM 描述异步懒加载:首次检索命中时后台生成,下次查询即可使用 -轻量描述示例: -``` -图表:图2.3,位于「综述 > 2.3发电」,第12页 -前文:受长江流域性严重枯水影响,2022年三峡电站年度发电量为787.90亿千瓦时... -后文:2.4航运 三峡船闸和葛洲坝船闸实行统一调度... -``` - -VLM 描述示例(更精准): -``` -图2.3 柱状图 主要内容描述:该柱状图展示了2003年至2022年每年的发电量(单位:亿千瓦时)。 -发电量在2003年为86.07亿千瓦时,随后逐年波动上升,至2020年达到峰值1118.02亿千瓦时... -``` - -### 10.4 ChromaDB 存储结构 +### 10.3 ChromaDB 存储结构 | 字段 | 类型 | 说明 | |------|------|------| | `ids` | str | 切片唯一标识,如 `doc.pdf_text_24` | | `embeddings` | List[float] | 向量表示 | -| `documents` | str | 切片内容(文本/描述/摘要) | +| `documents` | str | 切片内容(文本/VLM 描述/摘要) | | `metadatas` | dict | 元数据(见下表) | -**图片切片 metadata 示例**: +**图片切片 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 柱状图 主要内容...' + 'source': '三峡公报_2022.pdf', # 源文件名 + 'page': 12, # 页码 + 'chunk_type': 'chart', # 切片类型 + 'section': '综述 > 2.3发电', # 章节路径 + 'image_path': 'ab77281e7913.jpg', # 图片文件名 + 'has_vlm_desc': True, # 是否有 VLM 描述 + 'vlm_desc': '图2.3 柱状图...', # VLM 描述内容 + 'chunk_id': '三峡公报_chart_5', # 切片唯一标识 + 'version': 'v10', # 知识库版本号 + 'status': 'active', # 状态 } ``` +> **注意**:ChromaDB metadata 中**没有** `full_description` 字段。图片的完整描述存储在 `vlm_desc` 字段或 `documents` 字段中。`full_description` 仅在 `select_images()` 运行时动态计算。 + +### 10.4 懒加载增强 + +**入口文件**:`knowledge/lazy_enhance.py` + +对检索命中但没有 VLM 描述的图片切片,在后台线程异步调用 VLM 生成描述: + +```python +# 检索命中 → 后台线程调用 VLM → 写入文件缓存 + ChromaDB +enhance_retrieved_chunks(contexts, query, kb_name, defer_chromadb=True) +``` + +- **image/chart 切片**:更新 `ctx['doc']` 字段 +- **table 切片**:更新 `ctx['image_description']` 字段 +- **缓存目录**:`.data/cache/vlm/`、`.data/cache/llm/` + +--- + +## 十一、缓存系统 + +### 11.1 缓存层级 + +| 层级 | 类 | 用途 | 容量 | TTL | +|------|-----|------|------|-----| +| query_cache | LRUCache | 精确查询结果缓存 | 500 | 无 | +| embedding_cache | LRUCache | 查询 embedding 向量 | 500 | 无 | +| rerank_cache | TTLCache | Rerank 分数缓存 | 500 | 1h | +| semantic_cache | SemanticCache | 语义相似查询缓存 | 200 | 无 | +| intent_exact_cache | LRUCache | 意图分析精确缓存 | 200 | 无 | + +### 11.2 缓存失效 + +- **版本失效**:知识库版本号变更时,query_cache 和 rerank_cache 自动失效 +- **手动清除**:`POST /cache/clear`(DEV 模式) + +### 11.3 缓存 API + +``` +GET /cache/stats — 查看缓存统计 +POST /cache/clear — 清除所有缓存 +``` + --- -## 十一、关键文件索引 +## 十二、关键文件索引 | 文件 | 职责 | 关键函数/类 | |------|------|-------------| -| `api/chat_routes.py` | API 路由与请求调度 | `generate()`, `select_images()`, `score_image_relevance()` | +| `api/chat_routes.py` | API 路由与请求调度 | `rag()`, `select_images()`, `score_image_relevance()`, `_filter_images_by_answer()`, `_order_text_contexts_for_prompt()`, `_build_context_with_budget()` | +| `core/engine.py` | 检索引擎核心 | `search_knowledge()`, `rerank_results()`, `generate_answer_stream()` | | `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` | 元数据管理 | 知识库信息、检索统计 | +| `core/bm25_index.py` | BM25 关键词检索 | `BM25Index.search()` | +| `core/mmr.py` | MMR 去重 | `mmr_rerank()` | +| `core/cache.py` | 缓存管理 | `RAGCacheManager`, `get_cache_manager()` | +| `core/semantic_cache.py` | 语义缓存 | `SemanticCache` | +| `core/chunker.py` | 语义分块器 | `SemanticChunker` | +| `core/llm_utils.py` | LLM 调用工具 | `call_llm()`, `call_llm_stream()` | | `parsers/mineru_parser.py` | 文档解析 | `parse_with_mineru()`, `MinerUChunk` | -| `knowledge/manager.py` | 知识库管理 | `add_file_to_kb()`, `generate_lightweight_image_description()` | +| `knowledge/manager.py` | 知识库管理 | `add_file_to_kb()`, `KBManager` | | `knowledge/lazy_enhance.py` | 懒加载增强 | `lazy_vlm_description()`, `enhance_retrieved_chunks()` | +| `config.py` | 配置集中管理 | 模型/参数/开关 | diff --git a/docs/RAG系统完整指南.md b/docs/RAG系统完整指南.md index e4b7913..fd5cc3f 100644 --- a/docs/RAG系统完整指南.md +++ b/docs/RAG系统完整指南.md @@ -1,10 +1,10 @@ # RAG 系统完整指南 -> **版本**: v4.0(统一编排 + 四层缓存修复) +> **版本**: v4.1(模型切换 + 图片检索修复) > **生产入口**: `api/chat_routes.py::rag()` → `generate()` → `core/engine.py` -> **最后更新**: 2026-06-05 +> **最后更新**: 2026-06-21 > -> 本次更新:删除未使用的 AgenticRAG 备用编排路径(10 个文件 ~2050 行),修复 Query Cache 键不匹配与阈值问题,将语义缓存集成至生产 `/rag` 端点。 +> 本次更新:LLM/意图/VLM 模型统一切换至 mimo-v2.5(xiaomimimo.com API),Reranker 切换至 xop3qwen8breranker(讯飞云 API),新增 chart_contexts 降门槛、P0 安全网、答案后过滤等图片检索修复逻辑。 ## 一、功能概述 @@ -14,12 +14,13 @@ |------|------|----------| | **意图分析** | LLM 驱动的双层判断(是否需要检索)+ 查询改写 | `core/intent_analyzer.py` | | **混合检索** | 向量检索 + BM25 + RRF 融合 + Rerank 重排 | `core/engine.py` | -| **四层缓存** | Query + Embedding + Rerank(LRU)+ 语义缓存(FAISS) | `core/cache.py` + `core/semantic_cache.py` | +| **五层缓存** | Query + Embedding + Rerank(LRU)+ 语义缓存(FAISS)+ ChromaDB 元数据缓存 | `core/cache.py` + `core/semantic_cache.py` | | **流式生成** | SSE 流式答案输出,逐 token 推送 | `core/engine.py::generate_answer_stream()` | | **引用标注** | 自动标注信息来源和引用编号 | `api/chat_routes.py::_attach_citations()` | -| **富媒体** | 图片/表格的智能提取与展示 | `api/chat_routes.py` | +| **富媒体** | 图片/表格的智能提取与展示 + P0 安全网 + 答案后过滤 | `api/chat_routes.py` | | **查询理解** | 查询分解、扩展、MMR 去重、自适应 TopK | `core/` 各独立模块 | | **安全护栏** | 敏感信息过滤、Prompt 安全守卫 | `api/response_utils.py`、`core/prompt_guard.py` | +| **救援管线** | BM25 散度救援、词法匹配救援、章节聚类救援、表格救援 | `core/engine.py` | --- @@ -78,9 +79,12 @@ │ │ FAQ 加权 │→ │ 黑名单过滤 │→ │ 时间衰减 │ │ │ └───────────┘ └──────────────┘ └──────┬───────┘ │ │ ↓ │ -│ ┌───────────────┐ ┌──────────────┐ │ -│ │ 上下文扩展 │→ │ 自适应 TopK │ │ -│ └───────────────┘ └──────────────┘ │ +│ ┌───────────────┐ ┌──────────────┐ ┌──────────────┐ │ +│ │ 上下文扩展 │→ │ 自适应 TopK │→ │ 救援管线 │ │ +│ └───────────────┘ └──────────────┘ │ (BM25散度/ │ │ +│ │ 词法/章节/ │ │ +│ │ 表格救援) │ │ +│ └──────────────┘ │ └─────────────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────────────┐ @@ -105,7 +109,7 @@ --- -## 三、四层缓存架构 +## 三、五层缓存架构 ### 3.1 缓存层次概览 @@ -115,6 +119,7 @@ | L2 | Embedding Cache | LRU (OrderedDict) | 2000 条 | 24 小时 | 缓存向量化结果,避免重复调用 embedding 模型 | | L3 | Rerank Cache | LRU (OrderedDict) | 1000 条 | 1 小时 | 缓存 Rerank 分数,避免重复调用 Reranker | | L4 | Semantic Cache | FAISS IndexFlatIP | 10000 条 | 无过期 | 语义级缓存,相似查询也能命中 | +| L5 | ChromaDB 元数据缓存 | 进程内 dict | 无限制 | 无过期 | 缓存 ChromaDB Collection 元数据(kb_version 等),避免频繁查询 ChromaDB | ### 3.2 Query Cache @@ -288,9 +293,9 @@ search_knowledge(query, top_k=30) │ ├─ 7. 章节过滤(查询提到章节时优先匹配) │ - ├─ 8. ★ Rerank 重排 ★(云端 DashScope qwen3-rerank 或本地 BGE) - │ └─ rerank_results(query, results, top_k) - │ └─ 由 RERANK_BACKEND 控制(local/cloud/fallback) +├─ 8. ★ Rerank 重排 ★(云端讯飞 xop3qwen8breranker 或本地 BGE) +│ └─ rerank_results(query, results, top_k) +│ └─ 由 RERANK_BACKEND 控制(local/cloud/fallback) │ ├─ 9. MMR 去重 │ ├─ 语义向量版(MMR_USE_EMBEDDING=True) @@ -306,7 +311,9 @@ search_knowledge(query, top_k=30) │ ├─ 14. 自适应 TopK(根据置信度调整返回数量) │ - └─ 15. 缓存写入 → 返回结果 + ├─ 15. 救援管线(BM25散度/词法匹配/章节聚类/表格救援) + │ + └─ 16. 缓存写入 → 返回结果 ``` ### 5.2 混合检索代码示例 @@ -330,7 +337,7 @@ image_results = collection.query( # RRF 融合(动态权重) fused = reciprocal_rank_fusion([vector_results, bm25_results], weights=[vector_w, bm25_w]) -# Rerank 重排(云端 DashScope qwen3-rerank 或本地 BGE) +# Rerank 重排(云端讯飞 xop3qwen8breranker 或本地 BGE) reranked = rerank_results(query, fused, top_k=15) # MMR 去重 @@ -359,7 +366,7 @@ RRF分数 = Σ (权重 / (k + 排名位置)) | RERANK_BACKEND | 说明 | |----------------|------| -| `"cloud"` | 仅使用云端 DashScope `qwen3-rerank` API | +| `"cloud"` | 仅使用云端讯飞 `xop3qwen8breranker` API | | `"local"` | 仅使用本地 `BAAI/bge-reranker-base`(CrossEncoder / ONNX) | | `"fallback"` | 优先云端,失败时自动回退本地(推荐生产环境) | @@ -368,13 +375,13 @@ RRF分数 = Σ (权重 / (k + 排名位置)) ```python # config.py RERANK_BACKEND = os.getenv("RERANK_BACKEND", "local") -RERANK_CLOUD_MODEL = "qwen3-rerank" +RERANK_CLOUD_MODEL = "xop3qwen8breranker" RERANK_CLOUD_API_KEY = os.getenv("RERANK_CLOUD_API_KEY", DASHSCOPE_API_KEY) -RERANK_CLOUD_BASE_URL = "https://dashscope.aliyuncs.com/compatible-api/v1/reranks" +RERANK_CLOUD_BASE_URL = "https://maas-api.cn-huabei-1.xf-yun.com/v1/rerank" RERANK_CLOUD_TIMEOUT = 15 ``` -`CloudReranker` 类(`core/engine.py`)封装 DashScope 的 `/compatible-api/v1/reranks` 接口,提供与本地 `CrossEncoder.predict()` / `ONNXReranker.predict()` 一致的调用接口。 +`CloudReranker` 类(`core/engine.py`)封装讯飞云的 `/v1/rerank` 接口,提供与本地 `CrossEncoder.predict()` / `ONNXReranker.predict()` 一致的调用接口。 **本地 Reranker(备选)**: @@ -424,12 +431,16 @@ POST /rag (SSE 流式) ├─ 5. 图片补充检索 + 图片打分选择 (select_images) │ └─[DEV] 发 SSE: images_selected ├─ 6. 构建上下文 (_order_texts_for_prompt) + │ └─ chart_contexts 使用 min_score * 0.5 降门槛 │ └─[DEV] 发 SSE: context_built │ + ├─ 6.5. P0 安全网 — 确保 selected_images 描述完整进入 LLM 上下文 + │ ├─ 7. 流式答案生成 engine.generate_answer_stream() │ └─ 逐 token 发 SSE: chunk │ ├─ 8. 答案图号对齐过滤 + ├─ 8.5. 答案后过滤 (_filter_images_by_answer) — 根据 LLM 答案过滤不相关图片 ├─ 9. 引用标注 _attach_citations() ├─ 10. 敏感信息过滤 filter_response() ├─ 11. 语义缓存写入 SemanticCache.set() # 写入缓存供后续命中 @@ -454,6 +465,76 @@ POST /rag (SSE 流式) > 标注 [DEV] 的事件仅在 `IS_DEV=True` 时发送。 +### 6.1 图片检索子系统 + +图片检索是独立于文本上下文管线的子系统,存在特有的"两管线断裂"问题,已通过多重修复保障完整性。 + +**图片检索流程**: + +``` +search_knowledge() 返回混合检索结果 + ↓ +┌──────────────────────────────────────────────────────────────┐ +│ _order_text_contexts_for_prompt() — 上下文提取与构建 │ +│ │ +│ 1. 分离 text_contexts 和 chart_contexts │ +│ 2. chart_contexts 使用 min_score * 0.5 降门槛过滤 │ +│ (CrossEncoder 对图片打分系统性偏低:0.002-0.08) │ +│ 3. chart_contexts 的描述注入 context_text 【相关图片信息】 │ +│ 4. text_contexts 正常注入 context_text │ +└──────────────────────────────────────────────────────────────┘ + ↓ ↓ +┌──────────────────┐ ┌──────────────────────────────────────┐ +│ select_images() │ │ context_text 送入 LLM │ +│ 独立图片选择 │ │ (可能不包含所有选中图片的描述) │ +│ P1: BM25/向量 │ └──────────────────────────────────────┘ +│ P2: VLM 检查 │ +│ P3: CE 排名 │ +│ P4: 多样性去重 │ +└────────┬─────────┘ + ↓ +┌──────────────────────────────────────────────────────────────┐ +│ P0 安全网 — 保证 selected_images 描述完整进入 LLM 上下文│ +│ │ +│ 检查 context_text 中的 【相关图片信息】 部分, │ +│ 若 selected_images 的描述不在 context_text 中, │ +│ 强制追加缺失的描述。 │ +│ (解决两管线断裂:图片被选中但描述被 min_score 过滤掉) │ +└──────────────────────────────────────────────────────────────┘ + ↓ +┌──────────────────────────────────────────────────────────────┐ +│ _filter_images_by_answer() — 答案后过滤 │ +│ │ +│ LLM 生成答案后,根据答案内容过滤不相关图片。 │ +│ ⚠️ 鸡生蛋问题:若 LLM 因上下文缺失而回答"未找到", │ +│ 会导致正确图片被误过滤。P0 安全网可缓解此问题。 │ +└──────────────────────────────────────────────────────────────┘ +``` + +**select_images 四阶段选择**: + +| 阶段 | 方法 | 说明 | +|------|------|------| +| P1 | BM25/向量召回 | 从检索结果中筛选 chunk_type 为 image/chart 的候选 | +| P2 | VLM 相关性检查 | `_check_vlm_relevance()` 判断图片与查询的相关性,不相关者 -3.0 惩罚 | +| P3 | CrossEncoder 排名 | CE<0 移除,CE 0~2 保留无加成,CE>2 加分 | +| P4 | 多样性去重 | 同文档多图片按分数保留 TopN,避免单一文档图片垄断 | + +**CrossEncoder 图片评分特征**:CrossEncoder 对图片/图表的打分系统性低于文本(0.002-0.08 vs 0.3-0.9),因此 `chart_contexts` 使用 `min_score * 0.5` 的降门槛策略。 + +**VLM 相关性检查**:`_check_vlm_relevance()` 使用 VLM 模型判断图片内容与查询的相关性,阈值 0.3,不相关图片会被施加 -3.0 的分数惩罚。 + +### 6.2 救援管线 + +当常规检索召回不足时,以下救援机制依次尝试补充结果: + +| 救援类型 | 触发条件 | 策略 | +|----------|----------|------| +| BM25 散度救援 | 向量与 BM25 结果差异大 | 补充 BM25 独有但向量遗漏的高分结果 | +| 词法匹配救援 | 专有名词/术语精确匹配 | 对查询中的关键术语做精确匹配补充 | +| 章节聚类救援 | 同章节切片被部分召回 | 补充同章节内相邻切片(rerank 后、扩展前) | +| 表格救援 | 表格数据被碎片化 | 对 table 类型切片做整表补充 | + --- ## 七、API 调用方式 @@ -530,14 +611,24 @@ curl http://localhost:5001/cache/stats \ ```python # config.py -DASHSCOPE_API_KEY = "your-api-key" # 通义千问 API -DASHSCOPE_BASE_URL = "https://dashscope.aliyuncs.com/compatible-mode/v1" -DASHSCOPE_MODEL = "qwen3.6-flash" # 文本生成模型(主力 LLM) -INTENT_MODEL = "qwen-turbo" # 意图分析模型(轻量快速) -VLM_MODEL = "qwen-vl-plus" # 视觉语言模型(图片描述) -RAG_CHAT_MODEL = "qwen3.6-flash" # RAG 对话模型 +DASHSCOPE_API_KEY = "your-api-key" # mimo API 密钥 +DASHSCOPE_BASE_URL = "https://token-plan-cn.xiaomimimo.com/v1" +DASHSCOPE_MODEL = "mimo-v2.5" # 文本生成模型(主力 LLM) +INTENT_MODEL = "mimo-v2.5" # 意图分析模型(轻量快速) +VLM_MODEL = "mimo-v2.5" # 视觉语言模型(图片描述) +RAG_CHAT_MODEL = "mimo-v2.5" # RAG 对话模型 + +# Embedding 模型(本地) +EMBEDDING_MODEL_PATH = "models/bge-base-zh-v1.5" + +# Reranker 模型 +RERANK_MODEL_PATH = "models/bge-reranker-base" # 本地备选 +RERANK_CLOUD_MODEL = "xop3qwen8breranker" # 云端(讯飞云) +RERANK_CLOUD_BASE_URL = "https://maas-api.cn-huabei-1.xf-yun.com/v1/rerank" ``` +> **注意**:百炼 API(DashScope)额度用尽后,所有 LLM 调用已统一切换至 mimo API(xiaomimimo.com)。`get_intent_client()` 也使用 `DASHSCOPE_API_KEY` / `DASHSCOPE_BASE_URL`(即 mimo API),不再使用百炼 API。 + ### 8.2 检索参数 ```python @@ -553,7 +644,7 @@ RECALL_MULTIPLIER = 3 # 候选池最小倍数 # 重排序 USE_RERANK = True # 启用重排序 RERANK_BACKEND = "local" # "local"=本地模型, "cloud"=云端API, "fallback"=优先云端失败回退本地 -RERANK_CLOUD_MODEL = "qwen3-rerank" +RERANK_CLOUD_MODEL = "xop3qwen8breranker" RERANK_CANDIDATES = 20 # 送入重排序的候选数 RERANK_TOP_K = 15 # 重排序后保留数 RERANK_USE_ONNX = True # ONNX 加速(仅本地模式,环境变量控制) @@ -701,10 +792,10 @@ config/ # 运行时配置 | 检索方式 | 单一向量检索 | 向量 + BM25 + FAQ + 图片独立召回 | | 融合算法 | 无 | RRF 动态权重融合 | | 去重 | 无 | MMR 语义去重 | -| 重排序 | 无 | 云端 qwen3-rerank API(支持本地 BGE 回退) | +| 重排序 | 无 | 云端 xop3qwen8breranker API(讯飞云,支持本地 BGE 回退) | | 问题分解 | 无 | 自动拆分对比/推理类查询 | | 闲聊处理 | 无 | 意图分析自动判断 | -| 缓存体系 | 无 | 四层缓存(Query + Embedding + Rerank + 语义缓存) | +| 缓存体系 | 无 | 五层缓存(Query + Embedding + Rerank + 语义缓存 + 元数据缓存) | | 语义缓存 | 无 | FAISS 向量索引,相似查询复用(92x 加速) | | 自适应 TopK | 固定 top_k | 根据置信度动态调整 | | 上下文理解 | 无 | 多轮对话 + 历史上下文 | @@ -736,9 +827,9 @@ Rerank 在生产流程中有 **一个调用路径**: |--------|--------|------| | `USE_RERANK` | `True` | 总开关 | | `RERANK_BACKEND` | `"local"` | 后端选择 | -| `RERANK_CLOUD_MODEL` | `"qwen3-rerank"` | 云端模型名称 | +| `RERANK_CLOUD_MODEL` | `"xop3qwen8breranker"` | 云端模型名称 | | `RERANK_CLOUD_API_KEY` | 同 `DASHSCOPE_API_KEY` | 云端 API 密钥 | -| `RERANK_CLOUD_BASE_URL` | `https://dashscope.aliyuncs.com/compatible-api/v1/reranks` | 云端 API 地址 | +| `RERANK_CLOUD_BASE_URL` | `https://maas-api.cn-huabei-1.xf-yun.com/v1/rerank` | 云端 API 地址(讯飞云) | | `RERANK_CLOUD_TIMEOUT` | `15` | 云端请求超时(秒) | | `RERANK_MODEL_PATH` | `models/bge-reranker-base` | 本地模型路径 | | `RERANK_CANDIDATES` | `20` | 送入 Rerank 的候选数 | @@ -807,6 +898,26 @@ print({"hits": sc.hits, "misses": sc.misses, "total": sc.total_entries}) ## 十三、演进记录 +### v4.1(2026-06-21)— 模型切换 + 图片检索修复 + +**模型统一切换**:百炼 API(DashScope)额度用尽后,所有 LLM 调用统一切换至 mimo API(xiaomimimo.com): +- `DASHSCOPE_MODEL` / `RAG_CHAT_MODEL` / `INTENT_MODEL` / `VLM_MODEL`:`qwen3.6-flash` / `qwen-turbo` / `qwen-vl-plus` → `mimo-v2.5` +- `DASHSCOPE_BASE_URL`:`dashscope.aliyuncs.com` → `token-plan-cn.xiaomimimo.com/v1` +- `get_intent_client()`:从百炼 API 切换至 mimo API + +**Reranker 切换**: +- `RERANK_CLOUD_MODEL`:`qwen3-rerank` → `xop3qwen8breranker`(讯飞云) +- `RERANK_CLOUD_BASE_URL`:`dashscope.aliyuncs.com` → `maas-api.cn-huabei-1.xf-yun.com/v1/rerank` + +**图片检索修复**(P0 级): +1. **chart_contexts 降门槛**:CrossEncoder 对图片/图表打分系统性偏低(0.002-0.08 vs 文本 0.3-0.9),原 `min_score=0.05` 过滤掉几乎所有图表。修复为 `min_score * 0.5 = 0.025`。 +2. **P0 安全网**:`select_images` 独立于文本上下文管线选择图片,但图片描述可能因 min_score 过滤未进入 LLM 上下文。新增安全网检查:若 `selected_images` 的描述未出现在 `context_text` 中,强制注入。 +3. **答案后过滤**(`_filter_images_by_answer`):LLM 生成答案后,根据答案内容过滤不相关图片。注意:若 LLM 因上下文缺失而回答"未找到",会导致正确图片被误过滤(鸡生蛋问题)。 + +**救援管线**:新增 BM25 散度救援、词法匹配救援、章节聚类救援、表格救援等机制,确保低召回场景下的结果覆盖。 + +**缓存层扩展**:从四层缓存扩展为五层,新增 ChromaDB 元数据缓存(L5),避免频繁查询 ChromaDB 获取 kb_version 等元数据。 + ### v4.0(2026-06-05)— 统一编排 + 四层缓存修复 **删除未使用的备用编排路径**:移除了 `core/agentic.py` 及 8 个 Mixin 文件(共 10 个文件 ~2050 行)。这些文件实现了完整的决策循环编排(含置信度门控、质量评估、推理反思等),但从未接入任何 HTTP 路由。 @@ -820,7 +931,7 @@ print({"hits": sc.hits, "misses": sc.misses, "total": sc.total_entries}) ### v3.2 — 模型/Reranker/管线更新 -引入云端 qwen3-rerank、ONNX 加速、动态 RRF 权重等。 +引入云端 Reranker(现 xop3qwen8breranker/讯飞云,原 qwen3-rerank/DashScope)、ONNX 加速、动态 RRF 权重等。 --- @@ -828,7 +939,7 @@ print({"hits": sc.hits, "misses": sc.misses, "total": sc.total_entries}) ### Redis 缓存外部化 -当前四层缓存均为进程内内存存储,在多 Worker / 多实例部署时无法共享。已规划 Redis 迁移方案(详见 `reports/redis_migration_plan.md`),核心设计: +当前五层缓存均为进程内内存存储(L5 ChromaDB 元数据缓存除外),在多 Worker / 多实例部署时无法共享。已规划 Redis 迁移方案(详见 `reports/redis_migration_plan.md`),核心设计: - `RedisCacheManager` 提供与 `RAGCacheManager` 相同的接口 - 通过 `REDIS_CACHE_URL` 环境变量启用,向后兼容 diff --git a/docs/curl测试手册.md b/docs/curl测试手册.md index ab32948..40d3ff6 100644 --- a/docs/curl测试手册.md +++ b/docs/curl测试手册.md @@ -4,7 +4,7 @@ > > 测试日期:2026-06-10(最近更新)| 生产服务器:`47.116.16.222` | 服务地址:`http://127.0.0.1:5001` > -> 当前部署模型:`qwen-turbo`(DashScope)| 嵌入模型:`bge-base-zh-v1.5`(本地 CPU)| Rerank:`qwen3-rerank`(DashScope 云端 API) +> 当前部署模型:`mimo-v2.5`(xiaomimimo.com API)| 嵌入模型:`bge-base-zh-v1.5`(本地 CPU)| Rerank:`xop3qwen8breranker`(讯飞云 API) ## 目录 @@ -2106,7 +2106,7 @@ curl -s -X POST "http://localhost:5001/collections/public_kb/reindex" | 优化项 | 配置 | 效果 | |--------|------|------| -| Reranker 云端化 | `RERANK_BACKEND=cloud`(qwen3-rerank) | 搜索阶段从 33-48s 降至 ~0.3s | +| Reranker 云端化 | `RERANK_BACKEND=cloud`(xop3qwen8breranker/讯飞云) | 搜索阶段从 33-48s 降至 ~0.3s | | MMR 文本相似度 | `MMR_USE_EMBEDDING=false` | MMR 阶段从 ~36s 降至 ~0s | ### 耗时拆解(优化后 /rag 问答) @@ -2126,7 +2126,7 @@ curl -s -X POST "http://localhost:5001/collections/public_kb/reindex" └── 流式 token 生成 ~11s ``` -> **注**:LLM 生成阶段耗时取决于 qwen-turbo API 响应速度,非本地可优化。当前已使用 qwen-turbo(最快模型),如需进一步压缩可考虑减少检索切片数量或缩短 prompt。 +> **注**:LLM 生成阶段耗时取决于 mimo-v2.5 API 响应速度,非本地可优化。如需进一步压缩可考虑减少检索切片数量或缩短 prompt。 --- @@ -2174,14 +2174,14 @@ curl -s -X POST http://127.0.0.1:5001/collections//reindex ### Reranker 配置 -生产环境使用 DashScope 云端 Reranker 替代本地 CPU 推理,显著提升检索速度: +生产环境使用讯飞云 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 兼容端点 | +| `RERANK_CLOUD_MODEL` | `xop3qwen8breranker` | 讯飞云排序模型 | +| `RERANK_CLOUD_API_KEY` | `sk-*` | API Key | +| `RERANK_CLOUD_BASE_URL` | `https://maas-api.cn-huabei-1.xf-yun.com/v1/rerank` | 讯飞云 Rerank 端点 | | `MMR_USE_EMBEDDING` | `false` | MMR 使用文本相似度(零额外计算) | > **fallback 模式**:`RERANK_BACKEND=fallback` 优先使用云端 API,如果云端不可用则自动回退到本地模型,适合生产环境高可用场景。 diff --git a/docs/开发与系统模块说明.md b/docs/开发与系统模块说明.md index b699923..eb6fcac 100644 --- a/docs/开发与系统模块说明.md +++ b/docs/开发与系统模块说明.md @@ -312,11 +312,11 @@ pip install -r requirements.txt ```python # config.py - 必需配置 -# 通义千问 API(必需) +# LLM API(必需) DASHSCOPE_API_KEY = "your-api-key" -DASHSCOPE_BASE_URL = "https://dashscope.aliyuncs.com/compatible-mode/v1" -DASHSCOPE_MODEL = "qwen-flash" # 文本模型 -DASHSCOPE_VL_MODEL = "qwen-vl-plus" # 视觉模型(图片描述) +DASHSCOPE_BASE_URL = "https://token-plan-cn.xiaomimimo.com/v1" +DASHSCOPE_MODEL = "mimo-v2.5" # 文本模型 +VLM_MODEL = "mimo-v2.5" # 视觉模型(图片描述) # 兼容变量 API_KEY = DASHSCOPE_API_KEY diff --git a/docs/测试指南.md b/docs/测试指南.md index 984d85a..9c22680 100644 --- a/docs/测试指南.md +++ b/docs/测试指南.md @@ -84,9 +84,9 @@ ls models/bge-base-zh-v1.5/ ```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" # 意图分析模型(轻量、确定性高) +DASHSCOPE_BASE_URL = "https://token-plan-cn.xiaomimimo.com/v1" +DASHSCOPE_MODEL = "mimo-v2.5" # 主 LLM(文本生成 / RAG 对话) +INTENT_MODEL = "mimo-v2.5" # 意图分析模型 ``` > **注意**: Graph RAG(Neo4j)功能已废弃,相关配置(NEO4J_URI、USE_GRAPH_RAG 等)已移除。