find-papers

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Find Papers

查找论文

Key-free scholarly search. Produces verified paper metadata — titles, authors, year, venue, DOIs/arXiv ids, citation counts, open-access PDF links — from four free APIs, with the venue-name translation layer that makes cross-API search actually work. Serves the literature-review and related-work stages; feeds
fetch-paper
(full text) and
verify-citations
(bibliography gate).
无需密钥的学术搜索工具。可从四个免费API获取经过验证的论文元数据——标题、作者、年份、会议、DOI/arXiv编号、引用次数、开放获取PDF链接,其内置的会议名称转换层让跨API搜索真正可用。适用于文献综述和相关研究阶段;可为
fetch-paper
(获取全文)和
verify-citations
(参考文献校验)提供数据支持。

When to use

使用场景

  • "Find papers about X" / "survey related work on X"
  • "List everything published at <venue> <year>"
  • "What are the most-cited papers on X?" / "look up this DOI"
  • "What's new on arXiv about X?"
  • Another skill (literature-review, draft-related-work, study-exemplars, select-venue) needs real papers to ground its output
  • “查找关于X的论文” / “调研X的相关研究”
  • “列出<会议> <年份>发表的所有论文”
  • “关于X的高引用论文有哪些?” / “查询这个DOI的信息”
  • “arXiv上关于X的最新内容是什么?”
  • 其他技能(文献综述、撰写相关研究、研究范例选择、会议选择)需要真实论文作为输出基础

Inputs

输入参数

  • A topic, author, venue, year, DOI, or arXiv id (any combination)
  • CONTACT_EMAIL
    env var — required by every script (polite-pool identity); scripts prompt interactively if unset, or exit 1 with instructions
  • Optional:
    S2_API_KEY
    env var for reliable Semantic Scholar access
  • Optional: a venue profile
    venues/conferences/<id>.yml
    (schema in
    venues/schema.yml
    ) — its
    aliases:
    block is the authoritative venue-name mapping
  • 主题、作者、会议、年份、DOI或arXiv编号(任意组合)
  • CONTACT_EMAIL
    环境变量——所有脚本都需要该参数(用于标识请求来源);若未设置,脚本会交互式提示,或退出并给出设置说明
  • 可选:
    S2_API_KEY
    环境变量,用于稳定访问Semantic Scholar
  • 可选:会议配置文件
    venues/conferences/<id>.yml
    (配置格式参考
    venues/schema.yml
    )——其
    aliases:
    块是权威的会议名称映射表

Provider cheat sheet

服务商速查表

NeedProviderScript
Enumerate a CS venue-year ("all KDD 2025 papers")DBLP
dblp_search.py
DOI-backed metadata, publisher filters, date rangesCrossref
crossref_search.py
Abstracts, citation counts, OA PDF links, topic searchSemantic Scholar
s2_search.py
Preprints, newest work, full-text HTML linksarXiv
arxiv_search.py
Fan out across the indexes, union, fail over, report coverageall four
resolve_papers.py
Expand a saturated seed set along citations (anchors, competitors, infra)OpenAlex + Crossref
citation_graph.py
Pick the canonical instance of a named title (version drift / collision)any (post-process)
resolve_canonical.py
For any survey or venue enumeration you will present as reasonably complete, lead with
resolve_papers.py
: it queries several indexes, unions the hits deduped by any stable id (DOI, arXiv, DBLP, or anthology — a DOI is not required), keeps going when one index is rate-limited or down, and stamps the run COMPLETE or PARTIAL. Single-provider scripts are for one specific thing (a DOI lookup, the canonical DBLP toc, fresh arXiv preprints) — never treat one provider's silence as evidence a paper does not exist.
Details, rate limits, and the fallback matrix: references/api-notes.md.
需求服务商脚本
枚举计算机领域某会议某年份的论文(如“所有KDD 2025论文”)DBLP
dblp_search.py
基于DOI的元数据、出版商筛选、日期范围过滤Crossref
crossref_search.py
摘要、引用次数、开放获取PDF链接、主题搜索Semantic Scholar
s2_search.py
预印本、最新研究、全文HTML链接arXiv
arxiv_search.py
跨索引扩散、合并结果、故障转移、报告覆盖范围四个服务商全部
resolve_papers.py
通过引用关系扩展已饱和的种子论文集(基础研究、竞品研究、基础设施研究)OpenAlex + Crossref
citation_graph.py
确定同名标题的标准版本(版本差异/标题冲突)任意服务商(后处理)
resolve_canonical.py
对于任何需要保证完整性的综述或会议论文枚举任务,优先使用
resolve_papers.py
:它会查询多个索引,通过任意稳定ID(DOI、arXiv、DBLP或ACL论文集编号——不强制要求DOI)去重合并结果,当某个索引被限流或宕机时自动切换,最后标记运行状态为COMPLETE(完成)或PARTIAL(部分完成)。单一服务商脚本仅适用于特定场景(如DOI查询、DBLP标准目录、arXiv最新预印本)——绝不能因某一服务商未返回结果就判定论文不存在。
详细信息、限流规则和故障转移矩阵:references/api-notes.md

Process

操作流程

  1. Resolve the venue identity first (skip for pure topic searches). Venue names differ across every API and a wrong string silently returns zero results. Resolution order:
    • venues/conferences/<id>.yml
      aliases:
      block (dblp_key, s2_venue, crossref_container), if a profile exists;
    • otherwise references/venue-aliases.md (~20 top venues, with per-venue gotchas: SIGSPATIAL =
      conf/gis
      , VLDB =
      journals/pvldb
      by volume, post-2023 SIGMOD = PACMMOD, NeurIPS toc rename, ICLR/ICML absent from Crossref...);
    • otherwise discover live:
      python3 scripts/dblp_search.py --find-venue "<name>"
      , then follow "Adding or re-verifying a venue" in venue-aliases.md. When a venue profile supplied any fact the user will rely on (counts, coverage claims, deadlines mentioned alongside), re-verify against the profile's live
      cfp_url
      and the live APIs — profiles and alias tables go stale; the file itself says so.
  2. Ensure
    CONTACT_EMAIL
    is set.
    Ask the user for their email if needed:
    export CONTACT_EMAIL=you@university.edu
    . Never invent one.
  3. Run the right script(s) from the skill directory (all are Python 3 stdlib-only; every one supports
    --help
    ,
    --json
    ,
    --no-cache
    ):
    bash
    # Provider-agnostic fan-out (PREFER for surveys / "is this complete?"):
    # unions DBLP+Crossref+S2+arXiv, fails over on any outage, prints a
    # COMPLETE/PARTIAL coverage banner.
    python3 scripts/resolve_papers.py --query "trajectory similarity learning"
    python3 scripts/resolve_papers.py --venue-year --year 2025 \
        --dblp-key conf/gis --s2-venue "SIGSPATIAL/GIS" \
        --crossref-container "Advances in Geographic Information Systems"
    
    # Enumerate a venue-year (conference / journal volume / multi-volume toc)
    python3 scripts/dblp_search.py --key conf/gis --year 2025
    python3 scripts/dblp_search.py --key journals/pvldb --volume 18
    python3 scripts/dblp_search.py --toc db/conf/acl/acl2024-1.bht
    
    # Topic search with citation counts + OA PDFs (add --year to narrow)
    python3 scripts/s2_search.py --query "trajectory similarity learning" --limit 10
    
    # Venue search on S2 (exact venue string from the alias table!)
    python3 scripts/s2_search.py --venue "SIGSPATIAL/GIS" --year 2025
    
    # DOI-backed venue/date/type filtering
    python3 scripts/crossref_search.py --container SIGSPATIAL \
        --from-date 2025-01-01 --until-date 2025-12-31 --type proceedings-article
    
    # Fresh preprints
    python3 scripts/arxiv_search.py \
        --query 'cat:cs.DB AND abs:"spatial join"' --sort submittedDate
    
    # Expand a saturated seed set along the citation graph (see step 4)
    python3 scripts/citation_graph.py \
        --seed DOI:10.14778/3551793.3551844 --seed "<another on-topic title>" \
        --direction both --top 25
    Scripts enforce politeness themselves (1 req/s per host — 2s DBLP, 3s arXiv — UA with contact email, exponential 429 backoff, 24h cache under
    .cache/find-papers/
    , single-page fetches). Do not loop a script to paginate around its caps; refine the query instead.
  4. Cross-check, expand along the citation graph, and enrich. Dedupe by any stable id, not DOI alone (DOI → arXiv id → DBLP key → ACL-anthology id), falling back to normalized title+year —
    resolve_papers.py
    does this union for you. A missing DOI is not a reason to drop a paper: real ML-proceedings work (ICLR/ICML/NeurIPS) and modern EDBT mint no Crossref DOI, yet their arXiv/DBLP/anthology ids are perfectly citable. Distinguish two failure modes that look alike but must be handled oppositely: no verifiable identifier anywhere → drop as unusable; a provider was down / rate-limited → that is not a "0 results" — re-query the paper on the other indexes and, if still unconfirmed, keep it flagged
    unresolved-keep
    rather than discarding it for a single-index miss. DBLP gives the authoritative venue list but no abstracts or citations — enrich interesting hits one at a time:
    python3 scripts/s2_search.py --paper DOI:10.1145/3589132.3625571 --fields title,abstract,tldr,citationCount,openAccessPdf
    . An arXiv hit is not an accepted paper — confirm venue acceptance via DBLP/Crossref before presenting it as one.
    Mandatory once the keyword/venue pass saturates (a fresh query mostly returns papers you already have): topic search systematically misses three things that live one citation edge away — foundational/seminal anchors the sub-area is built on, direct competitors that share citers but not your keywords, and shared-infrastructure deps every paper cites but none names in a topic query. These are reached by edges, not words, and skipping this stage is the single largest driver of low recall. Take the top on-topic seeds and run the citation-graph expansion:
    bash
    python3 scripts/citation_graph.py --seed DOI:<seed1> --seed "<seed2 title>" \
        --direction both --top 25         # refs=anchors/infra, citedby=competitors
    It pulls both edge directions (references-of + cited-by) from OpenAlex (Crossref
    reference
    array as the key-free references fallback) and re-ranks neighbors by co-citation degree — how many seeds touch each — so high-degree hubs the keyword pass can't see float up. Then:
    • Foundational/seminal anchor sweep, per topical cluster: run
      --direction refs
      , read the top by global citations, and confirm every cluster has its obvious foundation (base model, canonical dataset, founding method) present — a recall hole if it doesn't.
    • Cover the niche, not just the canon: mine the brief's distinctive mechanism noun-phrases (the specific named technique/component/loss, not the generic topic words) into narrow targeted queries; feed new on-topic hits back in as seeds and re-expand.
    Every graph neighbor is a candidate, not a result — confirm venue/acceptance before presenting it, and surface why it surfaced (seed-degree, edges) so the user decides scope. Method and endpoints: references/citation-graph-expansion.md.
  5. Resolve to the canonical instance — not just a match. When the user named a specific paper (vs. a topic sweep), a relevance-ranked search will happily return an adjacent instance that passes a bare existence check but is the wrong citation: an older edition when a community-canonical successor exists (version/edition drift), or a different paper that shares a first author and a near-duplicate title (title collision). Before you present a single "the" paper, run the candidates through the guard:
    bash
    # pipe a title search straight in (works with any search script's --json)
    python3 scripts/s2_search.py --query "<the named title>" --json \
        | python3 scripts/resolve_canonical.py --stdin --title "<the named title>"
    It clusters near-duplicate titles, flags
    [VERSION DRIFT]
    and
    [TITLE COLLISION]
    , and marks a
    PREFERRED
    pick (latest canonical version for drift; highest impact / earliest seminal year for collisions) with a one-line
    CHOOSE:
    note. The PREFERRED pick is a suggestion — surface the siblings and let the user choose deliberately. Never silently collapse the cluster to one.
    When sibling records share a stable id but differ in surface title, the resolver emits
    title_variants
    (all distinct titles seen for that one work). Match and dedupe on the stable id first, titles second — an alternate title for the same DOI/eprint is an alias, not a different paper, and must never be scored as a miss. Once you pick the instance, overwrite the entry's title field with the canonical string the API returned for that exact id, not a recalled or hand-typed title, and keep the variants in a note so the chosen form is auditable. This is what stops a bib and a corpus from drifting to two different titles for the same work.
  6. Hand off.
    • Full text needed →
      fetch-paper
      skill (Unpaywall/arXiv/ACM-OA, fetch on demand, processed transiently).
    • Results entering a bibliography or any written claim →
      verify-citations
      skill. Never emit a citation this skill did not actually retrieve.
  1. 先解析会议身份(纯主题搜索可跳过)。不同API的会议名称各不相同,错误的名称会直接返回空结果。解析优先级:
    • 若存在会议配置文件
      venues/conferences/<id>.yml
      ,则使用其
      aliases:
      块中的映射(dblp_key、s2_venue、crossref_container);
    • 否则参考references/venue-aliases.md(约20个顶级会议,包含各会议的注意事项:如SIGSPATIAL对应
      conf/gis
      ,VLDB按卷对应
      journals/pvldb
      ,2023年后的SIGMOD对应PACMMOD,NeurIPS目录名称变更,ICLR/ICML未收录于Crossref等);
    • 若以上都没有,可实时发现:
      python3 scripts/dblp_search.py --find-venue "<会议名称>"
      ,然后按照venue-aliases.md中的“添加或重新验证会议”步骤操作。 当用户依赖会议配置文件中的任何信息(如论文数量、覆盖范围声明、截止日期)时,需通过配置文件中的
      cfp_url
      和实时API重新验证——配置文件和别名表会过期,文件本身也有相关提示。
  2. 确保已设置
    CONTACT_EMAIL
    。若未设置,可询问用户获取邮箱:
    export CONTACT_EMAIL=you@university.edu
    。绝不能伪造邮箱。
  3. 从技能目录运行合适的脚本(所有脚本均基于Python 3标准库;每个脚本都支持
    --help
    --json
    --no-cache
    参数):
    bash
    # 服务商无关的扩散搜索(推荐用于综述/“是否完整”验证):
    # 合并DBLP+Crossref+S2+arXiv的结果,遇故障自动切换,输出COMPLETE/PARTIAL覆盖范围标识。
    python3 scripts/resolve_papers.py --query "trajectory similarity learning"
    python3 scripts/resolve_papers.py --venue-year --year 2025 \
        --dblp-key conf/gis --s2-venue "SIGSPATIAL/GIS" \
        --crossref-container "Advances in Geographic Information Systems"
    
    # 枚举某会议某年份的论文(会议/期刊卷/多卷目录)
    python3 scripts/dblp_search.py --key conf/gis --year 2025
    python3 scripts/dblp_search.py --key journals/pvldb --volume 18
    python3 scripts/dblp_search.py --toc db/conf/acl/acl2024-1.bht
    
    # 带引用次数和开放获取PDF的主题搜索(可添加--year参数缩小范围)
    python3 scripts/s2_search.py --query "trajectory similarity learning" --limit 10
    
    # 在S2中搜索特定会议(需使用别名表中的准确会议名称!)
    python3 scripts/s2_search.py --venue "SIGSPATIAL/GIS" --year 2025
    
    # 基于DOI的会议/日期/类型筛选
    python3 scripts/crossref_search.py --container SIGSPATIAL \
        --from-date 2025-01-01 --until-date 2025-12-31 --type proceedings-article
    
    # 获取最新预印本
    python3 scripts/arxiv_search.py \
        --query 'cat:cs.DB AND abs:"spatial join"' --sort submittedDate
    
    # 通过引用关系扩展已饱和的种子论文集(见步骤4)
    python3 scripts/citation_graph.py \
        --seed DOI:10.14778/3551793.3551844 --seed "<另一篇相关主题的标题>" \
        --direction both --top 25
    脚本会自动遵守请求规范(每个主机每秒1次请求——DBLP为2秒,arXiv为3秒——请求头包含联系邮箱,遇429限流时指数退避重试,缓存有效期24小时,存储在
    .cache/find-papers/
    下,单次请求获取单页数据)。不要通过循环脚本来绕过分页限制,应优化查询条件。
  4. 交叉校验、通过引用关系扩展、丰富数据。通过任意稳定ID去重,而非仅依赖DOI(优先级:DOI → arXiv编号 → DBLP键 → ACL论文集编号),若没有稳定ID则通过标准化标题+年份去重——
    resolve_papers.py
    会自动完成合并操作。缺少DOI绝不是丢弃论文的理由:真实的机器学习会议论文(如ICLR/ICML/NeurIPS)和现代EDBT论文没有Crossref DOI,但它们的arXiv/DBLP/论文集编号完全可用于引用。需区分两种看似相似但处理方式相反的失败情况:无任何可验证标识符 → 标记为不可用并丢弃;服务商宕机/被限流 → 这不是“无结果”——需在其他索引中重新查询该论文,若仍未确认则标记为
    unresolved-keep
    (未解析但保留),而非因单一索引未命中就丢弃。 DBLP提供权威的会议列表,但没有摘要和引用数据——可单独为感兴趣的结果补充信息:
    python3 scripts/s2_search.py --paper DOI:10.1145/3589132.3625571 --fields title,abstract,tldr,citationCount,openAccessPdf
    。arXiv上的论文不代表已被会议录用——需通过DBLP/Crossref确认录用状态后再呈现给用户。
    当关键词/会议搜索达到饱和后(新查询返回的大多是已有的论文),必须执行以下步骤:主题搜索会系统性遗漏三类仅通过引用关系才能找到的内容——子领域的基础/开创性论文、共享引用但不包含关键词的直接竞品论文、所有论文都引用但未在主题查询中提及的基础设施依赖论文。这些内容只能通过引用关系发现,跳过此步骤是导致召回率低的主要原因。选取最相关的种子论文,运行引用关系扩展脚本:
    bash
    python3 scripts/citation_graph.py --seed DOI:<种子论文1> --seed "<种子论文2标题>" \
        --direction both --top 25         # refs=基础/基础设施研究,citedby=竞品研究
    该脚本从OpenAlex获取双向引用关系(参考文献+被引用文献,无密钥时使用Crossref的
    reference
    数组作为备选),并通过共引度(被多少种子论文引用)对邻居论文重新排序——这样主题搜索无法发现的高影响力核心论文会被优先展示。然后:
    • 基础/开创性论文排查:针对每个主题集群,运行
      --direction refs
      ,按全局引用次数排序查看顶部论文,确认每个集群都包含其核心基础研究(如基础模型、标准数据集、开创性方法)——若缺失则说明存在召回漏洞。
    • 覆盖细分领域,而非仅关注经典论文:提取研究中的独特机制名词短语(特定的命名技术/组件/损失函数,而非通用主题词),构建精准的窄范围查询;将新找到的相关论文作为种子重新扩展。
    所有引用关系中的邻居论文都是候选结果,而非最终结果——呈现前需确认其会议/录用状态,并说明其被发现的原因(种子论文关联度、引用关系),由用户决定是否纳入范围。方法和端点详情:references/citation-graph-expansion.md
  5. 确定标准版本——而非任意匹配结果。当用户指定某篇特定论文(而非主题搜索)时,基于相关性排序的搜索可能会返回一个满足基本存在检查但引用错误的相邻结果:如存在社区公认的新版本时返回旧版(版本差异),或返回同名作者且标题近似的另一篇论文(标题冲突)。在呈现“唯一”论文前,需将候选结果通过校验脚本处理:
    bash
    # 将标题搜索结果直接传入(可与任何搜索脚本的--json参数配合使用)
    python3 scripts/s2_search.py --query "<指定的标题>" --json \
        | python3 scripts/resolve_canonical.py --stdin --title "<指定的标题>"
    该脚本会聚类近似标题,标记
    [VERSION DRIFT]
    (版本差异)和
    [TITLE COLLISION]
    (标题冲突),并标记一个
    PREFERRED
    (推荐)版本(版本差异时选最新标准版本;标题冲突时选影响力最高/最早的开创性版本),同时给出一行
    CHOOSE:
    提示。推荐版本仅为建议——需展示所有候选版本,由用户自主选择。绝不能在未标记的情况下自动合并为单一结果。
    当不同记录共享同一稳定ID但标题不同时,解析器会输出
    title_variants
    (该论文的所有不同标题)。优先通过稳定ID匹配和去重,其次才是标题——同一DOI/预印本的不同标题是别名,而非不同论文,绝不能将其视为匹配失败。选定版本后,用该稳定ID对应的API返回的标准标题覆盖条目标题字段,而非手动输入或回忆的标题,并将其他变体保存为备注,以便所选标题可追溯。这能避免参考文献和语料库中同一论文出现不同标题的问题。
  6. 交付结果
    • 需要全文 → 使用
      fetch-paper
      技能(Unpaywall/arXiv/ACM开放获取,按需获取,临时处理)。
    • 结果将用于参考文献或书面声明 → 使用
      verify-citations
      技能。绝不能输出未被本技能实际检索到的引用内容。

Output

输出结果

A deduplicated result list presented as a markdown table (title, authors, year, venue, DOI/arXiv id, citations, OA link) plus the exact script commands used (so the search is reproducible). Use
--json
when piping into files the user asked for. Report total counts vs. shown counts honestly; when a provider failed (e.g. S2 429s exhausted), say so rather than filling gaps from memory.
Tag each result by evidence tier and keep titles canonical. A result that came only from a citation-graph edge or a single keyword hit is a candidate, not a confirmed citation of any specific paper: label it (graph / keyword / seed-degree) so a downstream consumer can threshold on confidence rather than treat every entry as equally certain. When the target is a known subset of a paper's bibliography, report recall against that subset separately from a precision band against the expected full count — never present over-fetched neighbors at the same confidence as confirmed hits. For every retained entry, take the title verbatim from the canonical record for its resolved id (don't hand-type or recall it), normalize to the publisher/DOI-canonical capitalization, and wrap proper nouns/acronyms in braces in any BibTeX so the as-published form survives — this is what prevents a title from fuzzy-matching yet failing a strict string lookup.
Always surface the coverage verdict. When you fan out with
resolve_papers.py
, repeat its COMPLETE/PARTIAL banner and the per-provider status to the user. If the run is PARTIAL — any authoritative index was unreachable or rate-limited — state plainly that the result is provisional and real, relevant papers may be missing for provider reasons, not relevance; offer to retry later or with
S2_API_KEY
. Never present a degraded run as if it were an exhaustive search, and never silently narrow to the one provider that happened to answer.
去重后的结果列表以Markdown表格形式呈现(包含标题、作者、年份、会议、DOI/arXiv编号、引用次数、开放获取链接),同时附上使用的精确脚本命令(确保搜索可复现)。若用户要求将结果写入文件,可使用
--json
参数。如实报告总数量与展示数量;当某服务商故障时(如S2的429限流重试耗尽),需明确说明,而非凭记忆填补空白。
为每个结果标记证据层级,并保持标题标准化。仅通过引用关系或单一关键词命中的结果是候选结果,而非特定论文的确认引用:需标记其来源(graph/keyword/seed-degree),以便下游使用者可根据置信度筛选,而非将所有条目视为同等确定。当目标是某篇论文参考文献的已知子集时,需单独报告该子集的召回率,以及针对预期总数的精度范围——绝不能将过度获取的邻居结果与确认命中结果置于同一置信度级别。对于每个保留的条目,需直接使用其解析后ID对应的标准记录中的标题(不要手动输入或回忆),统一为出版商/DOI标准的大小写格式,并在BibTeX中将专有名词/缩写用大括号包裹,以确保出版形式得以保留——这能避免标题模糊匹配但严格字符串查找失败的问题。
始终展示覆盖范围判定结果。使用
resolve_papers.py
进行扩散搜索时,需向用户重复其COMPLETE/PARTIAL标识和各服务商的状态。若运行状态为PARTIAL(任何权威索引无法访问或被限流),需明确说明结果是临时的,真实相关论文可能因服务商原因而非相关性缺失;可提供稍后重试或使用
S2_API_KEY
的选项。绝不能将降级运行的结果当作 exhaustive( exhaustive)搜索,也绝不能自动缩小范围至恰好可用的单一服务商。

References

参考资料

  • references/venue-aliases.md — the alias table for ~20 top venues across all four APIs, per-venue gotchas, verification provenance, how to add a venue
  • references/api-notes.md — per-API operational notes, rate limits, licensing, the fallback matrix, and the provider-coverage / graceful-failover contract (
    resolve_papers.py
    )
  • references/citation-graph-expansion.md — the citation-graph recall stage: edge directions, co-citation re-ranking, the foundational-anchor sweep, the claim-driven niche pass, key-free OpenAlex/Crossref endpoints
  • references/venue-aliases.md — 四个API中约20个顶级会议的别名表、各会议注意事项、验证来源、添加会议的方法
  • references/api-notes.md — 各API的操作说明、限流规则、许可协议、故障转移矩阵、服务商覆盖/优雅降级协议(
    resolve_papers.py
  • references/citation-graph-expansion.md — 引用关系召回阶段:引用方向、共引重排序、基础论文排查、基于研究声明的细分领域搜索、无密钥的OpenAlex/Crossref端点

Guardrails

约束规则

  • Never fabricate papers, DOIs, citation counts, or "I found N papers" claims — every presented result must come from an actual API response in this session. Route bibliography entries through
    verify-citations
    .
  • Provider outage ≠ no result, and one surviving provider ≠ a complete search. When an index is down or rate-limited, fail over to the others (≥2 independent indexes for any "complete" claim), keep an unconfirmed paper as
    unresolved-keep
    rather than dropping it on a single-index miss, and mark the run PARTIAL. A missing DOI is never grounds to discard a paper that has an arXiv / DBLP / anthology id. Never report a PARTIAL run as exhaustive.
  • A PARTIAL run is a retriable gate, not a finish line. The scripts already retry 429/5xx with exponential backoff before giving up; if a run still comes back PARTIAL (a provider exhausted its retries), do NOT accept it as done because the surviving indexes "happened to cover" the targets — recall then rests on luck. Re-issue the failed leg after a cool-down (and with
    S2_API_KEY
    for Semantic Scholar), or substitute an equivalent provider, before declaring the pass complete. When a target was recoverable only through a fallback path, surface that title explicitly in provenance so the gap is auditable rather than silently absorbed.
  • Gate completion per required cluster, not just per run. For any cluster the scope marks central (a direct-competitor or headline-contribution cluster), require that ≥2 independent indexes actually answered for that cluster's queries before treating it as covered; a central cluster confirmed by a single surviving provider is degraded, not complete, even if the overall run banner is COMPLETE.
  • For a named paper, existence is not enough: an older edition or a same-author near-duplicate title can pass a bare match yet be the wrong citation. Run candidates through
    resolve_canonical.py
    , prefer the latest canonical version / higher-impact instance, and surface siblings so the user chooses — never auto-collapse to one without flagging it.
  • Citation-graph neighbors (
    citation_graph.py
    ) are recall candidates, not results: confirm venue/acceptance before presenting any, never count raw neighbors as "papers found," and surface the seed-degree/edge reason so the human decides scope rather than auto-including.
  • Copyright: metadata is safe (DBLP is CC0, Crossref facts); abstracts and full text are fetched on demand and processed transiently — never commit them to the repo or bundle them in outputs. Attribute Semantic Scholar (ODC-BY) when its data is shown.
  • Respect the baked-in rate limits; never bulk-crawl, never page through an entire corpus, never strip the contact email from requests.
  • Never submit anything to any system on the user's behalf.
  • 绝不能伪造论文、DOI、引用次数或“找到N篇论文”的声明——所有呈现的结果必须来自本次会话中的实际API响应。参考文献条目需通过
    verify-citations
    技能处理。
  • 服务商宕机≠无结果,单一可用服务商≠完整搜索。当某索引宕机或被限流时,需切换至其他索引(任何“完整”声明需至少2个独立索引支持),将未确认的论文标记为
    unresolved-keep
    而非因单一索引未命中就丢弃,并将运行状态标记为PARTIAL。缺少DOI绝不是丢弃拥有arXiv/DBLP/论文集编号的论文的理由。绝不能将PARTIAL运行的结果报告为 exhaustive搜索。
  • PARTIAL运行是可重试的节点,而非终点。脚本已内置对429/5xx错误的指数退避重试逻辑,若运行仍返回PARTIAL(某服务商重试耗尽),绝不能因剩余索引“恰好覆盖”目标就视为完成——此时召回率依赖运气。需在冷却后重新发起失败的请求(Semantic Scholar可配合
    S2_API_KEY
    ),或替换为等效服务商,再判定搜索完成。当目标仅通过 fallback路径恢复时,需在来源信息中明确标注该标题,以便漏洞可追溯,而非默默掩盖。
  • 按所需集群判定完成状态,而非仅按运行状态。对于范围标记为核心的任何集群(直接竞品或核心贡献集群),需至少2个独立索引实际响应该集群的查询,才可视为已覆盖;即使整体运行状态为COMPLETE,仅由单一可用服务商确认的核心集群仍属于降级状态,而非完成状态。
  • 对于指定的论文,存在性是不够的:旧版或同名作者的近似标题可能通过基础匹配但引用错误。需将候选结果通过
    resolve_canonical.py
    处理,优先选择最新标准版本/更高影响力的实例,并展示所有候选版本供用户选择——绝不能在未标记的情况下自动合并为单一结果。
  • 引用关系中的邻居论文(
    citation_graph.py
    )是召回候选,而非结果:呈现前需确认其会议/录用状态,绝不能将原始邻居论文计数为“已找到的论文”,并需展示其种子关联度/引用关系原因,由用户决定范围,而非自动纳入。
  • 版权:元数据是安全的(DBLP为CC0协议,Crossref事实数据);摘要和全文按需获取并临时处理——绝不能提交至仓库或打包在输出中。展示Semantic Scholar数据时需注明来源(ODC-BY协议)。
  • 遵守内置的限流规则;绝不能批量爬取,绝不能遍历整个语料库,绝不能从请求中移除联系邮箱。
  • 绝不能代表用户向任何系统提交任何内容。