# 立创开源平台抓取 → Forge 接入 spec

**Date**: 2026-04-23
**给**: 同学爬 oshwhub.com / 立创开源广场
**目的**: 爬回来的数据按这份 spec 整理，我们直接批量接入 Forge 的 BoardReference corpus。

---

## 1. TL;DR — 你交付的目标形态

每个项目一个目录，**三件套**：

```
<project_id>/
├── schematic.kicad_sch   # ← 我们主要吃这个：KiCad 7+ S-expression 格式
├── manifest.json         # ← 项目元数据（见 §4）
└── source/               # ← 可选：原 EasyEDA 文件归档（.json / .epro 等）
```

**命名**: `project_id` = `oshwhub_<oshwhub数字ID>_<slug>`，纯小写下划线。
例：`oshwhub_1234567_esp32_bluepill_clone`。

**交付**: tar.gz 或 git repo 都行。放在我们能访问的路径；我们自己跑批量 migrate。

---

## 2. 硬性前置条件 —— 任何一条不满足就跳过这个项目

### 2.1 License 白名单（关键！）

**仅抓这些许可的项目**：
- MIT / BSD-2-Clause / BSD-3-Clause / Apache-2.0
- CC0-1.0 / CC-BY-4.0 / CC-BY-3.0
- CERN-OHL-P-2.0（permissive 变体）
- Unlicense / public domain

**必须拒掉的**：
- **CC-BY-SA**（share-alike，会污染我们 corpus——大比例立创项目是这个！务必过滤）
- GPL / LGPL / AGPL 全家
- "未声明 license" / "All rights reserved"
- CC-BY-NC（禁商用）

立创项目页面上会显示许可名。你的爬虫要**先读许可、后抓文件**——许可不合格直接跳过，节省带宽。

### 2.2 文件格式

**必须有 KiCad 7+ 格式的 `.kicad_sch`**：
- 文件首 20 字节包含 `(kicad_sch`
- version 声明 ≥ 20210621（早于这个版本 KiCad 10 加载会 warning 一大堆）

**如果项目只有 EasyEDA 原文件**：
- 用 [`easyeda2kicad`](https://github.com/uPesy/easyeda2kicad.py) 工具转（`pip install easyeda2kicad`）
- 或者立创 EDA Professional 的"导出 KiCad"功能
- 转完后**必须验证能被 kicad-cli 加载**（见 §6 自测）——转不出来就跳过、不要硬塞

### 2.3 基本质量

- 至少 5 个元件（太简单的 breadboard prototype 没参考价值）
- 不超过 300 元件（太大的我们暂时不处理）
- 不是纯 footprint 测试板（没电气连接）

---

## 3. `schematic.kicad_sch` 的要求

**原样保留**，不改、不格式化、不重新生成。我们的 corpus 依赖**字节级完整保留**才能做零漂移 byte-copy render。

**如果你用 easyeda2kicad 转换**：
- 转一次、存盘、不要后续再处理
- 多 sheet 项目：把主 sheet 存为 `schematic.kicad_sch`，其它 sheet 存为 `sheets/<name>.kicad_sch`
  （我们暂时只消费根 sheet，但不要删其它——留着我们以后升级）

---

## 4. `manifest.json` 格式

必填字段：

```json
{
  "project_id": "oshwhub_1234567_esp32_bluepill_clone",
  "source_platform": "oshwhub",
  "source_url": "https://oshwhub.com/username/esp32-bluepill-clone",
  "source_author": "username",
  "title": "ESP32 Blue Pill 克隆",
  "description": "一行到三行描述。可以直接从项目页面抓。双语都抓最好（en + zh），至少一个。",
  "description_zh": "中文描述 (如果有)",

  "license": "MIT",
  "license_source": "https://oshwhub.com/username/esp32-bluepill-clone/license",
  "license_verified": true,

  "components_used": ["ESP32-WROOM-32", "AMS1117-3.3", "USB_B_Micro", "C", "R", "LED"],

  "file_checksum_sha256": "abc123...",
  "file_size_bytes": 178825,
  "kicad_sch_version": "20230121",

  "crawl_timestamp": "2026-04-23T10:00:00Z",
  "converted_from": "easyeda",
  "conversion_tool": "easyeda2kicad 0.8.2",

  "tags_raw": ["esp32", "dev_board", "usb"]
}
```

字段说明：

- **`source_url`**：立创开源那个 project 的页面 URL。我们保存做 provenance。
- **`license`**：必须是 §2.1 白名单里的字符串之一。**抓不准就跳过**，宁缺毋滥。
- **`license_source`**：license 是从哪读到的（页面、LICENSE 文件、README）。方便我们事后验。
- **`components_used`**：从 sch 里或从 EasyEDA 原 BOM 抓。用于后续 tag 派生——越全越好。
- **`file_checksum_sha256`**：sha256 of `schematic.kicad_sch`。dedupe 用。
- **`converted_from` / `conversion_tool`**：原生 KiCad 的项目写 `"converted_from": "native"`；EasyEDA 转来的写具体工具和版本。
- **`tags_raw`**：立创页面上那个项目自带的标签/分类。原样塞。我们这边有一个 enrich 管线会基于 `components_used` + `description` 再派生更细的 tag（has_mcu / chip_tp4056 / use_portable...），你不用管这一步。

可选但有用：
- `source_easyeda_json_path`: 原 EasyEDA 文件的路径（如果你归档到 `source/` 下的话）
- `commit_sha` / `version`: 如果立创项目有版本号
- `star_count` / `fork_count`: 作为后续 quality 排序参考

---

## 5. 目录结构示例

```
batch_20260423/                         # 你一次交付的批次目录
├── README.md                           # 批次元数据（数量、时间、任何 caveat）
├── BATCH_MANIFEST.json                 # 机器可读的批次清单（见 §5.1）
│
├── oshwhub_1234567_esp32_bluepill/
│   ├── schematic.kicad_sch
│   ├── manifest.json
│   └── source/
│       └── schematic.json              # 原 EasyEDA
│
├── oshwhub_2234568_tp4056_charger_v2/
│   ├── schematic.kicad_sch
│   ├── manifest.json
│   └── source/
│       ├── schematic.json
│       └── pcb.json
│
├── oshwhub_3234569_audio_preamp/
│   ├── schematic.kicad_sch             # 原生 KiCad，无 source/
│   └── manifest.json
│
└── ... (N 个项目)
```

### 5.1 BATCH_MANIFEST.json

让我们能一键扫批次：

```json
{
  "batch_id": "batch_20260423",
  "created_at": "2026-04-23T12:00:00Z",
  "total_projects": 153,
  "by_license": {
    "MIT": 87,
    "CC-BY-4.0": 34,
    "Apache-2.0": 20,
    "CC0-1.0": 12
  },
  "projects": [
    "oshwhub_1234567_esp32_bluepill",
    "oshwhub_2234568_tp4056_charger_v2",
    ...
  ]
}
```

---

## 6. 自测 checklist（你交付前跑一遍）

对每个 `<project_id>/` 跑这 4 个检查，任一失败就把这个项目踢出批次：

```bash
# 1. .kicad_sch 首字节对头
head -c 20 <project_id>/schematic.kicad_sch | grep -q "(kicad_sch" || echo FAIL_1

# 2. 版本 ≥ 20210621
grep -oE "version \d{8}" <project_id>/schematic.kicad_sch | head -1
# expect 20210621 或更大

# 3. 能被 kicad-cli 加载（这一步最关键，catch 掉所有转换失败）
kicad-cli sch export netlist <project_id>/schematic.kicad_sch \
  -o /tmp/test.net 2>&1 | grep -qi "error\|失败" && echo FAIL_3

# 4. manifest.json 合法且必填字段齐
python3 -c "
import json, sys
m = json.load(open('<project_id>/manifest.json'))
required = ['project_id', 'source_url', 'license', 'components_used', 'file_checksum_sha256']
missing = [k for k in required if k not in m or not m[k]]
if missing:
    print('FAIL_4 missing:', missing)
    sys.exit(1)
"
```

把每个项目的检查结果写进 BATCH_MANIFEST 或单独一个 `failed/` 子目录归档。**交付前所有 4 个检查都要过**。

---

## 7. 已知坑 & 建议

### 7.1 立创开源的 license 现实

经验数据（不保证准确）：
- 立创项目里**可能 50%+ 是 "CC-BY-SA 4.0"**，这是默认选项 → 我们直接拒
- 大概 20-30% 是 "未声明 license" → 拒
- 10-15% 是 MIT / Apache / CC0 → 要！
- 少量 GPL → 拒

**预期 hit rate**: 你抓 1000 个项目可能只有 150-250 能通过 license 关卡。这是正常的，不要为了数量降低许可标准。

### 7.2 EasyEDA → KiCad 转换质量

`easyeda2kicad.py` 对大部分项目能用，但：
- 自定义 symbol（立创 SZLCSC 专有库）可能转不过来 → 转完 kicad-cli 加载会 fail → 被 §6 check 3 过滤掉
- 多 sheet hierarchy 偶尔乱 → 只保根 sheet 也能用
- footprint 转换常有问题——我们暂时不在乎 footprint（corpus 主要消费 schematic），转不过就空着

### 7.3 描述文字

立创项目标题和描述常常**中英混杂 + markdown 标记 + emoji**。原样保留没关系，我们的 enrich 管线会 strip markdown。但**别主动翻译或改写** —— 原文有最多信息。

### 7.4 同项目多版本

立创有的项目带版本（v1 / v2 / Rev.B）。**不同版本视作不同项目**，分别抓、分别起 project_id。不要合并。

### 7.5 电气图多页

立创项目经常一个 sch 项目分多页（root + mcu_sheet + power_sheet 等）。你遇到时：
- 如果 easyeda2kicad 能转出层级结构 → 主 sheet 存 `schematic.kicad_sch`，子 sheet 存 `sheets/<name>.kicad_sch`
- 如果只导出根 sheet → 根 sheet 存 `schematic.kicad_sch`，在 `manifest.json` 加 `"has_unsaved_subsheets": true` 标记
- 转不出来 → 跳

---

## 8. 交付后我们会做什么（你不用担心这部分）

参考、别去实现：

1. `batch_migrate_oshwhub.py` 扫整个批次，逐个 `board_ingest_from_file` 入 corpus
2. License 白名单再 double-check 一遍（防你漏过）
3. sha256 dedupe（跨批次）
4. `enrich_tags.py` 跑一遍基于 `components_used` + `description` 生成 chip_* / has_* / use_* tag 家族
5. 用 kicad-cli 跑 ERC 取 `source_erc_status`，脏的标记一下但仍入库（用户能看到警告）

---

## 9. 测试批次建议

**第一次交付先给小批 5-10 个项目**，覆盖：
- 3-5 个 MIT/Apache 原生 KiCad
- 2-3 个 CC0/CC-BY-4.0 从 EasyEDA 转的
- 1-2 个你不确定的 edge case（多 sheet、异常元件等）

我们跑一遍 ingest，给你反馈"格式上还要改啥" / "这块数据不够"，再开始量产爬。

---

## 10. 联系点

- 这份文档的源头：`hardware_agent_docs/OSHWHUB_INGEST_SPEC.md`
- 我们的 BoardReference schema 定义：`src/facere/references/schema.py`
- 已有的 ingest 入口：`src/facere/references/board_ingest.py`
- 批量 migrate 脚本参考：`benchmarks/corpus_build/migrate_raw_to_board_refs.py`

有问题直接问我（张大佬）—— 格式细节比速度重要，不清楚的**先对一下再做**。
