LangChain Mastery

Course Content

LangChain Mastery

7 sections · 109 lessons

How do you implement memory for multi-user conversations in LangChain?


Every key starts from the authenticated useruser u-881thread u-881:c-17store users, u-881messagestool resultsprofilefacts
If the user ID comes from the session and not the request body, no thread ID or namespace can point at someone else's data.

What you need to know

"Multi-user" means two problems

  • Isolation — user A must never see user B's history. This is a security requirement.
  • Scale — thousands of users across many worker processes must each get their own history, fast.

Both are solved by the same design: identifiers from auth, state in a shared store.

Keys and namespaces

MemoryKeyExample
One conversationthread_id"u-881:c-17"
A user's long-term factsstore namespace("users", "u-881")
Shared team knowledgestore namespace("org", "acme-legal", "notes")

A user can have many conversations; a thread belongs to exactly one user. Keep a table of (user_id, thread_id) so you can list and delete a user's threads.

With create_agent

Python
from dataclasses import dataclassfrom langchain.agents import create_agent@dataclassclass Ctx:    user_id: stragent = create_agent(model, tools=TOOLS, checkpointer=saver, store=store,                     context_schema=Ctx)def handle(request, conversation_id: str, text: str):    user_id = request.auth.user_id                       # from the session, not the body    thread_id = f"{user_id}:{conversation_id}"    if not db.thread_belongs_to(thread_id, user_id):      # ownership check        raise PermissionError("Not your conversation")    return agent.invoke(        {"messages": [{"role": "user", "content": text}]},        config={"configurable": {"thread_id": thread_id}},        context=Ctx(user_id=user_id),    )

Tools read runtime.context.user_id to choose the store namespace, so a tool physically cannot be pointed at another user's data by the model.

Legacy: RunnableWithMessageHistory

Older code solved the same problem with history_factory_config, which replaced the single session_id with several config fields, such as user_id and conversation_id, passed to the history factory as arguments. The wrapper is deprecated since langchain-core 1.3.3; the design rule — build the key from authenticated identifiers — carries over unchanged to thread_id.

Classic bugs

  • A module-level store = {} dict in a multi-worker server: each worker has its own copy, and memory seems to come and go.
  • A cached chain or memory object reused across requests, so one user's state leaks into the next request.
  • Taking user_id from the JSON body: anyone can read anyone's thread by changing a number.
  • Vector memory search without a user filter.

A real-life example

A law firm's contract assistant is used by 400 lawyers across 3 teams. Each lawyer has private research threads, and each team shares a notes namespace, ("team", team_id, "notes"), that any team member's agent can search.

During a security review, a tester changed the conversation_id in the API call to one from another lawyer. Because the thread_id was built from the authenticated user ID plus the conversation ID, and the ownership check ran before invoke, the request was rejected. The review found one real issue: a team-notes search tool used a team_id argument supplied by the model. The team moved team_id into runtime context from the user's directory record, and added an automated test where user A tries to reach user B's threads, store items and team notes in 20 different ways.

Follow-up questions to expect

  • "Where should the user ID come from?" — The authenticated session or token on the server, then passed in config and runtime context — never from the model or the request body.
  • "How do you share memory between users safely?" — A separate namespace for the shared data with explicit membership checks, read-only by default.
  • "How do you scale it?" — Stateless workers, a shared Postgres or Redis backend with a connection pool, and indexes on thread and namespace keys.