Zipcodebase
通过 OOMOL 连接查询全球邮政编码信息,支持城市、距离、区域等多维度检索。
通过DOI或标题自动获取开放获取论文PDF,支持多源优先尝试。
openclaw skills install @agents365-ai/paper-fetch命令、参数、文件名以原文为准
根据 DOI(或标题)获取论文的 PDF 文件。按优先级顺序尝试多个来源,一旦成功即停止。
原生代理支持。 输出为结构化 JSON 格式,标准错误输出为 NDJSON 进度信息(包含会话头以检测版本漂移,含 schema_version / cli_version),具备稳定退出码、机器可读的模式、TTY 感知的默认格式、幂等重试机制。在可重试的错误情况下,会输出 retry_after_hours。
在对话中首次调用本技能时,静默保持技能为最新状态。检查 <this-skill-dir>/.last_update 文件是否存在且未超过 24 小时。若文件缺失或已过期,执行以下命令:
git -C <this-skill-dir> pull --ff-only && date +%s > <this-skill-dir>/.last_update若拉取失败(离线、冲突、非 Git 仓库、工作区有未提交更改等),忽略错误并继续正常运行。除非用户主动询问,否则不要向用户提及更新。 在同一对话中的后续调用跳过此步骤。
https://api.unpaywall.org/v2/{doi}?email=$UNPAYWALL_EMAIL,读取 best_oa_location.url_for_pdf(当 UNPAYWALL_EMAIL 未设置时跳过)https://api.semanticscholar.org/graph/v1/paper/DOI:{doi}?fields=openAccessPdf,externalIdsexternalIds.ArXiv,则使用 https://arxiv.org/pdf/{arxiv_id}.pdfhttps://www.ncbi.nlm.nih.gov/pmc/articles/{pmcid}/pdf/10.1101,查询 https://api.biorxiv.org/details/{server}/{doi} 获取最新版本的 PDF 地址PAPER_FETCH_INSTITUTIONAL=1)—— 根据 DOI 前缀匹配出版社 PDF 模板(Nature / Science / Wiley / Springer / ACS / PNAS / NEJM / Sage / T&F / Elsevier)。获取权限依赖于调用方的订阅 IP / Cookie / EZproxy;未授权响应无法通过 %PDF 检查,将进入第 7 步PAPER_FETCH_NO_SCIHUB=1 禁用)—— 最后手段。按顺序尝试 PAPER_FETCH_SCIHUB_MIRRORS 中的镜像列表(或内置默认值:sci-hub.ru, sci-hub.st, sci-hub.su, sci-hub.box, sci-hub.red, sci-hub.al, sci-hub.mk, sci-hub.ee);全部失败后,每个进程只爬取一次 https://www.sci-hub.pub/ 以获取新镜像。遇到 CAPTCHA 或“找不到论文”页面(无 PDF iframe)时,静默跳过若仅提供标题,则直接通过 --title "<title>" 传入。解析链如下:
query.title — 主要途径,覆盖大多数期刊和会议的 DOI/paper/search/match** — 当 Crossref 的首选结果置信度低(match_score < 40)或与次优结果差距小于 3 时作为备选。关键点:S2 可覆盖 arXiv 专属预印本(无 Crossref DOI)。当 S2 返回的论文仅有 arXiv ID 时,会合成标准 10.48550/arXiv.<id> 以保持下载链统一meta.title_resolution.low_confidence: true 以及 low_confidence_reason(值为 score_below_threshold 或 ambiguous_runner_up),以便代理决定是否放弃或通过 --dry-run 确认无论哪种方式,最终解析出的 DOI、获胜解析器、所有尝试过的解析器列表,以及最佳候选匹配结果,均会在 meta.title_resolution 中完整呈现。
**若 asta-skill 已注册**,代理可选择先通过 Asta MCP 完成标题 → DOI 解析,再直接调用本技能。此举跳过 paper-fetch 的两阶段 Crossref/S2 解析流程,改用 Asta 更丰富的搜索能力(相关性排序、片段搜索、引用图谱)。工作流:调用 asta__search_paper_by_title("<title>", fields="title,year,authors,externalIds"),读取 externalIds.DOI(或当仅有 ArXiv 时使用 10.48550/arXiv.<ArXiv>),然后执行 paper-fetch <doi>。当 Asta 不可用或希望单条命令完成时,仍可使用 --title。
python scripts/fetch.py <DOI> [options]
python scripts/fetch.py --title "<paper title>" [options]
python scripts/fetch.py --batch <FILE|-> [options]
python scripts/fetch.py schema # 机器可读的自我描述| 参数 | 默认值 | 说明 |
|---|---|---|
doi | — | 要获取的 DOI(位置参数)。使用 - 可从标准输入读取单个 DOI |
--title TITLE | — | 论文标题;通过 Crossref 解析为 DOI 后再下载。与位置 DOI / --batch 互斥 |
--batch FILE | — | 包含每行一个 DOI 的文件,用于批量下载。使用 - 可从标准输入读取 |
--out DIR | pdfs | 输出目录 |
--dry-run | 关闭 | 仅解析来源,不下载;预览 PDF 地址和目标路径 |
--format | auto | json 用于代理,text 用于人类。自动检测:非 TTY 输出为 json,TTY 输出为 text |
--pretty | 关闭 | 以 2 空格缩进格式化输出 JSON |
--stream | 关闭 | 每个 DOI 解析后在标准输出中逐行输出一条 NDJSON,批量模式下最后输出摘要行 |
--overwrite | 关闭 | 即使目标文件已存在也重新下载 |
--idempotency-key KEY | — | 安全重试密钥。使用相同密钥重运行时,将从 <out>/.paper-fetch-idem/ 重现原始响应包,无需网络请求 |
--timeout SECONDS | 30 | 每个请求的 HTTP 超时时间(秒) |
--version | — | 打印 CLI 和 schema 版本后退出 |
schema 子命令python scripts/fetch.py schema在标准输出中发出完整的机器可读 CLI 描述(无需网络)。包含 cli_version、schema_version、参数类型、退出码、错误码、响应结构、环境变量等信息。建议代理首次读取后缓存该描述,并根据 schema_version 判断是否需要重新读取。
stdout 输出单个 JSON 包装对象。每个包都包含一个 meta 字段。
成功(所有 DOI 均已解析):
{
"ok": true,
"data": {
"results": [
{
"doi": "10.1038/s41586-021-03819-2",
"success": true,
"source": "unpaywall",
"pdf_url": "https://www.nature.com/articles/s41586-021-03819-2.pdf",
"file": "pdfs/Jumper_2021_Highly_accurate_protein_structure_predic.pdf",
"meta": {"title": "Highly accurate protein structure prediction with AlphaFold", "year": 2021, "author": "Jumper"},
"sources_tried": ["unpaywall"]
}
],
"summary": {"total": 1, "succeeded": 1, "failed": 0},
"next": []
},
"meta": {
"request_id": "req_a908f5156fc1",
"latency_ms": 2036,
"schema_version": "1.3.0",
"cli_version": "0.7.0",
"sources_tried": ["unpaywall"]
}
}部分成功(批量模式 — 部分 DOI 解析失败,退出码反映失败类型):
{
"ok": "partial",
"data": {
"results": [
{ "doi": "10.1038/s41586-021-03819-2", "success": true, "source": "unpaywall", ... },
{
"doi": "10.1234/nonexistent",
"success": false,
"source": null,
"pdf_url": null,
"file": null,
"meta": {},
"sources_tried": ["unpaywall", "semantic_scholar"],
"error": {
"code": "not_found",
"message": "No open-access PDF found",
"retryable": true,
"retry_after_hours": 168,
"reason": "OA availability changes over time; retry after embargo lifts or preprint appears"
}
}
],
"summary": {"total": 2, "succeeded": 1, "failed": 1},
"next": ["paper-fetch 10.1234/nonexistent --out pdfs"]
},
"meta": { ... }
}next 字段是一个建议的后续命令数组:重新调用这些命令将仅重试失败的部分。结合 --idempotency-key 使用,可安全地重试整个批次,而无需重复下载已成功的项目。
失败(参数错误,退出码为 3):
{
"ok": false,
"error": {
"code": "validation_error",
"message": "Provide a DOI or --batch file",
"retryable": false
},
"meta": { ... }
}单项跳过(目标文件已存在,未使用 --overwrite):
{
"doi": "10.1038/s41586-021-03819-2",
"success": true,
"source": "unpaywall",
"pdf_url": "https://...",
"file": "pdfs/Jumper_2021_...pdf",
"skipped": true,
"skip_reason": "file_exists",
"sources_tried": ["unpaywall"]
}幂等性重播(使用相同的 --idempotency-key 重新运行):
返回缓存的响应内容完全一致,但 meta.request_id 和 meta.latency_ms 会更新为当前调用的时间戳,且 meta.replayed_from_idempotency_key 被设置。不会发生任何网络 I/O 操作。
当使用 --format json 时,stderr 每行输出一个 JSON 对象,用于实时状态追踪:
{"event": "session", "request_id": "req_...", "elapsed_ms": 0, "cli_version": "0.6.1", "schema_version": "1.3.0"}
{"event": "start", "request_id": "req_...", "elapsed_ms": 2, "doi": "10.1038/..."}
{"event": "source_try", "request_id": "req_...", "elapsed_ms": 2, "doi": "...", "source": "unpaywall"}
{"event": "source_hit", "request_id": "req_...", "elapsed_ms": 2036, "doi": "...", "source": "unpaywall", "pdf_url": "..."}
{"event": "download_ok", "request_id": "req_...", "elapsed_ms": 4120, "doi": "...", "file": "..."}事件类型包括:session、start、source_try、source_hit、source_miss、source_skip、source_enrich、source_enrich_failed、download_ok、download_error、download_skip、dry_run、not_found。所有事件共享 request_id 和 elapsed_ms,便于协调器将 stderr 的进度与最终的 stdout 响应关联起来。session 事件在每次调用开始时触发一次,早于任何 DOI 处理或网络请求,并携带 cli_version / schema_version,使代理可在不等待最终响应的情况下检测与缓存副本的模式差异。
source_enrich 事件在 Semantic Scholar 被调用以补充缺失的 author / title 信息时触发(前提是其他来源已提供 PDF URL),其 fields 数组列出具体填充的字段。source_enrich_failed 事件在该补充请求失败时触发 — 此时仍使用 Unpaywall 提供的 PDF URL,文件名则回退至 unknown_<year>_…。
当使用 --format text 时,stderr 输出人类可读的文本信息。
| 代码 | 含义 | 可重试类别 |
|---|---|---|
0 | 所有 DOI 已成功解析或预览 | — |
1 | 未解析 — 一个或多个 DOI 无开放获取版本;无传输错误 | 不立即重试(建议在 retry_after_hours 后重试) |
2 | 保留用于认证错误(当前未使用) | — |
3 | 验证错误(参数错误、输入缺失) | 否 |
4 | 传输错误(网络 / 下载 / IO 失败) | 是 |
该分类体系允许协调器对失败情况进行确定性路由:退出码 4 应立即重试,退出码 1 不应立即重试,退出码 3 表示调用方存在错误。
所有可重试的错误均在错误对象中包含 retry_after_hours 提示,以便协调器能准确安排重试时间,无需猜测。
| 代码 | 含义 | 可重试 | retry_after_hours |
|---|---|---|---|
validation_error | 参数错误或输入为空 | 否 | — |
title_resolve_failed | Crossref 对给定的 --title 查询未返回任何结果(建议使用更长或更清晰的标题,或直接提供 DOI) | 否 | — |
not_found | 未找到开放获取的 PDF | 是 | 168(一周时间 — 开放获取内容在禁运期或预印本发布周期内) |
download_network_error | 下载过程中网络失败 | 是 | 1 |
download_not_a_pdf | 响应不是 PDF(而是 HTML 页面) | 否 | — |
download_host_not_allowed | PDF URL 通过 SSRF 安全检查失败(私有 IP / 非 http(s) / 非 80/443 端口 / 被阻止的元数据主机) | 否 | — |
download_size_exceeded | 响应超过 50 MB 限制 | 是 | 24 |
download_io_error | 本地文件系统写入失败 | 是 | 1 |
internal_error | 意外错误 | 否 | — |
标准映射定义在 scripts/fetch.py 中的 RETRY_AFTER_HOURS,并通过 schema.error_codes 暴露。
# 单个 DOI(管道输出为 JSON;终端中显示为文本)
python scripts/fetch.py 10.1038/s41586-020-2649-2
# 单个标题(通过 Crossref 解析为 DOI,然后下载)
python scripts/fetch.py --title "Highly accurate protein structure prediction with AlphaFold"
# 干运行预览(仅解析,不下载)
python scripts/fetch.py 10.1038/s41586-020-2649-2 --dry-run
# 标题 + 干运行 — 预览解析后的 DOI 和候选匹配项
python scripts/fetch.py --title "Attention Is All You Need" --dry-run
# 强制输出 JSON(适用于代理工具,即使在终端中也适用)
python scripts/fetch.py 10.1038/s41586-020-2649-2 --format json
# 人类可读格式,带彩色输出(用于管道)
python scripts/fetch.py 10.1038/s41586-020-2649-2 --format text
# 批量下载,支持安全重试
python scripts/fetch.py --batch dois.txt --out ./papers \
--idempotency-key monday-review-batch
# 从其他工具管道传入 DOI
zot -F ids.json query ... | jq -r '.[].doi' | python scripts/fetch.py --batch -
# 代理发现
python scripts/fetch.py schema --pretty
# 流式模式 — 每个 DOI 解析后逐行输出结果
python scripts/fetch.py --batch dois.txt --stream
# 不需要 UNPAYWALL_EMAIL(跳过 Unpaywall,仍使用其余 4 个来源)
python scripts/fetch.py 10.1038/s41586-020-2649-2| 变量 | 默认值 | 用途 |
|---|---|---|
UNPAYWALL_EMAIL | 未设置 | Unpaywall API 的联系邮箱。可选但推荐。若未设置,将跳过 Unpaywall(其余来源仍可用)。 |
PAPER_FETCH_INSTITUTIONAL | 未设置 | 设置任意值(如 1)以启用 机构模式 — 激活 1 req/s 速率限制和出版商直连回退机制。详见下文。 |
PAPER_FETCH_NO_SCIHUB | 未设置 | 设置任意值以禁用 Sci-Hub 回退(第 7 步)。 |
PAPER_FETCH_SCIHUB_MIRRORS | 未设置 | 优先级顺序的镜像主机名列表,用逗号分隔(例如 sci-hub.ru,sci-hub.st,sci-hub.su)。覆盖内置默认值。 |
许多研究人员可通过所在机构的 IP 地址范围(校园内或通过 VPN)获得合法订阅访问权限。Paper-fetch 可利用这种访问方式,让出版商自身的认证机制(您的 IP 地址、会话 Cookie)决定是否提供 PDF。
两种模式下的主机可达性无差异 —— 公共模式已信任来自开放获取 API(Unpaywall、Semantic Scholar、bioRxiv、PMC)返回的 URL,并对通过 SSRF 防御的 HTTPS 主机进行下载。机构模式新增两点:(1) 出版商直连回退(步骤 6),当所有开放获取源均未命中时,根据 DOI 前缀构造出版商侧的 PDF URL,以便您的机构 IP/Cookie 能够完成授权;(2) 1 req/s 速率限制,防止批量任务导致您的 IP 被限流或封禁。
开启方式: export PAPER_FETCH_INSTITUTIONAL=1
机构模式下的变化:
| 特性 | 公共模式(默认) | 机构模式 |
|---|---|---|
| 主机可达性 | 任何通过 SSRF 防御的公共 HTTPS 主机 | 相同 |
| SSRF 防护 | 强制执行(私有 IP / 非 http(s) / 非 80/443 端口 / 云元数据地址均被阻止) | 强制执行 — 规则相同 |
| 出版商直连回退 | 关闭 | 开启 — DOI 前缀 → 出版商 PDF URL,所有开放获取源失败后的最后手段 |
| 速率限制 | 无 | 1 req/s 令牌桶(所有出站请求) |
meta.auth_mode | "public" | "institutional" |
保持不变的内容:
%PDF 魔数校验与 50 MB 大小限制(防止 HTML 页面或过大响应绕过检测)%PDF 开头,paper-fetch 将自动转至下一个来源。PAPER_FETCH_INSTITUTIONAL 必须由人工操作者在 shell 环境中设置。这是信任边界。当 paper-fetch 未能找到开放获取副本且处于公共模式时,错误信息包中包含 suggest_institutional: true,并提示用户设置环境变量。代理可原样呈现此提示,而非静默失败。
使用条款提醒: 几乎所有出版商的订阅协议都禁止“系统性下载”。1 req/s 速率限制加上现有的按文件幂等性机制,旨在确保个人研究使用在合理范围内。运行多个并行的 paper-fetch 进程,或解除速率限制,可能导致出版商层面的 IP 封禁,影响整个机构。请勿如此操作。
UNPAYWALL_EMAIL,代理将自动继承该值。若缺少邮箱,将降级为使用其余 4 个数据源。%PDF 魔数字节检查以及 50 MB 大小限制均在环境层强制执行,而非由代理请求时决定。代理无法通过传递标志来放宽安全策略——启用机构模式(及其相应的速率限制风险)需通过环境变量由操作者主动配置。--out 路径重复运行时,会跳过已存在的文件(文件名确定:{first_author}_{year}_{journal_abbrev}_{short_title}.pdf;若元数据中无期刊/会议信息,则省略期刊部分)。配合 --idempotency-key 使用,可完全重放原始请求包而无需任何网络 I/O。PAPER_FETCH_INSTITUTIONAL=1 启用,使用调用方自身的订阅资源(如 IP 地址、Cookie 或 EZproxy)。./pdfs/。参见本文档顶部的 步骤 0。当通过 git clone 安装时,代理在每次对话首次调用时会同步执行 git pull --ff-only,并通过 <skill_dir>/.last_update 文件限制更新频率,确保每 24 小时最多更新一次。
强制立即检查更新,请删除 <skill_dir>/.last_update 文件。
已收录 1 个 Skill