plan: batch-200 expansion (100 Pro + 100 Std)

Doubles down on what worked in batch-50:
  - dev1 (Guangzhou) is primary execution host
  - Owner cap=2 for diversity
  - --max-source-mb 200 to defend against X86-class outliers
  - Pro 2.x deprecated-board fix is already in (commit c3cac97)
  - SSH transport for dev1 -> gitea (commit 8220c99)

Candidate pool:
  200 picks from A-tier (grade>=3 & like>=10) minus already-crawled 65
  Remaining A-tier corpus is 2,741 (Pro 1326 + Std 1415)
  173 unique authors, like median 258, grade dist 4:118 / 3:82

Estimated walltime ~25-35 min on dev1 for Step 1-4 (no attachments).
LFS increment ~2.5 GB (source only) or +10 GB if Step 5 attachments
included. Either way well within Gitea's 200 GB migration threshold.

Step 5 (attachment download) deferred — not on the critical path for
EPRO2/Std → KiCad work, can revisit when license-filtered Forge
projection demands it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-29 02:29:53 +08:00
parent 7f3729b89c
commit 7cb35020f4
3 changed files with 386 additions and 0 deletions

View File

@@ -0,0 +1,185 @@
# oshwhub 扩抓批 200执行计划
**创建**2026-04-29
**承接**`docs/plans/oshwhub_batch50.md`(已 100% 完结)
**目标**:从 65 项扩到 265 项 — 验证流水线在中等规模的稳定性,开始覆盖 A 档第二梯队
**候选清单**`data/state/oshwhub_batch200_candidates.jsonl`200 行)
**主执行机**dev1广州~30× 网络优势已实测)
---
## 1. batch-50 经验回放(影响这一批的设计)
| 教训 | 这一批的对策 |
|---|---|
| dev1 Guangzhou 网络是关键加速器 | 全部步骤都在 dev1 跑SG 这边只是同步消费 |
| Pro 2.x 占高质量 Pro 池 80% | Pro 2.x parser 必须稳deprecated-board 已修 |
| `git push` 单次 70 MB 在 dev1 → gitea 链路 6.5% 丢包 → 360 KB/s | dev1 origin 已切 SSH 协议commit `8220c99`),后续 push 流畅 |
| `--max-source-mb 200` 没触发25 Pro 最大才 9 MB| 保留 cap 防 X86-class outlier但预期触发率极低 |
| `git push 2>&1 \| tail -5` 永远 exit 0 吞掉错误 | 启用 `set -o pipefail` 或检查 PIPESTATUS |
| Pro 2.x boards[] 可能含废弃 sch/pcb 返 401 | 已修:`ticket.schematics`/`pcbs` 求交集commit `c3cac97`|
| 详情 HTML 抓取是 server-render bound~50ms / 项)| 不优化;本批 200 项 ~10s 即可 |
---
## 2. 候选池筛选标准(与 batch-50 一致)
数据源:`data/state/oshwhub_listing_full.jsonl`33,695 项全量索引)
**硬过滤**
- `grade >= 3 AND like >= 10`A 档)
- `uuid` 不在已抓 65 项内(剩余 A 档 = 2,741 项 Pro 1326 + Std 1415
**排序**`like*3 + star + fork*2 + views/100 + comments*2 + grade*50`
**多样性约束**
- 单作者全局上限 2
- Pro / Std 100:100 均衡(同 batch-50 思路)
---
## 3. 选出的 200 项概况
| 维度 | 值 |
|---|---|
| 总数 | 200100 Pro + 100 Std |
| 唯一作者 | 173max 单作者 2 项) |
| like 区间 | min=22 / median=258 / max=624 |
| grade 分布 | 4: 118 项, 3: 82 项 |
> like 中位数 258 比 batch-50 的 554 低——因为 batch-50 已经吃掉了 A 档头部。本批进入 A 档第二梯队,整体仍合格。
完整名单见 `data/state/oshwhub_batch200_candidates.jsonl`
---
## 4. 执行步骤
> 全部在 dev1 跑。SG 端只 `git pull` 消费成果。
### Step 0 — 候选池冻结 ✅
`oshwhub_batch200_candidates.jsonl` 已生成。
### Step 1 — 详情页扫 license~3 min @ concurrency=5
```bash
ssh dev1 'export PATH=$HOME/.local/bin:$PATH && \
cd ~/repo/FacereDataset && \
PYTHONUNBUFFERED=1 uv run python -u -m crawlers.oshwhub \
--from-jsonl data/state/oshwhub_batch200_candidates.jsonl \
--no-files --no-cover --concurrency 5 \
--out data/raw/oshwhub'
```
预期200 项 / ~3-5 分钟dev1 详情页 p90=73ms × 200/5 并发 ≈ 3s 网络 + server render 时间 ≈ 几分钟)。
### Step 2 — license 分布盘点(无过滤入库)
```bash
ssh dev1 'cd ~/repo/FacereDataset && uv run python - <<EOF
import json, glob, collections
target = {json.loads(ln)["uuid"] for ln in open("data/state/oshwhub_batch200_candidates.jsonl")}
licenses = collections.Counter()
for p in glob.glob("data/raw/oshwhub/*/metadata.json"):
m = json.load(open(p))
if m.get("project_id") in target:
licenses[m.get("license") or "<unknown>"] += 1
for lic, n in licenses.most_common():
print(f" {n:>4} {lic}")
EOF'
```
按 batch-50 数据外推:~64% GPL 3.0、~16% Forge-friendly 自由、~16% NC variants。
### Step 3 — Pro 100 项 backfill source~10-15 min
```bash
ssh dev1 'export PATH=$HOME/.local/bin:$PATH && \
cd ~/repo/FacereDataset && \
PRO_UUIDS=$(jq -r "select(.origin==\"pro\") | .uuid" \
data/state/oshwhub_batch200_candidates.jsonl | paste -sd,) && \
PYTHONUNBUFFERED=1 uv run python -u -m crawlers.oshwhub \
--backfill-pro-source \
--uuids "$PRO_UUIDS" \
--max-source-mb 200'
```
预期分布(按 batch-50 的 Pro 比例外推):
- Pro 2.x legacy ~80 项chain 平均 < 30每项 ~30s → ~40 分钟……
> 等等得拆估算dev1 端 Pro chain 是 0.5s sleep × N + CDN 0.2s × M。粗算单项 average = 0.5×5 + 0.2×30 = 8.5s。100 项串行 = ~14 min。
> 如果想再快可以加 backfill 路径的 concurrency目前没加是个小改动。先不优化看实测。
### Step 4 — Std 100 项 backfill source~5-10 min
```bash
STD_UUIDS=$(jq -r 'select(.origin=="std") | .uuid' \
data/state/oshwhub_batch200_candidates.jsonl | paste -sd,)
ssh dev1 ... --backfill-source --uuids "$STD_UUIDS"
```
每项 ~5-10sStd 比 Pro 快doc 数少100 项 ~10 min。
### Step 5 — 附件下载(**可选 / 推迟**
batch-50 的 50 项声明附件 2.36 GB外推 batch-200 ~9-10 GB。Gitea LFS 当前用量 < 5 GB加 10 GB 还在舒适区。
但下载时间是关键因素:
- 200 项 × 平均 50 MB 附件 / 5 MB/sdev1 → image.lceda.cn 估算)= ~30 分钟
-`--skip-ext mp4,qt,mov` 可砍 30-50% 体积
**建议**:先做 Step 1-4 拿到 license + source附件单独评估后再决定。如果只为 EPRO2 → KiCad 训练,附件不需要。
### Step 6 — 验收 + push
```bash
ssh dev1 'cd ~/repo/FacereDataset && \
uv run python scripts/build_index.py && \
git add data/raw/oshwhub/ projects.md && \
git commit -m "batch-200: ..." && \
git push origin main'
```
---
## 5. 资源预算
| 项 | 估算 | 备注 |
|---|---:|---|
| API 调用 | ~2,500 次 | 200 详情 + 100 Pro × 5 calls + 100 Std × 5 calls + chain blob 几百次 |
| dev1 walltimeStep 1-4| **~25-35 分钟** | 单 IP 串行;加 backfill concurrency 可压到 10-15 min |
| 源工程体积 | **~2.5 GB** | batch-50 实测 12 MB/项 × 200 = 2.4 GB |
| Gitea pushdev1 SSH 链路)| ~3-5 min | 同 batch-50 量级SSH transport 健康 |
| 附件(如做 Step 5| +10 GB / +30 min | 可砍 mp4 节流 |
| **LFS 总增量(不含附件)** | **~2.5 GB** | 当前 < 5 GB → batch-200 后 ~7-8 GB |
| **LFS 总增量(含附件)** | **~12 GB** | → 批后 ~17 GB离 200 GB 迁移线还远 |
## 6. 风险与预案
| 风险 | 概率 | 影响 | 预案 |
|---|---|---|---|
| Pro cookie 过期半路 | 低 | 100 项 Pro 可能多个失败 | 已在 dev1 + SG 同 cookie过期表现是连续 401监控前 5 个失败即停 |
| 单 Pro 项目超 200 MB | 低 | size cap 触发跳过 | `--max-source-mb 200` 拦截,记 oversize state |
| Pro 2.x 又出现新边缘 case非 deprecated-board| 中 | 单项 fail | 当前 fix 处理 deprecated 类;新 case 用 try/except 单点收敛后 retry |
| 附件 mp4 视频拉爆磁盘 | 中(如做 Step 5| 多消耗 5+ GB | `--skip-ext mp4,qt,mov` |
| dev1 → gitea 链路波动期间 push | 中 | 需手动重试 | 已切 SSHBBR 已开;最差 retry |
| 触发 oshwhub 限流 | 低 | 整批中断 | 现有 sleep 已在实测水位;监控 200/non-200 比例,连续 3 个 5xx 即停 |
## 7. 验收标准
- [ ] `data/raw/oshwhub/` 新增 ≥ 195 个项目目录(允许 ≤ 5 项 fail
- [ ] 每个新增项目的 `metadata.json``license` + `source_format` + `source_path`
- [ ] license 分布盘点写入 log
- [ ] `projects.md` 自动重建后包含全 265 项
- [ ] LFS 增量 < 5 GB不含附件/ < 15 GB含附件
- [ ] log.md 顶部一条总结性日志
- [ ] dev1 push 整批 ≤ 10 min wall
## 8. 后续候选
- **batch-500** —— 把 A 档2,741 项)一次性吃完(剩 ~2,540
- **B 档grade≥2 & like≥5扩量** —— 总池子 ~6,243 项
- **附件细分策略** —— 按 ext 分类 LFSGerber/STEP 留、视频走外链)
- **Pro 2.x → KiCad 解析链路** —— 复用 `easyeda2kicad.py`plan.md §1.7 提过但未做)
- **license 详情扫全 A 档** —— 全 2,806 项 license 落本地,给所有未来批选作过滤依据