跳转到内容

多模型适配 — Provider 切换与故障转移

学习目标

  • 理解 Browser Use 的 BaseChatModel Protocol 抽象
  • 掌握模型特定优化的实现方式
  • 了解 fallback LLM 的故障转移机制

前置知识

下文假设你已理解多模型适配的通用原理,直接聚焦 Browser Use 的具体实现。


项目实践

Protocol 抽象层

Browser Use 使用 Python typing.Protocol + @runtime_checkable 定义模型接口:

@runtime_checkable
class 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)是独立的目录实现
  • 所有实现都满足 BaseChatModel Protocol
  • 用户只需改 llm=ChatXXX(model="...") 即可切换 Provider

支持的 Provider 矩阵

Provider目录视觉支持结构化输出
OpenAIllm/openai/output_format
Anthropicllm/anthropic/output_format
Googlellm/google/output_format
Groqllm/groq/部分output_format
Ollamallm/ollama/部分output_format
LiteLLMllm/litellm/依赖底层output_format
Browser Usellm/browser_use/output_format
DeepSeekllm/deepseek/output_format
Mistralllm/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 已经足够。


参考来源