Skip to content

Tuning

Extraction vocabulary

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.

Four vocabulary lists are gathered before each run and rendered into the prompt:

ListHow manySelected by
Entity names and typesup to 500most recently updated first
Relation labelsup to 100most-used first
Attribute keysup to 100most-used first, with 25 slots for recent keys; live rows only
Action verbsup to 100most-used first, with 25 slots for recent verbs

Relation labels, attribute keys, and action verbs are counted within the write scope, ranked by frequency, and truncated to the most common. For attribute keys and action verbs, the last 25 of the 100 places are reserved for recently written keys and verbs that the frequency ranking does not already include, so a key created an hour ago is offered back before it is used often. Attribute keys are drawn only from records 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 key when 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 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. Two of those types, agent and service, are reserved for the self entities that the system creates for principals, and extraction is not taught to emit them.

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 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 follow:

  • 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.

Note

The entity list is the one that is not frequency-ranked: it lists the most recently updated entities first, and it is capped at 500. On a Context with more entities than that, an entity that has not been updated recently drops out of the prompt without any signal, however important it is - so on large graphs, seeding matters more, not less.

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 has a duplicates check intended to report near-identical entities, but it currently returns no candidates (see Entity matching), so review the entity list yourself.

Was this page helpful?