What are topics in Engram?

Short answer: Topics are categories inside a group—name, description, scoping, and optional bounds—that tell the pipeline what to extract and how to label it.

Each topic has a natural-language description that acts like a magnet: only content that matches becomes a memory. At search time you can filter by topic so an agent pulls food preferences without destinations, or tech-stack notes without biography. Groups decide which use case you are in; topics decide what that use case may remember. Unbounded topics accumulate many facts; bounded topics hold at most one memory per unique scope (memory id derived from topic and scope), fitting profiles and conversation summaries. The Personalization template’s ConversationSummary is typically user_id plus conversation_id and bounded so each turn rewrites one summary. Design a small set of topics; when one add targets multiple topics, supply the union of required scopes. If retrieval feels noisy, adjust topic descriptions first.

Topics are the categories inside an Engram group that tell the pipeline what to extract and how to label it. Each topic has a name, a natural-language description, scoping rules, and an optional bound that limits how many memories exist per scope. Descriptions act like magnets for facts. Only content that matches a topic description becomes a memory. At search time you can filter by topic names so an agent pulls food preferences without dragging in destinations, or tech-stack notes without personal biography. This chapter covers how topics are defined, why descriptions steer extraction, when to use bounded topics, how search filters work, and how Weaviate Engram makes topic configuration the main control surface for memory content.

Groups decide which use case you are in. Topics decide what that use case is allowed to remember. If you only adjust one thing when retrieval feels noisy, adjust topics first. Engram will not invent memories outside the topics you configured.

What is a topic inside a group?

Once a group exists, the next question is how raw text becomes categorized memories. A topic is a named category within that group. It carries four core properties. The name is the stable identifier you pass in search filters. The description is natural language the extraction pipeline reads when deciding where a fact belongs. Scoping says whether the topic requires a user_id and any custom property keys. The is_bounded flag says whether the topic may hold only one memory per unique scope.

Topics are defined when you create the project as part of the group configuration. Templates seed common starters such as UserKnowledge. Custom topics are declared the same way. You do not create topics on every write. You write content into a group, and the pipeline routes extracted facts into the topics whose descriptions match.

That design keeps application code simple. Your agent sends a conversation or a string. Engram returns a run id and processes asynchronously. When the run commits, each new memory carries a topic label. Later searches can restrict to that label or leave topics open to search the whole group.

Why does the topic description control extraction?

Knowing the fields raises a sharper question: which field actually changes what gets remembered? The description. It is not a comment for humans alone. It is prompt material for the pipeline. A travel-style agent might define one topic as places the user wants to visit and another as foods the user likes. When the user says they love sushi and visit Tokyo every spring, the pipeline can route sushi to food preferences and Tokyo to destinations because the descriptions draw different magnets.

Vague descriptions produce vague memories. Tight descriptions produce sharper ones. If a coding assistant needs a tech-stack topic, say that explicitly. Ask for languages, frameworks, and libraries. Keep personal biography in a separate topic with its own description. Then a code-review path can search only the tech-stack topic. A preferences path can search only personal knowledge. The same raw conversation can feed both topics without forcing every retrieval to see every fact.

Memories are only extracted when they match at least one topic. That is a feature. It keeps chatty filler out of durable storage. If something important never appears in search, check whether any topic description would attract that fact. Expanding or rewriting descriptions is usually more effective than adding more retrieval limit or changing hybrid settings.

When should a topic be bounded?

After you shape extraction with descriptions, another design choice appears: should this topic accumulate many memories, or keep one canonical object per scope? Unbounded topics are the default. Each new fact gets a fresh memory id. That fits open-ended preference stores and accumulating incident notes.

A bounded topic holds at most one memory per unique scope. The pipeline derives the memory id from the topic name and scope. Later writes update that same memory instead of creating siblings. Transform steps that would otherwise emit many facts for the same scope consolidate them down to one. That shape fits running conversation summaries and per-user profiles you always inject into a system prompt.

The Personalization template can optionally add a ConversationSummary topic. It is typically scoped by user_id plus a conversation_id property and marked bounded. Each new turn rewrites the single summary for that conversation. Token cost for including the summary stays roughly constant even as the chat grows. Use unbounded topics when history should grow as a set of discrete facts. Use bounded topics when one authoritative document must stay current.

How do you filter search results by topic?

Once memories land in topics, the next reader question is how to retrieve only the slice you need. Pass a topics array to client.memories.search. Engram returns matches from those topics only. Omit the array and the search covers every topic in the group. Hybrid retrieval still ranks inside that filter. Scoping parameters still apply. Topic filtering is the category dial on top of meaning search.

Here is a greenhouse operations assistant. The mist bay has visitor preferences and plant-care notes as separate topics in the same group. The agent stores one turn of notes, then searches only the plant-care topic when planning watering.

import os
from engram import EngramClient, HybridRetrieval

client = EngramClient(api_key=os.environ["ENGRAM_API_KEY"])

bay = "mist-bay-3"

run = client.memories.add(
    "Bay 3 visitor wants shorter tours and no flash near the orchids. "
    "Phalaenopsis shelf B needs humidity near 70 percent after the noon vent. "
    "Skip fertilizer on shelf B until the next leaf flush.",
    user_id=bay,
    group="default",
)
client.runs.wait(run.run_id)

care = client.memories.search(
    query="humidity and fertilizer for Phalaenopsis shelf B",
    user_id=bay,
    group="default",
    topics=["plant_care"],
    retrieval_config=HybridRetrieval(limit=5),
)

prefs = client.memories.search(
    query="tour length and flash near orchids",
    user_id=bay,
    group="default",
    topics=["visitor_prefs"],
    retrieval_config=HybridRetrieval(limit=5),
)

assert any("70" in m.content or "fertilizer" in m.content.lower() for m in care)
assert any("flash" in m.content.lower() or "tour" in m.content.lower() for m in prefs)
assert all(m.topic == "plant_care" for m in care)

The example assumes the project’s default group defines plant_care and visitor_prefs with descriptions that attract those facts. Without those topics configured, extraction cannot invent the labels. With them configured, the same input can populate both, and each search stays narrow. That is how Engram turns topic names into an API you can reason about in application code.

How should you design a small set of topics?

Filtering works best when the topic set is intentional. Start from the questions your agent must answer later. If the agent must answer “what does this user prefer” and “what stack do they use,” those are two topics. If both answers live under one broad UserKnowledge topic, hybrid search can still help, but topic filters cannot separate the slices. Split when retrieval paths diverge.

Keep names short and stable. Put the nuance in the description. Prefer a few sharp topics over many overlapping ones. Overlap makes the pipeline hesitate and makes search filters less decisive. When two use cases need the same topic name with different meanings, put them in different groups rather than overloading one group’s vocabulary.

Remember that scoping is per topic. A single group can mix user-scoped preference topics with project-wide playbook topics. When one add request targets multiple topics, you must supply the union of their required scope parameters. Search is more flexible. Scope parameters act as filters, and topics that do not use a property simply ignore it. Design topics so the required scopes match how your app already identifies users and conversations.

Our next chapter, How do scopes work in Engram?, digs into those isolation levels. You will see how project membership, user_id, and custom properties keep memories visible only where they belong.