多库检索与存储修复: - 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 - 更新多篇现有文档
43 KiB
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.pyimport;而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 编排才会发的调试事件,收到它们即证明走的是生产路径:
# 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:静态确认调用链
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 组合架构
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: finish(answer + 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 混合检索代码示例
# 向量检索(语义相似)
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(推荐):
# 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(备选):
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_KEYlocal/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() 方法
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() |
|
| 7 | _generate_fused_answer() |
融合答案生成(多源信息+冲突处理) |
| 8 | _verify_and_refine_answer() |
幻觉验证(防止 LLM 编造) |
| 9 | _extract_rich_media() |
富媒体提取(图片/表格) |
| 10 | _attach_citations() |
引用标注 |
七、API 调用方式
7.1 SSE 流式问答(主要接口)
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": 3200}
7.2 代码调用
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 调用
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 配置
# 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 检索参数
# 混合检索
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 缓存配置
# 查询结果缓存
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 设备配置
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 # 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 # 循环防护
├── llm_budget.py # LLM 调用预算控制
├── llm_utils.py # LLM 调用工具函数
├── semantic_cache.py # 语义缓存
├── cache.py # 三层缓存管理器(Query/Embedding/Rerank)
├── 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 入口
config/ # 运行时配置
└── banned_words.txt # 敏感词库
十、与传统 RAG 对比
| 特性 | 传统 RAG | Agentic RAG (当前) |
|---|---|---|
| 意图判断 | 无 | IntentAnalyzer LLM 双层判断 |
| 查询改写 | 无 | 口语化→专业术语 + 实体补全 + 指代消解 |
| 检索方式 | 单一向量检索 | 向量 + BM25 + FAQ + 图片独立召回 |
| 融合算法 | 无 | RRF 动态权重融合 |
| 去重 | 无 | MMR 语义去重 |
| 重排序 | 无 | 云端 qwen3-rerank API(支持本地 BGE 回退) |
| 问题分解 | 无 | 自动拆分对比/推理类查询 |
| 闲聊处理 | 无 | 意图分析自动判断 |
| 网络搜索 | 无 | 可选支持(Serper API) |
| 知识图谱 | 无 | |
| 幻觉验证 | 无 | 基于参考信息的答案验证 |
| 置信度门控 | 无 | 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 性能优化
# 减少迭代次数
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 调试技巧
# 查看检索调试信息
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记录推理过程 - 安全护栏:输入验证 + 输出过滤 + 权限控制
四、学术前沿
- RAG-Gym:三维度系统优化(提示工程 + 执行器调优 + 评判器训练)
- 过程监督 vs 结果监督:细粒度过程奖励显著提升训练效率
- Re2Search:推理反思机制,F1 score 提升 10%+
参考资料
- Xiong, G., et al. (2025). RAG-Gym: Systematic Optimization of Language Agents for Retrieval-Augmented Generation. arXiv:2502.13957
- Zhang, W., et al. (2025). Process vs. Outcome Reward: Which is Better for Agentic RAG Reinforcement Learning. arXiv:2505.14069
- Agentic RAG 实战指南:从查询重写到多步重查全掌握。火山引擎 ADG 社区