mirror of
https://github.com/hwchase17/langchain.git
synced 2026-02-03 15:55:44 +00:00
Compare commits
14 Commits
langchain-
...
eugene/add
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4dcebcf92d | ||
|
|
886bc6bba0 | ||
|
|
6d80c88afb | ||
|
|
6dca9f1ac6 | ||
|
|
c3db793600 | ||
|
|
dbb0d27280 | ||
|
|
f222e41747 | ||
|
|
c270187f80 | ||
|
|
c345bcbfdb | ||
|
|
0e521d661a | ||
|
|
23b20c70a8 | ||
|
|
aedc077ec4 | ||
|
|
162dc26531 | ||
|
|
92b07f7bd8 |
122
docs/docs/versions/migrating_memory/index.mdx
Normal file
122
docs/docs/versions/migrating_memory/index.mdx
Normal file
@@ -0,0 +1,122 @@
|
||||
---
|
||||
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 four main use cases:
|
||||
|
||||
1. Managing conversation history. For example, keep only the last `n` turns of the conversation between the user and the AI. See Migration guide for this use case is available [here](./manage_conversation_history).
|
||||
2. Extraction of structured information from the conversation history and providing it as context. For example, extract a list of facts learned about a user during the conversation.
|
||||
3. Implementations that provide composite logic on top of one or more memory implementations. For example, a memory implementation that combines a list of known facts about a user together with a list of facts learned during a given conversation.
|
||||
4. Custom user code based on the memory interface.
|
||||
|
||||
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. Many of these abstractions were written before the existence of chat models and do not work well with chat models. In addition,
|
||||
they did not provide a good way to handle multi-user, multi-conversation scenarios, which are often a requirement for real-world conversational AI applications.
|
||||
|
||||
This guide will help you migrate your usage of memory implementations from LangChain v0.0.x to the persistence implementations of LangGraph.
|
||||
|
||||
Where possible we may show an equivalent implementation using just [LCEL](/docs/concepts/#langchain-expression-language-lcel)
|
||||
without LangGraph; however, in general, we recommend that users swap to using LangGraph for most use cases.
|
||||
|
||||
## Advantages of LangGraph persistence
|
||||
|
||||
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 and 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 backend.
|
||||
|
||||
## 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.
|
||||
|
||||
Please follow [this guide](./manage_conversation_history) to see how to migrate to modern LangChain and LangGraph primitives for managing conversation history.
|
||||
|
||||
There are different strategies for managing conversation history. The following memory classes in LangChain v0.0.x fall into this category:
|
||||
|
||||
| Memory Type | Description |
|
||||
|-----------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `ConversationBufferMemory` | A basic memory implementation that simply stores the conversation history. |
|
||||
| `ConversationStringBufferMemory` | A special case of `ConversationBufferMemory` designed for LLMs and no longer relevant. |
|
||||
| `ConversationBufferWindowMemory` | Keeps the last `n` turns of the conversation. Drops the oldest turn when the buffer is full. |
|
||||
| `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. |
|
||||
| `ConversationSummaryMemory` | Continually summarizes the conversation history. The summary is updated after each conversation turn. The abstraction returns the summary of the conversation history. |
|
||||
| `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. |
|
||||
| `VectorStoreRetrieverMemory` | 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
|
||||
|
||||
We do not currently have a guide for this. If you need help with migrating these, please
|
||||
open an issue in the docs, and we'll try to prioritize and write migration guidelines.
|
||||
|
||||
The general strategy should be 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 when needed.
|
||||
|
||||
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. |
|
||||
|
||||
### 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. |
|
||||
|
||||
### 4. Custom user code based on the memory interface
|
||||
|
||||
If you have custom code that subclasses from `BaseMemory`, `BaseChatMemory`, or `BaseEntityStore`, you will need to re-implement the code using the appropriate primitives. Often, this involves lightweight processing of the conversation history, such as filtering or summarization.
|
||||
|
||||
Alternatively, you could use the existing implementations using the `load_memory_variables` and `save_context` API directly and write the appropriate glue code for your use case.
|
||||
|
||||
## 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/)
|
||||
@@ -0,0 +1,902 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "ce8457ed-c0b1-4a74-abbd-9d3d2211270f",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Migrating to modern conversation management\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",
|
||||
"| `ConversationBufferMemory` | A basic memory implementation that simply stores the conversation history. |\n",
|
||||
"| `ConversationStringBufferMemory` | A special case of `ConversationBufferMemory` designed for LLMs and no longer relevant. |\n",
|
||||
"| `ConversationBufferWindowMemory` | Keeps the last `n` turns of the conversation. Drops the oldest turn when the buffer is full. |\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",
|
||||
"| `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",
|
||||
"| `VectorStoreRetrieverMemory` | Stores the conversation history in a vector store and retrieves the most relevant parts of past conversation based on the input. |\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"The goal of managing conversation history is to store and retrieve it in a way that maximizes its usefulness for a chat model. This often requires trimming or summarizing the conversation to retain the most relevant details while ensuring it fits within the model's context window.\n",
|
||||
"\n",
|
||||
"LangChain v0.0.x offered several methods for managing conversation history. While these memory abstractions were helpful, they had limitations that made them less effective for real-world conversational AI. Many were developed before the advent of chat models, making them poorly suited for such applications. Additionally, they lacked robust solutions for handling multi-user, multi-conversation scenarios—an essential requirement for practical conversational AI systems.\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://api.python.langchain.com/en/latest/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 easier to configure than the equivalent LCEL, especially for more complex use cases.\n",
|
||||
"\n",
|
||||
"Some advantages to using LangGraph persistence:\n",
|
||||
"\n",
|
||||
"- LangGraph persistence is much more powerful than simple chat memory -- it lets you save and resume complex state at any time for error recovery, human-in-the-loop workflows, time travel interactions, and more.\n",
|
||||
"- LangGraph persistence is built to support multi user, multi thread scenarios and will work well in production settings.\n",
|
||||
"- Both LLMs and chat models are fully supported.\n",
|
||||
"- Users have granular control over how memory is updated.\n",
|
||||
"\n",
|
||||
"\n",
|
||||
":::hint\n",
|
||||
"\n",
|
||||
"LangChain provides a number of useful helper utilties to process message history. Please see the following \n",
|
||||
"how to guides to learn more:\n",
|
||||
"\n",
|
||||
"* [How to trim messages](https://python.langchain.com/docs/how_to/trim_messages)\n",
|
||||
"* [How to filter messages](https://python.langchain.com/docs/how_to/filter_messages/)\n",
|
||||
"* [How to merge message runs](https://python.langchain.com/docs/how_to/merge_message_runs/)\n",
|
||||
"\n",
|
||||
":::\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Migrating off ConversationBufferMemory / ConversationStringBufferMemory\n",
|
||||
"\n",
|
||||
"[ConversationBufferMemory](https://api.python.langchain.com/en/latest/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",
|
||||
"The `ConversationStringBufferMemory` is equivalent to `ConversationBufferMemory` but was targeting LLMs that were not chat models."
|
||||
]
|
||||
},
|
||||
{
|
||||
"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": "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": [
|
||||
"## 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": 3,
|
||||
"id": "8b6e1063-cf3a-456a-bf7d-830e5c1d2864",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"/tmp/ipykernel_2459205/1840360377.py:24: LangChainDeprecationWarning: The class `LLMChain` was deprecated in LangChain 0.1.17 and will be removed in 1.0. Use RunnableSequence, e.g., `prompt | llm` instead.\n",
|
||||
" legacy_chain = LLMChain(\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"{'text': 'Nice to meet you, Bob! How can I assist you today?', '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={})]}\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",
|
||||
" 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 = 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": 4,
|
||||
"id": "c7fa1618",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"'Your name is Bob. How can I assist you further, Bob?'"
|
||||
]
|
||||
},
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"legacy_result[\"text\"]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"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."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"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",
|
||||
"I'm sorry, but I do not have the ability to know your name as I am an AI assistant.\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",
|
||||
"# 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",
|
||||
"thread_id = uuid.uuid4()\n",
|
||||
"config = {\"configurable\": {\"thread_id\": thread_id}}\n",
|
||||
"# highlight-end\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\": \"2\"}}\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": [
|
||||
"### LCEL RunnableWithMessageHistory\n",
|
||||
"\n",
|
||||
"Alternatively, if you have a simple chain, you can wrap the chat model of the chain within a [RunnableWithMessageHistory](https://api.python.langchain.com/en/latest/runnables/langchain_core.runnables.history.RunnableWithMessageHistory.html).\n",
|
||||
"\n",
|
||||
"Please refer to [this how to guide](/docs/how_to/message_history/) for more information.\n",
|
||||
"\n",
|
||||
"## Agent Executor 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://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",
|
||||
"### Legacy Usage\n",
|
||||
"\n",
|
||||
"<details open>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"id": "dc2928de-d7a4-4f87-ab96-59bde9a3829f",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"/home/eugene/.pyenv/versions/3.11.4/envs/core/lib/python3.11/site-packages/langsmith/client.py:5301: LangChainBetaWarning: The function `loads` is in beta. It is actively being worked on, so the API may change.\n",
|
||||
" prompt = loads(json.dumps(prompt_object.manifest))\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"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='Hello Bob! You are 42 years old. If you need any more assistance or have any other questions, feel free to ask!', additional_kwargs={}, response_metadata={})], 'output': 'Hello Bob! You are 42 years old. If you need any more assistance or have any other questions, feel free to ask!'}\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='Hello Bob! You are 42 years old. If you need any more assistance or have any other questions, feel free to ask!', additional_kwargs={}, response_metadata={}), HumanMessage(content='do you remember my name?', additional_kwargs={}, response_metadata={}), AIMessage(content='Yes, your name is Bob. How can I assist you further, Bob?', additional_kwargs={}, response_metadata={})], 'output': 'Yes, your name is Bob. How can I assist you further, 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",
|
||||
"# Get the prompt to use - you can modify this!\n",
|
||||
"prompt = hub.pull(\"hwchase17/openai-functions-agent\")\n",
|
||||
"\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",
|
||||
"This example shows how to add memory to the pre-built react agent in langgraph. This is an agent for which you only need to supply the tool and prompt.\n",
|
||||
"\n",
|
||||
"After looking through the snippet below, you can reference [this guide](https://langchain-ai.github.io/langgraph/how-tos/create-react-agent-memory/) for more details.\n",
|
||||
"\n",
|
||||
"<details open>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"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_ewWp6keHVouwJyM4lRhk4X25)\n",
|
||||
" Call ID: call_ewWp6keHVouwJyM4lRhk4X25\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 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",
|
||||
"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": "code",
|
||||
"execution_count": 8,
|
||||
"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": [
|
||||
"# Now, let's confirm that if we use a different thread\n",
|
||||
"# that the bot will not know what our name is since\n",
|
||||
"# it's a new conversation!\n",
|
||||
"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": "0c0f5921-cc51-4f08-9364-a2eab0ad8b3d",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"</details>\n",
|
||||
"\n",
|
||||
"## Implementing Conversation History Processing\n",
|
||||
"\n",
|
||||
"Each of the following memory types applies specific logic to handle the conversation history:\n",
|
||||
"\n",
|
||||
"| Memory Type | Description |\n",
|
||||
"|---------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------|\n",
|
||||
"| `ConversationBufferWindowMemory` | Keeps the last `n` turns of the conversation. Drops the oldest turn when the buffer is full. |\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",
|
||||
"| `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",
|
||||
"The general approach involves writing the necessary logic for processing conversation history and integrating it at the correct point.\n",
|
||||
"\n",
|
||||
"We’ll start by building a simple processor using LangChain's built-in [trim_messages](https://python.langchain.com/api_reference/core/messages/langchain_core.messages.utils.trim_messages.html) function, and then demonstrate how to integrate it into your application.\n",
|
||||
"\n",
|
||||
"You can later replace this basic setup with more advanced logic tailored to your specific needs.\n",
|
||||
"\n",
|
||||
"\n",
|
||||
":::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 test and implement, it has a downside: as the conversation grows, s\n",
|
||||
"o does the latency, since the logic is re-applied to all previous exchanges 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",
|
||||
":::\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"### ConversationBufferWindowMemory, ConversationTokenBufferMemory"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"id": "a501806e-deac-458c-88b1-b615efde9930",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[SystemMessage(content=\"you're a good assistant, you always respond with a joke.\", additional_kwargs={}, response_metadata={}),\n",
|
||||
" AIMessage(content=\"Hmmm let me think.\\n\\nWhy, he's probably chasing after the last cup of coffee in the office!\", additional_kwargs={}, response_metadata={}),\n",
|
||||
" HumanMessage(content='why is 42 always the answer?', additional_kwargs={}, response_metadata={}),\n",
|
||||
" AIMessage(content='Because it’s the only number that’s constantly right, even when it doesn’t add up!', additional_kwargs={}, response_metadata={}),\n",
|
||||
" HumanMessage(content='What did the cow say?', additional_kwargs={}, response_metadata={})]"
|
||||
]
|
||||
},
|
||||
"execution_count": 12,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from langchain_core.messages import (\n",
|
||||
" AIMessage,\n",
|
||||
" BaseMessage,\n",
|
||||
" HumanMessage,\n",
|
||||
" SystemMessage,\n",
|
||||
" trim_messages,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"full_message_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",
|
||||
"def message_processor(messages: list[BaseMessage]) -> list[BaseMessage]:\n",
|
||||
" \"\"\"A sample message processor that:\n",
|
||||
"\n",
|
||||
" 1. Keeps the system message\n",
|
||||
" 2. Keeps up to max number of messages\n",
|
||||
" 3. Make sure that the last message is a HumanMessage\n",
|
||||
"\n",
|
||||
" You will likely want to instead count based on tokens,\n",
|
||||
" and/or increase the number of messages.\n",
|
||||
"\n",
|
||||
" Please see the API reference for trim_messages for more details.\n",
|
||||
"\n",
|
||||
" https://python.langchain.com/api_reference/core/messages/langchain_core.messages.utils.trim_messages.html\n",
|
||||
" \"\"\"\n",
|
||||
" return trim_messages(\n",
|
||||
" messages,\n",
|
||||
" token_counter=len, # <-- Will just count the number of messages rather than tokens\n",
|
||||
" max_tokens=5, # <-- allow up to 5 messages.\n",
|
||||
" strategy=\"last\",\n",
|
||||
" include_system=True,\n",
|
||||
" allow_partial=False,\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"message_processor(full_message_history)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "c1fe665c-b79e-42c1-942d-a0b3b22a5313",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## LCEL: Add a pre-processor in front of the chat model\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're using tools, remember to bind the tools to the model before adding a pre-processing step to it!\n",
|
||||
"\n",
|
||||
":::"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 13,
|
||||
"id": "5537b001-a49c-4d12-b25d-4087890f49b4",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_aYRmth57XGNqvZq3V5vn0Urg', 'function': {'arguments': '{}', 'name': 'what_did_the_cow_say'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 16, 'prompt_tokens': 126, 'total_tokens': 142, 'completion_tokens_details': {'reasoning_tokens': 0}}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': None, 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-93b89a08-b8a1-4a7a-bedd-ab7e2ce47894-0', tool_calls=[{'name': 'what_did_the_cow_say', 'args': {}, 'id': 'call_aYRmth57XGNqvZq3V5vn0Urg', 'type': 'tool_call'}], usage_metadata={'input_tokens': 126, 'output_tokens': 16, 'total_tokens': 142})"
|
||||
]
|
||||
},
|
||||
"execution_count": 13,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"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",
|
||||
"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_message_history in the previous code block.\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_message_history)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "aaa4e03a-0050-4f1d-bda1-530e387966bf",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"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": "84229e2e-a578-4b21-840a-814223406402",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## LangGraph\n",
|
||||
"\n",
|
||||
"### Agent Executor with a pre-built agent\n",
|
||||
"\n",
|
||||
"If you're migrating off a pre-built langchain agent that uses memory.\n",
|
||||
"\n",
|
||||
"You can create a pre-built agent using: [create_react_agent](https://langchain-ai.github.io/langgraph/reference/prebuilt/#create_react_agent).\n",
|
||||
"\n",
|
||||
"To add memory pre-processing to the agent, you can do the following:\n",
|
||||
"\n",
|
||||
"```python\n",
|
||||
"\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 message_processor(state['messages'])\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",
|
||||
"...\n",
|
||||
"\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"At each turn of the conversation, "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 14,
|
||||
"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_kTEpBUbRFbKE3DZolG9tFrgD)\n",
|
||||
" Call ID: call_kTEpBUbRFbKE3DZolG9tFrgD\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 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",
|
||||
"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 message_processor(state[\"messages\"])\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()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "7a231ade-9f33-4ff8-9a62-20be3fc159b6",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### ConversationSummaryMemory / ConversationSummaryBufferMemory\n",
|
||||
"\n",
|
||||
"It’s essential to summarize conversations efficiently to prevent growing latency as the conversation history grows.\n",
|
||||
"\n",
|
||||
"Please follow the guide [how to add summary of the conversation history](https://langchain-ai.github.io/langgraph/how-tos/memory/add-summary-conversation-history/) to see learn how to\n",
|
||||
"handle conversation summarization efficiently with 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/)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"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
|
||||
}
|
||||
Reference in New Issue
Block a user