The Production Data Plane
Choose one durable source of truth, add specialized infrastructure only after measurement, and design every cache around correctness.
Start with explicit production boundaries
Application containers should be disposable. Graph checkpoints, long-term memories, audit records, and idempotency claims must survive restarts outside the pod or process. Expensive background work belongs on a queue. Attachments belong in object storage. Caches accelerate reads but never become the only copy of truth.
This topology scales request workers independently from memory workers. A model-provider slowdown does not erase state, and a failed extraction job can retry without replaying an email action.
┌─────────────────────────────┐
email provider ────▶ │ API + LANGGRAPH WORKERS │
└───┬──────────┬──────────┬───┘
│ │ │
▼ ▼ ▼
POSTGRES REDIS OBJECT STORE
+ pgvector / VALKEY raw content
│
▼ events
DURABLE QUEUE ─────▶ MEMORY WORKERSAdd Qdrant only after a benchmark says why
There is no universal vector-count threshold at which pgvector suddenly fails. Data shape, filter selectivity, write rate, latency target, hardware, index tuning, and operational skill all matter. Stay with pgvector while it meets measured service levels and keeps the system simpler.
Add Qdrant—or another dedicated vector engine—when profiling shows that vector search needs independent sharding, replication, tuning, or scaling. Keep PostgreSQL as metadata authority and treat the vector service as a derived retrieval index. Use an outbox or versioned indexing job rather than fragile ad-hoc dual writes.
| Option | Best fit | Strength | Main cost |
|---|---|---|---|
| PostgreSQL + pgvector | Default and metadata-heavy retrieval | Transactions, joins, one backup path | ANN tuning competes with database workload |
| Qdrant | Independently scaled vector retrieval | Purpose-built filtering, sharding, replication | A second distributed data system |
| Managed vector database | Small platform team prioritizing operations | Vendor-managed scaling | Cost, portability, governance trade-offs |
| OpenSearch | Lexical + vector document discovery | BM25, filters, aggregations, vectors | Operational weight; weak canonical store |
The migration trigger should be a failed benchmark tied to a service-level objective, not an impressive round number.
Use Redis or Valkey for ephemeral acceleration
Redis or Valkey is useful for rate limits, distributed locks, idempotency claims, hot profiles, OAuth-token caches, embedding caches, and short-lived retrieval results. It is not the authoritative memory store: eviction, flushes, or regional failover must not erase durable user knowledge.
Cache-aside is easier to reason about than write-behind. Read the cache, fall back to the database, and populate a short-lived entry. On memory supersession or procedure activation, advance a namespace epoch or invalidate affected keys.
cache_key = stable_hash({
"tenant": tenant_id,
"user": user_id,
"query": normalized_query,
"memory_epoch": memory_epoch,
"embedding_model": embedding_model,
"policy_version": policy_version,
})
if cached := redis.get(cache_key):
return deserialize(cached)
results = search_authoritative_memory()
redis.setex(cache_key, 120, serialize(results))
return results
Never cache a side-effect decision such as “send,” “schedule,” or “delete” and replay it without fresh authorization. Cache latency, not authority.
Choose queue and object storage by workload
A managed cloud queue is a strong first choice for extraction, embedding, consolidation, and re-indexing. It provides retries, visibility timeouts, and dead-letter queues without operating a streaming platform. Choose Kafka when sustained throughput, ordered partitions, long replay, and multiple independent consumers justify its complexity.
Store raw MIME messages, attachments, large evaluation artifacts, and redacted source snapshots in S3, GCS, or an equivalent object store. Databases hold references and metadata. Use scoped service identities, encryption, retention policy, and lifecycle deletion.
Version embeddings and rebuild safely
Record the embedding provider, model, dimension, normalization, and content hash on every vector. A migration writes new embeddings into a parallel column or collection, measures retrieval quality against the labelled set, switches reads by version, and retires the old index only after rollback is no longer needed.
Do not re-embed in place without an audit trail. Mixing incompatible embeddings in one search silently corrupts ranking even when every individual write succeeds.
Design for degradation
If semantic retrieval times out, the agent may continue with current-thread context for a low-risk draft, clearly marking that durable preferences were unavailable. It should not perform a high-impact action whose safety depends on memory. If Redis is down, fall back to PostgreSQL with rate protection. If the queue is down, the visible response may succeed while an outbox retains the learning event for later publication.
Define these modes before incidents. “Memory unavailable” is an observable state, not an exception that prompts the model to invent missing context.
Key takeaways
- Keep graph state and long-term memory outside disposable application containers.
- Start with PostgreSQL plus pgvector as the authoritative store and benchmark before splitting vectors out.
- Use Qdrant when measured vector pressure justifies independent infrastructure.
- Redis or Valkey accelerates idempotency, profiles, and retrieval; it is not durable memory authority.
- Queues decouple background work, object storage holds large artifacts, and embedding versions make migration safe.
Checklist
- [ ] I can assign checkpoints, memories, caches, jobs, and attachments to the correct stores.
- [ ] I can explain why vector ranking happens after tenant filtering.
- [ ] I can define a measured trigger for adding Qdrant.
- [ ] I can construct a cache key that changes with memory and policy versions.
- [ ] I can describe a parallel-index embedding migration and rollback.