Use cases
The block reference tells you what each block does. This page is the other half: what you'd actually build with it, and why the sidecar shape makes it easier than rolling your own.
Every recipe below maps a concrete agent problem onto shipped features. They compose. A real deployment usually runs several at once against the same sidecar, sharing one auth model, one policy surface, and one observability story.
Cache expensive tool calls with a hot tier
Problem. Agents re-invoke the same expensive tools (web fetches, LLM sub-calls, database lookups) with identical arguments. Naïve caching either grows without bound or evicts the entries you're about to reuse.
Build it with the KV block. Key a namespace on the
tool-call signature, Put the result with a ttl, and Get before every
call. Turn on the cache-tier access policy to make it behave like a real
cache rather than a plain TTL map:
namespaces:
- block: kv
name: tool-cache
backend: mem-default
access:
track: true # count hits per key
slide_ttl_seconds: 300 # read-through: a Get pushes expiry out to now+5m
capacity: 10000 # cap the namespace
heat_half_life_seconds: 3600 # evict the *coldest* keys first, not the oldest
Hot keys stay resident because every read slides their TTL; cold keys age out
and, over capacity, are evicted by heat (access_count · 2^(−age/half_life))
rather than blindly by age. mindd.namespace.items and
mindd.eviction.total{cause="capacity"} let you watch the tier work.
See KV → cache-tier access policy.
Keep a knowledge base that stays correct as facts change
Problem. A long-running agent learns things that later turn out to be wrong or superseded. A plain vector store just accumulates contradictory duplicates, and can never answer "what did we believe last Tuesday?" for an audit.
Build it with the Semantic block's bitemporal lifecycle. When you write a correction, name what it replaces; the prior records are invalidated in the same call:
// Upsert: "v2 replaces v1", as a revision, not a second copy
{
"records": [{
"content": "The API rate limit is 1000 req/min.",
"supersedes": ["fact-rate-limit-v1"],
"source": "docs-crawl-2026-07"
}]
}
supersedesinvalidates the old record atomically. Search stops returning it.valid_from/valid_tomodel when a fact was true, independent of when you wrote it; an as-of search reconstructs the knowledge state at any past instant.deleted_atsoft-deletes (tombstone, auditable) instead of dropping rows.Expireretires a whole slice by filter: one bounded call to forget a source, a tenant, or everything before a cutoff.if_versiongives optimistic concurrency so two agents can't clobber each other's correction.
This is the difference between a memory that grows and a memory that stays true. See Semantic → lifecycle.
Retrieve reliably across paraphrase and exact tokens
Problem. Dense vector search is great at paraphrase but routinely misses the tokens that matter most to agents: error codes, SKUs, function names, UUIDs. Keyword search nails those but misses "the login thing is broken" ≈ "authentication failure".
Build it with the Semantic block's hybrid search (Q4). One request runs a dense lane and a sparse (full-text) lane and fuses them with Reciprocal Rank Fusion:
{
"namespace": "notes",
"query": "connection reset ECONNRESET during checkout",
"mode": "SEARCH_MODE_HYBRID",
"rerank_candidate_k": 50, // per-lane candidate depth before fusion
"top_k": 10,
"predicates": [{ "field": "service", "op": "EQ", "value": "checkout" }],
"created_after": "2026-06-01T00:00:00Z"
}
The exact token ECONNRESET is found by the sparse lane even when its
embedding is unremarkable; the paraphrased intent is found by the dense lane;
RRF merges them. Metadata predicates and a time window pre-filter the
candidate set, and ids_only returns just the ids for a cheap first stage that
feeds a rerank or a graph expansion. Configure the sparse lane's language with
text_search: on the namespace. See Semantic → search modes.
Reconstruct conversations and follow agents live
Problem. You need to replay exactly what an agent saw, group events by conversation, and (for a supervisor or a live dashboard) watch new events as they land.
Build it with the Episodic block. Append events
with first-class role and session_id, so grouping isn't a metadata
convention you have to remember to honour:
Rangereplays history with monotonic cursors and an event-timestamp window (after_time/before_time), forward orreverse.Tailstreams the live edge, or replays from a cursor and then transitions to live, the backbone of a "watch this agent" view. Slow readers are detached rather than allowed to block writers.
Pair it with the Semantic block to turn raw episodes into recallable memory: log everything to episodic, embed the parts worth remembering into semantic.
Model relationships that change over time
Problem. "What is like this?" is a vector question. "What is connected to this, and how?" is a graph question, and the connections themselves have a lifetime (who owned this service in Q1, who reported to whom before the re-org).
Build it with the Graph block. Write typed nodes and
edges (sharing ids with semantic records so the two views line up) and let
the sidecar serve bounded Neighbors / Traverse. Edges are
bitemporal (#18): give them valid_from / valid_to and ask an
as_of question:
// "Who did alice report to on 2026-03-01?" Traversal as of a past date
{
"start": "person:alice",
"edge_types": ["REPORTS_TO"],
"max_depth": 3,
"as_of": "2026-03-01T00:00:00Z"
}
Traversal cost is hard-capped server-side (depth × fan-out), so an agent can't walk the whole graph by accident, and a policy cap can lower the ceiling per tenant. See Graph → bitemporal edges.
Coordinate multiple agents on shared state
Problem. Two cooperating agents want to mutate the same resource, and if one crashes mid-operation the lock must not be held forever.
Build it with the Lease block. Acquire with a
required ttl and an optional wait_for; Renew on a heartbeat while you
work; Release when done. If the holder dies, the TTL expires the lease
automatically: no stuck locks, no manual reaper.
Store what agents generate
Problem. Agents produce artifacts (images, audio, rendered reports, large structured outputs) that are too big to sit in a record and that you want to fetch back later by id.
Build it with the Artifact block. Put is
client-streaming (upload in chunks), Get is server-streaming (download in
chunks), and Stat returns metadata without the body. The same API fronts a
local filesystem in dev and S3/MinIO in production. The agent code doesn't
change.
Govern cost for autonomous agents
Problem. An autonomous or adversarial agent issues a top_k: 100000
search or a maximum-depth traversal and melts your backend, and you have no
way to see whether your spend is going to writes (indexing) or queries.
Build it with the policy engine and observability, both of which live in the sidecar precisely because it sits on the request path.
- Bound magnitude per request with an
effect: caprule ontop_k, scanlimit, graphdepth/fan_out, or hybridrerank_candidate_k. Over the cap surfaces asResourceExhaustedso clients back off. - Rate-limit abusive callers with token-bucket rules, keyed per tenant/agent/namespace/op.
- See where the cost goes:
mindd_op_duration_secondssplits write/index time from query time (the distinction the memory literature centres on), andbackend.durationisolates the engine's share from the sidecar's overhead. The embedding cache's hit/miss rates show exactly how many provider calls you're avoiding.
This is memory-layer cost governance you'd otherwise have to bolt onto every agent by hand.
Swap backends without touching agent code
Cutting across all of the above: an agent only ever speaks the sidecar's gRPC API. Which engine actually stores the data (in-memory for tests and local dev, Postgres / pgvector / S3 in production) is a config decision, not a code change. The same is true of auth, policy, and telemetry: they're declared once at the edge, not re-implemented in every framework and every agent.
That's the whole thesis. The recipes above are what you get to build on top of not having to solve memory plumbing again.