# 数据流:论文从 arXiv 到可检索资产
> **当前内容契约更新**:固定来源 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 端到端的「主线剧情」。每个箭头都对应一个真实的 CLI 命令或 server 操作。
```{mermaid}
flowchart TB
subgraph SRC ["外部源"]
ARX[arXiv]
OTH[其他来源
本地 PDF]
OA[OpenAlex]
PUB[出版社 / OA 仓库
DOI 落地页]
end
subgraph STORE ["对象存储(不可变资产)"]
PDF[PDF
pdf/<yymm>/<id>v<n>.pdf]
MD[Markdown
md/<yymm>/<id>v<n>.md]
IMG[images/]
end
subgraph PG ["PostgreSQL(registry + corpus)"]
REG[paper registry
papers / paper_assets / paper_identities]
CORP[OpenAlex corpus
openalex_works]
end
subgraph SEARCH ["搜索引擎"]
ENG[POST /api/search
catalog / arxiv / openalex]
end
ARX -->|qatlas ingest| PDF
OTH -->|qatlas contrib pdf| PDF
PUB -->|Robust Downloader| PDF
PDF -->|MinerU| MD
PDF -->|MinerU| IMG
PDF & MD & IMG -->|register| REG
OA -->|qatlasd openalex bootstrap-pg| CORP
REG & CORP --> ENG
ARX & OA --> ENG
style STORE fill:#e3f2fd,stroke:#1976d2
style PG fill:#e8f5e9,stroke:#388e3c
style SEARCH fill:#fff3e0,stroke:#f57c00
```
## 四种获取路径
数据可以从四个地方进入资产层。**最终的保存路径都一样**(`/pdf//v.pdf` 等,S3 后端时是对应 bucket 里的对象 key),区别只是触发者:
### 1. 服务器自动抓取(arXiv)
```bash
qatlas ingest quant-ph/9508027 --parser mineru
```
server 调 arXiv API 抓 PDF + metadata,可选立刻调 MinerU 解析。**触发方需要 `papers:write` scope**。
### 2. 用户直接上传
```bash
qatlas contrib pdf 2501.00010v1 --pdf paper.pdf
```
适合:手里已经有 PDF(公司内部论文、扫描件、preprint 私下流传版本)。**需要 `papers:write` scope**。
### 3. 用户本地跑 MinerU 推回
```bash
# 配 mineru_api_tokens 后
qatlas contrib mineru 2501.00010v1 --push-pdf
# 或队列模式,处理 server 列表里所有待解析的
qatlas contrib mineru --batch-size 20
```
本地用自己的 MinerU 配额跑解析。**server 颁发 30 分钟原子 claim**——多个贡献者并发跑不会撞重。需要 `papers:write` scope。
### 4. Robust Downloader(DOI / 正式版批量抓取)
```text
POST /api/downloader/fetch
{"items": ["10.1038/s41586-024-07806-9", "arXiv:2401.12345"]}
```
给 DOI / arXiv id / 论文 URL(单批 ≤50 条),server 跑多范式策略阶梯
(arXiv 直下 → OpenAlex twin → OA 元数据 API → 出版社 URL 模板 → 落地页
挖掘 → 浏览器 lane / LLM 兜底 / 远端代理)抓**正式版 PDF**,统一验证后
入库并驱动 MinerU。SPA 页面 `/$lang/downloader`。与前两条路径的区别:
**来源不限于 arXiv**——绿色 OA 仓库、出版社模式 URL、需要 entitlement 的
落地页都在射程内。需要 `papers:write`(提交)/ `papers:read`(查 job)scope。
详见 [上传论文资产](../client/upload-assets.md)、[用 MinerU 解析](../client/parse-with-mineru.md) 和 [Robust Downloader](../server/downloader.md)。
## 懒加载摄入(lazy ingest)
`GET /api/papers/{id_or_doi}/markdown`(仅当部署方开启
`QATLAS_PAPER_ACCESS_ENABLED`;历史PDF410说明已被不可变内容契约替代;当前`/pdf`按gate鉴权交付,旧图中PDF抓取是
markdown 管线的内部阶段)在缓存未命中时**不阻塞**:server 立即返回
202 + `Operation-Location`,后台静默从 arxiv.org fetch PDF 并串 MinerU 转换,
客户端轮询 `/markdown/status` 直到 `state == cached` 再 GET 拿字节。同一篇
论文的 N 个并发请求被 server-side dedupe 成 1 次 fetch + 1 次 convert。完整协议见
[REST API · 长任务](../server/rest-api.md)。
## Registry 写入路径
```text
1. PUT s3://qatlas-pdf//.pdf ← 字节先落对象存储
2. INSERT ... ON CONFLICT papers + paper_assets ← PostgreSQL registry 同步
3. PostgreSQL 不可用时:HTTP 仍成功 + X-Catalog-Sync: deferred
4. 之后跑 qatlasd papers sync --full --from-rustfs 从对象存储重建缺失状态
```
对象存储是资产字节的 source of truth;PostgreSQL 是可重建的集合索引与租约层。
## 搜索路径
搜索只登记论文元数据,不自动下载 PDF,也不把新命中标为待下载。普通、逐平台、Agentic 和自定义评分搜索都遵循同一策略。用户在结果中勾选论文并点击「下载所选」后,前端才调用 `POST /api/downloader/fetch`,由主服务为明确选择的条目提交任务;worker 的领取与执行方式不变。升级前已有的排队任务不会自动取消。
`POST /api/search` 接收一个 SearchEntry JSON,engine 按 config.yaml 的
`search.providers`(默认 `catalog,arxiv,openalex`)fan-out:
- **catalog**:本地 PostgreSQL registry 的元数据 + 资产状态;
- **arxiv / openalex**:上游在线查询(建议配 `paper_access.openalex_mailto` 进 polite pool)。
`search.remote` 启用后另有 `POST /api/search/multi`:逐平台(backend)返回
**原始**命中列表,不做跨源融合——SPA 的 backend picker + 每平台 Tab 渲染
就是这条路径(见 [REST API · Search](../server/rest-api.md))。
语义向量检索不在 `/api/search` 的 provider 列表里:它由独立的 qatlas-rag
微服务提供(dense+sparse 混合检索,RRF + rerank),经 qatlas-search 作为
fan-out 的一个 backend 接入;qatlasd 在论文 ready 时向 qatlas-rag 推送
索引(config.yaml 的 `rag.remote` 段)。
## 关键不变量
```{admonition} 记住这几条
:class: note
- **对象存储是证据**,几乎不改,可追溯;
- **PostgreSQL 是 registry**,可重建、可 SQL 查询;冲突时以对象存储为准;
- **搜索是派生视图**,provider 可增删,不持有独立事实。
```
理解这三条就能理解 90% 的设计决定。