From e3107a645d34a3667c8821665b8b55fd11168071 Mon Sep 17 00:00:00 2001 From: pewdiepie-archdaemon Date: Thu, 17 Sep 2026 19:02:11 +0000 Subject: [PATCH] enforce distinct research refinement --- src/clean_agent_preview.py | 54 ++++++++++++++++++++++++++----- tests/test_clean_agent_preview.py | 22 +++++++++++++ 2 files changed, 68 insertions(+), 8 deletions(-) diff --git a/src/clean_agent_preview.py b/src/clean_agent_preview.py index bf566027c..f1e3e1d42 100644 --- a/src/clean_agent_preview.py +++ b/src/clean_agent_preview.py @@ -1662,6 +1662,10 @@ def normalize_preview_function_args(name, args, *, user_text=''): else: normalized_urls.append(item) args['urls'] = normalized_urls + single_url = str(args.get('url') or '').strip() + if single_url: + args['urls'] = list(dict.fromkeys([single_url, *args['urls']])) + args.pop('url', None) if canonical(name) == 'bash' and isinstance(args.get('command'), str): if re.search(r'\bhostname\b', str(user_text or ''), re.I): args['command'] = re.sub( @@ -3129,6 +3133,25 @@ def normalized_search_intent(query): return ' '.join(token for token in tokens if token not in cosmetic) +def repeated_search_refinement(query, prior_intents): + """Whether a follow-up only changes cosmetic freshness wording.""" + temporal = { + 'latest', 'recent', 'current', 'currently', 'today', 'week', 'month', + 'year', 'daily', 'weekly', 'now', 'new', 'this', 'past', + } + current = set(normalized_search_intent(query).split()) - temporal + if not current: + return False + for prior in prior_intents or (): + previous = set(str(prior or '').split()) - temporal + if current == previous: + return True + union = current | previous + if union and len(current & previous) / len(union) >= 0.8: + return True + return False + + def requested_web_source_links(user_text): return bool(re.search( r'\b(?:return|give|show|include|provide|cite|find)\b.{0,35}\b(?:source\s+)?links?\b' @@ -3767,6 +3790,7 @@ async def stream_preview(*, endpoint_url, model, messages, headers, turn_contrac permanently_suppressed_tools = set() successful_duplicate_counts = {} empty_search_intents = {} + successful_search_intents = [] empty_web_search_attempts = 0 successful_web_searches = 0 successful_web_retrievals = 0 @@ -3788,7 +3812,7 @@ async def stream_preview(*, endpoint_url, model, messages, headers, turn_contrac artifact_write_phase = False suppression_completion_attempted = False budget_completion_attempted = False - answer_recovery_attempted = False + answer_recovery_attempts = 0 force_no_tools_next_round = False force_web_search_next_round = False force_private_browser_next_round = False @@ -4046,10 +4070,10 @@ async def stream_preview(*, endpoint_url, model, messages, headers, turn_contrac break if ( contentless_final_response(content) - and not answer_recovery_attempted + and answer_recovery_attempts == 0 and round_number < round_limit ): - answer_recovery_attempted = True + answer_recovery_attempts += 1 force_no_tools_next_round = True replace_streamed_draft_on_finish = True history.pop() @@ -4088,21 +4112,22 @@ async def stream_preview(*, endpoint_url, model, messages, headers, turn_contrac if ( successful_web_searches and incomplete_broad_web_answer(content, direct_user_text) - and not answer_recovery_attempted + and answer_recovery_attempts < 2 and round_number < round_limit ): - answer_recovery_attempted = True + answer_recovery_attempts += 1 force_no_tools_next_round = True replace_streamed_draft_on_finish = True history.pop() history.append({ 'role': 'user', '_harness_control': True, 'content': ( - 'Completion check: the draft is only a fragment and does not ' + 'Completion check: the draft is still too shallow and does not ' 'answer the broad current-information request. Using the Web ' 'evidence already gathered, provide a complete useful briefing ' - 'with the main findings, source attribution, and any evidence ' - 'limitations. Do not call another tool.' + 'of at least several substantive paragraphs or equivalent bullets, ' + 'with the main findings, context, source links, and any evidence ' + 'limitations. Do not call another tool or return another one-sentence summary.' ), }) yield event({ @@ -4409,6 +4434,16 @@ async def stream_preview(*, endpoint_url, model, messages, headers, turn_contrac ) if tool_type == 'web_search': search_intent = normalized_search_intent(args.get('query')) + if repeated_search_refinement( + args.get('query'), successful_search_intents, + ): + force_web_search_next_round = True + calls += 1 + raise ValueError( + 'This follow-up search is equivalent to an earlier successful ' + 'query. Change the angle, missing subtopic, source type, or ' + 'corroboration target instead of only changing freshness wording.' + ) if search_intent and empty_search_intents.get(search_intent, 0) >= 2: calls += 1 raise ValueError( @@ -4748,6 +4783,9 @@ async def stream_preview(*, endpoint_url, model, messages, headers, turn_contrac }) if not failed and canonical(actual_tool) == 'web_search': successful_web_searches += 1 + successful_intent = normalized_search_intent(args.get('query')) + if successful_intent: + successful_search_intents.append(successful_intent) if successful_web_searches == 2 and not required_artifacts: round_recovery_messages.append( 'Research discovery is complete after two searches. Do not search ' diff --git a/tests/test_clean_agent_preview.py b/tests/test_clean_agent_preview.py index f872c5448..edab32592 100644 --- a/tests/test_clean_agent_preview.py +++ b/tests/test_clean_agent_preview.py @@ -1864,6 +1864,28 @@ def test_broad_current_web_request_covers_natural_phrasings(prompt): assert broad_current_web_request(prompt) +def test_followup_search_must_change_subject_angle_not_only_freshness(): + from src.clean_agent_preview import repeated_search_refinement + + assert repeated_search_refinement('latest AI news this week', ['ai news']) + assert repeated_search_refinement('current Sweden events', ['sweden events']) + assert not repeated_search_refinement( + 'Sweden election coalition negotiations', ['sweden current events'] + ) + + +def test_web_fetch_collapses_single_and_batch_url_fields_without_losing_targets(): + tool, args = normalize_preview_function_args('web_fetch', { + 'url': 'https://example.org/a', + 'urls': ['https://example.org/a', 'https://example.org/b'], + }) + + assert tool == 'web_fetch' + assert args == { + 'urls': ['https://example.org/a', 'https://example.org/b'], + } + + def test_preview_allows_only_reversible_client_local_ui_control(): assert preview_call_allowed( 'ui_control', {'action': 'open_panel', 'name': 'gallery'}, 'open gallery'