多模型适配 — Provider 切换与故障转移
学习目标
- 理解 Browser Use 的
BaseChatModelProtocol 抽象 - 掌握模型特定优化的实现方式
- 了解 fallback LLM 的故障转移机制
前置知识
- 多模型适配架构 — 统一抽象、认证策略、传输层重试
下文假设你已理解多模型适配的通用原理,直接聚焦 Browser Use 的具体实现。
项目实践
Protocol 抽象层
Browser Use 使用 Python typing.Protocol + @runtime_checkable 定义模型接口:
@runtime_checkableclass BaseChatModel(Protocol): model: str
@property def provider(self) -> str: ...
async def ainvoke( self, messages: list[BaseMessage], output_format: type[T] | None = None, **kwargs: Any ) -> ChatInvokeCompletion[T] | ChatInvokeCompletion[str]: ...与 Provider 支持的关系:
- 每个 Provider(OpenAI、Anthropic、Google、Groq、Ollama、LiteLLM、Mistral、DeepSeek、Cerebras、Vercel、Azure、AWS Bedrock、OCI、OpenRouter)是独立的目录实现
- 所有实现都满足
BaseChatModelProtocol - 用户只需改
llm=ChatXXX(model="...")即可切换 Provider
支持的 Provider 矩阵
| Provider | 目录 | 视觉支持 | 结构化输出 |
|---|---|---|---|
| OpenAI | llm/openai/ | 是 | output_format |
| Anthropic | llm/anthropic/ | 是 | output_format |
llm/google/ | 是 | output_format | |
| Groq | llm/groq/ | 部分 | output_format |
| Ollama | llm/ollama/ | 部分 | output_format |
| LiteLLM | llm/litellm/ | 依赖底层 | output_format |
| Browser Use | llm/browser_use/ | 是 | output_format |
| DeepSeek | llm/deepseek/ | 否 | output_format |
| Mistral | llm/mistral/ | 部分 | output_format |
模型特定自动优化
Browser Use 在 Agent 初始化时根据模型名称自动调整:
超时优化:
# 伪代码match model_name.lower(): if "gemini" in name: return 90 if "3-pro" in name else 75 if "groq" in name: return 30 # 推理快但易限流 if "o3" or "claude" or "sonnet" or "deepseek" in name: return 90 # 思考模式需要更长时间 default: return 75截图尺寸自适应:
if llm_screenshot_size is None: if model_name.startswith('claude-sonnet'): llm_screenshot_size = (1400, 850) # Claude 最优视觉分辨率视觉自动禁用:
- DeepSeek 模型:不支持图片输入,自动设置
use_vision=False - Grok-3 / grok-code:不支持视觉,自动禁用
Flash Mode 自动启用:
- Browser Use 自研模型(
browser-use/bu-*):自动启用flash_mode=True,因为模型经过 fine-tuning 不需要冗长的系统提示
Fallback LLM 机制
当主 LLM 遇到可重试错误时,自动切换到 fallback:
def _try_switch_to_fallback_llm(self, error): # 已在使用 fallback → 不能再 fallback if self._using_fallback_llm: return False
# 检查是否可重试(401/402/429/500/502/503/504) retryable_status_codes = {401, 402, 429, 500, 502, 503, 504} is_retryable = isinstance(error, ModelRateLimitError) or ( hasattr(error, 'status_code') and error.status_code in retryable_status_codes ) if not is_retryable: return False
# 检查是否有 fallback 配置 if self._fallback_llm is None: return False
# 切换(永久,不切回) self.llm = self._fallback_llm self._using_fallback_llm = True return True使用示例:
agent = Agent( task="...", llm=ChatOpenAI(model="gpt-4o"), fallback_llm=ChatAnthropic(model="claude-sonnet-4-6"),)问题与规避
模型名称匹配规则过时
问题:新模型发布时,硬编码的名称匹配规则(如 'claude-sonnet-4' in model_name)可能不匹配新命名。
对策:
- 匹配规则使用前缀而非精确匹配(如
'claude-sonnet'而非'claude-sonnet-4-6') - 用户可手动覆盖所有自动配置(如
llm_screenshot_size=(1600, 900)) - 日志中输出自动配置的值,便于调试
Fallback 切换后的 Token 计费
问题:切换到 fallback 后,两种模型的 Token 价格不同,计费可能混乱。
对策:
TokenCost服务分别追踪主 LLM 和 fallback LLM 的用量- 日志中标注当前使用的模型名称
- 用户可以通过
is_using_fallback_llm属性查询当前状态
不同 Provider 的结构化输出差异
问题:虽然都支持 output_format,但各 Provider 的实现细节不同(如 Anthropic 使用 tool_use,OpenAI 使用 json_schema)。
对策:
- 每个 Provider 实现自行处理
output_format参数 - Agent 层无需关心底层实现差异
- 返回的
ChatInvokeCompletion统一为解析后的 Pydantic 对象
设计取舍
Protocol vs 基类
| 方案 | 优势 | 代价 |
|---|---|---|
| Protocol | 不需要继承关系、任何满足协议的类都可以 | 无运行时验证、只检查属性存在性 |
| 抽象基类 | 可以包含共享实现、运行时类型检查 | 限制继承灵活性 |
Browser Use 选择 Protocol 是因为各 Provider 的实现差异巨大(HTTP 调用、SDK 封装、本地推理),几乎没有可以共享的基类逻辑。
两级 Fallback vs 多级
| 方案 | 优势 | 代价 |
|---|---|---|
| 两级(主 + fallback) | 简单可预测 | fallback 也失败时无更多选择 |
| 多级链 | 更高的可用性 | 状态管理复杂、调试困难 |
Browser Use 选择两级:对于大多数自动化场景,主 provider 出问题切换到备用 provider 已经足够。