- 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地址和模型名更新
964 lines
47 KiB
Markdown
964 lines
47 KiB
Markdown
# RAG 系统完整指南
|
||
|
||
> **版本**: v4.1(模型切换 + 图片检索修复)
|
||
> **生产入口**: `api/chat_routes.py::rag()` → `generate()` → `core/engine.py`
|
||
> **最后更新**: 2026-06-21
|
||
>
|
||
> 本次更新:LLM/意图/VLM 模型统一切换至 mimo-v2.5(xiaomimimo.com API),Reranker 切换至 xop3qwen8breranker(讯飞云 API),新增 chart_contexts 降门槛、P0 安全网、答案后过滤等图片检索修复逻辑。
|
||
|
||
## 一、功能概述
|
||
|
||
本系统是一个检索增强生成(RAG)问答系统,采用**单一统一编排路径**,由 `api/chat_routes.py` 的 `generate()` 函数直接编排全流程。核心能力包括:
|
||
|
||
| 功能 | 说明 | 实现位置 |
|
||
|------|------|----------|
|
||
| **意图分析** | LLM 驱动的双层判断(是否需要检索)+ 查询改写 | `core/intent_analyzer.py` |
|
||
| **混合检索** | 向量检索 + BM25 + RRF 融合 + Rerank 重排 | `core/engine.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()` |
|
||
| **富媒体** | 图片/表格的智能提取与展示 + P0 安全网 + 答案后过滤 | `api/chat_routes.py` |
|
||
| **查询理解** | 查询分解、扩展、MMR 去重、自适应 TopK | `core/` 各独立模块 |
|
||
| **安全护栏** | 敏感信息过滤、Prompt 安全守卫 | `api/response_utils.py`、`core/prompt_guard.py` |
|
||
| **救援管线** | BM25 散度救援、词法匹配救援、章节聚类救援、表格救援 | `core/engine.py` |
|
||
|
||
---
|
||
|
||
## 二、系统架构
|
||
|
||
### 2.1 整体架构图
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────────┐
|
||
│ 用户输入 │
|
||
└────────────────────────────┬────────────────────────────────────────┘
|
||
↓
|
||
┌─────────────────────────────────────────────────────────────────────┐
|
||
│ 语义缓存检查 (SemanticCache - FAISS) │
|
||
│ cosine ≥ 0.92 → 命中则直接返回缓存结果 │
|
||
│ 跳过检索 + 生成全流程(~100ms vs ~9s) │
|
||
└────────────────────────────┬────────────────────────────────────────┘
|
||
↓ 未命中
|
||
┌─────────────────────────────────────────────────────────────────────┐
|
||
│ 意图分析 (IntentAnalyzer) │
|
||
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
||
│ │ 改写查询 │ │ 双层判断 │ │ 子查询拆分 │ │
|
||
│ │ (指代消解) │ │ (是否检索) │ │ (对比/推理) │ │
|
||
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
|
||
└─────────┼──────────────────┼──────────────────┼─────────────────────┘
|
||
↓ ↓ ↓
|
||
┌──────────┐ ┌──────────────────────────────────────────┐
|
||
│ 直接回答 │ │ 统一编排流程 │
|
||
│ (LLM) │ │ 1. 混合检索 (engine.search_knowledge) │
|
||
└──────────┘ │ 2. 上下文提取 + 来源去重 │
|
||
│ 3. 图片补充检索 + 打分选择 │
|
||
│ 4. 构建上下文 │
|
||
│ 5. 流式答案生成 (engine.generate_stream) │
|
||
│ 6. 答案图号对齐 + 引用标注 │
|
||
│ 7. 敏感信息过滤 │
|
||
│ 8. 语义缓存写入 + SSE finish │
|
||
└────────────────────┬─────────────────────┘
|
||
↓
|
||
┌─────────────────────────────────────────────────────────────────────┐
|
||
│ 检索层 (RAGEngine) │
|
||
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
||
│ │ 查询缓存 │ │ 向量检索 │ │ BM25 检索 │ │
|
||
│ │ (LRU 500) │ │ (语义匹配) │ │ (关键词匹配) │ │
|
||
│ │ 命中直接返回│ └──────┬───────┘ └──────┬───────┘ │
|
||
│ └──────────────┘ └─────────────────┘ │
|
||
│ ↓ │
|
||
│ ┌──────────────┐ │
|
||
│ │ RRF 融合 │ ← 动态权重(查询类型/长度驱动)│
|
||
│ └──────┬───────┘ │
|
||
│ ↓ │
|
||
│ ┌───────────┐ ┌──────────────┐ ┌──────────────┐ │
|
||
│ │ 废止过滤 │→ │ Rerank 重排 │→ │ MMR 去重 │ │
|
||
│ └───────────┘ │ (云端API) │ └──────┬───────┘ │
|
||
│ └──────┬───────┘ ↓ │
|
||
│ ┌───────────┐ ┌──────────────┐ ┌──────────────┐ │
|
||
│ │ FAQ 加权 │→ │ 黑名单过滤 │→ │ 时间衰减 │ │
|
||
│ └───────────┘ └──────────────┘ └──────┬───────┘ │
|
||
│ ↓ │
|
||
│ ┌───────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
||
│ │ 上下文扩展 │→ │ 自适应 TopK │→ │ 救援管线 │ │
|
||
│ └───────────────┘ └──────────────┘ │ (BM25散度/ │ │
|
||
│ │ 词法/章节/ │ │
|
||
│ │ 表格救援) │ │
|
||
│ └──────────────┘ │
|
||
└─────────────────────────────────────────────────────────────────────┘
|
||
↓
|
||
┌─────────────────────────────────────────────────────────────────────┐
|
||
│ 答案生成 (LLM 流式) │
|
||
│ ┌────────────────────────────────────────────────────────────────┐ │
|
||
│ │ 整合多源信息 + 标注来源 + 引用编号 + SSE 流式输出 │ │
|
||
│ └────────────────────────────────────────────────────────────────┘ │
|
||
└─────────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 2.2 编排方式
|
||
|
||
系统采用**函数式编排**而非类组合模式。整个 RAG 流程由 `api/chat_routes.py` 的 `generate()` 函数直接控制,按步骤调用各独立模块:
|
||
|
||
- **意图分析**:`core/intent_analyzer.py` 的 `analyze_intent()`
|
||
- **检索管线**:`core/engine.py` 的 `search_knowledge()`
|
||
- **流式生成**:`core/engine.py` 的 `generate_answer_stream()`
|
||
- **引用标注**:`api/chat_routes.py` 内的 `_attach_citations()`
|
||
- **缓存系统**:`core/cache.py` 的 `RAGCacheManager` 单例 + `core/semantic_cache.py` 的 `SemanticCache` 单例
|
||
|
||
各模块通过 `get_engine()`、`get_cache_manager()`、`get_semantic_cache()` 等工厂函数获取全局单例实例。
|
||
|
||
---
|
||
|
||
## 三、五层缓存架构
|
||
|
||
### 3.1 缓存层次概览
|
||
|
||
| 层次 | 缓存类型 | 存储结构 | 容量 | TTL | 作用 |
|
||
|------|----------|----------|------|-----|------|
|
||
| L1 | Query Cache | LRU (OrderedDict) | 500 条 | 1 小时 | 缓存完整问答结果,命中后跳过整个检索+生成 |
|
||
| 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
|
||
|
||
Query Cache 是最外层的完整问答结果缓存。命中后直接返回缓存的 `answer + sources + citations`,跳过检索和生成全流程。
|
||
|
||
**缓存键设计**:`{query_hash}:{kb_name}:{kb_version}`
|
||
|
||
- 基于查询文本哈希 + 知识库名称 + 知识库版本号
|
||
- 知识库版本变更时(如文档更新/重新索引),相关缓存自动失效
|
||
|
||
**已修复的问题**:
|
||
|
||
1. **键不匹配问题(已修复)**:此前 `set_query_result()` 在有 `doc_ids` 参数时使用 `doc_hash` 分支生成键,而 `get_query_result()` 始终使用 `kb_version` 分支——导致 GET 和 SET 的键永远不匹配,命中率始终为 0%。修复后两端统一使用 `kb_version` 分支。
|
||
|
||
2. **写入阈值问题(已修复)**:`CACHE_MIN_SCORE` 原值为 `0.3`,但 ChromaDB 余弦距离经 `1 - dist` 计算后得分通常在 0.03-0.06 之间,远低于阈值,导致几乎不写入缓存。修复后设为 `0.0`。
|
||
|
||
### 3.3 Embedding Cache
|
||
|
||
缓存文本向量化结果,由 `RAGEngine` 在调用 embedding 模型前后自动读写。键为查询文本哈希,避免对相同文本重复调用 embedding 模型(如 DashScope text-embedding-v3)。
|
||
|
||
### 3.4 Rerank Cache
|
||
|
||
缓存 Rerank 重排序的分数结果。键为 `query + sorted(doc_ids)` 的精确匹配。注意:由于 RRF 融合产出差异,命中率可能偏低。
|
||
|
||
### 3.5 语义缓存(Semantic Cache)
|
||
|
||
语义缓存基于 FAISS 向量索引实现语义级匹配——即使查询文字不完全相同,只要语义足够相似(cosine similarity ≥ 0.92),就能命中缓存。
|
||
|
||
**工作机制**:
|
||
|
||
```
|
||
用户查询 → embedding 编码 → FAISS 向量检索
|
||
→ cosine ≥ 0.92 → 命中:返回缓存的 answer + sources + citations(~100ms)
|
||
→ cosine < 0.92 → 未命中:执行完整 RAG 流程后写入缓存
|
||
```
|
||
|
||
**集成位置**:
|
||
|
||
- **读取**:在 `generate()` 函数的意图分析之后、混合检索之前(跳过整个检索+生成流程)
|
||
- **写入**:在 `generate()` 函数生成完整答案后、发送 `finish` 事件之前
|
||
- **缓存内容**:answer、sources、citations、images、tables
|
||
|
||
**验证结果**:语义缓存命中率约 66.7%,平均响应从 ~9.2 秒降至 ~100 毫秒(约 92 倍加速)。
|
||
|
||
### 3.6 缓存失效机制
|
||
|
||
所有基于 LRU 的缓存(L1-L3)均支持基于知识库版本号(`kb_version`)的自动失效:
|
||
|
||
- 每个缓存条目关联 `kb_version`
|
||
- 知识库文档变更时 `kb_version` 递增
|
||
- 读取时检查 `kb_version` 是否匹配,不匹配则视为过期
|
||
|
||
语义缓存(L4)当前无 TTL 过期机制,仅受 `max_size=10000` 容量限制。
|
||
|
||
### 3.7 缓存配置
|
||
|
||
```python
|
||
# config.py / config.example.py
|
||
|
||
# 查询结果缓存
|
||
QUERY_CACHE_ENABLED = True
|
||
QUERY_CACHE_SIZE = 500
|
||
QUERY_CACHE_TTL = 3600 # 秒
|
||
|
||
# Embedding 缓存
|
||
EMBEDDING_CACHE_ENABLED = True
|
||
EMBEDDING_CACHE_SIZE = 2000
|
||
EMBEDDING_CACHE_TTL = 86400 # 24小时
|
||
|
||
# Rerank 缓存
|
||
RERANK_CACHE_ENABLED = True
|
||
RERANK_CACHE_SIZE = 1000
|
||
RERANK_CACHE_TTL = 3600
|
||
|
||
# 语义缓存
|
||
SEMANTIC_CACHE_ENABLED = True
|
||
SEMANTIC_CACHE_THRESHOLD = 0.92 # cosine 相似度阈值
|
||
|
||
# 缓存写入最低置信度
|
||
CACHE_MIN_SCORE = 0.0 # ChromaDB 余弦距离经 1-dist 后得分约 0.03-0.06,须设为 0
|
||
```
|
||
|
||
### 3.8 部署注意事项
|
||
|
||
当前所有缓存均为**进程内内存存储**(LRU 使用 `OrderedDict`,语义缓存使用 FAISS 内存索引),有以下部署影响:
|
||
|
||
- **单 Worker**:Gunicorn 默认 1 个 worker,所有请求共享同一缓存实例,缓存有效
|
||
- **多 Worker**:每个 worker 有独立缓存,不共享,缓存效率降低
|
||
- **冷启动**:进程重启后缓存全部丢失,需重新预热
|
||
- **`max_requests=1000`**:Gunicorn worker 定期重启会导致缓存周期性清空
|
||
|
||
对于生产环境多实例部署场景,已规划 Redis 外部缓存迁移方案(见 `reports/redis_migration_plan.md`)。
|
||
|
||
---
|
||
|
||
## 四、意图分析流程
|
||
|
||
### 4.1 IntentAnalyzer 双层判断
|
||
|
||
意图分析由 `core/intent_analyzer.py` 的 `IntentAnalyzer` 类完成,采用 **LLM 驱动** 的双层判断:
|
||
|
||
```
|
||
用户输入 + 对话历史
|
||
↓
|
||
┌─────────────────────────────────────────────┐
|
||
│ IntentAnalyzer.analyze() │
|
||
│ │
|
||
│ Step 1: 查询改写 │
|
||
│ - 指代消解:"那请假呢" → "出差相关请假流程"│
|
||
│ - 省略补全:"标准" → "差旅报销标准" │
|
||
│ - 语义缓存:相似问题复用结果 │
|
||
│ │
|
||
│ Step 2: 双层判断 │
|
||
│ - 第一层:历史上下文是否可答? │
|
||
│ - 第二层:是否需要外部知识(检索)? │
|
||
│ │
|
||
│ Step 3: 子查询拆分(对比/推理类) │
|
||
│ - "年假和调休的区别" → ["年假规定", "调休规定"]│
|
||
└─────────────────────────────────────────────┘
|
||
↓
|
||
IntentAnalysis:
|
||
- rewritten_query: 改写后的查询
|
||
- use_context: 是否使用上下文
|
||
- need_retrieval: 是否需要检索
|
||
- sub_queries: 子查询列表
|
||
- intent: factual/comparison/reasoning/instruction/other
|
||
```
|
||
|
||
### 4.2 QueryClassifier 规则分类
|
||
|
||
`core/query_classifier.py` 提供无 LLM 调用的快速规则分类:
|
||
|
||
| 查询类型 | 说明 | 示例 |
|
||
|----------|------|------|
|
||
| `META` | 元问题(文件列表、权限) | "有哪些文档?" |
|
||
| `REALTIME` | 实时信息 | "今天天气" |
|
||
| `SIMPLE` | 简单单属性查询 | "出差标准" |
|
||
| `FACT` | 事实查询 | "差旅补贴标准是多少" |
|
||
| `ENUMERATION` | 枚举/清单/条款 | "严禁哪些情形" |
|
||
| `COMPARISON` | 比较分析 | "年假和调休的区别" |
|
||
| `PROCESS` | 流程指引 | "如何申请调岗" |
|
||
| `FILE_SPECIFIC` | 特定文件内查询 | "xxx.pdf中有哪些图片" |
|
||
|
||
---
|
||
|
||
## 五、检索管线详解
|
||
|
||
### 5.1 完整检索流程
|
||
|
||
```
|
||
search_knowledge(query, top_k=30)
|
||
│
|
||
├─ 1. 查询缓存检查 → 命中则直接返回
|
||
│
|
||
├─ 2. 子查询并行检索(如有 sub_queries)
|
||
│ └─ 各子查询独立检索后合并去重
|
||
│
|
||
├─ 3. 查询拆分(QueryDecomposer)
|
||
│ └─ 对比/推理类查询自动拆分
|
||
│
|
||
├─ 4. 查询扩展(QueryExpansion)
|
||
│ └─ 同义词/语义扩展(threshold=0.8)
|
||
│
|
||
├─ 5. 多知识库检索(USE_MULTI_KB=True)
|
||
│ ├─ 各向量库并行检索(ThreadPoolExecutor)
|
||
│ ├─ 每个库: 向量检索 + BM25 + 图片独立召回
|
||
│ ├─ FAQ 集合独立召回
|
||
│ └─ RRF 融合
|
||
│
|
||
├─ 6. 废止切片过滤(status != "active")
|
||
│
|
||
├─ 7. 章节过滤(查询提到章节时优先匹配)
|
||
│
|
||
├─ 8. ★ Rerank 重排 ★(云端讯飞 xop3qwen8breranker 或本地 BGE)
|
||
│ └─ rerank_results(query, results, top_k)
|
||
│ └─ 由 RERANK_BACKEND 控制(local/cloud/fallback)
|
||
│
|
||
├─ 9. MMR 去重
|
||
│ ├─ 语义向量版(MMR_USE_EMBEDDING=True)
|
||
│ └─ 文本 Jaccard 版(MMR_USE_EMBEDDING=False,生产推荐)
|
||
│
|
||
├─ 10. FAQ 分数加权(Score Boosting)
|
||
│
|
||
├─ 11. 黑名单过滤(负反馈降权)
|
||
│
|
||
├─ 12. 时间衰减(Time Decay)
|
||
│
|
||
├─ 13. 上下文扩展(Rerank 后,补充相邻切片)
|
||
│
|
||
├─ 14. 自适应 TopK(根据置信度调整返回数量)
|
||
│
|
||
├─ 15. 救援管线(BM25散度/词法匹配/章节聚类/表格救援)
|
||
│
|
||
└─ 16. 缓存写入 → 返回结果
|
||
```
|
||
|
||
### 5.2 混合检索代码示例
|
||
|
||
```python
|
||
# 向量检索(语义相似)
|
||
vector_results = collection.query(query_embeddings=[query_vector], n_results=recall_k)
|
||
|
||
# BM25 检索(关键词匹配)
|
||
bm25_results = bm25_index.search(query, top_k=recall_k)
|
||
|
||
# FAQ 独立召回
|
||
faq_results = faq_collection.query(query_embeddings=[query_vector], n_results=3)
|
||
|
||
# 图片独立召回(P0 通道)
|
||
image_results = collection.query(
|
||
query_embeddings=[query_vector], n_results=5,
|
||
where={"chunk_type": {"$in": ["image", "chart", "table"]}}
|
||
)
|
||
|
||
# RRF 融合(动态权重)
|
||
fused = reciprocal_rank_fusion([vector_results, bm25_results], weights=[vector_w, bm25_w])
|
||
|
||
# Rerank 重排(云端讯飞 xop3qwen8breranker 或本地 BGE)
|
||
reranked = rerank_results(query, fused, top_k=15)
|
||
|
||
# MMR 去重
|
||
mmr_results = mmr_rerank(query_emb, reranked, top_k=30, lambda_param=0.5)
|
||
```
|
||
|
||
### 5.3 RRF 融合算法
|
||
|
||
```
|
||
RRF分数 = Σ (权重 / (k + 排名位置))
|
||
|
||
示例(k=60):
|
||
文档A: 向量排名1 → 0.5/(60+1) = 0.00820
|
||
BM25排名3 → 0.5/(60+3) = 0.00794
|
||
总分 = 0.01614
|
||
|
||
动态权重策略:
|
||
- 短查询(<15字): BM25权重↑ (0.6), 向量权重↓ (0.4)
|
||
- 长查询(>50字): 向量权重↑ (0.7), BM25权重↓ (0.3)
|
||
- 查询类型驱动: FACT→BM25优先, PROCESS→向量优先
|
||
```
|
||
|
||
### 5.4 Rerank 重排
|
||
|
||
**后端**:支持三种模式,由 `RERANK_BACKEND` 环境变量控制
|
||
|
||
| RERANK_BACKEND | 说明 |
|
||
|----------------|------|
|
||
| `"cloud"` | 仅使用云端讯飞 `xop3qwen8breranker` API |
|
||
| `"local"` | 仅使用本地 `BAAI/bge-reranker-base`(CrossEncoder / ONNX) |
|
||
| `"fallback"` | 优先云端,失败时自动回退本地(推荐生产环境) |
|
||
|
||
**云端 Reranker(推荐)**:
|
||
|
||
```python
|
||
# config.py
|
||
RERANK_BACKEND = os.getenv("RERANK_BACKEND", "local")
|
||
RERANK_CLOUD_MODEL = "xop3qwen8breranker"
|
||
RERANK_CLOUD_API_KEY = os.getenv("RERANK_CLOUD_API_KEY", DASHSCOPE_API_KEY)
|
||
RERANK_CLOUD_BASE_URL = "https://maas-api.cn-huabei-1.xf-yun.com/v1/rerank"
|
||
RERANK_CLOUD_TIMEOUT = 15
|
||
```
|
||
|
||
`CloudReranker` 类(`core/engine.py`)封装讯飞云的 `/v1/rerank` 接口,提供与本地 `CrossEncoder.predict()` / `ONNXReranker.predict()` 一致的调用接口。
|
||
|
||
**本地 Reranker(备选)**:
|
||
|
||
```python
|
||
def rerank_results(self, query, results, top_k=5):
|
||
pairs = [(query, doc) for doc in results['documents'][0]]
|
||
scores = self.reranker.predict(pairs) # CrossEncoder / ONNXReranker / CloudReranker
|
||
sorted_indices = np.argsort(scores)[::-1]
|
||
# 返回 top_k 个最高分结果
|
||
```
|
||
|
||
**调用位置**:`core/engine.py` 的 `search_knowledge()` 和 `_search_multi_kb()` 中,RRF 融合 + 废止/章节过滤之后、MMR 去重之前执行。
|
||
|
||
---
|
||
|
||
## 六、生产 /rag 完整流程
|
||
|
||
入口 `api/chat_routes.py::rag() → generate()`:
|
||
|
||
```
|
||
POST /rag (SSE 流式)
|
||
↓
|
||
[chat_routes.generate()]
|
||
│
|
||
├─ 发 SSE: start
|
||
│
|
||
├─ 1. 语义缓存检查 SemanticCache.get() # chat_routes
|
||
│ ├─ 命中 → 直接流式发 SSE: chunk + finish,结束(~100ms)
|
||
│ └─ 未命中 → 继续;记录 embedding 供后续写入
|
||
│
|
||
├─ 2. 意图分析 intent_analyzer.analyze_intent()
|
||
│ ├─ need_retrieval=False → 直接 LLM 回答(流式发 SSE: chunk),结束
|
||
│ │ └─ use_context=True 时带历史上下文,use_context=False 时纯闲聊
|
||
│ └─ 否则继续;sub_queries 传入检索
|
||
│ └─[DEV] 发 SSE: intent_result
|
||
│
|
||
├─ 3. 混合检索 search_hybrid() → engine.search_knowledge()
|
||
│ (内部:查询缓存检查 → 向量+BM25+RRF+废止过滤+章节过滤
|
||
│ +云端Rerank+MMR去重+FAQ加权+黑名单+时间衰减
|
||
│ +上下文扩展+自适应TopK)
|
||
│ └─[DEV] 发 SSE: retrieval_debug
|
||
│
|
||
├─ 4. 提取上下文/来源(按 source 去重,doc_type 驱动溯源展示)
|
||
│ └─[DEV] 发 SSE: chunks_retrieved
|
||
│ └─ 发 SSE: sources
|
||
│
|
||
├─ 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() # 写入缓存供后续命中
|
||
├─ 12. 发 SSE: finish(answer + sources + citations + images + timing)
|
||
└─[异常] 发 SSE: error
|
||
```
|
||
|
||
### SSE 事件序列
|
||
|
||
| SSE 事件 `type` | 含义 |
|
||
|----------------|------|
|
||
| `start` | 请求开始处理 |
|
||
| `intent_result` | 意图分析结果 [DEV] |
|
||
| `retrieval_debug` | 检索管线各步骤 [DEV] |
|
||
| `chunks_retrieved` | 召回切片详情 [DEV] |
|
||
| `sources` | 检索到的来源列表 |
|
||
| `images_selected` | 图片选择详情 [DEV] |
|
||
| `context_built` | 最终上下文构建 [DEV] |
|
||
| `chunk` | 流式答案的每个 token |
|
||
| `finish` | 含 `timing`、`sources`、`citations`、`images` |
|
||
| `error` | 处理异常时的错误信息 |
|
||
|
||
> 标注 [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 调用方式
|
||
|
||
### 7.1 SSE 流式问答(主要接口)
|
||
|
||
```bash
|
||
curl -X POST http://localhost:5001/rag \
|
||
-H "Content-Type: application/json" \
|
||
-H "Authorization: Bearer mock-token-admin" \
|
||
-d '{
|
||
"query": "出差补助标准是什么?",
|
||
"chat_history": []
|
||
}'
|
||
```
|
||
|
||
**响应格式**:SSE(Server-Sent Events)流式返回
|
||
|
||
```
|
||
event: token
|
||
data: {"text": "根据"}
|
||
|
||
event: token
|
||
data: {"text": "规定"}
|
||
|
||
...
|
||
|
||
event: finish
|
||
data: {"answer": "完整答案", "sources": [...], "citations": [...], "images": [...], "duration_ms": 200}
|
||
```
|
||
|
||
### 7.2 代码调用
|
||
|
||
```python
|
||
from core.engine import get_engine
|
||
|
||
# 初始化引擎
|
||
engine = get_engine()
|
||
|
||
# 检索
|
||
result = engine.search_knowledge("出差补助标准是什么?", top_k=10)
|
||
|
||
# 流式生成答案
|
||
for token in engine.generate_answer_stream(query, context, history=history):
|
||
print(token, end="", flush=True)
|
||
```
|
||
|
||
### 7.3 缓存统计查询
|
||
|
||
```bash
|
||
# 查看各层缓存命中率和统计
|
||
curl http://localhost:5001/cache/stats \
|
||
-H "Authorization: Bearer mock-token-admin"
|
||
```
|
||
|
||
返回示例:
|
||
|
||
```json
|
||
{
|
||
"query_cache": {"total_entries": 50, "hits": 10, "misses": 6, "hit_rate": 0.625},
|
||
"embedding_cache": {"total_entries": 200, "hits": 0, "misses": 0, "hit_rate": 0},
|
||
"rerank_cache": {"total_entries": 100, "hits": 0, "misses": 0, "hit_rate": 0},
|
||
"semantic_cache": {"total_entries": 15, "hits": 6, "misses": 3, "hit_rate": 0.667}
|
||
}
|
||
```
|
||
|
||
> 注:当 Query Cache 或 Semantic Cache 在外层拦截了重复查询时,Embedding Cache 和 Rerank Cache 的命中率为 0 是正常现象——重复查询根本不会到达这些层。
|
||
|
||
---
|
||
|
||
## 八、配置说明
|
||
|
||
### 8.1 LLM 配置
|
||
|
||
```python
|
||
# config.py
|
||
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
|
||
# 混合检索
|
||
USE_MULTI_KB = True # 多向量库模式
|
||
USE_HYBRID_SEARCH = True # 向量 + BM25 混合检索
|
||
VECTOR_WEIGHT = 0.5 # 向量检索权重
|
||
BM25_WEIGHT = 0.5 # BM25 检索权重
|
||
RAG_SEARCH_TOP_K = 30 # 最终返回结果数
|
||
RAG_SEARCH_CANDIDATES = 100 # 候选池大小
|
||
RECALL_MULTIPLIER = 3 # 候选池最小倍数
|
||
|
||
# 重排序
|
||
USE_RERANK = True # 启用重排序
|
||
RERANK_BACKEND = "local" # "local"=本地模型, "cloud"=云端API, "fallback"=优先云端失败回退本地
|
||
RERANK_CLOUD_MODEL = "xop3qwen8breranker"
|
||
RERANK_CANDIDATES = 20 # 送入重排序的候选数
|
||
RERANK_TOP_K = 15 # 重排序后保留数
|
||
RERANK_USE_ONNX = True # ONNX 加速(仅本地模式,环境变量控制)
|
||
|
||
# RRF 融合
|
||
RRF_K = 60 # RRF 常数
|
||
DYNAMIC_RRF_ENABLED = True # 动态权重
|
||
|
||
# MMR 去重
|
||
MMR_ENABLED = True
|
||
MMR_USE_EMBEDDING = False # True=语义向量(慢),False=文本Jaccard相似度(快,生产推荐)
|
||
MMR_TOP_K = 30 # MMR 保留数
|
||
MMR_LAMBDA = 0.5 # 相关性 vs 多样性权衡
|
||
```
|
||
|
||
### 8.3 设备配置
|
||
|
||
```python
|
||
DEVICE = "auto" # auto / cuda / cpu / cuda:0
|
||
EMBEDDING_DEVICE = DEVICE # 向量模型设备
|
||
RERANK_DEVICE = DEVICE # Rerank 模型设备
|
||
```
|
||
|
||
---
|
||
|
||
## 九、文件结构
|
||
|
||
```
|
||
core/ # RAG 核心引擎
|
||
├── engine.py # RAGEngine 单例(检索主流程、Rerank、RRF、流式生成)
|
||
├── cache.py # 三层 LRU 缓存管理器(Query/Embedding/Rerank)
|
||
├── semantic_cache.py # 语义缓存(FAISS IndexFlatIP)
|
||
├── bm25_index.py # BM25Index(关键词检索)
|
||
├── chunker.py # 文本分块器
|
||
├── mmr.py # MMR 去重(语义向量版 + 文本 Jaccard 版)
|
||
├── query_classifier.py # QueryClassifier(规则快速分类)
|
||
├── intent_analyzer.py # IntentAnalyzer(LLM 意图分析)
|
||
├── query_decomposer.py # QueryDecomposer(复杂查询拆分)
|
||
├── query_expansion.py # 查询扩展
|
||
├── adaptive_topk.py # AdaptiveTopK(自适应 TopK)
|
||
├── confidence_gate.py # ConfidenceGate(置信度门控,当前未接入生产流程)
|
||
├── quality_assessor.py # 多维质量评估(当前未接入生产流程)
|
||
├── reasoning_reflector.py # 推理反思(当前未接入生产流程)
|
||
├── loop_guard.py # 循环防护(当前未接入生产流程)
|
||
├── prompt_guard.py # Prompt 安全守卫
|
||
├── llm_budget.py # LLM 调用预算控制
|
||
├── llm_utils.py # LLM 调用工具函数
|
||
├── status_codes.py # 状态码定义
|
||
└── constants.py # 公共常量
|
||
|
||
knowledge/ # 知识库管理
|
||
├── manager.py # KnowledgeBaseManager(7 个 Mixin 组合)
|
||
├── router.py # KnowledgeBaseRouter(智能知识库路由)
|
||
├── sync.py # KnowledgeSyncService(文件变更监控+增量向量化)
|
||
├── base.py # 知识库基类定义
|
||
├── collection.py # 集合(Collection)CRUD 操作
|
||
├── document.py # 文档管理(上传/删除/状态流转)
|
||
├── document_versions.py # 文档版本管理
|
||
├── chunk.py # 分块操作(创建/查询/更新)
|
||
├── index.py # 索引管理(BM25 构建/重建)
|
||
├── search.py # 知识库内搜索
|
||
├── processing.py # 文档处理流水线(解析→分块→向量化)
|
||
├── permission.py # 权限控制(角色/部门级访问控制)
|
||
├── lazy_enhance.py # 延迟增强(按需生成摘要/关键词)
|
||
└── cleanup.py # 清理操作(孤立切片/过期数据)
|
||
|
||
api/ # API 路由层
|
||
├── __init__.py # create_app() 工厂
|
||
├── chat_routes.py # /chat, /rag(SSE), /search(核心编排入口)
|
||
├── kb_routes.py # /collections
|
||
├── document_routes.py # /documents/*
|
||
├── sync_routes.py # /sync
|
||
├── session_routes.py # /sessions(会话管理)
|
||
├── image_routes.py # /images(图片访问/上传)
|
||
├── feedback_routes.py # /feedback(用户反馈/点赞/踩)
|
||
├── auth_routes.py # /auth(认证/登录/Token)
|
||
├── audit_routes.py # /audit(审计日志)
|
||
└── response_utils.py # 响应工具函数
|
||
|
||
services/ # 业务服务层
|
||
├── session.py # 会话管理服务
|
||
├── feedback.py # 反馈处理服务
|
||
└── outline.py # 大纲生成服务
|
||
|
||
graph/ # (已清空,Graph RAG 不再使用)
|
||
|
||
auth/ # 认证与安全
|
||
├── gateway.py # API 网关认证
|
||
└── security.py # 安全工具(Token 验证/权限检查)
|
||
|
||
repositories/ # 数据持久层
|
||
├── session_repo.py # 会话仓储接口
|
||
├── sqlite_session_repo.py # SQLite 会话仓储实现
|
||
└── stateless_session_repo.py # 无状态会话仓储
|
||
|
||
parsers/ # 文档解析器
|
||
├── mineru_parser.py # MinerU PDF/DOCX/PPTX 解析
|
||
├── excel_parser.py # Excel 解析
|
||
├── txt_parser.py # 纯文本解析
|
||
├── image_extractor.py # 图片提取器(从文档中提取/处理图片)
|
||
└── pdf_mineru.py # MinerU PDF 辅助入口
|
||
|
||
exam_pkg/ # 出题系统
|
||
├── api.py # 出题/批阅 API
|
||
├── generator.py # 试题生成(Dify 工作流)
|
||
├── grader.py # 试卷批阅
|
||
├── manager.py # 出题管理器(核心业务逻辑)
|
||
└── local_db.py # 本地数据库(SQLite)
|
||
|
||
tools/ # 运维/分析工具
|
||
├── export_chunks.py # 导出切片
|
||
├── llm_evaluator.py # LLM 评估器
|
||
├── chunk_analyzer.py # 切片质量分析
|
||
├── chunk_metrics.py # 切片指标统计
|
||
├── chunk_report.py # 切片报告生成
|
||
├── clean_vector_store.py # 清理向量库
|
||
├── rebuild_pdf_vectors.py # 重建 PDF 向量
|
||
└── upload_test_files.py # 上传测试文件
|
||
|
||
deploy/ # 部署配置
|
||
├── Dockerfile # 开发环境 Docker
|
||
├── Dockerfile.prod # 生产环境 Docker
|
||
├── docker-compose.yml # 开发环境编排
|
||
├── docker-compose.prod.yml # 生产环境编排
|
||
├── nginx.conf # Nginx 反向代理配置
|
||
├── gunicorn.conf.py # Gunicorn WSGI 配置
|
||
└── wsgi.py # WSGI 入口
|
||
|
||
reports/ # 分析报告
|
||
├── cache_performance_report.md # 缓存性能验证报告
|
||
└── redis_migration_plan.md # Redis 缓存迁移方案(规划中)
|
||
|
||
config/ # 运行时配置
|
||
└── banned_words.txt # 敏感词库
|
||
```
|
||
|
||
---
|
||
|
||
## 十、与传统 RAG 对比
|
||
|
||
| 特性 | 传统 RAG | 本系统 |
|
||
|------|---------|--------|
|
||
| 意图判断 | 无 | IntentAnalyzer LLM 双层判断 |
|
||
| 查询改写 | 无 | 口语化→专业术语 + 实体补全 + 指代消解 |
|
||
| 检索方式 | 单一向量检索 | 向量 + BM25 + FAQ + 图片独立召回 |
|
||
| 融合算法 | 无 | RRF 动态权重融合 |
|
||
| 去重 | 无 | MMR 语义去重 |
|
||
| 重排序 | 无 | 云端 xop3qwen8breranker API(讯飞云,支持本地 BGE 回退) |
|
||
| 问题分解 | 无 | 自动拆分对比/推理类查询 |
|
||
| 闲聊处理 | 无 | 意图分析自动判断 |
|
||
| 缓存体系 | 无 | 五层缓存(Query + Embedding + Rerank + 语义缓存 + 元数据缓存) |
|
||
| 语义缓存 | 无 | FAISS 向量索引,相似查询复用(92x 加速) |
|
||
| 自适应 TopK | 固定 top_k | 根据置信度动态调整 |
|
||
| 上下文理解 | 无 | 多轮对话 + 历史上下文 |
|
||
| 响应时间 | ~2秒 | 首次 ~3-8秒 / 缓存命中 ~100-200毫秒 |
|
||
|
||
---
|
||
|
||
## 十一、Rerank 性能分析
|
||
|
||
### 11.1 Rerank 调用路径
|
||
|
||
Rerank 在生产流程中有 **一个调用路径**:
|
||
|
||
| 路径 | 位置 | 说明 |
|
||
|------|------|------|
|
||
| 主检索管线 | `engine.rerank_results()` | RRF 融合后、MMR 去重前执行,对候选重排取 top_k |
|
||
|
||
### 11.2 性能特征
|
||
|
||
| 项目 | 说明 |
|
||
|------|------|
|
||
| Rerank 缓存命中率 | 偏低——键基于 `query + sorted(doc_ids)` 精确匹配,RRF 融合产出稍有不同就无法命中 |
|
||
| 性能计时 | `rerank_results()` 返回 `_rerank_time_ms` 计时字段 |
|
||
| Query Cache 拦截 | 重复查询被 Query Cache 在外层拦截,不会到达 Rerank 层(正确行为) |
|
||
|
||
### 11.3 Rerank 配置参数
|
||
|
||
| 配置项 | 默认值 | 说明 |
|
||
|--------|--------|------|
|
||
| `USE_RERANK` | `True` | 总开关 |
|
||
| `RERANK_BACKEND` | `"local"` | 后端选择 |
|
||
| `RERANK_CLOUD_MODEL` | `"xop3qwen8breranker"` | 云端模型名称 |
|
||
| `RERANK_CLOUD_API_KEY` | 同 `DASHSCOPE_API_KEY` | 云端 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 的候选数 |
|
||
| `RERANK_TOP_K` | `15` | Rerank 后保留数 |
|
||
| `RERANK_USE_ONNX` | `True` | ONNX 加速开关 |
|
||
| `RERANK_DEVICE` | 跟随 `DEVICE` | 设备选择 |
|
||
| `RERANK_THRESHOLD` | `0.3` | 上下文过滤阈值 |
|
||
| `RERANK_CACHE_ENABLED` | `True` | 缓存开关 |
|
||
|
||
---
|
||
|
||
## 十二、最佳实践
|
||
|
||
### 12.1 何时使用 /rag 接口
|
||
|
||
**推荐使用**:
|
||
|
||
- 复杂问题需要检索知识库
|
||
- 用户表达模糊需要改写
|
||
- 需要区分闲聊和知识问答
|
||
- 需要多轮对话记忆
|
||
- 需要引用来源和证据
|
||
|
||
**不推荐使用**(改用 `/search` 接口更快):
|
||
|
||
- 简单明确的问题,只需返回原始检索结果
|
||
- 对响应时间极度敏感且不需要 LLM 生成答案的场景
|
||
|
||
### 12.2 性能优化
|
||
|
||
```python
|
||
# ONNX 加速默认已开启;如有兼容性问题可关闭(环境变量)
|
||
# RERANK_USE_ONNX=false
|
||
|
||
# 使用轻量 MMR(文本相似度代替语义向量)
|
||
# config.py: MMR_USE_EMBEDDING = False
|
||
|
||
# 调整语义缓存阈值(降低阈值可提高命中率,但可能降低准确性)
|
||
# config.py: SEMANTIC_CACHE_THRESHOLD = 0.90
|
||
|
||
# 调整缓存容量
|
||
# config.py: QUERY_CACHE_SIZE = 1000 # 增大查询缓存容量
|
||
```
|
||
|
||
### 12.3 调试技巧
|
||
|
||
```python
|
||
# 查看检索调试信息
|
||
result = engine.search_knowledge("问题", top_k=10)
|
||
debug = result.get('_debug', {})
|
||
for step in debug.get('steps', []):
|
||
print(f"步骤: {step['name']}, 详情: {step}")
|
||
|
||
# 查看缓存统计
|
||
from core.cache import get_cache_manager
|
||
cm = get_cache_manager()
|
||
print(cm.get_stats())
|
||
|
||
# 查看语义缓存统计
|
||
from core.semantic_cache import get_semantic_cache
|
||
sc = get_semantic_cache()
|
||
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 路由。
|
||
|
||
**修复 Query Cache**:
|
||
|
||
1. 修复 GET/SET 键不匹配——此前 SET 使用 `doc_hash` 分支,GET 使用 `kb_version` 分支,两端永远不匹配,命中率始终为 0%
|
||
2. 修复 `CACHE_MIN_SCORE = 0.3` 阈值过高——ChromaDB 余弦距离经 `1-dist` 后得分约 0.03-0.06,远低于 0.3,导致几乎不写入缓存
|
||
|
||
**集成语义缓存**:将 FAISS 语义缓存从已删除的备用路径移植到生产 `/rag` 端点,在意图分析后、混合检索前检查,命中时跳过整个检索+生成流程。验证结果:命中率 66.7%,92 倍加速。
|
||
|
||
### v3.2 — 模型/Reranker/管线更新
|
||
|
||
引入云端 Reranker(现 xop3qwen8breranker/讯飞云,原 qwen3-rerank/DashScope)、ONNX 加速、动态 RRF 权重等。
|
||
|
||
---
|
||
|
||
## 十四、未来规划
|
||
|
||
### Redis 缓存外部化
|
||
|
||
当前五层缓存均为进程内内存存储(L5 ChromaDB 元数据缓存除外),在多 Worker / 多实例部署时无法共享。已规划 Redis 迁移方案(详见 `reports/redis_migration_plan.md`),核心设计:
|
||
|
||
- `RedisCacheManager` 提供与 `RAGCacheManager` 相同的接口
|
||
- 通过 `REDIS_CACHE_URL` 环境变量启用,向后兼容
|
||
- 语义缓存采用混合方案:FAISS 索引保持在进程内,缓存结果存储到 Redis
|
||
- Query Cache、Embedding Cache、Rerank Cache 全部迁移到 Redis
|
||
|
||
### 可选能力接入
|
||
|
||
`core/` 目录下仍保留以下独立模块,当前未接入生产流程,可按需启用:
|
||
|
||
- `confidence_gate.py`:置信度门控,基于 Reranker 分数判断检索质量
|
||
- `quality_assessor.py`:多维质量评估(相关性/完整性/准确性/覆盖面)
|
||
- `reasoning_reflector.py`:推理反思,检查未验证的假设
|
||
- `loop_guard.py`:循环防护,防止重复检索
|
||
|
||
---
|
||
|
||
## 参考资料
|
||
|
||
1. Xiong, G., et al. (2025). RAG-Gym: Systematic Optimization of Language Agents for Retrieval-Augmented Generation. arXiv:2502.13957
|
||
2. Zhang, W., et al. (2025). Process vs. Outcome Reward: Which is Better for Agentic RAG Reinforcement Learning. arXiv:2505.14069
|