perf(cache): 修复缓存失效 Bug + 集成语义缓存 + 删除 AgenticRAG 死代码

- fix: Query Cache GET/SET Key 不匹配导致命中率始终为 0%
- fix: CACHE_MIN_SCORE 阈值 0.3 对 ChromaDB cosine distance 过于严格
- feat: 在 /rag 端点集成语义缓存(命中时跳过检索+生成,92x 加速)
- refactor: 删除 AgenticRAG 死代码路径(10 个 agentic_*.py,约 1950 行)
- cleanup: 移除 engine.py 死方法、路由死函数、初始化死代码
This commit is contained in:
lacerate551
2026-06-05 21:20:53 +08:00
parent 6deba8fae2
commit 505f79860e
16 changed files with 222 additions and 2052 deletions

View File

@@ -3,7 +3,7 @@ API 路由层 — Flask 应用工厂
本模块实现 Flask 应用工厂模式,负责:
- 创建和配置 Flask 应用实例
- 初始化核心服务(AgenticRAG、同步服务)
- 初始化核心服务(同步服务)
- 注册所有 API Blueprint
- 配置前端静态文件路由
@@ -47,7 +47,7 @@ def create_app() -> 'Flask':
1. 创建 Flask 应用,配置 CORS
2. 初始化 Repository会话存储
3. 初始化核心服务(AgenticRAG、同步服务)
3. 初始化核心服务(同步服务)
4. 注册所有 API Blueprint
5. 配置前端静态文件路由
6. 执行生产环境配置校验
@@ -93,19 +93,6 @@ def create_app() -> 'Flask':
# ==================== 核心服务初始化 ====================
# Agentic RAG 引擎
try:
from core.agentic import AgenticRAG
from config import ENABLE_WEB_SEARCH
agentic_rag = AgenticRAG(
enable_web_search=ENABLE_WEB_SEARCH,
)
app.config['AGENTIC_RAG'] = agentic_rag
logger.info(f"Agentic RAG 引擎已初始化(网络搜索={'启用' if ENABLE_WEB_SEARCH else '关闭'}")
except Exception as e:
app.config['AGENTIC_RAG'] = None
logger.warning(f"Agentic RAG 初始化失败: {e}")
# 同步服务
try:
from knowledge.sync import KnowledgeSyncService

View File

@@ -381,16 +381,6 @@ def _build_context_with_budget(contexts: List[Dict], max_chars: int, soft_limit:
return "\n\n".join(parts)
def _get_agentic_rag() -> 'AgenticRAG':
"""
获取 AgenticRAG 实例
Returns:
AgenticRAG: 当前应用中的 AgenticRAG 实例
"""
return current_app.config['AGENTIC_RAG']
def _attach_citations(answer: str, contexts: List[Dict]) -> Dict[str, Any]:
"""
自动为回答添加引用标记按段落级别匹配jieba 分词精准匹配)
@@ -1464,6 +1454,47 @@ def rag():
except Exception as e:
logger.warning(f"意图分析失败: {e},继续执行检索流程")
# 1.5 语义缓存检查(跳过检索+生成全流程)
_semantic_cache_emb = None
try:
from core.semantic_cache import get_semantic_cache
from core.engine import get_engine as _get_eng
_sc = get_semantic_cache()
_eng = _get_eng()
if _sc and _eng and hasattr(_eng, 'embedding_model'):
_semantic_cache_emb = _eng.embedding_model.encode(message)
cached = _sc.get(_semantic_cache_emb)
if cached is not None:
logger.info(f"[语义缓存] 命中: {message[:50]}...")
cached_answer = cached.get("answer", "")
# 流式返回缓存的答案
yield f"data: {json.dumps({'type': 'start', 'message': '正在检索知识库...'}, ensure_ascii=False)}\n\n"
# 分块发送缓存答案
chunk_size = 20
for i in range(0, len(cached_answer), chunk_size):
chunk = cached_answer[i:i+chunk_size]
full_answer.append(chunk)
yield f"data: {json.dumps({'type': 'chunk', 'content': chunk}, ensure_ascii=False)}\n\n"
finish_event = {
"type": "finish",
"answer": cached_answer,
"mode": "rag",
"session_id": session_id,
"sources": cached.get("sources", []),
"citations": cached.get("citations", []),
"images": cached.get("images", []),
"tables": cached.get("tables", []),
"sections": [],
"duration_ms": int((_time.time() - start_time) * 1000),
"confidence_score": 1.0,
"semantic_cache_hit": True
}
yield f"data: {json.dumps(finish_event, ensure_ascii=False)}\n\n"
return
except Exception as e:
logger.warning(f"[语义缓存] 检查失败: {e}")
_semantic_cache_emb = None
# 1. 发送开始事件
yield f"data: {json.dumps({'type': 'start', 'message': '正在检索知识库...'}, ensure_ascii=False)}\n\n"
@@ -1951,6 +1982,28 @@ def rag():
"rerank_cached": rerank_cached,
"total_ms": duration_ms
}
# 10.5 写入语义缓存
try:
from core.semantic_cache import get_semantic_cache
_sc = get_semantic_cache()
if _sc and filtered_answer:
if _semantic_cache_emb is None:
from core.engine import get_engine as _get_eng
_eng = _get_eng()
if _eng and hasattr(_eng, 'embedding_model'):
_semantic_cache_emb = _eng.embedding_model.encode(message)
if _semantic_cache_emb is not None:
_sc.set(_semantic_cache_emb, {
"answer": filtered_answer,
"sources": sources,
"citations": citation_result.get("citations", []),
"images": rich_media.get("images", []),
"tables": rich_media.get("tables", []),
})
logger.debug(f"[语义缓存] 写入成功: {message[:50]}...")
except Exception as e:
logger.info(f"[语义缓存] 写入失败: {e}")
yield f"data: {json.dumps(finish_event, ensure_ascii=False)}\n\n"
except Exception as e:
@@ -2019,3 +2072,68 @@ def search():
'metadatas': results['metadatas'][0],
'scores': results['scores'][0]
})
# ==================== 缓存调试接口(临时) ====================
@chat_bp.route('/cache/stats', methods=['GET'])
def cache_stats():
"""缓存统计接口(调试用)"""
stats = {}
try:
from core.cache import get_cache_manager
cache = get_cache_manager()
all_stats = cache.get_all_stats()
for name, s in all_stats.items():
stats[name] = {
'total_entries': s.total_entries,
'hits': s.hits,
'misses': s.misses,
'hit_rate': f"{s.hit_rate:.2%}",
'evictions': s.evictions,
}
except Exception as e:
stats['_error'] = str(e)
# 语义缓存统计(全局单例 + 意图分析器实例)
try:
from core.semantic_cache import get_semantic_cache
sc = get_semantic_cache()
if sc:
stats['semantic_cache'] = sc.get_stats()
except Exception:
pass
try:
from core.intent_analyzer import IntentAnalyzer
if hasattr(IntentAnalyzer, '_instance'):
ia = IntentAnalyzer._instance
if hasattr(ia, 'semantic_cache') and ia.semantic_cache:
stats['semantic_cache_intent'] = ia.semantic_cache.get_stats()
except Exception:
pass
return jsonify(stats)
@chat_bp.route('/cache/clear', methods=['POST'])
def cache_clear():
"""缓存清空接口(调试用)"""
result = {}
try:
from core.cache import get_cache_manager
cache = get_cache_manager()
cache.clear_all()
result['lru_cache'] = 'cleared'
except Exception as e:
result['lru_cache'] = f'error: {e}'
try:
from core.semantic_cache import get_semantic_cache
sc = get_semantic_cache()
sc.clear()
result['semantic_cache'] = 'cleared'
except Exception:
result['semantic_cache'] = 'not_available'
return jsonify({'status': 'ok', 'cleared': result})