"""检索请求与响应的数据模型""" from pydantic import BaseModel, Field class SearchRequest(BaseModel): """检索请求""" query: str = Field(description="查询文本") top_k: int | None = Field(default=None, description="返回结果数,为空时使用 settings.retrieval_final_k") summarize: bool = Field(default=False, description="是否对检索结果生成 AI 总结") class SearchHit(BaseModel): """单条检索命中结果""" text: str = Field(description="原文 chunk 内容") doc_id: str = Field(description="所属文档 ID") title: str = Field(default="", description="文档标题") section_path: str = Field(default="", description="chunk 所在的章节路径") score: float = Field(description="相关性得分") doc_summary: str = Field(default="", description="L1 文档总结,仅用于上下文标注") class ExtractedInfo(BaseModel): """AI 从 query 中提取的关键信息""" rewrite: str = Field(default="", description="改写后的 query") keywords: list[str] = Field(default_factory=list, description="关键词") entities: list[str] = Field(default_factory=list, description="实体(人名/产品/技术等)") intent: str = Field(default="", description="查询意图分类") time_range: str = Field(default="", description="时间范围,空表示无") categories: list[str] = Field(default_factory=list, description="命中类目名(含未达阈值的候选)") class SearchResponse(BaseModel): """检索响应""" query: str = Field(description="原始查询文本") hits: list[SearchHit] = Field(default_factory=list, description="命中结果列表") routed_categories: list[str] = Field(default_factory=list, description="query 路由命中的类目") fallback: bool = Field(default=False, description="是否走了全库兜底路径") extracted_info: ExtractedInfo | None = Field(default=None, description="AI 提取的 query 关键信息") summary: str | None = Field(default=None, description="检索结果 AI 总结,仅 summarize=true 时返回")