- 全面重写文档,反映统一编排路径和四层缓存架构 - 添加 Query Cache 修复说明和语义缓存集成文档 - 重命名 Agentic_RAG完整指南.md → RAG系统完整指南.md - 同步更新开发与系统模块说明.md 中的引用链接
39 KiB
RAG 系统完整指南
版本: v4.0(统一编排 + 四层缓存修复) 生产入口:
api/chat_routes.py::rag()→generate()→core/engine.py最后更新: 2026-06-05本次更新:删除未使用的 AgenticRAG 备用编排路径(10 个文件 ~2050 行),修复 Query Cache 键不匹配与阈值问题,将语义缓存集成至生产
/rag端点。
一、功能概述
本系统是一个检索增强生成(RAG)问答系统,采用单一统一编排路径,由 api/chat_routes.py 的 generate() 函数直接编排全流程。核心能力包括:
| 功能 | 说明 | 实现位置 |
|---|---|---|
| 意图分析 | LLM 驱动的双层判断(是否需要检索)+ 查询改写 | core/intent_analyzer.py |
| 混合检索 | 向量检索 + BM25 + RRF 融合 + Rerank 重排 | core/engine.py |
| 四层缓存 | Query + Embedding + Rerank(LRU)+ 语义缓存(FAISS) | core/cache.py + core/semantic_cache.py |
| 流式生成 | SSE 流式答案输出,逐 token 推送 | core/engine.py::generate_answer_stream() |
| 引用标注 | 自动标注信息来源和引用编号 | api/chat_routes.py::_attach_citations() |
| 富媒体 | 图片/表格的智能提取与展示 | api/chat_routes.py |
| 查询理解 | 查询分解、扩展、MMR 去重、自适应 TopK | core/ 各独立模块 |
| 安全护栏 | 敏感信息过滤、Prompt 安全守卫 | api/response_utils.py、core/prompt_guard.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 │ │
│ └───────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────────┐
│ 答案生成 (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 条 | 无过期 | 语义级缓存,相似查询也能命中 |
3.2 Query Cache
Query Cache 是最外层的完整问答结果缓存。命中后直接返回缓存的 answer + sources + citations,跳过检索和生成全流程。
缓存键设计:{query_hash}:{kb_name}:{kb_version}
- 基于查询文本哈希 + 知识库名称 + 知识库版本号
- 知识库版本变更时(如文档更新/重新索引),相关缓存自动失效
已修复的问题:
-
键不匹配问题(已修复):此前
set_query_result()在有doc_ids参数时使用doc_hash分支生成键,而get_query_result()始终使用kb_version分支——导致 GET 和 SET 的键永远不匹配,命中率始终为 0%。修复后两端统一使用kb_version分支。 -
写入阈值问题(已修复):
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 缓存配置
# 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 重排 ★(云端 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. 缓存写入 → 返回结果
5.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)
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" |
仅使用云端 DashScope qwen3-rerank API |
"local" |
仅使用本地 BAAI/bge-reranker-base(CrossEncoder / ONNX) |
"fallback" |
优先云端,失败时自动回退本地(推荐生产环境) |
云端 Reranker(推荐):
# config.py
RERANK_BACKEND = os.getenv("RERANK_BACKEND", "local")
RERANK_CLOUD_MODEL = "qwen3-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 去重之前执行。
六、生产 /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)
│ └─[DEV] 发 SSE: context_built
│
├─ 7. 流式答案生成 engine.generate_answer_stream()
│ └─ 逐 token 发 SSE: chunk
│
├─ 8. 答案图号对齐过滤
├─ 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时发送。
七、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": 200}
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 缓存统计查询
# 查看各层缓存命中率和统计
curl http://localhost:5001/cache/stats \
-H "Authorization: Bearer mock-token-admin"
返回示例:
{
"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 配置
# 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_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 设备配置
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 语义去重 |
| 重排序 | 无 | 云端 qwen3-rerank 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 |
"qwen3-rerank" |
云端模型名称 |
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 |
本地模型路径 |
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 性能优化
# 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 调试技巧
# 查看检索调试信息
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.0(2026-06-05)— 统一编排 + 四层缓存修复
删除未使用的备用编排路径:移除了 core/agentic.py 及 8 个 Mixin 文件(共 10 个文件 ~2050 行)。这些文件实现了完整的决策循环编排(含置信度门控、质量评估、推理反思等),但从未接入任何 HTTP 路由。
修复 Query Cache:
- 修复 GET/SET 键不匹配——此前 SET 使用
doc_hash分支,GET 使用kb_version分支,两端永远不匹配,命中率始终为 0% - 修复
CACHE_MIN_SCORE = 0.3阈值过高——ChromaDB 余弦距离经1-dist后得分约 0.03-0.06,远低于 0.3,导致几乎不写入缓存
集成语义缓存:将 FAISS 语义缓存从已删除的备用路径移植到生产 /rag 端点,在意图分析后、混合检索前检查,命中时跳过整个检索+生成流程。验证结果:命中率 66.7%,92 倍加速。
v3.2 — 模型/Reranker/管线更新
引入云端 qwen3-rerank、ONNX 加速、动态 RRF 权重等。
十四、未来规划
Redis 缓存外部化
当前四层缓存均为进程内内存存储,在多 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:循环防护,防止重复检索
参考资料
- 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