From b966be5417d93612ed0bad059a0f2f68a66df256 Mon Sep 17 00:00:00 2001 From: lacerate551 <128470311+lacerate551@users.noreply.github.com> Date: Mon, 22 Jun 2026 13:08:14 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E6=AD=A3=20RAG=E6=80=A7?= =?UTF-8?q?=E8=83=BD=E5=88=86=E6=9E=90=E6=8A=A5=E5=91=8A=E4=B8=AD=E7=9A=84?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E5=92=8C=E9=81=97=E6=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - INTENT_MAX_TOKENS 2048 不再标注为'浪费',推理模型思考链需 ~1000 token - 删除 P2 建议'INTENT_MAX_TOKENS 降低 2048→512'(推理模型不可降) - P0 优化建议标注 deepseek-v4-flash 额度已尽,需寻找替代轻量模型 - Rerank 配置补充云端 xop3qwen8breranker(讯飞云)信息 - §6.4 休眠模块:AgenticRAG 类已在 v4.0 删除,更新描述 - 阶段 7 补充两管线断裂问题:chart_contexts 降门槛、P0 安全网、答案后过滤鸡生蛋问题 - 阶段 9 补充推理模型 content 为空问题和 reasoning_content 回退机制 - 阶段 10 图片后过滤补充 _filter_images_by_answer 函数名 --- docs/RAG性能分析报告.md | 460 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 460 insertions(+) create mode 100644 docs/RAG性能分析报告.md diff --git a/docs/RAG性能分析报告.md b/docs/RAG性能分析报告.md new file mode 100644 index 0000000..6d520c5 --- /dev/null +++ b/docs/RAG性能分析报告.md @@ -0,0 +1,460 @@ +# RAG 知识库问答系统 — 性能分析报告 + +> 基于 15 题基准测试 + 代码静态分析 | 2026-06-22 +> +> 基准测试环境:本地 deepseek-v4-flash 模型,服务器使用 mimo-v2.5 时延迟特征可能不同 + +--- + +## 一、完整请求管线总览 + +一次 `/rag/stream` 请求经历 **10 个串行阶段**,每个阶段必须等待前一阶段完成后才能开始。SSE 流式响应在 LLM 生成阶段才开始产出用户可见内容,前 8 个阶段用户感知为"等待中"。 + +``` +用户提问 + │ + ├─ 0. 输入校验 & 安全过滤 ~1ms + ├─ 1. 意图分析(LLM 调用) ~3500-5000ms ◄── 瓶颈 #1 + ├─ 2. 语义缓存查找 ~50-200ms + ├─ 3. 混合检索(向量+BM25+图片) ~200-600ms + ├─ 4. RRF 融合 + Rerank ~300-800ms + ├─ 5. MMR 去重 ~5-20ms + ├─ 6. 救援管线(3层) ~5-30ms + ├─ 7. 图片选择 + VLM 增强 ~10-50ms + ├─ 8. 上下文构建 ~5-15ms + ├─ 9. LLM 流式生成 ~5000-15000ms ◄── 瓶颈 #2 + └─ 10. 后处理(对齐/引用/会话存储) ~50-200ms +``` + +**总耗时**:典型请求 **10-20 秒**(含流式输出时间),其中 LLM 调用占 **80%以上**。 + +--- + +## 二、各阶段详细分析 + +### 阶段 1:意图分析 — 最大瓶颈之一 + +**位置**:`core/intent_analyzer.py` → `analyze()` 方法 + +**功能**:一次非流式 LLM 调用,同时完成问题改写、指代消解、意图分类、子查询生成。 + +**执行流程**: + +``` +精确缓存查找(Dict, O(1)) + ↓ 未命中 +语义缓存查找(FAISS, cosine ≥ 0.92) + ↓ 未命中 +LLM 调用(非流式,JSON 输出) ◄── 主要耗时 + ↓ +写入双层缓存 + 返回 IntentAnalysis +``` + +**关键参数**: + +| 参数 | 服务器值 | 说明 | +|------|----------|------| +| INTENT_MODEL | mimo-v2.5 | 与主模型共用,无法使用轻量模型 | +| INTENT_TEMPERATURE | 0.1 | 低温度保证确定性输出 | +| INTENT_MAX_TOKENS | 2048 | 推理模型思考链 ~1000 token + JSON 输出 ~200 token,配置合理 | +| INTENT_HISTORY_WINDOW | 6 | 取最近 6 条历史消息 | +| 精确缓存上限 | 500 条 | 内存 Dict,重启清零 | +| 语义缓存阈值 | 0.92 | 余弦相似度,过于严格则命中率低 | + +**实测耗时**(deepseek-v4-flash):3500-5000ms。mimo-v2.5 因模型更大,预期 **4000-7000ms**。 + +**瓶颈分析**: + +1. **非流式调用**:必须等待完整 JSON 响应才能解析,无法提前返回。这是所有阶段中唯一必须完整等待的 LLM 调用。 +2. **模型过重**:意图分析本质是分类+改写任务,不需要 mimo-v2.5 这样的推理大模型。换用轻量非推理模型可减少 30-40% 延迟,但百炼 deepseek-v4-flash 额度已尽,需寻找其他可用轻量模型。 +3. **MAX_TOKENS 配置合理**:mimo-v2.5 是推理模型,思考链消耗 ~1000 token,JSON 输出 ~200 token,2048 是必要预算(此前 1024 导致 content 为空、全部输出进入 reasoning_content 引发解析错误)。 +4. **缓存命中率低**:精确缓存 key 包含历史上下文,同一用户连续问不同问题时不会命中;语义缓存阈值 0.92 过于严格。 + +### 阶段 2:语义缓存查找 + +**位置**:`core/semantic_cache.py` + +**功能**:FAISS 向量索引,用 embedding 余弦相似度匹配历史问答,命中则跳过整个检索+生成流程。 + +**查找逻辑**: +1. 将 `"{query}|{collections}"` 编码为向量 +2. FAISS IndexFlatIP 搜索 top-1(向量已归一化,内积=余弦) +3. 阈值 ≥ 0.92 → 命中 +4. 验证 `cache_type == "rag_answer"`(区分意图缓存和问答缓存) + +**性能**:50-200ms(主要是 embedding 编码耗时,FAISS 搜索本身 <1ms) + +**瓶颈分析**:此阶段本身不慢,但受限于 0.92 的高阈值,实际命中率较低。同一个问题的不同表述方式(如"智启平台支持哪些数据源" vs "智启能对接什么数据库")余弦相似度可能在 0.85-0.91 之间,无法命中。降低阈值会引入误命中风险。 + +### 阶段 3:混合检索 + +**位置**:`core/engine.py` → `search_knowledge()` + +**功能**:三路并行召回 + RRF 融合 + +**三路召回**: + +| 召回路径 | 数量 | 后端 | 耗时 | +|----------|------|------|------| +| 向量检索 | recall_k = max(20, 30×3) = 90 | ChromaDB + bge-base-zh-v1.5 (CPU) | 100-300ms | +| BM25 关键词 | top_k=30 | rank_bm25 (jieba 分词) | 10-30ms | +| 图片独立召回 | n_results=5 | ChromaDB,过滤 chunk_type | 20-50ms | + +**RRF 融合**(Reciprocal Rank Fusion): + +``` +score = weight / (k + rank + 1),k = 60 + +动态权重(按查询长度自适应): + alpha = clamp((len(query) - 15) / 35, 0, 1) + vector_weight = 0.3 + 0.4 × alpha # 短查询 0.3,长查询 0.7 + bm25_weight = 1.0 - vector_weight +``` + +**性能**:总计 200-600ms。Embedding 编码是主要耗时(CPU 推理),ChromaDB 查询本身很快。 + +**瓶颈分析**: + +1. **Embedding 在 CPU 上运行**:bge-base-zh-v1.5 编码 90 个候选文档需要 100-200ms。GPU 可降至 10-30ms,但当前服务器未配置 GPU。 +2. **召回数量偏大**:`recall_k=90` 意味着 embedding 编码量大。实际 Rerank 只取 20 个,多召回的 70 个是浪费。可降低 `RECALL_MULTIPLIER` 从 3 到 2。 +3. **BM25 无瓶颈**:纯内存计算,jieba 分词+BM25 打分共 10-30ms。 + +### 阶段 4:Rerank + +**位置**:`core/engine.py` → `rerank_results()` + +**功能**:CrossEncoder 精排,对融合后的候选重打分 + +**服务器配置**:`RERANK_BACKEND = "local"`,使用 ONNX 格式的 bge-reranker-base。云端备选为 `xop3qwen8breranker`(讯飞云 API),`RERANK_BACKEND = "cloud"` 或 `"fallback"` 时启用。 + +**流程**: +1. 检查 Rerank 缓存(MD5 of query + sorted_doc_ids) +2. 缓存未命中 → 调用 ONNX 推理,predict(query, doc) × N +3. 按分数排序,截取 `RERANK_TOP_K=15` + +**性能**: +- 缓存命中:<1ms +- 缓存未命中(20 个候选):300-800ms(CPU ONNX 推理) + +**瓶颈分析**: + +1. **CPU 推理**:CrossEncoder 是最重的 CPU 计算任务。每个 (query, doc) pair 需要一次 forward pass。20 个候选 × ~30ms/pair ≈ 600ms。 +2. **max_length=512**:长文档截断到 512 token,避免 OOM 但可能丢失信息。 +3. **缓存效果好**:同一查询+同一文档集合直接命中缓存。但首次查询必经此阶段。 + +### 阶段 5:MMR 去重 + +**位置**:`core/mmr.py` + +**当前配置**:`MMR_USE_EMBEDDING = False`(文本模式),使用 jieba 词级 Jaccard 相似度 + +**算法**:贪心选择,每次取与已选集合最不相似且与查询最相关的候选 + +**性能**:5-20ms(纯文本计算,无 embedding 开销) + +**瓶颈分析**:**无瓶颈**。文本模式 Jaccard 非常轻量。 + +### 阶段 6:救援管线(3 层) + +**位置**:`api/chat_routes.py` 内部函数 + `core/engine.py` + +**第一层 — BM25 发散救援**: +- 当 BM25 top-3 中的文档被 Rerank 打到极低分时,恢复其分数到 `CLUSTER_RESCUE_FLOOR=0.06` +- 防止关键词完全匹配但语义分低的文档被丢弃 + +**第二层 — 词法匹配救援**: +- 提取查询 bigram,对每个低分文档计算 bigram 匹配率 +- 匹配率 > 0.35 → 提升到 floor +- 附带邻居救援:同文档 ±8 个 chunk_index 的邻居也被提升 + +**第三层 — 章节聚类救援**(engine.py `_section_cluster_boost`): +- 按 `(source, normalized_section)` 分组 +- 检测"全灭章节":所有成员分数 < min_score +- 要求 ≥ 3 成员 + ≥ 2 种 chunk_type +- 提升最强聚类的 top-3 章节,每节最多 8 个 chunk + +**性能**:5-30ms(纯规则计算) + +**瓶颈分析**:**无瓶颈**。但在复杂表格/对比查询中,救援管线的效果直接影响回答质量。 + +### 阶段 7:图片选择 + +**位置**:`api/chat_routes.py` → `select_images()` + +**评分体系**(多层叠加): + +| 评分因子 | 分值 | 条件 | +|----------|------|------| +| 图号精确匹配 | +10.0 | 查询提到"图2.3"且匹配 | +| 表号精确匹配 | +10.0 | 查询提到"表1"且匹配 | +| 关键词匹配 | +2.0/词 | jieba 分词后匹配,上限 +8.0 | +| 字符重叠 | +0.2/字 | 上限 +3.0 | +| 章节匹配 | +1.5/关键词 | 图片章节与查询关键词重叠 | +| 图表类型 | +2.0 (chart) / +1.0 (image) | chunk_type 加分 | +| 引用+章节匹配 | +8.0 + 5.0 | 被文本引用且章节/来源匹配 | +| VLM 相关性 < 0.3 | -3.0 | VLM 描述与查询不相关 | +| VLM 相关性 ≥ 0.5 | +2.0 | VLM 描述与查询相关 | +| 章节距离过远 | -5.0 | section_similarity < 0.3 且非引用 | + +**动态预算**: + +| 场景 | MAX_IMAGES | MIN_SCORE | +|------|------------|-----------| +| 精确图号查询 | 2 | 5.0 | +| 检索结果含图片数据 | 5 | 2.0 | +| 文本中有图号引用 | 3 | 2.0 | +| 默认 | 2 | 3.0 | + +**性能**:10-50ms(规则计算 + VLM 相关性检查) + +**瓶颈分析**:图片选择本身不慢。瓶颈在 **VLM 描述生成**(`knowledge/lazy_enhance.py`),需要调用 VLM 模型为每张图片生成文字描述。当前设计为懒加载(首次查询时生成并缓存),首次查询可能有 60s 超时。 + +**两管线断裂问题**:`select_images()` 独立于文本上下文管线选择图片,但图片描述可能因 `min_score` 过滤未进入 LLM 上下文。具体问题链: + +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 安全网**:即使降门槛后,图片描述仍可能被 `_build_context_with_budget()` 截断。安全网在 LLM 生成前检查:若 `selected_images` 的描述不在 `context_text` 中,强制追加缺失描述。日志标记为 `[P0] 图片描述未完整注入 context`。 +3. **答案后过滤鸡生蛋问题**:`_filter_images_by_answer()` 根据 LLM 答案内容过滤图片。若 LLM 因上下文缺失而回答"未找到某图表",正确图片会被误过滤。P0 安全网可缓解此问题。 + +### 阶段 8:上下文构建 + +**位置**:`api/chat_routes.py` → `_build_context_with_budget()` / `_order_text_contexts_for_prompt()` + +**预算控制**: + +``` +CONTEXT_MAX_CHARS = 8000 # 硬上限(约 4000 token) +CONTEXT_SOFT_LIMIT = 6000 # 软限制(超过后只接受高分 chunk) +MAX_CONTEXT_CHUNKS = 20 # 最大切片数 +``` + +**排序策略**: +1. 枚举/对比查询:保持原文顺序,注入 `━ section ━` 分隔符 +2. 普通查询:按 (source, section) 分组 → 组内按 chunk_index 排序 → 组间按 max_rerank_score 排序 +3. 表格保护:即使分数低于 min_score,只要同章节有高分文本 chunk,表格 chunk 也保留(floor = min_score × 0.3) + +**性能**:5-15ms + +**瓶颈分析**:**无瓶颈**。但上下文质量直接影响 LLM 生成质量。8000 字符 ≈ 4000 token,对于需要对比多个文档的查询可能不够。 + +### 阶段 9:LLM 流式生成 — 最大瓶颈 + +**位置**:`core/engine.py` → `generate_answer_stream()` + +**功能**:将上下文 + 历史 + 查询组装成 prompt,调用 LLM 流式输出 + +**Prompt 结构**: +``` +[System] 你是一个专业的知识库问答助手...(规则指令) +[System] 参考资料:(context, 最多 8000 字符) +[User×N] 历史对话(最多 10 轮) +[User] 当前问题 +``` + +**关键参数**: + +| 参数 | 服务器值 | 说明 | +|------|----------|------| +| MODEL | mimo-v2.5 | 主生成模型 | +| LLM_TEMPERATURE | 0.7 | 较高温度,生成更发散 | +| LLM_MAX_TOKENS | 3000 | 最大输出 token | +| LLM_TOP_P | (未设置) | 默认 1.0 | + +**实测耗时**(deepseek-v4-flash):5000-15000ms(含流式输出)。mimo-v2.5 推理模型预计 **8000-20000ms**。 + +**瓶颈分析**: + +1. **模型延迟不可控**:LLM API 的 TTFT(首 token 时间)和 TPS(token/秒)取决于服务端负载和网络。这是整个管线中唯一无法通过代码优化的瓶颈。 +2. **推理模型 content 为空问题**:mimo-v2.5 是推理模型,`max_tokens` 不足时思考链消耗全部预算,`content` 为空需从 `reasoning_content` 提取内容。当前 `LLM_MAX_TOKENS=3000` 足够,但若降低此值需注意思考链预算。 +3. **Prompt 长度影响 TTFT**:8000 字符上下文 + 10 轮历史 ≈ 6000-8000 input token。输入越长,TTFT 越高。 +4. **流式缓解**:用户看到第一个 token 的等待时间 = 意图分析 + 检索 + TTFT ≈ 6-12 秒。流式输出减少了感知等待,但总耗时不变。 +5. **MAX_TOKENS=3000 配置合理**:推理模型思考链占用部分预算,实际回答通常 500-1500 token,3000 平衡速度与质量。 + +### 阶段 10:后处理 + +**功能**:答案对齐、图片过滤、引用附加、会话存储、语义缓存写入 + +**子步骤**: + +| 步骤 | 耗时 | 说明 | +|------|------|------| +| 图号引用提取 | ~5ms | 正则匹配答案中的图/表引用 | +| 图片后过滤 | ~5ms | `_filter_images_by_answer()`,根据 LLM 答案内容裁剪不相关图片 | +| 引用清理 | ~2ms | 去除 LLM 添加的 [N] 标记 | +| 引用附加 | ~10ms | 添加结构化 [ref:chunk_id] | +| 敏感内容过滤 | ~5ms | prompt_guard 检查 | +| 会话存储 | ~20-100ms | SQLite 写入 | +| 语义缓存写入 | ~10-50ms | FAISS 索引添加 | + +**总计**:50-200ms。**无瓶颈**。 + +--- + +## 三、缓存体系分析 + +### 五层缓存架构 + +``` +精确缓存层 + ├─ Query Cache LRU 500, TTL 1h 最终结果缓存(含 rerank 分数) + ├─ Embedding Cache LRU 2000, TTL 24h 文档向量缓存 + ├─ Rerank Cache LRU 1000, TTL 1h CrossEncoder 分数缓存 + └─ Intent Cache Dict 500, 无 TTL 意图分析结果缓存 + +语义缓存层 + └─ FAISS Cache max 10000, 阈值 0.92 向量化相似问答缓存 +``` + +### 缓存命中场景 + +| 场景 | 命中层 | 节省时间 | +|------|--------|----------| +| 完全相同的问题 + 相同知识库 | Query Cache | 跳过整个检索(节省 ~1s) | +| 相似问题(余弦 ≥ 0.92) | Semantic Cache | 跳过检索 + 生成(节省 ~10s) | +| 相同文档集合被 rerank | Rerank Cache | 跳过 CrossEncoder(节省 ~600ms) | +| 相同文档需要 embedding | Embedding Cache | 跳过编码(节省 ~100ms) | +| 相同问题(意图层面) | Intent Cache | 跳过意图分析 LLM(节省 ~4s) | + +### 缓存问题 + +1. **内存存储,重启清零**:所有缓存都在进程内存中,服务重启后第一次请求全部冷启动。 +2. **语义缓存阈值过高**:0.92 的余弦阈值导致换一种说法就命中不了。 +3. **Rerank 缓存不参与 kb_version 失效**:文档更新后 rerank 缓存可能返回旧分数。 +4. **语义缓存淘汰策略粗暴**:达到 10000 上限时全量清空(`clear()`),不是 LRU。 + +--- + +## 四、瓶颈总结与优化优先级 + +### 耗时分布(典型请求,基于 deepseek-v4-flash 实测) + +``` +意图分析 ████████████████████ 25-35% ~4000ms +LLM 生成 ████████████████████████████████ 45-60% ~8000ms +检索+Rerank ████████ 10-15% ~1200ms +其他阶段 ██ 3-5% ~400ms +``` + +### 优化机会排序 + +| 优先级 | 方向 | 预期收益 | 难度 | 风险 | +|--------|------|----------|------|------| +| **P0** | 意图分析换轻量模型 | 节省 2-3s/请求 | 低 | 低(需寻找可用轻量模型,百炼 deepseek-v4-flash 额度已尽) | +| **P0** | 主模型 API 优化(换供应商/批处理) | 节省 3-5s/请求 | 中 | 中(需要评估质量) | +| **P1** | 意图分析结果缓存优化 | 重复问题节省 4s | 低 | 低 | +| **P1** | 语义缓存阈值调优(0.92→0.88) | 提高命中率,节省 10s | 低 | 中(可能误命中) | +| **P2** | RECALL_MULTIPLIER 降低(3→2) | 减少 embedding 编码量 ~30ms | 低 | 低 | +| **P2** | LLM_TEMPERATURE 降低(0.7→0.3) | 减少发散,回答更精准 | 低 | 中(可能影响创造性) | +| **P3** | Rerank 换 GPU 推理 | 节省 ~500ms | 高(需硬件) | 低 | +| **P3** | Embedding 换 GPU 推理 | 节省 ~150ms | 高(需硬件) | 低 | +| **P3** | 语义缓存改 LRU 淘汰 | 避免全量清空抖动 | 中 | 低 | + +--- + +## 五、配置参数速查表 + +### 模型与服务 + +| 参数 | 当前值 | 说明 | +|------|--------|------| +| DASHSCOPE_MODEL | mimo-v2.5 | 主 LLM | +| INTENT_MODEL | mimo-v2.5 | 意图分析模型 | +| VLM_MODEL | mimo-v2.5 | 图片描述模型 | +| EMBEDDING_MODEL_PATH | bge-base-zh-v1.5 | 本地 embedding | +| RERANK_BACKEND | local | 本地 ONNX rerank(可选 cloud/fallback) | +| RERANK_CLOUD_MODEL | xop3qwen8breranker | 云端 rerank(讯飞云 API) | + +### 检索参数 + +| 参数 | 当前值 | 说明 | +|------|--------|------| +| RAG_SEARCH_TOP_K | 30 | 最终返回数 | +| RECALL_MULTIPLIER | 3 | 向量召回倍数 | +| RERANK_CANDIDATES | 20 | 送入 Rerank 的候选数 | +| RERANK_TOP_K | 15 | Rerank 后保留数 | +| RERANK_CONTEXT_MIN_SCORE | 0.05 | 最低 rerank 分数阈值 | +| MMR_TOP_K | 30 | MMR 处理后保留数 | +| MMR_LAMBDA | 0.5 | 相关性/多样性平衡 | +| MMR_USE_EMBEDDING | False | 使用 jieba Jaccard | +| RRF_K | 60 | RRF 平滑参数 | +| DYNAMIC_RRF_ENABLED | True | 按查询长度自适应权重 | + +### 上下文构建 + +| 参数 | 当前值 | 说明 | +|------|--------|------| +| CONTEXT_MAX_CHARS | 8000 | 上下文硬上限(字符) | +| CONTEXT_SOFT_LIMIT | 6000 | 软限制(超此只接受高分) | +| MAX_CONTEXT_CHUNKS | 20 | 最大切片数 | +| MAX_HISTORY_ROUNDS | 10 | 对话历史上限 | + +### LLM 生成 + +| 参数 | 当前值 | 说明 | +|------|--------|------| +| LLM_TEMPERATURE | 0.7 | 生成温度 | +| LLM_MAX_TOKENS | 3000 | 最大输出 token | +| LLM_TOP_P | 未设置 | 默认 1.0(不限制) | + +### 缓存 + +| 参数 | 当前值 | 说明 | +|------|--------|------| +| QUERY_CACHE_SIZE | 500 | 查询缓存容量 | +| QUERY_CACHE_TTL | 3600s | 查询缓存有效期 | +| EMBEDDING_CACHE_SIZE | 2000 | 向量缓存容量 | +| EMBEDDING_CACHE_TTL | 86400s | 向量缓存有效期 | +| RERANK_CACHE_SIZE | 1000 | Rerank 缓存容量 | +| RERANK_CACHE_TTL | 3600s | Rerank 缓存有效期 | +| SEMANTIC_CACHE_THRESHOLD | 0.92 | 语义缓存余弦阈值 | + +### 救援管线 + +| 参数 | 当前值 | 说明 | +|------|--------|------| +| CLUSTER_MIN_MEMBERS | 3 | 聚类最少成员数 | +| CLUSTER_MIN_TYPES | 2 | 聚类最少类型数 | +| CLUSTER_SEED_FLOOR | 0.35 | 种子分数下限 | +| CLUSTER_RESCUE_FLOOR | 0.06 | 救援后分数 | +| CLUSTER_MAX_SECTIONS | 3 | 最多救援章节数 | +| BM25_DIVERGENCE_RESCUE_ENABLED | True | BM25 发散救援开关 | +| BM25_DIVERGENCE_MAX_RANK | 3 | BM25 top-N 参与救援 | + +### 置信度 + +| 参数 | 当前值 | 说明 | +|------|--------|------| +| CONFIDENCE_WARN_THRESHOLD | 0.15 | 低于此值:谨慎回答 | +| CONFIDENCE_CAUTION_THRESHOLD | 0.30 | 低于此值:引用原文 | + +--- + +## 六、架构层面的潜在风险 + +### 6.1 单点故障 + +- **LLM API 单点**:所有 LLM 调用(意图分析 + 生成)都走同一个 DASHSCOPE_BASE_URL。API 限流或故障时整个服务不可用。意图分析有降级兜底(默认 need_retrieval=True),但生成阶段无降级。 +- **ChromaDB 单点**:向量库文件损坏时所有检索失败。已有 ChromaDB corruption 风险分析文档。 + +### 6.2 内存压力 + +- **FAISS 语义缓存**:10000 条 × 768 维 float32 = ~30MB 向量数据 + 等量元数据。达到上限全量清空时可能有瞬间抖动。 +- **BM25 全量加载**:所有文档的全文 + jieba 分词结果都在内存中。知识库增长时内存线性增长。 +- **三层 LRU 缓存**:Query(500) + Embedding(2000) + Rerank(1000) = 最多 3500 条缓存。Embedding 缓存存 768 维向量,2000 条 ≈ 6MB。 + +### 6.3 并发安全 + +- gunicorn gthread 模式 2 线程 + ChromaDB 文件锁。并发写入 ChromaDB 时可能冲突。 +- FAISS 语义缓存使用 RLock,线程安全。 +- 精确缓存使用 RLock,线程安全。 +- IntentAnalyzer._exact_cache 是普通 Dict,Python GIL 保护下基本安全。 + +### 6.4 休眠模块 + +以下模块当前未接入生产流程,属于**独立休眠模块**(原属 AgenticRAG 类,该类已在 v4.0 删除): + +- `confidence_gate.py` — 置信度门控 +- `quality_assessor.py` — 质量评估器 +- `reasoning_reflector.py` — 推理反思器 +- `loop_guard.py` — 循环守卫 + +这些模块增加了代码库的维护负担但不影响运行时性能。可按需启用。