""" 语义搜索服务 SQLite 数据库,使用 LIKE 关键词搜索。 """ from __future__ import annotations import logging import time from typing import List from sqlalchemy import text from sqlalchemy.ext.asyncio import AsyncSession from app.schemas.search import SearchRequest, SearchResult, SearchResponse logger = logging.getLogger(__name__) class SearchService: """语义搜索服务""" def __init__(self, db: AsyncSession): self.db = db async def search(self, request: SearchRequest) -> SearchResponse: """ 执行搜索:使用 LIKE 关键词搜索 """ start_time = time.time() # LLM 查询扩展(可选) expanded_queries = [request.query] try: from app.services.llm_service import LLMService expanded_queries = await LLMService.expand_query(request.query) except Exception as exc: logger.debug("查询扩展跳过: %s", exc) results: list[SearchResult] = [] seen_chunk_ids = set() for q in expanded_queries: like_pattern = f"%{q}%" sql = text(""" SELECT kc.id AS chunk_id, kp.id AS page_id, kp.title AS page_title, kc.content, kp.course_name, kp.teacher_name, kp.live_date FROM knowledge_chunks kc JOIN knowledge_pages kp ON kc.page_id = kp.id WHERE kc.content LIKE :pattern AND (:course IS NULL OR kp.course_name = :course) AND (:teacher IS NULL OR kp.teacher_name = :teacher) ORDER BY kc.id LIMIT :limit """) params = { "pattern": like_pattern, "course": request.course_name, "teacher": request.teacher_name, "limit": request.top_k, } result = await self.db.execute(sql, params) rows = result.fetchall() for row in rows: if row.chunk_id not in seen_chunk_ids: seen_chunk_ids.add(row.chunk_id) # 简单的相关性评分:关键词出现次数 score = min(1.0, row.content.lower().count(q.lower()) * 0.2 + 0.5) results.append(SearchResult( chunk_id=row.chunk_id, page_id=row.page_id, page_title=row.page_title, content=row.content, score=round(score, 4), course_name=row.course_name, teacher_name=row.teacher_name, live_date=row.live_date, highlight=None, )) results.sort(key=lambda x: x.score, reverse=True) results = results[:request.top_k] elapsed_ms = (time.time() - start_time) * 1000 return SearchResponse( query=request.query, total=len(results), results=results, elapsed_ms=round(elapsed_ms, 2), )