# Layer-Specific Auto-Registry 设计文档

> **定位**: `layer-registry` 是一个通用 PyPI 包，为任意 Python 项目提供 layer 级自动注册机制。
>
> **核心哲学**: **继承即注册** — 类的继承关系本身就是 "我是这个 layer 的一个实现" 的声明，不需要任何额外的注册步骤。
>
> **相关文档**:
> - [config-typing-design.md](./config-typing-design.md) — Config typing 自动生成（同级子包）
> - [specific_benchmark.md](./specific_benchmark.md) — browseruse-bench 消费者应用示例

---

## 1. 设计灵魂

### 1.1 一句话定位

`layer-registry` 让 Python 分层架构中的 "注册" 消失 — 开发者只需要写类、写继承，注册自动发生。

### 1.2 核心哲学：继承即注册

当你写 `class FooAgent(BaseAgent)` 时，Python 继承已经声明了两件事：

1. `FooAgent` **是** Agent layer 的一个实现
2. `FooAgent` **满足** Agent layer 的接口约束（继承保证）

既然继承本身已经包含 "我是谁" 和 "我能做什么" 的全部信息，为什么还要手动 `registry["foo"] = FooAgent`？

**这个包的存在意义就是消除这个冗余步骤。**

这个哲学不仅适用于框架内建的实现类，也适用于外部用户通过继承带来的扩展类。任何人，只要继承了正确的基类，他的实现就自动可用 — 不需要理解注册机制，不需要阅读框架文档中 "如何注册你的 agent" 章节。

### 1.3 目标用户

本包面向两类用户，他们处于同一个生态的上下游：

**用户 A：框架开发者**

构建可扩展的分层 Python 应用的开发者。典型场景：

- **Benchmark 框架** — 评测多个 agent/LLM/browser provider 的框架（如 browseruse-bench）
- **Agent 框架** — 集成多种 LLM、工具、浏览器后端的 agent 框架
- **插件系统** — 支持第三方扩展的应用

痛点：每个 layer 都需要 registry + factory + 接口校验，N 个 layer 意味着 N 份重复样板。新增实现后忘记注册是常见 bug。

**用户 B：框架用户**

使用上述框架、通过 PyPI 生态扩展框架能力的开发者。典型场景：

- 安装第三方 agent 包（`pip install some-cool-agent`）
- import 该 agent 类，继承并 override 某些方法
- 希望修改后的 agent **立即可用于框架**，无需了解注册机制

痛点：想评测自己修改过的 agent，却需要理解框架的注册系统才能让它生效。

**设计优先级**：用户 B 的体验是第一优先。框架开发者（用户 A）只需要一次性配置；框架用户（用户 B）的每一次扩展都应该零摩擦。

### 1.4 目标场景：端到端故事

以 benchmark 框架为例，展示从框架搭建到用户扩展的完整故事：

```
框架开发者 (Alice)                              框架用户 (Bob)
════════════════                              ════════════════

1. Alice 定义 Agent layer:                    5. Bob 安装第三方 agent:
   AgentRegistrar = create_registrar(            pip install cool-agent
     "agent", AgentProtocol, ...)

2. Alice 创建基类:                             6a. 外部 agent 继承了 BaseAgent:
   class BaseAgent(                               from cool_agent import CoolAgent
     metaclass=AgentRegistrar.Meta):               class MyCool(CoolAgent):  # 继承链上有 metaclass
     __abstract__ = True                               ...  # → 自动注册 ✓

3. Alice 实现内建 agents:                      6b. 外部 agent 未继承 BaseAgent:
   class BrowserUseAgent(BaseAgent):               from ext_agent import ExtAgent
     ...  # → 自动注册为 "browser_use"              ExtBridge = AgentRegistrar.bridge(ExtAgent)
                                                   class MyExt(ExtBridge):
4. Alice 发布框架:                                     ...  # → 自动注册 ✓
   pip install alice-bench

                                               7. Bob 评测多个变体:
                                                  class VariantA(ExtBridge):
                                                      ...  # → "variant_a" ✓
                                                  class VariantB(ExtBridge):
                                                      ...  # → "variant_b" ✓

                                               8. Bob 运行评测:
                                                  alice-bench run --agent variant_a
                                                  alice-bench run --agent variant_b
```

**关键设计目标**：Bob 在第 6-7 步中，**不需要理解注册机制**。他只需要知道 "继承 BaseAgent 或其桥接类 = 自动可用于框架"。

### 1.5 场景深度分析：外部 Agent 集成

对于 benchmark 框架，框架用户集成外部 agent 是**最高频的场景**。被评测的 agents（browser-use、skyvern、agent-tars 等）都是独立框架，不会依赖 benchmark 包。

根据外部 agent 是否继承了框架基类，分两种情况：

**情况 1：外部 agent 继承了框架基类（理想路径）**

当第三方包选择依赖 benchmark 框架时：

```python
# 第三方包 cool-agent (依赖 alice-bench)
from alice_bench.agents import BaseAgent

class CoolAgent(BaseAgent):  # metaclass 继承 → 自动注册 ✓
    async def step(self, ...): ...
```

此时用户子类天然获得 metaclass：

```python
from cool_agent import CoolAgent

class MyCoolV1(CoolAgent):  # metaclass 继承 → 自动注册 ✓
    async def step(self, ...): ...  # override

class MyCoolV2(CoolAgent):  # metaclass 继承 → 自动注册 ✓
    async def step(self, ...): ...  # 不同修改
```

**零额外步骤，完美符合 "继承即注册"。**

**情况 2：外部 agent 未继承框架基类（常见路径）**

当第三方包是独立框架、不依赖 benchmark 包时：

```python
# 独立框架 ext-agent (不依赖 alice-bench)
class ExtAgent:  # 无 metaclass
    async def step(self, ...): ...
```

此时用户需要一次性 "桥接"，之后所有继承自动生效：

```python
from ext_agent import ExtAgent
from alice_bench.agents import AgentRegistrar

# 一次性桥接（一行代码）
ExtBridge = AgentRegistrar.bridge(ExtAgent)

# 之后 "继承即注册" 恢复工作
class MyExtV1(ExtBridge):  # → 自动注册 ✓
    async def step(self, ...): ...

class MyExtV2(ExtBridge):  # → 自动注册 ✓
    async def step(self, ...): ...
```

**一次桥接，N 次自动注册。** 用户不需要理解 metaclass 或 registry 内部机制，只需要知道 "先桥接、再继承"。

### 1.6 设计原则

| 原则 | 含义 |
|------|------|
| **零注册心智负担** | 框架用户不需要理解注册机制，继承即可用 |
| **继承即注册为主路径** | metaclass 继承（含桥接）覆盖 90% 场景，手动注册是 10% 逃生舱 |
| **领域无关** | 包不预设任何领域概念（agent、llm、provider），纯通用基础设施 |
| **显式优于隐式** | `discover()` 显式触发，`__abstract__=True` 显式标记，`__registry_key__` 显式覆盖 |
| **fail-fast** | key 重复抛异常，Protocol 不满足立即报错，不允许静默覆盖 |
| **最小侵入** | 对消费者代码的侵入最小化 — 一个 metaclass 参数 + 一行 discover 调用 |

---

## 2. 问题与动机

当一个 Python 项目按 "分层架构" 组织时（如 Agent Layer、Provider Layer、Plugin Layer），每个 layer 通常需要：

1. **注册表** — 存储 key → 实现类的映射
2. **工厂** — 根据配置字符串查找并实例化具体实现
3. **接口约束** — 确保注册的类满足该 layer 的接口契约

如果每个 layer 各自手写 registry + factory + match/case，会导致：

- **大量重复代码** — N 个 layer × (注册/查询/校验) = 冗余样板
- **注册遗漏** — 新增实现后忘记手动添加到 factory
- **接口不一致** — 各 layer 的注册/查询 API 风格不统一
- **外部扩展困难** — 第三方插件无法安全注册到已有注册表
- **用户扩展壁垒** — 框架用户想添加自己的实现，却需要深入理解注册系统

本包设计一套统一的注册基础设施，解决以上问题。

**核心洞察**：继承关系本身已经是最好的 "注册声明"，我们只需要在类创建时自动捕获它。

---

## 3. 设计概览

### 3.1 包结构

```
layer_registry/                  PyPI 包根
├── __init__.py                  统一导出（registration + config 的公共 API）
│
├── registration/                自动注册子包
│   ├── __init__.py              导出注册 API
│   ├── key_transform.py         KeyTransform 协议 + 内建实现 + infer_key()
│   ├── registry.py              LayerRegistry[P] 泛型注册表
│   ├── auto.py                  create_auto_registrar() 元类工厂
│   ├── registrar.py             LayerRegistrar[P] + create_registrar() 一键工厂
│   └── discover.py              discover() 模块发现
│
├── config/                      Config typing 生成子包（同级，见 config-typing-design.md）
│   └── ...
│
└── cli.py                       CLI 入口
```

### 3.2 组件关系

```
                layer_registry/
                      │
           ┌──────────┴───────────┐
           │                      │
     registration/              config/
           │                      │
    ┌──────┼────────┐      ┌─────┼──────────┐
    │      │        │      │     │          │
Registry Auto  KeyTransform  Extractor Builder Codegen
    │      │        │
    │  ┌───┘   ┌────┘
    ▼  ▼       ▼
  LayerRegistrar[P]
    │
    ├─ create_registrar() ← 一键工厂（推荐入口）
    │
    ├─ .Meta              → metaclass for 基类
    ├─ .get(key)          → 查询
    ├─ .bridge()          → 外部类桥接（路径 B）
    ├─ .register()        → 手动注册装饰器（路径 C）
    └─ .build_config()    → config union (调用 config/ 子包)
```

### 3.3 三条注册路径

本包提供三条注册路径，覆盖从零心智负担到完全控制的全部场景：

```
路径 A: 继承注册 — 框架内建实现的主路径
═══════════════════════════════════════════════════════

class ConcreteImpl(BaseLayer):           ← 继承基类（基类使用 AutoRegistrar metaclass）
         │
    AutoRegistrar.__new__()              ← metaclass 自动拦截
         │
    key_transform(class_name)            ← 推断 key（可配置策略）
         │
    registry.add(key, cls, protocol_check=False)  ← 继承即合规，免 Protocol 校验
         │
    registry["concrete_impl"] = ConcreteImpl ✓

    适用场景: 框架开发者的内建实现；任何继承了框架基类的类
    用户体验: 零额外代码，写类即注册


路径 B: 桥接注册 — 外部类族的推荐路径
═══════════════════════════════════════════════════════

# Step 1: 一次性创建桥接基类
ExtBridge = MyRegistrar.bridge(ExternalClass)
# 等价于:
# class ExtBridge(ExternalClass, metaclass=MyRegistrar.Meta):
#     __abstract__ = True

# Step 2: 继承即注册（与路径 A 完全相同的机制）
class VariantA(ExtBridge):               ← metaclass 继承生效
    def required_method(self): ...       → 自动注册为 "variant_a"

class VariantB(ExtBridge):               ← 同上
    def required_method(self): ...       → 自动注册为 "variant_b"

    适用场景: 外部 agent/plugin 不继承框架基类，用户需要创建多个变体
    用户体验: 一次桥接（一行），之后 N 次继承即注册


路径 C: 显式注册 — 单个外部类的逃生舱
═══════════════════════════════════════════════════════

@MyRegistrar.register("custom")          ← 不继承基类，手动注册
class CustomImpl:
    def required_method(self): ...
         │
    registry.runtime_check(cls)          ← issubclass(cls, Protocol)
         │
    ┌────┴────────────┐
    │                 │
  满足 Protocol ✓   缺方法 → TypeError (列出缺失的方法名)
    │
    registry["custom"] = CustomImpl ✓

    适用场景: 单个外部类一次性集成，不需要多个变体
    用户体验: 一行装饰器
```

**路径选择指南**:

| 场景 | 推荐路径 | 理由 |
|------|---------|------|
| 框架内建实现 | **A** | 天然继承基类，零额外代码 |
| 外部 agent + 需要多个变体 | **B** | 一次桥接，N 次继承即注册 |
| 单个外部类一次性集成 | **C** | 一行装饰器搞定 |
| 外部类 + metaclass 冲突 | **C** (propagate=True) | 不涉及 metaclass，无冲突风险 |

**路径 A 和 B 共享同一个底层机制（AutoRegistrar metaclass）**。路径 B 只是在外部类和 metaclass 之间加了一层桥接。路径 C 使用独立的 `__init_subclass__` 注入机制（当 propagate=True 时）或完全不传播（默认）。

---

## 4. Key 推断策略 — `registration/key_transform.py`

### 4.1 设计变更动机

原始设计中 `infer_key()` 硬编码了特定领域的后缀/前缀列表（如 `"Agent"`, `"Provider"`, `"Chat"`），这使得通用包耦合了消费者的命名约定。

新设计将 key 推断策略**参数化**，由消费者在创建 registrar 时配置。

### 4.2 KeyTransform 协议

```python
@runtime_checkable
class KeyTransform(Protocol):
    """可调用协议：类名 → 注册 key。

    所有 key 推断函数（内建和自定义）都必须满足此签名。
    """
    def __call__(self, class_name: str) -> str: ...
```

**职责**: 将 `CamelCase` 类名转换为注册表使用的 `snake_case` key。

### 4.3 内部工具函数 `_camel_to_snake`

```python
def _camel_to_snake(name: str) -> str:
    """将 CamelCase 转换为 snake_case。纯字符串操作，不做任何剥离。

    Args:
        name: CamelCase 字符串

    Returns:
        snake_case 字符串

    实现思路:
        两步正则替换，覆盖所有 CamelCase 边界：
        1. r'([A-Z]+)([A-Z][a-z])' → 处理连续大写后跟小写
           "HTTPSHandler" → "HTTPS_Handler"
        2. r'([a-z\d])([A-Z])' → 处理小写/数字后跟大写
           "HTTPS_Handler" → "HTTPS_Handler" (无变化)
           "browserUse"    → "browser_Use"
        3. .lower()

    Examples:
        "BrowserUseAgent" → "browser_use_agent"
        "ChatOpenAI"      → "chat_open_ai"
        "HTTPSHandler"    → "https_handler"
        "DOM"             → "dom"
        "MyV2Agent"       → "my_v2_agent"
    """
```

### 4.4 `default_key_transform`

```python
def default_key_transform(class_name: str) -> str:
    """默认 key 推断：纯 CamelCase → snake_case，不做任何剥离。

    Args:
        class_name: 类名

    Returns:
        snake_case key

    实现: 直接委托 _camel_to_snake(class_name)。

    这是 create_registrar() 未指定任何推断参数时的默认策略。
    """
```

### 4.5 `make_key_transform`

```python
def make_key_transform(
    *,
    suffixes: list[str] | None = None,
    prefixes: list[str] | None = None,
) -> KeyTransform:
    """工厂函数：创建一个带后缀/前缀剥离的 KeyTransform。

    Args:
        suffixes: 要从类名末尾剥离的后缀列表。
            按列表顺序尝试，匹配第一个即停止。
            例: ["Agent", "Handler"] → "BrowserUseAgent" 先匹配 "Agent" → 剥离
        prefixes: 要从类名开头剥离的前缀列表。
            按列表顺序尝试，匹配第一个即停止。
            例: ["Base", "Abstract"] → "BaseHandler" 先匹配 "Base" → 剥离

    Returns:
        满足 KeyTransform 协议的可调用对象

    实现思路:
        返回一个闭包，闭包内部逻辑：
        1. 遍历 suffixes，若 class_name.endswith(suffix) 且剥离后非空 → 剥离
        2. 遍历 prefixes，若 class_name.startswith(prefix) 且剥离后非空 → 剥离
        3. 对剥离后的结果调用 _camel_to_snake()
        注意：后缀和前缀剥离**同时生效**（先后缀后前缀），不是互斥的。
        注意：剥离后为空字符串时**不剥离**（防止 class_name == suffix 的情况）。

    Examples:
        make_key_transform(suffixes=["Agent"])
          "BrowserUseAgent" → "browser_use"
          "Agent"           → "agent" (不剥离，因为剥离后为空)

        make_key_transform(suffixes=["Agent"], prefixes=["Base"])
          "BaseBrowserUseAgent" → "browser_use"
          "BaseAgent"           → "agent" (先去 Agent → "Base"，再去 Base → 空，不去 Base)
    """
```

### 4.6 三种配置方式

```python
# 方式 1: 使用默认策略（纯 snake_case，不剥离）
R = create_registrar("plugin", PluginProtocol)
# MyPlugin → "my_plugin"

# 方式 2: 指定要剥离的后缀/前缀（内建 make_key_transform）
R = create_registrar("agent", AgentProtocol,
                     strip_suffixes=["Agent"],
                     strip_prefixes=["Base"])
# BrowserUseAgent → "browser_use"
# BaseAgent       → "agent" (但基类通常 __abstract__=True 不会注册)

# 方式 3: 完全自定义 key_transform（覆盖一切）
R = create_registrar("llm", LLMProtocol,
                     key_transform=lambda name: name.lower().replace("chat", ""))
# ChatOpenAI → "openai"
```

优先级：`key_transform` > `strip_suffixes/strip_prefixes` > `default_key_transform`。

### 4.7 `__registry_key__` 始终可覆盖

无论使用哪种推断策略，类上显式定义 `__registry_key__` 始终优先：

```python
class ChatAzureOpenAI(ChatBaseModel):
    __registry_key__ = "azure"   # 覆盖任何推断结果
```

这是 20% 场景的逃生舱，处理推断结果不理想的情况。

**注意**：`__registry_key__` 的检测发生在 `AutoRegistrar.__new__` 和 `_inject_auto_registration` 中，检查的是 `cls.__dict__`（而非 `getattr`），避免从父类继承 key。

---

## 5. 核心组件职责

### 5.1 `registration/registry.py` — LayerRegistry[P]

**职责**: 泛型注册表，每个 layer 实例化一个。是所有注册操作的底层存储。纯粹的 key→class 映射，不涉及注册路径（metaclass/bridge/手动）的逻辑。

#### 类定义

```python
class LayerRegistry(Generic[P]):
    """泛型注册表。P 是 @runtime_checkable Protocol 类型参数。

    每个 layer 实例化一个 LayerRegistry。它是底层存储，不感知
    注册路径（metaclass/bridge/手动），只负责 key→class 映射和 Protocol 校验。
    """
```

#### 内部状态

| 属性 | 类型 | 说明 |
|------|------|------|
| `name` | `str` | layer 名称，用于错误信息（如 `"agent"`、`"llm"`） |
| `protocol` | `type[P]` | `@runtime_checkable Protocol`，定义该 layer 的接口契约 |
| `_store` | `dict[str, type]` | 强引用存储，key→class 映射。注册的类不会被 GC |
| `_lock` | `threading.Lock` | 线程安全锁，保护 `_store` 的并发读写 |
| `_check_cache` | `WeakSet` | Protocol check **正缓存**：已确认满足 Protocol 的类 |
| `_check_negative_cache` | `WeakSet` | Protocol check **负缓存**：已确认不满足 Protocol 的类 |
| `_invalidation_counter` | `int` | 类属性（全局）。每次 `remove()` 时递增，使所有负缓存失效 |
| `_negative_cache_version` | `int` | 实例属性。与全局计数器比较，判断本实例负缓存是否过期 |

#### `__init__`

```python
def __init__(self, name: str, protocol: type[P]) -> None:
    """初始化注册表。

    Args:
        name: layer 名称，仅用于错误信息和调试输出。
        protocol: @runtime_checkable Protocol 子类。
            用于路径 C 手动注册时的 runtime_check。

    Raises:
        TypeError: protocol 不是 @runtime_checkable Protocol。

    实现思路:
        1. 校验 protocol 是否标记了 @runtime_checkable
           (检查 getattr(protocol, '_is_runtime_protocol', False))
        2. 初始化 _store = {}
        3. 初始化 _lock = threading.Lock()
        4. 初始化缓存: _check_cache = WeakSet(), _check_negative_cache = WeakSet()
        5. _negative_cache_version = type(self)._invalidation_counter
    """
```

#### `runtime_check`

```python
def runtime_check(self, cls: type) -> None:
    """检查 cls 是否满足本 layer 的 Protocol 契约。

    Args:
        cls: 要校验的类

    Raises:
        TypeError: cls 未满足 Protocol，错误信息列出所有缺失的方法名。

    实现思路:
        1. 正缓存命中 → 直接返回
        2. 检查负缓存版本是否过期（与全局计数器比较）
           过期 → 清空负缓存，更新版本号
        3. 负缓存命中 → 抛 TypeError（使用缓存的错误信息）
        4. 缓存未命中 → 执行实际检查:
           a. 获取 Protocol 要求的方法集合:
              protocol_methods = {
                  name for name in dir(self.protocol)
                  if not name.startswith('_')
                  and callable(getattr(self.protocol, name, None))
              }
              注意：也可通过 Protocol.__protocol_attrs__ (CPython 内部属性)
              获取，但 dir() 方式更稳定。
           b. 检查 cls 是否实现了所有方法:
              missing = {name for name in protocol_methods
                         if not hasattr(cls, name)
                         or not callable(getattr(cls, name))}
           c. missing 为空 → 加入正缓存
           d. missing 非空 → 加入负缓存，抛 TypeError:
              f"{cls.__qualname__} missing methods required by "
              f"{self.name} protocol: {', '.join(sorted(missing))}"

    替代方案:
        也可使用 issubclass(cls, self.protocol) (runtime_checkable 支持)。
        但该方式不返回缺失的方法名列表，错误信息不够友好。
        建议同时使用：先 issubclass 快速判断，失败时再手动检测缺失方法以提供详细错误。
    """
```

#### `add`

```python
def add(
    self,
    key: str,
    cls: type,
    *,
    protocol_check: bool = False,
) -> None:
    """将一个类以指定 key 注册到本 layer 的注册表中。

    Args:
        key: 注册表中的查找键。后续通过 get(key) 检索该类。
        cls: 要注册的类。
        protocol_check: 是否在注册前执行 Protocol 合规校验。
            - True:  路径 C（显式注册）使用。无继承保证，需 runtime 校验。
            - False: 路径 A/B（继承注册）使用。继承即合规，免检。

    Raises:
        ValueError: key 已存在于注册表中（防止静默覆盖）。
        TypeError:  protocol_check=True 且 cls 未满足 Protocol。

    实现思路:
        1. if protocol_check: self.runtime_check(cls)
        2. with self._lock:
            a. if key in self._store:
                existing = self._store[key]
                raise ValueError(
                    f"Duplicate key '{key}' in {self.name} registry: "
                    f"{cls.__qualname__} conflicts with {existing.__qualname__}. "
                    f"Use __registry_key__ to assign a unique key."
                )
            b. self._store[key] = cls
    """
```

#### `remove`

```python
def remove(self, key: str) -> None:
    """移除注册。仅用于测试清理。

    Args:
        key: 要移除的 key

    Raises:
        KeyError: key 不存在

    实现思路:
        with self._lock:
            del self._store[key]  # KeyError if missing
        type(self)._invalidation_counter += 1  # 全局计数器递增，使所有负缓存失效
    """
```

#### `get` / `get_or_none`

```python
def get(self, key: str) -> type:
    """查询已注册的类。

    Args:
        key: 注册 key

    Returns:
        注册的类

    Raises:
        KeyError: key 不存在。错误信息列出所有可用 key 辅助调试:
            f"Key '{key}' not found in {self.name} registry. "
            f"Available keys: {', '.join(sorted(self._store.keys()))}"

    实现: with self._lock: return self._store[key]（KeyError 时包装错误信息）
    """

def get_or_none(self, key: str) -> type | None:
    """安全查询，不存在返回 None。

    实现: with self._lock: return self._store.get(key)
    """
```

#### `keys` / `items`

```python
def keys(self) -> list[str]:
    """返回所有已注册 key 的快照（列表，非视图）。

    实现: with self._lock: return list(self._store.keys())
    注意：返回快照而非视图，避免并发修改问题。
    """

def items(self) -> list[tuple[str, type]]:
    """返回所有 (key, class) 对的快照。

    实现: with self._lock: return list(self._store.items())
    """
```

#### `_dump_registry`

```python
def _dump_registry(self, file=None) -> None:
    """Debug 内省。借鉴 ABCMeta._dump_registry()。

    输出格式:
        Registry: agent
        Invalidation counter: 3
        Entries (5):
          browser_use → BrowserUseAgent
          skyvern    → SkyvernAgent
          ...
        Check cache: {BrowserUseAgent, SkyvernAgent}
        Negative cache: {BadAgent}
        Negative cache version: 3
    """
```

#### 设计要点小结

- **不包含传播（propagate）逻辑** — 传播是路径层面的关注点，由 registrar 和 metaclass 处理
- **Protocol check 缓存借鉴 ABCMeta** — 正缓存永不失效，负缓存在 `remove()` 时通过全局计数器失效
- **所有公共方法都持有 `_lock`** — 支持多线程并发注册/查询

### 5.2 `registration/auto.py` — AutoRegistrar 元类工厂

#### `create_auto_registrar`

```python
def create_auto_registrar(
    registry: LayerRegistry,
    key_transform: KeyTransform,
    *,
    base_metaclass: type = type,
) -> type:
    """创建 AutoRegistrar 元类。

    返回的 metaclass 使得：使用该 metaclass 的基类的所有具体子类，
    在类 **定义时** 自动注册到 registry 中。

    Args:
        registry: 目标注册表实例。所有子类注册写入此处。
        key_transform: key 推断函数。将类名转换为注册 key。
        base_metaclass: AutoRegistrar 继承的父 metaclass。
            默认 type。当消费者基类使用 ABCMeta 等其他 metaclass 时，
            传入该 metaclass 以解决冲突。

    Returns:
        AutoRegistrar metaclass（type 的子类）。

    实现思路:
        1. 定义 class AutoRegistrar(base_metaclass):
           — 通过闭包捕获 registry 和 key_transform
        2. 实现 __new__(mcs, name, bases, namespace, **kwargs)
           — 见下方 AutoRegistrar.__new__ 详细实现
        3. 设置 AutoRegistrar.__qualname__ = f"AutoRegistrar[{registry.name}]"
           — 便于调试时识别属于哪个 layer
        4. 返回 AutoRegistrar
    """
```

#### AutoRegistrar.__new__ 详细实现

```python
def __new__(mcs, name: str, bases: tuple, namespace: dict, **kwargs) -> type:
    """元类的类创建钩子。每当使用此 metaclass 的基类被继承时调用。

    实现步骤:

    ── Step 1: 创建类 ──
    cls = super().__new__(mcs, name, bases, namespace, **kwargs)
    # 正确传递 **kwargs，不破坏其他 metaclass 的 keyword args
    # 如 class Foo(Base, some_param=True) 中的 some_param

    ── Step 2: 初始化 per-class 元数据 ──
    cls.__registry_name__ = registry.name     # 标记属于哪个 layer
    # 不在此处设置 __registry_key__，只在实际注册时设置

    ── Step 3: 注册判定（跳过条件）──
    # 条件 a: 根基类（无 bases）→ 跳过
    #   当用户写 class BaseAgent(metaclass=Meta) 时，bases=() 是空的
    #   这是 metaclass 的直接使用者，不应注册
    if not bases:
        return cls

    # 条件 b: 显式抽象标记 → 跳过
    #   检查 namespace（而非 getattr），只看当前类自己声明的
    #   避免从父类继承 __abstract__=True 导致子类也被跳过
    if namespace.get("__abstract__", False):
        return cls

    ── Step 4: Key 推断 ──
    # 优先级: namespace 中显式声明的 __registry_key__ > key_transform
    # 注意: 检查 namespace（cls.__dict__）而非 getattr(cls, ...)
    # 因为 getattr 会沿 MRO 查找，可能拿到父类的 key
    explicit_key = namespace.get("__registry_key__")
    key = explicit_key if explicit_key is not None else key_transform(name)

    ── Step 5: 注册写入 ──
    registry.add(key, cls, protocol_check=False)
    # 继承路径 → 免 Protocol 校验（继承自基类天然满足接口）
    cls.__registry_key__ = key

    ── Step 6: __config_schema__ 校验（可选）──
    config_schema = namespace.get("__config_schema__")
    if config_schema is not None:
        if not (isinstance(config_schema, type)
                and issubclass(config_schema, BaseModel)):
            raise TypeError(
                f"{name}.__config_schema__ must be a BaseModel subclass, "
                f"got {type(config_schema).__name__}"
            )

    ── Step 7: 返回 cls ──
    return cls
    """
```

#### 注册规则总结

| 条件 | 行为 | 示例 |
|------|------|------|
| `bases` 为空 | 跳过（根基类） | `class BaseAgent(metaclass=Meta)` |
| `namespace["__abstract__"]` is True | 跳过（中间抽象层/桥接类） | `class MiddleAgent(BaseAgent): __abstract__ = True` |
| 其余 | 注册 | `class FooAgent(BaseAgent)` → registered |

**路径 A 和路径 B 都依赖此机制**。路径 B 的桥接类（`__abstract__=True`）被跳过，其子类被正常注册。

#### 边界情况

- **多重继承**: `class Foo(BaseA, BaseB)` — 只要 metaclass 相同（或兼容），`__new__` 只触发一次。key 从类名推断，不受多继承影响。
- **菱形继承**: Python MRO 保证 `__new__` 只调用一次。
- **`__init_subclass__` 协作**: `super().__new__()` 内部会调用 `__init_subclass__`。我们的 `__new__` 在 `super().__new__()` 之后执行注册逻辑，不干扰 `__init_subclass__` 的正常工作。
- **metaclass kwargs**: `**kwargs` 透传给 `super().__new__()`，支持 `class Foo(Base, some_param=True)` 语法。

**与 ABCMeta 的关键差异与借鉴 — 见 Section 6 详述。**

### 5.3 `registration/registrar.py` — LayerRegistrar[P] + create_registrar()

**职责**: Layer 专属注册器，将 Protocol + Registry + AutoRegistrar + KeyTransform 四者绑定在一起，对外提供统一的 API。是消费者直接交互的入口。

#### 类定义与类级属性

```python
class LayerRegistrar(Generic[P]):
    """Layer 专属注册器。通过 create_registrar() 创建子类实例化。

    所有公共方法都是 @classmethod，消费者直接在类上调用：
        MyRegistrar.get("foo")
        MyRegistrar.bridge(ExtAgent)
    """

    # ── 子类必须设置（由 create_registrar 自动填充）──
    protocol: type[P]              # @runtime_checkable Protocol
    _registry: LayerRegistry[P]    # 底层注册表实例
    Meta: type                     # AutoRegistrar metaclass
    _key_transform: KeyTransform   # key 推断函数
    discriminator_field: str       # config union 的区分字段名（如 "name", "provider"）
```

#### `create_registrar()` — 一键工厂（推荐入口）

```python
def create_registrar(
    name: str,
    protocol: type,
    *,
    discriminator_field: str = "",
    strip_suffixes: list[str] | None = None,
    strip_prefixes: list[str] | None = None,
    key_transform: KeyTransform | None = None,
    base_metaclass: type = type,
) -> type[LayerRegistrar]:
    """创建一个 Layer 专属的 Registrar 类。推荐的入口 API。

    Args:
        name: Layer 名称（如 "agent", "llm", "provider"）。
            用于错误信息、debug 输出、生成的 config 类名前缀。
        protocol: @runtime_checkable Protocol 类。
            定义该 layer 所有实现必须满足的接口契约。
        discriminator_field: Config union 的区分字段名。
            build_config() 使用此字段生成 Literal discriminator。
            如 "name" → AgentConfig 中有 name: Literal["browser_use"]。
            可为空（不使用 config 生成功能时）。
        strip_suffixes: key 推断时要剥离的后缀列表。
            传入时内部调用 make_key_transform(suffixes=..., prefixes=...)。
            与 key_transform 互斥。
        strip_prefixes: key 推断时要剥离的前缀列表。
            与 strip_suffixes 配合使用。
        key_transform: 完全自定义的 key 推断函数。
            优先级最高，传入时忽略 strip_suffixes/strip_prefixes。
        base_metaclass: AutoRegistrar 继承的父 metaclass。
            默认 type。当消费者基类使用 ABCMeta 等其他 metaclass 时传入。

    Returns:
        LayerRegistrar 的子类（注意：返回的是类，不是实例）。
        该子类的类级属性已全部填充，可直接使用。

    实现思路:
        1. 确定 key_transform:
           if key_transform is not None:
               kt = key_transform
           elif strip_suffixes or strip_prefixes:
               kt = make_key_transform(suffixes=strip_suffixes, prefixes=strip_prefixes)
           else:
               kt = default_key_transform

        2. 创建 LayerRegistry 实例:
           registry = LayerRegistry(name, protocol)

        3. 创建 AutoRegistrar metaclass:
           meta = create_auto_registrar(registry, kt, base_metaclass=base_metaclass)

        4. 动态创建 LayerRegistrar 子类:
           registrar_cls = type(
               f"{name.title()}Registrar",       # 类名如 "AgentRegistrar"
               (LayerRegistrar,),
               {
                   "protocol": protocol,
                   "_registry": registry,
                   "Meta": meta,
                   "_key_transform": staticmethod(kt),
                   "discriminator_field": discriminator_field,
               }
           )
           return registrar_cls

    Raises:
        TypeError: protocol 不是 @runtime_checkable Protocol。
    """
```

#### API 总览

| API | 路径 | 职责 |
|-----|------|------|
| `.Meta` | A, B | AutoRegistrar 元类，用于基类的 `metaclass=` |
| `.bridge(external_cls)` | B | 为外部类创建桥接基类 |
| `.register(key=None, *, propagate=False)` | C | 手动注册装饰器（执行 `runtime_check`） |
| `.get(key)` | — | 查询已注册的类 |
| `.get_or_none(key)` | — | 安全查询 |
| `.get_all()` | — | 返回所有 `{key: class}` |
| `.keys()` | — | 返回所有已注册的 key |
| `.unregister(key)` | — | 移除注册（测试隔离用） |
| `.build_config()` | — | 构建 config union（调用 config/ 子包） |
| `.config_union_type()` | — | 快捷获取 union type |
| `.get_config_schema(key)` | — | 获取指定 key 的 config schema |

#### 查询与管理 API

```python
@classmethod
def get(cls, key: str) -> type:
    """查询已注册的类。委托 cls._registry.get(key)。"""

@classmethod
def get_or_none(cls, key: str) -> type | None:
    """安全查询。委托 cls._registry.get_or_none(key)。"""

@classmethod
def get_all(cls) -> dict[str, type]:
    """返回所有 {key: class} 映射的快照。

    实现: return dict(cls._registry.items())
    """

@classmethod
def keys(cls) -> list[str]:
    """返回所有已注册 key。委托 cls._registry.keys()。"""

@classmethod
def unregister(cls, key: str) -> None:
    """移除注册。仅用于测试隔离。委托 cls._registry.remove(key)。"""
```

**`bridge()` — 路径 B 的关键 API**:

```python
@classmethod
def bridge(cls, external_class: type, *, name: str = None) -> type:
    """为外部类创建桥接基类，使其子类可通过继承自动注册。

    自动处理 metaclass 冲突。返回的桥接类标记为 __abstract__=True，
    不会被注册到注册表中。

    Args:
        external_class: 外部类（不使用本框架 metaclass 的类）。
            可以是任何 Python 类——普通类、ABC、使用其他 metaclass 的类。
        name: 桥接类名称，默认为 f"{external_class.__name__}Bridge"。
            生成的桥接类的 __name__ 和 __qualname__ 将使用此名称。

    Returns:
        桥接基类。特征:
            - metaclass 是 cls.Meta（或与 external_class 的 metaclass 的组合）
            - __abstract__ = True（不会被注册到注册表）
            - 继承自 external_class（保留其全部接口和行为）
            - 该类的子类将通过 metaclass 继承获得自动注册能力

    Raises:
        TypeError: metaclass 冲突无法自动解决时（理论上不应发生，
            因为 type() 动态创建组合 metaclass 总是可行的）。

    Metaclass 冲突解决策略（按优先级）:
        1. ext_meta is type
            → 外部类无自定义 metaclass，直接使用 cls.Meta。最常见情况。
        2. issubclass(cls.Meta, ext_meta)
            → cls.Meta 已经是 ext_meta 的子类（如 base_metaclass=ABCMeta 时），
              用 cls.Meta 即可满足双方需求。
        3. issubclass(ext_meta, cls.Meta)
            → ext_meta 已经是 cls.Meta 的子类（外部类恰好使用了我们的 metaclass），
              用 ext_meta（更具体的那个）。
        4. else
            → 两个 metaclass 无继承关系，创建组合 metaclass:
              type(f"{bridge_name}Meta", (cls.Meta, ext_meta), {})
              MRO 确保 cls.Meta.__new__ 先于 ext_meta.__new__ 执行。

    实现:
    """
    bridge_name = name or f"{external_class.__name__}Bridge"
    ext_meta = type(external_class)

    if ext_meta is type:
        meta = cls.Meta
    elif issubclass(cls.Meta, ext_meta):
        meta = cls.Meta
    elif issubclass(ext_meta, cls.Meta):
        meta = ext_meta
    else:
        meta = type(f"{bridge_name}Meta", (cls.Meta, ext_meta), {})

    return meta(bridge_name, (external_class,), {"__abstract__": True})
```

**`register()` — 路径 C 的 API**:

```python
@classmethod
def register(cls, key=None, *, propagate=False):
    """手动注册装饰器。对目标类执行 Protocol runtime_check。

    Args:
        key: 注册 key。
            - 传入字符串 → 使用该字符串作为 key
            - 省略/None → 使用 cls._key_transform(target_cls.__name__) 推断
        propagate: 是否让子类也自动注册。
            - False (默认): 仅注册此类本身。适用于一次性集成。
            - True: 通过注入 __init_subclass__ 实现 definition-time
                    子类自动注册。子类同样执行 protocol_check。
                    适用于外部类族且 bridge() 不可用时。

    Returns:
        装饰器函数。装饰后的类不被修改（propagate=False 时）
        或被注入 __init_subclass__（propagate=True 时），原类返回。

    Raises:
        ValueError: key 已存在于注册表（来自 registry.add）。
        TypeError: 目标类未满足 Protocol（来自 runtime_check）。

    Usage:
        # 单个类注册
        @MyRegistrar.register("custom")
        class CustomImpl: ...

        # key 自动推断
        @MyRegistrar.register()
        class CustomImpl: ...  # → key = key_transform("CustomImpl")

        # 注册 + 子类传播
        @MyRegistrar.register("base_custom", propagate=True)
        class CustomBase: ...
        class CustomV1(CustomBase):  # → 自动注册
            ...

    实现思路:
        返回一个 decorator 闭包:
        1. actual_key = key or cls._key_transform(target_cls.__name__)
        2. cls._registry.add(actual_key, target_cls, protocol_check=True)
           — 注意: 始终 protocol_check=True（手动路径无继承保证）
        3. target_cls.__registry_key__ = actual_key
        4. if propagate: cls._inject_auto_registration(target_cls)
        5. return target_cls
    """
```

**`_inject_auto_registration()` — propagate 的实现机制**:

```python
@classmethod
def _inject_auto_registration(cls, target_cls: type) -> None:
    """注入 __init_subclass__ 使子类在 definition-time 自动注册。

    当 register(propagate=True) 时调用。

    Args:
        target_cls: 被 register() 装饰的类。将被注入 __init_subclass__。

    实现思路:
        1. 通过闭包捕获 registry 和 key_transform
        2. 保存 target_cls.__dict__ 中原有的 __init_subclass__（如果有）
           注意: 用 __dict__.get() 而非 getattr()，避免拿到继承的版本
        3. 定义新的 __init_subclass__(sub_cls, **kwargs):
           a. 链式调用原有的 __init_subclass__:
              - 如果原 hook 存在: original.__func__(sub_cls, **kwargs)
              - 否则: super(target_cls, sub_cls).__init_subclass__(**kwargs)
           b. 跳过 __abstract__=True 的子类
           c. 推断 key: sub_cls.__dict__.get("__registry_key__") or key_transform(name)
              注意: 用 __dict__ 而非 getattr，避免继承父类的 key
           d. registry.add(sub_key, sub_cls, protocol_check=True)
              注意: 始终 protocol_check=True（手动路径的子类也无 metaclass 保护）
           e. sub_cls.__registry_key__ = sub_key
        4. target_cls.__init_subclass__ = __init_subclass__

    传播行为:
        __init_subclass__ 自然通过 Python 继承机制传播:
        - class A: __init_subclass__ 被注入
        - class B(A): A.__init_subclass__ 触发 → B 注册
        - class C(B): B.__init_subclass__ (继承自 A) 触发 → C 注册
        间接子类也自动注册，无需额外处理。

    已知限制:
        - 修改了 target_cls（注入 __init_subclass__），有侵入性
        - 如果 target_cls 的 __init_subclass__ 有复杂逻辑，链式调用可能有边界情况
        - 子类的 __config_schema__ 不会被校验（metaclass __new__ 才做此校验）
        推荐：优先使用 bridge() (路径 B)，仅在 bridge 不可用时使用 propagate=True
    """
    registry = cls._registry
    key_transform = cls._key_transform
    original = target_cls.__dict__.get("__init_subclass__")

    @classmethod
    def __init_subclass__(sub_cls, **kwargs):
        if original:
            original.__func__(sub_cls, **kwargs)
        else:
            super(target_cls, sub_cls).__init_subclass__(**kwargs)

        if getattr(sub_cls, "__abstract__", False):
            return

        sub_key = (
            sub_cls.__dict__.get("__registry_key__")
            or key_transform(sub_cls.__name__)
        )
        registry.add(sub_key, sub_cls, protocol_check=True)
        sub_cls.__registry_key__ = sub_key

    target_cls.__init_subclass__ = __init_subclass__
```

**路径 B vs 路径 C (propagate=True) 对比**：

| 维度 | 路径 B: bridge() | 路径 C: register(propagate=True) |
|------|-----------------|----------------------------------|
| 底层机制 | metaclass 继承（Python 原生） | `__init_subclass__` 注入 |
| 对外部类的侵入 | 无（创建新的桥接类） | 注入 `__init_subclass__` 到外部类 |
| metaclass 冲突 | `bridge()` 自动解决 | 不涉及 metaclass，无冲突 |
| 子类的 `__config_schema__` 校验 | 有（metaclass `__new__` 中校验） | 无（`__init_subclass__` 不做此校验） |
| 推荐程度 | **推荐**（更健壮、更 Pythonic） | 备选（适合快速原型或 metaclass 冲突无法解决时） |

#### Config Generation API

```python
@classmethod
def build_config(cls) -> "LayerConfigResult":
    """构建该 layer 的 config union 类型。

    遍历注册表中所有实现类，提取每个类的 config schema，
    注入 discriminator 字段，组装为 Pydantic discriminated union。

    Returns:
        LayerConfigResult，包含:
            - union_type: Annotated[Union[...], Field(discriminator=...)]
            - per_key_models: {key: BaseModel subclass}
            - layer_name, discriminator_field

    Raises:
        ValueError: discriminator_field 未设置。

    实现: 委托 layer_registry.config.builder.build_layer_config(cls)。
          详见 config-typing-design.md Section 5。
    """

@classmethod
def config_union_type(cls) -> type:
    """快捷方法: 直接获取 union type（运行时使用）。

    实现: return cls.build_config().union_type
    """

@classmethod
def get_config_schema(cls, key: str) -> "type[BaseModel] | None":
    """获取指定 key 的 config schema。

    Args:
        key: 注册 key

    Returns:
        Pydantic BaseModel 子类，或 None（无 config schema 时）

    实现: 委托 layer_registry.config.extractor.extract_config_schema(cls.get(key))。
          详见 config-typing-design.md Section 3。
    """
```

#### `create_registrar()` 使用示例

```python
MyRegistrar = create_registrar(
    "my_layer",
    MyProtocol,
    discriminator_field="name",       # config 区分字段
    strip_suffixes=["Handler"],       # key 推断：去后缀
)
```

替代手动继承 `LayerRegistrar` + 绑定 `Registry` + `Meta` + `KeyTransform` 的 5 行样板。

手动继承仍支持（适用于需要 override 方法的高级场景）。

### 5.4 `registration/discover.py` — discover()

```python
def discover(*package_paths: str) -> list[str]:
    """递归导入指定包下的所有模块，触发 import-time 注册。

    Python 的 metaclass 注册依赖模块被 import 才能触发 AutoRegistrar.__new__()。
    discover() 确保所有实现模块被导入 → 触发类定义 → 填充注册表。

    Args:
        *package_paths: 一个或多个 Python 包的 dotted path。
            如 "my_app.agents", "my_app.providers"。
            每个路径必须是一个已安装的 Python 包（有 __init__.py）。

    Returns:
        所有成功导入的模块名列表（用于调试/日志）。

    Raises:
        ModuleNotFoundError: package_path 对应的包不存在。
        注意: 单个子模块的 import 错误**不会**中止整个发现过程，
        而是收集后统一报告（warn 或 raise，取决于 strict 模式）。

    实现思路:
        for pkg_path in package_paths:
            1. importlib.import_module(pkg_path) → 获取包对象
            2. pkgutil.walk_packages(
                   path=package.__path__,
                   prefix=package.__name__ + ".",
                   onerror=_handle_import_error
               )
               — walk_packages 递归遍历所有子模块和子包
               — 每个模块被 import 时，其中的类定义触发 metaclass.__new__
            3. 对每个 module_info:
               importlib.import_module(module_info.name)
            4. 收集并返回所有成功导入的模块名

    错误处理策略:
        - 包不存在 → 立即抛 ModuleNotFoundError（fail-fast）
        - 子模块 import 失败 → 记录 warning，继续发现其余模块
          （某个实现类有 bug 不应阻止其他类的注册）
        - 后续可考虑 strict=True 参数控制是否在子模块失败时中止

    Usage:
        # 在应用入口/main.py 中调用
        from layer_registry import discover

        discover("my_app.agents", "my_app.providers", "my_app.llm.providers")

        # discover 之后，注册表已填充，可以查询
        agent_cls = AgentRegistrar.get("browser_use")
    """
```

**设计选择**: 显式在入口调用，而非 lazy import。原因：
- 注册时机明确可控
- 避免循环导入
- 消费者可精确指定需要发现的包路径
- 错误在启动时暴露，而非运行时第一次查询时

### 5.5 组件联动流程

**路径 A: 继承注册（框架内建实现）**

```
消费者定义 Registrar (一次性)
═══════════════════════════════════════════
create_registrar("agent", AgentProtocol, strip_suffixes=["Agent"])
    │
    ├─ 创建 LayerRegistry("agent", AgentProtocol)
    ├─ 创建 key_transform = make_key_transform(suffixes=["Agent"])
    ├─ 创建 AutoRegistrar = create_auto_registrar(registry, key_transform)
    └─ 组装 LayerRegistrar 子类 (protocol + registry + Meta + key_transform)


程序启动 (每次运行)
═══════════════════════════════════════════
discover("my_app.agents")
    │
    ├─ import my_app.agents.foo
    │       └─ class FooAgent(BaseAgent)            # 类定义
    │              └─ AutoRegistrar.__new__()        # 元类拦截
    │                     ├─ key_transform("FooAgent") → "foo"
    │                     └─ registry.add("foo", FooAgent, protocol_check=False)
    │
    ├─ import my_app.agents.bar
    │       └─ class BarAgent(BaseAgent)
    │              └─ → registry.add("bar", BarAgent, protocol_check=False)
    │
    ▼
注册表就绪
    │
    ▼
AgentRegistrar.get("foo")  →  FooAgent
AgentRegistrar.get("bar")  →  BarAgent
```

**路径 B: 桥接注册（外部类族）**

```
框架用户创建桥接 (一次性)
═══════════════════════════════════════════
from ext_framework import ExtAgent

ExtBridge = AgentRegistrar.bridge(ExtAgent)
    │
    ├─ 检测 type(ExtAgent) 是否与 AgentRegistrar.Meta 冲突
    ├─ 自动解决 metaclass 冲突（如需要）
    └─ 创建 ExtBridge(ExtAgent, metaclass=Meta, __abstract__=True)


用户定义变体
═══════════════════════════════════════════
class MyExtV1(ExtBridge):                # 类定义
    def step(self): ...
         │
    AutoRegistrar.__new__()              # metaclass 继承自 ExtBridge
         │
    key_transform("MyExtV1") → "my_ext_v1"
         │
    registry.add("my_ext_v1", MyExtV1, protocol_check=False)
         │
    AgentRegistrar.get("my_ext_v1")  →  MyExtV1 ✓
```

**路径 C: 显式注册（单个外部类）**

```
手动注册
═══════════════════════════════════════════
@AgentRegistrar.register("custom")
class CustomAgent:
    def step(self): ...
         │
    registry.runtime_check(CustomAgent)     ← Protocol 校验
         │
    registry.add("custom", CustomAgent, protocol_check=True)
         │
    AgentRegistrar.get("custom")  →  CustomAgent ✓


手动注册 + propagate=True
═══════════════════════════════════════════
@AgentRegistrar.register("custom_base", propagate=True)
class CustomBase:
    def step(self): ...
         │
    registry.add("custom_base", CustomBase, protocol_check=True)
    _inject_auto_registration(CustomBase)   ← 注入 __init_subclass__

class CustomV1(CustomBase):                 ← __init_subclass__ 触发
    def step(self): ...
         │
    key_transform("CustomV1") → "custom_v1"
    registry.add("custom_v1", CustomV1, protocol_check=True)
```

---

## 6. ABCMeta 设计参考

`AutoRegistrar` 参考了 Python `ABCMeta` 的元类设计。本节分析 ABCMeta 的关键机制，明确哪些需要借鉴、哪些不需要。

### 6.1 Python ABCMeta 架构

ABCMeta 有两套实现：

| 实现 | 位置 | 角色 |
|------|------|------|
| **C 扩展 `_abc`** | CPython 内置 C 模块 | **主实现**，性能关键路径 |
| **纯 Python `_py_abc`** | `Lib/_py_abc.py` | **后备实现**，当 C 扩展不可用时使用 |

`abc.py` 的加载逻辑：

```python
# Lib/abc.py
try:
    from _abc import (get_cache_token, _abc_init, _abc_register,
                      _abc_instancecheck, _abc_subclasscheck, ...)
except ImportError:
    from _py_abc import ABCMeta, get_cache_token
```

**关键认知**: `.py` 文件是 fallback，不是主实现。C 版本处理了所有性能关键路径（`isinstance`/`issubclass` 检查）。纯 Python 版本 (`_py_abc.py`) 是最佳的算法参考文档。

### 6.2 ABCMeta 的四层缓存机制

每个 ABCMeta 派生的类拥有四个缓存结构：

| 缓存 | 类型 | 用途 |
|------|------|------|
| `_abc_registry` | `WeakSet` | 存储通过 `.register()` 注册的虚拟子类 |
| `_abc_cache` | `WeakSet` | **正缓存** — 已确认是子类的类 |
| `_abc_negative_cache` | `WeakSet` | **负缓存** — 已确认不是子类的类 |
| `_abc_negative_cache_version` | `int` | 与全局计数器比较，判断负缓存是否过期 |

**全局失效计数器** `ABCMeta._abc_invalidation_counter`:
- 每次任何 ABC 调用 `.register()` 时递增
- 所有 ABC 的 `_abc_negative_cache_version` 立即过期
- 下次 `__subclasscheck__` 时检测到版本差异 → 清空负缓存 → 重新计算

**`__subclasscheck__` 解析顺序** (6 步):

```
1. 正缓存命中?            → return True
2. 负缓存有效且命中?       → return False
   (版本过期则清空负缓存)
3. __subclasshook__ 返回值?  → 缓存结果并返回 (非 NotImplemented 时)
4. MRO 中包含 cls?         → 加入正缓存, return True
5. _abc_registry 递归检查?  → 加入正缓存, return True
6. 全部未命中              → 加入负缓存, return False
```

**设计亮点**:
- 正缓存**永不失效** — 新注册不会让已确认的子类变成非子类
- 负缓存**保守失效** — 任何注册变更都可能影响负判断，所以全部清空
- WeakSet 防止内存泄漏 — 类被 GC 后自动从缓存消失

### 6.3 ABCMeta.__new__ 做了什么

```
ABCMeta.__new__(name, bases, namespace):
    1. super().__new__() 创建类
    2. 计算 __abstractmethods__ (frozenset)
       — 收集当前类和基类中标记 @abstractmethod 且未被覆盖的方法
    3. 初始化四层缓存 (每个类独立的 WeakSet 实例)
    4. 设置 _abc_negative_cache_version = 当前全局计数器
```

### 6.4 ABCMeta.register() 与我们的设计对比

ABCMeta 的 `register()` 实现：

```python
def register(cls, subclass):
    if not isinstance(subclass, type):
        raise TypeError("Can only register classes")
    if issubclass(subclass, cls):
        return subclass  # Already a subclass → no-op
    if issubclass(cls, subclass):
        raise RuntimeError("Refusing to create an inheritance cycle")
    cls._abc_registry.add(subclass)
    ABCMeta._abc_invalidation_counter += 1
    return subclass
```

**关键行为**：`__subclasscheck__` 中递归检查 `_abc_registry` 成员的子类：

```python
for rcls in cls._abc_registry:
    if issubclass(subclass, rcls):  # ← 子类的子类也算！
        cls._abc_cache.add(subclass)
        return True
```

**ABCMeta 的 register 天然传播** — `register(Foo)` 后，Foo 的所有子类都被视为 ABC 的子类。但它是 **query-time** 延迟判定的。

**与我们设计的本质差异**：

| 维度 | ABCMeta.register() | 我们的 register() |
|------|--------------------|--------------------|
| **语义** | 类型系统集成（isinstance/issubclass） | 功能查找（key → class） |
| **需要 key** | 不需要，只做 subclass 判定 | **必须**有 key，注册表的核心 |
| **传播时机** | query-time（`__subclasscheck__` 中递归） | definition-time（需立即分配 key） |
| **传播机制** | `__subclasscheck__` 递归 + 缓存 | `__init_subclass__` 注入 或 metaclass 继承 |
| **存储** | WeakSet（弱引用，允许 GC） | dict[str, type]（强引用，防止 GC） |

**核心洞察**：ABCMeta 可以用 query-time 延迟传播，因为它不需要分配 key — 只需要判断 "是否为子类"。我们必须在 definition-time 传播，因为每个子类需要一个唯一的 key。这就是为什么路径 B 使用 metaclass 继承（definition-time 拦截）、路径 C 的 propagate 使用 `__init_subclass__` 注入（也是 definition-time）。

### 6.5 AutoRegistrar 应借鉴的机制

| ABCMeta 机制 | AutoRegistrar 是否需要 | 理由 |
|-------------|----------------------|------|
| **`__new__` 中初始化 per-class 元数据** | **是** | 每个类应有自己的 `__registry_key__`、注册时间戳等元数据 |
| **Protocol check 结果缓存** | **是** | 路径 C 调用 `runtime_check()` 时缓存正/负结果，避免重复检查 |
| **全局失效计数器** | **是** | `remove()` 或动态修改类时，负缓存需要失效 |
| **WeakSet 用于缓存** | **是** | Protocol check 缓存应用 WeakSet，防止 GC'd 类残留 |
| **Debug 内省方法** | **是** | `_dump_registry()` 输出注册表和缓存状态，辅助调试 |
| **`__init_subclass__` 协作** | **是** | `AutoRegistrar.__new__` 应正确链式调用 `__init_subclass__`，不破坏用户定义的钩子 |

### 6.6 AutoRegistrar 不需要的机制

| ABCMeta 机制 | 不需要的理由 |
|-------------|-------------|
| **`__abstractmethods__` 计算** | AutoRegistrar 不关心抽象方法，通过 `__abstract__` 标志区分抽象/具体 |
| **`__instancecheck__` / `__subclasscheck__` 重写** | 我们不改变 `isinstance`/`issubclass` 的语义，Protocol check 是独立方法 |
| **`__subclasshook__`** | Protocol 的 `@runtime_checkable` 已提供结构子类型检查，不需要额外 hook |
| **`_abc_registry` (虚拟子类注册)** | 我们的注册表是显式 key→class 映射，不做虚拟继承。我们的 `register()` 语义完全不同于 ABCMeta 的 `register()` |
| **WeakSet 用于主存储** | 注册表 `_store` 应是强引用 dict，注册的类不应被 GC |

### 6.7 AutoRegistrar.__new__ — ABCMeta 视角的总结

> **完整实现思路见 Section 5.2。** 本节从 ABCMeta 借鉴的角度总结关键步骤。

综合借鉴后，`AutoRegistrar.__new__` 与 ABCMeta.__new__ 的对照：

| 步骤 | ABCMeta.__new__ | AutoRegistrar.__new__ |
|------|-----------------|----------------------|
| 1. 创建类 | `super().__new__()` | 相同，透传 `**kwargs` |
| 2. 元数据初始化 | `__abstractmethods__` + 四层缓存 | `__registry_name__`（不初始化缓存，缓存在 Registry 上） |
| 3. 注册判定 | 无（ABCMeta 不做注册判定） | 无 bases → 跳过；`__abstract__=True` → 跳过 |
| 4. Key 推断 | 无 | `namespace["__registry_key__"]` > `key_transform(name)` |
| 5. 注册写入 | 无 | `registry.add(key, cls, protocol_check=False)` |
| 6. 额外校验 | 无 | `__config_schema__` 类型校验（可选） |

### 6.8 Metaclass 冲突处理

当消费者的基类同时使用 ABCMeta 和 AutoRegistrar 时，Python 会报 `TypeError: metaclass conflict`。

**解决方案**: `create_auto_registrar()` 接受可选的 `base_metaclass` 参数，AutoRegistrar 继承自该 metaclass 而非 `type`：

```python
# 场景：基类同时是 ABC 和 auto-registered
meta = create_auto_registrar(registry, key_transform, base_metaclass=ABCMeta)

class BaseHandler(ABC, metaclass=meta):
    __abstract__ = True
    @abstractmethod
    def handle(self): ...
```

默认 `base_metaclass=type`，无冲突时无需感知。

`bridge()` 方法也自动处理 metaclass 冲突（见 Section 5.3）。

---

## 7. 公共 API

### 7.1 registration/ 导出

```python
from layer_registry import (
    # 一键工厂 — 推荐入口
    create_registrar,

    # 底层组件 — 高级场景
    LayerRegistry,
    LayerRegistrar,
    create_auto_registrar,

    # Key 推断
    default_key_transform,
    make_key_transform,

    # 模块发现
    discover,
)
```

### 7.2 config/ 导出

```python
from layer_registry.config import (
    extract_config_schema,
    build_layer_config,
    LayerConfigResult,
    generate_layer_config_source,
    generate_layer_json_schema,
)
```

### 7.3 消费者使用最小路径

**框架开发者（用户 A）只需要两个 import**:

```python
from layer_registry import create_registrar, discover
```

**框架用户（用户 B）只需要知道一件事**:

```python
# 情况 1: 外部类继承了框架基类 → 直接继承
class MyAgent(SomeBaseAgent):  # 自动注册
    ...

# 情况 2: 外部类未继承框架基类 → 先桥接
MyBridge = SomeRegistrar.bridge(ExternalClass)
class MyAgent(MyBridge):  # 自动注册
    ...
```

---

## 8. 标准流程

### 8.1 框架开发者：新增 Layer

#### Step 1: 定义 Protocol + 一行创建 Registrar

```python
# my_layer/_registrar.py

from typing import Protocol, runtime_checkable
from layer_registry import create_registrar

@runtime_checkable
class MyProtocol(Protocol):
    def required_method(self) -> SomeType: ...

MyRegistrar = create_registrar(
    "my_layer",
    MyProtocol,
    discriminator_field="name",
    strip_suffixes=["Handler"],   # MyFooHandler → "my_foo"
)
```

#### Step 2: 创建基类

```python
# my_layer/base.py

from my_layer._registrar import MyRegistrar

class BaseHandler(metaclass=MyRegistrar.Meta):
    __abstract__ = True
    ...
```

#### Step 3: 写具体实现（自动注册）

```python
# my_layer/foo.py

class FooHandler(BaseHandler):    # → auto-registered as "foo"
    def required_method(self): ...
```

#### Step 4: 在入口触发发现

```python
discover("my_layer")
```

完成。4 步接入一个新 layer。

### 8.2 框架用户：集成外部实现

以下场景假设框架开发者已按 8.1 完成设置。

#### 场景 A: 外部实现继承了框架基类

最理想的情况 — 零额外步骤：

```python
# user_project/my_agents.py

from ext_package import CoolAgent  # CoolAgent 继承了 BaseAgent

class MyCoolV1(CoolAgent):         # → 自动注册为 "my_cool_v1"
    def step(self): ...            # override

class MyCoolV2(CoolAgent):         # → 自动注册为 "my_cool_v2"
    def step(self): ...            # 不同修改
```

用户只需确保模块被 `discover()` 覆盖。

#### 场景 B: 外部实现未继承框架基类（推荐：桥接）

一次桥接，之后 N 次继承即注册：

```python
# user_project/my_agents.py

from ext_framework import ExtAgent
from my_framework.agents import AgentRegistrar

# 一次性桥接
ExtBridge = AgentRegistrar.bridge(ExtAgent)

class MyExtV1(ExtBridge):          # → 自动注册为 "my_ext_v1"
    def step(self): ...

class MyExtV2(ExtBridge):          # → 自动注册为 "my_ext_v2"
    def step(self): ...
```

#### 场景 C: 单个外部类一次性集成

不需要多个变体时：

```python
from ext_framework import ExtAgent
from my_framework.agents import AgentRegistrar

@AgentRegistrar.register("ext_agent")
class WrappedExtAgent(ExtAgent):
    pass  # 直接使用，或 override 部分方法
```

#### 场景 D: 外部类 + 多个变体 + metaclass 冲突

当 `bridge()` 因 metaclass 冲突无法解决时（极少见），使用 `register(propagate=True)`：

```python
from problematic_framework import ProblematicAgent  # 使用不兼容的 metaclass
from my_framework.agents import AgentRegistrar

@AgentRegistrar.register("problematic", propagate=True)
class ProblematicBase(ProblematicAgent):
    pass

class ProblematicV1(ProblematicBase):   # → 自动注册为 "problematic_v1"
    def step(self): ...

class ProblematicV2(ProblematicBase):   # → 自动注册为 "problematic_v2"
    def step(self): ...
```

---

## 9. PyPI 包文件结构

```
layer_registry/                            # PyPI 包
├── __init__.py                            # 统一导出 registration + config API
├── registration/                          # 自动注册子包
│   ├── __init__.py
│   ├── key_transform.py                   #   KeyTransform 协议 + default + make
│   ├── registry.py                        #   LayerRegistry[P]
│   ├── auto.py                            #   create_auto_registrar()
│   ├── registrar.py                       #   LayerRegistrar[P] + create_registrar()
│   └── discover.py                        #   discover()
├── config/                                # Config typing 生成子包
│   ├── __init__.py
│   ├── extractor.py                       #   extract_config_schema()
│   ├── builder.py                         #   build_layer_config()
│   ├── codegen.py                         #   generate_layer_config_source()
│   └── json_schema.py                     #   generate_layer_json_schema()
└── cli.py                                 # CLI 入口
```

---

## 10. 设计决策

| # | 决策 | 理由 |
|---|------|------|
| 1 | **metaclass (`type.__new__`) 而非 `__init_subclass__`** | metaclass 在类创建时拦截，能为每个子类注入 `__registry_key__` 等元数据；`__init_subclass__` 在基类上定义，无法区分 "建表者" 和 "被注册者" |
| 2 | **三条注册路径（A 继承 / B 桥接 / C 显式）** | 路径 A 覆盖框架内建实现；路径 B 覆盖外部类族（benchmark 用户最常见场景）；路径 C 是单次集成的逃生舱 |
| 3 | **路径 B (bridge) 作为外部类集成的推荐路径** | 桥接使用标准 metaclass 继承机制（与路径 A 相同底层），比 `__init_subclass__` 注入更健壮；一次桥接解锁 N 次继承即注册 |
| 4 | **路径 C 的 propagate 使用 `__init_subclass__` 注入而非 query-time 延迟发现** | 我们必须在 definition-time 分配 key，query-time 延迟发现无法做到这一点。ABCMeta 可以延迟是因为它不需要 key |
| 5 | **registry.add() 不含 propagate 参数** | 传播是路径层面的关注点（metaclass 或 `__init_subclass__`），不是存储层的关注点。Registry 是纯 key→class 存储 |
| 6 | **bridge() 自动解决 metaclass 冲突** | 框架用户不应需要理解 metaclass 冲突。`bridge()` 检测外部类的 metaclass 并自动创建组合 metaclass |
| 7 | **参考 ABCMeta 但不继承** | ABCMeta 的缓存/失效策略是成熟设计，但其核心关注点（抽象方法、虚拟子类）与我们无关，完全继承会引入不必要的复杂性 |
| 8 | **ABCMeta.register() 的语义与我们的 register() 完全不同** | ABCMeta.register() 是类型系统操作（影响 isinstance/issubclass）；我们的 register() 是注册表操作（key→class 映射）。ABCMeta 的 query-time 递归传播不适用于我们的场景 |
| 9 | **Protocol check 缓存 (正/负缓存 + 全局计数器)** | 直接借鉴 ABCMeta 的缓存策略：正缓存永不失效，负缓存在注册变更时失效。避免重复的 `issubclass` 调用 |
| 10 | **WeakSet 用于缓存，dict 用于主存储** | 缓存应让 GC'd 类自动消失（WeakSet）；注册表应持有强引用（dict），防止注册的类被意外回收 |
| 11 | **KeyTransform 可配置化** | 默认纯 snake_case，消费者按需配置 `strip_suffixes`/`strip_prefixes` 或完全自定义。包不预设任何领域约定 |
| 12 | **`__registry_key__` 始终可覆盖** | 自动推断覆盖 80% 场景，20% 需要显式指定（如 evaluator key 需对齐数据集名） |
| 13 | **Protocol 和 Registry 分离** | Protocol 定义契约 (what)，Registry 管存储 (where)，LayerRegistrar 做组合 (glue)，职责单一 |
| 14 | **继承路径 (A/B) `protocol_check=False`** | 继承自基类 = 编译期保证接口合规，runtime_check 是冗余开销 |
| 15 | **手动路径 (C) `protocol_check=True`** | 不继承 = 无编译期保证，必须 runtime_check 兜底 |
| 16 | **用户 B 的体验是第一优先** | 框架开发者（用户 A）只需一次性配置；框架用户（用户 B）的每一次扩展都应该零摩擦。设计选择在冲突时偏向用户 B 的简洁性 |
| 17 | **`create_registrar()` 一键工厂** | 1 行替代 5+ 行样板，降低新 layer 接入成本；手动继承仍支持高级场景 |
| 18 | **registration/ 与 config/ 同级子包** | 两者是独立关注点，同级放置避免人为的嵌套依赖 |
| 19 | **`discover()` 在入口显式调用** | 比 lazy import 可控；注册时机明确；避免循环导入 |
| 20 | **key 重复抛 `ValueError`** | 静默覆盖是 bug 的温床，fail-fast |
| 21 | **`threading.Lock` 线程安全** | 支持并发注册/查询场景 |
| 22 | **`base_metaclass` 参数解决冲突** | 消费者基类可能同时使用 ABCMeta 等其他 metaclass，需要组合方案 |
| 23 | **Debug 内省 (`_dump_registry`)** | 借鉴 ABCMeta 的 `_dump_registry()`，方便调试注册表和缓存状态 |
