Fix search_web over-triggering when user references existing notes

Add a Python fast-path regex (_PRIOR_WORK_REFS) in classify_intent that
detects phrases like "research you did", "note you made", "using your
research", "based on the research" etc. and returns no-tool immediately —
saving the 19s intent LLM call and correctly letting the main model answer
using search_notes/context rather than firing off a web search.

Also tighten the intent prompt rules for search_web: explicitly prohibit
using it for creative/brainstorming requests or when the user references
existing notes, and add a rule that creative/ideation questions ("think of",
"come up with", "brainstorm") always route to null (chat).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-28 18:12:58 -05:00
parent fb3a6d2445
commit 4998d04739
+39 -3
View File
@@ -100,9 +100,12 @@ Rules:
- "read", "open", "show me", "what does X say", "display", "pull up" a specific note → use get_note with query=<note name>.
- "list my notes", "show notes", "recent notes", "browse notes", "notes tagged X" → use list_notes (with optional q or tags).
- "tag X with Y", "add tag Y to X", "untag Y from X", "remove tag Y from X" → use update_note with tags=[Y] and tag_mode="add" or "remove".
- search_web: user wants a quick factual answer retrieved from the web, without creating a note.
Use for brief lookups where a short summary suffices (current version numbers, quick facts,
"what is X", "look up X"). Do NOT use when the user wants a detailed written reference.
- search_web: user explicitly wants a quick factual answer from the web — current events, version
numbers, real-time facts ("what is the latest version of X", "who won the game last night").
ONLY use when the information is clearly not in their notes and they need something fresh from
the internet. Do NOT use for creative questions, brainstorming, game design, writing help, or
when the user is building on content they already have. Do NOT use when the user references
their own notes or prior research — use search_notes instead.
- research_topic: user wants a comprehensive, multi-section research note created from web sources.
Use whenever the user wants to deeply understand, learn about, or get a full written reference
on any subject — regardless of how they phrase it. The topic can be anything: technical subjects,
@@ -110,12 +113,35 @@ Rules:
The "topic" argument should capture the full subject matter of the request.
Prefer this over search_web when the user's request implies wanting thorough coverage rather than
a quick answer.
- For creative/ideation requests ("think of", "come up with", "ideas for", "help me design",
"imagine", "brainstorm") use null (chat) — the main model answers these directly. Only route
to a tool if the user also explicitly asks to search, look something up, or create/save something.
- If the user references notes or research the assistant previously created, prefer search_notes
(to retrieve the relevant note) or null (chat) so the main model can use its tools to find it.
Never use search_web when existing notes likely contain the answer.
- "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."""
# Fast-path: "Research: <topic>" sent by the Research button.
_RESEARCH_PREFIX = re.compile(r"^[Rr]esearch:\s+(.+)", re.DOTALL)
# When the user refers to work the assistant previously did, they want the main
# model to answer using existing context — not a web search. Skip intent and
# fall through to the streaming path so the model can call search_notes itself.
_PRIOR_WORK_REFS = re.compile(
r"\b("
r"research (you|that you|we) did"
r"|research (you|that you|we) (made|created|compiled|wrote)"
r"|note (you|that you|we) (made|created|wrote)"
r"|notes (you|that you|we) (made|created|wrote)"
r"|using (your|the) research"
r"|based on (your|the) research"
r"|from (your|the) (research|note|notes)"
r")\b",
re.IGNORECASE,
)
async def classify_intent(
user_message: str,
@@ -149,6 +175,16 @@ async def classify_intent(
ack=f"I'll research that and compile a comprehensive note.",
)
# Fast-path: user references prior assistant work ("research you did", "note you
# made", etc.) — this is a request to use existing content, not search the web.
# Return no-tool so the main model answers conversationally with search_notes
# available if it needs to retrieve the note.
if _PRIOR_WORK_REFS.search(user_message):
logger.info(
"Intent fast-path: prior-work reference detected → skipping tool dispatch"
)
return IntentResult()
tool_summary = _build_tool_summary(tools)
today = date_type.today().isoformat()