paper-fetch について
DOI またはタイトルから論文を特定し、複数のソースを順に試して PDF を取得するコマンドラインスキルです。バッチ処理とエージェント向けの構造化出力にも対応します。
主な機能
- 01DOI またはタイトルで論文を特定
- 02優先順に PDF ソースを検索
- 03複数論文の一括ダウンロード
- 04JSON と進捗イベントの出力
- 05機械可読な CLI スキーマ
活用シーン
- 01DOI から論文 PDF を取得
- 02論文タイトルから PDF を検索
- 03論文リストの一括ダウンロード
- 04構造化エラーに基づく再試行
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ agents365-ai/ paper-fetch --skill paper-fetchインストール後、Claude Code で/paper-fetchを入力して読み込まれたか確認します。
同梱ファイルscripts/cloak_pdf.pyscripts/fetch.py
詳細
paper-fetch
DOI(またはタイトル)を指定して論文の PDF を取得します。優先順位に従って複数のソースを試し、最初に見つかった時点で停止します。
解決の優先順位
- Unpaywall —
https://api.unpaywall.org/v2/{doi}?email=$UNPAYWALL_EMAILを呼び出し、best_oa_location.url_for_pdfを読み取ります(UNPAYWALL_EMAILが設定されていない場合はスキップします)。 - Semantic Scholar —
https://api.semanticscholar.org/graph/v1/paper/DOI:{doi}?fields=openAccessPdf,externalIds - arXiv —
externalIds.ArXivが存在する場合、https://arxiv.org/pdf/{arxiv_id}.pdf - PubMed Central OA — PMCID が存在する場合、
https://www.ncbi.nlm.nih.gov/pmc/articles/{pmcid}/pdf/ - bioRxiv / medRxiv — DOI のプレフィックスが
10.1101の場合、https://api.biorxiv.org/details/{server}/{doi}を呼び出して最新バージョンの PDF URL を取得します。 - 出版社から直接取得(機関モードのみ —
PAPER_FETCH_INSTITUTIONAL=1)— DOI プレフィックスから出版社の PDF テンプレートを適用します(Nature / Science / Wiley / Springer / ACS / PNAS / NEJM / Sage / T&F / Elsevier)。取得を許可するのは呼び出し元自身の購読 IP / Cookie / EZproxy です。許可されていない応答は%PDFチェックに失敗し、ステップ 7 に進みます。 - Sci-Hub ミラー(デフォルトで有効。無効にするには
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)を順に試します。すべて失敗した場合は、プロセスごとに 1 回だけhttps://www.sci-hub.pub/をスクレイピングして新しいミラーを取得します。CAPTCHA / 論文が見つからないページには PDF の iframe がないため、通知なしで次に進みます。 - それ以外の場合 → タイトルと著者を添えて失敗を報告し、ユーザーが ILL で依頼できるようにします。
CloakBrowser によるフォールバック(ダウンロード層、オプトイン — PAPER_FETCH_CLOAK=1)。 これは独立した取得元ではなく、ダウンロードのボトルネックに適用されるため、上記の取得元すべてに適用されます。解決された PDF URL が Cloudflare にブロックされた場合(HTTP 403/429、またはファイルの代わりに「Just a moment…」という HTML のインタースティシャルが返される場合)、オペレーターがオプトインしていれば、cloak_pdf.py コンパニオンを介して CloakBrowser(JS チャレンジを通過できるステルス Chromium)経由で URL を再試行します。返されたバイト列は、同じ %PDF マジックバイトと 50 MB のチェックで再検証されます。成功すると、結果には via: "cloak" が含まれます。デフォルトでは無効で、フェイルクローズドです(CloakBrowser がない場合は通知なしで次に進みます)。エージェントはオプトインできません。下記の CloakBrowser access を参照してください。
タイトルだけが指定された場合は、--title "<title>" で直接渡します。解決チェーンは次のとおりです。
- Crossref
query.title— 優先。主要なジャーナルや学会の DOI を網羅します。 - Semantic Scholar
/paper/search/match— Crossref の最上位候補の信頼度が低い(match_score < 40)場合、または次点候補との差が< 3の場合のフォールバックです。特に重要なのは、S2 が arXiv のみのプレプリント(Crossref DOI なし)も扱うことです。S2 で arXiv ID のみを持つ論文が見つかった場合は、正規の10.48550/arXiv.<id>を生成し、ダウンロードチェーンを統一します。 - Crossref の最有力候補(信頼度低) — 両方の解決サービスでうまく解決できなかった場合にのみ使用します。結果のエンベロープでは、
meta.title_resolution.low_confidence: trueとlow_confidence_reason(score_below_threshold/ambiguous_runner_up)が設定されます。これにより、エージェントは処理を中断するか、--dry-runで確認できます。
いずれの場合も、解決された DOI、採用された解決サービス、完全な resolvers_tried リスト、および最上位候補との一致状況が meta.title_resolution に表示されます。
semanticscholar-skill が登録されている場合は、タイトルから DOI への解決に、より充実した前段階の処理として利用できます。単に DOI が必要なだけでなく、関連度のランキング、スニペット検索、引用のコンテキストも必要な場合に便利です。エージェントは、このスキルの match_title() を使って externalIds.DOI を読み取る Python スクリプトを作成し、その後 paper-fetch <doi> を実行します。結果に ArXiv ID のみが含まれ、DOI がない場合は、10.48550/arXiv.<ArXiv> を生成して paper-fetch に渡します。
DOI だけが必要な場合は、--title が単一コマンドで実行する方法です。paper-fetch に組み込まれた Crossref → S2 チェーンが、ほとんどのケースに対応します。
使い方
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 # machine-readable self-description
フラグ
通常の使用時にエージェントが指定するフラグは以下のとおりです。完全な仕様(--dry-run、--pretty、--stream、--overwrite、--timeout、--version、パラメーターの型、終了コードの対応関係を含む)については、python scripts/fetch.py schema を実行してください(機械可読形式で、schema_version を使って変更を検出します)。
| フラグ | デフォルト | 説明 |
|---|---|---|
doi | — | 取得する DOI(位置引数)。標準入力から DOI を 1 つ読み込む場合は - を使用します |
--title TITLE | — | 論文のタイトル。ダウンロード前に Crossref を使って DOI に解決します。位置引数の DOI/--batch とは併用できません |
--batch FILE | — | 一括ダウンロード用の、1 行に DOI が 1 つ記載されたファイル。標準入力から読み込む場合は - を使用します |
--out DIR | pdfs | 出力ディレクトリ |
--format | auto | エージェント向けの json、人間向けの text。自動検出されます。stdout が TTY でない場合は json、TTY の場合は text です |
--idempotency-key KEY | — | 安全な再試行のためのキー。同じキーで再実行すると、ネットワーク I/O を行わずに <out>/.paper-fetch-idem/ から元のエンベロープを再生します |
エージェントによる情報取得:schema サブコマンド
python scripts/fetch.py schema
ネットワークを使用せず、CLI の完全な機械可読形式の説明を stdout に出力します。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.9.0",
"cli_version": "0.13.1",
"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 は発生しません。
stderr の進捗状況(NDJSON)
--format json の場合、稼働状況を示すため、stderr に 1 行につき 1 つの JSON オブジェクトが出力されます。
{"event": "session", "request_id": "req_...", "elapsed_ms": 0, "cli_version": "0.13.1", "schema_version": "1.9.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、resolve_error。すべてのイベントに request_id と elapsed_ms が含まれるため、オーケストレーターは stderr の進捗状況と最後の stdout エンベロープを関連づけられます。session イベントは呼び出しごとに、DOI の処理やネットワーク I/O の前に一度発生します。このイベントには cli_version/schema_version が含まれており、最後のエンベロープを待たずに、キャッシュしたコピーと照合してスキーマの変更を検出できます。
source_enrich は、別のソースから PDF URL がすでに取得されており、不足している author/title を補完する目的だけで Semantic Scholar が呼び出されたときに発生します。fields 配列には、補完されたフィールドのみが列挙されます。その補完呼び出しが失敗すると、source_enrich_failed が発生します。この場合も Unpaywall の PDF URL は使用され、ファイル名は unknown_<year>_… にフォールバックします。
--format text の場合、stderr には人間が読みやすい文章が出力されます。
終了コード
| コード | 意味 | 再試行可能な分類 |
|---|---|---|
0 | すべての DOI を解決/プレビューしました | — |
1 | 未解決 — 1 件以上の DOI に OA 版がありませんでした。通信障害はありません | 今は再試行しない(retry_after_hours の後に再試行) |
2 | 認証エラー用に予約済み(現在は未使用) | — |
3 | 検証エラー(引数が不正、入力がありません) | いいえ |
4 | 通信エラー(ネットワーク/ダウンロード/I/O の障害) | はい |
この分類により、オーケストレーターは失敗を確実に振り分けられます。終了コード 4 はすぐに再試行する価値があり、終了コード 1 はそうではありません。終了コード 3 は呼び出し側のバグです。
JSON のエラーコード
再試行可能なエラーにはすべて、エラーオブジェクトに retry_after_hours のヒントが含まれています。これにより、オーケストレーターは推測せずに再試行をスケジュールできます。
| コード | 意味 | 再試行可能 | retry_after_hours |
|---|---|---|---|
validation_error | 引数が不正、または入力が空です | いいえ | — |
title_resolve_failed | 指定された --title クエリに対して Crossref が項目を返しませんでした(より長く明確なタイトルを試すか、DOI を直接渡してください) | いいえ | — |
not_found | オープンアクセスの PDF が見つかりませんでした | はい | 168(1 週間 — OA 版が公開されるまでの期間は、エンバーゴ/プレプリントのタイムスケールに相当します) |
resolve_network_error | メタデータリゾルバーで通信エラー(タイムアウト/5xx/403)が発生しました。OA の提供状況は不明なため、not_found として扱わずに再試行してください。終了コード 4 に対応します。 | はい | 1 |
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 に公開されています。
例
# Single DOI (JSON output when piped; text when in a terminal)
python scripts/fetch.py 10.1038/s41586-020-2649-2
# Single title (resolved to DOI via Crossref, then downloaded)
python scripts/fetch.py --title "Highly accurate protein structure prediction with AlphaFold"
# Dry-run preview (resolve without downloading)
python scripts/fetch.py 10.1038/s41586-020-2649-2 --dry-run
# Title + dry-run — preview the resolved DOI and candidate matches
python scripts/fetch.py --title "Attention Is All You Need" --dry-run
# Force JSON (for agents even inside a terminal)
python scripts/fetch.py 10.1038/s41586-020-2649-2 --format json
# Human-readable with pretty colors in a pipeline
python scripts/fetch.py 10.1038/s41586-020-2649-2 --format text
# Batch download, safely retriable
python scripts/fetch.py --batch dois.txt --out ./papers \
--idempotency-key monday-review-batch
# Pipe DOIs from another tool
zot -F ids.json query ... | jq -r '.[].doi' | python scripts/fetch.py --batch -
# Agent discovery
python scripts/fetch.py schema --pretty
# Streaming mode — one result per line as each DOI resolves
python scripts/fetch.py --batch dois.txt --stream
# Works without UNPAYWALL_EMAIL (skips Unpaywall, uses remaining 4 sources)
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)。組み込みのデフォルトを上書きします。 |
PAPER_FETCH_CLOAK | 未設定 | 任意の値を設定すると、CloakBrowser フォールバックが有効になります — Cloudflare にブロックされた PDF(HTTP 403/429 または PDF 以外のインタースティシャル)を、ステルス Chromium 経由で再試行します。以下の CloakBrowser アクセス を参照してください。 |
CLOAKBROWSER_PYTHON | 自動 | import cloakbrowser を実行できる Python のパスです。cloak フォールバックで使用されます。自動検出の順序:この変数 → ~/github/CloakBrowser/.venv/bin/python → 現在のインタープリター。 |
PAPER_FETCH_CLOAK_HEADED | 未設定 | 任意の値を設定すると、ヘッドレスではなく ヘッドあり(画面に表示される)ブラウザーを起動します。より厳しい Cloudflare チャレンジ(例:science.org)はヘッドレスモードでは突破できず、実際のウィンドウでのみ解除されます。cloak フォールバックが HTTP 403/「Just a moment…」を繰り返し返す場合に設定してください。ディスプレイが必要です。 |
CloakBrowser によるアクセス(オプトイン)
一部の出版社(例:science.org)は Cloudflare の背後にあり、通常の HTTP クライアントには PDF ではなく、403/429 または「Just a moment…」という JavaScript チャレンジページを返します。そのため、ブラウザーからは正規にアクセスできる URL であっても、デフォルトの urllib によるダウンロードでは取得できません。CloakBrowser は、こうしたチャレンジを突破するステルス Chromium です。このスキルは、cloakFetch のアプローチを採用しています。
オプトイン: export PAPER_FETCH_CLOAK=1 を設定します(さらに、cloakbrowser をインポートできる Python が必要です。CLOAKBROWSER_PYTHON を参照してください)。
仕組み: フォールバックは新しいソースとしてではなく、ダウンロード層に実装されています。そのため、解決されたあらゆる URL(Unpaywall、出版社の直接リンク、Sci-Hub など)に適用されます。Cloudflare にブロックされると、fetch.py は解決済みの Python を介して補助スクリプト cloak_pdf.py を起動します。CloakBrowser は PDF ホストのオリジンを読み込んで JavaScript チャレンジを解決し、その後、ページ内の fetch() で PDF を取得します(これにより、ブラウザー固有の実際のフィンガープリントと cf_clearance Cookie がリクエストに付与されます)。そして、取得したバイト列を標準出力に返します。fetch.py 自体は標準ライブラリのみを使用し、cloakbrowser をインポートすることはありません。
ヘッドレスと画面表示あり: 補助スクリプトはデフォルトでヘッドレスモードで実行されます。一部のチャレンジ(例:science.org)はヘッドレス Chromium を突破し、画面に「Just a moment…」と表示されたまま進まないため、PAPER_FETCH_CLOAK_HEADED=1 を設定してウィンドウを表示すると解決できます。確認済み:通常のクライアントには 403 を返す www.science.org/doi/pdf/… の PDF を、画面表示ありの CloakBrowser フォールバックで正常にダウンロードできます。
同一オリジンのみ: ページ内の fetch は同一オリジンで実行されるため、解決された URL がブロック対象ホスト上の PDF への直接リンクである場合(例:www.science.org/doi/pdf/…)にフォールバックが機能します。別オリジンへリダイレクトする URL(例:doi.org/… のみのリンク)や、チャレンジが解決されない古いホストでは、フェイルクローズで失敗し、次のソースにフォールスルーします。
変わらない点:
- 返されたバイト列は、同じ
%PDFマジックバイト検証と 50 MB のサイズ上限で再検証されます。ブラウザーが起動される前に、引き続き SSRF 対策によって URL が制限されます。 - CAPTCHA の解決には対応しません。 CloakBrowser が突破できるのは自動化された JavaScript チャレンジであり、対話型のチャレンジではありません。対話型の Turnstile は引き続きフェイルクローズし、次のソースにフォールスルーします。
- フェイルクローズです。
cloakbrowserをインポートできる Python がない、補助スクリプトがない、またはその他のエラーが発生した場合は、何も表示せず次のソースにフォールスルーします。ブロックされた取得が成功したとエージェントに伝わることはありません。 - エージェント自身がオプトインすることはできません。
PAPER_FETCH_CLOAKは人間の運用者が設定する必要があります。機関向けモードと同じ信頼境界です。 - 成功時は、結果オブジェクトに
via: "cloak"が含まれるため、オーケストレーターはフォールバックが使用されたことを確認できます。
コスト: フォールバックが実行されると、実際のブラウザーが起動します(約 20~40 秒、初回ダウンロード時には Chromium が約 200 MB)。通常のダウンロードがブロックされた後にのみ実行されるため、正常に取得できる場合の経路には影響しません。
機関アクセス(オプトイン)
多くの研究者は、所属機関の IP 範囲(キャンパス内または VPN 経由)を通じて、正規のサブスクリプションアクセスを利用できます。Paper-fetch は、PDF を提供するかどうかの判断を出版社独自の認証(お使いの IP、セッション Cookie)に委ねることで、このアクセスを利用できます。
モードによってホストへの到達可能性は変わりません。パブリックモードでも、OA API(Unpaywall、Semantic Scholar、bioRxiv、PMC)が返す URL はすでに信頼され、SSRF 防御を通過する HTTPS ホストであればどこからでも取得できます。機関モードでは、次の 2 つが追加されます。(1)OA ソースがすべて見つからなかった場合に DOI プレフィックスを使って出版社側の PDF URL を構築する出版社直接フォールバック(上記の手順 6)。これにより、所属機関の IP/Cookie で取得を認証できます。(2)バッチジョブによって IP がスロットリングまたは「組織的なダウンロード」を理由にブロックされないようにする、1 リクエスト/秒のレートリミッター。
オプトインするには: export PAPER_FETCH_INSTITUTIONAL=1
機関モードでの変更点:
| 項目 | パブリック(デフォルト) | 機関 |
|---|---|---|
| ホストへの到達可能性 | SSRF 防御を通過する、任意のパブリック HTTPS ホスト | 同じ |
| SSRF 防御 | 適用(プライベート IP/非 HTTP(S)/80、443 以外/クラウドメタデータをすべてブロック) | 適用 — 同じルール |
| 出版社直接フォールバック | 無効 | 有効 — DOI プレフィックスから出版社の PDF URL を構築し、OA ソースがすべて見つからなかった場合の最終手段として使用 |
| レート制限 | なし | 1 リクエスト/秒のトークンバケット(すべての送信リクエスト) |
meta.auth_mode | "public" | "institutional" |
変わらない点:
%PDFのマジックバイトチェックと 50 MB のサイズ上限(HTML のランディングページや過大なレスポンスがすり抜けるのを防ぎます)- CAPTCHA は一切解きません。出版社がチャレンジを表示した場合、レスポンスは
%PDFで始まらないため、paper-fetch は次のソースに進みます。 - 機関モード自体はブラウザー自動化、Playwright、ステルス機能を使用しません。出版社の直接 URL に対する通常の HTTP 取得です。(ブラウザー自動化は別のオプトインである CloakBrowser フォールバックで、上記のとおり
PAPER_FETCH_CLOAKで制御されます。) - エージェントが独自にオプトインすることはできません。
PAPER_FETCH_INSTITUTIONALは、シェル環境で人間のオペレーターが設定する必要があります。これが信頼境界です。
パブリックモードで OA 版を見つけられない場合、エラーエンベロープには suggest_institutional: true と、ユーザーに環境変数の設定を促すヒントが含まれます。エージェントは、何も知らせずに失敗するのではなく、この情報をそのまま表示できます。
利用規約に関する注意: ほぼすべての出版社のサブスクリプションで「組織的なダウンロード」が禁止されています。1 リクエスト/秒のレート制限と既存のファイル単位の冪等性により、個人の研究用途が許容範囲内に収まるように設計されています。paper-fetch プロセスを多数並列で実行したり、レート制限を解除したりすると、出版社が機関全体に影響する IP ブロックを実施するおそれがあります。やめてください。
メモ
- 認証は委任されます。 エージェントがログイン用サブコマンドを実行することはありません。人間またはオーケストレーターが環境に
UNPAYWALL_EMAILを設定し、エージェントはそれを継承します。メールアドレスが設定されていない場合は、残りの 4 つのソースにフォールバックします。 - 信頼の方向性は決まっています。 CLI 引数はエントリーポイントで一度だけ検証されます。SSRF 防御、
%PDFのマジックバイトチェック、50 MB のサイズ上限は、エージェントのリクエスト側ではなく環境レイヤーで適用されます。エージェントがフラグを渡して安全性を緩めることはできません。機関モード(およびそのレート制限に伴うリスク特性)へのオプトインは、環境変数を使ってオペレーターが行います。 - ダウンロードは自然に冪等になります。 同じ
--outを指定して再実行すると、すでに存在するファイルはスキップされます(決定的なファイル名:{first_author}_{year}_{journal_abbrev}_{short_title}.pdf。メタデータにジャーナル/開催地がない場合、ジャーナル部分は省略されます)。--idempotency-keyを併用すると、ネットワーク I/O を一切行わずに、同一のエンベロープを再現できます。 - デフォルトの出力ディレクトリ:
./pdfs/。