Skip to main content

Semantic memory

SemanticMemory is durable, queryable knowledge backed by an embedding model and a vector store. You write strings into it (RememberAsync) and query for the most relevant ones (RecallAsync) — typically to inject them into an agent's system prompt.

It lives in LogicGrid.Memory.

What this class is — and isn't

Role-scoped, not run-scoped. A SemanticMemory instance is meant to live as long as the role it represents. Construct it once and reuse it across many RunAsync calls. It is not a per-run cache — the durability is the point. If you only need short-lived state for one turn of a conversation, use MessageHistory on the admin instead.

Admins deliberately do not own this. An AdminBase already exposes MessageHistory — the verbatim conversation transcript for a single multi-agent run. SemanticMemory is a different abstraction: durable, queryable knowledge attached to a role (a user profile, a domain corpus, a team's accumulated facts). Folding it into the admin would blur "what was just said" with "what is known", and the two have different lifetimes, different access patterns, and different storage backends. Keep them separate on purpose.

Share one instance to build team or session memory. Pass the same SemanticMemory into several agents and they all read and write against the same vector store. That is the natural way to build pipeline-level knowledge today — no extra primitives required. Use metadata on each entry (e.g. agent, topic, session) to filter or attribute later.

Use it

using LogicGrid.Memory;
using LogicGrid.Memory.Embeddings;
using LogicGrid.Memory.VectorStores;

var embedder = new OllamaEmbeddingClient("nomic-embed-text");
var store = new InMemoryVectorStore();
var memory = new SemanticMemory(embedder, store);

// Store memories — anytime
await memory.RememberAsync("User's name is Alice.");
await memory.RememberAsync(
"Alice prefers technical answers, not analogies.",
metadata: new Dictionary<string, string> { ["topic"] = "preferences" });

// Recall when needed
var hits = await memory.RecallAsync(
"How does Alice like to be answered?",
topK: 3);

Wire it into an agent

The cleanest pattern is to override RenderSystemPromptAsync and prepend the recalled memories to the system prompt:

public class PersonalisedAgent : AgentBase<string>
{
private readonly SemanticMemory _memory;

public override string Name => "Personalised";
public override string Description => "An agent that remembers across runs.";
protected override string SystemPrompt =>
"You are a helpful assistant.\n\n" +
"What you remember about this user:\n{{memories}}";
protected override LlmClientBase LlmClient { get; }

public PersonalisedAgent(LlmClientBase llm, SemanticMemory memory)
{
LlmClient = llm;
_memory = memory;
}

protected override async Task<string> RenderSystemPromptAsync(
string input, AgentContext ctx, CancellationToken ct)
{
var memories = await _memory.RecallAsContextAsync(input, topK: 5, ct);

var template = new LogicGrid.Core.Prompt.PromptTemplate(SystemPrompt);
return template.Render(input, new Dictionary<string, string>
{
["memories"] = string.IsNullOrEmpty(memories) ? "(nothing yet)" : memories,
});
}
}

Sharing memory across multiple agents

Construct SemanticMemory once and inject it into every agent that should participate in the shared pool:

var memory = new SemanticMemory(embedder, store);

var researcher = new ResearchAgent(llm, memory);
var writer = new WriterAgent(llm, memory);
var reviewer = new ReviewerAgent(llm, memory);

// All three see — and contribute to — the same knowledge base.

Use metadata to keep things attributable:

await memory.RememberAsync(
"Sources confirmed: Q3 revenue was $42M.",
new Dictionary<string, string>
{
["agent"] = "Researcher",
["session"] = ctx.RunId,
});

Storing memories from agent output

After every run, store anything notable. The simplest approach is a post-run hook — call RememberAsync from the code that drives the admin:

var output = await admin.RunAsync(input);

if (output.Contains("PREFERENCE_NOTED:"))
{
await memory.RememberAsync(output);
}

For automatic capture, write a small "memory writer" agent that runs after the main agent and decides what's worth remembering.

Options

var memory = new SemanticMemory(
embedder,
store,
new SemanticMemoryOptions
{
MinimumScore = 0.6f,
ContextHeader = "Things you know about this user:",
});
OptionDefaultEffect
MinimumScore0.5fCosine threshold — drop hits below this.
ContextHeader"Relevant memories from past interactions:"Header line prepended by RecallAsContextAsync.

Persisting across processes

Use a persistent vector store like QdrantVectorStore so memories survive restarts:

var store = new QdrantVectorStore(
collectionName: "alice-memories",
dimensions: 768, // match your embedder
baseUrl: "http://qdrant:6333");
var memory = new SemanticMemory(embedder, store);

When to use semantic memory

Use it when:

  • A role (user, agent, team) should accumulate knowledge over time.
  • You want to recall facts mentioned in previous sessions.
  • Multiple agents need to share durable, queryable knowledge.
  • The "what did I tell this user before" question is real.

Don't use it when:

  • All context fits in a single prompt — just build a system prompt.
  • You only need the current conversation transcript — use the admin's MessageHistory.
  • You'd be better off with proper RAG over a document corpus — see the RAG pipeline.