从 ConversationBufferWindowMemory 或 ConversationTokenBufferMemory 迁移
如果你试图从下面列出的旧记忆类中迁移出去,请按照本指南操作:
| 记忆类型 | 描述 |
|---|---|
ConversationBufferWindowMemory | Keeps the last n messages of the conversation. Drops the oldest messages when there are more than n messages. |
ConversationTokenBufferMemory | Keeps only the most recent messages in the conversation under the constraint that the total number of tokens in the conversation does not exceed a certain limit. |
ConversationBufferWindowMemory 和 ConversationTokenBufferMemory 会在原始对话历史的基础上进行额外处理,以将对话历史裁剪到适合聊天模型上下文窗口的大小。
此处理功能可以使用 LangChain 内置的 trim_messages 函数来完成。
我们将首先探索一种简单的方法,该方法涉及对整个对话历史应用处理逻辑。
虽然这种方法易于实现,但它有一个缺点:随着对话的进行,延迟也会增加,因为在每一轮对话中,都需要对之前的所有交流内容重新应用该逻辑。
更高级的策略侧重于逐步更新对话历史,以避免冗余处理。
例如,langgraph 摘要处理指南 演示了如何在丢弃较旧消息的同时保持对话的持续摘要,确保在后续轮次中不会重新处理这些消息。
设置
%%capture --no-stderr
%pip install --upgrade --quiet langchain-openai langchain
import os
from getpass import getpass
if "OPENAI_API_KEY" not in os.environ:
os.environ["OPENAI_API_KEY"] = getpass()
使用 LLMChain / Conversation Chain 的旧版方法
详细信息
from langchain.chains import LLMChain
from langchain.memory import ConversationBufferWindowMemory
from langchain_core.messages import SystemMessage
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.prompts.chat import (
ChatPromptTemplate,
HumanMessagePromptTemplate,
MessagesPlaceholder,
)
from langchain_openai import ChatOpenAI
prompt = ChatPromptTemplate(
[
SystemMessage(content="You are a helpful assistant."),
MessagesPlaceholder(variable_name="chat_history"),
HumanMessagePromptTemplate.from_template("{text}"),
]
)
memory = ConversationBufferWindowMemory(memory_key="chat_history", return_messages=True)
legacy_chain = LLMChain(
llm=ChatOpenAI(),
prompt=prompt,
memory=memory,
)
legacy_result = legacy_chain.invoke({"text": "my name is bob"})
print(legacy_result)
legacy_result = legacy_chain.invoke({"text": "what was my name"})
print(legacy_result)
{'text': 'Nice to meet you, Bob! How can I assist you today?', 'chat_history': []}
{'text': 'Your name is Bob. How can I assist you further, Bob?', 'chat_history': [HumanMessage(content='my name is bob', additional_kwargs={}, response_metadata={}), AIMessage(content='Nice to meet you, Bob! How can I assist you today?', additional_kwargs={}, response_metadata={})]}
重新实现 ConversationBufferWindowMemory 逻辑
首先,让我们创建适当的逻辑来处理对话历史,然后我们会看到如何将其集成到应用程序中。之后,您可以将此基本设置替换为更适合您特定需求的更高级逻辑。
我们将使用 trim_messages 来实现保持对话最后 n 条消息的逻辑。当消息数量超过 n 时,它会删除最旧的消息。
此外,如果存在系统消息,我们也会保留它——当系统消息存在时,它是对话中的第一条消息,包含对聊天模型的指令。
from langchain_core.messages import (
AIMessage,
BaseMessage,
HumanMessage,
SystemMessage,
trim_messages,
)
from langchain_openai import ChatOpenAI
messages = [
SystemMessage("you're a good assistant, you always respond with a joke."),
HumanMessage("i wonder why it's called langchain"),
AIMessage(
'Well, I guess they thought "WordRope" and "SentenceString" just didn\'t have the same ring to it!'
),
HumanMessage("and who is harrison chasing anyways"),
AIMessage(
"Hmmm let me think.\n\nWhy, he's probably chasing after the last cup of coffee in the office!"
),
HumanMessage("why is 42 always the answer?"),
AIMessage(
"Because it’s the only number that’s constantly right, even when it doesn’t add up!"
),
HumanMessage("What did the cow say?"),
]
from langchain_core.messages import trim_messages
selected_messages = trim_messages(
messages,
token_counter=len, # <-- len will simply count the number of messages rather than tokens
max_tokens=5, # <-- allow up to 5 messages.
strategy="last",
# Most chat models expect that chat history starts with either:
# (1) a HumanMessage or
# (2) a SystemMessage followed by a HumanMessage
# start_on="human" makes sure we produce a valid chat history
start_on="human",
# Usually, we want to keep the SystemMessage
# if it's present in the original history.
# The SystemMessage has special instructions for the model.
include_system=True,
allow_partial=False,
)
for msg in selected_messages:
msg.pretty_print()
================================[1m System Message [0m================================
you're a good assistant, you always respond with a joke.
==================================[1m Ai Message [0m==================================
Hmmm let me think.
Why, he's probably chasing after the last cup of coffee in the office!
================================[1m Human Message [0m=================================
why is 42 always the answer?
==================================[1m Ai Message [0m==================================
Because it’s the only number that’s constantly right, even when it doesn’t add up!
================================[1m Human Message [0m=================================
What did the cow say?
重新实现 ConversationTokenBufferMemory 逻辑
在这里,我们将使用 trim_messages 来保留系统消息和对话中最近的消息,同时确保对话中的总标记数不超过某个限制。
from langchain_core.messages import trim_messages
selected_messages = trim_messages(
messages,
# Please see API reference for trim_messages for other ways to specify a token counter.
token_counter=ChatOpenAI(model="gpt-4o"),
max_tokens=80, # <-- token limit
# The start_on is specified
# Most chat models expect that chat history starts with either:
# (1) a HumanMessage or
# (2) a SystemMessage followed by a HumanMessage
# start_on="human" makes sure we produce a valid chat history
start_on="human",
# Usually, we want to keep the SystemMessage
# if it's present in the original history.
# The SystemMessage has special instructions for the model.
include_system=True,
strategy="last",
)
for msg in selected_messages:
msg.pretty_print()
================================[1m System Message [0m================================
you're a good assistant, you always respond with a joke.
================================[1m Human Message [0m=================================
why is 42 always the answer?
==================================[1m Ai Message [0m==================================
Because it’s the only number that’s constantly right, even when it doesn’t add up!
================================[1m Human Message [0m=================================
What did the cow say?
使用 LangGraph 的现代用法
下面的示例展示了如何使用 LangGraph 添加简单的对话预处理逻辑。
如果你想避免每次都要对整个对话历史进行计算,可以参考 摘要处理的指南,其中演示了如何丢弃较早的消息,确保它们在后续轮次中不会被重新处理。
详细信息
import uuid
from IPython.display import Image, display
from langchain_core.messages import HumanMessage
from langgraph.checkpoint.memory import MemorySaver
from langgraph.graph import START, MessagesState, StateGraph
# Define a new graph
workflow = StateGraph(state_schema=MessagesState)
# Define a chat model
model = ChatOpenAI()
# Define the function that calls the model
def call_model(state: MessagesState):
selected_messages = trim_messages(
state["messages"],
token_counter=len, # <-- len will simply count the number of messages rather than tokens
max_tokens=5, # <-- allow up to 5 messages.
strategy="last",
# Most chat models expect that chat history starts with either:
# (1) a HumanMessage or
# (2) a SystemMessage followed by a HumanMessage
# start_on="human" makes sure we produce a valid chat history
start_on="human",
# Usually, we want to keep the SystemMessage
# if it's present in the original history.
# The SystemMessage has special instructions for the model.
include_system=True,
allow_partial=False,
)
response = model.invoke(selected_messages)
# We return a list, because this will get added to the existing list
return {"messages": response}
# Define the two nodes we will cycle between
workflow.add_edge(START, "model")
workflow.add_node("model", call_model)
# Adding memory is straight forward in langgraph!
memory = MemorySaver()
app = workflow.compile(
checkpointer=memory
)
# The thread id is a unique key that identifies
# this particular conversation.
# We'll just generate a random uuid here.
thread_id = uuid.uuid4()
config = {"configurable": {"thread_id": thread_id}}
input_message = HumanMessage(content="hi! I'm bob")
for event in app.stream({"messages": [input_message]}, config, stream_mode="values"):
event["messages"][-1].pretty_print()
# Here, let's confirm that the AI remembers our name!
config = {"configurable": {"thread_id": thread_id}}
input_message = HumanMessage(content="what was my name?")
for event in app.stream({"messages": [input_message]}, config, stream_mode="values"):
event["messages"][-1].pretty_print()
================================[1m Human Message [0m=================================
hi! I'm bob
==================================[1m Ai Message [0m==================================
Hello Bob! How can I assist you today?
================================[1m Human Message [0m=================================
what was my name?
==================================[1m Ai Message [0m==================================
Your name is Bob. How can I help you, Bob?
与预构建的 langgraph 代理一起使用
此示例展示了如何使用代理执行器与使用 create_tool_calling_agent 函数构建的预构建代理。
如果您正在使用旧版 LangChain 预构建代理之一,您应该能够用新版的 langgraph 预构建代理 替换该代码,它利用了聊天模型的原生工具调用功能,可能开箱即用效果更好。
详细信息
import uuid
from langchain_core.messages import (
AIMessage,
BaseMessage,
HumanMessage,
SystemMessage,
trim_messages,
)
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI
from langgraph.checkpoint.memory import MemorySaver
from langgraph.prebuilt import create_react_agent
@tool
def get_user_age(name: str) -> str