Hey everyone!
You can find all the C# code samples here: MicrosoftAgentFramework GitHub Repo
Many engineering teams spend significant effort building custom memory pipelines to give their AI agents long-term context. Before you start building complex database workflows from scratch, it’s worth exploring what Microsoft Foundry Memory provides out of the box (however still in preview).
In this post, we’ll do a architectural deep dive into the Microsoft Foundry Memory Store, inspect its low-level REST mechanics, and walk through a C# integration using FoundryMemoryProvider in the Microsoft Agent Framework.
Before reading this post, you may find these memory related posts interesting too:
- Microsoft Agent Framework Tutorial: Get Started with AI Agents in .NET
- Persistent Agent Memory: Implementing ChatHistoryMemoryProvider in .NET
- Getting Started with Mem0 in .NET | Long-Term AI Memory in Microsoft Agent Framework
Microsoft Foundry Memory Store Architecture

When designing stateful AI agents, the first distinction to understand is the difference between short-term context and long-term memory:
- Short-Term Context: This is the active conversation thread passed directly in the LLM’s prompt window. Once a thread ends or a new session starts, this context vanishes unless explicitly persisted.
- Long-Term Memory: This is dynamic, durable knowledge persisted across sessions, chats, and applications without relying on static system prompts or manual file uploads.
Portal Setup & Model Deployments

In the Azure AI portal (ai.azure.com), creating a Memory Store requires configuring two specific Azure OpenAI model deployments:
- Chat Completions Model: Used behind the scenes to extract facts and consolidate memory records.
- Embedding Model: Generates vector embeddings for vector similarity search during memory retrieval.
Preview Limitations: In its current preview state, memory stores carry regional constraints and quota limits (such as a maximum of 100 memory scopes per store). Additionally, model configurations defined at creation time cannot be changed later.
Supported Memory Types

Within a single store, you can track three distinct memory categories:
| Memory Type | Purpose | Example |
| User Profile | Stores persistent facts and user preferences. | “User lives in Poland and prefers outdoor activities.” |
| Chat Summary | Summarizes entire conversation threads for continuity. | “Discussed itinerary options for a trip to Kraków.” |
| Procedural Memory | Captures routines, preferences, and operational rules. | “Do not search for flights; first calculate location boundary.” |
You can also customize extraction guidelines via a User Profile Details Prompt (e.g., instructing a fitness coach agent to record dietary habits while ignoring unrelated personal facts) and configure a Time-to-Live (TTL) for temporary memories.
Connecting memory store to Foundry Agent in the Foundry portal

When you want to connect memory to an agent defined directly in the Foundry portal (and not created using Microsoft Agent Framework) then it is as easy as linking a memory store to such an agent. Of course, you could link it programatically too. Behind the scenes it adds a tool that AI agent can call to add and search memories.
Under the Hood: Extraction, Consolidation, and Retrieval

Why does a managed memory store require both a Chat model and an Embedding model? Memory creation is not a raw log dump because it goes through a multi-stage execution pipeline.
- Context Lookup: When an update request arrives, the store searches existing memories for relevant items.
- Fact Extraction: The LLM processes raw user and assistant messages to extract structured facts rather than verbatim transcripts.
- Consolidation (Deduplication): The LLM merges newly extracted facts with existing stored records to eliminate redundancy and update altered information.
- Embedding Generation: The configured embedding model vectorizes the consolidated facts for future similarity searches.
Memory Scopes, REST APIs, and System Design Decisions
Scope Partitioning
Memory items are strictly isolated using a scope parameter. In production, this scope is typically tied to a user identifier extracted from an Entra ID JWT claim (oid), or a compound string combining user and application IDs:
// Scope pattern examples
string userScope = "usr_94a81b22";
string userAppScope = "app_sales|usr_94a81b22";While Microsoft Foundry Memory Store currently uses a single string for scopes, libraries like Mem0 support richer key-value metadata filtering (user_id, app_id, session_id).
Asynchronous Execution & Eventual Consistency

The low-level REST API exposes key operations including SearchMemories and UpdateMemories. Memory processing operates asynchronously: update requests enter a queue and are processed in the background.
System Design Insight: Asynchronous memory extraction is the right trade-off. Synchronous extraction would add seconds of latency to every user interaction. Eventual consistency causes zero friction because active short-term chat history handles conversation flow during an ongoing session.
Integrating Foundry Memory with Microsoft Agent Framework in C#
In .NET, integration with the Microsoft Agent Framework is handled through the Microsoft.Agents.AI.Foundry package via FoundryMemoryProvider.
FoundryMemoryProvider inherits from AIContextProvider and implements two key lifecycle methods:
ProvideAIContextAsync: Invoked before the LLM call to retrieve relevant memories and hydrate the prompt context.StoreAIContextAsync: Invoked after the LLM response to queue conversation turns for memory processing.
C# Implementation Example
namespace _09_Memory_FoundryMemory
{
internal class FoundryMemoryProviderExample
{
private readonly AIAgent _mafAgent;
public FoundryMemoryProviderExample()
{
// trimmed for brevity
var foundryMemoryProvider = new FoundryMemoryProvider(
new AIProjectClient(new Uri(Environment.GetEnvironmentVariable("FOUNDRY_PROJECT_NAME")!), credential),
Environment.GetEnvironmentVariable("FOUNDRY_MEMORY_STORE_NAME")!,
stateInitializer: _ => new(new FoundryMemoryProviderScope("Michal-123")));
_mafAgent = new ChatClientAgent(responesClient, new ChatClientAgentOptions()
{
Name = "Agent powered by Foundry Memory",
ChatOptions = new ChatOptions
{
Instructions = "You are a helpful assistant that always responds in English.",
Reasoning = new ReasoningOptions() { Effort = ReasoningEffort.Low, Output = ReasoningOutput.None }
},
AIContextProviders = [foundryMemoryProvider]
});
}
public async Task RunAsync()
{
var foundryMemoryProvider = _mafAgent.GetService<FoundryMemoryProvider>();
AgentSession firstSession = await _mafAgent.CreateSessionAsync();
await foundryMemoryProvider!.EnsureStoredMemoriesDeletedAsync(firstSession);
Console.WriteLine(await _mafAgent.RunAsync("I live in Poland", firstSession));
Console.WriteLine("\n---------------\n");
var start = Stopwatch.StartNew();
await foundryMemoryProvider!.WhenUpdatesCompletedAsync(pollingInterval: TimeSpan.FromSeconds(1));
start.Stop();
Console.WriteLine($"Memory update completed in {start.Elapsed.TotalSeconds}s");
Console.WriteLine("\n---------------\n");
AgentSession secondSession = await _mafAgent.CreateSessionAsync();
Console.WriteLine(await _mafAgent.RunAsync("What is the capital of the country I live in?", secondSession));
}
}
}
You can see that we have to pass AIProjectClient to the FoundryMemoryProvider and that is because that provider is just a wrapper around memory store related actions that AIProjectClient exposes.
This is what gets saved into Microsoft Foundry memory store and what is then retrieved by the second session, thanks to which the correct answer can be provided.

Low-Level APIs: Controlling updateDelay, Lineage, and State Machines
When invoking AIProjectClient memory APIs directly, you gain fine-grained control over execution mechanics.
Key Parameters:
UpdateDelay: Instructs the background queue to wait before processing (e.g., 300 seconds). This prevents rapid, repetitive extraction calls while a user is actively typing multi-turn prompts.PreviousUpdateId: Acts as a parent state pointer. Passing the previous update ID allows the backend to perform incremental diffs or supersede older pending updates.
var updateResult = await _foundryProjectClient.MemoryStores.UpdateMemoriesAsync(
Environment.GetEnvironmentVariable("FOUNDRY_MEMORY_STORE_NAME")!,
options: new MemoryUpdateOptions(_scope)
{
UpdateDelay = (int) updateDelay.TotalMilliseconds,
PreviousUpdateId = previousUpdateId,
Items = // user and assistant messages
});Processing statuses
When you run the 2nd example FoundryMemoryStoreApiExample you will see the following statuses (except for the Failed):
Queued: Request is submitted and waiting in the processing queue (respectingUpdateDelay).InProgress: Fact extraction, deduplication, embedding generation, and entity linking are actively running.Completed: Operation succeeded and the updated memory items are persisted in the store.Superseded: Superseded by a newer operation (PreviousUpdateId).Failed: Terminated due to an unrecoverable error (e.g., rate limits, bad payloads, or authorization failures).
Final Verdict
Microsoft Foundry Memory Store simplifies long-term context management for Azure-native workloads. While third-party options like Mem0 currently offer more granular metadata filtering and custom categories, Foundry’s managed approach, seamless C# AIContextProvider integration, and native agent service ecosystem make it a compelling out-of-the-box solution as it progresses toward General Availability.
Summary
Giving AI agents long-term memory doesn’t require building custom database pipelines and chunking logic. Microsoft Foundry Memory provides a managed, asynchronous extraction and consolidation pipeline out of the box. By integrating FoundryMemoryProvider into the Microsoft Agent Framework, your C# agents can automatically retain user profiles, chat summaries, and procedural routines across completely separate conversation sessions.
