mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-06-15 17:25:26 -04:00
Tool retrieval: HARD drop manage_memory when query is a contact-save pattern
Description-level steering wasn't enough — even with the explicit 'DO NOT use for info about another person' in manage_memory's description, models kept choosing memory over manage_contact. They can't if memory isn't in the toolset. New logic in ToolIndex.get_tools_for_query: detect three contact-save patterns and discard manage_memory from the returned set (overriding ALWAYS_AVAILABLE): 1. 'save [up to 3 words] for/to <name>' where <name> isn't a timing / pronoun stopword (later, tomorrow, me, you, future, etc.). Catches the canonical 'save this for X' and the wider 'save this address for X', 'save it for X'. 2. 'to/in/into (my) contacts' or 'address book'. Catches both 'add X to my contacts' and 'put this in my address book for X'. 3. Possessive: 'save (his/her/their) (address/phone/email/...)'. Stronger signal — also force-adds manage_contact to the set in case the keyword fallback missed it. Verified: 8 positive contact patterns all drop memory, 10 false- positive 'save X for later/tomorrow/me/the next thing' all keep it.
This commit is contained in:
@@ -514,6 +514,53 @@ class ToolIndex:
|
||||
# prompts do not drag web schemas into the agent context.
|
||||
if self._WEB_RE.search(query):
|
||||
base.update({"web_search", "web_fetch"})
|
||||
# Hard steering: when the query is a clear "save info about a specific
|
||||
# person" pattern (address paste + name, phone next to a name, etc.),
|
||||
# the model has been observed defaulting to manage_memory even with
|
||||
# manage_contact in the toolset. Pull memory out for these queries so
|
||||
# the model literally cannot pick it. ALWAYS_AVAILABLE includes
|
||||
# manage_memory by default; we override that here.
|
||||
# The "for/to <word>" check needs to allow lowercase names (users
|
||||
# don't always capitalize) but filter out timing/pronoun stopwords
|
||||
# so "save this for later" / "save for tomorrow" don't trigger.
|
||||
_CONTACT_STOPWORDS_AFTER_FOR = {
|
||||
"later", "tomorrow", "yesterday", "now", "then", "today",
|
||||
"tonight", "me", "us", "you", "him", "her", "them", "myself",
|
||||
"yourself", "next", "this", "that", "the", "a", "an", "future",
|
||||
"real", "use", "uses", "another", "future", "reference",
|
||||
}
|
||||
# Regex catches "save (this|it|the|her|...|<noun>) for <name>" / "to my
|
||||
# contacts" patterns. More forgiving than literal-keyword matching —
|
||||
# 'save this address for Alex' uses one extra word between 'save' and
|
||||
# 'for' that breaks the contiguous 'save this for' phrase.
|
||||
save_for_match = re.search(
|
||||
r"\bsave\b(?:\s+\w+){0,3}\s+(?:for|to)\s+([A-Za-z]+)",
|
||||
ql,
|
||||
)
|
||||
# "to my contacts", "into my contacts", "in my address book", etc.
|
||||
to_contacts = re.search(r"\b(?:to|in|into)\s+(?:my\s+)?(?:contacts|address\s+book)\b", ql)
|
||||
# Possessive: "save (his|her|their) (address|phone|email|number) ..."
|
||||
# — strong contact signal even without "for <name>". Force-include
|
||||
# manage_contact here too since the keyword fallback misses this
|
||||
# construction.
|
||||
possessive_contact = re.search(
|
||||
r"\bsave\b(?:\s+\w+){0,2}\s+(?:his|her|their)\s+(?:address|phone|number|email|contact|details)",
|
||||
ql,
|
||||
)
|
||||
word_after = (
|
||||
save_for_match.group(1).lower() if save_for_match else None
|
||||
)
|
||||
contact_only_signal = (
|
||||
(save_for_match is not None
|
||||
and word_after is not None
|
||||
and word_after not in _CONTACT_STOPWORDS_AFTER_FOR)
|
||||
or to_contacts is not None
|
||||
or possessive_contact is not None
|
||||
)
|
||||
if possessive_contact is not None:
|
||||
base.add("manage_contact")
|
||||
if contact_only_signal and "manage_contact" in base:
|
||||
base.discard("manage_memory")
|
||||
return base
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user