Agentic Memory (Memory-as-Tool)¶
Stack: Python 3.9+, src-layout, pytest, stdlib only (no new deps) Date: 2026-04-13 Status: Draft
Problem¶
Selectools memory is currently passive — EntityMemory, KnowledgeMemory, and ConversationMemory are wired into _prepare_run() to inject context automatically. The agent never decides what to remember or when to recall. Every competitor with "agentic memory" (Agno, mem0, LangChain) lets the agent store and retrieve via explicit tool calls, giving it agency over its own knowledge.
A partial implementation exists: toolbox/memory_tools.py has a make_remember_tool() factory for KnowledgeMemory.remember(). But there is no corresponding recall tool, no EntityMemory tool integration, no semantic search, and the existing factory isn't documented or exported from the top-level API.
Solution¶
Expand toolbox/memory_tools.py into a complete agentic memory toolkit with factory functions that create Tool instances backed by existing memory classes:
| Tool | Backed by | Purpose |
|---|---|---|
remember | KnowledgeMemory.remember() | Store a fact with category and importance |
recall | KnowledgeStore.search() | Semantic search over stored knowledge |
note_entity | EntityMemory.update() | Explicitly register/update an entity |
recall_entities | EntityMemory.entities | Retrieve known entities, optionally filtered |
Plus a convenience factory make_memory_tools() that returns all applicable tools for a given memory configuration.
The agent decides when to call these tools — no automatic injection needed when using agentic memory mode.
Acceptance Criteria¶
-
make_remember_tool(knowledge: KnowledgeMemory) -> Tool— existing, cleaned up: addsimportanceparam (float, optional, default 0.5), addsttl_daysparam (int, optional) -
make_recall_tool(knowledge: KnowledgeMemory) -> Tool— new:recall(query: str, category: str = "", max_results: int = 5) -> strreturns formatted knowledge entries matching the query -
make_note_entity_tool(entity_memory: EntityMemory) -> Tool— new:note_entity(name: str, entity_type: str, attributes: str = "") -> strcreates/updates an Entity -
make_recall_entities_tool(entity_memory: EntityMemory) -> Tool— new:recall_entities(filter_type: str = "") -> strreturns formatted entity list -
make_memory_tools(knowledge: Optional[KnowledgeMemory] = None, entity_memory: Optional[EntityMemory] = None) -> List[Tool]— convenience factory, returns tools for whichever memory backends are provided -
recalltool usesKnowledgeStore.search()if the store supports it, falls back toKnowledgeStore.list()with client-side substring match - All tools return
str(required by tool executor contract) - All tools work with both
FileKnowledgeStoreandSQLiteKnowledgeStore -
attributesparam onnote_entityaccepts comma-separatedkey=valuepairs (LLM-friendly format) - Existing
make_remember_toolbehavior is preserved — no breaking changes to current users - All factories exported from
selectools.toolboxand top-levelselectoolspackage - Stability marker:
@betaon all new public functions; existingmake_remember_toolstays as-is - ≥95% test coverage on new/modified code
- Cookbook recipe:
docs/cookbook/agentic-memory.mddemonstrating remember + recall + entity tools in an agent - One example in
examples/showing a multi-turn conversation where the agent decides to remember and later recall
Non-Goals¶
- Replacing passive memory injection — agentic memory is an alternative mode, not a replacement. Users can still use
AgentConfig.entity_memoryfor auto-injection. - Vector-store-backed semantic recall —
KnowledgeStore.search()is keyword/metadata-based today. True vector search requires aVectorStoreintegration which is a separate feature. - Cross-agent memory sharing — tools operate on the memory instance passed in. Sharing is the user's responsibility (pass same instance to multiple agents).
- Memory consolidation/summarization as a tool — the existing
ConversationMemorysummarize-on-trim handles this passively. - Tool-level access control — any agent with the tool can read/write. No per-key permissions.
Technical Approach¶
Modified file: src/selectools/toolbox/memory_tools.py¶
Current state: ~65 lines with make_remember_tool() only.
Changes:
-
Enhance
make_remember_tool: addimportanceandttl_daysparameters to the inner_remember()function. Parseimportancefrom string (LLMs send strings). Backward compatible — new params are optional. -
Add
make_recall_tool:def make_recall_tool(knowledge: KnowledgeMemory) -> Tool: def _recall(query: str, category: str = "", max_results: int = 5) -> str: entries = knowledge._store.search(query, category=category or None, limit=max_results) if not entries: return "No matching memories found." return "\n---\n".join( f"[{e.category}] {e.content} (importance: {e.importance}, created: {e.created_at:%Y-%m-%d})" for e in entries ) -
Add
make_note_entity_tool:def make_note_entity_tool(entity_memory: EntityMemory) -> Tool: def _note_entity(name: str, entity_type: str, attributes: str = "") -> str: attrs = {} if attributes: for pair in attributes.split(","): if "=" in pair: k, v = pair.split("=", 1) attrs[k.strip()] = v.strip() entity = Entity(name=name, entity_type=entity_type, attributes=attrs, ...) entity_memory.update([entity]) return f"Entity '{name}' ({entity_type}) stored with {len(attrs)} attributes." -
Add
make_recall_entities_tool: formatentity_memory.entitiesas a readable list, optionally filtered byentity_type. -
Add
make_memory_tools: introspect which backends are provided, return applicable tool list.
KnowledgeStore.search() support¶
Check if KnowledgeStore protocol and implementations already have search():
FileKnowledgeStore— haslist()withcategoryfilter. Addsearch(query, category, limit)that filterslist()results by substring match oncontent.SQLiteKnowledgeStore— haslist(). Addsearch()withLIKE '%query%'SQL (mark# nosec B608— query is user-knowledge content, not untrusted SQL).
Other modified files¶
| File | Change |
|---|---|
toolbox/__init__.py | Export make_recall_tool, make_note_entity_tool, make_recall_entities_tool, make_memory_tools |
__init__.py | Add new factories to top-level exports with @beta |
knowledge.py | Add search() to KnowledgeStore protocol + both implementations |
Dependencies¶
KnowledgeMemoryandEntityMemory— already exist and stableEntitydataclass — already inentity_memory.pyToolandToolParameter— already intools/base.pymake_remember_tool— already intoolbox/memory_tools.pyKnowledgeStoreprotocol — already inknowledge.py
Risks¶
| Risk | Mitigation |
|---|---|
LLMs send args as strings (e.g., importance: "0.8") | Parse with float() / int() with try/except, same pattern as existing persistent param |
search() on FileKnowledgeStore is O(N) substring match | Acceptable for <1000 entries. Document that SQLiteKnowledgeStore is recommended for large stores |
entity_memory.update() requires LLM-extracted entities in current API | note_entity constructs an Entity directly, bypassing LLM extraction — this is intentional for agentic use |
attributes string parsing is fragile | Simple key=value comma-split is LLM-friendly. Document the format in tool description. Edge cases (commas in values) are acceptable for v1 |