Initial commit: QMDSearch 分层信息检索服务
- FastAPI + Qdrant + Redis + Ollama 技术栈 - L1→L2→L3→chunk 四层分层检索(dense + sparse RRF 融合) - 文档三级总结与 2.5 级回退 - query 解析路由与分类 - /admin 管理页面
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
"""检索 API:POST /api/v1/search"""
|
||||
|
||||
from hashlib import sha256
|
||||
from typing import Any
|
||||
|
||||
import structlog
|
||||
from fastapi import APIRouter
|
||||
|
||||
from app.api.response import ApiError, ok
|
||||
from app.core.retriever import Retriever
|
||||
from app.models.search import SearchRequest
|
||||
from app.services.redis import get_cache
|
||||
|
||||
logger = structlog.get_logger()
|
||||
|
||||
router = APIRouter(prefix="/api/v1", tags=["search"])
|
||||
|
||||
# 模块级懒加载单例,避免每请求重建 Qdrant/Embedding client
|
||||
_retriever: Retriever | None = None
|
||||
|
||||
|
||||
def _get_retriever() -> Retriever:
|
||||
global _retriever
|
||||
if _retriever is None:
|
||||
_retriever = Retriever()
|
||||
return _retriever
|
||||
|
||||
|
||||
def _cache_key(request: SearchRequest) -> str:
|
||||
"""检索缓存键:query + top_k 的短哈希(top_k 影响结果集,需参与键计算)"""
|
||||
digest = sha256((request.query + "|" + str(request.top_k)).encode()).hexdigest()[:16]
|
||||
return f"search:{digest}"
|
||||
|
||||
|
||||
@router.post("/search")
|
||||
async def search(request: SearchRequest) -> dict[str, Any]:
|
||||
"""分层检索入口,返回统一包装的 SearchResponse
|
||||
|
||||
先查 Redis 缓存:命中直接返回缓存的响应;未命中走检索流程并回写缓存。
|
||||
缓存读写失败均降级为无缓存行为,不影响检索。
|
||||
"""
|
||||
cache_key = _cache_key(request)
|
||||
cached = await get_cache().get_json(cache_key)
|
||||
if cached is not None:
|
||||
logger.info("检索缓存命中", query=request.query, cache_key=cache_key)
|
||||
return cached
|
||||
|
||||
try:
|
||||
response = await _get_retriever().search(request)
|
||||
except ApiError:
|
||||
raise
|
||||
except Exception as exc:
|
||||
logger.exception("检索失败", query=request.query)
|
||||
raise ApiError(2000, f"检索失败: {exc}") from exc
|
||||
|
||||
result = ok(response.model_dump())
|
||||
await get_cache().set_json(cache_key, result)
|
||||
return result
|
||||
Reference in New Issue
Block a user