 RAG 检索流程（从用户输入到回答生成）

  ┌─────────────────────────────────────────────────────────────────────────┐
  │                           1. 用户输入问题                                 │
  │                           "蓄水以来逐年发电量"                            │
  └─────────────────────────────────────────────────────────────────────────┘
                                      │
                                      ▼
  ┌─────────────────────────────────────────────────────────────────────────┐
  │  2. 意图分析 (core/intent_analyzer.py)                                   │
  │  ├── 问题改写：指代消解、省略补全                                         │
  │  ├── use_context：是否使用历史上下文                                      │
  │  ├── need_retrieval：是否需要检索知识库                                   │
  │  └── 重复提问强制检索（新增规则）                                          │
  └─────────────────────────────────────────────────────────────────────────┘
                                      │
                                      ▼
  ┌─────────────────────────────────────────────────────────────────────────┐
  │  3. 混合检索 (core/engine.py - search_knowledge)                         │
  │                                                                          │
  │  3.1 向量检索                                                            │
  │      query_vector = embedding_model.encode(query)                       │
  │      text_results = collection.query(query_embeddings, n_results=100)   │
  │                                                                          │
  │  3.2 图片独立召回（P0 新增）                                              │
  │      image_results = collection.query(                                  │
  │          where={'chunk_type': {'$in': ['image', 'chart', 'table']}},    │
  │          n_results=5  # 独立控制图片数量                                 │
  │      )                                                                   │
  │                                                                          │
  │  3.3 BM25 关键词检索（可选）                                              │
  │      bm25_results = bm25_index.search(query, top_k=100)                 │
  │                                                                          │
  │  3.4 RRF 融合                                                            │
  │      fused_results = reciprocal_rank_fusion([text, image, bm25])        │
  │                                                                          │
  │  3.5 MMR 去重                                                            │
  │      fused_results = _apply_mmr(query, fused_results, top_k=30)         │
  │                                                                          │
  │  3.6 Rerank 重排序                                                       │
  │      final_results = rerank_results(query, fused_results, top_k=15)     │
  └─────────────────────────────────────────────────────────────────────────┘
                                      │
                                      ▼
  ┌─────────────────────────────────────────────────────────────────────────┐
  │  4. 检索结果处理 (api/chat_routes.py)                                    │
  │                                                                          │
  │  4.1 构建 contexts                                                       │
  │      for each result:                                                    │
  │          if 图片切片:                                                     │
  │              doc = meta['full_description']  # 使用完整描述（P1）         │
  │          else:                                                           │
  │              doc = result['document']                                    │
  │          contexts.append({'doc': doc, 'meta': meta})                    │
  │                                                                          │
  │  4.2 懒加载增强（可选）                                                   │
  │      enhance_retrieved_chunks(contexts, query, kb_name)                 │
  │      └── 对无 VLM 描述的图片生成 VLM 描述                                  │
  └─────────────────────────────────────────────────────────────────────────┘
                                      │
                                      ▼
  ┌─────────────────────────────────────────────────────────────────────────┐
  │  5. 图片选择 (api/chat_routes.py - select_images)                        │
  │                                                                          │
  │  5.1 意图检测                                                            │
  │      - 精确图号查询："图2.3" → MAX_IMAGES=2, MIN_SCORE=5.0               │
  │      - 弱图片意图："发电量图" → MAX_IMAGES=1                              │
  │      - 普通查询 → MAX_IMAGES=2                                           │
  │                                                                          │
  │  5.2 提取图表引用（从 top 5 文本块）                                      │
  │      referenced_figures = {'2.3': {来源文件}}                            │
  │                                                                          │
  │  5.3 图片打分                                                            │
  │      for each image in contexts:                                         │
  │          score = score_image_relevance(query, meta, doc)                │
  │          └── 图号匹配 +10 分                                             │
  │          └── 关键词匹配 +2 分/个                                          │
  │          └── 章节匹配 +1.5 分                                             │
  │          └── 引用匹配 +8 分（需章节相关）                                  │
  │                                                                          │
  │  5.4 图文关联补充（P2 新增）                                              │
  │      for text_chunk in top 5:                                            │
  │          for fig_num in text_chunk['referenced_images']:                │
  │              查找对应的图片切片并补充到结果中                              │
  │                                                                          │
  │  5.5 返回 top N 图片                                                      │
  │      return scored_images[:MAX_IMAGES]                                  │
  └─────────────────────────────────────────────────────────────────────────┘
                                      │
                                      ▼
  ┌─────────────────────────────────────────────────────────────────────────┐
  │  6. 构建 LLM Prompt                                                      │
  │                                                                          │
  │  6.1 文本上下文                                                           │
  │      context_text = "\n\n".join([ctx['doc'] for ctx in contexts[:5]])   │
  │                                                                          │
  │  6.2 图片信息                                                             │
  │      if selected_images:                                                 │
  │          image_info = "【可用图片】\n" + 图片描述列表                      │
  │                                                                          │
  │  6.3 最终 Prompt                                                         │
  │      enhanced_context = context_text + image_info                       │
  └─────────────────────────────────────────────────────────────────────────┘
                                      │
                                      ▼
  ┌─────────────────────────────────────────────────────────────────────────┐
  │  7. LLM 生成回答                                                          │
  │                                                                          │
  │  for token in engine.generate_answer_stream(query, enhanced_context):   │
  │      yield token  # 流式输出                                             │
  └─────────────────────────────────────────────────────────────────────────┘
                                      │
                                      ▼
  ┌─────────────────────────────────────────────────────────────────────────┐
  │  8. 返回结果                                                              │
  │  {                                                                       │
  │    "type": "finish",                                                     │
  │    "answer": "完整回答文本",                                              │
  │    "sources": [...],       // 引用来源                                    │
  │    "images": [...]         // 精选图片                                    │
  │  }                                                                       │
  └─────────────────────────────────────────────────────────────────────────┘

  关键优化点总结

  ┌──────┬──────────────────┬────────────────────────────────────┐
  │ 阶段 │       优化       │                效果                │
  ├──────┼──────────────────┼────────────────────────────────────┤
  │ 检索 │ P0: 图片独立召回 │ 图片保证进入候选池                 │
  ├──────┼──────────────────┼────────────────────────────────────┤
  │ 入库 │ P1: 双字段存储   │ 短摘要用于检索，完整描述用于上下文 │
  ├──────┼──────────────────┼────────────────────────────────────┤
  │ 入库 │ P2: 图文关联索引 │ 文本命中时补充关联图片             │
  ├──────┼──────────────────┼────────────────────────────────────┤
  │ 选择 │ 章节相关性检查   │ 避免不相关图片加分                 │
  ├──────┼──────────────────┼────────────────────────────────────┤
  │ 意图 │ 重复提问强制检索 │ 避免复用错误上下文                 │
  └──────┴──────────────────┴────────────────────────────────────┘