mirror of
https://github.com/hwchase17/langchain
synced 2024-11-13 19:10:52 +00:00
docs: Add migration instructions for v0.0.x memory abstractions (#26668)
This PR adds a migration guide for any code that relies on old memory abstractions.
This commit is contained in:
parent
eeab6a688c
commit
acf8c2c13e
@ -0,0 +1,554 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "ce8457ed-c0b1-4a74-abbd-9d3d2211270f",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Migrating off ConversationBufferMemory or ConversationStringBufferMemory\n",
|
||||
"\n",
|
||||
"[ConversationBufferMemory](https://python.langchain.com/api_reference/langchain/memory/langchain.memory.buffer.ConversationBufferMemory.html)\n",
|
||||
"and [ConversationStringBufferMemory](https://python.langchain.com/api_reference/langchain/memory/langchain.memory.buffer.ConversationStringBufferMemory.html)\n",
|
||||
" were used to keep track of a conversation between a human and an ai asstistant without any additional processing. \n",
|
||||
"\n",
|
||||
"\n",
|
||||
":::note\n",
|
||||
"The `ConversationStringBufferMemory` is equivalent to `ConversationBufferMemory` but was targeting LLMs that were not chat models.\n",
|
||||
":::\n",
|
||||
"\n",
|
||||
"The methods for handling conversation history using existing modern primitives are:\n",
|
||||
"\n",
|
||||
"1. Using [LangGraph persistence](https://langchain-ai.github.io/langgraph/how-tos/persistence/) along with appropriate processing of the message history\n",
|
||||
"2. Using LCEL with [RunnableWithMessageHistory](https://python.langchain.com/api_reference/core/runnables/langchain_core.runnables.history.RunnableWithMessageHistory.html#) combined with appropriate processing of the message history.\n",
|
||||
"\n",
|
||||
"Most users will find [LangGraph persistence](https://langchain-ai.github.io/langgraph/how-tos/persistence/) both easier to use and configure than the equivalent LCEL, especially for more complex use cases."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "d07f9459-9fb6-4942-99c9-64558aedd7d4",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Set up"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "b99b47ec",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%%capture --no-stderr\n",
|
||||
"%pip install --upgrade --quiet langchain-openai langchain"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "717c8673",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"from getpass import getpass\n",
|
||||
"\n",
|
||||
"if \"OPENAI_API_KEY\" not in os.environ:\n",
|
||||
" os.environ[\"OPENAI_API_KEY\"] = getpass()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "e3621b62-a037-42b8-8faa-59575608bb8b",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Usage with LLMChain / ConversationChain\n",
|
||||
"\n",
|
||||
"This section shows how to migrate off `ConversationBufferMemory` or `ConversationStringBufferMemory` that's used together with either an `LLMChain` or a `ConversationChain`.\n",
|
||||
"\n",
|
||||
"### Legacy\n",
|
||||
"\n",
|
||||
"Below is example usage of `ConversationBufferMemory` with an `LLMChain` or an equivalent `ConversationChain`.\n",
|
||||
"\n",
|
||||
"<details open>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"id": "8b6e1063-cf3a-456a-bf7d-830e5c1d2864",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"{'text': 'Hello Bob! How can I assist you today?', 'chat_history': [HumanMessage(content='my name is bob', additional_kwargs={}, response_metadata={}), AIMessage(content='Hello Bob! How can I assist you today?', additional_kwargs={}, response_metadata={})]}\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from langchain.chains import LLMChain\n",
|
||||
"from langchain.memory import ConversationBufferMemory\n",
|
||||
"from langchain_core.messages import SystemMessage\n",
|
||||
"from langchain_core.prompts import ChatPromptTemplate\n",
|
||||
"from langchain_core.prompts.chat import (\n",
|
||||
" ChatPromptTemplate,\n",
|
||||
" HumanMessagePromptTemplate,\n",
|
||||
" MessagesPlaceholder,\n",
|
||||
")\n",
|
||||
"from langchain_openai import ChatOpenAI\n",
|
||||
"\n",
|
||||
"prompt = ChatPromptTemplate(\n",
|
||||
" [\n",
|
||||
" MessagesPlaceholder(variable_name=\"chat_history\"),\n",
|
||||
" HumanMessagePromptTemplate.from_template(\"{text}\"),\n",
|
||||
" ]\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# highlight-start\n",
|
||||
"memory = ConversationBufferMemory(memory_key=\"chat_history\", return_messages=True)\n",
|
||||
"# highlight-end\n",
|
||||
"\n",
|
||||
"legacy_chain = LLMChain(\n",
|
||||
" llm=ChatOpenAI(),\n",
|
||||
" prompt=prompt,\n",
|
||||
" # highlight-next-line\n",
|
||||
" memory=memory,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"legacy_result = legacy_chain.invoke({\"text\": \"my name is bob\"})\n",
|
||||
"print(legacy_result)\n",
|
||||
"\n",
|
||||
"legacy_result = legacy_chain.invoke({\"text\": \"what was my name\"})"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"id": "c7fa1618",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"'Your name is Bob. How can I assist you today, Bob?'"
|
||||
]
|
||||
},
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"legacy_result[\"text\"]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "3599774f-b56e-4ba3-876c-624f0270b8ac",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
":::note\n",
|
||||
"Note that there is no support for separating conversation threads in a single memory object\n",
|
||||
":::"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "cdc3b527-c09e-4c77-9711-c3cc4506cd95",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"</details>\n",
|
||||
"\n",
|
||||
"### LangGraph\n",
|
||||
"\n",
|
||||
"The example below shows how to use LangGraph to implement a `ConversationChain` or `LLMChain` with `ConversationBufferMemory`.\n",
|
||||
"\n",
|
||||
"This example assumes that you're already somewhat familiar with `LangGraph`. If you're not, then please see the [LangGraph Quickstart Guide](https://langchain-ai.github.io/langgraph/tutorials/introduction/) for more details.\n",
|
||||
"\n",
|
||||
"`LangGraph` offers a lot of additional functionality (e.g., time-travel and interrupts) and will work well for other more complex (and realistic) architectures.\n",
|
||||
"\n",
|
||||
"<details open>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"id": "e591965c-c4d7-4df7-966d-4d14bd46e157",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"================================\u001b[1m Human Message \u001b[0m=================================\n",
|
||||
"\n",
|
||||
"hi! I'm bob\n",
|
||||
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
|
||||
"\n",
|
||||
"Hello Bob! How can I assist you today?\n",
|
||||
"================================\u001b[1m Human Message \u001b[0m=================================\n",
|
||||
"\n",
|
||||
"what was my name?\n",
|
||||
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
|
||||
"\n",
|
||||
"Your name is Bob. How can I help you today, Bob?\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import uuid\n",
|
||||
"\n",
|
||||
"from IPython.display import Image, display\n",
|
||||
"from langchain_core.messages import HumanMessage\n",
|
||||
"from langgraph.checkpoint.memory import MemorySaver\n",
|
||||
"from langgraph.graph import START, MessagesState, StateGraph\n",
|
||||
"\n",
|
||||
"# Define a new graph\n",
|
||||
"workflow = StateGraph(state_schema=MessagesState)\n",
|
||||
"\n",
|
||||
"# Define a chat model\n",
|
||||
"model = ChatOpenAI()\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Define the function that calls the model\n",
|
||||
"def call_model(state: MessagesState):\n",
|
||||
" response = model.invoke(state[\"messages\"])\n",
|
||||
" # We return a list, because this will get added to the existing list\n",
|
||||
" return {\"messages\": response}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Define the two nodes we will cycle between\n",
|
||||
"workflow.add_edge(START, \"model\")\n",
|
||||
"workflow.add_node(\"model\", call_model)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Adding memory is straight forward in langgraph!\n",
|
||||
"# highlight-next-line\n",
|
||||
"memory = MemorySaver()\n",
|
||||
"\n",
|
||||
"app = workflow.compile(\n",
|
||||
" # highlight-next-line\n",
|
||||
" checkpointer=memory\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# The thread id is a unique key that identifies\n",
|
||||
"# this particular conversation.\n",
|
||||
"# We'll just generate a random uuid here.\n",
|
||||
"# This enables a single application to manage conversations among multiple users.\n",
|
||||
"thread_id = uuid.uuid4()\n",
|
||||
"# highlight-next-line\n",
|
||||
"config = {\"configurable\": {\"thread_id\": thread_id}}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"input_message = HumanMessage(content=\"hi! I'm bob\")\n",
|
||||
"for event in app.stream({\"messages\": [input_message]}, config, stream_mode=\"values\"):\n",
|
||||
" event[\"messages\"][-1].pretty_print()\n",
|
||||
"\n",
|
||||
"# Here, let's confirm that the AI remembers our name!\n",
|
||||
"input_message = HumanMessage(content=\"what was my name?\")\n",
|
||||
"for event in app.stream({\"messages\": [input_message]}, config, stream_mode=\"values\"):\n",
|
||||
" event[\"messages\"][-1].pretty_print()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "9893029f-43f3-4703-89bf-e0e8fa18aff3",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"</details>\n",
|
||||
"\n",
|
||||
"### LCEL RunnableWithMessageHistory\n",
|
||||
"\n",
|
||||
"Alternatively, if you have a simple chain, you can wrap the chat model of the chain within a [RunnableWithMessageHistory](https://python.langchain.com/api_reference/core/runnables/langchain_core.runnables.history.RunnableWithMessageHistory.html).\n",
|
||||
"\n",
|
||||
"Please refer to the following [migration guide](/docs/versions/migrating_chains/conversation_chain/) for more information.\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"## Usasge with a pre-built agent\n",
|
||||
"\n",
|
||||
"This example shows usage of an Agent Executor with a pre-built agent constructed using the [create_tool_calling_agent](https://python.langchain.com/api_reference/langchain/agents/langchain.agents.tool_calling_agent.base.create_tool_calling_agent.html) function.\n",
|
||||
"\n",
|
||||
"If you are using one of the [old LangChain pre-built agents](https://python.langchain.com/v0.1/docs/modules/agents/agent_types/), you should be able\n",
|
||||
"to replace that code with the new [langgraph pre-built agent](https://langchain-ai.github.io/langgraph/how-tos/create-react-agent/) which leverages\n",
|
||||
"native tool calling capabilities of chat models and will likely work better out of the box.\n",
|
||||
"\n",
|
||||
"### Legacy Usage\n",
|
||||
"\n",
|
||||
"<details open>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"id": "dc2928de-d7a4-4f87-ab96-59bde9a3829f",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"{'input': 'hi! my name is bob what is my age?', 'chat_history': [HumanMessage(content='hi! my name is bob what is my age?', additional_kwargs={}, response_metadata={}), AIMessage(content='Bob, you are 42 years old.', additional_kwargs={}, response_metadata={})], 'output': 'Bob, you are 42 years old.'}\n",
|
||||
"\n",
|
||||
"{'input': 'do you remember my name?', 'chat_history': [HumanMessage(content='hi! my name is bob what is my age?', additional_kwargs={}, response_metadata={}), AIMessage(content='Bob, you are 42 years old.', additional_kwargs={}, response_metadata={}), HumanMessage(content='do you remember my name?', additional_kwargs={}, response_metadata={}), AIMessage(content='Yes, your name is Bob.', additional_kwargs={}, response_metadata={})], 'output': 'Yes, your name is Bob.'}\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from langchain import hub\n",
|
||||
"from langchain.agents import AgentExecutor, create_tool_calling_agent\n",
|
||||
"from langchain.memory import ConversationBufferMemory\n",
|
||||
"from langchain_core.tools import tool\n",
|
||||
"from langchain_openai import ChatOpenAI\n",
|
||||
"\n",
|
||||
"model = ChatOpenAI(temperature=0)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"@tool\n",
|
||||
"def get_user_age(name: str) -> str:\n",
|
||||
" \"\"\"Use this tool to find the user's age.\"\"\"\n",
|
||||
" # This is a placeholder for the actual implementation\n",
|
||||
" if \"bob\" in name.lower():\n",
|
||||
" return \"42 years old\"\n",
|
||||
" return \"41 years old\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"tools = [get_user_age]\n",
|
||||
"\n",
|
||||
"prompt = ChatPromptTemplate.from_messages(\n",
|
||||
" [\n",
|
||||
" (\"placeholder\", \"{chat_history}\"),\n",
|
||||
" (\"human\", \"{input}\"),\n",
|
||||
" (\"placeholder\", \"{agent_scratchpad}\"),\n",
|
||||
" ]\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Construct the Tools agent\n",
|
||||
"agent = create_tool_calling_agent(model, tools, prompt)\n",
|
||||
"# Instantiate memory\n",
|
||||
"# highlight-start\n",
|
||||
"memory = ConversationBufferMemory(memory_key=\"chat_history\", return_messages=True)\n",
|
||||
"# highlight-end\n",
|
||||
"\n",
|
||||
"# Create an agent\n",
|
||||
"agent = create_tool_calling_agent(model, tools, prompt)\n",
|
||||
"agent_executor = AgentExecutor(\n",
|
||||
" agent=agent,\n",
|
||||
" tools=tools,\n",
|
||||
" # highlight-next-line\n",
|
||||
" memory=memory, # Pass the memory to the executor\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Verify that the agent can use tools\n",
|
||||
"print(agent_executor.invoke({\"input\": \"hi! my name is bob what is my age?\"}))\n",
|
||||
"print()\n",
|
||||
"# Verify that the agent has access to conversation history.\n",
|
||||
"# The agent should be able to answer that the user's name is bob.\n",
|
||||
"print(agent_executor.invoke({\"input\": \"do you remember my name?\"}))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "a4866ae9-e683-44dc-a77b-da1737d3a645",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"</details>\n",
|
||||
"\n",
|
||||
"### LangGraph\n",
|
||||
"\n",
|
||||
"You can follow the standard LangChain tutorial for [building an agent](/docs/tutorials/agents/) an in depth explanation of how this works.\n",
|
||||
"\n",
|
||||
"This example is shown here explicitly to make it easier for users to compare the legacy implementation vs. the corresponding langgraph implementation.\n",
|
||||
"\n",
|
||||
"This example shows how to add memory to the [pre-built react agent](https://langchain-ai.github.io/langgraph/reference/prebuilt/#create_react_agent) in langgraph.\n",
|
||||
"\n",
|
||||
"For more details, please see the [how to add memory to the prebuilt ReAct agent](https://langchain-ai.github.io/langgraph/how-tos/create-react-agent-memory/) guide in langgraph.\n",
|
||||
"\n",
|
||||
"<details open>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"id": "bdb29c9b-bc57-4512-9430-c5d5e3f91e3c",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"================================\u001b[1m Human Message \u001b[0m=================================\n",
|
||||
"\n",
|
||||
"hi! I'm bob. What is my age?\n",
|
||||
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
|
||||
"Tool Calls:\n",
|
||||
" get_user_age (call_oEDwEbIDNdokwqhAV6Azn47c)\n",
|
||||
" Call ID: call_oEDwEbIDNdokwqhAV6Azn47c\n",
|
||||
" Args:\n",
|
||||
" name: bob\n",
|
||||
"=================================\u001b[1m Tool Message \u001b[0m=================================\n",
|
||||
"Name: get_user_age\n",
|
||||
"\n",
|
||||
"42 years old\n",
|
||||
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
|
||||
"\n",
|
||||
"Bob, you are 42 years old! If you need any more assistance or information, feel free to ask.\n",
|
||||
"================================\u001b[1m Human Message \u001b[0m=================================\n",
|
||||
"\n",
|
||||
"do you remember my name?\n",
|
||||
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
|
||||
"\n",
|
||||
"Yes, your name is Bob. If you have any other questions or need assistance, feel free to ask!\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import uuid\n",
|
||||
"\n",
|
||||
"from langchain_core.messages import HumanMessage\n",
|
||||
"from langchain_core.tools import tool\n",
|
||||
"from langchain_openai import ChatOpenAI\n",
|
||||
"from langgraph.checkpoint.memory import MemorySaver\n",
|
||||
"from langgraph.prebuilt import create_react_agent\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"@tool\n",
|
||||
"def get_user_age(name: str) -> str:\n",
|
||||
" \"\"\"Use this tool to find the user's age.\"\"\"\n",
|
||||
" # This is a placeholder for the actual implementation\n",
|
||||
" if \"bob\" in name.lower():\n",
|
||||
" return \"42 years old\"\n",
|
||||
" return \"41 years old\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# highlight-next-line\n",
|
||||
"memory = MemorySaver()\n",
|
||||
"model = ChatOpenAI()\n",
|
||||
"app = create_react_agent(\n",
|
||||
" model,\n",
|
||||
" tools=[get_user_age],\n",
|
||||
" # highlight-next-line\n",
|
||||
" checkpointer=memory,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# highlight-start\n",
|
||||
"# The thread id is a unique key that identifies\n",
|
||||
"# this particular conversation.\n",
|
||||
"# We'll just generate a random uuid here.\n",
|
||||
"# This enables a single application to manage conversations among multiple users.\n",
|
||||
"thread_id = uuid.uuid4()\n",
|
||||
"config = {\"configurable\": {\"thread_id\": thread_id}}\n",
|
||||
"# highlight-end\n",
|
||||
"\n",
|
||||
"# Tell the AI that our name is Bob, and ask it to use a tool to confirm\n",
|
||||
"# that it's capable of working like an agent.\n",
|
||||
"input_message = HumanMessage(content=\"hi! I'm bob. What is my age?\")\n",
|
||||
"\n",
|
||||
"for event in app.stream({\"messages\": [input_message]}, config, stream_mode=\"values\"):\n",
|
||||
" event[\"messages\"][-1].pretty_print()\n",
|
||||
"\n",
|
||||
"# Confirm that the chat bot has access to previous conversation\n",
|
||||
"# and can respond to the user saying that the user's name is Bob.\n",
|
||||
"input_message = HumanMessage(content=\"do you remember my name?\")\n",
|
||||
"\n",
|
||||
"for event in app.stream({\"messages\": [input_message]}, config, stream_mode=\"values\"):\n",
|
||||
" event[\"messages\"][-1].pretty_print()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "87d14cef-a51e-44be-b376-f31b723caaf8",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"If we use a different thread ID, it'll start a new conversation and the bot will not know our name!"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"id": "fe63e424-1111-4f6a-a9c9-0887eb150ab0",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"================================\u001b[1m Human Message \u001b[0m=================================\n",
|
||||
"\n",
|
||||
"hi! do you remember my name?\n",
|
||||
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
|
||||
"\n",
|
||||
"Hello! Yes, I remember your name. It's great to see you again! How can I assist you today?\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"config = {\"configurable\": {\"thread_id\": \"123456789\"}}\n",
|
||||
"\n",
|
||||
"input_message = HumanMessage(content=\"hi! do you remember my name?\")\n",
|
||||
"\n",
|
||||
"for event in app.stream({\"messages\": [input_message]}, config, stream_mode=\"values\"):\n",
|
||||
" event[\"messages\"][-1].pretty_print()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "b2717810",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"</details>\n",
|
||||
"\n",
|
||||
"## Next steps\n",
|
||||
"\n",
|
||||
"Explore persistence with LangGraph:\n",
|
||||
"\n",
|
||||
"* [LangGraph quickstart tutorial](https://langchain-ai.github.io/langgraph/tutorials/introduction/)\n",
|
||||
"* [How to add persistence (\"memory\") to your graph](https://langchain-ai.github.io/langgraph/how-tos/persistence/)\n",
|
||||
"* [How to manage conversation history](https://langchain-ai.github.io/langgraph/how-tos/memory/manage-conversation-history/)\n",
|
||||
"* [How to add summary of the conversation history](https://langchain-ai.github.io/langgraph/how-tos/memory/add-summary-conversation-history/)\n",
|
||||
"\n",
|
||||
"Add persistence with simple LCEL (favor langgraph for more complex use cases):\n",
|
||||
"\n",
|
||||
"* [How to add message history](/docs/how_to/message_history/)\n",
|
||||
"\n",
|
||||
"Working with message history:\n",
|
||||
"\n",
|
||||
"* [How to trim messages](/docs/how_to/trim_messages)\n",
|
||||
"* [How to filter messages](/docs/how_to/filter_messages/)\n",
|
||||
"* [How to merge message runs](/docs/how_to/merge_message_runs/)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "ce4c48e1-b613-4aab-bc2b-617c811fad1d",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.4"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
@ -0,0 +1,696 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "ce8457ed-c0b1-4a74-abbd-9d3d2211270f",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Migrating off ConversationBufferWindowMemory or ConversationTokenBufferMemory\n",
|
||||
"\n",
|
||||
"Follow this guide if you're trying to migrate off one of the old memory classes listed below:\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"| Memory Type | Description |\n",
|
||||
"|----------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n",
|
||||
"| `ConversationBufferWindowMemory` | Keeps the last `n` messages of the conversation. Drops the oldest messages when there are more than `n` messages. |\n",
|
||||
"| `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. |\n",
|
||||
"\n",
|
||||
"`ConversationBufferWindowMemory` and `ConversationTokenBufferMemory` apply additional processing on top of the raw conversation history to trim the conversation history to a size that fits inside the context window of a chat model. \n",
|
||||
"\n",
|
||||
"This processing functionality can be accomplished using LangChain's built-in [trim_messages](https://python.langchain.com/api_reference/core/messages/langchain_core.messages.utils.trim_messages.html) function."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "79935247-acc7-4a05-a387-5d72c9c8c8cb",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
":::important\n",
|
||||
"\n",
|
||||
"We’ll begin by exploring a straightforward method that involves applying processing logic to the entire conversation history.\n",
|
||||
"\n",
|
||||
"While this approach is easy to implement, it has a downside: as the conversation grows, so does the latency, since the logic is re-applied to all previous exchanges in the conversation at each turn.\n",
|
||||
"\n",
|
||||
"More advanced strategies focus on incrementally updating the conversation history to avoid redundant processing.\n",
|
||||
"\n",
|
||||
"For instance, the langgraph [how-to guide on summarization](https://langchain-ai.github.io/langgraph/how-tos/memory/add-summary-conversation-history/) demonstrates\n",
|
||||
"how to maintain a running summary of the conversation while discarding older messages, ensuring they aren't re-processed during later turns.\n",
|
||||
":::"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "d07f9459-9fb6-4942-99c9-64558aedd7d4",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Set up"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "b99b47ec",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%%capture --no-stderr\n",
|
||||
"%pip install --upgrade --quiet langchain-openai langchain langchain-community"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "7127478f-4413-48be-bfec-d0cd91b8cf70",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"from getpass import getpass\n",
|
||||
"\n",
|
||||
"if \"OPENAI_API_KEY\" not in os.environ:\n",
|
||||
" os.environ[\"OPENAI_API_KEY\"] = getpass()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "d6a7bc93-21a9-44c8-842e-9cc82f1ada7c",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Legacy usage with LLMChain / Conversation Chain\n",
|
||||
"\n",
|
||||
"<details open>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"id": "371616e1-ca41-4a57-99e0-5fbf7d63f2ad",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"{'text': 'Nice to meet you, Bob! How can I assist you today?', 'chat_history': []}\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from langchain.chains import LLMChain\n",
|
||||
"from langchain.memory import ConversationBufferWindowMemory\n",
|
||||
"from langchain_core.messages import SystemMessage\n",
|
||||
"from langchain_core.prompts import ChatPromptTemplate\n",
|
||||
"from langchain_core.prompts.chat import (\n",
|
||||
" ChatPromptTemplate,\n",
|
||||
" HumanMessagePromptTemplate,\n",
|
||||
" MessagesPlaceholder,\n",
|
||||
")\n",
|
||||
"from langchain_openai import ChatOpenAI\n",
|
||||
"\n",
|
||||
"prompt = ChatPromptTemplate(\n",
|
||||
" [\n",
|
||||
" SystemMessage(content=\"You are a helpful assistant.\"),\n",
|
||||
" MessagesPlaceholder(variable_name=\"chat_history\"),\n",
|
||||
" HumanMessagePromptTemplate.from_template(\"{text}\"),\n",
|
||||
" ]\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# highlight-start\n",
|
||||
"memory = ConversationBufferWindowMemory(memory_key=\"chat_history\", return_messages=True)\n",
|
||||
"# highlight-end\n",
|
||||
"\n",
|
||||
"legacy_chain = LLMChain(\n",
|
||||
" llm=ChatOpenAI(),\n",
|
||||
" prompt=prompt,\n",
|
||||
" # highlight-next-line\n",
|
||||
" memory=memory,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"legacy_result = legacy_chain.invoke({\"text\": \"my name is bob\"})\n",
|
||||
"print(legacy_result)\n",
|
||||
"\n",
|
||||
"legacy_result = legacy_chain.invoke({\"text\": \"what was my name\"})"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "f48cac47-c8b6-444c-8e1b-f7115c0b2d8d",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"</details>\n",
|
||||
"\n",
|
||||
"## Reimplementing ConversationBufferWindowMemory logic\n",
|
||||
"\n",
|
||||
"Let's first create appropriate logic to process the conversation history, and then we'll see how to integrate it into an application. You can later replace this basic setup with more advanced logic tailored to your specific needs.\n",
|
||||
"\n",
|
||||
"We'll use `trim_messages` to implement logic that keeps the last `n` messages of the conversation. It will drop the oldest messages when the number of messages exceeds `n`.\n",
|
||||
"\n",
|
||||
"In addition, we will also keep the system message if it's present -- when present, it's the first message in a conversation that includes instructions for the chat model."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"id": "0a92b3f3-0315-46ac-bb28-d07398dd23ea",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_core.messages import (\n",
|
||||
" AIMessage,\n",
|
||||
" BaseMessage,\n",
|
||||
" HumanMessage,\n",
|
||||
" SystemMessage,\n",
|
||||
" trim_messages,\n",
|
||||
")\n",
|
||||
"from langchain_openai import ChatOpenAI\n",
|
||||
"\n",
|
||||
"messages = [\n",
|
||||
" SystemMessage(\"you're a good assistant, you always respond with a joke.\"),\n",
|
||||
" HumanMessage(\"i wonder why it's called langchain\"),\n",
|
||||
" AIMessage(\n",
|
||||
" 'Well, I guess they thought \"WordRope\" and \"SentenceString\" just didn\\'t have the same ring to it!'\n",
|
||||
" ),\n",
|
||||
" HumanMessage(\"and who is harrison chasing anyways\"),\n",
|
||||
" AIMessage(\n",
|
||||
" \"Hmmm let me think.\\n\\nWhy, he's probably chasing after the last cup of coffee in the office!\"\n",
|
||||
" ),\n",
|
||||
" HumanMessage(\"why is 42 always the answer?\"),\n",
|
||||
" AIMessage(\n",
|
||||
" \"Because it’s the only number that’s constantly right, even when it doesn’t add up!\"\n",
|
||||
" ),\n",
|
||||
" HumanMessage(\"What did the cow say?\"),\n",
|
||||
"]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"id": "e7ddf8dc-ea27-43e2-8800-9f7c1d4abdc1",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"================================\u001b[1m System Message \u001b[0m================================\n",
|
||||
"\n",
|
||||
"you're a good assistant, you always respond with a joke.\n",
|
||||
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
|
||||
"\n",
|
||||
"Hmmm let me think.\n",
|
||||
"\n",
|
||||
"Why, he's probably chasing after the last cup of coffee in the office!\n",
|
||||
"================================\u001b[1m Human Message \u001b[0m=================================\n",
|
||||
"\n",
|
||||
"why is 42 always the answer?\n",
|
||||
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
|
||||
"\n",
|
||||
"Because it’s the only number that’s constantly right, even when it doesn’t add up!\n",
|
||||
"================================\u001b[1m Human Message \u001b[0m=================================\n",
|
||||
"\n",
|
||||
"What did the cow say?\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from langchain_core.messages import trim_messages\n",
|
||||
"\n",
|
||||
"selected_messages = trim_messages(\n",
|
||||
" messages,\n",
|
||||
" token_counter=len, # <-- len will simply count the number of messages rather than tokens\n",
|
||||
" max_tokens=5, # <-- allow up to 5 messages.\n",
|
||||
" strategy=\"last\",\n",
|
||||
" include_system=True, # <-- Keep the system message\n",
|
||||
" allow_partial=False,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"for msg in selected_messages:\n",
|
||||
" msg.pretty_print()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "18f73819-05e0-41f3-a0e7-a5fd6701d9ef",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Reimplementing ConversationTokenBufferMemory logic\n",
|
||||
"\n",
|
||||
"Here, we'll use `trim_messages` to keeps the system message and 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. \n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"id": "6442f74b-2c36-48fd-a3d1-c7c5d18c050f",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"================================\u001b[1m System Message \u001b[0m================================\n",
|
||||
"\n",
|
||||
"you're a good assistant, you always respond with a joke.\n",
|
||||
"================================\u001b[1m Human Message \u001b[0m=================================\n",
|
||||
"\n",
|
||||
"why is 42 always the answer?\n",
|
||||
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
|
||||
"\n",
|
||||
"Because it’s the only number that’s constantly right, even when it doesn’t add up!\n",
|
||||
"================================\u001b[1m Human Message \u001b[0m=================================\n",
|
||||
"\n",
|
||||
"What did the cow say?\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from langchain_core.messages import trim_messages\n",
|
||||
"\n",
|
||||
"selected_messages = trim_messages(\n",
|
||||
" messages,\n",
|
||||
" # Please see API reference for trim_messages for other ways to specify a token counter.\n",
|
||||
" token_counter=ChatOpenAI(model=\"gpt-4o\"),\n",
|
||||
" max_tokens=80, # <-- token limit\n",
|
||||
" strategy=\"last\",\n",
|
||||
" include_system=True, # <-- Keep the system message\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"for msg in selected_messages:\n",
|
||||
" msg.pretty_print()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "0f05d272-2d22-44b7-9fa6-e617a48584b4",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Modern usage with LangGraph\n",
|
||||
"\n",
|
||||
"The example below shows how to use LangGraph to add simple conversation pre-processing logic.\n",
|
||||
"\n",
|
||||
":::note\n",
|
||||
"\n",
|
||||
"If you want to avoid running the computation on the entire conversation history each time, you can follow\n",
|
||||
"the [how-to guide on summarization](https://langchain-ai.github.io/langgraph/how-tos/memory/add-summary-conversation-history/) that demonstrates\n",
|
||||
"how to discard older messages, ensuring they aren't re-processed during later turns.\n",
|
||||
"\n",
|
||||
":::\n",
|
||||
"\n",
|
||||
"<details open>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"id": "7d6f79a3-fda7-48fd-9128-bbe4aad84199",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"================================\u001b[1m Human Message \u001b[0m=================================\n",
|
||||
"\n",
|
||||
"hi! I'm bob\n",
|
||||
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
|
||||
"\n",
|
||||
"Hello Bob! How can I assist you today?\n",
|
||||
"================================\u001b[1m Human Message \u001b[0m=================================\n",
|
||||
"\n",
|
||||
"what was my name?\n",
|
||||
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
|
||||
"\n",
|
||||
"Your name is Bob. How can I assist you today, Bob?\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import uuid\n",
|
||||
"\n",
|
||||
"from IPython.display import Image, display\n",
|
||||
"from langchain_core.messages import HumanMessage\n",
|
||||
"from langgraph.checkpoint.memory import MemorySaver\n",
|
||||
"from langgraph.graph import START, MessagesState, StateGraph\n",
|
||||
"\n",
|
||||
"# Define a new graph\n",
|
||||
"workflow = StateGraph(state_schema=MessagesState)\n",
|
||||
"\n",
|
||||
"# Define a chat model\n",
|
||||
"model = ChatOpenAI()\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Define the function that calls the model\n",
|
||||
"def call_model(state: MessagesState):\n",
|
||||
" # highlight-start\n",
|
||||
" selected_messages = trim_messages(\n",
|
||||
" state[\"messages\"],\n",
|
||||
" token_counter=len, # <-- len will simply count the number of messages rather than tokens\n",
|
||||
" max_tokens=5, # <-- allow up to 5 messages.\n",
|
||||
" strategy=\"last\",\n",
|
||||
" include_system=True, # <-- Keep the system message\n",
|
||||
" allow_partial=False,\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # highlight-end\n",
|
||||
" response = model.invoke(selected_messages)\n",
|
||||
" # We return a list, because this will get added to the existing list\n",
|
||||
" return {\"messages\": response}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Define the two nodes we will cycle between\n",
|
||||
"workflow.add_edge(START, \"model\")\n",
|
||||
"workflow.add_node(\"model\", call_model)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Adding memory is straight forward in langgraph!\n",
|
||||
"# highlight-next-line\n",
|
||||
"memory = MemorySaver()\n",
|
||||
"\n",
|
||||
"app = workflow.compile(\n",
|
||||
" # highlight-next-line\n",
|
||||
" checkpointer=memory\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# The thread id is a unique key that identifies\n",
|
||||
"# this particular conversation.\n",
|
||||
"# We'll just generate a random uuid here.\n",
|
||||
"thread_id = uuid.uuid4()\n",
|
||||
"# highlight-next-line\n",
|
||||
"config = {\"configurable\": {\"thread_id\": thread_id}}\n",
|
||||
"\n",
|
||||
"input_message = HumanMessage(content=\"hi! I'm bob\")\n",
|
||||
"for event in app.stream({\"messages\": [input_message]}, config, stream_mode=\"values\"):\n",
|
||||
" event[\"messages\"][-1].pretty_print()\n",
|
||||
"\n",
|
||||
"# Here, let's confirm that the AI remembers our name!\n",
|
||||
"config = {\"configurable\": {\"thread_id\": thread_id}}\n",
|
||||
"input_message = HumanMessage(content=\"what was my name?\")\n",
|
||||
"for event in app.stream({\"messages\": [input_message]}, config, stream_mode=\"values\"):\n",
|
||||
" event[\"messages\"][-1].pretty_print()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "84229e2e-a578-4b21-840a-814223406402",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"</details>\n",
|
||||
"\n",
|
||||
"## Usage with a pre-built langgraph agent\n",
|
||||
"\n",
|
||||
"This example shows usage of an Agent Executor with a pre-built agent constructed using the [create_tool_calling_agent](https://api.python.langchain.com/en/latest/agents/langchain.agents.tool_calling_agent.base.create_tool_calling_agent.html) function.\n",
|
||||
"\n",
|
||||
"If you are using one of the [old LangChain pre-built agents](https://python.langchain.com/v0.1/docs/modules/agents/agent_types/), you should be able\n",
|
||||
"to replace that code with the new [langgraph pre-built agent](https://langchain-ai.github.io/langgraph/how-tos/create-react-agent/) which leverages\n",
|
||||
"native tool calling capabilities of chat models and will likely work better out of the box.\n",
|
||||
"\n",
|
||||
"<details open>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"id": "f671db87-8f01-453e-81fd-4e603140a512",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"================================\u001b[1m Human Message \u001b[0m=================================\n",
|
||||
"\n",
|
||||
"hi! I'm bob. What is my age?\n",
|
||||
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
|
||||
"Tool Calls:\n",
|
||||
" get_user_age (call_8bCAm5B4H6WzebGzMnUj2xmN)\n",
|
||||
" Call ID: call_8bCAm5B4H6WzebGzMnUj2xmN\n",
|
||||
" Args:\n",
|
||||
" name: bob\n",
|
||||
"=================================\u001b[1m Tool Message \u001b[0m=================================\n",
|
||||
"Name: get_user_age\n",
|
||||
"\n",
|
||||
"42 years old\n",
|
||||
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
|
||||
"\n",
|
||||
"Bob, you are 42 years old.\n",
|
||||
"================================\u001b[1m Human Message \u001b[0m=================================\n",
|
||||
"\n",
|
||||
"do you remember my name?\n",
|
||||
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
|
||||
"\n",
|
||||
"Yes, your name is Bob.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import uuid\n",
|
||||
"\n",
|
||||
"from langchain_core.messages import (\n",
|
||||
" AIMessage,\n",
|
||||
" BaseMessage,\n",
|
||||
" HumanMessage,\n",
|
||||
" SystemMessage,\n",
|
||||
" trim_messages,\n",
|
||||
")\n",
|
||||
"from langchain_core.tools import tool\n",
|
||||
"from langchain_openai import ChatOpenAI\n",
|
||||
"from langgraph.checkpoint.memory import MemorySaver\n",
|
||||
"from langgraph.prebuilt import create_react_agent\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"@tool\n",
|
||||
"def get_user_age(name: str) -> str:\n",
|
||||
" \"\"\"Use this tool to find the user's age.\"\"\"\n",
|
||||
" # This is a placeholder for the actual implementation\n",
|
||||
" if \"bob\" in name.lower():\n",
|
||||
" return \"42 years old\"\n",
|
||||
" return \"41 years old\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"memory = MemorySaver()\n",
|
||||
"model = ChatOpenAI()\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# highlight-start\n",
|
||||
"def state_modifier(state) -> list[BaseMessage]:\n",
|
||||
" \"\"\"Given the agent state, return a list of messages for the chat model.\"\"\"\n",
|
||||
" # We're using the message processor defined above.\n",
|
||||
" return trim_messages(\n",
|
||||
" state[\"messages\"],\n",
|
||||
" token_counter=len, # <-- len will simply count the number of messages rather than tokens\n",
|
||||
" max_tokens=5, # <-- allow up to 5 messages.\n",
|
||||
" strategy=\"last\",\n",
|
||||
" include_system=True, # <-- Keep the system message\n",
|
||||
" allow_partial=False,\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# highlight-end\n",
|
||||
"\n",
|
||||
"app = create_react_agent(\n",
|
||||
" model,\n",
|
||||
" tools=[get_user_age],\n",
|
||||
" checkpointer=memory,\n",
|
||||
" # highlight-next-line\n",
|
||||
" state_modifier=state_modifier,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# The thread id is a unique key that identifies\n",
|
||||
"# this particular conversation.\n",
|
||||
"# We'll just generate a random uuid here.\n",
|
||||
"thread_id = uuid.uuid4()\n",
|
||||
"config = {\"configurable\": {\"thread_id\": thread_id}}\n",
|
||||
"\n",
|
||||
"# Tell the AI that our name is Bob, and ask it to use a tool to confirm\n",
|
||||
"# that it's capable of working like an agent.\n",
|
||||
"input_message = HumanMessage(content=\"hi! I'm bob. What is my age?\")\n",
|
||||
"\n",
|
||||
"for event in app.stream({\"messages\": [input_message]}, config, stream_mode=\"values\"):\n",
|
||||
" event[\"messages\"][-1].pretty_print()\n",
|
||||
"\n",
|
||||
"# Confirm that the chat bot has access to previous conversation\n",
|
||||
"# and can respond to the user saying that the user's name is Bob.\n",
|
||||
"input_message = HumanMessage(content=\"do you remember my name?\")\n",
|
||||
"\n",
|
||||
"for event in app.stream({\"messages\": [input_message]}, config, stream_mode=\"values\"):\n",
|
||||
" event[\"messages\"][-1].pretty_print()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "f4d16e09-1d90-4153-8576-6d3996cb5a6c",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"</details>\n",
|
||||
"\n",
|
||||
"## LCEL: Add a preprocessing step\n",
|
||||
"\n",
|
||||
"The simplest way to add complex conversation management is by introducing a pre-processing step in front of the chat model and pass the full conversation history to the pre-processing step.\n",
|
||||
"\n",
|
||||
"This approach is conceptually simple and will work in many situations; for example, if using a [RunnableWithMessageHistory](/docs/how_to/message_history/) instead of wrapping the chat model, wrap the chat model with the pre-processor.\n",
|
||||
"\n",
|
||||
"The obvious downside of this approach is that latency starts to increase as the conversation history grows because of two reasons:\n",
|
||||
"\n",
|
||||
"1. As the conversation gets longer, more data may need to be fetched from whatever store your'e using to store the conversation history (if not storing it in memory).\n",
|
||||
"2. The pre-processing logic will end up doing a lot of redundant computation, repeating computation from previous steps of the conversation.\n",
|
||||
"\n",
|
||||
":::caution\n",
|
||||
"\n",
|
||||
"If you want to use a chat model's tool calling capabilities, remember to bind the tools to the model before adding the history pre-processing step to it!\n",
|
||||
"\n",
|
||||
":::\n",
|
||||
"\n",
|
||||
"<details open>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"id": "072046bb-3892-4206-8ae5-025e93110dcc",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
|
||||
"Tool Calls:\n",
|
||||
" what_did_the_cow_say (call_M4OGGCfbN2EF5yGBjdM7qNiz)\n",
|
||||
" Call ID: call_M4OGGCfbN2EF5yGBjdM7qNiz\n",
|
||||
" Args:\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from langchain_core.messages import (\n",
|
||||
" AIMessage,\n",
|
||||
" BaseMessage,\n",
|
||||
" HumanMessage,\n",
|
||||
" SystemMessage,\n",
|
||||
" trim_messages,\n",
|
||||
")\n",
|
||||
"from langchain_core.tools import tool\n",
|
||||
"from langchain_openai import ChatOpenAI\n",
|
||||
"\n",
|
||||
"model = ChatOpenAI()\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"@tool\n",
|
||||
"def what_did_the_cow_say() -> str:\n",
|
||||
" \"\"\"Check to see what the cow said.\"\"\"\n",
|
||||
" return \"foo\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# highlight-start\n",
|
||||
"message_processor = trim_messages( # Returns a Runnable if no messages are provided\n",
|
||||
" token_counter=len, # <-- len will simply count the number of messages rather than tokens\n",
|
||||
" max_tokens=5, # <-- allow up to 5 messages.\n",
|
||||
" strategy=\"last\",\n",
|
||||
" include_system=True, # <-- Keep the system message\n",
|
||||
" allow_partial=False,\n",
|
||||
")\n",
|
||||
"# highlight-end\n",
|
||||
"\n",
|
||||
"# Note that we bind tools to the model first!\n",
|
||||
"model_with_tools = model.bind_tools([what_did_the_cow_say])\n",
|
||||
"\n",
|
||||
"# highlight-next-line\n",
|
||||
"model_with_preprocessor = message_processor | model_with_tools\n",
|
||||
"\n",
|
||||
"full_history = [\n",
|
||||
" SystemMessage(\"you're a good assistant, you always respond with a joke.\"),\n",
|
||||
" HumanMessage(\"i wonder why it's called langchain\"),\n",
|
||||
" AIMessage(\n",
|
||||
" 'Well, I guess they thought \"WordRope\" and \"SentenceString\" just didn\\'t have the same ring to it!'\n",
|
||||
" ),\n",
|
||||
" HumanMessage(\"and who is harrison chasing anyways\"),\n",
|
||||
" AIMessage(\n",
|
||||
" \"Hmmm let me think.\\n\\nWhy, he's probably chasing after the last cup of coffee in the office!\"\n",
|
||||
" ),\n",
|
||||
" HumanMessage(\"why is 42 always the answer?\"),\n",
|
||||
" AIMessage(\n",
|
||||
" \"Because it’s the only number that’s constantly right, even when it doesn’t add up!\"\n",
|
||||
" ),\n",
|
||||
" HumanMessage(\"What did the cow say?\"),\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# We pass it explicity to the model_with_preprocesor for illustrative purposes.\n",
|
||||
"# If you're using `RunnableWithMessageHistory` the history will be automatically\n",
|
||||
"# read from the source the you configure.\n",
|
||||
"model_with_preprocessor.invoke(full_history).pretty_print()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "5da7225a-5e94-4f53-bb0d-86b6b528d150",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"</details>\n",
|
||||
"\n",
|
||||
"If you need to implement more efficient logic and want to use `RunnableWithMessageHistory` for now the way to achieve this\n",
|
||||
"is to subclass from [BaseChatMessageHistory](https://api.python.langchain.com/en/latest/chat_history/langchain_core.chat_history.BaseChatMessageHistory.html) and\n",
|
||||
"define appropriate logic for `add_messages` (that doesn't simply append the history, but instead re-writes it).\n",
|
||||
"\n",
|
||||
"Unless you have a good reason to implement this solution, you should instead use LangGraph."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "b2717810",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Next steps\n",
|
||||
"\n",
|
||||
"Explore persistence with LangGraph:\n",
|
||||
"\n",
|
||||
"* [LangGraph quickstart tutorial](https://langchain-ai.github.io/langgraph/tutorials/introduction/)\n",
|
||||
"* [How to add persistence (\"memory\") to your graph](https://langchain-ai.github.io/langgraph/how-tos/persistence/)\n",
|
||||
"* [How to manage conversation history](https://langchain-ai.github.io/langgraph/how-tos/memory/manage-conversation-history/)\n",
|
||||
"* [How to add summary of the conversation history](https://langchain-ai.github.io/langgraph/how-tos/memory/add-summary-conversation-history/)\n",
|
||||
"\n",
|
||||
"Add persistence with simple LCEL (favor langgraph for more complex use cases):\n",
|
||||
"\n",
|
||||
"* [How to add message history](/docs/how_to/message_history/)\n",
|
||||
"\n",
|
||||
"Working with message history:\n",
|
||||
"\n",
|
||||
"* [How to trim messages](/docs/how_to/trim_messages)\n",
|
||||
"* [How to filter messages](/docs/how_to/filter_messages/)\n",
|
||||
"* [How to merge message runs](/docs/how_to/merge_message_runs/)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "f4adad0b-3e25-47d9-a8e6-6a9c6c616f14",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.4"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "ce8457ed-c0b1-4a74-abbd-9d3d2211270f",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Migrating off ConversationSummaryMemory or ConversationSummaryBufferMemory\n",
|
||||
"\n",
|
||||
"Follow this guide if you're trying to migrate off one of the old memory classes listed below:\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"| Memory Type | Description |\n",
|
||||
"|---------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------|\n",
|
||||
"| `ConversationSummaryMemory` | Continually summarizes the conversation history. The summary is updated after each conversation turn. The abstraction returns the summary of the conversation history. |\n",
|
||||
"| `ConversationSummaryBufferMemory` | Provides a running summary of the conversation together with 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. |\n",
|
||||
"\n",
|
||||
"Please follow the following [how-to guide on summarization](https://langchain-ai.github.io/langgraph/how-tos/memory/add-summary-conversation-history/) in LangGraph. \n",
|
||||
"\n",
|
||||
"This guide shows how to maintain a running summary of the conversation while discarding older messages, ensuring they aren't re-processed during later turns."
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.4"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
118
docs/docs/versions/migrating_memory/index.mdx
Normal file
118
docs/docs/versions/migrating_memory/index.mdx
Normal file
@ -0,0 +1,118 @@
|
||||
---
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
# How to migrate from v0.0 memory
|
||||
|
||||
The concept of memory has evolved significantly in LangChain since its initial release.
|
||||
|
||||
Broadly speaking, LangChain 0.0.x memory was used to handle three main use cases:
|
||||
|
||||
| Use Case | Example |
|
||||
|--------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Managing conversation history | Keep only the last `n` turns of the conversation between the user and the AI. |
|
||||
| Extraction of structured information | Extract structured information from the conversation history, such as a list of facts learned about the user. |
|
||||
| Composite memory implementations | Combine multiple memory sources, e.g., a list of known facts about the user along with facts learned during a given conversation. |
|
||||
|
||||
While the LangChain 0.0.x memory abstractions were useful, they were limited in their capabilities and not well suited for real-world conversational AI applications. These memory abstractions lacked built-in support for multi-user, multi-conversation scenarios, which are essential for practical conversational AI systems.
|
||||
|
||||
This guide will help you migrate your usage of memory implementations from LangChain v0.0.x to the persistence implementations of LangGraph.
|
||||
|
||||
## Why use LangGraph for memory?
|
||||
|
||||
The main advantages of persistence implementation in LangGraph are:
|
||||
|
||||
- Built-in support for multi-user, multi-conversation scenarios which is often a requirement for real-world conversational AI applications.
|
||||
- Ability to save and resume complex state at any time for error recovery, human-in-the-loop workflows, time travel interactions, and more.
|
||||
- Full support for both [LLM](/docs/concepts/#llms) and [chat models](/docs/concepts/#chat-models). In contrast, the v0.0.x memory abstractions were created prior to the existence and widespread adoption of chat model APIs, and so it does not work well with chat models (e.g., fails with tool calling chat models).
|
||||
- Offers a high degree of customization and control over the memory implementation, including the ability to use different backends.
|
||||
|
||||
## Migrations
|
||||
|
||||
:::info Prerequisites
|
||||
|
||||
These guides assume some familiarity with the following concepts:
|
||||
- [LangGraph](https://langchain-ai.github.io/langgraph/)
|
||||
- [v0.0.x Memory](https://python.langchain.com/v0.1/docs/modules/memory/)
|
||||
- [How to add persistence ("memory") to your graph](https://langchain-ai.github.io/langgraph/how-tos/persistence/)
|
||||
:::
|
||||
|
||||
### 1. Managing conversation history
|
||||
|
||||
The goal of managing conversation history is to store and retrieve the history in a way that is optimal for a chat model to use.
|
||||
|
||||
Often this involves trimming and / or summarizing the conversation history to keep the most relevant parts of the conversation while having the conversation fit inside the context window of the chat model.
|
||||
|
||||
Memory classes that fall into this category include:
|
||||
|
||||
| Memory Type | How to Migrate | Description |
|
||||
|-----------------------------------|:-------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `ConversationBufferMemory` | [Link to Migration Guide](conversation_buffer_memory) | A basic memory implementation that simply stores the conversation history. |
|
||||
| `ConversationStringBufferMemory` | [Link to Migration Guide](conversation_buffer_memory) | A special case of `ConversationBufferMemory` designed for LLMs and no longer relevant. |
|
||||
| `ConversationBufferWindowMemory` | [Link to Migration Guide](conversation_buffer_window_memory) | Keeps the last `n` turns of the conversation. Drops the oldest turn when the buffer is full. |
|
||||
| `ConversationTokenBufferMemory` | [Link to Migration Guide](conversation_buffer_window_memory) | 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. |
|
||||
| `ConversationSummaryMemory` | [Link to Migration Guide](conversation_summary_memory) | Continually summarizes the conversation history. The summary is updated after each conversation turn. The abstraction returns the summary of the conversation history. |
|
||||
| `ConversationSummaryBufferMemory` | [Link to Migration Guide](conversation_summary_memory) | Provides a running summary of the conversation together with 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. |
|
||||
| `VectorStoreRetrieverMemory` | No migration guide yet | Stores the conversation history in a vector store and retrieves the most relevant parts of past conversation based on the input. |
|
||||
|
||||
|
||||
### 2. Extraction of structured information from the conversation history
|
||||
|
||||
Memory classes that fall into this category include:
|
||||
|
||||
| Memory Type | Description |
|
||||
|----------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `BaseEntityStore` | An abstract interface that resembles a key-value store. It was used for storing structured information learned during the conversation. The information had to be represented as a dictionary of key-value pairs. |
|
||||
| `ConversationEntityMemory` | Combines the ability to summarize the conversation while extracting structured information from the conversation history. |
|
||||
|
||||
And specific backend implementations of abstractions:
|
||||
|
||||
| Memory Type | Description |
|
||||
|---------------------------|----------------------------------------------------------------------------------------------------------|
|
||||
| `InMemoryEntityStore` | An implementation of `BaseEntityStore` that stores the information in the literal computer memory (RAM). |
|
||||
| `RedisEntityStore` | A specific implementation of `BaseEntityStore` that uses Redis as the backend. |
|
||||
| `SQLiteEntityStore` | A specific implementation of `BaseEntityStore` that uses SQLite as the backend. |
|
||||
| `UpstashRedisEntityStore` | A specific implementation of `BaseEntityStore` that uses Upstash as the backend. |
|
||||
|
||||
These abstractions have not received much development since their initial release. The reason
|
||||
is that for these abstractions to be useful they typically require a lot of specialization for a particular application, so these
|
||||
abstractions are not as widely used as the conversation history management abstractions.
|
||||
|
||||
For this reason, there are no migration guides for these abstractions. If you're struggling to migrate an applications
|
||||
that relies on these abstractions, please open an issue on the LangChain GitHub repository and we'll try to prioritize providing
|
||||
more guidance on how to migrate these abstractions.
|
||||
|
||||
The general strategy for extracting structured information from the conversation history is to use a chat model with tool calling capabilities to extract structured information from the conversation history.
|
||||
The extracted information can then be saved into an appropriate data structure (e.g., a dictionary), and information from it can be retrieved and added into the prompt as needed.
|
||||
|
||||
### 3. Implementations that provide composite logic on top of one or more memory implementations
|
||||
|
||||
Memory classes that fall into this category include:
|
||||
|
||||
| Memory Type | Description |
|
||||
|------------------------|--------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `CombinedMemory` | This abstraction accepted a list of `BaseMemory` and fetched relevant memory information from each of them based on the input. |
|
||||
| `SimpleMemory` | Used to add read-only hard-coded context. Users can simply write this information into the prompt. |
|
||||
| `ReadOnlySharedMemory` | Provided a read-only view of an existing `BaseMemory` implementation. |
|
||||
|
||||
These implementations did not seem to be used widely or provide significant value. Users should be able
|
||||
to re-implement these without too much difficulty in custom code.
|
||||
|
||||
## Related Resources
|
||||
|
||||
Explore persistence with LangGraph:
|
||||
|
||||
* [LangGraph quickstart tutorial](https://langchain-ai.github.io/langgraph/tutorials/introduction/)
|
||||
* [How to add persistence ("memory") to your graph](https://langchain-ai.github.io/langgraph/how-tos/persistence/)
|
||||
* [How to manage conversation history](https://langchain-ai.github.io/langgraph/how-tos/memory/manage-conversation-history/)
|
||||
* [How to add summary of the conversation history](https://langchain-ai.github.io/langgraph/how-tos/memory/add-summary-conversation-history/)
|
||||
|
||||
Add persistence with simple LCEL (favor langgraph for more complex use cases):
|
||||
|
||||
* [How to add message history](https://python.langchain.com/docs/how_to/message_history/)
|
||||
|
||||
Working with message history:
|
||||
|
||||
* [How to trim messages](https://python.langchain.com/docs/how_to/trim_messages)
|
||||
* [How to filter messages](https://python.langchain.com/docs/how_to/filter_messages/)
|
||||
* [How to merge message runs](https://python.langchain.com/docs/how_to/merge_message_runs/)
|
Loading…
Reference in New Issue
Block a user