DEV Community

Cover image for Agent Memory After pip install: What Six Python Packages Actually Store Between Sessions
mech.app
mech.app

Posted on Originally published at mech.app

Agent Memory After pip install: What Six Python Packages Actually Store Between Sessions

You run pip install, paste the quickstart code, and the first call fails. Not because your code is wrong. The package wants a database URL, an API key, a token budget, or it wants nothing and quietly stores your conversation history in a dictionary that vanishes when the process exits.

The install worked. The memory did not arrive with it.

Edward Izgorodin's comparison of eleven Python agent memory packages exposes the gap between installation and actual persistence. The runtime dependency list on PyPI, the exported class names, and the constructor signature tell you what you bought before you write a single line of application code.

What the Dependency List Reveals

Every package ships as either a client or an engine. The dependency count is the first signal.

  • Two dependencies: You installed a client. Memory lives somewhere else. You need credentials, network access, and a hosted service or self-hosted backend.
  • Eight or more dependencies: You installed an engine. Memory can run in-process, but you still need to configure storage, embeddings, and pruning logic.

The mem0ai package ships both. The Memory class is an eight-dependency engine that runs in your process or as a self-hosted server. The MemoryClient class is a two-dependency client that points to Mem0's hosted platform. Same install command, two different runtime shapes.

Where Memory Actually Lives

Package Default Storage Serialization Survives Process Exit Requires External Service
mnemoverse Hosted only Vendor-managed Yes Yes (hosted platform)
mem0ai (Memory) In-process dict or self-hosted JSON + embeddings No (dict), Yes (DB) Optional (OpenAI for embeddings)
mem0ai (MemoryClient) Hosted platform Vendor-managed Yes Yes (Mem0 platform)
LangChain memory In-process list Pickle or JSON No No
LlamaIndex memory In-process dict JSON No Optional (vector DB)
Haystack memory In-process list JSON No Optional (document store)

The in-process packages (LangChain, LlamaIndex, Haystack) default to ephemeral storage. You can plug in a database, but the quickstart examples use Python data structures that disappear on exit.

The hosted packages (mnemoverse, MemoryClient) require network calls and API keys. Memory persists, but you lose control over serialization format, pruning policy, and query latency.

The hybrid packages (mem0ai Memory class) let you choose. You can run in-process for development and switch to PostgreSQL or Qdrant for production, but you have to configure the backend yourself.

Serialization and Retrieval Mechanics

JSON-Based Stores

LangChain's ConversationBufferMemory serializes messages as JSON arrays. Retrieval is linear scan. No indexing, no embeddings, no semantic search. You get the last N messages or the full history.

from langchain.memory import ConversationBufferMemory

memory = ConversationBufferMemory()
memory.save_context({"input": "What is the capital of France?"}, {"output": "Paris"})
# Stored in memory.chat_memory.messages as a list of BaseMessage objects
# Serialized to JSON when you call memory.load_memory_variables({})
Enter fullscreen mode Exit fullscreen mode

This works for short sessions. For multi-turn conversations spanning days, you need to manually prune or switch to a database-backed store like RedisChatMessageHistory.

Embedding-Based Stores

Mem0's Memory class embeds every message and stores vectors in Qdrant, Pinecone, or Chroma. Retrieval is semantic search, not chronological scan. You get the most relevant messages, not the most recent.

from mem0 import Memory

config = {
    "vector_store": {"provider": "qdrant", "config": {"host": "localhost", "port": 6333}},
    "llm": {"provider": "openai", "config": {"model": "gpt-4"}},
}
memory = Memory.from_config(config)
memory.add("The user prefers dark mode", user_id="alice")
# Embeds the text, stores vector in Qdrant, returns memory_id
Enter fullscreen mode Exit fullscreen mode

The trade-off: you need a running vector database and an embedding model. The quickstart assumes OpenAI, but you can swap in local models. Connection failures to Qdrant or the embedding API will block memory writes.

Pickle-Based Stores

LangChain's ConversationSummaryMemory uses an LLM to summarize history and stores the summary as a pickled Python object. This is faster than embedding every message, but pickle is not portable across Python versions and introduces deserialization risks.

Connection Failure Modes

Hosted Platforms

If the API is down, memory writes fail. No local fallback. You can cache reads, but writes require network access. Rate limits and token quotas apply.

Self-Hosted Databases

If Qdrant or PostgreSQL is unreachable, the agent crashes or silently drops memory. You need retry logic, circuit breakers, and health checks.

LlamaIndex's VectorStoreIndex will raise a connection error if the vector store is unavailable. LangChain's RedisChatMessageHistory will raise a redis.exceptions.ConnectionError. Neither package provides automatic failover.

In-Process Stores

No network dependency, but no persistence. If the process crashes, memory is gone. You can serialize to disk manually, but the packages do not do this by default.

Memory Pruning Strategies

Token Budget

LangChain's ConversationTokenBufferMemory tracks token count and evicts old messages when the budget is exceeded. You configure the limit, the package handles eviction.

from langchain.memory import ConversationTokenBufferMemory
from langchain.llms import OpenAI

memory = ConversationTokenBufferMemory(llm=OpenAI(), max_token_limit=500)
Enter fullscreen mode Exit fullscreen mode

This prevents context overflow, but you lose early conversation context. No semantic ranking, just FIFO eviction.

Manual Checkpointing

Mem0's Memory class does not auto-prune. You call memory.delete(memory_id) explicitly or let the vector store handle capacity limits. This gives you control but requires application-level logic.

Summarization

LangChain's ConversationSummaryMemory condenses history into a running summary. You keep the summary and discard the original messages. This reduces token count but loses granularity.

Deployment Shapes

Single-Process Agent

Use in-process memory (LangChain, LlamaIndex) for prototypes. No external dependencies, fast iteration, but no persistence.

Multi-Instance Agent

Use a shared database (PostgreSQL, Redis, Qdrant) with mem0ai's Memory class or LangChain's database-backed stores. All instances read and write to the same memory. You need connection pooling and transaction isolation.

Serverless Agent

Use a hosted platform (mnemoverse, MemoryClient) or a managed vector database (Pinecone, Weaviate). Cold starts are slower because the first call must fetch memory from the network.

Observability Gaps

None of these packages expose structured logs for memory operations. You get exceptions on failure, but no metrics for:

  • Memory write latency
  • Retrieval accuracy (precision/recall for semantic search)
  • Pruning frequency
  • Storage size growth

You have to instrument this yourself. Wrap memory calls in a decorator that logs to Prometheus or Datadog.

import time
from functools import wraps

def observe_memory(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        duration = time.time() - start
        print(f"Memory operation {func.__name__} took {duration:.3f}s")
        return result
    return wrapper

memory.add = observe_memory(memory.add)
Enter fullscreen mode Exit fullscreen mode

Technical Verdict

Use in-process stores (LangChain, LlamaIndex) for single-session prototypes where memory loss on crash is acceptable. No setup, no credentials, fast iteration.

Use database-backed stores (mem0ai Memory + Qdrant) for multi-session agents that need semantic retrieval and cross-instance memory. You own the infrastructure, you control pruning, but you manage connection failures and scaling.

Use hosted platforms (mnemoverse, MemoryClient) for production agents where you want persistence without operating a database. You pay for convenience with vendor lock-in and network latency.

Avoid pickle-based stores in production. They are not portable, not auditable, and introduce deserialization risks.

Avoid packages with no explicit pruning strategy for long-running agents. Token budgets or manual checkpointing are required to prevent unbounded memory growth.

The install command is the least informative fact. Check the dependency count, read the constructor signature, and test the failure modes before you commit.


Source Links

Top comments (0)