Free-form extraction invents vocabulary. Given a conversation turn, the model infers attribute names and relation labels from the language in front of it, so the same concept surfaces under different names depending on phrasing. A customer's subscription plan becomes plan in one turn, subscription_tier in the next, then pricing_tier. Nothing is wrong with any single extraction, but the graph fragments: a query for plan misses two thirds of the data.
SurrealDB Agent Memory addresses this by feeding the vocabulary already in the graph back into the extraction prompt. Every extraction run is told which names, keys, labels, and verbs the Context has used before, and is instructed to reuse them when the meaning matches. Vocabulary converges as the graph grows, without configuration.
What extraction is shown
Four vocabulary lists are gathered before each run and rendered into the prompt:
| List | How many | Selected by |
|---|---|---|
| Entity names and types | up to 500 | no ranking |
| Relation labels | up to 100 | most-used first |
| Attribute keys | up to 100 | most-used first, live rows only |
| Action verbs | up to 100 | most-used first |
Relation labels, attribute keys, and action verbs are counted within the write scope, ranked by frequency, and truncated to the most common. Attribute keys are drawn only from rows that are still current - superseded and expired values do not keep a dead key alive in the prompt.
The instruction attached to each list is the same shape. For attribute keys:
Attribute keys already used in this memory. Reuse one as an attribute
keywhen it means the same thing; only mint a new key when none fits. A key invented for a single fact can never be looked up again.
Reuse is a strong preference, not a constraint. Extraction can still mint a new key when nothing fits, which is what keeps a novel concept from being forced into a nearby-but-wrong slot.
Entity types are a fixed set
Entity types are not part of this vocabulary and cannot be extended. They are a closed list - person, organisation, project, location, topic, product, policy, concept, event, agent, service, other - and a supplied type outside it normalises to other rather than creating a new type.
This matters more than it looks: the type is half an entity's identity, so an invented type silently produces a second entity. See Entity matching.
The cold start is the weak point
The mechanism is self-reinforcing, which cuts both ways. An empty Context has no vocabulary to offer, so the earliest extractions set the terms that everything later converges on. Whatever the first few documents happen to call something becomes the house style.
Two consequences worth planning around:
Seed the names you care about before a bulk import. Entities that already exist are listed in the prompt and get reused. Creating them up front is the most reliable lever available for keeping references together.
Ingest in a deliberate order where you can. A representative document first establishes better vocabulary than an unusual one.
The entity list is the one that is not frequency-ranked, and it is capped at 500. On a Context with more entities than that, which names reach the prompt is arbitrary - so on large graphs, seeding matters more, not less, and important entities can drop out of the prompt without any signal.
What is not configurable
There is no API for supplying your own controlled vocabulary. Allowed entity types, attribute keys, and relation labels cannot be pinned per Context; the vocabulary is emergent, derived from what the graph already holds.
If you need harder guarantees than convergence provides, normalise before the write: map your variants to canonical names in your own pipeline and send those, or write structured triples with infer: "triples" to bypass model-chosen vocabulary entirely. For repairing drift after the fact, POST /fsck with the duplicates check reports near-identical entities for you to act on.
Related reading
Reconciliation and supersession - exact-key entity matching, and why nicknames fragment
Storing memories -
infermodes, including caller-supplied triplesExtraction pipeline - where extraction sits in ingest