Files
law-kb/app/routers/search.py
T
freedak 641e33b834 feat: QYLAW 法律法规知识库
- 语义检索(FAISS + embedding)+ 精确查找(法规名+条号)
- RAG 问答(SSE 流式,支持 thinking 折叠显示)
- 法规浏览(原文阅读)
- 历史记录(检索+对话持久化到 SQLite)
- 设置页(系统提示词/模板/LLM 参数可配置)
- 检索质量评估脚本

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-07 14:55:25 +08:00

90 lines
2.9 KiB
Python

"""检索 API — /api/search"""
from fastapi import APIRouter, Request, HTTPException, Query
from typing import Optional
from app.services.retriever import Retriever
from app.services.embedding import embed_text
from app.services.history import add_search_history
from app.config import MAX_QUERY_LENGTH
router = APIRouter()
@router.get("/search")
async def search(
request: Request,
query: str = Query(..., min_length=1, max_length=MAX_QUERY_LENGTH, description="查询文本"),
mode: str = Query("semantic", description="检索模式:semantic(语义) / keyword(关键词精确)"),
category: Optional[str] = Query(None, description="法规类别过滤"),
province: Optional[str] = Query(None, description="省份过滤"),
city: Optional[str] = Query(None, description="市级过滤"),
top_k: int = Query(20, ge=1, le=100, description="返回数量"),
page: int = Query(1, ge=1, description="页码"),
page_size: int = Query(20, ge=1, le=100, description="每页数量"),
):
"""检索法规条文(语义检索或关键词精确搜索)
- mode=semantic: 向量化查询 → FAISS 检索 → metadata 过滤(默认)
- mode=keyword: 按法规名+条号精确匹配,支持"郑州市劳动用工条例 第三十二条"
"""
trace_id = getattr(request.state, "trace_id", "")
retriever = Retriever.get_instance()
if not retriever.is_ready():
raise HTTPException(status_code=503, detail="索引未加载,请等待或检查索引文件")
if mode == "keyword":
# 关键词精确搜索
results = retriever.keyword_search(
query=query,
top_k=top_k * 3 if (category or province or city) else top_k,
category=category,
province=province,
city=city,
)
else:
# 语义检索
try:
query_vec = await embed_text(query)
except Exception as e:
raise HTTPException(
status_code=503,
detail=f"embedding 服务不可用: {str(e)}",
)
fetch_k = top_k * 3 if (category or province or city) else top_k
results = retriever.search(
query_vec=query_vec,
top_k=fetch_k,
category=category,
province=province,
city=city,
)
# 分页
total = len(results)
start = (page - 1) * page_size
end = start + page_size
page_results = results[start:end]
# 记录检索历史
add_search_history(
query=query,
category=category,
province=province,
city=city,
result_count=total,
top_k=top_k,
)
return {
"code": 0,
"message": "ok",
"data": {
"results": page_results,
"total": total,
"page": page,
"page_size": page_size,
},
"trace_id": trace_id,
}