# 竞品分析：现有 Python Config/Registry 方案对 layer-registry 的借鉴价值

> **目的**: 分析回复中提到的 5 个方案的源码与设计，提炼对 `layer-registry` 有借鉴价值的模式。
>
> **评估维度**: 注册机制 / 签名反射 / Config 校验 / IDE 补全 / 通用性

---

## 1. 方案概览

| 方案 | 定位 | 注册 | 签名反射 | Config 校验 | IDE 补全 | 通用性 |
|------|------|------|---------|------------|---------|--------|
| **yacs-stubgen** | YACS 的 .pyi 生成器 | ✗ | 值反射 | ✗ | .pyi | 仅 YACS |
| **hydra-zen** | Hydra 的零 YAML 方案 | `store()` | `builds()` | OmegaConf | dataclass | 仅 Hydra |
| **Hydra ConfigStore** | Hydra 全局配置注册 | `store()` | 手写 dataclass | OmegaConf | dataclass | 仅 Hydra |
| **MMEngine Registry** | OpenMMLab 注册表 | 装饰器 | ✗ | build-time | ✗ | mmlab 生态 |
| **jsonargparse** | Lightning 底层 CLI | ✗ | 签名+stub | argparse | CLI | 通用 |
| **layer-registry** (ours) | 通用注册+config typing | metaclass | 签名反射 | Pydantic fail-fast | .py+JSON Schema | 通用 |

---

## 2. 逐个分析

### 2.1 yacs-stubgen

**做了什么**: 为 YACS 的 `CfgNode` 对象生成 `.pyi` stub 文件。在 config 文件末尾调用 `build_pyi()`，每次 import 时自动生成 stub，IDE 就能读到进行类型检查。

**核心机制**:
```python
# 用户在 config.py 末尾加三行
from yacs_stubgen import build_pyi
_C = CfgNode()
_C.TRAIN.BATCH_SIZE = 32
build_pyi(__name__, _C)  # ← 每次 import 时触发
```

- 遍历 `CfgNode.items()` → 用 `type(value).__name__` 获取类型
- 递归处理嵌套 CfgNode → 生成嵌套 class stub
- **纯字符串拼接**生成 `.pyi`（非 AST 操作）
- 包含 PEP 561 `py.typed` 标记

**生成的 stub 长这样**:
```python
# Auto-generated
from yacs.config import CfgNode

class cfg(CfgNode):
    class TRAIN(CfgNode):
        BATCH_SIZE: int
        LR: float
    class MODEL(CfgNode):
        BACKBONE: str
```

**局限**:
- **无缓存/指纹**: 每次 import 都重新生成，有不必要的 IO 开销
- **类型推断原始**: 从实例值推断 (`type(32).__name__` → `"int"`)，无法识别 `Optional`、`Union`、`Annotated`
- **仅适配 YACS**: 不通用
- **无运行时校验**: 只生成 stub，不做 fail-fast 验证

#### 值得借鉴

| 模式 | 细节 | 对 layer-registry 的意义 |
|------|------|------------------------|
| **PEP 561 `py.typed` 标记** | 让 mypy/pyright 自动发现生成的 stub | 生成的 stub 目录应包含 `py.typed` 文件 |
| **字符串拼接生成 stub** | 简单可读，比 AST 操作易调试 | 验证了 `codegen.py` 用字符串模板的正确性 |
| **Module-level 触发** | import 时自动执行 | `discover()` 集成的自动保鲜思路类似，但 layer-registry 加了指纹缓存避免无谓 IO |

#### 应避免

| 问题 | layer-registry 如何规避 |
|------|----------------------|
| 无缓存，每次 import 重新生成 | 指纹比对，相同则跳过（Section 7） |
| 从实例值推断类型 | `get_type_hints(include_extras=True)` 从签名精确提取 |
| 无运行时校验 | Pydantic discriminated union + `load_config()` fail-fast |

---

### 2.2 hydra-zen

**做了什么**: 消除所有手写 YAML 配置。通过 `builds()` 从函数/类签名**自动生成** dataclass 配置，通过 `store()` 提供全局注册表。

**核心机制 — `builds()`**:
```python
from hydra_zen import builds

# 自动从 __init__ 签名生成 dataclass config
DNN_Conf = builds(DNN, populate_full_signature=True)
# 等价于手写:
# @dataclass
# class DNN_Conf:
#     _target_: str = "my_module.DNN"
#     hidden_size: int = 256
#     dropout: float = 0.1
```

**签名反射的关键实现**:
```python
# hydra-zen 的提取逻辑（简化版）
sig = inspect.signature(target.__init__)
hints = get_type_hints(target.__init__, include_extras=True)

for param_name, param in sig.parameters.items():
    if param.kind is param.VAR_KEYWORD:
        continue  # 直接跳过 **kwargs
    if param.kind is param.VAR_POSITIONAL:
        continue  # 直接跳过 *args
    # 提取 type + default → 生成 dataclass field
```

**`populate_full_signature` 特性**:
- 当 `True` 时，遍历完整 MRO 链收集所有 `__init__` 参数（含父类继承的）
- 这是 hydra-zen 的杀手级功能

**`store()` 注册表**:
```python
from hydra_zen import store

# 注册 config
store(DNN_Conf, name="dnn", group="model")

# 装饰器模式
@store(name="adam", group="optimizer")
def adam_config():
    return builds(torch.optim.Adam, lr=0.001)

# 延迟刷入 Hydra ConfigStore
store.add_to_hydra_store()
```

**延迟注册（Deferred Registration）模式**:
- `store()` 先在内部字典积累注册
- `add_to_hydra_store()` 再一次性刷入 Hydra 的 ConfigStore
- 将 "声明" 和 "激活" 分离

**`Builds[T]` 幻影类型**:
```python
DNN_Conf: Type[Builds[DNN]] = builds(DNN)
# IDE 知道 DNN_Conf "关联" DNN 类，可追溯 config→目标类
```

**`**kwargs` 处理**: 直接跳过，不生成字段。比 layer-registry 简单但丢失了 open/closed 语义。

**局限**:
- 深度绑定 Hydra/OmegaConf 生态
- 生成的是内存中的 dataclass，不生成 `.pyi` 文件
- `**kwargs` 被简单忽略，没有 open/closed schema 区分
- OmegaConf 类型系统有限（不支持 Union、复杂泛型）
- 无 discriminated union（用 Hydra config groups 实现多态）

#### 值得借鉴

| 模式 | 细节 | 对 layer-registry 的意义 |
|------|------|------------------------|
| **MRO 感知参数收集** | `populate_full_signature` 走完整继承链 | extractor 应考虑收集继承的 `__init__` 参数，不只看当前类 |
| **延迟注册** | 内部积累 → 显式刷入 | `discover()` 已有类似思路，但可以更显式 |
| **`Builds[T]` 幻影类型** | config 携带目标类类型参数 | `per_key_models` 可携带类似类型关联，增强 IDE 体验 |
| **重度 `@overload`** | `builds()` 返回类型通过 overload 精确推断 | `create_registrar()` 的返回类型可用 overload 增强 |
| **Annotated 解包** | `_unpack_annotated()` 提取基础类型和元数据 | 验证了 Tier 2 提取方法的正确性 |

---

### 2.3 Hydra + OmegaConf Structured Configs

**做了什么**: ML 社区最广泛使用的配置方案。用 dataclass 创建配置对象，OmegaConf 提供运行时类型安全。

**ConfigStore — 全局单例注册表**:
```python
from hydra.core.config_store import ConfigStore

cs = ConfigStore.instance()  # 线程安全单例

cs.store(name="mysql",    node=MySQLConfig,    group="db")
cs.store(name="postgres", node=PostgresConfig, group="db")
```

- **Singleton 模式**: 全局唯一实例，`instance()` 方法获取
- **`group` 命名空间**: 类似 layer-registry 的 "layer" 概念
- **Provider 追踪**: 记录每个 config 来自哪个 provider（方便调试）

**Structured Configs**:
```python
@dataclass
class MySQLConfig:
    driver: str = "mysql"
    host: str = "localhost"
    port: int = 3306
    user: str = MISSING  # 必填字段（类似 Pydantic 的 ...）
```

**Config Group → 多态选择**:
```
conf/
├── config.yaml          # defaults: [{db: mysql}]
└── db/
    ├── mysql.yaml
    └── postgres.yaml
```
```bash
python app.py db=postgres  # CLI 覆盖
```

**与 Pydantic discriminated union 的关键区别**:

| 方面 | Hydra Config Group | layer-registry Discriminated Union |
|------|-------------------|----------------------------------|
| 选择机制 | 文件名/CLI 覆盖 | 配置字段值（`provider: openai`） |
| 类型安全 | 一次一个 config | Union 类型，IDE 自动收窄 |
| IDE 支持 | 看到单个 dataclass | 看到 Union + Literal 收窄 |
| 校验时机 | merge/access 时 | parse 时（`load_config()` 一步到位） |

**校验时机问题**: OmegaConf 的 `MISSING` 只在**访问时**才报错，不是加载时。比 layer-registry 的 fail-fast 晚。

**局限**:
- **用户必须手写 dataclass** — IDE 补全的前提是你已经写了 typed config
- Config Group 是文件系统机制，不是类型系统机制
- `MISSING` 值延迟报错
- OmegaConf 类型系统无法表达 Union、Annotated 约束等

#### 值得借鉴

| 模式 | 细节 | 对 layer-registry 的意义 |
|------|------|------------------------|
| **Provider 追踪** | 每个 stored config 记录来源 | `registry.add()` 可记录注册来源模块路径，`_dump_registry()` 输出 |
| **Struct flag** | `struct=True` 禁止添加未知 key | 映射到 `extra="forbid"`，验证了设计方向 |
| **全局 registrar 查找** | `ConfigStore.instance()` 全局访问 | 考虑增加 `get_registrar("llm")` 全局查找 API（插件生态有用） |

#### 本质差异

```
Hydra 思路（Config-First）:
  手写 dataclass → Hydra 加载 → OmegaConf 校验 → 应用消费

layer-registry 思路（Code-First）:
  写 __init__ → 自动提取 schema → 生成 stub → IDE 补全 + 运行时校验
```

**Hydra 的 IDE 补全是因为用户手写了 dataclass；layer-registry 的创新是自动提取等效类型信息，消除手写步骤。**

---

### 2.4 MMEngine Registry + Config

**做了什么**: OpenMMLab 系列的基础设施。`Registry` 存储 key→class 映射，`Config` 类读取配置文件，`build()` 方法实例化。

**Registry 核心 API**:
```python
MODELS = Registry('models', locations=['mmengine.models'])

# 装饰器注册
@MODELS.register_module()
class ResNet:
    def __init__(self, depth=50): ...

# 或手动注册
MODELS.register_module(name='my_resnet', module=ResNet)

# 构建实例
cfg = dict(type='ResNet', depth=50)
model = MODELS.build(cfg)  # → ResNet(depth=50)
```

**关键设计细节**:

1. **Auto name**: `name=None` 时用 `module.__name__`（原样，不做 snake_case 转换）
2. **`force` 参数**: `force=True` 允许覆盖已有注册（默认 False 抛 KeyError）
3. **硬编码 `type` 作为 discriminator**: 不可配置
4. **Lazy import**: `locations` 参数 + 首次访问时 import

**分层作用域**:
```python
# 根注册表
MODELS = Registry('models', locations=['mmengine.models'])

# 子注册表（mmdet 级别）
MODELS = Registry('models', parent=mmengine_MODELS, scope='mmdet')
```

- 查找 `'mmdet.ResNet'` → 按 `.` 分割 → 导航 parent→child 链
- 未找到 → 回退到根注册表

**Config 类**: 支持 .py / .json / .yaml，有 `_base_` 继承机制。但 **Config 不做类型校验** — 校验只在 `build()` 调用 `__init__` 时才发生。

**这正是 layer-registry 要解决的核心痛点**: MMEngine 用户写完 config → 启动 → 加载模型/数据/环境 → 跑了 N 步 → 才发现 config 里参数名拼错了。

**局限**:
- **无 typing/IDE 支持** — config 是 untyped dict
- **build-time 才校验** — 太晚了
- **装饰器样板** — 每个实现都要 `@register_module()`
- **忘记注册** — 新增实现后最常见的 bug

#### 值得借鉴

| 模式 | 细节 | 对 layer-registry 的意义 |
|------|------|------------------------|
| **分层作用域** | parent/child registry 层级 | 多项目生态的未来扩展方向 |
| **Lazy import + `locations`** | 首次访问时自动 import | 作为 `discover()` 的可选替代方案 |
| **`force` 参数** | 允许覆盖注册 | 测试场景有用，可加 `force` 到 `registry.add()` |
| **多名称注册** | `register_module(name=['resnet', 'ResNet'])` | 可支持别名（alias） |
| **`build()` 集成** | Registry 直接实例化 | 考虑可选的 `registrar.build(key, **kwargs)` 便捷方法 |

#### 应避免

| 问题 | 细节 |
|------|------|
| 无 typing 支持 | OpenMMLab 生态的 #1 痛点 |
| 硬编码 `type` discriminator | 不可配置 |
| Config 是可执行 Python | 安全隐患，无法静态校验 |
| 延迟校验 | build-time 才报错 |
| 可变注册表 + `force=True` | 线程不安全，允许静默覆盖 |

---

### 2.5 jsonargparse（PyTorch Lightning 底层）

**做了什么**: 从类型注解自动创建 CLI，支持嵌套类型、泛型、restricted types。能从 `.pyi` stub 文件读取类型信息。

**签名提取 — `_parameter_resolvers.py`**:

jsonargparse 的参数提取是所有方案中最成熟的：

```python
# 关键数据结构
@dataclass
class ParamData:
    name: str
    annotation: Any        # 类型标注
    default: Any           # 默认值
    kind: ParameterKind    # POSITIONAL_ONLY, KEYWORD_ONLY, etc.
    component: Any         # 来源类/函数
    origin: str            # "class"/"method"/"function"
    doc: str               # 参数描述（从 docstring 提取！）
```

**三个独特能力**:

**能力 1: MRO 遍历**
```python
# 走完整 MRO 收集所有 __init__ 参数（含父类继承的）
for parent in inspect.getmro(cls):
    if '__init__' in parent.__dict__:
        sig = inspect.signature(parent.__dict__['__init__'])
        for name, param in sig.parameters.items():
            if name not in all_params:  # 首次定义优先
                all_params[name] = param
```

**能力 2: Docstring 参数描述提取**
```python
class ChatOpenAI:
    """OpenAI LLM provider.

    Args:
        model_id: 模型 ID，如 gpt-4o
        temperature: 生成温度，0-2 之间
    """
    def __init__(self, *, model_id: str, temperature: float = 0.0): ...

# jsonargparse 自动提取 "模型 ID，如 gpt-4o" 作为 model_id 的 description
# 无需 Annotated[str, Field(description="...")]
```

这是**独一无二的能力** — 其他所有方案都不从 docstring 提取参数描述。

**能力 3: AST 级 `**kwargs` 追踪**
```python
# jsonargparse 用 AST 分析追踪 **kwargs 的转发目标
class Child(Parent):
    def __init__(self, extra_param: int, **kwargs):
        super().__init__(**kwargs)  # ← AST 检测到转发给 Parent

# jsonargparse 会递归获取 Parent.__init__ 的参数，合并进来
```

**Stub 文件读取 — `_stubs_resolver.py`**:
```python
# 查找顺序:
# 1. 同目录 .pyi 文件 (foo.pyi next to foo.py)
# 2. Stub 包 (foo-stubs/foo.pyi)
# 3. typeshed (内建类型 stub)

# 用 ast.parse() 解析，无需 import stub 文件
tree = ast.parse(stub_source)
for node in ast.walk(tree):
    if isinstance(node, ast.ClassDef):
        # 提取字段类型标注
```

主要用于**无内联类型标注的 C 扩展模块和第三方库**。

**类型解析 — `_typehints.py`** (~3000+ 行):
- `Union[A, B]` → 为每个类型创建子 parser
- `Optional[T]` → 正确处理 None 默认值
- `Literal["a", "b"]` → 枚举选择
- `Annotated[T, metadata]` → 提取验证元数据
- Forward references → 延迟解析
- Pydantic BaseModel 检测 → 委托给 Pydantic 校验

**局限**:
- 22k+ 行，过度工程化
- 以 CLI 为中心，不适合 config-object 模式
- 无注册表概念
- 单体 `_typehints.py` 处理所有边界情况

#### 值得借鉴

| 模式 | 细节 | 对 layer-registry 的意义 |
|------|------|------------------------|
| **Docstring 参数描述提取** | 从 Google/NumPy/reST 格式 docstring 提取 `Args:` 描述 | **高价值** — 作为 "Tier 1.5" 路径，无需 Annotated 即可获得描述 |
| **MRO 感知参数收集** | 遍历完整继承链 | 与 hydra-zen 相互验证，extractor 应支持 |
| **AST 级 `**kwargs` 追踪** | 追踪 kwargs 转发到父类 | 更精准的 open/closed 判定（中等价值） |
| **Stub 文件查找顺序** | .pyi → stubs 包 → typeshed | 定义生成 stub 的标准位置 |
| **Pydantic BaseModel 参数检测** | `issubclass(param_type, BaseModel)` | 验证了 Tier 3 单参数 BaseModel 识别方法 |

---

## 3. 横向对比矩阵

### 3.1 注册机制

| 方案 | 机制 | 自动化程度 | 外部类支持 |
|------|------|-----------|-----------|
| yacs-stubgen | 无注册 | — | — |
| hydra-zen | `store()` 显式注册 | 手动 | 通过 `builds()` 包装 |
| Hydra ConfigStore | `store()` 显式注册 | 手动 | 手动 store |
| MMEngine | `@register_module()` 装饰器 | 半自动 | 装饰器包装 |
| jsonargparse | 无注册 | — | — |
| **layer-registry** | **metaclass 继承** | **全自动** | **`bridge()` + `register()`** |

### 3.2 Config Schema 来源

| 方案 | Schema 来源 | 实现者负担 |
|------|-----------|-----------|
| yacs-stubgen | 运行时实例值 | 零（但精度低） |
| hydra-zen | `__init__` 签名反射 | 零 |
| Hydra | 手写 dataclass | **高** — 必须维护 dataclass |
| MMEngine | 无 schema | — |
| jsonargparse | `__init__` 签名 + docstring | 零 |
| **layer-registry** | **`__init__` 签名 (3 tier)** | **零 (Tier 1) → 极低 (Tier 2)** |

### 3.3 校验时机

| 方案 | 校验时机 | fail-fast? |
|------|---------|-----------|
| yacs-stubgen | 无校验 | ✗ |
| hydra-zen | OmegaConf merge/access 时 | 部分 |
| Hydra | access 时（MISSING 延迟报错） | ✗ |
| MMEngine | `build()` 时（实例化 `__init__`） | ✗ |
| jsonargparse | CLI parse 时 | ✓ (仅 CLI) |
| **layer-registry** | **`load_config()` 入口处** | **✓ 完全 fail-fast** |

### 3.4 IDE 补全

| 方案 | Python IDE | YAML 编辑器 | 机制 |
|------|-----------|------------|------|
| yacs-stubgen | ✓ (.pyi) | ✗ | 生成 .pyi |
| hydra-zen | ✓ (dataclass) | ✗ | 内存中 dataclass |
| Hydra | ✓ (dataclass) | ✗ | 手写 dataclass |
| MMEngine | ✗ | ✗ | 无 |
| jsonargparse | ✗ | ✗ | CLI 用 |
| **layer-registry** | **✓ (.py stub)** | **✓ (JSON Schema)** | **双输出生成** |

---

## 4. 分级建议

### 4.1 高价值（建议直接采纳）

#### P0: Docstring 参数描述提取

**来源**: jsonargparse 独有

**现状**: layer-registry 的 Tier 1 路径只能提取 `type + default`，无描述信息。只有 Tier 2 通过 `Annotated[T, Field(description=...)]` 才有描述。

**建议**: 增加 "Tier 1.5" — 当无 `Annotated` 时，从 docstring 的 `Args:` 段落提取参数描述。

```python
# 优先级: Annotated[..., Field(description=...)] > docstring > 无
class ChatOpenAI(ChatBaseModel):
    """OpenAI provider.

    Args:
        model_id: 模型 ID，如 gpt-4o
        temperature: 生成温度
    """
    def __init__(self, *, model_id: str, temperature: float = 0.0): ...

# 提取结果:
# model_id: str, description="模型 ID，如 gpt-4o"  ← 来自 docstring
# temperature: float = 0.0, description="生成温度"  ← 来自 docstring
```

**实现**: 可选依赖 `docstring_parser` 库（支持 Google/NumPy/reST 格式）。

**价值**: Tier 1 用户无需任何额外代码就能在 YAML 补全中看到参数描述。大幅降低 Tier 1 → Tier 2 的差距。

#### P0: MRO 感知参数收集

**来源**: hydra-zen (`populate_full_signature`) + jsonargparse（MRO 遍历）

**现状**: extractor 只看 `cls.__init__` 自身签名。如果子类没有重写 `__init__`，会遗漏从父类继承的参数。

**建议**: `extract_config_schema()` 应使用 `get_type_hints(cls.__init__, include_extras=True)` — 这个函数**天然走 MRO**。但如果子类没有自己的 `__init__`，需要沿 MRO 找到定义 `__init__` 的那个类。

```python
# 找到实际定义 __init__ 的类
for klass in inspect.getmro(cls):
    if '__init__' in klass.__dict__:
        actual_init_owner = klass
        break

sig = inspect.signature(actual_init_owner.__init__)
hints = get_type_hints(actual_init_owner.__init__, include_extras=True)
```

#### P1: PEP 561 `py.typed` 标记

**来源**: yacs-stubgen

**建议**: 在生成的 stub 输出目录中包含空的 `py.typed` 文件，让 mypy/pyright 自动识别生成的类型信息。

#### P1: 注册来源追踪

**来源**: Hydra ConfigStore 的 provider 字段

**建议**: `registry.add()` 可选记录 `cls.__module__` 和注册路径，`_dump_registry()` 输出时显示。方便调试 "这个 key 是哪个模块注册的？"。

```
Registry: agent
Entries (3):
  browser_use → BrowserUseAgent  [from: agents.browser_use]
  skyvern    → SkyvernAgent     [from: agents.skyvern]
  my_custom  → MyCustomAgent    [from: user_project.agents]
```

### 4.2 中等价值（可作为扩展方向）

| 模式 | 来源 | 说明 | 优先级 |
|------|------|------|--------|
| **AST 级 `**kwargs` 追踪** | jsonargparse | 追踪 kwargs 转发链 → 更精准 open/closed | P2 |
| **分层作用域** | MMEngine | parent/child registry 支持多项目生态 | P3 |
| **`Builds[T]` 幻影类型** | hydra-zen | config 类型携带目标类信息 | P2 |
| **别名注册** | MMEngine | 一个类多个 key | P3 |
| **全局 registrar 查找** | Hydra ConfigStore | `get_registrar("llm")` | P3 |
| **`force` 参数** | MMEngine | 测试时允许覆盖注册 | P2 |
| **Lazy import** | MMEngine | `locations` + 首次访问时 import | P3 |
| **`@overload` 类型推断** | hydra-zen | `create_registrar()` 返回类型精确推断 | P2 |

### 4.3 验证了现有设计正确性

| 设计决策 | 被谁验证 | 细节 |
|---------|---------|------|
| `__init__` 签名即配置源 | hydra-zen, jsonargparse | 两者都这么做，是成熟模式 |
| 字符串模板生成 stub | yacs-stubgen | 简单有效，无需 AST |
| `**kwargs` → open/closed | hydra-zen (跳过), jsonargparse (追踪) | layer-registry 的语义最丰富（检测+策略映射） |
| Pydantic > OmegaConf | Hydra 的限制 | OmegaConf 无法表达 Union、Annotated 约束 |
| fail-fast 校验 | MMEngine, Hydra 的痛点 | 两者都是延迟校验 — 正是 layer-registry 解决的问题 |
| 显式 `discover()` | MMEngine lazy import 的调试困难 | 显式比隐式好，错误在启动时暴露 |
| `include_extras=True` | hydra-zen, jsonargparse | 必须保留 Annotated 元数据 |
| Discriminated union | Hydra config group 的局限 | 字段级 discriminator 比文件级选择更类型安全 |

---

## 5. layer-registry 的独特定位

**没有任何现有方案同时做到这三点**:

```
                        注册自动化    签名→Config    fail-fast 校验
                        ──────────   ──────────    ─────────────
yacs-stubgen               ✗             △              ✗
hydra-zen                  △             ✓              △
Hydra ConfigStore          △             ✗              △
MMEngine                   △             ✗              ✗
jsonargparse               ✗             ✓              △ (仅CLI)

layer-registry             ✓             ✓              ✓
```

**layer-registry 的核心差异化**:

1. **继承即注册** — metaclass 自动化，零样板（hydra-zen 和 MMEngine 都需要显式调用）
2. **`__init__` 签名 → Pydantic typed config** — 自动提取，实现者零负担
3. **Pydantic fail-fast 校验** — `load_config()` 一步到位拒绝非法配置
4. **双输出** — Python stub (.py) + JSON Schema (.schema.json)，同时覆盖 Python IDE 和 YAML 编辑器
5. **通用** — 不绑定任何特定生态（Hydra/OmegaConf/YACS/mmlab）

---

## 6. 建议的 Design Doc 更新

基于以上分析，建议对设计文档做以下补充：

### 6.1 `config-typing-design.md` 补充

**Section 3 新增 Tier 1.5**:
```
Tier 1:   纯 __init__ 签名         → type + default
Tier 1.5: __init__ + docstring      → type + default + description (from docstring)
Tier 2:   __init__ + Annotated      → type + default + description + 约束
Tier 3:   外部 Config 类            → 完全控制
```

**Section 4 extractor 补充**:
- MRO 遍历逻辑（找到实际定义 `__init__` 的类）
- docstring 提取优先级
- PEP 561 `py.typed` 输出

### 6.2 `registry-auto-registration.md` 补充

**Section 5.1 registry.py 补充**:
- `add()` 可选记录 `source_module` 参数
- `_dump_registry()` 输出注册来源

**Section 10 设计决策补充**:
- 决策 24: Docstring 作为 description 的 fallback 来源
- 决策 25: MRO 感知参数收集（与 hydra-zen/jsonargparse 对齐）
- 决策 26: PEP 561 `py.typed` 标记
