导语
科研 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 都是重要的科研数据基础设施,但各自的重点不同。下面的比较旨在说明使用方式差异,而不是判断谁能替代谁。
| 结构化文献元数据 | 支持 | 核心能力 | 核心能力 | 核心能力 | 生物医学领域核心能力 |
| 字段目录的运行时发现 | 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 的字段描述可能包括:
| 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 为准。
import os
import time
import requests
BASE_URL = "https://api.sciverse.space"
API_TOKEN = os.environ["SCIVERSE_API_TOKEN"]
HEADERS = {
"Authorization": f"Bearer {API_TOKEN}",
"Content-Type": "application/json",
}
def request_with_retry(method, url, **kwargs):
"""处理 429 和可重试的网关错误。"""
for attempt in range(4):
response = requests.request(
method,
url,
headers=HEADERS,
timeout=30,
**kwargs,
)
if response.status_code == 429:
retry_after = response.headers.get("Retry-After")
wait_seconds = (
int(retry_after)
if retry_after and retry_after.isdigit()
else 2 ** attempt
)
time.sleep(wait_seconds)
continue
if response.status_code in {502, 503, 504}:
time.sleep(2 ** attempt)
continue
response.raise_for_status()
return response
raise RuntimeError("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(
(
field
for field in fields
if field.get("filterable")
and field.get("sample_values")
and field.get("operators")
),
None,
)
if candidate is None:
raise RuntimeError("当前 catalog 中没有适合本示例的可筛选字段")
field_name = candidate["name"]
sample_value = candidate["sample_values"][0]
operators = candidate["operators"]
# 优先使用等值算子;服务端未声明时不自行编造
operator = next(
(op for op in operators if op == "FILTER_OP_EQ"),
operators[0],
)
# 3. 用运行时发现的字段构造 meta-search
search_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"))
for paper in search_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")
if next_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 从“猜参数”升级为“按数据契约行动”。


