添加内存¶
聊天机器人现在可以使用工具来回答用户的问题,但它不记得之前交互的上下文。这限制了它进行连贯的多轮对话的能力。
LangGraph通过**持久化检查点(persistent checkpointing)**解决了这个问题。如果你在编译图时提供一个checkpointer
,并在调用图时提供一个thread_id
,LangGraph会在每一步自动保存状态。当你使用相同的thread_id
再次调用图时,图会加载其保存的状态,从而使聊天机器人能够继续之前的对话。
我们稍后会看到,**检查点(checkpointing)**比简单的聊天记忆要强大得多——它允许你在任何时间保存和恢复复杂的状态,用于错误恢复、人机协作的工作流、时间旅行式交互等。但首先,让我们添加检查点功能以支持多轮对话。
Note
本教程基于添加工具。
1. 创建 MemorySaver
检查点器¶
创建一个 MemorySaver
检查点器:
这是一个内存中的检查点器,适合教程使用。然而,在生产应用程序中,你可能会将其更改为使用 SqliteSaver
或 PostgresSaver
并连接数据库。
2. 编译图¶
使用提供的检查点器编译图,这将在图通过每个节点时对 State
进行检查点记录:
from IPython.display import Image, display
try:
display(Image(graph.get_graph().draw_mermaid_png()))
except Exception:
# 这需要一些额外的依赖项,并且是可选的
pass
3. 与你的聊天机器人进行交互¶
现在你可以与你的机器人进行交互了!
-
选择一个线程作为此次对话的键。
-
调用你的聊天机器人:
user_input = "Hi there! My name is Will." # config 是调用 stream() 或 invoke() 时的 **第二个位置参数**! events = graph.stream( {"messages": [{"role": "user", "content": user_input}]}, config, stream_mode="values", ) for event in events: event["messages"][-1].pretty_print()
================================ Human Message ================================= Hi there! My name is Will. ================================== Ai Message ================================== Hello Will! It's nice to meet you. How can I assist you today? Is there anything specific you'd like to know or discuss?
Note
在调用我们的图时,config 作为 第二个位置参数 提供。重要的是,它并不嵌套在图输入(
{'messages': []}
)中。
4. 提出后续问题¶
提出一个后续问题:
user_input = "Remember my name?"
# config 是 stream() 或 invoke() 的 **第二个位置参数**!
events = graph.stream(
{"messages": [{"role": "user", "content": user_input}]},
config,
stream_mode="values",
)
for event in events:
event["messages"][-1].pretty_print()
================================ Human Message =================================
Remember my name?
================================== Ai Message ==================================
当然,我记得你的名字,Will。我总是尽量注意用户与我分享的重要细节。还有其他你想聊的内容或问题吗?我很乐意帮助你处理各种主题或任务。
注意 我们没有使用外部列表来存储记忆:这一切都由 checkpointer 处理!你可以在这个 LangSmith trace 中查看完整的执行过程。
不相信我?尝试使用不同的配置进行操作。
# 唯一的不同是我们在这里将 `thread_id` 改为 "2" 而不是 "1"
events = graph.stream(
{"messages": [{"role": "user", "content": user_input}]},
{"configurable": {"thread_id": "2"}},
stream_mode="values",
)
for event in events:
event["messages"][-1].pretty_print()
================================ Human Message =================================
Remember my name?
================================== Ai Message ==================================
抱歉,我没有关于你名字的先前上下文或记忆。作为一个 AI 助手,我不保留过去对话的信息。每次互动都是从头开始。你能告诉我你的名字吗,这样我就可以在本次对话中正确地称呼你?
注意 我们所做的**唯一**更改是修改了配置中的 thread_id
。你可以通过这个调用的 LangSmith trace 进行比较。
5. 检查状态¶
到目前为止,我们已经在两个不同的线程中创建了一些检查点。但检查点中包含哪些内容呢?要随时检查某个配置下图的 state
,可以调用 get_state(config)
。
StateSnapshot(values={'messages': [HumanMessage(content='Hi there! My name is Will.', additional_kwargs={}, response_metadata={}, id='8c1ca919-c553-4ebf-95d4-b59a2d61e078'), AIMessage(content="Hello Will! It's nice to meet you. How can I assist you today? Is there anything specific you'd like to know or discuss?", additional_kwargs={}, response_metadata={'id': 'msg_01WTQebPhNwmMrmmWojJ9KXJ', 'model': 'claude-3-5-sonnet-20240620', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 405, 'output_tokens': 32}}, id='run-58587b77-8c82-41e6-8a90-d62c444a261d-0', usage_metadata={'input_tokens': 405, 'output_tokens': 32, 'total_tokens': 437}), HumanMessage(content='Remember my name?', additional_kwargs={}, response_metadata={}, id='daba7df6-ad75-4d6b-8057-745881cea1ca'), AIMessage(content="Of course, I remember your name, Will. I always try to pay attention to important details that users share with me. Is there anything else you'd like to talk about or any questions you have? I'm here to help with a wide range of topics or tasks.", additional_kwargs={}, response_metadata={'id': 'msg_01E41KitY74HpENRgXx94vag', 'model': 'claude-3-5-sonnet-20240620', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 444, 'output_tokens': 58}}, id='run-ffeaae5c-4d2d-4ddb-bd59-5d5cbf2a5af8-0', usage_metadata={'input_tokens': 444, 'output_tokens': 58, 'total_tokens': 502})]}, next=(), config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1ef7d06e-93e0-6acc-8004-f2ac846575d2'}}, metadata={'source': 'loop', 'writes': {'chatbot': {'messages': [AIMessage(content="Of course, I remember your name, Will. I always try to pay attention to important details that users share with me. Is there anything else you'd like to talk about or any questions you have? I'm here to help with a wide range of topics or tasks.", additional_kwargs={}, response_metadata={'id': 'msg_01E41KitY74HpENRgXx94vag', 'model': 'claude-3-5-sonnet-20240620', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 444, 'output_tokens': 58}}, id='run-ffeaae5c-4d2d-4ddb-bd59-5d5cbf2a5af8-0', usage_metadata={'input_tokens': 444, 'output_tokens': 58, 'total_tokens': 502})]}}, 'step': 4, 'parents': {}}, created_at='2024-09-27T19:30:10.820758+00:00', parent_config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1ef7d06e-859f-6206-8003-e1bd3c264b8f'}}, tasks=())
上面的快照包含了当前的状态值、对应的配置以及下一个需要处理的节点。在我们的例子中,图已经达到了一个 END
状态,因此 next
是空的。
恭喜! 由于 LangGraph 的检查点系统,你的聊天机器人现在可以在会话之间保持对话状态。这为更自然、上下文相关的交互打开了令人兴奋的可能性。LangGraph 的检查点系统甚至可以处理 任意复杂的图状态,这比简单的聊天记忆更加表达性强且功能强大。
查看下面的代码片段,以回顾本教程中的图:
{}¶
import os
from langchain.chat_models import init_chat_model
os.environ["AZURE_OPENAI_API_KEY"] = "..."
os.environ["AZURE_OPENAI_ENDPOINT"] = "..."
os.environ["OPENAI_API_VERSION"] = "2025-03-01-preview"
llm = init_chat_model(
"azure_openai:gpt-4.1",
azure_deployment=os.environ["AZURE_OPENAI_DEPLOYMENT_NAME"],
)
from typing import Annotated
from langchain.chat_models import init_chat_model
from langchain_tavily import TavilySearch
from langchain_core.messages import BaseMessage
from typing_extensions import TypedDict
from langgraph.checkpoint.memory import MemorySaver
from langgraph.graph import StateGraph
from langgraph.graph.message import add_messages
from langgraph.prebuilt import ToolNode, tools_condition
class State(TypedDict):
messages: Annotated[list, add_messages]
graph_builder = StateGraph(State)
tool = TavilySearch(max_results=2)
tools = [tool]
llm_with_tools = llm.bind_tools(tools)
def chatbot(state: State):
return {"messages": [llm_with_tools.invoke(state["messages"])]}
graph_builder.add_node("chatbot", chatbot)
tool_node = ToolNode(tools=[tool])
graph_builder.add_node("tools", tool_node)
graph_builder.add_conditional_edges(
"chatbot",
tools_condition,
)
graph_builder.add_edge("tools", "chatbot")
graph_builder.set_entry_point("chatbot")
memory = MemorySaver()
graph = graph_builder.compile(checkpointer=memory)
下一步¶
在下一个教程中,你将向聊天机器人添加人工干预环节,以处理需要指导或验证的情况。