工具调用协议 — LLM 决策到浏览器操作的映射
学习目标
- 理解 Browser Use 如何将 LLM 的 JSON 输出映射为浏览器动作
- 掌握动态 discriminated union Pydantic 模型的生成机制
- 了解页面感知工具过滤和坐标点击的实现
前置知识
- 工具调用协议与执行模型 — Function Calling 协议、动态工具模型、上下文感知可用性
下文假设你已理解工具调用的通用原理,直接聚焦 Browser Use 的具体实现。
项目实践
动态 Discriminated Union 模型生成
Browser Use 的 Registry.create_action_model() 在运行时动态生成包含所有注册动作的联合 Pydantic 模型:
# 伪代码示意ActionModel = Annotated[ Union[ Annotated[ClickElementAction, Field("click_element")], Annotated[TypeTextAction, Field("type_text")], Annotated[NavigateAction, Field("navigate")], Annotated[ScrollAction, Field("scroll")], # ... 更多动作 ], Field(discriminator=True)]关键设计:
- 每个动作对应一个 Pydantic 模型,包含参数定义
- 使用 Pydantic v2 的
discriminator字段实现快速反序列化 - 当注册新工具(通过
@tools.action()装饰器)后,重新调用create_action_model()生成新模型 - Agent 的
AgentOutput类型使用type_with_custom_actions()工厂方法,将动态 ActionModel 嵌入输出 schema
内置动作集合
Browser Use 提供 15+ 内置浏览器动作:
| 动作 | 参数 | 说明 |
|---|---|---|
click_element | index 或 coordinate | 点击交互元素 |
type_text | index, text | 向输入框输入文本 |
navigate | url, new_tab | 导航到 URL |
scroll | direction, amount | 滚动页面 |
go_back | — | 浏览器后退 |
extract | query | 从页面提取信息 |
screenshot | — | 截取当前页面 |
switch_tab | target_id | 切换到标签页 |
close_tab | — | 关闭当前标签页 |
search_google | query | Google 搜索 |
send_keys | keys | 发送键盘快捷键 |
upload_file | index, paths | 上传文件 |
get_dropdown_options | index | 获取下拉选项 |
select_dropdown_option | index, value | 选择下拉选项 |
页面过滤动作
根据当前页面 URL 动态调整可用动作:
# 在 Registry 中注册 URL 级过滤registry.register_action( name="click_buy_button", description="点击购买按钮", url_filter=lambda url: "cart" in url or "checkout" in url)Agent 在每次 turn 前调用 registry.get_prompt_description(url) 获取当前页面可用的动作描述,注入到上下文。这减少了发送给 LLM 的 schema 大小,并避免了在不适用的页面上出现不相关的动作。
坐标点击
对支持视觉的模型(Claude Sonnet/Opus、Gemini 3 Pro、Browser Use 自研模型),工具返回元素的像素坐标 (x, y) 而非仅索引:
# 在 Agent 初始化时自动检测model_name = getattr(llm, 'model', '').lower()supports_coordinate_clicking = any( pattern in model_name for pattern in [ 'claude-sonnet-4', 'claude-opus-4', 'gemini-3-pro', 'browser-use/' ])if supports_coordinate_clicking: tools.set_coordinate_clicking(True)坐标点击的优势是不受 DOM 动态变化的影响——LLM 直接在截图上定位点击位置。
动作超时保护
每个动作有独立的超时限制:
- 默认:180s(
_ACTION_TIMEOUT_FALLBACK_S) - 环境变量:
BROWSER_USE_ACTION_TIMEOUT_S覆盖 - 超时后返回
ActionResult(error="timeout")而非挂起
问题与规避
工具 schema 过大消耗 Token
问题:当注册了大量自定义工具时,所有工具的 JSON Schema 会显著增加 prompt 大小。
对策:
- 使用页面过滤:只发送当前页面可用的工具
- 将常用工具保持精简的参数定义
- 对不常用工具使用
exclude_actions参数临时禁用
动态 DOM 导致元素索引失效
问题:Agent 获取 DOM 快照后,页面可能动态变化,导致之前分配的索引 [5] 指向不同的元素。
对策:
- 每次 step 重新获取 DOM 快照,索引仅在单步内有效
- 坐标点击进一步降低了索引失效的风险(基于截图而非 DOM)
- 动作执行时通过 CDP 重新验证元素存在性
LLM 输出格式错误
问题:LLM 可能返回不符合 discriminated union schema 的 JSON。
对策:
- Pydantic 校验失败时,捕获
ValidationError并返回错误消息 - 错误消息包含期望的 schema 格式,引导 LLM 修正
- 对于空动作输出,自动注入安全 noop(
donewithsuccess=False)
设计取舍
元素索引 vs 坐标点击
| 方案 | 优势 | 代价 |
|---|---|---|
| 元素索引 | 不依赖视觉、所有模型通用 | DOM 动态变化时失效 |
| 坐标点击 | 抗 DOM 变化、更贴近人类操作 | 需要视觉模型、对截图质量敏感 |
Browser Use 两者兼有:对视觉模型启用坐标点击,对纯文本模型回退到索引。
动态模型 vs 静态模型
| 方案 | 优势 | 代价 |
|---|---|---|
| 动态 discriminated union | 工具热插拔、运行时扩展 | 类型检查器支持弱 |
| 静态 Union 类型 | 类型安全、IDE 提示好 | 修改工具需改代码 |
Browser Use 选择动态模型是为了支持用户通过 @tools.action() 装饰器在运行时注册自定义工具。