From d1c7ce88dfa8e97cf2ce997d79905064e63f9d59 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Sat, 28 Feb 2026 13:07:39 -0500 Subject: [PATCH] Fix intent classifier missing Research button requests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two fixes for the intent model failing to route 'Research: X' messages to research_topic: 1. Fast-path in classify_intent: if the message matches ^Research:\s+.+ (the exact format the UI Research button always sends), skip the LLM call entirely and return research_topic with high confidence. This is 100% reliable and saves an unnecessary model call for this pattern. 2. Expanded research_topic rule examples in the system prompt to include "Research: X" prefix format, shopping-style queries ("research where to buy X"), and clarification that the topic is everything after the keyword — improves LLM routing for natural-language research requests that don't match the previous narrow examples. Root cause: qwen2.5:1.5b misclassified "Research: where to buy three- quarter sleeve tee shirts" as general chat (shopping query phrasing combined with the colon confused the small model). Co-Authored-By: Claude Sonnet 4.6 --- src/fabledassistant/services/intent.py | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/fabledassistant/services/intent.py b/src/fabledassistant/services/intent.py index 0473bf6..7d8b1e8 100644 --- a/src/fabledassistant/services/intent.py +++ b/src/fabledassistant/services/intent.py @@ -104,12 +104,17 @@ Rules: ("search for X", "look up X", "what is the latest version of X", "find X online", "google X", "what is X" for quick factual answers — NOT when they want a comprehensive note) - research_topic: user wants to research a topic and create a comprehensive note from web sources - ("research X", "research X and make a note", "compile notes on X", "write a report on X", - "deep dive into X", "find everything about X", "comprehensive guide to X") + ("research X", "Research: X", "research X and make a note", "compile notes on X", "write a report on X", + "deep dive into X", "find everything about X", "comprehensive guide to X", + "research where to buy X", "research how to X", "research X and ship to me" — the topic + is everything after "Research:" or "research") - "ack": one short, natural sentence confirming the action (tool path only). Vary phrasing — do not always start with "Let me". Omit (null) for chat-only responses. - Do NOT wrap the JSON in markdown code fences.""" +_RESEARCH_PREFIX = re.compile(r"^[Rr]esearch:\s+(.+)", re.DOTALL) + + async def classify_intent( user_message: str, tools: list[dict], @@ -127,6 +132,21 @@ async def classify_intent( if not tools: return IntentResult() + # Fast-path: "Research: " is the canonical format sent by the Research + # button in the UI. It always means research_topic — skip the LLM call entirely. + valid_names = {t.get("function", {}).get("name") for t in tools} + if "research_topic" in valid_names: + m = _RESEARCH_PREFIX.match(user_message.strip()) + if m: + topic = m.group(1).strip() + logger.info("Intent fast-path: 'Research:' prefix → research_topic, topic='%s'", topic[:80]) + return IntentResult( + tool_name="research_topic", + arguments={"topic": topic}, + confidence="high", + ack=f"I'll research that and compile a comprehensive note.", + ) + tool_summary = _build_tool_summary(tools) today = date_type.today().isoformat()