An Agent without memory starts every conversation from scratch. passInMemorySaver + thread_id, allowing the Agent to remember the conversation history of each user.
By default,create_agentCreated Agentno memory. every timeagent.invoke()They are all completely independent: the Agent does not know what was said before, nor does it know the user's name.
agent = create_agent(deepseek_model())
# 第一次对话:告诉 Agent 我的名字和爱好
response1 = agent.invoke({
"messages": [HumanMessage(content="Hello my name is Seán and my favourite colour is green")]
})
print(response1['messages'][-1].content)
# 第二次对话(独立的 invoke):问 Agent 我的爱好
response2 = agent.invoke({
"messages": [HumanMessage(content="What's my favourite colour?")]
})
print(response2['messages'][-1].content)
The second time it was called, the agent had no memory of the first conversation—it didn’t know the user’s name was Seán or that he loved the color green. every timeinvokeIt's all a new, context-free conversation.
LangGraph’s memory mechanism is calledcheckpointer. How it works is simple:
agent.invoke()After execution, checkpointer will complete theState (including all message history)Save to storagethread_idWhen calling, checkpointer firstLoad from storagePrevious Statecheckpointer does not let LLM itself remember anything - LLM is stateless on every call. What checkpointer does is: convert historical messagesas contextPass it to LLM together so that LLM can "see" what was said before. Essentially, the historical message is put into the context window of this call.
| checkpointer type | storage location | Applicable scenarios |
|---|---|---|
InMemorySaver |
Python process memory | Development/testing, disappears after process restart |
SqliteSaver |
SQLite file | Local persistence, stand-alone production environment |
PostgresSaver |
PostgreSQL database | Multi-instance, high-concurrency production environment |
| LangGraph Cloud | Hosted cloud storage | Fully managed cloud deployment |
existcreate_agentIncomingcheckpointer=InMemorySaver(), you can enable the memory function for Agent:
from langgraph.checkpoint.memory import InMemorySaver
from langchain.agents import create_agent
agent = create_agent(
deepseek_model(),
checkpointer=InMemorySaver(), # 开启内存记忆
)
It’s not enough to have a checkpointer—it also needs to passconfigParameters passed inthread_id, telling the Agent which "thread" this conversation belongs to (that is, which user).
thread_idIs the unique identifier of the conversation. same onethread_idmany timesinvokeShared message history; differentthread_idThe conversations are completely isolated and do not interfere with each other.
# config 字典:指定这次 invoke 属于哪个线程
config = {"configurable": {"thread_id": "1"}}
# 第一次调用:告知名字和爱好
response1 = agent.invoke(
{"messages": [HumanMessage(content="Hello my name is Seán and my favourite colour is green")]},
config # 传入 config
)
# 第二次调用:同一个 config(同一个 thread_id)
response2 = agent.invoke(
{"messages": [HumanMessage(content="What's my favourite colour?")]},
config # 同一个 config = 同一个对话历史
)
print(response2['messages'][-1].content)
In practical applications,thread_idusuallyUser ID or session ID,For example"user_12345"or"session_abc123". In this way, different users have independent conversation histories without interfering with each other. Multiple conversations with the same user (same session) use the samethread_id, you can continue to remember the context.
Here is the complete two-turn conversation code, showing how memory works in an actual multi-turn conversation:
from langgraph.checkpoint.memory import InMemorySaver
from langchain.agents import create_agent
from pprint import pprint
# 创建有记忆的 Agent
agent = create_agent(
deepseek_model(),
checkpointer=InMemorySaver()
)
config = {"configurable": {"thread_id": "1"}}
# 轮次 1:建立上下文
response1 = agent.invoke(
{"messages": [{"role": "user", "content": "Hello my name is Seán and my favourite colour is green"}]},
config
)
# 轮次 2:利用上下文
response2 = agent.invoke(
{"messages": [{"role": "user", "content": "What's my favourite colour?"}]},
config
)
# 查看第二次 invoke 的完整消息历史
for msg in response2["messages"]:
pprint(msg.content)
print()
Note that the message history includesAll messages from two invokes(4 items in total). On the second call, the Agent saw the color green mentioned by Seán in the first conversation, so it answered correctly.
When using an Agent with memory, each timeinvokeJust need to sendNew news for the current round, no need to manually splice historical messages. checkpointer will automatically load the history. If you manually transfer historical messages, the messages will appear twice, causing abnormal behavior.
| Strategy | principle | Applicable scenarios |
|---|---|---|
| InMemorySaver | Full message history stored in memory | Development testing, Demo |
| SqliteSaver / PostgresSaver | Full message history database | Persistence requirements, small and medium-sized applications |
| Message summary | Regularly compress historical messages into summaries to save context space | Long-term dialogue, context-limited models |
| sliding window | Only keep the latest N messages | Scenarios that require only short-term context |
| vector database memory | Embedding historical conversations into a vector library and retrieving related history | Ultra-long-term dialogue, intelligent memory retrieval |
checkpointer=InMemorySaver()config = {"configurable": {"thread_id": "1"}}