Skip to content

Agentic Memory

Memory is one half of Kyma's context engine. Through the same MCP server, an agent doesn't only recall stored facts — it queries live data (logs, traces, data sources, the catalog) in KQL/SQL and traverses the graph that links memories to the real resources they're about. Recall + live context + relationships, in one place. That's the difference between a memory store and a context engine.

Kyma gives agents a persistent memory that survives across sessions and machines: durable facts, decisions, preferences, learnings, and procedures — linked to the real resources they're about (repos, services, tables, traces) and recalled in near-realtime. It's built on Kyma's own columnar engine and graph layer, so memory is first-class queryable (KQL/SQL/Discover) and renders in the unified graph alongside everything else Kyma ingests.

The design synthesizes the strongest open-source patterns: Mem0-style LLM fact extraction with ADD / UPDATE / NOOP / INVALIDATE conflict resolution, and Zep/Graphiti-style bi-temporal validity (invalidate, don't delete) and hybrid + graph retrieval.

The use case. A coding agent (Claude Code, Cursor, …) connected to Kyma remembers your conventions and decisions, knows which service a fact is about, and pulls the right context onto each prompt — without you re-explaining.

The lifecycle

Ingestion runs in the background (the consolidation pipeline). Recall runs on the hot path with no LLM — vector + keyword candidates fused with Reciprocal Rank Fusion, then graph-expanded over the memory edges into a contextual subgraph.

What gets remembered

Extraction produces atomic memories, each typed:

TypeMeaning
factA stable truth ("the payments service owns the ledger table").
decisionA choice made ("we standardized on pgvector for embeddings").
preferenceHow you like things done ("prefer KQL over SQL in examples").
learningA cause→fix ("the flaky test failed on timezone; pin TZ=UTC").
procedureA convention / runbook ("deploy = tag, then make release").
entityA lightweight node for a person/repo/service/file/table/config, linked to the real catalog node.
summaryA deterministic activity digest (the no-LLM fallback).

Each memory carries an importance (0–1), a realm (namespace — usually the project, plus a shared global), a status (active/background/archived), and bi-temporal validity: valid_at (when it became true) and invalid_at (when it was superseded — NULL means currently valid). Contradicted memories are invalidated, not deleted, so history and point-in-time recall survive.

Memories connect through typed edges:

EdgeFrom → To
REFERENCESmemory → an entity it's about
RESOLVES_TOa memory-side entity → the real catalog graph node (cross-graph)
RELATES_TOentity ↔ entity (predicate in props)
DERIVED_FROMmemory → the source event/row it was extracted from
INVALIDATESa new memory → the memory it supersedes
MERGED_INTOa memory → the memory it was folded into

How recall works

Recall is graph-aware hybrid search, designed to "find anything fast":

  1. Candidates, in parallel — semantic (vector cosine_distance) and keyword (token-set LIKE, which the columnar engine prunes via per-extent stats).
  2. Bi-temporal filter — invalidated memories drop out (unless you ask for a point-in-time as_of or include_invalidated).
  3. RRF fusion — the two ranked lists are fused (1/(k+rank)).
  4. Graph expansion — the top hits seed a 1–2 hop walk over memory_edges, pulling in connected memories and the catalog resources/traces they link to (via target_namespace).
  5. Blend — a final score combines RRF, semantic similarity, keyword overlap, graph proximity, importance, and recency. No LLM in the default path.

The result is a ranked list of memories plus a linked list of connected resources plus a ready-to-inject context block.

Near-realtime at scale: native ANN

Vector recall is accelerated by a native columnar ANN index — each extent stores a centroid + radius for its embedding column (on the unit sphere, where the bound is provably correct for cosine). When the ann_threshold setting is on, recall pushes a distance predicate down to the scan, pruning extents that can't contain a near neighbor. It's conservative (no false negatives) and falls back to an exact scan for any extent lacking the stat.

Surfaces

For coding agents — the MCP tool

Agents connected to Kyma's MCP server get memory_search (and save_memory, recall_memory, list_memories, link_memory_to_entity, ingest_entity):

jsonc
// tools/call memory_search
{
  "query": "how do we run the release?",
  "limit": 8,
  "expand_hops": 1          // pull connected resources/traces too
}
// → { memories: [...], linked: [...], context: "Relevant memories:\n- [procedure] ..." }

Call it first when a question may depend on prior context, decisions, or how entities relate; then follow linked node ids with graph_traverse for a deeper subgraph.

Writing & curating. save_memory takes structured why / where / learned fields (folded into the body) and an optional topic_key — a stable key (e.g. architecture/auth-model) so a later save with the same realm+topic_key updates the memory in place instead of duplicating (deterministic, no LLM). update_memory_status / update_memory_importance re-weight or archive during housekeeping. memory_session_summary records a structured end-of-session recap (goal / instructions / discoveries / accomplished / next steps / files) for the next session to resume from.

Resolving conflicts. When recall surfaces a contradiction, memory_compare fetches two memories side by side and memory_judge records the verdict on the graph: supersedes invalidates the target (bi-temporal — it drops from default recall but stays for audit), merged archives it, and conflicts/related/compatible write a RELATES_TO edge.

Dynamic ingestion. ingest_entity mints a virtual resource — a service, repo, table, person, file, config, or concept — and wires it to existing graph nodes (cross-graph via target_namespace, e.g. a data-source-ingested repo:owner/name) and to memories (memory:<uuid>). Entities render in the unified graph alongside data source resources and are idempotent per (realm, kind, name). The Claude Code /kyma-ingest command drives this — and also triggers on-demand data source pulls so an agent can fill the graph from GitHub/Prometheus/etc. without leaving the editor.

Privacy. Content wrapped in <private>…</private> is stripped at the store layer before anything is embedded or persisted.

HTTP API

bash
# Near-realtime recall (no LLM). mode:"agentic" adds a synthesized brief.
curl -X POST http://localhost:8080/v1/agent/memory/query \
  -H 'content-type: application/json' \
  -d '{ "query": "what did we decide about embeddings?", "expand_hops": 1 }'

mode: "fast" (default) returns the no-LLM hybrid recall; mode: "agentic" adds an LLM-synthesized brief over the retrieved context. Response: { memories[], linked[], context, brief?, took_ms }. Filters: realms, memory_type, tags, importance_min, as_of, include_invalidated, limit, expand_hops — full request shape in the API reference.

Backup, portability, sync. Three endpoints (see API reference):

EndpointUse
GET /v1/agent/memory/export[?realm=]Full snapshot — latest node versions + edges, with embeddings.
POST /v1/agent/memory/importIdempotent re-import of an export onto another instance.
GET /v1/agent/memory/changes?since=<rfc3339>Only nodes/edges changed since a timestamp.

To replicate memory between a local engine and a control plane, poll changes?since=<last_sync> and apply the delta via import — the building block for keeping two stores in step.

The Memory workspace (web)

The /memory page has three tabs:

  • Search — run recall interactively; see scores, validity intervals, the graph path each result arrived by, and connected resources.
  • Ingestion — realtime view of the firehose, the memory store, and consolidation-pipeline runs (with extraction vs deterministic mode + counts).
  • Settings — tune everything below, live.

Claude Code plugin

The bundled plugin wires the firehose, recall injection, and session-end distillation into Claude Code's hooks. See Claude Code memory plugin.

Tuning

Every knob is editable from Memory → Settings (or PUT /v1/agent/memory/settings) and applied without a redeploy:

SettingWhat it controls
extraction_enabledLLM extraction + conflict resolution vs deterministic summaries.
min_eventsHow much new firehose activity a project needs before it's consolidated.
default_limit / default_expand_hopsRecall defaults when a query doesn't override them.
ann_thresholdNative-ANN cosine cutoff for extent pruning. 0 = off (exact scan).
w_rrf · w_semantic · w_keyword · w_graph · w_importance · w_recencyThe hybrid relevance blend.
half_life_daysRecency decay half-life.
rrf_kReciprocal-rank-fusion constant.

Defaults (server mode persists to Postgres; local mode to a JSON file):

jsonc
{
  "extraction_enabled": true, "min_events": 1,
  "default_limit": 8, "default_expand_hops": 1, "ann_threshold": 0.0,
  "w_rrf": 1.0, "w_semantic": 0.6, "w_keyword": 0.3,
  "w_graph": 0.5, "w_importance": 0.4, "w_recency": 0.3,
  "half_life_days": 30.0, "rrf_k": 60.0,
  "dreaming": { "enabled": false, "interval_secs": 86400, "mode": "full" }
}
bash
curl http://localhost:8080/v1/agent/memory/settings            # read
curl -X PUT http://localhost:8080/v1/agent/memory/settings \
  -H 'content-type: application/json' \
  -d '{ "w_graph": 0.7 }'                                       # patch one knob

The recall blend: candidates from semantic + keyword search are fused by Reciprocal Rank Fusion (1/(rrf_k + rank)), graph-expanded, then scored with the w_* weights; recency decays as exp(-ln2 · age_days / half_life_days).

Dreaming knobs

dreaming is off by default. When enabled it runs scheduled housekeeping — see Dreaming for what each run does. Knobs:

KnobDefaultControls
enabledfalseMaster switch for scheduled runs.
interval_secs86400Seconds between runs (daily).
mode"full"full · housekeeping_only · sources.
realm_scope[]Realms in scope; empty = all.
max_tool_calls100Agent-loop budget per run.
wall_clock_secs600Hard wall-clock cap per run.
data_source_read_budget25Max data_source_read calls per run (gap-fill).
data_source_read_max_bytes4194304Max bytes fetched per run (4 MiB).
mutation_cap60Max memory mutations per run.

Engine & embeddings

Extraction and the optional agentic recall brief reuse the configured agent engine (Anthropic / OpenAI / Ollama — see Engines). With no usable engine (or the Claude Code CLI engine, which doesn't run through the adk path), ingestion falls back to deterministic summaries — recall still works fully. Embeddings come from the same backend that powers schema RAG (fastembed by default; see Dynamic and vectors).

End-to-end: a coding agent that remembers

bash
# 1. Connect a terminal / coding agent to Kyma and install the plugin.
kyma connect http://localhost:8080 --token <bearer>
kyma install-plugin

# 2. Work normally in Claude Code. Hooks firehose your turns into
#    default.claude_code_events; the consolidation pipeline distills them
#    into durable memories (extraction when an engine is set).

# 3. On the next prompt, the UserPromptSubmit hook recalls relevant context:
kyma recall "how do we handle DB migrations?"
# - [procedure] Migrations live in crates/kyma-catalog/migrations, numbered NNN_*.sql ...
# - [decision] We invalidate-don't-delete superseded facts (bi-temporal) ...

# 4. Inspect or tune anything in the web app at /memory.

The agent now grounds answers in what you've actually decided and how your resources relate — and finds it in milliseconds.

See also

An open-source project · MIT licensed.