{"slug": "user-scoped-memory-in-deep-agents-what-it-is-and-why-you-need-it", "title": "User-Scoped Memory in Deep Agents: What It Is and Why You Need It", "summary": "Deep Agents introduced user-scoped memory to isolate long-term memory per user in shared AI agents, preventing preference leakage between users. The implementation uses a namespace based on the user ID, ensuring each user's memory files remain separate, as demonstrated with Alice and Bob receiving distinct responses.", "body_md": "Imagine you build one AI agent and 1,000 people use it.\n\nAlice tells the agent:\n\n\"I prefer Python examples and concise answers.\"\n\nLater, Bob asks:\n\n\"How do I read a CSV file?\"\n\nBob should not suddenly receive a short Python-focused answer just because Alice used the agent before.\n\nThis is exactly the problem **user-scoped memory** solves.\n\nUser-scoped memory means giving **each user their own separate long-term memory**.\n\nThe same agent can be shared by many users, but the memory belonging to one user is isolated from everyone else.\n\nFor example:\n\n```\n                    One AI Agent\n                         |\n          +--------------+--------------+\n          |              |              |\n       Alice            Bob          Charlie\n          |              |              |\n     Alice's memory  Bob's memory  Charlie's memory\n```\n\nAlice might have:\n\n```\n- Likes concise answers\n- Prefers Python\n```\n\nBob might have:\n\n```\n- Likes detailed explanations\n- Prefers TypeScript\n```\n\nWhen Alice talks to the agent, the agent reads Alice's memory. When Bob talks to it, the agent reads Bob's memory.\n\nDeep Agents implements this using a **namespace**. The namespace can be based on the user's ID, such as:\n\n```\nnamespace=lambda current_runtime: (\n    current_runtime.context.user_id,\n)\n```\n\nThis makes the user's ID the boundary between their memories. LangChain's documentation describes the same concept as user-scoped memory: each user gets an isolated copy of the memory files.\n\nWithout user-scoped memory, you can accidentally create **shared memory**.\n\nSuppose your application has:\n\n``` php\nUser A -> Agent -> preferences.md\nUser B -> Agent -> preferences.md\n```\n\nIf both users access the same memory namespace, the agent could potentially read information written by the other user.\n\nThat can cause several problems.\n\nAlice says:\n\n\"Always give me Python examples.\"\n\nBob could later receive Python examples even though he prefers TypeScript.\n\nA memory file might contain information about a user's previous interactions, preferences, or other personal context.\n\nIf that memory is shared, one user's information could become available to another user.\n\nImagine 100 users are constantly teaching the same agent different preferences:\n\n```\nUser A: Be concise.\nUser B: Give detailed explanations.\nUser C: Use Python.\nUser D: Use TypeScript.\n```\n\nIf all of this goes into one shared memory, the agent has no reliable way to know **whose preference it should follow**.\n\nThere is an important distinction.\n\n**Short-term memory** is generally associated with a conversation/thread. It helps the agent remember what is happening in the current conversation.\n\n**Long-term memory** survives across conversations.\n\nUser-scoped long-term memory adds another layer:\n\n```\nConversation 1 ─┐\nConversation 2 ─┼──> Alice's long-term memory\nConversation 3 ─┘\n```\n\nSo Alice can start a completely new conversation and the agent can still know her saved preferences.\n\nDeep Agents uses memory files for long-term memory and a backend/store to control where those files are stored.\n\nThere are three important pieces in the example.\n\n```\nMEMORY_PATH = \"/memories/preferences.md\"\n```\n\nThis tells the agent which memory file it should use.\n\nOur application provides:\n\n```\n@dataclass(frozen=True)\nclass UserContext:\n    user_id: str\n```\n\nFor example:\n\n```\nuser-alice\nuser-bob\n```\n\nThe important part is:\n\n```\nnamespace=lambda current_runtime: (\n    current_runtime.context.user_id,\n)\n```\n\nThis means:\n\n``` php\nuser-alice -> namespace (\"user-alice\",)\nuser-bob   -> namespace (\"user-bob\",)\n```\n\nThe same `/preferences.md`\n\npath can therefore exist independently for both users.\n\nConceptually:\n\n```\nStore\n│\n├── (\"user-alice\",)\n│   └── /preferences.md\n│\n└── (\"user-bob\",)\n    └── /preferences.md\n```\n\nThe file has the same name, but it belongs to a different namespace.\n\nLangChain recommends user scope when memory should belong to individual users, and specifically notes that user A's preferences should not leak into user B's conversations.\n\nA good default is:\n\nIf memory does not need to be shared, make it user-scoped.\n\nShared memory should be used deliberately. LangChain's documentation also warns that allowing one user to write memory that another user can read can create security problems, including malicious instructions being inserted into shared state.\n\nFor shared organizational policies, read-only memory is often more appropriate.\n\nThe following example creates two users:\n\nBoth use the **same agent**, but their memories are isolated.\n\nInstall the required packages and set your `NVIDIA_API_KEY`\n\n, then save the code as `user_scoped_memory.py`\n\nand run:\n\n```\nuv run user_scoped_memory.py\n```\n\nThe example uses NVIDIA's model through `ChatNVIDIA`\n\n, while the memory isolation itself is handled by Deep Agents and `InMemoryStore`\n\n.\n\n```\n\"\"\"Runnable user-scoped long-term memory example.\n\nRun with ``uv run user_scoped_memory.py`` after setting `` NVIDIA_API_KEY`` in\nthe environment or in a ``.env`` file.\n\nEach invocation supplies a user ID through the graph context. The backend uses\nthat ID as the store namespace, so users can share one agent without sharing\ntheir preference files.\n\"\"\"\n\nimport os\nfrom dataclasses import dataclass\n\nfrom dotenv import load_dotenv\nfrom langchain_nvidia_ai_endpoints import ChatNVIDIA\nfrom langgraph.store.memory import InMemoryStore\nfrom requests.exceptions import Timeout\n\nfrom deepagents import create_deep_agent\nfrom deepagents.backends import CompositeBackend, StateBackend, StoreBackend\nfrom deepagents.backends.utils import create_file_data\n\nMEMORY_PATH = \"/memories/preferences.md\"\nSTORE_MEMORY_PATH = \"/preferences.md\"\n\n@dataclass(frozen=True)\nclass UserContext:\n    user_id: str\n\ndef build_model():\n    \"\"\"Create the chat model used by the demo.\"\"\"\n    if not os.getenv(\"NVIDIA_API_KEY\"):\n        raise RuntimeError(\n            \"Set NVIDIA_API_KEY in the environment or a .env file before \"\n            \"running this example.\"\n        )\n\n    model_name = os.getenv(\n        \"NVIDIA_MODEL\",\n        \"nvidia:nvidia/nemotron-3-ultra-550b-a55b\",\n    ).removeprefix(\"nvidia:\")\n\n    timeout_seconds = int(\n        os.getenv(\"NVIDIA_TIMEOUT_SECONDS\", \"180\")\n    )\n\n    max_completion_tokens = int(\n        os.getenv(\"NVIDIA_MAX_COMPLETION_TOKENS\", \"1024\")\n    )\n\n    return ChatNVIDIA(\n        model=model_name,\n        timeout=timeout_seconds,\n        max_completion_tokens=max_completion_tokens,\n        model_kwargs={\"parallel_tool_calls\": False},\n    )\n\ndef seed_memory(store: InMemoryStore) -> None:\n    \"\"\"Create an isolated preference file for each demo user.\"\"\"\n    preferences = {\n        \"user-alice\": \"\"\"## Preferences\n- Likes concise bullet points.\n- Prefers Python examples.\n\"\"\",\n        \"user-bob\": \"\"\"## Preferences\n- Likes detailed explanations.\n- Prefers TypeScript examples.\n\"\"\",\n    }\n\n    for user_id, content in preferences.items():\n        store.put(\n            (user_id,),\n            STORE_MEMORY_PATH,\n            create_file_data(content),\n        )\n\ndef build_backend(store: InMemoryStore) -> CompositeBackend:\n    \"\"\"Route memory to the namespace belonging to the current user.\"\"\"\n    return CompositeBackend(\n        default=StateBackend(),\n        routes={\n            \"/memories/\": StoreBackend(\n                store=store,\n                namespace=lambda current_runtime: (\n                    current_runtime.context.user_id,\n                ),\n            ),\n        },\n    )\n\ndef build_agent(store: InMemoryStore):\n    return create_deep_agent(\n        model=build_model(),\n        memory=[MEMORY_PATH],\n        backend=build_backend(store),\n        context_schema=UserContext,\n        store=store,\n        name=\"user-scoped-memory-demo\",\n    )\n\ndef invoke_for_user(\n    agent,\n    user_id: str,\n    thread_id: str,\n    prompt: str,\n) -> str:\n    result = agent.invoke(\n        {\"messages\": [{\"role\": \"user\", \"content\": prompt}]},\n        config={\"configurable\": {\"thread_id\": thread_id}},\n        context=UserContext(user_id=user_id),\n    )\n\n    return result[\"messages\"][-1].content\n\ndef main() -> None:\n    load_dotenv()\n\n    store = InMemoryStore()\n    seed_memory(store)\n    agent = build_agent(store)\n\n    prompt = (\n        \"How do I read a CSV file? Use only the preferences stored in \"\n        f\"{MEMORY_PATH}. State which language and response style you used.\"\n    )\n\n    print(\"Alice's isolated memory\")\n\n    try:\n        print(\n            invoke_for_user(\n                agent,\n                user_id=\"user-alice\",\n                thread_id=\"alice-memory-thread\",\n                prompt=prompt,\n            )\n        )\n    except Timeout:\n        print(\n            \"The NVIDIA model timed out. The memory store is configured \"\n            \"correctly; try NVIDIA_MODEL=meta/llama-3.2-3b-instruct or \"\n            \"increase NVIDIA_TIMEOUT_SECONDS.\"\n        )\n\n    print(\"\\nBob's isolated memory\")\n\n    try:\n        print(\n            invoke_for_user(\n                agent,\n                user_id=\"user-bob\",\n                thread_id=\"bob-memory-thread\",\n                prompt=prompt,\n            )\n        )\n    except Timeout:\n        print(\n            \"The NVIDIA model timed out. The memory store is configured \"\n            \"correctly; try NVIDIA_MODEL=meta/llama-3.2-3b-instruct or \"\n            \"increase NVIDIA_TIMEOUT_SECONDS.\"\n        )\n\nif __name__ == \"__main__\":\n    main()\n```\n\nYou do not need a separate agent for every user.\n\nYou can have:\n\n``` php\nOne Agent\n   |\n   +-- User ID A -> Memory A\n   |\n   +-- User ID B -> Memory B\n   |\n   +-- User ID C -> Memory C\n```\n\nThe **user ID becomes the memory boundary**.\n\nThat is the core idea behind user-scoped memory in Deep Agents.\n\nFor production applications, `InMemoryStore`\n\nis only suitable for a simple demonstration. Deep Agents' documentation notes that a persistent/platform store should be used when deploying rather than relying on an in-memory store.\n\nFor the complete and current implementation details, see [LangChain Deep Agents Memory documentation](https://docs.langchain.com/oss/python/deepagents/memory?utm_source=chatgpt.com).", "url": "https://wpnews.pro/news/user-scoped-memory-in-deep-agents-what-it-is-and-why-you-need-it", "canonical_source": "https://dev.to/syeedmdtalha/user-scoped-memory-in-deep-agents-what-it-is-and-why-you-need-it-4n3p", "published_at": "2026-08-18 10:50:59+00:00", "updated_at": "2026-08-18 11:14:10.508237+00:00", "lang": "en", "topics": ["ai-agents", "large-language-models", "ai-infrastructure"], "entities": ["Deep Agents", "LangChain"], "alternates": {"html": "https://wpnews.pro/news/user-scoped-memory-in-deep-agents-what-it-is-and-why-you-need-it", "markdown": "https://wpnews.pro/news/user-scoped-memory-in-deep-agents-what-it-is-and-why-you-need-it.md", "text": "https://wpnews.pro/news/user-scoped-memory-in-deep-agents-what-it-is-and-why-you-need-it.txt", "jsonld": "https://wpnews.pro/news/user-scoped-memory-in-deep-agents-what-it-is-and-why-you-need-it.jsonld"}}