Deep research: don't treat a bare 'yes' as the research topic (#858)

Deep research asks 2-3 clarifying questions first. When the user answers
with a bare affirmation ('yes', 'ok', 'go ahead'), that short message
becomes latest_message and the query-synthesis fallback returned it
verbatim, so research ran on the literal word 'yes'.

In ResearchHandler.synthesize_query, when synthesis can't run (history
too short) or fails, fall back to the earliest substantive user message
(the original ask) only when the latest message is an explicit
affirmation/continuation phrase or is empty/punctuation-only. There is
deliberately no length heuristic: a short answer like 'UK', 'C++', or
'Rust' in a clarification flow is a real topic and is left untouched.

Tests cover query/topic selection: bare 'yes' -> original ask, short
answers (UK, C++) kept, short-only-substantive message kept, and a
multi-word follow-up still flows through synthesis.
This commit is contained in:
Mahdi Salmanzade
2026-06-02 06:30:53 +04:00
committed by GitHub
parent 00f16d66a3
commit e152a339d1
2 changed files with 135 additions and 2 deletions

View File

@@ -69,8 +69,40 @@ class ResearchHandler:
"""
# Build conversation context from history
history = getattr(sess, 'history', [])
# A bare affirmation ("yes", "ok", "go ahead") is the user accepting the
# clarifying-question round, NOT a research topic — researching the word
# "yes" is the classic failure here. When synthesis can't run or fails,
# fall back to the earliest substantive user message (the original ask)
# rather than the literal follow-up.
#
# Match on an explicit affirmation/continuation phrase only (plus the
# empty/punctuation-only case). We deliberately do NOT use a length
# heuristic: a short answer like "UK", "C++", or "Rust" is a real topic
# in a clarification flow and must be left untouched.
_AFFIRMATIONS = {
"yes", "y", "yeah", "yep", "yup", "sure", "sure thing", "ok", "okay",
"k", "kk", "go", "go ahead", "go for it", "do it", "please",
"yes please", "sounds good", "continue", "proceed", "lets go",
"let's go", "yes go ahead",
}
def _normalize(text: str) -> str:
return (text or "").strip().lower().strip("!.? ")
def _fallback() -> str:
normalized = _normalize(latest_message)
if normalized and normalized not in _AFFIRMATIONS:
return latest_message # short or long, it's a real topic
# Affirmation, or empty/punctuation-only: use the original ask.
for m in history:
c = (m.content or "").strip()
if m.role == "user" and c and _normalize(c) not in _AFFIRMATIONS:
return c
return latest_message
if len(history) <= 1:
return latest_message # No conversation to synthesize
return _fallback() # No conversation to synthesize
# Take last 6 messages max for context
recent = history[-6:]
@@ -104,7 +136,7 @@ class ResearchHandler:
except Exception as e:
logger.warning(f"Query synthesis failed: {e}")
return latest_message # Fallback
return _fallback()
async def generate_plan(
self, query: str, llm_endpoint: str, llm_model: str, llm_headers: dict = None,