Introduction
Hey Everyone!
You can find all the C# code samples here: MicrosoftAgentFramework GitHub Repo
Are you planning to add a memory layer to your AI agents? If so, that is a great step toward building stateful, personal experiences. However, there is one critical system design decision you must make upfront: how to partition and isolate your agent memory stores.
In this article, I will show you how to choose and implement the right AI memory scopes in Microsoft Foundry and the Microsoft Agent Framework (MAF). By setting up proper memory boundaries, your agents can retain long-term context accurately without risking data leaks across users, domains, or applications.
Before you read this post, you may check other posts related to AI memory:
- 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
- Mastering Microsoft Foundry Memory: Long-Term Context for AI Agents
What Are AI Memory Scopes?
When discussing memory scopes, we are fundamentally talking about data isolation and access boundaries. Unlike semantic knowledge injected dynamically via Retrieval Augmented Generation (RAG), long-term agent memory persists user preferences, facts, and interaction history across separate sessions and chat threads.
We will analyze four foundational memory scoping patterns:
- Per User, Per Agent (Strict 1:1 isolation)
- Per User, Across Agents (Shared profile across domain agents)
- Per Agent, Across Users (Collective team or group knowledge)
- Multi-Store Coordination Per User (Specialized, multi-provider memory)
For each architectural pattern, I will walk through C# implementation details using Microsoft Agent Framework Memory scopes and the Microsoft Foundry memory feature, while highlighting key architectural differences in how memory scopes in Mem0 handle metadata filtering.
Pattern 1: Per User, Per Agent

In this pattern, memory is isolated strictly to one specific user and one specific agent.
For example, imagine a Gym Trainer Agent. Everything you tell this gym trainer stays inside this 1:1 relationship. If you later switch to a Dietitian Agent, that dietitian agent cannot access any memories created with your gym trainer.
Memory Scopes in Foundry vs. Mem0
When applying Memory Scopes in Foundry, isolation relies on a simple string key (for example, user-michal but in reality it will be likely an ID from your CIAM solution). Because Microsoft Foundry memory is in preview, this string-based scope is simple to set up, but it can feel a bit rigid for complex scenarios.
By contrast, Memory Scopes in Mem0 provide more built-in flexibility. Mem0 organizes scopes using structured attributes (user_id, agent_id, app_id, session_id) and key-value metadata. During context lookup, Mem0 filters memories directly using these parameters.
C# Implementation with Microsoft Agent Framework
Using Microsoft Agent Framework, I create the connection to our memory store using FoundryMemoryProvider (which was discussed in details in the previous post).
public FoundryMemoryScopesExample(
string agentName,
string instructions,
IReadOnlyList<(string StoreName, string ScopeId)> storeConfigs)
{
// trimmed for brevity
var projectClient = new AIProjectClient(
new Uri(Environment.GetEnvironmentVariable("FOUNDRY_PROJECT_NAME")!),
credential
);
foreach (var (storeName, scopeId) in storeConfigs)
{
_memoryProviders.Add(new FoundryMemoryProvider(
projectClient,
storeName,
stateInitializer: _ => new(new FoundryMemoryProviderScope(scopeId)),
new FoundryMemoryProviderOptions()
{
StateKey = $"{typeof(FoundryMemoryProvider).Name}-{storeName}"
}
));
}
_agent = new ChatClientAgent(responseClient, new ChatClientAgentOptions()
{
Name = agentName,
ChatOptions = new ChatOptions
{
Instructions = instructions,
Reasoning = new ReasoningOptions() { Effort = ReasoningEffort.Low, Output = ReasoningOutput.None }
},
AIContextProviders = _memoryProviders
});
}For the memory store used in the 1st example (gym-store), I enabled User Profile memory inside Microsoft Foundry. Writing a clear, detailed store description is key here because it tells the underlying LLM exactly which facts to extract and which ones to ignore.

Multi-Session Walkthrough
public static async Task PerUserPerAgentExample()
{
var pattern1Example = new FoundryMemoryScopesExample(
agentName: "Gym Trainer",
instructions: "You are a personal gym trainer. Never use more than 60 words in your responses.",
storeConfigs:
[
(GYM_STORE_NAME, "user-michal")
]
);
await pattern1Example.EnsureStoredMemoriesDeletedAsync();
// Conversation 1: Teach the agent in Session 1
await pattern1Example.RunConversationAsync(
[
"I cannot do deadlift but I prefer FBW (max. 3 exercises) and power exercies like squats, soldier press, bench press etc. and I can train only 2 times per week."
]);
// Conversation 2: Teach the agent in Session 2
await pattern1Example.RunConversationAsync(
[
"My left shoulder pinches whenever I do heavy overhead presses."
]);
// Conversation 3: Test cross-session retrieval in Session 3
await pattern1Example.RunConversationAsync(
[
"Prepare a training plan for this week. A friend of mine recommended overhead presses!"
]);
}To test this pattern across separate chat sessions (simulating three brand new chat windows):
- Conversation 1: I tell the agent: “I cannot do deadlift but I prefer FBW (max. 3 exercises) and power exercies like squats, soldier press, bench press etc. and I can train only 2 times per week.”
- Conversation 2: In a new thread, I add: “My left shoulder pinches whenever I do heavy overhead presses.”
- Conversation 3: In another new thread, I ask: “Prepare a training plan for this week. A friend of mine recommended overhead presses!”
Even though each conversation uses a completely new agent session thread and therefore doesn’t have access to the chat history of another session (like a brand new chat window), the agent retrieves long-term memories and generates a tailored plan:

The response demonstrates the core value of AI Memory Scopes:
- It limits the plan to two sessions.
- It excludes deadlifts and plans squats.
- It warns about overhead presses and suggests warm-ups for the shoulder injury.
And here are the memories which are saved in Microsoft Foundry gym-store Memory Store.

Pattern 2: Per User, Across Agents (Shared Profile)

In this pattern, multiple agents share a single memory store for the same user. This creates a unified user profile where facts learned by one agent become immediately available to other domain agents.
For example, if you interact with a Gym Trainer Agent and later switch to a Dietitian Agent, both agents read from and write to the exact same memory store.
Cross-Agent Context Sharing in Action
Imagine this multi-agent interaction:
- Gym Trainer Agent: You mention: “I am strictly lactose intolerant and recovering from a knee injury.”
- Dietitian Agent: In a completely separate conversation, you ask: “What post-workout snack do you recommend for me today?”
Because both agents share the same memory store, the Dietitian Agent automatically recommends a lactose-free snack without you needing to repeat your dietary restrictions.

C# Implementation with Microsoft Agent Framework
To implement Microsoft Agent Framework Memory scopes for shared profiles, I instantiate both agents using the exact same memory store configuration in C#:
public static async Task PerUserAcrossAgentsExample()
{
// Agent 1
var gymAgent = new FoundryMemoryScopesExample(
agentName: "Gym Trainer",
instructions: "You are a personal gym trainer. Never use more than 60 words in your responses.",
storeConfigs: [(SHARED_STORE_NAME, "user-michal")]
);
// Agent 2
var dietitianAgent = new FoundryMemoryScopesExample(
agentName: "Dietitian",
instructions: "You are a personal dietitian. Never use more than 60 words in your responses.",
storeConfigs: [(SHARED_STORE_NAME, "user-michal")]
);
await gymAgent.EnsureStoredMemoriesDeletedAsync();
// Conversation 1: Gym Trainer writes context to UserProfileStore
await gymAgent.RunConversationAsync(
[
"I am strictly lactose intolerant and recovering from a knee injury."
]);
// Conversation 2: Dietitian reads directly from the SAME UserProfileStore
await dietitianAgent.RunConversationAsync(
[
"What post-workout snack do you recommend for me today?"
]);
}When setting up Memory Scopes in Foundry, both agents attach to the same default memory store and use the same scope string (user-michal).

Architectural Trade-Offs: Shared Profile vs. Dedicated Stores
While shared profiles simplify cross-agent context, they bring key system design trade-offs when choosing AI Memory Scopes:
Metadata Filtering in Mem0: This is where Memory Scopes in Mem0 offer more precision. Mem0 allows you to attach structured filters a.k.a. entities (such as agent_id or app_id or user_id) and key-value pairs a.k.a. metadata, keeping extraction targeted while maintaining a unified profile layer.
Fact Extraction Clutter: During memory processing, an LLM uses extraction instructions to decide what facts to save. If 15 specialized agents share one memory store, a single instruction set must cover all domains. This can cause memory clutter and reduce retrieval quality.
Pattern 3: Per Agent, Across Users (Collective Knowledge)

In this pattern, an agent maintains a shared memory pool accessible across all users. Instead of isolating data per user, the agent aggregates operational knowledge, facility rules, or schedule updates from every interaction and applies them globally.
For example, when Member 1 reports a facility schedule restriction to the Gym Assistant Agent, the agent logs this operational fact. When Member 2 asks about facility availability in a completely new session, the agent retrieves that shared context to give an accurate answer.
Collective Memory in Action
Here is how cross-user context sharing functions in this setup:
- Member 1: “Please remember that the heavy lifting zone is reserved for private coaching every Tuesday at 10 AM.”
- Member 2: “Is the heavy lifting zone open for general use on Tuesday at 10 AM?”
Because the Gym Assistant reads from a shared scope (shared-gym-team-01), it informs Member 2 that the area is reserved for private coaching.

C# Implementation with Microsoft Agent Framework
To implement Microsoft Agent Framework Memory scopes for shared facility knowledge, I attach the Gym Assistant agent to a static shared scope string (shared-gym-team-01) across user sessions:
public static async Task PerAgentAcrossUsersExample()
{
// Single agent managing shared facility knowledge for all gym members
var teamAgent = new FoundryMemoryScopesExample(
agentName: "Gym Assistant",
instructions: "You are a gym assistant managing facility schedules and rules. Never use more than 60 words in your responses.",
storeConfigs:
[
(GYM_STORE_NAME, "shared-gym-team-01")
]
);
await teamAgent.EnsureStoredMemoriesDeletedAsync();
// Conversation 1: Member 1 adds shared facility information
await teamAgent.RunConversationAsync(
[
"User 1: Please remember that the heavy lifting zone is reserved for private coaching every Tuesday at 10 AM."
]);
// Conversation 2: Member 2 asks about facility availability in a new session
await teamAgent.RunConversationAsync(
[
"User 2: Is the heavy lifting zone open for general use on Tuesday at 10 AM?"
]);
}When setting up memory scopes in Foundry, all member interactions route to this single store key, ensuring facility rules persist globally across callers.


Architectural Trade-Offs: Shared Knowledge Risks
While collective knowledge gives agents continuous context updates, it comes with key architectural considerations when using AI Memory Scopes:
- Data Leakage & PII: If a user accidentally includes private personal details while sharing a facility rule, that sensitive info could be stored in the shared memory scope and exposed to other users.
- Memory Poisoning: Misleading or incorrect facility info provided by one user can corrupt the shared store, causing the agent to propagate bad schedule rules to everyone else.
Pattern 4: Multi-Store Coordination (Cross-Domain Context)

In this pattern, a single agent connects to multiple distinct memory stores simultaneously, using the same user scope across each store. Rather than dumping all user context into one unstructured bucket, this design maintains domain segregation (e.g., separating workout routines from dietary restrictions) while allowing the agent to query and synthesize cross-domain insights in a single prompt.
For example, a Health Coordinator Agent attaches to both a Gym Store and a Diet Store under the target scope user-michal. The agent pulls training intensity from the workout store and carbohydrate constraints from the diet store to construct a personalized post-workout nutrition strategy.
Multi-Store Coordination in Action
Here is how cross-domain context synthesis functions across separate memory stores:
- Session 1 (Ingestion): “I train heavy leg day on Mondays and I follow a strict low-carb diet.” (The system extracts workout schedule facts into the gym store and nutritional guidelines into the diet store under
user-michal.) - Session 2 (Synthesis): “Based on my workout schedule and dietary rules, what should my Monday post-workout meal look like?” (The agent queries both stores concurrently, combines Monday’s heavy leg session context with low-carb rules, and suggests a high-protein, low-carb recovery meal.)

C# Implementation with Microsoft Agent Framework
To implement Microsoft Agent Framework Memory scopes across multiple specialized stores, I configure the Health Coordinator with an array of store pairings pointing to distinct domain stores (GYM_STORE_NAME and DIET_STORE_NAME), both bound to user-michal:
public static async Task CoordinateMultipleStoresExample()
{
// Single agent connected to multiple distinct memory stores with the same user scope
var healthAgent = new FoundryMemoryScopesExample(
agentName: "Health Coordinator",
instructions: "You are a holistic health assistant coordinating both fitness and nutrition plans. Never use more than 60 words in your responses.",
storeConfigs:
[
(GYM_STORE_NAME, "user-michal"),
(DIET_STORE_NAME, "user-michal")
]
);
await healthAgent.EnsureStoredMemoriesDeletedAsync();
// Session 1: Teach the agent facts across both domain stores
await healthAgent.RunConversationAsync(
[
"I train heavy leg day on Mondays and I follow a strict low-carb diet."
]);
// Session 2: Verify the single agent can query and synthesize context from both stores
await healthAgent.RunConversationAsync(
[
"Based on my workout schedule and dietary rules, what should my Monday post-workout meal look like?"
]);
}When configuring Memory Scopes in Foundry, registering multiple stores per agent enables the framework to perform parallel vector and key-value retrieval across separate store endpoints before building the agent’s turn prompt.
Architectural Trade-Offs: Multi-Store Complexity
While domain-segregated multi-store setups maintain clean boundaries and prevent context contamination, they introduce specific system trade-offs when implementing AI Memory Scopes:
- Increased Latency & Token Usage: Fetching memories from multiple distinct stores on every turn multiplies network requests and inflates system prompt size.
- Retrieval Threshold Calibration: If vector distance thresholds are too permissive across multiple stores, irrelevant facts from adjacent domain stores can inject unnecessary noise into response generation.
- Mem0 vs Foundry Isolation: In Foundry, cross-domain separation requires registering multiple distinct memory store instances. Mem0 achieves similar domain isolation inside a single store by using custom metadata tags (like
category="fitness"vscategory="nutrition") to filter queries during retrieval.
Read vs. Write Scope Variations
When coordinating multiple memory stores, you don’t always want symmetric read and write permissions across every store:
- Read-Write Stores (the most common): The agent actively extracts new facts from the conversation and persists updates back into the store (e.g., logging a new workout PR or updated dietary targets in the Gym Store).
- Read-Only Stores: The agent retrieves existing episodic context (e.g., reading a read-only User Medical Profile Store populated during initial onboarding) to ground its decisions, but is restricted from writing new conversation facts back to that store.
Controlling read vs. write access prevents agents from polluting foundational user profiles with routine chat noise or domain-crossed memory updates.
Summary
I hope this post helped you understand the four foundational memory scopes so that when you build your own memory layer or evaluate an off-the-shelf solution, you have a solid framework to guide your decision.
Thanks for reading and see you in the next post!
