diff --git a/backend/apps/agents/browser/browser_fast_path.py b/backend/apps/agents/browser/browser_fast_path.py index 2d03bc4e..b30d0b9a 100644 --- a/backend/apps/agents/browser/browser_fast_path.py +++ b/backend/apps/agents/browser/browser_fast_path.py @@ -54,7 +54,10 @@ P_CLASSIFIER_SYSTEM = ( "spreadsheets, SMS to a phone number, or other desktop apps. Also NO for " "plain conversation or questions answerable without visiting any site. " "Also NO when the task names an app the user message says is connected as " - "a native integration: the main agent's own tools beat a browser there.\n" + "a native integration: the main agent's own tools beat a browser there. " + "Also NO when the task needs MORE THAN TWO distinct pages or sites visited " + "(comparing several articles, per-item facts across a list): you route one " + "focused visit, not a sweep.\n" "The browser is the LAST resort: prefer READ over ACT whenever the goal is " "information rather than an action on a page, and prefer NO over READ when " "a plain web search could answer it without visiting a specific page.\n" @@ -66,6 +69,8 @@ P_CLASSIFIER_SYSTEM = ( "'count the messages in my linkedin thread with bob' -> ACT\n" "'find the report on stripe.com and save it to my desktop' -> NO\n" "'text 555-0102 that I'm late' -> NO\n" + "'fetch the wikipedia articles for six scientists and give a table' -> NO (a " + "multi-page sweep, not one visit)\n" "If line 1 is NO, reply with exactly the word NO and nothing else.\n" "If line 1 is READ or ACT, follow it with a short browsing brief:\n" "ENTRY: the best starting URL; use a direct deep/search URL when the site's " @@ -93,6 +98,9 @@ def fast_path_eligible( return False if not prompt or not prompt.strip(): return False + # A sweep across many pages is main-loop work: the fast path does ONE focused visit, and a grabbed sweep answers "INSUFFICIENT" from a single read (ENG-355). + if len(set(re.findall(r'https?://[^\s<>"\')\]]+', prompt))) >= 3: + return False return bool(P_BROWSY_RE.search(prompt)) diff --git a/backend/tests/test_browser_fast_path.py b/backend/tests/test_browser_fast_path.py index c2f17771..ee95a3b7 100644 --- a/backend/tests/test_browser_fast_path.py +++ b/backend/tests/test_browser_fast_path.py @@ -189,3 +189,19 @@ def test_connected_integration_names_route_native_not_browser(): assert fp.browsy_beyond_connected("go to slack.com and read the pricing page", ["slack"]) is True assert fp.browsy_beyond_connected("add a row to my Notion tracker", ["notion"]) is False assert fp.browsy_beyond_connected("send a linkedin message to my recruiter", ["slack", "notion"]) is True + + +def test_multi_url_sweep_not_eligible(): + """ENG-355: 3+ distinct URLs is a sweep; the fast path does one visit and a grabbed sweep answered INSUFFICIENT.""" + from backend.apps.agents.browser.browser_fast_path import fast_path_eligible + sweep = ("fetch https://en.wikipedia.org/wiki/A and https://en.wikipedia.org/wiki/B " + "and https://en.wikipedia.org/wiki/C and report each") + assert fast_path_eligible(sweep, "agent", "d1", True, False) is False + + +def test_single_and_double_url_still_eligible(): + from backend.apps.agents.browser.browser_fast_path import fast_path_eligible + one = "open https://news.ycombinator.com and tell me the top story" + two = "compare https://a.com/x with https://b.com/y and tell me which loads" + assert fast_path_eligible(one, "agent", "d1", True, False) is True + assert fast_path_eligible(two, "agent", "d1", True, False) is True