# arXiv ID 格式 > **当前内容契约更新**:固定来源 PDF 已恢复为 paper_access 开关下的鉴权字节, > 关闭404,qa_、别名、Range、历史原件不能绕过。新内容在独立 qatlas-content 桶的 > content/ 命名空间不可变发布:source 绑定确切 PDF SHA,arXiv vN 是语义版本、 > S3VersionId 是物理版本、parse revision 是独立不可变身份;三者不可混用。 > 同 paper/SHA 可复用 source;冻结导入别名不同字节409(overwrite也不替换)。 > 解析上传必须完整支持的 Middle+Markdown,保留所有成员原名/字节,MD-only拒收; > manifest最后核验发布且PG必需,重传新revision,不覆盖历史。旧MD/JSON/images忽略, > 只在内容/PDF访问按需冻结旧PDF,不bulk回填、不删除旧桶;没有nightly/boot/入库自动推理。 > 下文涉及旧三桶覆盖、仅full.md+images、原PDF410/外部直链的段落保留为历史背景, > **不作为当前操作规则**。当前API/上传/续读统一见[论文内容契约](../server/paper-content.md) > 与[Upload API](../server/upload-api.md);完整read视图不是原Middle JSON。 QuantumAtlas 以 arXiv ID 为论文的主键。文本侧支持两种 ID 格式 + 版本后缀;存 储侧把每条 ID 拆成「年月分片 / 子领域 / 文件 stem」三段做 sharding。本文档讲 完整规则,包括 2026-06 引入的**按子领域分目录**新布局以及背后的根因。 ## 1. 两种格式 | 时代 | 格式 | 例子 | |---|---|---| | **老式**(≤ 2007-03) | `/<7-digit>` | `quant-ph/9508027`、`hep-th/0207001`、`cs.AI/0101001`、`physics.atom-ph/0001001` | | **新式**(2007-04 起) | `.<4–6-digit>` | `2501.00010`、`2403.12345`、`2401.123456` | 注意 **老式 ID 的 `category` 含斜杠**,所以会出现在 URL path 里。server 端 `splitPapersPath` 按"最后一段"匹配 action / arxiv_id 切分能正确处理。 ### 1.1 老式 category 词表 支持的 category 形态(按 `internal/paperassets::oldStyleCategoryRE` 实现的语法): | 形态 | 例子 | 说明 | |---|---|---| | 无子分类 | `quant-ph`、`hep-th`、`gr-qc`、`math-ph` | 主题字段为 lower-case,可含 `-` | | 大写 2 字母子分类 | `cs.AI`、`math.AG`、`q-bio.NC`、`nlin.CD` | 子字段 `\.[A-Z]{2}` | | 小写连字符子分类 | `physics.atom-ph`、`cond-mat.stat-mech` | 子字段 `\.[a-z][a-z\-]*` | > A1 之前的正则只覆盖前两种,碰到 `physics.atom-ph/0001001v1` 这类直接 reject。 > 现在统一收下。 ## 2. 版本后缀 `vN` 任何 arXiv 论文都可以有多个版本(v1、v2、...)。 | 入口 | 是否要求 `vN` | |---|---| | `qatlas ingest ` | 可不带;server 取 arXiv 当前最新版 | | `qatlas contrib pdf ` | **必填**;对象寻址按 `v` 命名 | | `qatlas contrib mineru ` | **必填** | | `GET /api/papers/{id}/markdown`(及 images 等) | 可不带;server 优先查本地 catalog 补最新版本,未收录才抓 arxiv.org(多版本时在响应里显式标)| | `GET /api/papers/{id}/pdf` | **鉴权固定来源PDF字节**,source_id或语义version互斥,开关禁用404、Range同样鉴权;不需解析token| 论文详情 `GET /api/papers/{id}` 与各资产端点还接受 **`qa_` paper_id**(服务端 解析成该论文的 canonical 身份,取最高已收录的 arXiv 版本;DOI-only 论文走 DOI 管线)。 ## 3. 对象寻址映射(post-A1 layout) server 把 ID 拆成 `/<可选 category>/.`: | arxiv_id | object key(PDF) | 说明 | |---|---|---| | `2501.00010v1` | `pdf/2501/2501.00010v1.pdf` | 新式扁平 | | `2403.12345v2` | `pdf/2403/2403.12345v2.pdf` | 新式扁平 | | `quant-ph/9508027v1` | `pdf/9508/quant-ph/9508027v1.pdf` | **老式带子目录** | | `hep-th/0207001v3` | `pdf/0207/hep-th/0207001v3.pdf` | **老式带子目录** | | `cs.AI/0101001v1` | `pdf/0101/cs.AI/0101001v1.pdf` | 含子分类的老式 | `md/` / `json/` / `images/` 子目录采用**同样的三段布局**(kind + shard + 可选 category + stem)。完整规则在 `internal/paperassets/path.go::AssetKeyFor`。 `` 跟 [arxiv.org bulk-data 目录约定](https://info.arxiv.org/help/bulk_data.html) 一致。 带 category 子目录是 QuantumAtlas 在 A1(2026-06)后引入的额外维度,**bulk-data 上游 没有这一层** —— 我们引入它是为了避免下一节描述的跨子领域冲突。 ### 3.1 为什么老式 ID 要保留 category 子目录 **老式 7-digit number 不是全局唯一的,只在 category 内唯一**。同一个 `YYMMnnn` 在不同 category 下对应**完全不同**的论文: | arxiv URL | 标题 | |---|---| | `arxiv.org/abs/quant-ph/0207065` | Relation between classical communication capacity and entanglement capability for two-qubit unitary operations | | `arxiv.org/abs/hep-th/0207065` | Does curvature-dilaton coupling with Kalb Ramond field lead to an accelerating Universe? | | `arxiv.org/abs/math/0207065` | A duality proof of Tchakaloff's theorem | | `arxiv.org/abs/gr-qc/0207065` | Gravitomagnetic effects | 剥掉 `category` 后写到同一个 bucket key(`pdf/0207/0207065v1.pdf`)= **silent overwrite**。A1 之前确实是这么存的;当前生产之所以没踩,纯粹是因为 OpenAlex bootstrap 加了 quant-only filter。一旦扩到 hep-th / cond-mat 等任意第二个子领域, 立刻冲突。 A1 的 `pdf///.` 子目录布局从结构上彻底排除这种冲突。 ### 3.2 新旧布局对照 | arxiv_id | A1 之前(legacy) | A1 之后(new) | |---|---|---| | `2501.00010v1` | `pdf/2501/2501.00010v1.pdf` | `pdf/2501/2501.00010v1.pdf` *(unchanged)* | | `quant-ph/9508027v1` | `pdf/9508/9508027v1.pdf` | `pdf/9508/quant-ph/9508027v1.pdf` | | `hep-th/0207065v1` | `pdf/0207/0207065v1.pdf`(冲突!)| `pdf/0207/hep-th/0207065v1.pdf` | 新式 ID 的 layout 不变(数字本来就全局唯一)。仅老式有变更。 ### 3.3 Dual-read 过渡期 A1 后立刻部署 server 不会 break 已有数据: - **读路径** (`paperassets.LocateAsset` / `LocateAssetByID`) 先查新 layout,未命中 时对老式 canonical ID **回退**查 legacy bare key - **写路径** (`AssetKey` / `AssetKeyFor`) 总是写新 layout,不写 legacy 也就是说: - 老对象(pre-A1,bare layout)→ 通过 fallback 仍可读 - 新写入(post-A1)→ 都进 per-category 子目录 - migration 把 legacy 对象 `mc cp` 到新位置(manifest-based,三阶段;plan §4 Phase E)→ 同步更新 catalog 主键 - 一个 release 后移除 dual-read fallback 路径 dual-read 命中由 `paperassets.LegacyLayoutReads()` 计数,运维可观测 migration 进度。 ## 4. kind 后缀 每篇论文除了 PDF 还可能有: | kind | key 模板(新式) | key 模板(老式 canonical) | 内容 | |---|---|---|---| | `pdf` | `pdf//.pdf` | `pdf///.pdf` | 原始 PDF | | `markdown` | `markdown//.md` | `markdown///.md` | MinerU 解析的 markdown | | `json` | `json//.json` | `json///.json` | arXiv 元数据(题目 / 作者 / abstract) | | `images` | `images//.zip` | `images///.zip` | MinerU 解析出的图片 zip | `qatlas contrib pdf --pdf` 把 PDF 字节落到 `pdf/`。论文元数据(题目 / 作者 / 摘要 / 引用)走 OpenAlex 上游同步进 PostgreSQL paper registry,不再通过 upload 端点写 `json/`(v0.7.0 起;该前缀仅保留兼容历史对象的读路径)。 ## 5. 规范化与结构化解析 `internal/paperassets` 提供两层 API: - **轻量 helpers**(保留兼容历史调用方): - `NormalizeIdentifier(s)` — 去空白 + 剥 `arXiv:` 前缀 - `StripVersion(s)` — 去掉尾部 `vN` - `SafeKey(s)` — 把 `/` 换成 `__` 给老式磁盘 fallback 用 - **结构化解析**(新代码优先用): - `Parse(s) (ParsedArxivID, error)` — 拆成 Category / Stem / StemBase / Version / YYMM 等字段 - `MustParse(s)` — 测试常量用,invalid 输入直接 panic - `AssetKeyFor(kind, p)` — 给 `ParsedArxivID` 渲染对象 key - `LegacyAssetKeyFor(kind, p)` — 给 dual-read fallback 用的 pre-A1 key - `LocateAsset(ctx, store, kind, p)` — 一站式 read 接口:自动新 layout 优先 + legacy fallback 老式 bare ID(`9508027v1`,无 category)由 `Parse` 标记 `IsBare=true`,调用方决 定怎么处理: - **upload 入口** (`ValidateUploadID`):当前**仍兼容**接受 bare(issue #4 缓解) - **storage 入口** (`AssetKeyFor`):bare 输入回退到 legacy layout `pdf//.` - **未来**(plan §H3 / [issue #12](https://github.com/IAI-USTC-Quantum/QuantumAtlas/issues/12)):catalog migration 完成后 `ValidateUploadID` 拒 bare,由上层 resolver 反查 registry 拿到 category 再调用 ## 6. 常见坑 ~~~{admonition} upload 时漏掉 `v1` :class: caution ``` qatlas contrib pdf 2501.00010 --pdf paper.pdf ``` server 返回 400 `arxiv_id must include version suffix`。改成 `2501.00010v1`。 ~~~ ~~~{admonition} 用了 `arxiv.org/abs/...` 完整 URL :class: caution server 只接受 ID 部分。从 URL `https://arxiv.org/abs/2501.00010` 抽出 `2501.00010` 就行。 ~~~ ~~~{admonition} 老式带版本是合法的 :class: tip `quant-ph/9508027v1`(含斜杠 + 版本)—— 是合法的,正常工作。 ~~~ ~~~{admonition} 老式 bare 形态(`9508027v1`,无 category)当前仍接受但**有歧义** :class: warning 由 [issue #4](https://github.com/IAI-USTC-Quantum/QuantumAtlas/issues/4) 历史原因,server `ValidateUploadID` 仍兼容裸形态。但裸 7-digit 号在不同子 领域下指向**不同论文**(见 §3.1);当前仅靠"catalog 只有 quant-ph"这个事 实保证不冲突。新代码 / 客户端**建议永远带 category 前缀**调用。 [issue #12](https://github.com/IAI-USTC-Quantum/QuantumAtlas/issues/12) 跟踪 最终拒绝裸形态的工作。 ~~~ ~~~{admonition} DOI 入口同一个端点 :class: note `GET /api/papers/{id_or_doi}/markdown` 自动 detect DOI(`^10\.\d{4,9}/`), 经 OpenAlex 反查成 arxiv_id 后走同一套 handler。详见 [REST API · DOI 寻址](../server/rest-api.md#doi-入口)。 ~~~