Skip to content

Architecture

A memory system, not a vector store with a nice wrapper.

This page is written for the engineer deciding whether to integrate Smara. It covers what each pillar stores, how retrieval combines vectors with graph traversal, what the consolidation worker actually does, and the guarantees around provenance, time and deletion.

Shape of the system

Two planes, four stores.

The data plane is what your agents call: assemble context, search memory, traverse the graph, append episodes. It authenticates with a per-tenant API key. The control plane is what humans call: tenants, members, keys. It authenticates with a Firebase ID token. They share a tenant boundary and nothing else.

Underneath, each pillar is stored in the engine that fits its access pattern rather than in one general-purpose database asked to be good at everything.

Spanner

Semantic + graph

Facts, entities and typed edges live in one database with both a vector index and a property graph over the same rows — so similarity search and traversal never disagree about what exists.

Firestore

Episodic ledger

Append-only session and event documents, partitioned by tenant. Cheap unconditional writes on the agent's hot path, streamed to consolidation via Pub/Sub.

Memorystore (Valkey)

Working memory

Per-session scratchpad and assembled context cache. Deliberately volatile: nothing here is authoritative, and losing it costs a re-assembly, not a memory.

Cloud Storage

Procedural artifacts

Versioned skill and playbook bodies, content-addressed. The pointer and its approval state live in the control plane; the bytes live here, immutable per version.

Runs on Google Cloud: Cloud Run for the API and the consolidation worker pool, Pub/Sub between them, Vertex AI for embeddings and extraction.

The four pillars

Separated by lifetime and by write path.

Ephemeral · per request

Working memory

  • POST /v1/context
  • POST /v1/context/scratchpad

Working memory is the assembly step, not a store. For each request Smara composes a context window from the system prompt, the agent's scratchpad, the recent turns of the session, and the long-term memory retrieved for this query — then trims to the token budget you specify.

Everything in it is a copy. If a fact only exists in working memory it is not remembered; it will be gone on the next call unless it was written to episodic memory or promoted to semantic memory. Keeping that boundary strict is what stops an agent's context window from quietly becoming an undocumented database.

Versioned · human-approved

Procedural memory

  • GET /v1/skills
  • POST /v1/skills/{id}/versions
  • POST /v1/skills/{id}/versions/{v}/approve

Procedural memory holds behaviour — the reusable skills, standard operating procedures, playbooks and conditional policies an agent follows. It is the part of memory most likely to cause damage if it changes silently, so it is the part with the strictest write path.

Edits produce a new immutable version rather than mutating the live one. Promotion from draft to active requires an explicit human approval, and every activation records who approved it and when. Rolling back is selecting an older version, not restoring a backup.

Durable · typed · queryable

Semantic memory + knowledge graph

  • POST /v1/memory/search
  • GET /v1/graph/entities/{id}
  • POST /v1/graph/traverse

Facts are stored with embeddings for similarity search and as nodes and typed edges in a property graph. Neither alone is enough: vector search finds text that looks relevant, graph traversal finds the things that are actually connected to it. Smara runs both and merges the results.

Every fact and every edge carries temporal validity (valid_from / valid_to), a confidence score, and provenance back to the sessions it was derived from. A fact is never edited in place — a newer fact supersedes an older one, and the old one keeps its validity window so you can still answer questions about the past.

Append-only · ledger

Episodic memory

  • POST /v1/episodes
  • GET /v1/sessions/{id}/episodes

Episodic memory is an append-only ledger of events — turns, tool calls and their results, outcomes, errors. Writes are cheap and unconditional; the agent does not have to decide at runtime whether something is worth remembering, because deciding is consolidation's job.

Because nothing is rewritten, the ledger doubles as the audit trail: when a semantic fact looks wrong, its provenance points back at the exact episodes it came from. Retention is policy-driven — episodes can be pruned on a schedule after the facts derived from them have been consolidated.

Hybrid retrieval

Vectors find candidates. The graph finds consequences.

Similarity search is good at “find text that looks like this question” and bad at “and what does that imply?” A query about a renewal date will happily match a paragraph containing the word renewal while missing the contract that actually governs it.

Smara runs both passes over the same records. Vector search returns the top candidates by cosine distance over 768-dimension embeddings; the graph then expands one to three hops from those candidates along typed edges, pulling in the entities and facts they are connected to. Results are merged, de-duplicated, filtered by scope and confidence, and packed into the token budget the caller asked for.

Because both passes read the same rows, there is no sync job between an index and a graph and no window where they disagree.

hybrid search
POST /v1/memory/search
{
  "scope":       { "tenant_id": "…", "user_id": "user:8871" },
  "query":       "when does this account renew?",
  "vector":      { "top_k": 20, "distance": "COSINE" },
  "graph":       { "min_hops": 1, "max_hops": 3 },
  "as_of":       "2026-07-27T00:00:00Z",
  "min_confidence": 0.6
}

Defaults: top_k 20 · cosine · 1–3 hops · embeddings from gemini-embedding-001, truncated to 768 dimensions.

Consolidation pipeline

The part that runs while nobody is waiting.

New episodes publish to Pub/Sub as they land. A worker pool picks them up on its own schedule, batches by session and scope, and runs extraction over the batch — pulling out candidate facts, candidate entities and candidate typed relationships, each with a confidence score and a provenance record naming the sessions and the model that produced it.

Reconciliation is where the work actually is. A candidate that matches an existing fact reinforces it. A candidate that refines one supersedes it, closing the incumbent’s validity window instead of deleting the row. A candidate that flatly contradicts a high-confidence incumbent does not win by being newer — it opens a conflict for review. New entities and edges are merged into the graph under the same rules.

None of this is on the request path. Consolidation getting slower because you have a lot of history does not make your agent slower; it makes consolidation lag, which is the correct thing to trade.

Guarantees

What you can promise your own compliance team.

Temporal validity

Every fact and edge carries valid_from and an optional valid_to. Superseding a fact closes the old window rather than deleting the row, so “what did we believe in March?” stays answerable. Pass as_of on a query to read the graph as it stood at any point in time.

Provenance and audit

Consolidated records point back at the exact source sessions, the extraction model that produced them, and the timestamp. When a fact looks wrong you can walk from the answer to the transcript that caused it, without guessing.

Confidence, not certainty

Extraction produces a score, and retrieval takes a floor. Low-confidence candidates can be surfaced for review instead of being silently promoted into the graph — and a genuine contradiction opens a conflict rather than overwriting the incumbent.

Controlled forgetting

Retention is a policy, not an accident. Episodes can be pruned on a schedule once the facts derived from them are consolidated; facts can be expired, superseded, or deleted outright for a subject-erasure request — and the deletion propagates through the graph edges that referenced them.

Human-in-the-loop for behaviour

Facts can be written automatically because a wrong fact is a retrieval problem. Procedures cannot, because a wrong procedure is an incident. Skill versions require explicit approval before they go live, and every activation is attributed.

Isolation you cannot misconfigure

Scope — tenant, user, agent — is a required field on every long-term record, not a filter applied at query time. Keys resolve to exactly one tenant, and there is no endpoint that reads across tenants.

The reference is the contract.

Read the full endpoint list, then issue a key and try it against your own tenant.