init: RAG 知识库服务初始提交
- 后端 API(Flask + Gunicorn) - RAG 引擎(混合检索 + 云端 Reranker + 引用溯源) - 文档解析(MinerU + 多格式支持) - Docker 生产部署配置 - 排除前端项目、敏感配置、模型文件
This commit is contained in:
152
core/agentic_search.py
Normal file
152
core/agentic_search.py
Normal file
@@ -0,0 +1,152 @@
|
||||
"""
|
||||
Agentic RAG - 检索 Mixin
|
||||
|
||||
包含知识库检索、网络搜索等方法
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
import requests
|
||||
|
||||
from .agentic_base import (
|
||||
logger, HAS_SERPER, SERPER_API_KEY,
|
||||
SOURCE_KB, SOURCE_WEB
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SearchMixin:
|
||||
"""检索功能方法"""
|
||||
|
||||
def _web_search(self, query: str, top_k: int = 5) -> list:
|
||||
"""网络搜索(使用Serper API)"""
|
||||
if not HAS_SERPER:
|
||||
return []
|
||||
|
||||
try:
|
||||
url = "https://google.serper.dev/search"
|
||||
payload = json.dumps({
|
||||
"q": query,
|
||||
"gl": "cn",
|
||||
"hl": "zh-cn",
|
||||
"num": top_k
|
||||
})
|
||||
headers = {
|
||||
'X-API-KEY': SERPER_API_KEY,
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
|
||||
response = requests.post(url, headers=headers, data=payload, timeout=10)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
|
||||
results = []
|
||||
for item in data.get('organic', [])[:top_k]:
|
||||
results.append({
|
||||
'title': item.get('title', ''),
|
||||
'link': item.get('link', ''),
|
||||
'snippet': item.get('snippet', ''),
|
||||
'date': item.get('date', '')
|
||||
})
|
||||
|
||||
return results
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"网络搜索失败: {e}")
|
||||
return []
|
||||
|
||||
def _should_web_search(self, query: str) -> bool:
|
||||
"""判断是否需要网络搜索"""
|
||||
realtime_keywords = [
|
||||
"今天", "最新", "今日", "当前", "现在",
|
||||
"天气", "新闻", "股价", "行情", "汇率",
|
||||
"最近", "近期", "这周", "本月", "今年",
|
||||
"实时", "动态", "热点", "发生"
|
||||
]
|
||||
|
||||
query_lower = query.lower()
|
||||
return any(kw in query_lower for kw in realtime_keywords)
|
||||
|
||||
def _web_search_flow(self, query: str, log_trace: list, emit_log, verbose: bool,
|
||||
allowed_levels: list = None) -> list:
|
||||
"""
|
||||
网络搜索流程
|
||||
|
||||
Args:
|
||||
query: 查询
|
||||
log_trace: 日志追踪列表
|
||||
emit_log: 日志发射函数
|
||||
verbose: 是否详细输出
|
||||
allowed_levels: 允许的安全级别
|
||||
|
||||
Returns:
|
||||
网络搜索结果列表
|
||||
"""
|
||||
if not self.enable_web_search or not HAS_SERPER:
|
||||
return []
|
||||
|
||||
if emit_log:
|
||||
emit_log("🌐 触发网络搜索...")
|
||||
|
||||
web_results = self._web_search(query, top_k=5)
|
||||
|
||||
if not web_results:
|
||||
if emit_log:
|
||||
emit_log("⚠️ 网络搜索未返回结果")
|
||||
return []
|
||||
|
||||
# 转换为统一上下文格式
|
||||
web_contexts = []
|
||||
for item in web_results:
|
||||
web_contexts.append({
|
||||
'doc': f"{item.get('title', '')}\n{item.get('snippet', '')}",
|
||||
'meta': {
|
||||
'source': self.SOURCE_WEB,
|
||||
'link': item.get('link', ''),
|
||||
'date': item.get('date', '')
|
||||
},
|
||||
'source_type': self.SOURCE_WEB,
|
||||
'query': query
|
||||
})
|
||||
|
||||
log_trace.append({
|
||||
'phase': 'web_search',
|
||||
'query': query,
|
||||
'results_count': len(web_contexts)
|
||||
})
|
||||
|
||||
if emit_log:
|
||||
emit_log(f"✅ 网络搜索返回 {len(web_contexts)} 条结果")
|
||||
|
||||
return web_contexts
|
||||
|
||||
def _is_kb_result_sufficient(self, query: str, docs: list) -> bool:
|
||||
"""判断知识库检索结果是否充分"""
|
||||
if not docs:
|
||||
return False
|
||||
|
||||
# 结果数量检查
|
||||
if len(docs) >= 3:
|
||||
# 至少3条结果,检查相关性
|
||||
high_rel_count = 0
|
||||
for doc in docs:
|
||||
score = doc.get('score', 0) or doc.get('distance', 1)
|
||||
# cosine 距离转相似度
|
||||
if isinstance(score, (int, float)):
|
||||
sim = 1 - score if score <= 1 else score
|
||||
if sim >= 0.6:
|
||||
high_rel_count += 1
|
||||
|
||||
if high_rel_count >= 2:
|
||||
return True
|
||||
|
||||
# 有高质量结果
|
||||
for doc in docs[:2]:
|
||||
score = doc.get('score', 0) or doc.get('distance', 1)
|
||||
if isinstance(score, (int, float)):
|
||||
sim = 1 - score if score <= 1 else score
|
||||
if sim >= 0.8:
|
||||
return True
|
||||
|
||||
return False
|
||||
Reference in New Issue
Block a user