[eric] browser: OSW_BROWSER_NO_LEVERS bench flag to A/B the speed levers on/off

This commit is contained in:
ciregenz
2026-06-06 12:12:38 -07:00
parent 581af5b128
commit 01ec866c2a
2 changed files with 32 additions and 15 deletions
+6 -5
View File
@@ -63,6 +63,7 @@ from backend.apps.agents.browser import browser_wait
from backend.apps.agents.browser import browser_schema
from backend.apps.agents.browser.browser_schema import (
_ACTION_TOOLS_REQUIRING_REPORT,
_LEVERS_ON,
ACTION_MAP,
BROWSER_TOOLS_SCHEMA,
MAX_TURNS,
@@ -802,7 +803,7 @@ async def run_browser_agent(
# wording), but a similar verified route may exist; hand it to the model as
# advisory text so it follows a known path instead of re-exploring.
route_hint_keys: list[tuple] = []
if not replay_prefix_note:
if _LEVERS_ON and not replay_prefix_note:
_h_skill, _h_score = browser_skills.find_similar_skill(replay_host, skill_key_task)
if _h_skill:
_hint, route_hint_keys = browser_skills.render_route_hint(_h_skill, skill_key_task, _h_score)
@@ -816,7 +817,7 @@ async def run_browser_agent(
# Pre-nav landed on a results page (the cold entry case): scan it NOW so the
# model's very first turn can pick a candidate instead of read-then-decide.
_start_url = (current_url or initial_url or "").split("#")[0]
if _start_url and _RESULTS_URL_RE.search(_start_url):
if _LEVERS_ON and _start_url and _RESULTS_URL_RE.search(_start_url):
auto_scanned_urls.add(_start_url)
_scan_json, _sc_ms = await _scan_results(task)
if _scan_json:
@@ -1442,7 +1443,7 @@ async def run_browser_agent(
# per URL, only on the tight throwaway-dismiss vocabulary that
# never sits on a task-needed control, so it can't close anything
# required. After closing, re-list so the model sees the page beneath.
if tu.name in _AUTO_STATE_TOOLS and "error" not in result:
if _LEVERS_ON and tu.name in _AUTO_STATE_TOOLS and "error" not in result:
_pop_url = (result.get("url") or last_seen_url or "").split("#")[0]
if _pop_url and _pop_url not in dismissed_popup_urls:
_close = interstitial_dismiss_target("\n".join(attached_state_seen))
@@ -1464,7 +1465,7 @@ async def run_browser_agent(
# costs a read-then-decide turn pair; the cheap aux model reads it
# now so the pick happens on this same turn. Capped, per-URL,
# fail-silent (a miss just means the old two-turn dance).
if (tu.name in _AUTO_STATE_TOOLS and "error" not in result
if (_LEVERS_ON and tu.name in _AUTO_STATE_TOOLS and "error" not in result
and auto_scan_count < _AUTO_SCAN_MAX_PER_RUN):
_scan_url = (result.get("url") or last_seen_url or "").split("#")[0]
if _scan_url and _scan_url not in auto_scanned_urls and _RESULTS_URL_RE.search(_scan_url):
@@ -1513,7 +1514,7 @@ async def run_browser_agent(
if replay_prefix_note:
result["text"] = f"{result.get('text') or ''}{replay_prefix_note}"
replay_prefix_note = ""
elif not route_hint_keys:
elif _LEVERS_ON and not route_hint_keys:
_h_skill, _h_score = browser_skills.find_similar_skill(cur_host, skill_key_task)
if _h_skill:
_hint, route_hint_keys = browser_skills.render_route_hint(_h_skill, skill_key_task, _h_score)
+26 -10
View File
@@ -6,6 +6,30 @@ prompt, and the turn/report invariants. Exceeds the 300-LOC soft ceiling on
purpose because it is one cohesive data blob, not multiple responsibilities.
"""
import os
# Bench-only A/B switch: OSW_BROWSER_NO_LEVERS=1 reverts the prompt to its
# pre-speed-lever behavior (narration allowed beside actions, no merge-verify
# shortcut) so a controlled run can measure whether the levers move the needle.
# Default (unset) = levers ON, the shipped behavior.
_LEVERS_ON = os.environ.get("OSW_BROWSER_NO_LEVERS", "") not in ("1", "true", "TRUE")
_THINK_SHORTER = (
"Do NOT also write a free-text sentence next to your action tools: your ReportProgress "
"fields ARE your thinking, and a separate prose explanation just repeats them and slows "
"the turn (it is shown to the user twice). The ONLY time to write a plain message is your "
"FINAL turn, when the task is done and you call no action tool: that message is your "
"answer to the user (the OUTCOME line). Every other turn: ReportProgress + tools, no prose.\n"
) if _LEVERS_ON else ""
_MERGE_VERIFY = (
"When that `expect` CONFIRMS (the result says 'Confirmed: ...'), that IS your "
"verification: go STRAIGHT to your final OUTCOME line and cite it. Do NOT spend an "
"extra screenshot or read turn to re-check what the confirmation already proved, that "
"is a wasted round-trip. Only take a separate verification step when `expect` came "
"back 'NOT confirmed' or you forgot to pass one.\n"
) if _LEVERS_ON else ""
MODEL_MAP = {
"sonnet": "claude-sonnet-4-6",
"opus": "claude-opus-4-6",
@@ -621,11 +645,7 @@ SYSTEM_PROMPT = (
"needs (the exact selector, index, or value). Each token you write is generated one at a "
"time and is the main thing that slows a turn, so write the fewest that still carry the "
"plan forward. Only write working_memory when you learn something NEW this turn; else 'none'.\n"
"Do NOT also write a free-text sentence next to your action tools: your ReportProgress "
"fields ARE your thinking, and a separate prose explanation just repeats them and slows "
"the turn (it is shown to the user twice). The ONLY time to write a plain message is your "
"FINAL turn, when the task is done and you call no action tool: that message is your "
"answer to the user (the OUTCOME line). Every other turn: ReportProgress + tools, no prose.\n"
+ _THINK_SHORTER +
"Emit ReportProgress and your action tool(s) together in the same response. "
"If you skip ReportProgress, your action tools will be REJECTED with an error "
"and you will have to retry. This is not optional. Read-only tools "
@@ -642,11 +662,7 @@ SYSTEM_PROMPT = (
"last one in the thread), pass `expect` set to proof it landed, and NEVER fire it a "
"second time unless you have verified the first did NOT go through. This is how you "
"avoid both ghost-successes and double-sends.\n"
"When that `expect` CONFIRMS (the result says 'Confirmed: ...'), that IS your "
"verification: go STRAIGHT to your final OUTCOME line and cite it. Do NOT spend an "
"extra screenshot or read turn to re-check what the confirmation already proved, that "
"is a wasted round-trip. Only take a separate verification step when `expect` came "
"back 'NOT confirmed' or you forgot to pass one.\n\n"
+ _MERGE_VERIFY + "\n"
"## Loop awareness\n"
"If you see a tool result containing 'LOOP DETECTED' or '⚠️', it means you "