# browseruse-bench 注册表应用

> **前置**: [registry-auto-registration.md](./registry-auto-registration.md)（通用 PyPI 包设计）
>
> **定位**: 本文档描述 `browseruse-bench` 项目如何作为消费者使用 `layer-registry` 包。包含各 layer 的 Protocol 定义、具体实现注册、模块发现配置、config.yaml 映射。

---

## 1. 注册表概览

browseruse-bench 定义了 6 个 layer，各自创建独立的注册表：

| 注册表 | Registrar 位置 | 注册内容 | 查找 Key 示例 | discriminator |
|--------|---------------|----------|--------------|---------------|
| Agent | `agents/_registrar.py` | BrowserUseAgent, SkyvernAgent, AgentTarsAgent | `"browser_use"`, `"skyvern"` | `"name"` |
| LLM Provider | `core/llm/_registrar.py` | ChatOpenAI, ChatAnthropic, ChatAzure... | `"openai"`, `"anthropic"` | `"provider"` |
| Browser Provider | `core/providers/_registrar.py` | Lexmount, AgentBay, Local, CloudNative | `"lexmount"`, `"local"` | `"id"` |
| Benchmark | `benchmark_data/_registrar.py` | LexBench, Mind2Web, BrowseComp | `"lexbench_browser"` | `"name"` |
| Evaluator | `evaluator/_registrar.py` | LexBenchEval, Mind2WebEval... | `"lexbench_browser"` | `"evaluator"` |
| DOM Filter | `core/dom/filters/_registrar.py` | Visibility, Interactivity, PaintOrder... | `"visibility"` | `"name"` |

---

## 2. 各 Layer 应用

### 2.1 Agent Layer

**Protocol 定义** — Agent 必须实现的接口契约：

```python
# agents/_registrar.py

from typing import Protocol, runtime_checkable, Any
from layer_registry import create_registrar
from schemas.results.agent_result import AgentResult


@runtime_checkable
class AgentProtocol(Protocol):
    """Agent 接口契约。

    路径 A (继承 BaseReActAgent): 天然满足，自动注册时免检。
    路径 B (手动注册):            必须实现以下所有方法，注册时 runtime_check 校验。
    """

    async def run(self) -> AgentResult: ...
    def build_system_prompt(self) -> str: ...
    def parse_llm_output(self, output: Any) -> list: ...
    def get_output_model(self) -> type: ...


AgentRegistrar = create_registrar(
    "agent", AgentProtocol,
    discriminator_field="name",
    strip_suffixes=["Agent"],
)
```

**基类** — 使用 `AgentRegistrar.Meta` 作为 metaclass：

```python
# agents/base_react_agent.py

from agents._registrar import AgentRegistrar

class BaseReActAgent(metaclass=AgentRegistrar.Meta):
    """ReAct 循环基类。所有 agent 共享。

    __abstract__ = True 表示自身不注册到 agent 注册表。
    只有具体子类 (BrowserUseAgent 等) 会被注册。
    """
    __abstract__ = True

    def __init__(self, task, llm, session, config):
        self.task = task
        self.llm = llm
        self.session = session
        self.config = config

    async def run(self) -> AgentResult: ...
    async def step(self) -> StepResult: ...

    # 子类必须实现
    def build_system_prompt(self) -> str: ...
    def parse_llm_output(self, output) -> list: ...
    def get_output_model(self) -> type: ...
```

**路径 A — 继承自动注册**：

```python
# agents/browser_use/agent.py
class BrowserUseAgent(BaseReActAgent):
    # key = "browser_use" (strip "Agent" + snake_case)
    def build_system_prompt(self) -> str: ...
    def parse_llm_output(self, output) -> list: ...
    def get_output_model(self) -> type: ...

# agents/skyvern/agent.py
class SkyvernAgent(BaseReActAgent):
    # key = "skyvern"
    ...

# agents/agent_tars/agent.py
class AgentTarsAgent(BaseReActAgent):
    # key = "agent_tars"
    ...
```

**路径 B — 手动注册 (外部插件)**：

```python
from agents._registrar import AgentRegistrar

@AgentRegistrar.register("my_custom_agent")
class MyCustomAgent:
    """第三方 agent。不继承 BaseReActAgent，但实现了 AgentProtocol。"""
    async def run(self): ...
    def build_system_prompt(self): ...
    def parse_llm_output(self, output): ...
    def get_output_model(self): ...
```

**使用注册表**：

```python
# agents/runner.py

from agents._registrar import AgentRegistrar

class AgentRunner:
    async def run_single(self, task, agent_name: str, config):
        agent_cls = AgentRegistrar.get(agent_name)  # 替代 match/case
        agent = agent_cls(task=task.task_text, llm=llm, session=session, config=config)
        return await agent.run()

    async def run_parallel(self, task, agent_names: list[str], config):
        tasks = [self.run_single(task, name, config) for name in agent_names]
        return dict(zip(agent_names, await asyncio.gather(*tasks)))
```

---

### 2.2 LLM Provider Layer

```python
# core/llm/_registrar.py

from typing import Protocol, runtime_checkable
from layer_registry import create_registrar

@runtime_checkable
class ChatModelProtocol(Protocol):
    """LLM 接口契约。"""
    @property
    def model_name(self) -> str: ...
    async def ainvoke(self, messages, output_format=None, **kwargs): ...


LLMRegistrar = create_registrar(
    "llm", ChatModelProtocol,
    discriminator_field="provider",
    strip_prefixes=["Chat"],
)
```

```python
# core/llm/base.py
class ChatBaseModel(metaclass=LLMRegistrar.Meta):
    __abstract__ = True
    ...

# core/llm/providers/openai.py
class ChatOpenAI(ChatBaseModel):       # → "openai" (strip "Chat" + snake_case)
    ...

# core/llm/providers/anthropic.py
class ChatAnthropic(ChatBaseModel):    # → "anthropic"
    ...

# core/llm/providers/azure.py
class ChatAzureOpenAI(ChatBaseModel):
    __registry_key__ = "azure"         # 显式指定 (推断结果 "azure_open_ai" 不理想)
    ...

# core/llm/providers/deepseek.py
class ChatDeepSeek(ChatBaseModel):
    __registry_key__ = "deepseek"      # 显式修正 (推断结果 "deep_seek")
    ...

# core/llm/providers/google.py
class ChatGoogle(ChatBaseModel):       # → "google"
    ...

# core/llm/providers/bedrock.py
class ChatBedrock(ChatBaseModel):      # → "bedrock"
    ...
```

```python
# core/llm/factory.py — 工厂函数简化为一行查询

from core.llm._registrar import LLMRegistrar

def create_chat_model(config: LLMConfig) -> ChatBaseModel:
    """根据 config 创建 LLM 实例。Agent 调此函数，不感知具体 Provider。"""
    cls = LLMRegistrar.get(config.provider)
    return cls(config)
```

---

### 2.3 Browser Provider Layer

```python
# core/providers/_registrar.py

from typing import Protocol, runtime_checkable
from layer_registry import create_registrar

@runtime_checkable
class BrowserProviderProtocol(Protocol):
    """云浏览器供应商接口契约。"""
    @property
    def provider_id(self) -> str: ...
    async def open(self, config) -> CDPSession: ...
    async def close(self, session) -> None: ...
    def get_capability(self, cap_type): ...


ProviderRegistrar = create_registrar(
    "provider", BrowserProviderProtocol,
    discriminator_field="id",
    strip_suffixes=["Provider"],
)
```

```python
# core/providers/base.py
class BrowserProvider(metaclass=ProviderRegistrar.Meta):
    __abstract__ = True
    ...

# core/providers/lexmount.py
class LexmountProvider(BrowserProvider):     # → "lexmount"
    ...

# core/providers/agentbay.py
class AgentBayProvider(BrowserProvider):      # → "agent_bay"
    ...

# core/providers/local.py
class LocalProvider(BrowserProvider):         # → "local"
    ...

# core/providers/cloudnative.py
class CloudNativeProvider(BrowserProvider):   # → "cloud_native"
    ...
```

```python
# core/providers/registry.py — 工厂

from core.providers._registrar import ProviderRegistrar

def get_provider(config: ProviderConfig) -> BrowserProvider:
    cls = ProviderRegistrar.get(config.id)
    return cls(config)
```

---

### 2.4 Evaluator Layer

```python
# evaluator/_registrar.py

from typing import Protocol, runtime_checkable
from layer_registry import create_registrar

@runtime_checkable
class EvaluatorProtocol(Protocol):
    """评估器接口契约。"""
    async def evaluate(self, result) -> EvalResult: ...


EvaluatorRegistrar = create_registrar(
    "evaluator", EvaluatorProtocol,
    discriminator_field="evaluator",
    strip_suffixes=["Evaluator"],
)
```

```python
# evaluator/base.py
class BaseEvaluator(metaclass=EvaluatorRegistrar.Meta):
    __abstract__ = True
    ...

# evaluator/evaluators/lexbench.py
class LexBenchEvaluator(BaseEvaluator):
    __registry_key__ = "lexbench_browser"    # 显式指定，和数据集名对齐
    ...

# evaluator/evaluators/mind2web.py
class Mind2WebEvaluator(BaseEvaluator):
    __registry_key__ = "online_mind2web"     # 显式指定
    ...

# evaluator/evaluators/browsecomp.py
class BrowseCompEvaluator(BaseEvaluator):
    __registry_key__ = "browsecomp"
    ...
```

---

### 2.5 Benchmark Loader Layer

```python
# benchmark_data/_registrar.py

from typing import Protocol, runtime_checkable
from layer_registry import create_registrar

@runtime_checkable
class BenchmarkLoaderProtocol(Protocol):
    """Benchmark 数据加载器接口契约。"""
    def load_tasks(self) -> list: ...
    @property
    def meta(self) -> dict: ...


BenchmarkRegistrar = create_registrar(
    "benchmark", BenchmarkLoaderProtocol,
    discriminator_field="name",
    strip_suffixes=["Loader"],
)
```

```python
# benchmark_data/base.py
class BaseBenchmarkLoader(metaclass=BenchmarkRegistrar.Meta):
    __abstract__ = True
    ...

# benchmark_data/lexbench_browser/__init__.py
class LexBenchLoader(BaseBenchmarkLoader):
    __registry_key__ = "lexbench_browser"
    ...

# benchmark_data/online_mind2web/__init__.py
class Mind2WebLoader(BaseBenchmarkLoader):
    __registry_key__ = "online_mind2web"
    ...
```

---

### 2.6 DOM Filter Layer

```python
# core/dom/filters/_registrar.py

from typing import Protocol, runtime_checkable, Any
from layer_registry import create_registrar

@runtime_checkable
class DOMFilterProtocol(Protocol):
    """DOM 过滤器接口契约。"""
    def apply(self, dom: Any) -> Any: ...


DOMFilterRegistrar = create_registrar(
    "dom_filter", DOMFilterProtocol,
    discriminator_field="name",
    strip_suffixes=["Filter"],
)
```

```python
# core/dom/filters/base.py
class BaseDOMFilter(metaclass=DOMFilterRegistrar.Meta):
    __abstract__ = True
    ...

# core/dom/filters/visibility.py
class VisibilityFilter(BaseDOMFilter):       # → "visibility"
    ...

# core/dom/filters/interactivity.py
class InteractivityFilter(BaseDOMFilter):    # → "interactivity"
    ...

# core/dom/filters/paint_order.py
class PaintOrderFilter(BaseDOMFilter):       # → "paint_order"
    ...

# core/dom/filters/disabled_elements.py
class DisabledElementsFilter(BaseDOMFilter): # → "disabled_elements"
    ...
```

```python
# core/dom/filters/pipeline.py — 从 config 动态组装

from core.dom.filters._registrar import DOMFilterRegistrar

def build_pipeline(filter_configs: list[dict]) -> list:
    """从配置动态构建 filter 链。

    filter_configs 格式:
      - "VisibilityFilter"                     # 无参
      - {"VisibilityFilter": {"threshold": 1000}}  # 带参
    """
    filters = []
    for item in filter_configs:
        if isinstance(item, str):
            name, kwargs = item, {}
        else:
            name = next(iter(item))
            kwargs = item[name]
        filter_cls = DOMFilterRegistrar.get(name)
        filters.append(filter_cls(**kwargs))
    return filters
```

---

## 3. 模块发现与初始化

### 3.1 入口处触发发现

```python
# cli/run.py

from layer_registry import discover

def main():
    # 一次性递归导入所有实现模块 → 触发 AutoRegistrar → 填充注册表
    discover(
        "agents.browser_use",
        "agents.skyvern",
        "agents.agent_tars",
        "core.llm.providers",
        "core.providers",
        "evaluator.evaluators",
        "benchmark_data.lexbench_browser",
        "benchmark_data.online_mind2web",
        "benchmark_data.browsecomp",
        "core.dom.filters",
    )

    # 注册表已就绪，可以按 config 查询
    agent_cls = AgentRegistrar.get(config.agent.name)
    llm = LLMRegistrar.get(config.llm.provider)(config.llm)
    provider = ProviderRegistrar.get(config.provider.id)(config.provider)
    ...
```

### 3.2 注册时序

```
程序启动
    │
    ▼
cli/run.py → discover(...)
    │
    ├─ import agents.browser_use.agent
    │       └─ class BrowserUseAgent(BaseReActAgent)  定义
    │              └─ AutoRegistrar[agent].__new__()   拦截
    │                     └─ agent_registry.add("browser_use", BrowserUseAgent, check=False)
    │
    ├─ import core.llm.providers.openai
    │       └─ class ChatOpenAI(ChatBaseModel)         定义
    │              └─ AutoRegistrar[llm].__new__()     拦截
    │                     └─ llm_registry.add("openai", ChatOpenAI, check=False)
    │
    ├─ ... (其他模块同理)
    │
    ▼
所有注册表就绪
    │
    ▼
根据 config.yaml 查询 → 实例化 → 执行
```

---

## 4. config.yaml 与注册表的对应

```yaml
# config.yaml 中的 name/id/provider 字段直接作为注册表的查找 key

provider:
  id: lexmount               # → ProviderRegistrar.get("lexmount")

llm:
  provider: openai            # → LLMRegistrar.get("openai")

agent:
  name: browser_use           # → AgentRegistrar.get("browser_use")

dom:
  filter_pipeline:            # → DOMFilterRegistrar.get(each name)
    - DisabledElementsFilter
    - VisibilityFilter:
        viewport_threshold: 1000
    - InteractivityFilter
    - PaintOrderFilter
  serializer: tree            # → (可扩展 SerializerRegistrar)

benchmark:
  name: lexbench_browser      # → BenchmarkRegistrar.get("lexbench_browser")

eval:
  evaluator: lexbench_browser # → EvaluatorRegistrar.get("lexbench_browser")
```

---

## 5. 消费者项目文件结构

```
browseruse_bench/                          # 消费者项目（pip install layer-registry）
│
├── core/
│   ├── llm/
│   │   ├── _registrar.py                 #   ChatModelProtocol + LLMRegistrar
│   │   ├── base.py                       #   ChatBaseModel (metaclass=LLMRegistrar.Meta)
│   │   ├── factory.py                    #   create_chat_model() — 一行查询
│   │   └── providers/
│   │       ├── openai.py                 #   ChatOpenAI → auto "openai"
│   │       ├── anthropic.py              #   ChatAnthropic → auto "anthropic"
│   │       ├── azure.py                  #   ChatAzureOpenAI → explicit "azure"
│   │       ├── deepseek.py               #   ChatDeepSeek → explicit "deepseek"
│   │       ├── google.py                 #   ChatGoogle → auto "google"
│   │       └── bedrock.py               #   ChatBedrock → auto "bedrock"
│   │
│   ├── providers/
│   │   ├── _registrar.py                 #   BrowserProviderProtocol + ProviderRegistrar
│   │   ├── base.py                       #   BrowserProvider (metaclass=ProviderRegistrar.Meta)
│   │   ├── lexmount.py                   #   LexmountProvider → auto "lexmount"
│   │   ├── agentbay.py                   #   AgentBayProvider → auto "agent_bay"
│   │   ├── local.py                      #   LocalProvider → auto "local"
│   │   └── cloudnative.py               #   CloudNativeProvider → auto "cloud_native"
│   │
│   └── dom/filters/
│       ├── _registrar.py                 #   DOMFilterProtocol + DOMFilterRegistrar
│       ├── base.py                       #   BaseDOMFilter (metaclass=DOMFilterRegistrar.Meta)
│       ├── visibility.py                 #   → auto "visibility"
│       ├── interactivity.py              #   → auto "interactivity"
│       ├── paint_order.py                #   → auto "paint_order"
│       ├── disabled_elements.py          #   → auto "disabled_elements"
│       └── pipeline.py                   #   build_pipeline() 动态组装
│
├── agents/
│   ├── _registrar.py                     #   AgentProtocol + AgentRegistrar
│   ├── base_react_agent.py               #   BaseReActAgent (metaclass=AgentRegistrar.Meta)
│   ├── runner.py                         #   AgentRunner — AgentRegistrar.get() 查询
│   ├── browser_use/agent.py              #   → auto "browser_use"
│   ├── skyvern/agent.py                  #   → auto "skyvern"
│   └── agent_tars/agent.py              #   → auto "agent_tars"
│
├── benchmark_data/
│   ├── _registrar.py                     #   BenchmarkLoaderProtocol + BenchmarkRegistrar
│   ├── base.py                           #   BaseBenchmarkLoader
│   ├── lexbench_browser/                 #   → explicit "lexbench_browser"
│   ├── online_mind2web/                  #   → explicit "online_mind2web"
│   └── browsecomp/                       #   → explicit "browsecomp"
│
├── evaluator/
│   ├── _registrar.py                     #   EvaluatorProtocol + EvaluatorRegistrar
│   ├── base.py                           #   BaseEvaluator
│   └── evaluators/
│       ├── lexbench.py                   #   → explicit "lexbench_browser"
│       ├── mind2web.py                   #   → explicit "online_mind2web"
│       └── browsecomp.py                #   → explicit "browsecomp"
│
└── cli/
    └── run.py                            #   discover() 入口
```
