Memory that outlives the conversation
A store is a saver for facts that outlive any one conversation, filed as JSON documents under a namespace and a key.
Last updated: 27 Sep, 2026 · LangChain 1.4
Ravi told the shop once to leave parcels at the back door. That belongs to Ravi, not to any one conversation, so a new thread next week should still know it.
Pick one to watch it run, step by step.
The InMemoryStore API
from langgraph.store.memory import InMemoryStore
store = InMemoryStore()
store.put(("customers", "ravi"), "delivery", {"note": "..."}) # namespace, key, value
item = store.get(("customers", "ravi"), "delivery") # an item, or NonePutting and getting
Save a note under a folder for Ravi, then read it back. Asking a folder that has nothing under that key returns None.
from langgraph.store.memory import InMemoryStore
store = InMemoryStore()
store.put(("customers", "ravi"), "delivery", {"note": "leave it at the back door"})
item = store.get(("customers", "ravi"), "delivery") # read Ravi's note
print(item.key, item.value)
print(store.get(("customers", "mei"), "delivery")) # nothing there -> Nonefrom langgraph.store.memory import InMemoryStore
store = InMemoryStore()
store.put(("customers", "ravi"), "delivery", {"note": "leave it at the back door"})
item = store.get(("customers", "ravi"), "delivery")
print(item.key, item.value)
print(store.get(("customers", "mei"), "delivery"))The customer and the orders
A tool can reach the store through its runtime. Start with the customer type and the order data.
from dataclasses import dataclass
@dataclass
class Customer: # who is asking, passed as context
name: str
ORDERS = {"A17": "shipped on 3 March", "C40": "waiting for stock"}A tool that uses the store
The tool reads Ravi's delivery note into its answer and writes down which order he asked about last. runtime.store is the store the agent was given.
from langchain.tools import ToolRuntime, tool
@tool
def lookup_order(order_id: str, runtime: ToolRuntime[Customer]) -> str:
"""Look up an order's shipping status by its id, such as A17."""
folder = ("customers", runtime.context.name) # this customer
runtime.store.put(folder, "last_order", {"order_id": order_id}) # write
note = runtime.store.get(folder, "delivery") # read note
status = f"{order_id} {ORDERS.get(order_id, 'is not an order we have')}."
return f"{status} Delivery note: {note.value['note']}." if note else statusThe agent with a store
Give the agent the store, the context type, and a checkpointer. Seed the store with Ravi's note first.
from langchain.agents import create_agent
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.store.memory import InMemoryStore
from shop_model import ShopModel
from tools import Customer, lookup_order
store = InMemoryStore()
store.put(("customers", "ravi"), "delivery", {"note": "leave it at the back door"})
agent = create_agent(ShopModel(), tools=[lookup_order], context_schema=Customer,
checkpointer=InMemorySaver(), store=store)Looking up A17 with a saved note
Ask as Ravi. The delivery note comes out of the store, not the conversation.
ravi = Customer("ravi")
result = agent.invoke({"messages": [{"role": "user", "content": "Where is A17?"}]},
{"configurable": {"thread_id": "monday"}}, context=ravi)
print(result["messages"][-1].text)A new thread, the same customer
A different thread_id starts a fresh conversation, but the store is shared. Ravi's note is still there, and the last order is now C40.
result = agent.invoke({"messages": [{"role": "user", "content": "And C40?"}]},
{"configurable": {"thread_id": "friday"}}, context=ravi)
print(len(result["messages"]))
print(store.get(("customers", "ravi"), "last_order").value)What the store kept across threads
- The namespace is a tuple that works like a folder path, here one folder per customer; the key names a document, and the value is a dictionary.
- A missing document returns
None, so Mei's folder givesNonefor a note only Ravi set. - The delivery note in Ravi's answer came out of the store, and the tool wrote A17 down as the order he asked about last.
- The Friday thread starts with four messages, none from Monday: the checkpointer keeps threads apart. The store does not, so the note stays and the last order is now C40.
Thread vs store
| Thread (checkpointer) | Store | |
|---|---|---|
| Holds | One conversation | Facts across all conversations |
| Keyed by | thread_id | namespace + key |
| A new thread | Starts empty | Still has the facts |
| Read with | get_state | store.get |
Where long-term memory fits
- Remembering a customer's preferences from one visit to the next.
- Any fact that belongs to a user or account rather than a single chat.
store= and a tool that calls runtime.store raises at run time. The store has to be handed to the agent before a tool can read or write it.Related
- Previous: Trimming and removing messages
- Next: Semantic search in long-term memory
- Reference: Long-term memory
- Invoke as Mei and check that the answer has no delivery note.
- Print
[i.key for i in store.search(("customers", "ravi"))]. - Create the agent without
store=and read the error the tool raises.
Little by little, you're building something great.