管理内存

许多 AI 应用程序需要内存才能在多次交互中共享上下文。LangGraph 支持构建对话代理至关重要的两种内存类型:

  • 短期内存:通过在会话中维护消息历史来跟踪正在进行的对话。
  • 长期内存:在会话之间存储用户特定或应用程序级别的数据。

启用短期内存后,长对话可能会超出 LLM 的上下文窗口。常见的解决方案有:

这使得代理能够跟踪对话,而不会超出 LLM 的上下文窗口。

添加短期内存

短期内存使代理能够跟踪多轮对话。

API 参考:InMemorySaver | StateGraph

<span id="__span-0-1">from langgraph.checkpoint.memory import InMemorySaver
<span id="__span-0-2">from langgraph.graph import StateGraph
<span id="__span-0-3">
<span id="__span-0-4">checkpointer = InMemorySaver()
<span id="__span-0-5">
<span id="__span-0-6">builder = StateGraph(...)
<span id="__span-0-7">graph = builder.compile(checkpointer=checkpointer)
<span id="__span-0-8">
<span id="__span-0-9">graph.invoke(
<span id="__span-0-10">    {"messages": [{"role": "user", "content": "hi! i am Bob"}]},
<span id="__span-0-11">    {"configurable": {"thread_id": "1"}},
<span id="__span-0-12">)

请参阅持久化指南,了解有关使用短期内存的更多信息。

添加长期内存

使用长期内存可以在对话之间存储用户特定或应用程序特定的数据。这对于聊天机器人等应用程序非常有用,您可能希望记住用户偏好或其他信息。

API 参考:StateGraph

<span id="__span-1-1">from langgraph.store.memory import InMemoryStore
<span id="__span-1-2">from langgraph.graph import StateGraph
<span id="__span-1-3">
<span id="__span-1-4">store = InMemoryStore()
<span id="__span-1-5">
<span id="__span-1-6">builder = StateGraph(...)
<span id="__span-1-7">graph = builder.compile(store=store)

请参阅持久化指南,了解有关使用长期内存的更多信息。

修剪消息

要修剪消息历史,您可以使用 trim_messages 函数

API 参考:trim_messages | count_tokens_approximately

<span id="__span-2-1">from langchain_core.messages.utils import (
<span id="__span-2-2">    trim_messages,
<span id="__span-2-3">    count_tokens_approximately
<span id="__span-2-4">)
<span id="__span-2-5">
<span id="__span-2-6">def call_model(state: MessagesState):
<span id="__span-2-7">    messages = trim_messages(
<span id="__span-2-8">        state["messages"],
<span id="__span-2-9">        strategy="last",
<span id="__span-2-10">        token_counter=count_tokens_approximately,
<span id="__span-2-11">        max_tokens=128,
<span id="__span-2-12">        start_on="human",
<span id="__span-2-13">        end_on=("human", "tool"),
<span id="__span-2-14">    )
<span id="__span-2-15">    response = model.invoke(messages)
<span id="__span-2-16">    return {"messages": [response]}
<span id="__span-2-17">
<span id="__span-2-18">builder = StateGraph(MessagesState)
<span id="__span-2-19">builder.add_node(call_model)
<span id="__span-2-20">...

完整示例:修剪消息

<span id="__span-3-1">from langchain_core.messages.utils import (
<span id="__span-3-2">    trim_messages,
<span id="__span-3-3">    count_tokens_approximately
<span id="__span-3-4">)
<span id="__span-3-5">from langchain.chat_models import init_chat_model
<span id="__span-3-6">from langgraph.graph import StateGraph, START, MessagesState
<span id="__span-3-7">
<span id="__span-3-8">model = init_chat_model("anthropic:claude-3-7-sonnet-latest")
<span id="__span-3-9">summarization_model = model.bind(max_tokens=128)
<span id="__span-3-10">
<span id="__span-3-11">def call_model(state: MessagesState):
<span id="__span-3-12">    messages = trim_messages(
<span id="__span-3-13">        state["messages"],
<span id="__span-3-14">        strategy="last",
<span id="__span-3-15">        token_counter=count_tokens_approximately,
<span id="__span-3-16">        max_tokens=128,
<span id="__span-3-17">        start_on="human",
<span id="__span-3-18">        end_on=("human", "tool"),
<span id="__span-3-19">    )
<span id="__span-3-20">    response = model.invoke(messages)
<span id="__span-3-21">    return {"messages": [response]}
<span id="__span-3-22">
<span id="__span-3-23">checkpointer = InMemorySaver()
<span id="__span-3-24">builder = StateGraph(MessagesState)
<span id="__span-3-25">builder.add_node(call_model)
<span id="__span-3-26">builder.add_edge(START, "call_model")
<span id="__span-3-27">graph = builder.compile(checkpointer=checkpointer)
<span id="__span-3-28">
<span id="__span-3-29">config = {"configurable": {"thread_id": "1"}}
<span id="__span-3-30">graph.invoke({"messages": "hi, my name is bob"}, config)
<span id="__span-3-31">graph.invoke({"messages": "write a short poem about cats"}, config)
<span id="__span-3-32">graph.invoke({"messages": "now do the same but for dogs"}, config)
<span id="__span-3-33">final_response = graph.invoke({"messages": "what's my name?"}, config)
<span id="__span-3-34">
<span id="__span-3-35">final_response["messages"][-1].pretty_print()
<span id="__span-4-1">================================== Ai Message ==================================
<span id="__span-4-2">
<span id="__span-4-3">Your name is Bob, as you mentioned when you first introduced yourself.

总结消息

处理长对话历史的有效策略是,一旦达到某个阈值,就总结较早的消息

API 参考:AnyMessage | count_tokens_approximately | StateGraph | START

<span id="__span-5-1">from typing import Any, TypedDict
<span id="__span-5-2">
<span id="__span-5-3">from langchain_core.messages import AnyMessage
<span id="__span-5-4">from langchain_core.messages.utils import count_tokens_approximately
<span id="__span-5-5">from langmem.short_term import SummarizationNode
<span id="__span-5-6">from langgraph.graph import StateGraph, START, MessagesState
<span id="__span-5-7">
<span id="__span-5-8">class State(MessagesState):
<span id="__span-5-9">    context: dict[str, Any]  
<span id="__span-5-10">
<span id="__span-5-11">class LLMInputState(TypedDict):  
<span id="__span-5-12">    summarized_messages: list[AnyMessage]
<span id="__span-5-13">    context: dict[str, Any]
<span id="__span-5-14">
<span id="__span-5-15">summarization_node = SummarizationNode(
<span id="__span-5-16">    token_counter=count_tokens_approximately,
<span id="__span-5-17">    model=summarization_model,
<span id="__span-5-18">    max_tokens=512,
<span id="__span-5-19">    max_tokens_before_summary=256,
<span id="__span-5-20">    max_summary_tokens=256,
<span id="__span-5-21">)
<span id="__span-5-22">
<span id="__span-5-23">def call_model(state: LLMInputState):  
<span id="__span-5-24">    response = model.invoke(state["summarized_messages"])
<span id="__span-5-25">    return {"messages": [response]}
<span id="__span-5-26">
<span id="__span-5-27">builder = StateGraph(State)
<span id="__span-5-28">builder.add_node(call_model)
<span id="__span-5-29">builder.add_node("summarize", summarization_node)
<span id="__span-5-30">builder.add_edge(START, "summarize")
<span id="__span-5-31">builder.add_edge("summarize", "call_model")
<span id="__span-5-32">...

完整示例:总结消息

<span id="__span-6-1">from typing import Any, TypedDict
<span id="__span-6-2">
<span id="__span-6-3">from langchain.chat_models import init_chat_model
<span id="__span-6-4">from langchain_core.messages import AnyMessage
<span id="__span-6-5">from langchain_core.messages.utils import count_tokens_approximately
<span id="__span-6-6">from langgraph.graph import StateGraph, START, MessagesState
<span id="__span-6-7">from langgraph.checkpoint.memory import InMemorySaver
<span id="__span-6-8">from langmem.short_term import SummarizationNode
<span id="__span-6-9">
<span id="__span-6-10">model = init_chat_model("anthropic:claude-3-7-sonnet-latest")
<span id="__span-6-11">summarization_model = model.bind(max_tokens=128)
<span id="__span-6-12">
<span id="__span-6-13">class State(MessagesState):
<span id="__span-6-14">    context: dict[str, Any]  
<span id="__span-6-15">
<span id="__span-6-16">class LLMInputState(TypedDict):  
<span id="__span-6-17">    summarized_messages: list[AnyMessage]
<span id="__span-6-18">    context: dict[str, Any]
<span id="__span-6-19">
<span id="__span-6-20">summarization_node = SummarizationNode(
<span id="__span-6-21">    token_counter=count_tokens_approximately,
<span id="__span-6-22">    model=summarization_model,
<span id="__span-6-23">    max_tokens=256,
<span id="__span-6-24">    max_tokens_before_summary=256,
<span id="__span-6-25">    max_summary_tokens=128,
<span id="__span-6-26">)
<span id="__span-6-27">
<span id="__span-6-28">def call_model(state: LLMInputState):  
<span id="__span-6-29">    response = model.invoke(state["summarized_messages"])
<span id="__span-6-30">    return {"messages": [response]}
<span id="__span-6-31">
<span id="__span-6-32">checkpointer = InMemorySaver()
<span id="__span-6-33">builder = StateGraph(State)
<span id="__span-6-34">builder.add_node(call_model)
<span id="__span-6-35">builder.add_node("summarize", summarization_node)
<span id="__span-6-36">builder.add_edge(START, "summarize")
<span id="__span-6-37">builder.add_edge("summarize", "call_model")
<span id="__span-6-38">graph = builder.compile(checkpointer=checkpointer)
<span id="__span-6-39">
<span id="__span-6-40"># Invoke the graph
<span id="__span-6-41">config = {"configurable": {"thread_id": "1"}}
<span id="__span-6-42">graph.invoke({"messages": "hi, my name is bob"}, config)
<span id="__span-6-43">graph.invoke({"messages": "write a short poem about cats"}, config)
<span id="__span-6-44">graph.invoke({"messages": "now do the same but for dogs"}, config)
<span id="__span-6-45">final_response = graph.invoke({"messages": "what's my name?"}, config)
<span id="__span-6-46">
<span id="__span-6-47">final_response["messages"][-1].pretty_print()
<span id="__span-6-48">print("\nSummary:", final_response["context"]["running_summary"].summary)
<code tabindex="0"><span id="__span-7-1">================================== Ai Message ==================================
<span id="__span-7-2">
<span id="__span-7-3">From our conversation, I can see that you introduced yourself as Bob. That's the name you shared with me when we began talking.
<span id="__span-7-4">
<span id="__span-7-5">Summary: In this conversation, I was introduced to Bob, who then asked me to write a poem about cats. I composed a poem titled "The Mystery of Cats" that captured cats' graceful movements, independent nature, and their special relationship with humans. Bob then requested a similar poem about dogs, so I wrote "The Joy of Dogs," which highlighted dogs' loyalty, enthusiasm, and loving companionship. Both poems were written in a similar style but emphasized the distinct characteristics that make each pet special.

删除消息

要从图状态中删除消息,您可以使用 RemoveMessage

  • 删除特定消息

    <span id="__span-8-1">from langchain_core.messages import RemoveMessage
    <span id="__span-8-2">
    <span id="__span-8-3">def delete_messages(state):
    <span id="__span-8-4">    messages = state["messages"]
    <span id="__span-8-5">    if len(messages) &gt; 2:
    <span id="__span-8-6">        # remove the earliest two messages
    <span id="__span-8-7">        return {"messages": [RemoveMessage(id=m.id) for m in messages[:2]]}
    
  • 删除所有消息

    <span id="__span-9-1">from langgraph.graph.message import REMOVE_ALL_MESSAGES
    <span id="__span-9-2">
    <span id="__span-9-3">def delete_messages(state):
    <span id="__span-9-4">    return {"messages": [RemoveMessage(id=REMOVE_ALL_MESSAGES)]}
    

有效的消息历史

删除消息时,请确保生成的消息历史有效。检查您正在使用的 LLM 提供商的限制。例如:

  • 有些提供商期望消息历史以 user 消息开头
  • 大多数提供商要求带有工具调用的 assistant 消息后跟相应的 tool 结果消息。

完整示例:删除消息

<span id="__span-10-1">from langchain_core.messages import RemoveMessage
<span id="__span-10-2">
<span id="__span-10-3">def delete_messages(state):
<span id="__span-10-4">    messages = state["messages"]
<span id="__span-10-5">    if len(messages) &gt; 2:
<span id="__span-10-6">        # remove the earliest two messages
<span id="__span-10-7">        return {"messages": [RemoveMessage(id=m.id) for m in messages[:2]]}
<span id="__span-10-8">
<span id="__span-10-9">def call_model(state: MessagesState):
<span id="__span-10-10">    response = model.invoke(state["messages"])
<span id="__span-10-11">    return {"messages": response}
<span id="__span-10-12">
<span id="__span-10-13">builder = StateGraph(MessagesState)
<span id="__span-10-14">builder.add_sequence([call_model, delete_messages])
<span id="__span-10-15">builder.add_edge(START, "call_model")
<span id="__span-10-16">
<span id="__span-10-17">checkpointer = InMemorySaver()
<span id="__span-10-18">app = builder.compile(checkpointer=checkpointer)
<span id="__span-10-19">
<span id="__span-10-20">for event in app.stream(
<span id="__span-10-21">    {"messages": [{"role": "user", "content": "hi! I'm bob"}]},
<span id="__span-10-22">    config,
<span id="__span-10-23">    stream_mode="values"
<span id="__span-10-24">):
<span id="__span-10-25">    print([(message.type, message.content) for message in event["messages"]])
<span id="__span-10-26">
<span id="__span-10-27">for event in app.stream(
<span id="__span-10-28">    {"messages": [{"role": "user", "content": "what's my name?"}]},
<span id="__span-10-29">    config,
<span id="__span-10-30">    stream_mode="values"
<span id="__span-10-31">):
<span id="__span-10-32">    print([(message.type, message.content) for message in event["messages"]])
<code tabindex="0"><span id="__span-11-1">[('human', "hi! I'm bob")]
<span id="__span-11-2">[('human', "hi! I'm bob"), ('ai', 'Hi Bob! How are you doing today? Is there anything I can help you with?')]
<span id="__span-11-3">[('human', "hi! I'm bob"), ('ai', 'Hi Bob! How are you doing today? Is there anything I can help you with?'), ('human', "what's my name?")]
<span id="__span-11-4">[('human', "hi! I'm bob"), ('ai', 'Hi Bob! How are you doing today? Is there anything I can help you with?'), ('human', "what's my name?"), ('ai', 'Your name is Bob.')]
<span id="__span-11-5">[('human', "what's my name?"), ('ai', 'Your name is Bob.')]