docs: RAG数据流程新增缓存/图片/VLM/切片/性能章节 + 待解决风险项
- RAG数据流程.md: 更新模型配置为 deepseek-v4-flash,补充 v7.1 变更 - 新增十二~十六章节:缓存系统、图片选择管线、VLM 懒加载、 文档解析与切片、性能插桩 - 待解决风险项.md: 记录 7 个已识别但未实施的风险项
This commit is contained in:
335
docs/RAG数据流程.md
335
docs/RAG数据流程.md
@@ -67,18 +67,27 @@
|
||||
|
||||
| 用途 | 模型 | 说明 |
|
||||
|------|------|------|
|
||||
| 主 LLM | qwen3.6-flash | 回答生成、上下文理解 |
|
||||
| 意图分析 | qwen-turbo | 轻量快速,用于问题改写与意图判断 |
|
||||
| 主 LLM | deepseek-v4-flash | 回答生成、上下文理解 |
|
||||
| 意图分析 | deepseek-v4-flash | JSON 结构化输出(`INTENT_RESPONSE_FORMAT=True`) |
|
||||
| VLM | qwen-vl-plus | 图片理解与描述生成 |
|
||||
| 云端重排序 | qwen3-rerank | DashScope API 调用,替代本地 BGE-reranker |
|
||||
| 云端重排序 | qwen3-rerank | DashScope API 调用 |
|
||||
| 本地重排序 | bge-reranker-base | ONNX Runtime,用于图片 CrossEncoder 二次评分 |
|
||||
| Embedding | bge-base-zh-v1.5 | 文本向量化 |
|
||||
|
||||
### 1.3 v7.0.0 架构变更要点
|
||||
### 1.3 架构变更要点
|
||||
|
||||
- **Reranker**:从本地 BGE-reranker 切换为云端 DashScope API(qwen3-rerank)
|
||||
- **Reranker**:从本地 BGE-reranker 切换为云端 DashScope API(qwen3-rerank),本地 reranker 保留用于图片 CrossEncoder 二次评分
|
||||
- **MMR 去重**:使用文本相似度模式(`MMR_USE_EMBEDDING=false`),基于 Jaccard 系数
|
||||
- **Agentic 引擎**:拆分为多个子模块(agentic_search / agentic_answer / agentic_citation 等)
|
||||
- **Graph RAG**:模块已清空,不再使用
|
||||
|
||||
**v7.1 新增变更**:
|
||||
|
||||
- **性能插桩**:`/rag` 流式响应新增 `stages_ms` 字段,记录 8 个管线阶段耗时(intent / cache / search / vlm / image / rescue / llm / post)
|
||||
- **CrossEncoder 图片二次评分**:`select_images` 最终排序前新增 P3 阶段,用本地 bge-reranker-base 对 `(query, vlm_desc)` 做语义精排,映射为 ±5 分调整量;CE 负分图片直接剔除
|
||||
- **意图分析 JSON 强制输出**:新增 `INTENT_RESPONSE_FORMAT` 配置开关,推理模型需设为 False
|
||||
- **缓存管理端点**:新增 `POST /cache/clear` 一键清除精确缓存 + 语义缓存
|
||||
|
||||
---
|
||||
|
||||
## 二、请求入口
|
||||
@@ -340,8 +349,11 @@ enhance_retrieved_chunks(contexts, query, kb_name)
|
||||
| 查询类型 | 参数调整 |
|
||||
|----------|----------|
|
||||
| 精确图号查询("图2.3") | `MAX_IMAGES=2, MIN_SCORE=5.0` |
|
||||
| 弱图片意图("发电量图") | `MAX_IMAGES=1` |
|
||||
| 普通查询 | `MAX_IMAGES=2` |
|
||||
| 数据驱动(检索含图片切片) | `MAX_IMAGES=5, MIN_SCORE=2.0` |
|
||||
| 文本引用图表 | `MAX_IMAGES=3, MIN_SCORE=2.0` |
|
||||
| 普通查询 | `MAX_IMAGES=2, MIN_SCORE=3.0` |
|
||||
|
||||
表格嵌入图片时 `MAX_IMAGES` 可扩展至 15。
|
||||
|
||||
**步骤二:提取图表引用**
|
||||
|
||||
@@ -359,18 +371,37 @@ referenced_figures = {'2.3': {'source_file': 'xxx.pdf'}, ...}
|
||||
|--------|------|
|
||||
| 图号精确匹配(查询中有"图2.3") | +10 分 |
|
||||
| 表号精确匹配 | +10 分 |
|
||||
| 关键词匹配("发电量"等) | +2 分/个 |
|
||||
| 字符重叠 | +0.2 分/字符 |
|
||||
| 关键词匹配("发电量"等) | +2 分/个,上限 8 |
|
||||
| 字符重叠 | +0.2 分/字符,上限 3 |
|
||||
| 章节匹配 | +1.5 分 |
|
||||
| 图片类型(chart > image) | +2 / +1 分 |
|
||||
| 向量相似度 | +2 分(最高) |
|
||||
| 引用匹配(需章节相关) | +8 分 |
|
||||
|
||||
**步骤四:图文关联补充**
|
||||
**步骤四:VLM 与章节关联调整**
|
||||
|
||||
遍历 top 5 文本块中引用的图表编号,查找对应的图片切片并补充到结果中。
|
||||
| 条件 | 调整 |
|
||||
|------|------|
|
||||
| VLM 描述与查询相关(`_check_vlm_relevance >= 0.5`) | +2 分 |
|
||||
| VLM 描述与查询不相关(`< 0.3`) | -3 分 |
|
||||
| 图片章节与主检索不匹配(`section_similarity < 0.3`) | -5 分 |
|
||||
| 图号/表号被文本引用 + 章节匹配 | +8~+13 分 |
|
||||
| 来源文件匹配 primary_sources | +2 分 |
|
||||
|
||||
**步骤五:返回 top N 图片**
|
||||
**步骤五:CrossEncoder 语义精排(P3)**
|
||||
|
||||
对所有过线候选图片,用本地 bge-reranker-base 对 `(query, vlm_desc_or_caption)` 做二次评分:
|
||||
|
||||
```python
|
||||
ce_scores = engine.reranker.predict([(query, desc) for desc in descriptions])
|
||||
# 映射:ce_score > 0 → adjustment ∈ (0, +5],ce_score < 0 → adjustment ∈ [-5, 0)
|
||||
img['score'] += adjustment
|
||||
# CE 负分图片直接剔除
|
||||
```
|
||||
|
||||
**步骤六:后置过滤**
|
||||
|
||||
`_filter_images_by_answer()` 根据 LLM 回答内容反向筛选图片,过滤回答中未提及的无关图片。
|
||||
|
||||
**步骤七:返回 top N 图片**
|
||||
|
||||
```python
|
||||
scored_images.sort(key=lambda x: x['score'], reverse=True)
|
||||
@@ -607,3 +638,279 @@ VLM 描述示例(更精准):
|
||||
| `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()` |
|
||||
| `core/cache.py` | 三层精确缓存 | `RAGCacheManager`, `get_cache_manager()` |
|
||||
| `core/semantic_cache.py` | 语义缓存 | `SemanticCache`, `get_semantic_cache()` |
|
||||
| `knowledge/image_cleanup.py` | 孤儿文件清理 | `cleanup_image_orphans()`, `collect_referenced_images()` |
|
||||
| `cleanup_orphans.py` | 孤儿文件清理脚本 | `--force` 执行删除,默认 dry-run |
|
||||
| `sync_vlm_cache.py` | VLM 缓存同步 | `--re-embed` 强制重算 embedding |
|
||||
| `eval_image_retrieval.py` | 图片检索评测 | 检索层 + 选择层分层评测 |
|
||||
|
||||
---
|
||||
|
||||
## 十二、缓存系统
|
||||
|
||||
### 12.1 三层精确缓存(core/cache.py)
|
||||
|
||||
`RAGCacheManager` 管理三个独立的 `LRUCache` 实例:
|
||||
|
||||
| 缓存层 | Key 构造 | TTL | 容量 | kb_version 关联 |
|
||||
|--------|----------|-----|------|-----------------|
|
||||
| Query Cache | `MD5(query:kb_name:kb_version)` | 1h | 500 | ✅ 版本失效 |
|
||||
| Embedding Cache | `MD5(emb:{text})` | 24h | 2000 | ✅ 版本失效 |
|
||||
| Rerank Cache | `MD5(rerank:{query}:{sorted_doc_ids})` | 1h | 1000 | ❌ 变更时全清空 |
|
||||
|
||||
**知识库版本失效机制**:
|
||||
|
||||
```python
|
||||
def increment_kb_version(kb_name):
|
||||
old_version = self._kb_versions.get(kb_name, 0)
|
||||
self._kb_versions[kb_name] = old_version + 1
|
||||
# 失效旧版本
|
||||
self.query_cache.invalidate_by_version(old_version)
|
||||
self.embedding_cache.invalidate_by_version(old_version)
|
||||
self.rerank_cache.clear() # Rerank 无 kb_version,全量清空
|
||||
```
|
||||
|
||||
`sync.py` 文档变更时调用 `increment_kb_version()` 触发失效。
|
||||
|
||||
**写入保护**:`CACHE_MIN_SCORE = 0.3`,检索结果最高分低于此值时不写缓存,避免低质量结果被缓存。
|
||||
|
||||
### 12.2 语义缓存(core/semantic_cache.py)
|
||||
|
||||
`SemanticCache` 使用 FAISS 向量索引实现语义级缓存:
|
||||
|
||||
```
|
||||
查找:query_embedding → FAISS ANN 搜索 → 余弦相似度 > 0.92 → 候选
|
||||
二次验证:Jaccard(query, cached._raw_query) >= 0.5 → 确认命中
|
||||
存储:query_embedding + result → FAISS add
|
||||
```
|
||||
|
||||
**Jaccard 二次验证**(字符级):
|
||||
|
||||
```python
|
||||
set_a = set(query) # 字符集合
|
||||
set_b = set(cached_query)
|
||||
similarity = |intersection| / |union|
|
||||
```
|
||||
|
||||
**Intent 与 RAG 共用 FAISS 索引**,通过 `cache_type` 字段隔离:
|
||||
|
||||
| 缓存类型 | cache_type 值 | Embedding 输入 | 读取条件 |
|
||||
|----------|---------------|---------------|---------|
|
||||
| RAG 回答 | `"rag_answer"` | `embedding(query|collections)` | `cache_type == "rag_answer"` |
|
||||
| 意图分析 | `"intent_analysis"` | `embedding(query)` | `cache_type != "rag_answer"` |
|
||||
|
||||
**容量管理**:默认 `max_size=10000`,达到上限时 `self.clear()` 全清空(非 LRU 淘汰)。
|
||||
|
||||
**Intent 精确缓存**(独立于 FAISS):`_exact_cache: Dict[str, IntentAnalysis]`,key = `query + 最近2条历史摘要`,上限 500 条,无 TTL,满后停止接收新条目。
|
||||
|
||||
### 12.3 缓存命中链路
|
||||
|
||||
一个 `/rag` 请求依次经过以下缓存检查点:
|
||||
|
||||
```
|
||||
请求进入 chat_routes.rag()
|
||||
│
|
||||
├─ ① 语义缓存读取(FAISS)
|
||||
│ key = embedding(retrieval_query|collections)
|
||||
│ 要求 cache_type == "rag_answer"
|
||||
│ 二次验证: Jaccard >= 0.5
|
||||
│ ├─ 命中 → 直接流式返回缓存答案,流程结束
|
||||
│ └─ 未命中 → 继续
|
||||
│
|
||||
├─ ② intent_analyzer.analyze()
|
||||
│ ├─ 精确缓存: key = query + 历史摘要
|
||||
│ ├─ 语义缓存: key = embedding(query),cache_type != "rag_answer"
|
||||
│ └─ 未命中 → 调 LLM → 写入两层缓存
|
||||
│
|
||||
├─ ③ engine.search()
|
||||
│ ├─ Query Cache: MD5(query:kb_name:kb_version)
|
||||
│ ├─ Embedding Cache: MD5(emb:{text})
|
||||
│ └─ Rerank Cache: MD5(rerank:{query}:{sorted_doc_ids})
|
||||
│
|
||||
├─ ④ LLM 生成回答
|
||||
│
|
||||
└─ ⑤ 语义缓存写入(FAISS)
|
||||
存入 { cache_type: "rag_answer", answer, sources, images, _raw_query }
|
||||
```
|
||||
|
||||
**缓存清除**:`POST /cache/clear` 端点清除 Query/Embedding/Rerank 缓存 + 语义缓存(需网关认证)。
|
||||
|
||||
---
|
||||
|
||||
## 十三、图片选择管线
|
||||
|
||||
### 13.1 评分流程(select_images)
|
||||
|
||||
完整的图片选择管线包含 7 个阶段:
|
||||
|
||||
```
|
||||
score_image_relevance() ← P1: 基础分(关键词/图号/章节/类型)
|
||||
↓
|
||||
VLM 相关性调整 ← _check_vlm_relevance() -3 / +2 分
|
||||
↓
|
||||
章节关联惩罚 ← section_similarity < 0.3 → -5 分
|
||||
↓
|
||||
图号/表号引用加分 ← 文本引用匹配 → +8~+13 分
|
||||
↓
|
||||
来源文件加分 ← primary_sources 匹配 → +2 分
|
||||
↓
|
||||
MIN_SCORE 过滤 ← 动态阈值 2.0~5.0
|
||||
↓
|
||||
CrossEncoder 二次评分 ← P3: 本地 reranker 对 (query, desc) 精排
|
||||
↓ 正相关 +5,负相关 -5 且直接剔除
|
||||
MAX_IMAGES 预算控制 ← 动态上限 2~15 张
|
||||
↓
|
||||
_filter_images_by_answer() ← 后置过滤:LLM 回答关键词重叠
|
||||
```
|
||||
|
||||
### 13.2 图片召回保障
|
||||
|
||||
图片切片在检索层面临 CrossEncoder 系统性低分(0.002~0.08 vs 文本 0.3~0.9),有多重保障机制:
|
||||
|
||||
| 机制 | 位置 | 说明 |
|
||||
|------|------|------|
|
||||
| 图片独立召回 | `engine._search_image_chunks()` | 对 image/chart 切片单独查询 ChromaDB |
|
||||
| `_image_boost` 标记 | `chat_routes.py` | 图片意图查询时,为匹配的图片切片打 1.5x/2.0x boost 标记 |
|
||||
| 章节聚类救援 | `_rescue_section_cluster()` | 当整章节切片全被 rerank 压制时,保底分配分数 |
|
||||
| BM25 分歧救援 | `_rescue_bm25_divergence()` | BM25 高排名但 rerank 低分的切片被恢复 |
|
||||
| 词法匹配救援 | `_rescue_lexical_match()` | 切片文本精确包含查询关键词时提升分数 |
|
||||
|
||||
---
|
||||
|
||||
## 十四、VLM 懒加载增强
|
||||
|
||||
**入口文件**:`knowledge/lazy_enhance.py`
|
||||
**触发位置**:`chat_routes.py` `generate()` 函数
|
||||
|
||||
### 14.1 触发条件
|
||||
|
||||
| 切片类型 | 触发条件 | 调用模型 |
|
||||
|----------|---------|---------|
|
||||
| image / chart | `has_vlm_desc=False` 且 `image_path` 非空 | VLM(qwen-vl-plus) |
|
||||
| table | `has_summary=False` 且 `score > 0.7` | LLM(表格摘要) |
|
||||
| table + 关联图片 | `has_vlm_desc=False` 且 `image_path` 非空 | VLM |
|
||||
|
||||
### 14.2 增强流程
|
||||
|
||||
```
|
||||
缓存检查(图片 MD5 哈希 → .data/cache/vlm/{hash}.txt)
|
||||
├─ 缓存命中(内容 ≥ 5 字符)→ 直接返回
|
||||
└─ 缓存未命中 →
|
||||
├─ 调用 VLM/LLM 生成描述/摘要
|
||||
├─ 空描述保护(< 5 字符)→ 不写缓存,不更新向量库
|
||||
├─ 写入缓存文件
|
||||
└─ 更新 ChromaDB
|
||||
├─ metadata: { has_vlm_desc: True, vlm_desc: description }
|
||||
├─ embedding: 用 VLM 描述重新计算向量(通过 RAGEngine 单例获取模型)
|
||||
└─ document: 更新为 VLM 描述文本
|
||||
```
|
||||
|
||||
**embedding 模型获取**:`KnowledgeBaseManager` 没有 `embedding_model` 属性,通过 `_get_embedding_model()` 从 `RAGEngine` 单例获取。
|
||||
|
||||
### 14.3 后台异步化
|
||||
|
||||
VLM/LLM 调用在后台线程执行,不阻塞主流程:
|
||||
|
||||
```python
|
||||
# chat_routes.py 中
|
||||
_bg_contexts = copy.deepcopy(contexts) # 深拷贝避免线程竞争
|
||||
_t = threading.Thread(target=_background_enhance, daemon=True)
|
||||
_t.start()
|
||||
# 主流程继续 select_images + LLM 生成
|
||||
```
|
||||
|
||||
后台线程使用 `asyncio.run()` + 60s 超时保护。首次查询时图片使用 caption + section 作为临时描述,后台生成完成后写入 ChromaDB,下次查询直接使用缓存。
|
||||
|
||||
---
|
||||
|
||||
## 十五、文档解析与切片
|
||||
|
||||
### 15.1 MinerU 解析流程
|
||||
|
||||
**入口文件**:`parsers/mineru_parser.py`
|
||||
|
||||
支持两种云端 API 模式:
|
||||
|
||||
| 模式 | 端点 | 延迟 | 适用场景 |
|
||||
|------|------|------|---------|
|
||||
| v4 precise | `/api/v4/` | 较长(需排队) | 高精度 PDF 解析 |
|
||||
| v1 agent | `/api/v1/agent/` | ~10s | 轻量级文档处理 |
|
||||
|
||||
**V2 格式转换**(`_parse_v2_content_list`):
|
||||
|
||||
MinerU 返回的 content_list 被转换为 `MinerUChunk` 对象,提取的字段包括:`_v2_table_type`、`_v2_table_nest_level`、`_v2_list_type`、`sub_type`(部分字段在下游切片时被丢弃)。
|
||||
|
||||
**标题检测**(`parsers/heading_rules.py`):
|
||||
|
||||
基于规则的多级标题检测,`_validate_level()` 长度守卫防止长文本误判为标题:
|
||||
|
||||
| 级别 | 最大长度 | 超出后 |
|
||||
|------|---------|--------|
|
||||
| H1 | 40 字符 | 降级为普通文本 |
|
||||
| H2 | 60 字符 | 降级为普通文本 |
|
||||
| H3 | 50 字符 | 降级为普通文本 |
|
||||
|
||||
`chinese_article` 规则额外限制 `max_length=30`,`numeric_level1` 排除以标点结尾的误判。
|
||||
|
||||
### 15.2 切片后处理
|
||||
|
||||
**`_post_process_chunks`** 核心逻辑:
|
||||
|
||||
1. **连续标题链合并**:连续 title 类切片合并为一个 chunk(保留最高 text_level)
|
||||
2. **body 合并后 text_level 重置**:body 合并进 title buffer 后执行 `buffer.text_level = 0`,防止下一个标题被错误合并
|
||||
3. **元数据写入**:
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `chunk_type` | str | `text` / `title` / `table` / `image` / `chart` |
|
||||
| `section` | str | 章节路径(如 `综述 > 2.3发电`) |
|
||||
| `text_level` | int | 标题级别(0=body, 1=H1, 2=H2, 3=H3) |
|
||||
| `image_path` | str | 图片文件名 |
|
||||
| `bbox` | str | 页面坐标(仅 PDF) |
|
||||
| `has_vlm_desc` | bool | 是否已有 VLM 描述 |
|
||||
|
||||
---
|
||||
|
||||
## 十六、性能插桩
|
||||
|
||||
**入口文件**:`api/chat_routes.py` `generate()` 函数
|
||||
|
||||
### 16.1 各阶段计时
|
||||
|
||||
在 SSE 流式生成器中初始化计时字典,记录 8 个管线阶段耗时:
|
||||
|
||||
| 阶段 key | 记录时机 | 覆盖范围 |
|
||||
|----------|----------|----------|
|
||||
| `intent` | 意图分析完成后 | IntentAnalyzer.analyze() |
|
||||
| `cache` | 语义缓存检查后 | semantic_cache.lookup() |
|
||||
| `search` | 混合检索+切片处理完成后 | engine.search() + 上下文提取 |
|
||||
| `vlm` | VLM 懒加载完成后 | enhance_retrieved_chunks() |
|
||||
| `image` | select_images 完成后 | 图片选择 |
|
||||
| `rescue` | 救援管线+上下文构建完成后 | 聚类/BM25/预算 |
|
||||
| `llm` | LLM 流式生成完成后 | engine.generate_answer_stream() |
|
||||
| `post` | finish 事件构建完成后 | 答案对齐+debug 事件 |
|
||||
|
||||
### 16.2 数据输出
|
||||
|
||||
**SSE finish 事件**:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "finish",
|
||||
"timing": {
|
||||
"total_ms": 12345,
|
||||
"stages_ms": {
|
||||
"intent": 4200, "cache": 50, "search": 900,
|
||||
"vlm": 10, "image": 30, "rescue": 200,
|
||||
"llm": 6800, "post": 150
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**服务端日志**:
|
||||
|
||||
```
|
||||
[性能] 总12345ms | 意图4200 缓存50 检索900 VLM10 图片30 救援200 LLM6800 后处理150
|
||||
```
|
||||
|
||||
125
docs/待解决风险项.md
Normal file
125
docs/待解决风险项.md
Normal file
@@ -0,0 +1,125 @@
|
||||
# 待解决风险项
|
||||
|
||||
> 本文档记录已识别但尚未实施修复的风险项,供后续迭代参考。
|
||||
> 更新日期:2026-06-21
|
||||
|
||||
---
|
||||
|
||||
## P1: SemanticCache 满容量全清空
|
||||
|
||||
**文件**:`core/semantic_cache.py:161-164`
|
||||
|
||||
**现状**:当 FAISS 索引达到 `max_size`(默认 10000)时,执行 `self.clear()` 全清空,而非 LRU 淘汰最旧条目。
|
||||
|
||||
**影响**:高使用量下缓存命中率周期性断崖式下降,下一波请求全部 miss。
|
||||
|
||||
**建议方案**:
|
||||
- 短期:可接受,断崖后缓存会重新积累
|
||||
- 长期:改为分批淘汰(每次淘汰最旧 N 条),或引入 TTL 自动过期
|
||||
|
||||
**风险等级**:中(性能波动,不影响正确性)
|
||||
|
||||
---
|
||||
|
||||
## P2: _exact_cache 满后永久拒绝新条目
|
||||
|
||||
**文件**:`core/intent_analyzer.py:412-413`
|
||||
|
||||
**现状**:
|
||||
|
||||
```python
|
||||
if len(self._exact_cache) < self._exact_cache_max: # 500
|
||||
self._exact_cache[exact_key] = analysis
|
||||
```
|
||||
|
||||
500 条满后不再接受新条目,且无淘汰机制。服务运行一段时间后,精确缓存变为固定快照。
|
||||
|
||||
**影响**:后续查询的精确缓存命中率逐渐归零,退化为每次都走语义缓存或 LLM 调用。
|
||||
|
||||
**建议方案**:改用 `OrderedDict` + 淘汰最旧条目,与 `LRUCache` 保持一致。
|
||||
|
||||
**风险等级**:低(性能退化,不影响正确性)
|
||||
|
||||
---
|
||||
|
||||
## P3: Jaccard 字符级阈值偏松
|
||||
|
||||
**文件**:`core/intent_analyzer.py:450-473`
|
||||
|
||||
**现状**:语义缓存二次验证使用字符级 Jaccard 相似度,阈值 0.5。中文短句共享大量单字(如"如何申请" vs "如何拒绝"),Jaccard = 2/4 = 0.5 刚好过线。
|
||||
|
||||
**影响**:语义相似但意图不同的查询可能误命中缓存。
|
||||
|
||||
**建议方案**:
|
||||
- 方案 A:提升阈值到 0.6
|
||||
- 方案 B:改用 bigram Jaccard(相邻字对作为集合元素)
|
||||
|
||||
**风险等级**:低(误命中概率不高,且有 embedding 0.92 阈值前置过滤)
|
||||
|
||||
---
|
||||
|
||||
## P4: /cache/clear 无 DEV_MODE 守卫
|
||||
|
||||
**文件**:`api/sync_routes.py:322`
|
||||
|
||||
**现状**:`POST /cache/clear` 端点仅使用 `@require_gateway_auth`,没有 `DEV_MODE` 检查。生产环境任何认证用户都能清缓存。其他 dev-only 端点(如 `document_routes.py` 的预览接口)有 `DEV_MODE` 守卫。
|
||||
|
||||
**影响**:生产环境缓存被误清,导致短暂的性能下降。
|
||||
|
||||
**建议方案**:添加 `DEV_MODE` 检查,或限制为 admin 角色。
|
||||
|
||||
**风险等级**:中(生产环境影响)
|
||||
|
||||
---
|
||||
|
||||
## P5: PDF TOC 目录数据未单独处理
|
||||
|
||||
**文件**:`parsers/mineru_parser.py`
|
||||
|
||||
**现状**:PDF 文档的目录页(TOC)被 MinerU 解析为多个 text_level=1 的标题切片,内容包含 `....1` 等页码标记。这些目录切片入库后污染 section 元数据,干扰章节过滤和上下文扩展。
|
||||
|
||||
**影响**:RAG 检索时目录切片可能被误判为相关章节,干扰上下文扩展的 section 精确匹配。
|
||||
|
||||
**建议方案**:
|
||||
- 解析阶段检测 TOC 模式(连续短标题 + 页码标记)
|
||||
- 标记 `chunk_type: 'toc'` 或直接跳过不入库
|
||||
|
||||
**风险等级**:中(影响检索质量)
|
||||
|
||||
---
|
||||
|
||||
## P6: PDF chart VLM 描述缺失
|
||||
|
||||
**文件**:`knowledge/lazy_enhance.py`、`parsers/mineru_parser.py`
|
||||
|
||||
**现状**:部分 PDF 图表切片的 VLM 描述为空。原因可能是:
|
||||
1. MinerU VLM API 对部分图表返回空 content(上游问题)
|
||||
2. 解析阶段的提取条件过严(如要求 `'|' in markdown` 才提取 chart_markdown)
|
||||
|
||||
**影响**:无 VLM 描述的图表切片退化为纯关键词匹配,图片选择准确率下降。
|
||||
|
||||
**建议方案**:
|
||||
- 上游:跟进 MinerU API 的空 content 问题
|
||||
- 本地兜底:用 `caption + section + 上下文文本` 拼接作为 fallback 描述
|
||||
- 定期重算:用 `sync_vlm_cache.py --re-embed` 批量重算
|
||||
|
||||
**风险等级**:低(有 lazy_enhance 兜底,首次查询后补生成)
|
||||
|
||||
---
|
||||
|
||||
## P7: 图片选择负面用例误召回
|
||||
|
||||
**文件**:`api/chat_routes.py`
|
||||
|
||||
**现状**:定义/原则类查询(如"五化终端定义"、"防洪调度原则")不应返回图片,但 `select_images` 仍返回了 2-3 张。根因:
|
||||
1. `_filter_images_by_answer` 的关键词重叠阈值对长回答偏宽松
|
||||
2. 兜底逻辑:过滤后为空时保留分数最高的 1 张
|
||||
|
||||
**影响**:用户看到不相关的图片,降低信任度。
|
||||
|
||||
**建议方案**:
|
||||
- 增加查询意图判断:纯定义/原则类查询不应触发图片返回
|
||||
- 移除兜底逻辑或提高兜底阈值
|
||||
- 增加"不应返回图片"的负面评测用例
|
||||
|
||||
**风险等级**:中(影响用户体验)
|
||||
Reference in New Issue
Block a user