Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.maximem.ai/llms.txt

Use this file to discover all available pages before exploring further.

Swap Agno’s in-memory database for a Synap-backed one. Agents that use enable_user_memories=True keep working without code changes — they just gain a persistent, semantically searchable memory store.

Overview

This guide shows how to add Synap to an Agno application to build agents that:
  • Persist user memories across processes and deployments
  • Retrieve memories semantically rather than by raw key lookup
  • Serve many users from a single database instance, scoped per-call by user_id
The Synap Agno integration ships a single class — a subclass of Agno’s InMemoryDb that routes memory operations through Synap.
ClassAgno interfacePurpose
SynapDbInMemoryDb subclassPersistent user-memory store backed by Synap

Setup

Install the package alongside Agno:
pip install maximem-synap-agno agno
Configure your API key. Generate one from the Synap Dashboard.
.env
SYNAP_API_KEY=synap_your_key_here
OPENAI_API_KEY=your-openai-api-key
Initialize the SDK once at application startup:
from maximem_synap import MaximemSynapSDK

sdk = MaximemSynapSDK()
await sdk.initialize()
See SDK Initialization for the full lifecycle and configuration options.

Basic integration

The smallest useful integration constructs a SynapDb, hands it to an Agent, and turns on enable_user_memories. Each agent.run(..., user_id=...) call now reads and writes against Synap rather than a process-local dict:
from agno.agent import Agent
from agno.models.openai import OpenAIChat
from synap_agno import SynapDb

db = SynapDb(sdk=sdk, customer_id="acme")   # customer_id is optional

agent = Agent(
    db=db,
    model=OpenAIChat(id="gpt-4o-mini"),
    enable_user_memories=True,
)

agent.run("Remember that I prefer async communication", user_id="alice")
reply = agent.run("What are my communication preferences?", user_id="alice")
Notice that user_id is supplied per call — SynapDb is constructed once and serves all users in the process. The customer_id (organization scope) is fixed at construction. Memory reads degrade gracefully on Synap outages; writes raise so silent data loss is impossible.

Core concepts

SynapDb

SynapDb extends Agno’s InMemoryDb and overrides the four user-memory methods. Everything else (session state, non-memory storage) is inherited unchanged, so existing Agno code continues to work:
from synap_agno import SynapDb

db = SynapDb(
    sdk=sdk,
    customer_id="acme",   # optional — required for B2B instances
)
The overridden methods map to Synap as follows:
MethodBehavior
upsert_user_memory(user_id, memory)Writes a new or updated memory to Synap
get_user_memory(user_id, memory_id)Fetches a specific memory by ID
get_user_memories(user_id, query)Retrieves the user’s memories via semantic search
get_all_memory_topics(user_id)Returns unique memory topics via a broad search

Per-call user scoping

Agno passes user_id as a runtime argument to every memory call rather than baking it into the database. That means one SynapDb instance can serve any number of users:
db = SynapDb(sdk=sdk, customer_id="acme")
agent = Agent(db=db, model=OpenAIChat(id="gpt-4o-mini"), enable_user_memories=True)

for user_id in ["alice", "bob", "carol"]:
    agent.run("What do you remember about me?", user_id=user_id)
Each call hits Synap with its own user_id scope, so memories never leak between users — even though the database object is shared.

Complete example: long-lived agent serving many users

The pattern below is what most production Agno deployments end up with: one SynapDb and one Agent at module load, and a thin handler that supplies user_id per request:
from agno.agent import Agent
from agno.models.openai import OpenAIChat
from synap_agno import SynapDb


# Define once at startup
db = SynapDb(sdk=sdk, customer_id="acme")

agent = Agent(
    db=db,
    model=OpenAIChat(id="gpt-4o-mini"),
    enable_user_memories=True,
    description=(
        "You are a personal assistant with long-term memory. "
        "Recall the user's preferences and decisions whenever they ask. "
        "When the user shares a new fact, write it down."
    ),
)


# Per-request handler — user_id varies, db/agent do not
async def handle_request(user_id: str, message: str) -> str:
    return agent.run(message, user_id=user_id).content


# Usage
await handle_request("alice", "I prefer email over Slack for updates.")
await handle_request("bob", "Remind me what timezone I'm in — it's Pacific.")
reply = await handle_request("alice", "How should you contact me?")
# → "Over email, since that's your preferred channel."
Three things to notice in this pattern:
  1. One SynapDb, many users. The database is constructed once; user_id is the per-call scope.
  2. The customer_id is the tenant boundary. All users sharing a SynapDb instance are inside the same customer_id — for cross-tenant services, build a separate SynapDb per tenant.
  3. Agno’s behavior is unchanged. enable_user_memories, description, and run signatures all work exactly as in vanilla Agno; you’ve only swapped the storage layer.

Advanced patterns

Multi-tenant scoping

SynapDb takes the customer-tenant scope at construction (customer_id) and the user scope per call (user_id). customer_id is required on B2B Synap instances and ignored on single-tenant ones. See Memory Scopes.
# Single-tenant instance
db = SynapDb(sdk=sdk)

# Multi-tenant — one db per customer
db_acme = SynapDb(sdk=sdk, customer_id="acme")
db_initech = SynapDb(sdk=sdk, customer_id="initech")
For services that serve many tenants, route requests to the matching SynapDb rather than mixing tenants on one instance.

Memory topic discovery

get_all_memory_topics is implemented as a broad Synap search and returns unique topics across the user’s memory pool. Useful for building UIs that let a user browse what’s been remembered about them, or for prompt enrichment (“Here are the topics we’ve discussed: …”).

Failure semantics

The integration follows the Synap-wide contract:
  • get_user_memory / get_user_memories degrade gracefully — return empty results and log an error if Synap is unreachable.
  • upsert_user_memory surfaces failures — raises SynapIntegrationError so the agent and caller know persistence failed.
This is by design: read failures shouldn’t break a user-facing answer, but silent write failures would let memory drift away from reality.

Next steps

Google ADK

FunctionTool factory for Google ADK agents.

OpenAI Agents

Function tools for the OpenAI Agents SDK.

Memory Scopes

How user_id and customer_id interact across reads and writes.

Ingestion

Direct ingestion API for pipelines that need finer control than upsert_user_memory.