Table of Contents

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:

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:

  1. Per User, Per Agent (Strict 1:1 isolation)
  2. Per User, Across Agents (Shared profile across domain agents)
  3. Per Agent, Across Users (Collective team or group knowledge)
  4. 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

Diagram of the Per User, Per Agent pattern demonstrating AI Memory Scopes and Microsoft Agent Framework Memory scopes with user Michal and the Gym Trainer 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.

Microsoft Foundry configuration screen for Memory Scopes in Foundry showing User Profile extraction instructions.

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:

C# console output showing Microsoft Agent Framework Memory scopes retaining context across three chat sessions using AI Memory Scopes.

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.

Microsoft Foundry memory store table showing stored profile facts for Memory Scopes in Foundry.

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

Architectural diagram of the Per User, Across Agents pattern demonstrating AI Memory Scopes shared between Dietitian and Gym Trainer agents using Microsoft Agent Framework Memory scopes.

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:

  1. Gym Trainer Agent: You mention: “I am strictly lactose intolerant and recovering from a knee injury.”
  2. 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.

Terminal output showing cross-agent context sharing using Microsoft Agent Framework Memory scopes, where the Dietitian agent accesses memories saved by the Gym Trainer.

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

Shared profile store view for Memory Scopes in Foundry listing extracted lactose intolerance and injury details.

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)

Architectural diagram of the Per Agent, Across Users pattern showing multiple users interacting with a Gym Assistant agent connected to a shared store using Microsoft Agent Framework Memory scopes and AI Memory Scopes.

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:

  1. Member 1: “Please remember that the heavy lifting zone is reserved for private coaching every Tuesday at 10 AM.”
  2. 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.

Terminal output demonstrating cross-user collective knowledge sharing in Memory Scopes in Foundry, where facility schedule rules set by User 1 are retrieved for User 2.

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.

Gym store memories view in Memory Scopes in Foundry displaying shared memory entries stored under the shared-gym-team-01 scope using Microsoft Agent Framework Memory scopes.
Detailed memory inspection panel in Memory Scopes in Foundry showing extracted facility reservation details stored across users.

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)

Architectural diagram of the Multi-Store Coordination pattern showing a Health Coordinator agent querying distinct gym and diet stores under a single user scope using Microsoft Agent Framework Memory scopes and AI Memory Scopes.

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:

  1. 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.)
  2. 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.)
Terminal console output demonstrating cross-domain context synthesis in Memory Scopes in Foundry, where an agent combines workout schedules and nutrition rules from separate stores compared to Memory Scopes in Mem0.

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" vs category="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!

Categorized in:

Agents,