Tutorial

LangGraph + Ujex Recall: Long-Term Memory Without Building Your Own

Akshay Sarode
Direct answer

Use LangGraph's BaseStore with Ujex Recall as the backing implementation. Memory becomes .md files in your Cloud Storage bucket; Firestore holds the derived BM25 + vector index. Your agent reads/writes via the LangGraph store API; everything is queryable, greppable, and migratable.

LangGraph's memory documentation distinguishes short-term (thread-scoped, via checkpointer) and long-term (cross-session, via BaseStore). Default backings: SqliteSaver for dev, PostgresSaver for production. Ujex Recall plugs in as the BaseStore.

Why use Ujex Recall instead of Postgres

Setup

pip install langgraph ujex-recall
from langgraph.graph import StateGraph
from ujex_recall import RecallStore

store = RecallStore(api_key=os.environ['UJEX_API_KEY'], agent_id='my-agent')

graph = StateGraph(MyState).compile(store=store)

# In a node:
def remember(state, store):
    store.put(('user_facts',), 'preferred_name', 'Akshay')

def recall(state, store):
    name = store.get(('user_facts',), 'preferred_name').value
    return {'greeting': f'Hi {name}'}

What the bucket looks like

my-agent/
├── _MEMORY.md          # auto-generated index
├── user_facts/
│   ├── preferred_name.md
│   ├── timezone.md
│   └── communication_style.md
├── conversations/
│   ├── 2026-01-15/
│   │   └── thread-abc123.md
│   └── ...
└── episodes/
    ├── ep-001.md
    └── ...

Each .md has YAML frontmatter (type, tags, created, updated) followed by content. You can open any of them in a text editor.

Search

# BM25 (default)
results = store.search(('conversations',), query='deployment failure', limit=5)

# Vector (Vertex AI embeddings)
results = store.search(('conversations',), query='deployment failure', limit=5, mode='vector')

# Hybrid
results = store.search(('conversations',), query='deployment failure', limit=5, mode='hybrid')

Migrating from Postgres / Sqlite

Export your existing store to JSON. For each row, write a .md file in the appropriate bucket prefix. The frontmatter holds metadata; the body is content. Re-embed if you want vector search.

Tradeoffs

SqliteSaverPostgresStoreUjex RecallStore
Local dev fast✓ (with docker)✓ (with emulator)
Production scale~ small
Human-readable storage✓ (.md files)
Cross-session
Vector searchExternalpgvectorBuilt-in (Vertex AI)
Self-hostable

What this doesn't replace

The LangGraph checkpointer (short-term thread state) is still SqliteSaver / PostgresSaver. Long-term and short-term are different concerns; only long-term is the store.

FAQ

Does this work in production?

Yes — the SDK is in use at small scale. Cloud Storage and Firestore are battle-tested for the underlying primitives.

Can I use both Postgres (short-term) and Ujex Recall (long-term)?

Yes — they handle different things. Checkpointer for thread state, store for cross-session memory.

What's the latency on a search?

BM25 query: 80–200ms (Firestore composite index). Vector query: 200–500ms (Vertex AI). Hybrid: max of both.