Agent Long-Term Memory
Agents can learn from conversations and remember useful information for future interactions. Unlike chat memory, which is scoped to a specific conversation's message window and is lost when that chat is deleted, long-term memory is stored independently of any conversation. It persists even after a chat is deleted and is available across all future interactions. When a user asks something the agent has learned before, it retrieves that information automatically.
How it works
The memory system operates in two phases during agent execution:
Recall: When a user submits a query, the agent searches for relevant memories and injects them into the system prompt before processing.
Memorization: After responding, the agent analyzes the conversation to determine if anything should be saved for future use.
Both phases run automatically when you enable memory callbacks in the agent configuration.
Memory types
Memories fall into two categories:
- User-specific memories: Personal to the user who created them. The agent uses these only when that user asks questions.
- Global memories: Available to all users. Useful for organizational knowledge that applies broadly. This is set by the boolean field
globalon memory records and is configurable in the memory creation prompt and schema Genai.Memory.LongTermMemory.Config.
Enable memory for your agent
Memory runs through callbacks attached to the agent pipeline. You configure these in your agent's JSON config file.
Add the callback specification to your agent config:
{
"name": "MyAgent_default",
"onStepCallbacksSpec": {
"onCallStepStart": [
{
"type": "Genai.Agent.Dynamic.Callback",
"id": "agent_memory_recall",
"callback": {
"type": "ActionRef",
"typeName": "Genai.Agent.Dynamic.Callback.Memory",
"actionName": "recall"
}
}
],
"onCallStepEnd": [
{
"type": "Genai.Agent.Dynamic.Callback",
"id": "agent_memory_launchAsyncMemorization",
"callback": {
"type": "ActionRef",
"typeName": "Genai.Agent.Dynamic.Callback.Memory",
"actionName": "launchAsyncMemorization"
}
},
{
"type": "Genai.Agent.Dynamic.Callback",
"id": "agent_memory_cleanup",
"callback": {
"type": "ActionRef",
"typeName": "Genai.Agent.Dynamic.Callback.Memory",
"actionName": "cleanup"
}
}
]
}
}Before step execution begins, the recall callback runs in onCallStepStart. After the step finishes, launchAsyncMemorization and cleanup both run in onCallStepEnd. The recall callback retrieves relevant memories and adds them to the system prompt (it adds the MEMORY_RECALL parameter). The launchAsyncMemorization callback analyzes the conversation and creates new memories if needed. The cleanup callback clears any temporary parameters (MEMORY_RECALL) used for memory recall.
Configure memory settings
Memory behavior is controlled through Genai.Memory.LongTermMemory.Config. The default configuration includes:
| Setting | Default | Description |
|---|---|---|
memoryRecallCount | 5 | Number of memories retrieved per query |
memoryResolutionCount | 10 | Maximum memories to compare during conflict resolution |
dynamicAgentName | DynamicAgent_default | Agent used for memory processing |
You can customize these by creating a new config record or updating the default.
Prompt requirements
For memory recall to work, your agent's system prompt or force-solution prompt must include a {{MEMORY_RECALL}} placeholder parameter. The callback replaces this placeholder with relevant memories formatted as context.
Example system prompt snippet:
You are a helpful assistant.
## Previous Knowledge
{{MEMORY_RECALL}}
## Instructions
...If neither the system prompt nor the force-solution prompt defines this parameter, the recall callback throws an error.
Memory resolution
When the agent creates a new memory similar to an existing one, it runs a resolution process. The agent determines one of three actions:
- KEEP: The memories are unrelated, so both remain.
- UPDATE: The memories overlap but have complementary information. The agent merges them.
- DELETE: The new memory contradicts the old one. The agent removes the outdated memory.
For example, if a user previously said "Call me Dr. Smith" but later says "Actually, call me Alex." the agent deletes the old memory because they directly conflict.
Resolution runs asynchronously after memory creation.
UI feedback
When memory creation begins, a placeholder resource appears in the UI showing "Creating Memory..." status. After completion, the UI updates to show the created memories.
This polling mechanism uses the Genai.Agent.Resource.MemoryCreation type with a status field that transitions from creating to completed.
Memory storage
Memories are stored in Genai.Memory.LongTermMemory.Pg and accessed via a GenaiCore.Agent.MemoryStore.Pg configured on Genai.Memory.LongTermMemory.Config. The store:
- Generates embeddings for similarity search
- Filters by user ID for user-specific memories
- Supports metadata tagging for categorization
The default memory store is available from the long-term memory config:
config = c3.Genai.Memory.LongTermMemory.Config.inst()
memory_store = config.memoryStoreCreate memories programmatically
You can create memories directly using the memory store from Genai.Memory.LongTermMemory.Config. This ensures the memory is embedded and stored in the same Genai.Memory.LongTermMemory.Pg collection that the recall callback queries.
user_id = User.myUser().id
memory_store = Genai.Memory.LongTermMemory.Config.inst().memoryStore
passage = "What is my preferred date format?"
metadata = [{
"content": "ISO 8601 (YYYY-MM-DD)",
"contentToEmbed": passage,
"description": "User prefers ISO date format",
"global": false,
"user": {"id": user_id},
"details": {"query": passage, "specialization": "formatting"},
}]
memory_store.addMemories([passage], metadata=metadata)
# Trigger resolution to handle any conflicts with existing memories
Genai.Memory.LongTermMemory.Resolver.launchAsyncResolution(memory_store.id)Retrieve memories
To query existing memories for a user:
user_id = User.myUser().id
# Get memory store from long-term memory config
memory_store = Genai.Memory.LongTermMemory.Config.inst().memoryStore
# Get all memories for the current user
memories = Genai.Memory.LongTermMemory.Pg.fetch({
"filter": Filter.eq("user.id", user_id).and(Filter.eq("memoryStoreId", memory_store.id)),
"include": "this, details, content, description",
"order": "descending(meta.updated)"
}).objs
# Search by similarity
spec = GenaiCore.SimilaritySearchSpec.make({
"k": 5,
"metadataFilter": Filter.eq("user.id", user_id)
})
results = memory_store.similaritySearch("date formatting preferences", spec)Delete memories
Remove a specific memory by ID:
Genai.Memory.LongTermMemory.Pg.forId(memory_id).remove()Or remove all memories for a user:
memory_store = Genai.Memory.LongTermMemory.Config.inst().memoryStore
Genai.Memory.LongTermMemory.Pg.removeAll(
{
"filter": Filter.eq("user.id", user_id).and_().eq("memoryStoreId", memory_store.id)
},
confirm=True
)Troubleshooting
Memories not being recalled
- Verify the system prompt includes
{MEMORY_RECALL}parameter. - Check that a memory store is configured in Genai.Memory.LongTermMemory.Config.
- Confirm the recall callback is listed in
onCallStepStart.
Memories not being created
- Review agent logs for the
G.A.D.Callback.Memoryprefix. - Verify the memorization callback is listed in
onCallStepEnd. - Check that the LLM decides the conversation contains memorable information. Not every conversation results in a memory.
Duplicate memories appearing
- Memory resolution runs asynchronously. Wait a few seconds and query again.
- Check
resolvedfield on memory records; unresolved memories are pending.
To learn more about agent memories, check the python notebook GenAI Platform Tutorials - Agent Memory.