LangMemLangMem 0.0.30 · LangGraph 1.2 · Python 3.10+
Dashboard
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
19 small wins to finish your path

Project: support assistant

The support assistant project is the course's finished build: an agent that answers from memory during the chat, a background store manager that writes memories after it, and a store that carries them to the next conversation.

Last updated: 30 Sep, 2026 · LangMem 0.0.30

The overview promised an assistant that remembers Asha's broken order and her contact preference in a conversation days later, and that cannot recall anything for a customer it has never met. This lesson builds it from pieces the course taught: the store with semantic search, a store manager, the search tool, an agent with a checkpointer, and the reflection executor.

Answer now, remember afterwards
searchthe threadmemoriesa messageuser_id, thread_idagentsearch_memory onlyInMemoryStoreGemini embeddingsReflectionExecutorwaits, then runs oncestore managerextracts and saves
Hover or tap a piece to see what it is and which lesson built it.
Follow a chat

Pick one to watch it run, step by step.

How the assistant is split

  • The agent only searches. It gets search_memory, not manage_memory, so answering never waits on saving.
  • Saving happens in the background. After each reply the whole thread goes to the ReflectionExecutor; the store manager extracts from it once the thread goes quiet.
  • One store for both sides, with Gemini embeddings so the agent's search finds memories by meaning.

Syntax:

python
agent, reflector, store = build()
reply, saved = chat(agent, reflector, user_id, thread_id, text, delay=60)

The agent and the manager in build()

Both use the same model and store. The prompt is grounded: answer only from what the customer said and what the search returned.

python
agent = create_agent(model, tools=[create_search_memory_tool(namespace=NAMESPACE)],
                     store=store, checkpointer=InMemorySaver(), system_prompt=PROMPT)
manager = create_memory_store_manager(model, namespace=NAMESPACE, instructions=INSTRUCTIONS, store=store)
return agent, ReflectionExecutor(manager, store=store), store

One turn in chat()

Answer first, then submit the whole thread for reflection and hand back the future. A real app would pass a delay of minutes and never wait on the future; the demo waits so it can print the store.

python
result = agent.invoke({"messages": [{"role": "user", "content": text}]}, config=config)
saved = reflector.submit({"messages": result["messages"]}, config=config, after_seconds=delay)
return result["messages"][-1].content, saved

The assistant end to end

Save the first file as assistant.py and the second as main.py in the same folder, then run python main.py.

python
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.store.memory import InMemoryStore
from langmem import ReflectionExecutor, create_memory_store_manager, create_search_memory_tool

NAMESPACE = ("memories", "{user_id}")
INSTRUCTIONS = "Extract what helps support this customer. Record everything in a single Memory call."
PROMPT = (
    "You are a support assistant for an online shop. Before answering, search memory for what you know "
    "about this customer. Answer in one or two short sentences, using only what the customer said and "
    "what the search returned. If you do not know something, say so."
)


def build():
    model = init_chat_model("groq:openai/gpt-oss-120b", temperature=0)
    store = InMemoryStore(index={"dims": 3072, "embed": "google_genai:gemini-embedding-2"})
    agent = create_agent(
        model,
        tools=[create_search_memory_tool(namespace=NAMESPACE)],
        store=store,
        checkpointer=InMemorySaver(),
        system_prompt=PROMPT,
    )
    manager = create_memory_store_manager(model, namespace=NAMESPACE, instructions=INSTRUCTIONS, store=store)
    return agent, ReflectionExecutor(manager, store=store), store


def chat(agent, reflector, user_id, thread_id, text, delay=0):
    config = {"configurable": {"user_id": user_id, "thread_id": thread_id}}
    result = agent.invoke({"messages": [{"role": "user", "content": text}]}, config=config)
    saved = reflector.submit({"messages": result["messages"]}, config=config, after_seconds=delay)
    return result["messages"][-1].content, saved
ExampleAPI keymain.py
from assistant import build, chat

agent, reflector, store = build()
with reflector:
    for text in ["Hi, I'm Asha. Order A-1001 arrived broken.", "Please email me about it, I work nights."]:
        reply, saved = chat(agent, reflector, "asha", "asha-monday", text, delay=1)
        print("asha (monday):", text)
        print("  assistant:", reply)
    saved.result()
    for item in store.search(("memories", "asha")):
        print("  remembered:", item.value["content"]["content"])

    for user in ["asha", "ravi"]:
        question = "Which order did I write about, and how should you contact me?"
        reply, saved = chat(agent, reflector, user, f"{user}-friday", question)
        print(f"{user} (friday):", question)
        print("  assistant:", reply)

Monday's chat and Friday's questions

  • Monday, first message: nothing was stored for Asha yet, and the agent asked for details instead of inventing any. The grounded prompt is doing its job.
  • Monday, second message: it asked for an email address, which it did not have. Each chat submitted the whole thread to the executor with a one-second delay; a newer submit replaces an older one only while it is still waiting, and here the store ended with one memory for Asha.
  • remembered: one memory with the broken order, the email preference and the night shifts, plus "Email address has not yet been provided", which the model added from the conversation.
  • Friday, Asha, in a new thread: nothing from Monday was in the agent's messages. It searched the store and answered with A-1001 and email.
  • Friday, Ravi: the same question found nothing in Ravi's namespace, and the agent said it had no information. That is the failure the overview promised to show: memory exists only for customers the assistant has met.

What the course left out

TopicWhat it is forWhere in the docs
create_memory_searcherA runnable that writes search queries from a conversation, then searches the storeMemory API reference
SummarizationNodeThe running summary as a node in a LangGraph graphSummarization guide
create_multi_prompt_optimizerOptimizing several agents' prompts from shared feedbackOptimize multiple prompts
Memory tools in other agentsUsing LangMem's tools from CrewAI or a hand-written Anthropic or OpenAI loopCrewAI guide
Async APIsainvoke, asummarize_messages and AsyncPostgresStore for async serversAPI reference

Where to take the assistant next

  • Add the profile from Profiles in its own namespace, so the contact channel is a field your code can read.
  • Record an episode per resolved ticket, as in Episodic memory, and put the best match in the prompt.
  • Move to a persistent store, as in Integrations.
Watch out. The background extraction only helps once it has run. A question asked in a new thread before the executor's delay ends finds nothing in the store. Choose the delay for how soon a customer comes back, and never wait on the future in a real request.
Try it yourself
  • Ask Asha's Friday question without saved.result() on Monday and see what the agent finds.
  • Add a third Monday message, "Text me instead", and check which contact method Friday's answer gives.
  • Give the agent create_manage_memory_tool as well and ask it on Friday to forget the order.
PreviousIntegrations

Slow is fine. Stopping is the only problem.