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:
@@ -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
|
||||
|
||||
@@ -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})
|
||||
|
||||
Reference in New Issue
Block a user