Skip to content

Memory

Memory is a durable store of facts your applications choose to remember — preferences, decisions, event summaries — that survive across sessions. Your apps write to it over HTTP or MCP; the Memory console (/app/memory) is where you see and correct what they wrote.

It is scoped to your tenant, and below that to whichever end user the record belongs to. It is a separate thing from the agent memory UGENT keeps for itself.

What a memory is

One record is one self-contained statement — "the deploy target is eu-central-1", not a transcript. Each carries:

  • Kindsemantic (facts and preferences), episodic (event summaries), or procedural (instructions).
  • Tiercore surfaces first, contextual is the default, archival surfaces only on a strong match.
  • Importance and recency, which weight recall ranking.
  • Optional expiry, and supersession when a newer record replaces an older one.

Scoping fields let one tenant serve many end users and apps: actor_id (the end user), app_id, scope_id (a free-form grouping such as a project), plus agent_id and session_id for attribution.

The console

Five tabs:

  • Records — browse, filter, correct, and delete. Filter by actor, app, scope, kind, tier, and category.
  • Recall test — run a real recall and see exactly what an application would get back, including the score each record was given. The fastest way to answer "why did it not remember that?".
  • Ingest — paste a conversation transcript and queue it for memory creation. Smart mode extracts durable facts with an LLM and stores each one individually; raw mode stores the transcript as a single episodic record. Jobs run asynchronously; the tab polls until they finish.
  • Activity — the mutation log: what was stored, updated, superseded, or forgotten, and when.
  • Stats — counts broken down per dimension, plus your record cap.

Import and export are available from the Records tab (NDJSON), so you can back up, bulk-load, or move memories between environments.

How recall ranks

Worth knowing before you conclude a memory is missing:

  1. Lexical full-text search finds candidates.
  2. Vector similarity finds more, when your operator has enabled embedding for memory.
  3. The two lists are fused by reciprocal rank fusion — a record found by both outranks one found by only one.
  4. The fused score is weighted by importance and recency (recency decays with a 30-day half-life).

Recall with no query is a filter-only listing instead, ordered by tier, then importance, then recency.

Expired and superseded records are hidden from recall by default. The console can show them; applications have to ask explicitly.

Privacy between your users

Passing actor_id on recall returns that person's private records plus tenant-shared ones (records stored without an actor). It never returns another actor's private records — including through the vector tier, where results are re-checked against the same filter before they can surface.

The tenant boundary itself is resolved server-side from the API key and is never accepted from a request argument.

Correcting and forgetting

  • Correct a record in place from the Records tab when the content is wrong.
  • Supersede it instead when the fact changed — the old record is kept as history but hidden from normal recall. Prefer this to deleting; it keeps the trail.
  • Forget deletes permanently, by id or by filter (actor, app, scope, category, before a timestamp). Filters combine with AND, and an empty selector is rejected — there is no way to delete everything by accident.

For a user exercising a deletion request, forget-by-actor_id is the tool.

Limits and cost

Your tenant has a record cap; the Stats tab shows where you are against it. A store that would exceed it fails with a limit error rather than silently dropping older records — nothing is evicted behind your back.

When memory embedding is enabled, the embedding calls it makes are metered under their own memory line in your usage breakdown, so memory spend is separable from search spend. If a store would exceed your token quota, it fails before anything is written, so you never get a half-stored record.

Released under the Private Beta License.