先读字段,再开始搜索:科研 Agent 为什么需要 Schema Discovery
导语
科研 Agent 最危险的检索错误,未必是漏掉一篇论文,而是自信地调用一个不存在、无权限或不支持当前算子的字段。真正可维护的科研检索工作流,不应把元数据结构写死在 Prompt 里,而应先读取数据契约,再构造查询。
正文
当 Agent 开始维护科学软件,接口契约比 Prompt 更重要
2026 年 7 月,OpenAI 发布了一份关于 Agent 辅助科学计算的探索性报告,汇总了 8 个以生命科学为主的项目。报告观察到,研究者的角色正在从具体实现转向验证与编排:定义系统应该构建什么、怎样判断正确,以及何时可以交付。
同月,ACL Findings 收录的一篇综述将 Science of Science 场景中的 AI Agent 区分为两类:一类模拟科学共同体,另一类作为工具参与数据分析与科研工作流。综述同时把可靠性、数据质量与偏差列为关键挑战。
这两个信号指向同一个工程问题:
科研 Agent 不仅要会调用工具,还要知道工具此刻允许它怎样调用。
对于文献系统,这个问题尤其明显。用户可能提出:
“找出 2023 年以后发表、与固态电池界面稳定性相关、英文、可读取全文的高影响力论文。”
人类读到的是一个自然语言需求,Agent 却必须把它拆成多个机器约束:
- “2023 年以后”对应哪个年份字段?
- 语言字段叫
language、lang,还是别的名字? - 影响力能否排序?支持哪种排序值?
- “可读取全文”是否有可筛选字段?
- 当前 Token 是否有权访问这些字段?
- 字段支持等于、范围、包含,还是短语匹配?
如果模型仅凭训练记忆拼参数,生成的 JSON 即使语法正确,也可能根本不是一个合法查询。
科研检索中的 Schema 幻觉
普通问答中的幻觉通常出现在答案里;工具型 Agent 的幻觉还会出现在请求参数中。
一种常见实现,是把字段直接写入系统提示词:
年份使用 publication_year 语言使用 language 引用数使用 citation_count这在原型阶段很方便,但会迅速产生三类风险。
第一类是版本漂移。数据服务增加、改名或调整字段能力后,Prompt 中的旧字段不会自动更新。
第二类是权限漂移。不同 Token 能看到的字段范围可能不同。文档存在某个字段,不等于当前调用者一定可以使用。
第三类是算子错配。字符串、日期、数值和枚举字段支持的操作不相同。Agent 如果只知道字段名,不知道filterable、sortable和operators,仍然可能构造错误请求。
因此,科研 Agent 需要的不只是 API 文档,还需要一个可在运行时读取的数据契约。
不同学术数据服务,解决的是不同层次的问题
OpenAlex、Semantic Scholar、Crossref 和 PubMed 都是重要的科研数据基础设施,但各自的重点不同。下面的比较旨在说明使用方式差异,而不是判断谁能替代谁。
| 能力维度 | Sciverse | OpenAlex | Semantic Scholar | Crossref | PubMed |
|---|---|---|---|---|---|
| 结构化文献元数据 | 支持 | 核心能力 | 核心能力 | 核心能力 | 生物医学领域核心能力 |
| 字段目录的运行时发现 | meta-catalog面向 Agent 返回字段能力与算子 | 主要依据公开 API schema | 主要依据公开 API 文档 | 主要依据 REST API 文档 | 主要依据 E-utilities 规范 |
| 自然语言证据片段检索 | agentic-search | 非核心定位 | 提供检索与论文数据能力 | 非核心定位 | 以生物医学文献检索为主 |
| 原文上下文续读 | content是公开调用链的一部分 | 非核心定位 | 非核心定位 | 非核心定位 | 取决于关联全文来源 |
| 面向 Agent 的工具封装 | 提供 SDK、MCP 与 Agent Tools | 通常需要开发者封装 | 通常需要开发者封装 | 通常需要开发者封装 | 通常需要开发者封装 |
如果任务是构建开放学术图谱,OpenAlex 很合适;如果要获取 DOI 注册元数据,Crossref 是重要来源;如果聚焦生物医学检索,PubMed 仍有清晰的领域优势。
Sciverse 的切入点不同:它把科学文献检索、元数据筛选和原文取证组织成可进入 Agent 工作流的数据接口,并通过meta-catalog让 Agent 在运行时发现当前可用的元数据能力。
meta-catalog:让数据接口描述自己
根据当前公开 OpenAPI,Sciverse 对外提供 6 个接口,其中:
GET /meta-catalog:发现当前 Token 可见的元数据字段、字段类型、筛选与排序能力、合法算子及可选样本值。POST /meta-search:依据这些字段执行过滤、排序、字段投影、分页和 facets 查询。
两者不是两个孤立功能,而是一组“发现—执行”协议:
用户自然语言需求 ↓ Agent 提取筛选意图 ↓ GET /meta-catalog 读取字段、类型、能力、operators ↓ 字段映射与请求校验 ↓ POST /meta-search ↓ 处理 results / total_count / next_cursor ↓ 必要时再进入原文或其他证据链路meta-catalog的字段描述可能包括:
| 返回信息 | Agent 应如何使用 |
|---|---|
name | 作为meta-search的真实字段名,禁止自行改写 |
type | 判断值应按字符串、数值、日期或其他类型处理 |
filterable | 决定字段能否进入filters |
sortable | 决定字段能否进入sort |
searchable | 判断字段是否支持检索语义 |
operators | 从服务端允许的算子中选择,而非自行发明 |
sample_values | 辅助识别枚举取值;仅在请求且服务可提供时出现 |
description | 帮助模型把自然语言概念映射到正确字段 |
这里最重要的设计不是“多调用一次接口”,而是改变 Agent 的决策顺序:
先用服务端返回的 schema 约束模型,再让模型生成检索请求。
一个更稳健的 Agent 架构
实际系统可以把字段自发现分成四层。
第一层:意图解析
模型只负责提取概念,不立即生成最终 API 字段。例如:
{"topic":"solid-state battery interface stability","constraints":{"publication_year":{"gte":2023},"language":"English"},"preferences":{"fulltext_required":true,"rank_by":"citation impact"}}这里的publication_year和language只是内部语义标签,不直接发送给 Sciverse。
第二层:Schema Resolver
Resolver 调用meta-catalog,寻找与内部语义最匹配且满足能力要求的字段。
例如,年份约束必须找到:
- 语义描述匹配“发表年份”;
filterable=true;operators包含合适的范围算子。
如果找不到,系统应明确返回“当前数据契约不支持该筛选”,而不是猜一个字段。
第三层:请求编译与校验
将解析后的意图编译为meta-search请求,并在发出前校验:
- 每个字段都出现在本次 catalog 中;
- 每个字段支持当前操作;
- 只对
sortable=true的字段排序; - 非空
query不与sort同时发送; - 页码、页大小和深分页方式符合最新文档。
第四层:结果路由
meta-search返回的是候选论文元数据,不是最终科学结论。Agent 后续可以根据任务继续读取原文、核验上下文或组织证据,但不能把一组元数据记录直接包装成确定性结论。
Python:先发现字段,再构造查询
以下示例使用当前公开 REST 接口,不依赖虚构 SDK。它先读取 catalog,再从服务端返回的数据中选择一个真实可筛选字段和合法算子,最后执行一次元数据查询。
以下字段以最新线上文档 / OpenAPI 为准。
importosimporttimeimportrequests BASE_URL="https://api.sciverse.space"API_TOKEN=os.environ["SCIVERSE_API_TOKEN"]HEADERS={"Authorization":f"Bearer{API_TOKEN}","Content-Type":"application/json",}defrequest_with_retry(method,url,**kwargs):"""处理 429 和可重试的网关错误。"""forattemptinrange(4):response=requests.request(method,url,headers=HEADERS,timeout=30,**kwargs,)ifresponse.status_code==429:retry_after=response.headers.get("Retry-After")wait_seconds=(int(retry_after)ifretry_afterandretry_after.isdigit()else2**attempt)time.sleep(wait_seconds)continueifresponse.status_codein{502,503,504}:time.sleep(2**attempt)continueresponse.raise_for_status()returnresponseraiseRuntimeError("Sciverse API 多次限流或暂时不可用")# 1. 读取当前 Token 可见的数据契约catalog_response=request_with_retry("GET",f"{BASE_URL}/meta-catalog",params={"include_sample_values":"true"},)catalog_payload=catalog_response.json()# 兼容直接返回与统一 data 信封;以实际 OpenAPI 响应为准catalog=catalog_payload.get("data",catalog_payload)fields=catalog.get("fields",[])# 2. 选择服务端明确标记为可筛选、且提供样本值的字段candidate=next((fieldforfieldinfieldsiffield.get("filterable")andfield.get("sample_values")andfield.get("operators")),None,)ifcandidateisNone:raiseRuntimeError("当前 catalog 中没有适合本示例的可筛选字段")field_name=candidate["name"]sample_value=candidate["sample_values"][0]operators=candidate["operators"]# 优先使用等值算子;服务端未声明时不自行编造operator=next((opforopinoperatorsifop=="FILTER_OP_EQ"),operators[0],)# 3. 用运行时发现的字段构造 meta-searchsearch_body={"filters":[{"field":field_name,"operator":operator,"value":sample_value,}],"fields":["title",field_name],"page":1,"page_size":10,}search_response=request_with_retry("POST",f"{BASE_URL}/meta-search",json=search_body,)search_payload=search_response.json()search_data=search_payload.get("data",search_payload)# 4. 处理响应字段print("使用字段:",field_name)print("使用算子:",operator)print("总结果数:",search_data.get("total_count"))forpaperinsearch_data.get("results",[]):print({"doc_id":paper.get("doc_id"),"title":paper.get("title"),field_name:paper.get(field_name),})next_cursor=search_data.get("next_cursor")ifnext_cursor:print("存在下一页 cursor,可按最新文档继续深分页")生产系统还应该增加两项控制。
其一,把 catalog 按 Token、环境和版本短期缓存,避免在每次搜索前重复读取;但不能把缓存固化成永不过期的代码常量。
其二,记录“用户意图—匹配字段—选用算子—最终请求”的编译轨迹。这样当检索结果异常时,开发者能判断问题来自自然语言解析、字段映射,还是数据服务本身。
为什么不能只把 OpenAPI 全部塞进上下文
把完整 OpenAPI 放进 Agent 的系统提示词,看起来也能解决字段问题,但它和运行时发现并不等价。
首先,长 schema 会持续占用上下文;当 Agent 只需要两个过滤字段时,没必要携带完整接口说明。
其次,静态 OpenAPI 描述的是公开契约,而运行时 catalog 可以反映当前 Token 可见的字段和能力。权限相关的信息更适合在执行前确认。
再次,Agent 真正需要的不是“读过文档”,而是一个确定性校验步骤。即使模型上下文里已经有字段说明,程序仍应在发送请求前检查字段与算子是否合法。
因此,更合适的分工是:
- OpenAPI 定义稳定的接口结构;
meta-catalog提供运行时元数据能力;- 模型解释用户意图;
- 程序负责请求编译、校验和错误处理。
如何验证 Schema Discovery 是否真的有效
本文未进行实测跑分,仅提供可复现评测方案。
可以准备一组包含正常、模糊和不可满足条件的科研检索任务,对比两种 Agent:
- 基线组:Prompt 中硬编码字段,直接生成
meta-search请求。 - 实验组:先调用
meta-catalog,再映射字段并执行本地校验。
建议记录以下指标:
| 评测指标 | 验证方法 |
|---|---|
| 字段合法率 | 请求中字段是否出现在本次 catalog |
| 算子合法率 | 所选算子是否属于对应字段的operators |
| 首次请求成功率 | 是否无需修正即可得到 2xx 响应 |
| 约束忠实度 | 最终请求是否保留用户提出的年份、语言等条件 |
| 不支持条件识别率 | 字段不存在时是否明确拒绝,而非虚构参数 |
| Schema 更新适应性 | 修改可用字段后,是否无需改 Prompt 即可恢复工作 |
| 额外调用成本 | 统计 catalog 缓存命中率与增加的请求次数 |
| 可审计性 | 是否完整记录意图到字段的映射过程 |
测试任务不应只包含容易映射的“按年份搜索”,还应加入:
- 用户使用字段别名;
- 一个条件存在多个近似字段;
- 字段可返回但不可筛选;
- 字段可筛选但不可排序;
- 当前 Token 无权访问目标字段;
- 用户同时提出全文关键词与排序要求;
- 用户要求一个 catalog 中不存在的概念。
真正可靠的 Agent,不是每次都勉强生成一个请求,而是知道什么时候应该停止并说明能力边界。
从“会调接口”走向“理解数据契约”
科研 Agent 的能力上限,不只由模型决定,也由工具能否被稳定发现、组合和验证决定。
meta-catalog看起来只是一个字段目录接口,实际解决的是 Agent 工程中的基础问题:让模型面对变化的数据结构时,不必依赖参数记忆和 Prompt 硬编码。
Sciverse 的定位也由此更清楚:它不是普通文献搜索框,也不替 Agent 生成最终科学结论,而是面向科研 Agent 的 AI-ready 科学数据层。它向 Cursor、Claude、Codex、RAG 和 MCP 工作流提供可发现、可调用、可继续核验的科学数据能力。
如果正在构建 Literature Review Agent、科研筛选器或文献 RAG,可以从一个简单约束开始:
不允许 Agent 使用任何未经当前 schema 验证的元数据字段。
查看 Sciverse 文档,核对最新 OpenAPI;接入 Sciverse Agent Tools,把list_catalog与search_papers纳入同一调用链;再通过 Cursor、Claude、Codex 或 MCP,让科研 Agent 从“猜参数”升级为“按数据契约行动”。
参考来源
- Sciverse 官方文档
- Sciverse 最新公开 OpenAPI
- Sciverse llms.txt
- Sciverse llms-full.txt
- Sciverse Agent Tools
- OpenAI:Scientific computing in the age of agentic AI
- ACL Anthology:AI Agents for the Science of Science
