Dynamiq
Agents

Memory

Give agents conversation memory with pluggable backends — in-memory, SQL, vector stores, or the Dynamiq platform — plus save modes, retrieval strategies, and long-term fact memory.

dynamiq.memory.Memory stores and retrieves conversation messages for agents. Attach a Memory instance to an agent and pass user_id / session_id at run time — the agent loads prior messages scoped to those ids before reasoning and persists the new turn afterwards.

Attach memory to an agent

from dynamiq import Workflow
from dynamiq.connections import OpenAI as OpenAIConnection
from dynamiq.flows import Flow
from dynamiq.memory import Memory
from dynamiq.memory.backends.in_memory import InMemory
from dynamiq.nodes.agents import Agent
from dynamiq.nodes.llms import OpenAI

llm = OpenAI(connection=OpenAIConnection(), model="gpt-4o")

agent = Agent(
    name="assistant",
    llm=llm,
    role="Helpful assistant that remembers prior turns.",
    memory=Memory(backend=InMemory()),
)

wf = Workflow(flow=Flow(nodes=[agent]))
ids = {"user_id": "demo-user", "session_id": "demo-session"}

wf.run(input_data={"input": "My name is Alex and I work at TechCorp.", **ids})
result = wf.run(input_data={"input": "Where do I work?", **ids})
print(result.output[agent.id]["output"]["content"])  # mentions TechCorp

The agent-side knobs:

memoryMemory
The Memory instance the agent reads from and writes to.
memory_limitint
Maximum messages retrieved into the prompt (default 100).
memory_retrieval_strategyMemoryRetrievalStrategy
ALL (default), RELEVANT, or BOTH — see below.

Memory configuration

backendMemoryBackend
Storage backend (default InMemory).
message_limitint
Default retrieval limit (default 1000).
filtersdict
Default metadata filters applied to every search.
save_modeMemorySaveMode
FULL (default) persists every non-system message including tool calls and observations; INPUT_OUTPUT persists only the user input and the final assistant response.
from dynamiq.memory import Memory, MemorySaveMode

memory = Memory(
    backend=InMemory(),
    save_mode=MemorySaveMode.INPUT_OUTPUT,  # clean multi-turn chat, no tool traces
)

Use MemorySaveMode.FULL when you need replay/debug fidelity; use INPUT_OUTPUT for lean chat history that keeps the next turn's prompt small.

Retrieval strategies

MemoryRetrievalStrategy controls which messages are loaded into the agent's prompt:

StrategyBehavior
ALL (default)The most recent messages, up to the limit
RELEVANTMessages semantically relevant to the current input
BOTHRecent messages merged with relevant ones
from dynamiq.memory import MemoryRetrievalStrategy

agent = Agent(
    name="assistant",
    llm=llm,
    memory=memory,
    memory_limit=50,
    memory_retrieval_strategy=MemoryRetrievalStrategy.BOTH,
)

RELEVANT and BOTH need a backend that can rank by similarity: vector-store backends use embeddings, while InMemory ranks with BM25 keyword scoring.

Backends

All backends live in dynamiq.memory.backends: InMemory, SQLite, PostgreSQL, DynamoDB, Pinecone, Qdrant, Weaviate, and Dynamiq.

InMemory

No setup; messages vanish with the process. Best for tests and notebooks.

from dynamiq.memory.backends import InMemory

memory = Memory(backend=InMemory())

SQLite

from dynamiq.memory.backends import SQLite

memory = Memory(backend=SQLite(db_path="conversations.db", index_name="conversations"))

PostgreSQL

from dynamiq.connections import PostgreSQL as PostgreSQLConnection
from dynamiq.memory.backends import PostgreSQL

memory = Memory(
    backend=PostgreSQL(
        connection=PostgreSQLConnection(),   # host/port/user/password from env
        table_name="conversations",
        create_if_not_exist=True,
    )
)

DynamoDB

from dynamiq.connections import AWS
from dynamiq.memory.backends import DynamoDB

memory = Memory(
    backend=DynamoDB(
        connection=AWS(),
        table_name="conversations",
        create_if_not_exist=True,
    )
)

Vector-store backends (Pinecone, Qdrant, Weaviate)

These store each message as an embedded vector, enabling RELEVANT retrieval. They require an embedder:

from dynamiq.connections import Pinecone as PineconeConnection
from dynamiq.memory.backends import Pinecone
from dynamiq.nodes.embedders import OpenAIDocumentEmbedder
from dynamiq.storages.vector.pinecone.pinecone import PineconeIndexType

memory = Memory(
    backend=Pinecone(
        connection=PineconeConnection(),
        embedder=OpenAIDocumentEmbedder(model="text-embedding-3-small"),
        index_name="conversations",
        index_type=PineconeIndexType.SERVERLESS,
        cloud="aws",
        region="us-east-1",
        create_if_not_exist=True,
    )
)

Qdrant takes connection, embedder, index_name, and dimension; Weaviate takes connection, embedder, and collection_name, plus an alpha parameter for hybrid keyword/vector search.

Dynamiq platform backend

The Dynamiq backend persists messages to a memory resource hosted on the Dynamiq platform, so SDK agents and deployed Apps can share the same conversation store:

import os

from dynamiq.connections import Dynamiq as DynamiqConnection
from dynamiq.memory import Memory, MemorySaveMode
from dynamiq.memory.backends.dynamiq import Dynamiq as DynamiqBackend

backend = DynamiqBackend(
    connection=DynamiqConnection(
        url=os.getenv("DYNAMIQ_URL", "https://api.getdynamiq.ai"),
        api_key=os.environ["DYNAMIQ_API_KEY"],
    ),
    memory_id=os.environ["DYNAMIQ_MEMORY_ID"],  # id of the remote memory resource
)

memory = Memory(backend=backend, save_mode=MemorySaveMode.FULL)

user_id and session_id from the agent's run input become metadata filters on the remote store, so multiple users and sessions share one memory resource without leaking into each other.

Working with memory directly

Memory is also usable without an agent:

from dynamiq.prompts import MessageRole

memory.add(role=MessageRole.USER, content="I need help with billing.",
           metadata={"user_id": "u-1", "session_id": "s-1"})

recent = memory.get_all(limit=20)
relevant = memory.search(query="billing", filters={"user_id": "u-1"}, limit=10)
conversation = memory.get_agent_conversation(filters={"user_id": "u-1", "session_id": "s-1"})

memory.delete(session_id="s-1", user_id="u-1")  # scoped cleanup

Long-term memory (facts across sessions)

Everything above is short-term memory: a transcript of messages scoped to a session_id. Long-term memory is a separate, complementary store of durable, user-scoped facts the agent can write and recall across every session. It is configured through the agent's long_term_memory field, independent of memory — an agent can use either or both.

When enabled, the agent gains two tools — remember_fact and recall_facts — and decides during its loop when to save or look up facts. Every write and read is scoped to the run's user_id, so long-term memory requires user_id on the run (the agent raises if it is enabled without one). Writes deduplicate by meaning: remember() embeds the fact and, when it is near-identical to an existing one (cosine similarity above upsert_threshold, default 0.85), updates that fact in place instead of storing a duplicate.

from dynamiq.connections import OpenAI as OpenAIConnection
from dynamiq.connections import PostgreSQL as PostgreSQLConnection
from dynamiq.memory.long_term import LongTermMemoryConfig
from dynamiq.memory.long_term.backends import PostgresLongTermMemoryBackend
from dynamiq.nodes.agents import Agent
from dynamiq.nodes.embedders import OpenAITextEmbedder
from dynamiq.nodes.llms import OpenAI

llm = OpenAI(connection=OpenAIConnection(), model="gpt-4o")
embedder = OpenAITextEmbedder(
    connection=OpenAIConnection(),
    model="text-embedding-3-small",  # 1536-dimensional output
)

agent = Agent(
    name="assistant",
    id="agent",
    llm=llm,
    role="Helpful assistant. Save durable facts about the user and recall them when relevant.",
    long_term_memory=LongTermMemoryConfig(
        backend=PostgresLongTermMemoryBackend(
            connection=PostgreSQLConnection(),  # host/port/user/password from env
            embedder=embedder,
            table_name="user_facts",
            dimension=1536,  # must match the embedder's output size
        ),
    ),
)

# user_id is mandatory whenever long-term memory is enabled
agent.run(input_data={"input": "I'm vegetarian and I live in Berlin.", "user_id": "u-1"})
later = agent.run(input_data={"input": "Suggest a restaurant for tonight.", "user_id": "u-1"})
print(later.output["content"])  # accounts for the saved facts

LongTermMemoryConfig takes enabled (default True — set False to keep a backend wired but turn the tools off for a run) and the backend.

Backends

Long-term backends live in dynamiq.memory.long_term.backends. Each takes a TextEmbedder (used to vectorize facts on write and queries on read) and shares the upsert_threshold dedup control:

BackendStorageKey parameters
InMemoryLongTermMemoryBackendProcess-local, lost on restartembedder — no external service; best for tests
PostgresLongTermMemoryBackendPostgres + pgvectorconnection, embedder, table_name (default user_facts), dimension (default 1536)
PineconeLongTermMemoryBackendPineconeconnection, embedder, index_name (default user_facts), namespace (default default), dimension
QdrantLongTermMemoryBackendQdrantconnection, embedder, collection_name (default user_facts), dimension
WeaviateLongTermMemoryBackendWeaviateconnection, embedder, collection_name (default UserFacts), dimension

Long-term memory is an SDK capability — there is no platform (Agent node) surface for it yet. Configure it in code as shown above.

Next steps

On this page