Skip to content

[Fix] Browser worker prompt only describes tools the worker actually has - #7

Closed
sirotaoleg wants to merge 1 commit into
ThinkFlowLab:jiuwen-jevfrom
sirotaoleg:pr/browser-prompt-matches-tool-surface
Closed

sirotaoleg wants to merge 1 commit into
ThinkFlowLab:jiuwen-jevfrom
sirotaoleg:pr/browser-prompt-matches-tool-surface

Conversation

@sirotaoleg

Copy link
Copy Markdown

Problem. The worker's system prompt always told the model to use browser_probe_interactives, browser_probe_cards, browser_batch_interact and browser_custom_action. But the tool list the worker receives often doesn't contain them:

  • the runtime never adds browser_custom_action;
  • callers that narrow the tool surface drop the rest (allowed_tool_names, or removing abilities after ensure_started(), as the WebVoyager harness does).

Fix. The prompt now renders each helper fragment only when its tools are really in the tool list sent on that request.

  • build_browser_worker_system_prompt() takes tool_names.
  • refresh_browser_worker_system_prompt() re-renders from ability_manager.list_tool_info(), the same source the ReAct loop uses for the tool list. BrowserService calls it before every worker invoke.
  • With the full tool surface the prompt is byte-identical to today (pinned by a sha256 test).

Result. −22% input tokens per task on the same tasks; step count unchanged.

Tests. test_browser_prompt_tool_surface.py:

  • the prompt never names a tool that isn't in the request;
  • full surface → unchanged prompt;
  • the refresh picks up tools removed after start.

The Playwright worker system prompt was a static string that told the
model to use browser_probe_interactives, browser_probe_cards,
browser_batch_interact and browser_custom_action unconditionally. The
tool list the model receives often lacks them: BrowserAgentRuntime never
adds browser_custom_action / browser_list_custom_actions to the worker,
and callers that narrow the surface (allowed_tool_names, ability removal
after ensure_started(), as the WebVoyager harness does) drop the rest.

Evidence (WebVoyager, 105 tasks, @playwright/mcp 0.0.78, agent-core
18b840c): every request had tool_count=14 (CORE_BROWSER_TOOL_NAMES)
while the prompt pushed probe/batch tools; the model noted on step 1
that those tools were not in its list. Tool use fell back to
browser_find + browser_click pairs (evaluate 326, find 255, navigate
241, click 143 over 90 tasks); Google Flights averaged 47.8 tool calls
per task (max 60, ~304 s against a 600 s timeout), Amazon 15.7,
Booking 15.3; the prompt cost ~5.4k input tokens per request.

Invariant: every tool name the worker prompt tells the model to use is
in the tool list sent on that request. Guidance for an absent tool is
not rendered at all, not even as a negation.

- build_browser_worker_system_prompt() takes keyword-only tool_names and
  renders each helper fragment (probes, PageState/generation_id,
  batch_interact, the batch-vs-custom_action note) only when its tools
  are present. tool_names=None keeps every fragment.
- refresh_browser_worker_system_prompt() re-renders the worker's prompt
  template from ability_manager.list_tool_info(), the same call
  ReActAgent uses to build the request's tool list. BrowserService calls
  it before each worker invoke, so helpers added by the runtime and
  abilities removed after ensure_started() are both reflected.

Full tool surface: prompt unchanged (byte-identical, pinned by sha256).
The conditional browser_run_code / browser_take_screenshot wording is
left as it was.
@sirotaoleg

Copy link
Copy Markdown
Author

Superseded by #12

@sirotaoleg sirotaoleg closed this Oct 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant