# Survey 检索与 Scorer 的整合边界 本文件说明 survey 与可编程 Scorer 共存时的接口边界。survey 不支持 Scorer DSL;两者保留各自的检索与评分规则。 ## 两个入口各自做什么 | 接口 | 用途 | 排序控制 | |---|---|---| | `POST /v1/search` | 文本/身份检索,fused或multi,可选网页反喂 | 保留默认、`ranking: "auto"`、`ranking: "scorer"` | | `POST /v1/survey-search` | 有界的多查询综述检索与作者/venue/年份/引用规则过滤 | `rules.sort` 为 relevance/newest/citations;可选agentic规划建议权重 | `agentic: true` 在 survey 中表示一次 LLM 查询规划,不是 `/v1/search` 的 `agent: true` 总结,也不是自定义 scorer。 ## Survey 请求示例 在微服务已经部署并可达时,可发送: ```json { "goal": "surface code threshold", "queries": ["surface code threshold", "surface code decoding"], "sources": ["arxiv", "openalex"], "rules": { "year_from": 2020, "min_citations": 10, "sort": "citations" }, "max_results": 10, "agentic": false } ``` - goal为非空文本,最多4000字符。 - queries可选,最多6条,每条非空且最多500字符;缺省用规则式规划产生查询。 - sources缺省为semantic_scholar、arxiv、openalex,不使用search.default_tools。 - rules可指定authors、venues、year_from、year_to、min_citations、sort。 - max_results默认25,范围1~100,表示最终聚合数量,不是全局上游请求预算。 - agentic=true且LLM配置齐全时,可扩展查询并建议有界权重;显式规则不能被LLM修改。 - api_keys由可信调用方注入并在handler中裁剪,不回显在plan中,也不在SurveyRequest的repr中展示。 响应包含plan、usage、hits、queries、errors、coverage;不包含scorer元数据或score_explanation。 ## 不支持的参数必须显式拒绝 survey请求的**顶层**不得出现以下字段,即使值为null或false: - ranking - scorer - explain 出现时返回400,例如: ```json { "detail": { "code": "unsupported_survey_parameter", "field": "scorer", "message": "survey-search does not support scorer; use /v1/search" } } ``` 错误在规划、LLM和backend调用之前产生,不会忽略程序后执行默认排序。 需要自定义评分时使用 `/v1/search`,详见 [Scorer 使用指南](scorers.md)。其他普通未知字段按SurveyRequest的兼容策略忽略;survey模型并非整体设置为extra=forbid。 ## 请求安全保护 `/v1/search` 和 `/v1/survey-search` 共享以下保护: 1. 配置service_token时,在读取、解码请求体之前校验Bearer令牌。 2. 实际请求体最多64KiB,不能通过省略/伪造Content-Length或分块传输绕过。 3. 请求体必须为UTF-8 JSON,不接受字面NUL或UTF-16/32字节编码。 4. JSON容器嵌套最多64,在完整JSON解析前检查。 5. 支持ASGI root_path和尾部斜杠路径;其他非搜索路由行为不变。 令牌未配置时接口仍开放。上述请求保护不能替代部署网络隔离、令牌配置或配置加载失败时的风险审查。 ### Survey 错误输出 | 情况 | HTTP状态 | |---|---:| | 缺少/错误令牌(配置了service_token) | 401 | | 非法编码 | 400 | | 顶层ranking/scorer/explain | 400 | | 请求体超64KiB | 413 | | JSON过深、JSON语法错误、缺字段、字段类型/范围或规则验证失败 | 422 | 一般验证错误返回: ```json { "detail": { "code": "invalid_survey_request", "message": "Invalid survey request; check the reported fields.", "errors": [{"type": "missing", "loc": ["body", "goal"]}], "truncated": false } } ``` 最多列出20个验证错误;超出时truncated=true。保留静态字段路径和错误类型,不回显input、body、ctx或验证器原始message。api_keys中的动态键名在位置中替换为`[key]`,避免把调用方输入作为错误路径泄漏。 仅survey使用这个新的安全验证错误协议;原有search、admin等路由的验证响应保持原样。搜索的ScorerError仍走它原来的安全错误处理。 ## 保留而未改写的检索语义 - survey对各源记录先执行rules,再合并;跨源互补的作者/引用信息不会救回先前已过滤的记录。 - source/author/venue等缺失元数据不满足相应规则;作者规范化匹配不等于ORCID身份消歧。 - queries顺序执行,单个查询的backends并行执行。 - survey通过run_multi后自行rank,不使用普通fused搜索的网页reinject阶段。 - coverage.exhaustive始终为false;返回的是有界取回集合,不是全部文献中的全局排序。 - 返回hit使用Paper.model_dump,不统一改成search的四位小数账单序列化。 这些差异需由调用方理解,不能把survey当作search的可互换替代接口。 ## Semantic Scholar 节流的行为变化 RequestSlots使同一S2 Key在一个进程内的请求发起至少间隔1秒;重试、身份查询、默认/auto/scorer、survey和reinject共享同一闸门。匿名调用共用anonymous槽。 - 空闲Key的首请求不必等待;并非每次固定增加1秒。 - 闸门等待发生在HTTP请求timeout之外;当前没有总等待deadline,也没有Key槽回收机制。 - 并发或多查询时,整个检索的等待时间可能超过request_timeout。 - 多进程/副本需要外部限流协调或同Key粘性路由。 - 只选择arxiv、openalex时不会直接进入S2闸门;这个节流不会解决OpenAlex的429。 若部署有严格并发或延迟要求,应另行评估并测试有界等待和槽回收,不能仅靠单次HTTP请求timeout推断总延迟上限。 ## 验证与发布 `tests/test_survey_service.py` 覆盖进程内FastAPI/ASGI路径和模拟backend/LLM;默认测试同时回归scorer、survey及S2行为,不调用真实外部API。从仓库根目录执行: ```bash uv sync --locked --extra dev uv run --no-sync --offline python -m pytest -q ``` 版本由本仓 `pyproject.toml` 与 Commitizen 流程管理,见 [项目概览:版本与发布开发流程](overview.md#版本与发布开发流程)。不手改VERSION、重写旧tag或覆盖已发布产物。离线测试不证明qatlasd/网页的端到端透传与展示正确;选定版本仍需单独验证调用方兼容性。