Files
rag/docs/Agentic_RAG完整指南.md
lacerate551 cb75b9b274 fix(boundary): 修复多库边界问题、版本管理及删除清理
多库检索与存储修复:
- RRF 融合去重改用 (collection, chunk_id) 复合键,修复同名文件结果被吞
- DocStore 存储路径加 collection 前缀,修复跨库同名切片数据覆盖
- search_multiple 去重改用复合键
- chunk_id 解析改用 rsplit 兼容下划线文件名

上传与版本管理修复:
- 同名文件上传改为覆盖模式,自动清理旧切片
- 修复首次上传不创建版本记录
- 修复覆盖上传版本号回退到 v1
- sync ADDED 分支改用动态版本号生成
- _generate_version_id 改为基于全部版本递增
- 废止/恢复操作同步 SQLite 版本记录
- mark_document_as_superseded 改为仅更新 SQLite

删除清理修复:
- 删除文档时同步清理 SQLite 版本记录和变更日志
- 删除向量库时同步清理该库所有版本记录
- cleanup 改为清理 SQLite 记录而非 ChromaDB

测试:
- test_version_management.py: 27 条版本管理单元测试
- test_edge_cases.py: 28 条边界用例测试
- test_upload_dedup.py: 5 条上传去重测试
- e2e_risk_test.py: 27 条端到端风险测试

文档:
- 新增风险边界问题修复注意事项.md(面向后端的对接文档)
- 新增向量库边界风险分析.md
- 更新多篇现有文档
2026-06-04 23:58:44 +08:00

907 lines
43 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Agentic RAG 完整指南
> **版本**: v3.2(模型/Reranker/管线更新)
> **生产入口**: `api/chat_routes.py::rag()` → `core/engine.py`(轻量编排,当前启用)
> **备用编排**: `core/agentic.py::AgenticRAG.process()` + 8 个 Mixin完整决策循环未接线
> **最后更新**: 2026-06-04
>
> ⚠️ 项目存在两套编排,生产 `/rag` 走的不是 `AgenticRAG`——详见下方「一·五、两套编排路径」。
## 一、功能概述
Agentic RAG 是一个智能问答系统,基于 Mixin 模式组合 8 个功能模块,具备以下核心能力:
| 功能 | 说明 | Mixin 模块 |
|------|------|-----------|
| **意图分析** | LLM 驱动的查询改写 + 双层判断(是否需要检索) | `IntentAnalyzer`(独立模块) |
| **查询重写** | 口语化→专业术语、实体补全、指代消解 | `QueryRewriteMixin` |
| **混合检索** | 向量检索 + BM25 + RRF 融合 + Rerank 重排 | `SearchMixin``RAGEngine` |
| **多源融合** | 知识库 + 网络搜索,智能处理冲突 | `AnswerMixin` |
| **幻觉验证** | 基于参考信息验证答案,防止 LLM 编造 | `AnswerMixin` |
| **引用标注** | 自动标注信息来源和引用编号 | `CitationMixin` |
| **富媒体提取** | 图片/表格的智能提取与展示 | `RichMediaMixin` |
| **质量评估** | 多维度质量评估(相关性/完整性/准确性/覆盖面) | `QualityMixin` |
| **上下文压缩** | Rerank 阈值过滤 + Token 预算控制 | `ContextMixin` |
| **元问题处理** | 文件列表、权限查询等非知识类问题 | `MetaQuestionMixin` |
| **置信度门控** | 基于 Reranker 分数判断检索质量,低分触发补救 | `ConfidenceGate`(独立模块) |
---
## ⚠️ 一·五、两套编排路径(务必先读)
> **关键认知**:本项目存在**两套并存的编排orchestration**。生产 HTTP 接口 `/rag` 走的是**轻量编排**,而 `AgenticRAG.process()` 那套**完整决策循环目前处于备用状态、未接入任何 HTTP 路由**。
> 阅读下方所有架构图前请先理解这一点——下面 2.1 的「整体架构图」描绘的是**备用路径AgenticRAG.process**,不是当前生产实际跑的流程。
### 路径对比
| 维度 | 🟢 生产路径(当前启用) | 💤 备用路径(未接线) |
|------|----------------------|---------------------|
| 入口 | `api/chat_routes.py``rag()``generate()` | `core/agentic.py``AgenticRAG.process()` |
| 编排者 | `chat_routes` 自己的流程代码 | `AgenticRAG`8 个 Mixin 组合) |
| 意图分析 | ✅ `intent_analyzer.analyze_intent()` | ✅ `IntentAnalyzer` / `QueryRewriteMixin` |
| 检索 | ✅ `search_hybrid()``engine.search_knowledge()` | ✅ `engine.search_knowledge()` |
| 查询分解/扩展/MMR/自适应TopK | ✅ 在 `engine` 内部执行 | ✅ 同左 |
| 答案生成 | ✅ `engine.generate_answer_stream()`(流式) | `AnswerMixin._generate_fused_answer()` |
| 引用标注 | ✅ `chat_routes._attach_citations()`(本地版) | `CitationMixin._attach_citations()` |
| 置信度门控 | ❌ 不调用 | `ConfidenceGate`(仅此路径用) |
| 多维质量评估 | ❌ 不调用 | `QualityMixin._assess_quality()` |
| 推理反思 | ❌ 不调用 | `QualityMixin._reflect_on_answer()` |
| 循环防护 | ❌ 不调用 | `LoopGuard`(仅此路径用) |
| 幻觉验证 | ❌ 不调用 | `AnswerMixin._verify_and_refine_answer()` |
### 重要结论
- **Agentic 的核心能力是活跃的**:意图分析+LLM改写、子查询拆分、查询扩展、自适应 TopK、MMR 去重、混合检索+Rerank——这些都在 `/rag` 中**真实运行**,只是由 `chat_routes` + `engine` 直接调用,而非通过 `AgenticRAG` 类。
- **休眠的只是「决策循环编排类」**`AgenticRAG.process()` 及其独有组件(置信度门控 / 质量评估 / 推理反思 / 循环防护 / 幻觉验证)未接入 `/rag`
- import 证据:`confidence_gate.py``quality_assessor.py``reasoning_reflector.py``loop_guard.py` 以及 8 个 `agentic_*` Mixin **只被 `core/agentic.py` import**;而 `AgenticRAG` 实例虽在 `api/__init__.py:90` 启动时创建,但其唯一读取入口 `_get_agentic_rag()` **零调用**
- **这不是死代码可删**`AgenticRAG` 在启动时被实例化(直接删会导致启动报错),且 `_extract_rich_media``scripts/test_rag_image_recall.py` 使用。它是「**一套更重、更完整、目前未启用的 Agentic 决策闭环**」,未来可选择接入。
### 🔬 如何验证「系统现在到底走哪套流程」
**方法 1看开发环境 SSE 调试事件(最直接)**
`/rag``IS_DEV=True` 时会发出一串**只有 `chat_routes` 编排才会发**的调试事件,收到它们即证明走的是生产路径:
```bash
# UTF-8 payload 避免 Windows shell 编码问题
curl -s -N -X POST http://localhost:5001/rag \
-H "Content-Type: application/json; charset=utf-8" \
-H "Authorization: Bearer mock-token-admin" \
--data-binary @payload.json
```
观察 SSE 事件序列,**生产路径**会依次出现这些 `type``AgenticRAG.process` 不发这些):
| SSE 事件 `type` | 来源代码 | 含义 |
|----------------|---------|------|
| `start` | `chat_routes.py:1232` | 请求开始处理 |
| `intent_result` | `chat_routes.py:1228` | 意图分析结果(来自 `intent_analyzer`[DEV] |
| `retrieval_debug` | `chat_routes.py:1311` | 检索管线各步骤(来自 `engine.search_knowledge``_debug`[DEV] |
| `chunks_retrieved` | `chat_routes.py:1416` | 召回切片详情 [DEV] |
| `sources` | `chat_routes.py:1547` | 检索到的来源列表 |
| `images_selected` | `chat_routes.py:1574` | 图片选择详情 [DEV] |
| `context_built` | `chat_routes.py:1622` | 最终上下文构建 [DEV] |
| `chunk` | `chat_routes.py:1630` | 流式答案的每个 token |
| `finish` | `chat_routes.py:1699` | 含 `timing``sources``citations` |
| `error` | `chat_routes.py:1733` | 处理异常时的错误信息 |
> 标注 [DEV] 的事件仅在 `IS_DEV=True` 时发送,其余事件在生产环境也会发送。
**方法 2看服务端日志**
- 启动时:出现一次 `Agentic RAG 引擎已初始化``api/__init__.py:95`,仅实例化,不代表被调用)。
- 每次 `/rag` 请求:出现 `[意图分析] use_context=... need_retrieval=...``chat_routes.py:1224`)。
- **不会**出现任何来自 `AgenticRAG.process()` 内部的日志(如查询重写 `📝 查询重写``🔍 知识库检索: N 条结果`)——若出现则说明走了备用路径。
**方法 3埋点验证最确定**
临时在 `core/agentic.py``AgenticRAG.process()` 第一行加 `logger.warning("AgenticRAG.process CALLED")`,重启后发 `/rag` 请求——**该日志不会触发**,即证明生产不走 `AgenticRAG`
**方法 4静态确认调用链**
```bash
grep -rn "_get_agentic_rag()" --include="*.py" . # 仅定义,无调用者 → AgenticRAG 实例未被请求使用
grep -rn "\.process(" --include="*.py" api/ # /rag、/chat 均无 .process() 调用
```
---
## 二、系统架构
> ⚠️ 注意:下方 2.1「整体架构图」描绘的是**备用路径 `AgenticRAG.process()`** 的完整设计;当前生产 `/rag` 的实际流程见上方「一·五」及本节 2.3「生产 /rag 实际流程」。
### 2.1 整体架构图
```
┌─────────────────────────────────────────────────────────────────────┐
│ 用户输入 │
└────────────────────────────┬────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ 意图分析 (IntentAnalyzer) │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ 改写查询 │ │ 双层判断 │ │ 子查询拆分 │ │
│ │ (指代消解) │ │ (是否检索) │ │ (对比/推理) │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
└─────────┼──────────────────┼──────────────────┼─────────────────────┘
↓ ↓ ↓
┌──────────┐ ┌──────────────────────────────────────────┐
│ 直接回答 │ │ AgenticRAG.process() │
│ (LLM) │ │ 1. 元问题检查 │
└──────────┘ │ 2. 查询重写 (QueryRewriteMixin) │
│ 3. 知识库检索 (RAGEngine.search_knowledge)│
│ 4. 上下文压缩 (ContextMixin) │
│ 5. 网络搜索 (SearchMixin, 可选) │
│ 6. 图谱检索已废弃graph/ 目录已清空) │
│ 7. 融合答案生成 (AnswerMixin) │
│ 8. 幻觉验证 (AnswerMixin) │
│ 9. 富媒体提取 (RichMediaMixin) │
│ 10. 引用标注 (CitationMixin) │
└────────────────────┬─────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ 检索层 (RAGEngine) │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ 向量检索 │ │ BM25 检索 │ │ FAQ 独立召回 │ │
│ │ (语义匹配) │ │ (关键词匹配) │ │ (精准命中) │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
│ └─────────────────┼─────────────────┘ │
│ ↓ │
│ ┌──────────────┐ │
│ │ RRF 融合 │ ← 动态权重(查询类型/长度驱动)│
│ └──────┬───────┘ │
│ ↓ │
│ ┌───────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ 废止过滤 │→ │ Rerank 重排 │→ │ MMR 去重 │ │
│ └───────────┘ │ (云端API) │ └──────┬───────┘ │
│ └──────┬───────┘ ↓ │
│ ┌───────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ FAQ 加权 │→ │ 黑名单过滤 │→ │ 时间衰减 │ │
│ └───────────┘ └──────────────┘ └──────┬───────┘ │
│ ↓ │
│ ┌───────────────┐ ┌──────────────┐ │
│ │ 上下文扩展 │→ │ 自适应 TopK │ │
│ └───────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ 答案生成 (LLM 流式) │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ 整合多源信息 + 标注来源 + 处理冲突 + 引用编号 + SSE 流式输出 │ │
│ └────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
```
### 2.2 Mixin 组合架构
```python
class AgenticRAG(
QueryRewriteMixin, # 查询重写:口语化→专业术语、实体补全
SearchMixin, # 检索功能:网络搜索
AnswerMixin, # 答案生成:融合回答、幻觉验证
CitationMixin, # 引用处理:来源标注、引用编号
RichMediaMixin, # 富媒体:图片/表格提取
QualityMixin, # 质量评估:多维评估
ContextMixin, # 上下文处理:压缩、过滤
MetaQuestionMixin # 元问题:文件列表、权限查询
):
...
```
> 注:以上 2.1 / 2.2 是 `AgenticRAG`(备用路径)的设计。**当前生产 `/rag` 不实例化走这条链**,实际流程见下方 2.3。
### 2.3 生产 /rag 实际流程(当前启用)
入口 `api/chat_routes.py::rag() → generate()`**不经过 `AgenticRAG`**
```
POST /rag (SSE 流式)
[chat_routes.generate()] ← 轻量编排,不实例化 AgenticRAG
├─ 发 SSE: start
├─ 1. 意图分析 intent_analyzer.analyze_intent() # chat_routes:1222
│ ├─ need_retrieval=False → 直接 LLM 回答(流式发 SSE: chunk结束
│ │ └─ use_context=True 时带历史上下文use_context=False 时纯闲聊
│ └─ 否则继续sub_queries 传入检索
│ └─[DEV] 发 SSE: intent_result
├─ 2. 混合检索 search_hybrid() → engine.search_knowledge() # chat_routes:1300
│ (内部:向量+BM25+RRF+废止过滤+章节过滤
│ +云端Rerank+MMR去重+FAQ加权+黑名单+时间衰减
│ +上下文扩展+自适应TopK
│ └─[DEV] 发 SSE: retrieval_debug
├─ 3. 提取上下文/来源(按 source 去重doc_type 驱动溯源展示) # chat_routes:1362
│ └─[DEV] 发 SSE: chunks_retrieved
│ └─ 发 SSE: sources
├─ 4. 图片补充检索 + 图片打分选择 (select_images)
│ └─[DEV] 发 SSE: images_selected
├─ 5. 构建上下文 (_order_text_contexts_for_prompt)
│ └─[DEV] 发 SSE: context_built
├─ 6. 流式答案生成 engine.generate_answer_stream() # chat_routes:1628
│ └─ 逐 token 发 SSE: chunk
├─ 7. 答案图号对齐过滤
├─ 8. 引用标注 chat_routes._attach_citations()(本地版,非 CitationMixin # chat_routes:1668
├─ 9. 敏感信息过滤 filter_response()
├─ 10. 发 SSE: finishanswer + sources + citations + images + timing
└─[异常] 发 SSE: error
```
**与备用路径AgenticRAG.process的差异**:生产路径**没有**置信度门控、多维质量评估、推理反思、循环防护、幻觉验证这几步——它们只存在于 `AgenticRAG.process()`
---
## 三、意图分析流程
### 3.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
```
### 3.2 QueryClassifier 规则分类
`core/query_classifier.py` 提供无 LLM 调用的快速规则分类:
| 查询类型 | 说明 | 示例 |
|----------|------|------|
| `META` | 元问题(文件列表、权限) | "有哪些文档?" |
| `REALTIME` | 实时信息 | "今天天气" |
| `SIMPLE` | 简单单属性查询 | "出差标准" |
| `FACT` | 事实查询 | "差旅补贴标准是多少" |
| `ENUMERATION` | 枚举/清单/条款 | "严禁哪些情形" |
| `COMPARISON` | 比较分析 | "年假和调休的区别" |
| `PROCESS` | 流程指引 | "如何申请调岗" |
| `FILE_SPECIFIC` | 特定文件内查询 | "xxx.pdf中有哪些图片" |
---
## 四、检索管线详解
### 4.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 重排 ★(云端 DashScope qwen3-rerank 或本地 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. 缓存写入 → 返回结果
```
### 4.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 重排(云端 DashScope qwen3-rerank 或本地 BGE
reranked = rerank_results(query, fused, top_k=15)
# MMR 去重
mmr_results = mmr_rerank(query_emb, reranked, top_k=30, lambda_param=0.5)
```
### 4.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→向量优先
```
### 4.4 Rerank 重排
**后端**: 支持三种模式,由 `RERANK_BACKEND` 环境变量控制
| RERANK_BACKEND | 说明 |
|----------------|------|
| `"cloud"` | 仅使用云端 DashScope `qwen3-rerank` API |
| `"local"` | 仅使用本地 `BAAI/bge-reranker-base`CrossEncoder / ONNX |
| `"fallback"` | 优先云端,失败时自动回退本地(推荐生产环境) |
**云端 Reranker推荐**:
```python
# config.py
RERANK_BACKEND = os.getenv("RERANK_BACKEND", "local") # local / cloud / fallback
RERANK_CLOUD_MODEL = "qwen3-rerank" # DashScope 云端 Rerank 模型
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_TIMEOUT = 15 # 云端请求超时(秒)
```
`CloudReranker` 类(`core/engine.py`)封装 DashScope 的 `/compatible-api/v1/reranks` 接口,提供与本地 `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 去重之前执行。
**引擎初始化顺序**: `RAGEngine.__init__()` 中按 `RERANK_BACKEND` 决定加载策略:
- `cloud` / `fallback`:先尝试创建 `CloudReranker`,需要 `RERANK_CLOUD_API_KEY`
- `local` / `fallback`(云端失败时):加载本地 `BAAI/bge-reranker-base`,支持 ONNX 加速
---
## 五、置信度门控
`core/confidence_gate.py` 基于 Reranker 分数判断检索结果质量:
```
检索结果 → Reranker 计算置信度 → 阈值判断 → 决策
┌─────────────────┼─────────────────┐
↓ ↓ ↓
PASS (≥0.4) REWRITE (0.2~0.4) WEB_SEARCH (<0.2)
继续生成 查询重写 网络搜索补救
```
**阈值配置**:
- `PASS_THRESHOLD = 0.2`: 通过阈值(低于此值需要补救)
- `GOOD_THRESHOLD = 0.4`: 良好阈值(高质量结果)
- `EXCELLENT_THRESHOLD = 0.7`: 优秀阈值
---
## 六、AgenticRAG 主流程
### 6.1 process() 方法
```python
def process(self, query, verbose=True, history=None,
allowed_levels=None, role=None, department=None,
emit_log=None) -> dict:
"""
返回:
{
"answer": str, # 最终答案
"sources": list, # 来源列表
"images": list, # 图片列表
"tables": list, # 表格列表
"citations": list, # 引用列表
"log_trace": list # 推理过程追踪
}
"""
```
### 6.2 流程步骤
| 步骤 | 方法 | 说明 |
|------|------|------|
| 1 | `_is_meta_question()` | 检查元问题(文件列表、权限等) |
| 2 | `should_rewrite()` + `_rewrite_query()` | 查询重写(口语化→专业术语、实体补全) |
| 3 | `engine.search_knowledge()` / `engine.search_multiple()` | 知识库检索(含向量+BM25+RRF+MMR+Rerank |
| 4 | `_compress_contexts()` | 上下文压缩Rerank 阈值过滤) |
| 5 | `_web_search_flow()` | 网络搜索(可选,需 SERPER_API_KEY |
| 6 | ~~`_graph_search()`~~ | ~~图谱检索已废弃graph/ 目录已清空)~~ |
| 7 | `_generate_fused_answer()` | 融合答案生成(多源信息+冲突处理) |
| 8 | `_verify_and_refine_answer()` | 幻觉验证(防止 LLM 编造) |
| 9 | `_extract_rich_media()` | 富媒体提取(图片/表格) |
| 10 | `_attach_citations()` | 引用标注 |
---
## 七、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": []
}'
```
**响应格式**: SSEServer-Sent Events流式返回
```
event: token
data: {"text": "根据"}
event: token
data: {"text": "规定"}
...
event: finish
data: {"answer": "完整答案", "sources": [...], "citations": [...], "images": [...], "duration_ms": 3200}
```
### 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 AgenticRAG 调用
```python
from core.agentic import AgenticRAG
rag = AgenticRAG(max_iterations=3, enable_web_search=True)
result = rag.process("出差补助标准是什么?")
print(f"答案: {result['answer']}")
print(f"来源: {result['sources']}")
print(f"图片: {result['images']}")
print(f"引用: {result['citations']}")
```
---
## 八、配置说明
### 8.1 LLM 配置
```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 对话模型
```
### 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 = "qwen3-rerank" # 云端 Rerank 模型DashScope API
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
# 查询结果缓存
QUERY_CACHE_ENABLED = True
QUERY_CACHE_SIZE = 500
QUERY_CACHE_TTL = 3600 # 1小时
# 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 # 1小时
# 语义缓存
SEMANTIC_CACHE_ENABLED = True
SEMANTIC_CACHE_THRESHOLD = 0.92 # 相似度阈值
```
### 8.4 设备配置
```python
DEVICE = "auto" # auto / cuda / cpu / cuda:0
EMBEDDING_DEVICE = DEVICE # 向量模型设备
RERANK_DEVICE = DEVICE # Rerank 模型设备
```
---
## 九、文件结构
```
core/ # RAG 核心引擎
├── engine.py # RAGEngine 单例检索主流程、Rerank、RRF
├── agentic.py # AgenticRAG 主类Mixin 组合)
├── agentic_base.py # 基础常量与条件导入
├── agentic_query.py # QueryRewriteMixin查询重写
├── agentic_search.py # SearchMixin网络搜索
├── agentic_answer.py # AnswerMixin答案生成、幻觉验证
├── agentic_citation.py # CitationMixin引用标注
├── agentic_media.py # RichMediaMixin富媒体提取
├── agentic_quality.py # QualityMixin质量评估
├── agentic_context.py # ContextMixin上下文压缩
├── agentic_meta.py # MetaQuestionMixin元问题处理
├── bm25_index.py # BM25Index关键词检索
├── chunker.py # 文本分块器
├── mmr.py # MMR 去重(语义向量版 + 文本 Jaccard 版)
├── query_classifier.py # QueryClassifier规则快速分类
├── intent_analyzer.py # IntentAnalyzerLLM 意图分析)
├── 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 # 循环防护
├── llm_budget.py # LLM 调用预算控制
├── llm_utils.py # LLM 调用工具函数
├── semantic_cache.py # 语义缓存
├── cache.py # 三层缓存管理器Query/Embedding/Rerank
├── status_codes.py # 状态码定义
└── constants.py # 公共常量
knowledge/ # 知识库管理
├── manager.py # KnowledgeBaseManager7 个 Mixin 组合)
├── router.py # KnowledgeBaseRouter智能知识库路由
├── sync.py # KnowledgeSyncService文件变更监控+增量向量化)
├── base.py # 知识库基类定义
├── collection.py # 集合CollectionCRUD 操作
├── 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 入口
config/ # 运行时配置
└── banned_words.txt # 敏感词库
```
---
## 十、与传统 RAG 对比
| 特性 | 传统 RAG | Agentic RAG (当前) |
|------|---------|-------------------|
| 意图判断 | 无 | IntentAnalyzer LLM 双层判断 |
| 查询改写 | 无 | 口语化→专业术语 + 实体补全 + 指代消解 |
| 检索方式 | 单一向量检索 | 向量 + BM25 + FAQ + 图片独立召回 |
| 融合算法 | 无 | RRF 动态权重融合 |
| 去重 | 无 | MMR 语义去重 |
| 重排序 | 无 | 云端 qwen3-rerank API支持本地 BGE 回退) |
| 问题分解 | 无 | 自动拆分对比/推理类查询 |
| 闲聊处理 | 无 | 意图分析自动判断 |
| 网络搜索 | 无 | 可选支持Serper API |
| 知识图谱 | 无 | ~~可选支持Neo4j~~已废弃graph/ 目录已清空) |
| 幻觉验证 | 无 | 基于参考信息的答案验证 |
| 置信度门控 | 无 | Reranker 分数驱动,低分触发补救 |
| 缓存 | 无 | 三层缓存 + 语义缓存 |
| 自适应 TopK | 固定 top_k | 根据置信度动态调整 |
| 上下文理解 | 无 | 多轮对话 + 历史上下文 |
| 响应时间 | ~2秒 | ~3-8秒取决于 Rerank + LLM |
---
## 十一、Rerank 性能分析
### 11.1 Rerank 调用路径
Rerank 在系统中有 **两个独立调用路径**
| 路径 | 位置 | 说明 |
|------|------|------|
| 主检索管线 | `engine.rerank_results()` | RRF 融合后、MMR 去重前执行,对候选重排取 top_k |
| 置信度门控 | `confidence_gate._compute_scores()` | 直接调用 `reranker.predict()`,可能重复推理 |
### 11.2 性能瓶颈
| 瓶颈 | 严重程度 | 说明 |
|------|---------|------|
| Rerank 缓存命中率偏低 | 🟡 中 | `rerank_results()` 已正确调用缓存读写,但缓存 key 基于 `query + sorted(doc_ids)` 精确匹配RRF 融合产出稍有不同就无法命中 |
| 置信度门控重复推理 | 🟡 中 | 同一 query+documents 可能被 Rerank 两次(当前仅备用路径使用,暂未影响生产) |
| ~~无性能计时~~ | ~~🟡 中~~ | 已修复:`rerank_results()` 现返回 `_rerank_time_ms` 计时字段 |
| 查询分类器策略未生效 | 🟢 低 | `QueryClassifier` 定义的差异化 rerank 参数未传递到引擎 |
### 11.3 Rerank 配置参数
| 配置项 | 默认值 | 说明 |
|--------|--------|------|
| `USE_RERANK` | `True` | 总开关 |
| `RERANK_BACKEND` | `"local"` | 后端选择:`local`=本地模型, `cloud`=云端API, `fallback`=优先云端失败回退本地 |
| `RERANK_CLOUD_MODEL` | `"qwen3-rerank"` | 云端 Rerank 模型名称DashScope API |
| `RERANK_CLOUD_API_KEY` | 同 `DASHSCOPE_API_KEY` | 云端 API 密钥 |
| `RERANK_CLOUD_BASE_URL` | `https://dashscope.aliyuncs.com/compatible-api/v1/reranks` | 云端 API 地址 |
| `RERANK_CLOUD_TIMEOUT` | `15` | 云端请求超时(秒) |
| `RERANK_MODEL_PATH` | `models/bge-reranker-base` | 本地模型路径(仅 local/fallback 模式) |
| `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` | 缓存开关(已在 `rerank_results()` 中使用) |
---
## 十二、最佳实践
### 12.1 何时使用 Agentic RAG
**推荐使用**
- 复杂问题需要多轮检索
- 用户表达模糊需要改写
- 需要区分闲聊和知识问答
- 需要多轮对话记忆
- 需要引用来源和幻觉验证
**不推荐使用**
- 简单明确的问题(用 `/search` 接口更快)
- 对响应时间极度敏感的场景
### 12.2 性能优化
```python
# 减少迭代次数
rag = AgenticRAG(max_iterations=2)
# 禁用网络搜索
rag = AgenticRAG(enable_web_search=False)
# ONNX 加速默认已开启;如有兼容性问题可关闭(环境变量)
# RERANK_USE_ONNX=false
# 使用轻量 MMR文本相似度代替语义向量
# config.py: MMR_USE_EMBEDDING = False
```
### 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}")
```
---
## 附加篇Agentic RAG 深入优化与工作机制
### 一、Agentic RAG 的核心架构
Agentic RAG 构建了动态的决策闭环,核心组件包括:
- **意图分析器**LLM 驱动的双层判断,替代硬编码规则
- **查询重写器**:口语化→专业术语、实体补全、指代消解
- **混合检索引擎**:向量 + BM25 + FAQ + 图片独立召回 + RRF 融合
- **MMR 去重**平衡相关性与多样性Rerank 后进一步精炼结果
- **Rerank 重排**:云端 qwen3-rerank API 精确排序,支持本地 BGE 回退
- **置信度门控**Reranker 分数驱动,低分触发补救流程
- **幻觉验证**:基于参考信息验证答案,防止 LLM 编造
### 二、分阶段优化策略
#### 1. 检索前:优化查询质量
- **智能查询重写**:口语化表述 → 精准检索术语
- **复杂问题分解**:对比/推理类查询自动拆分为子查询
- **意图分析**LLM 双层判断,避免不必要的检索
#### 2. 检索中:提升召回精准度
- **多路召回与融合**:向量 + BM25 + FAQ + 图片独立召回
- **动态 RRF 权重**:查询类型/长度驱动的权重调整
- **MMR 去重**Rerank 后进一步精炼平衡相关性与多样性召回100 → Rerank取15 → MMR精炼
- **Rerank 重排**:云端 qwen3-rerank 精排,置信度门控过滤低质量结果
#### 3. 检索后:质量评估与自我迭代
- **多维质量评估**:相关性/完整性/准确性/覆盖面
- **推理反思**:检查推理过程中未验证的假设
- **分层补救**:低置信度 → 查询重写 → 网络搜索
### 三、系统级优化
#### 1. 避免"循环检索"陷阱
- 循环防护器(`loop_guard.py`):最多允许 N 次重写检索
- 置信度递增检查:连续两次无提升则终止
#### 2. 平衡智能性与效率
- 轻量级决策模型:意图分析使用低温度、少 token 的 LLM 调用
- 三层缓存Query Cache + Embedding Cache + Rerank Cache
- 语义缓存相似查询复用结果threshold=0.92
- LLM 预算控制:`MAX_LLM_CALLS_PER_QUERY = 2`
#### 3. 安全与可解释性
- 证据溯源:引用标注 + 来源编号
- 思维链展示:`log_trace` 记录推理过程
- 安全护栏:输入验证 + 输出过滤 + 权限控制
### 四、学术前沿
1. **RAG-Gym**:三维度系统优化(提示工程 + 执行器调优 + 评判器训练)
2. **过程监督 vs 结果监督**:细粒度过程奖励显著提升训练效率
3. **Re2Search**推理反思机制F1 score 提升 10%+
### 参考资料
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
3. Agentic RAG 实战指南:从查询重写到多步重查全掌握。火山引擎 ADG 社区