diff --git a/docs/versioned_docs/version-0.2.x/tutorials/agents.ipynb b/docs/versioned_docs/version-0.2.x/tutorials/agents.ipynb
index 496272fffc30b..a108473bb165f 100644
--- a/docs/versioned_docs/version-0.2.x/tutorials/agents.ipynb
+++ b/docs/versioned_docs/version-0.2.x/tutorials/agents.ipynb
@@ -17,7 +17,12 @@
"source": [
"# Build an Agent\n",
"\n",
- "To best understand the agent framework, let's build an agent that has two tools: one to look things up online, and one to look up specific data that we've exposed via a retriever.\n",
+ "By themselves, language models can't take actions - they just output text.\n",
+ "A big use case for LangChain is creating **agents**.\n",
+ "Agents are systems that use an LLM as a reasoning enginer to determine which actions to take and what the inputs to those actions should be.\n",
+ "The results of those actions can then be fed back into the agent and it determine whether more actions are needed, or whether it is okay to finish.\n",
+ "\n",
+ "In this tutorial we will build an agent that can interact with multiple different tools: one being a local database, the other being a search engine. You will be able to ask this agent questions, watch it call tools, and have conversations with it.\n",
"\n",
":::{.callout-important}\n",
"This section will cover building with LangChain Agents. LangChain Agents are fine for getting started, but past a certain point you will likely want flexibility and control that they do not offer. For working with more advanced agents, we'd reccommend checking out [LangGraph](/docs/concepts/#langgraph)\n",
@@ -26,10 +31,10 @@
"## Concepts\n",
"\n",
"Concepts we will cover are:\n",
- "- Using [language models](/docs/concepts/#chat-models)\n",
- "- Using [PromptTemplates](/docs/concepts/#prompt-templates) and [OutputParsers](/docs/concepts/#output-parsers)\n",
+ "- Using [language models](/docs/concepts/#chat-models), in particular their tool calling ability\n",
"- Creating a [Retriever](/docs/concepts/#retrievers) to expose specific information to our agent\n",
- "- Using a [Tool](/docs/concepts/#tools) to look up things online\n",
+ "- Using a Search [Tool](/docs/concepts/#tools) to look up things online\n",
+ "- [`Chat History`](/docs/concepts/#chat-history), which allows a chatbot to \"remember\" past interactions and take them into account when responding to followup questions. \n",
"- Debugging and tracing your application using [LangSmith](/docs/concepts/#langsmith)\n",
"\n",
"## Setup\n",
@@ -84,11 +89,7 @@
"\n",
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
"os.environ[\"LANGCHAIN_API_KEY\"] = getpass.getpass()\n",
- "```\n",
- "\n",
- "## Define tools\n",
- "\n",
- "We first need to create the tools we want to use. We will use two tools: [Tavily](/docs/integrations/tools/tavily_search) (to search online) and then a retriever over a local index we will create"
+ "```\n"
]
},
{
@@ -96,6 +97,10 @@
"id": "c335d1bf",
"metadata": {},
"source": [
+ "## Define tools\n",
+ "\n",
+ "We first need to create the tools we want to use. We will use two tools: [Tavily](/docs/integrations/tools/tavily_search) (to search online) and then a retriever over a local index we will create\n",
+ "\n",
"### [Tavily](/docs/integrations/tools/tavily_search)\n",
"\n",
"We have a built-in tool in LangChain to easily use Tavily search engine as tool.\n",
@@ -110,7 +115,7 @@
},
{
"cell_type": "code",
- "execution_count": 1,
+ "execution_count": 5,
"id": "482ce13d",
"metadata": {},
"outputs": [],
@@ -120,7 +125,7 @@
},
{
"cell_type": "code",
- "execution_count": 4,
+ "execution_count": 6,
"id": "9cc86c0b",
"metadata": {},
"outputs": [],
@@ -130,7 +135,7 @@
},
{
"cell_type": "code",
- "execution_count": 5,
+ "execution_count": 7,
"id": "e593bbf6",
"metadata": {},
"outputs": [
@@ -138,12 +143,12 @@
"data": {
"text/plain": [
"[{'url': 'https://www.weatherapi.com/',\n",
- " 'content': \"{'location': {'name': 'San Francisco', 'region': 'California', 'country': 'United States of America', 'lat': 37.78, 'lon': -122.42, 'tz_id': 'America/Los_Angeles', 'localtime_epoch': 1713914854, 'localtime': '2024-04-23 16:27'}, 'current': {'last_updated_epoch': 1713914100, 'last_updated': '2024-04-23 16:15', 'temp_c': 16.1, 'temp_f': 61.0, 'is_day': 1, 'condition': {'text': 'Overcast', 'icon': '//cdn.weatherapi.com/weather/64x64/day/122.png', 'code': 1009}, 'wind_mph': 16.1, 'wind_kph': 25.9, 'wind_degree': 210, 'wind_dir': 'SSW', 'pressure_mb': 1016.0, 'pressure_in': 29.99, 'precip_mm': 0.0, 'precip_in': 0.0, 'humidity': 58, 'cloud': 100, 'feelslike_c': 16.1, 'feelslike_f': 61.0, 'vis_km': 16.0, 'vis_miles': 9.0, 'uv': 4.0, 'gust_mph': 20.5, 'gust_kph': 33.0}}\"},\n",
- " {'url': 'https://forecast.weather.gov/zipcity.php?inputstring=San francisco,CA',\n",
- " 'content': 'Current conditions at SAN FRANCISCO DOWNTOWN (SFOC1) Lat: 37.77056°NLon: 122.42694°WElev: 150.0ft. NA. 54°F. 12°C. ... 1am PDT Apr 23, 2024-6pm PDT Apr 29, 2024 . Forecast Discussion . Additional Resources. ... National Weather Service; San Francisco Bay Area, CA; Comments? Questions? Please Contact Us. Disclaimer; Information Quality;'}]"
+ " 'content': \"{'location': {'name': 'San Francisco', 'region': 'California', 'country': 'United States of America', 'lat': 37.78, 'lon': -122.42, 'tz_id': 'America/Los_Angeles', 'localtime_epoch': 1714000492, 'localtime': '2024-04-24 16:14'}, 'current': {'last_updated_epoch': 1713999600, 'last_updated': '2024-04-24 16:00', 'temp_c': 15.6, 'temp_f': 60.1, 'is_day': 1, 'condition': {'text': 'Overcast', 'icon': '//cdn.weatherapi.com/weather/64x64/day/122.png', 'code': 1009}, 'wind_mph': 10.5, 'wind_kph': 16.9, 'wind_degree': 330, 'wind_dir': 'NNW', 'pressure_mb': 1018.0, 'pressure_in': 30.06, 'precip_mm': 0.0, 'precip_in': 0.0, 'humidity': 72, 'cloud': 100, 'feelslike_c': 15.6, 'feelslike_f': 60.1, 'vis_km': 16.0, 'vis_miles': 9.0, 'uv': 5.0, 'gust_mph': 14.8, 'gust_kph': 23.8}}\"},\n",
+ " {'url': 'https://www.weathertab.com/en/c/e/04/united-states/california/san-francisco/',\n",
+ " 'content': 'San Francisco Weather Forecast for Apr 2024 - Risk of Rain Graph. Rain Risk Graph: Monthly Overview. Bar heights indicate rain risk percentages. Yellow bars mark low-risk days, while black and grey bars signal higher risks. Grey-yellow bars act as buffers, advising to keep at least one day clear from the riskier grey and black days, guiding ...'}]"
]
},
- "execution_count": 5,
+ "execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
@@ -164,7 +169,7 @@
},
{
"cell_type": "code",
- "execution_count": 6,
+ "execution_count": 8,
"id": "9c9ce713",
"metadata": {},
"outputs": [],
@@ -185,7 +190,7 @@
},
{
"cell_type": "code",
- "execution_count": 8,
+ "execution_count": 9,
"id": "dae53ec6",
"metadata": {},
"outputs": [
@@ -195,7 +200,7 @@
"Document(page_content='# The data to predict and grade over evaluators=[exact_match], # The evaluators to score the results experiment_prefix=\"sample-experiment\", # The name of the experiment metadata={ \"version\": \"1.0.0\", \"revision_id\": \"beta\" },)import { Client, Run, Example } from \\'langsmith\\';import { runOnDataset } from \\'langchain/smith\\';import { EvaluationResult } from \\'langsmith/evaluation\\';const client = new Client();// Define dataset: these are your test casesconst datasetName = \"Sample Dataset\";const dataset = await client.createDataset(datasetName, { description: \"A sample dataset in LangSmith.\"});await client.createExamples({ inputs: [ { postfix: \"to LangSmith\" }, { postfix: \"to Evaluations in LangSmith\" }, ], outputs: [ { output: \"Welcome to LangSmith\" }, { output: \"Welcome to Evaluations in LangSmith\" }, ], datasetId: dataset.id,});// Define your evaluatorconst exactMatch = async ({ run, example }: { run: Run; example?:', metadata={'source': 'https://docs.smith.langchain.com/overview', 'title': 'Getting started with LangSmith | \\uf8ffü¶úÔ∏è\\uf8ffüõ†Ô∏è LangSmith', 'description': 'Introduction', 'language': 'en'})"
]
},
- "execution_count": 8,
+ "execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
@@ -214,7 +219,7 @@
},
{
"cell_type": "code",
- "execution_count": 9,
+ "execution_count": 10,
"id": "117594b5",
"metadata": {},
"outputs": [],
@@ -224,7 +229,7 @@
},
{
"cell_type": "code",
- "execution_count": 10,
+ "execution_count": 11,
"id": "7280b031",
"metadata": {},
"outputs": [],
@@ -248,7 +253,7 @@
},
{
"cell_type": "code",
- "execution_count": 11,
+ "execution_count": 12,
"id": "b8e8e710",
"metadata": {},
"outputs": [],
@@ -258,43 +263,174 @@
},
{
"cell_type": "markdown",
- "id": "40ccec80",
+ "id": "e00068b0",
"metadata": {},
"source": [
- "## Create the agent\n",
+ "## Using Language Models\n",
+ "\n",
+ "Next, let's learn how to use a language model by to call tools. LangChain supports many different language models that you can use interchangably - select the one you want to use below!\n",
"\n",
- "Now that we have defined the tools, we can create the agent. We will be using a tool calling agent - for more information on this type of agent, as well as other options, see [this guide](/docs/concepts/#agent_types/).\n",
+ "```{=mdx}\n",
+ "import ChatModelTabs from \"@theme/ChatModelTabs\";\n",
"\n",
- "First, we choose the LLM we want to be guiding the agent."
+ "\n",
+ "```"
]
},
{
"cell_type": "code",
- "execution_count": 12,
- "id": "f70b0fad",
+ "execution_count": 4,
+ "id": "69185491",
"metadata": {},
"outputs": [],
"source": [
+ "# | output: false\n",
+ "# | echo: false\n",
+ "\n",
"from langchain_openai import ChatOpenAI\n",
"\n",
- "llm = ChatOpenAI(model=\"gpt-3.5-turbo-0125\", temperature=0)"
+ "model = ChatOpenAI(model=\"gpt-4\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "642ed8bf",
+ "metadata": {},
+ "source": [
+ "You can call the language model by passing in a list of messages. By default, the response is a `content` string."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "id": "c96c960b",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "'Hello! How can I assist you today?'"
+ ]
+ },
+ "execution_count": 13,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "from langchain_core.messages import HumanMessage\n",
+ "\n",
+ "response = model.invoke([HumanMessage(content=\"hi!\")])\n",
+ "response.content"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "47bf8210",
+ "metadata": {},
+ "source": [
+ "We can now see what it is like to enable this model to do tool calling. In order to enable that we use `.bind_tools` to give the language model knowledge of these tools"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "id": "ba692a74",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "model_with_tools = model.bind_tools(tools)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "fd920b69",
+ "metadata": {},
+ "source": [
+ "We can now call the model. Let's first call it with a normal message, and see how it responds. We can look at both the `content` field as well as the `tool_calls` field."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 18,
+ "id": "b6a7e925",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "ContentString: Hello! How can I assist you today?\n",
+ "ToolCalls: []\n"
+ ]
+ }
+ ],
+ "source": [
+ "response = model_with_tools.invoke([HumanMessage(content=\"Hi!\")])\n",
+ "\n",
+ "print(f\"ContentString: {response.content}\")\n",
+ "print(f\"ToolCalls: {response.tool_calls}\")"
]
},
{
"cell_type": "markdown",
- "id": "5d1a95ce",
+ "id": "e8c81e76",
+ "metadata": {},
+ "source": [
+ "Now, let's try calling it with some input that would expect a tool to be called."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 19,
+ "id": "688b465d",
"metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "ContentString: \n",
+ "ToolCalls: [{'name': 'tavily_search_results_json', 'args': {'query': 'current weather in San Francisco'}, 'id': 'call_4HteVahXkRAkWjp6dGXryKZX'}]\n"
+ ]
+ }
+ ],
"source": [
- "Next, we choose the prompt we want to use to guide the agent.\n",
+ "response = model_with_tools.invoke([HumanMessage(content=\"What's the weather in SF?\")])\n",
+ "\n",
+ "print(f\"ContentString: {response.content}\")\n",
+ "print(f\"ToolCalls: {response.tool_calls}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "83c4bcd3",
+ "metadata": {},
+ "source": [
+ "We can see that there's now no content, but there is a tool call! It wants us to call the Tavily Search tool.\n",
+ "\n",
+ "This isn't calling that tool yet - it's just telling us to. In order to actually calll it, we'll want to create our agent."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "40ccec80",
+ "metadata": {},
+ "source": [
+ "## Create the agent\n",
+ "\n",
+ "Now that we have defined the tools and the LLM, we can create the agent. We will be using a tool calling agent - for more information on this type of agent, as well as other options, see [this guide](/docs/concepts/#agent_types/).\n",
+ "\n",
+ "We can first choose the prompt we want to use to guide the agent.\n",
"\n",
"If you want to see the contents of this prompt and have access to LangSmith, you can go to:\n",
"\n",
- "https://smith.langchain.com/hub/hwchase17/openai-functions-agent"
+ "[https://smith.langchain.com/hub/hwchase17/openai-functions-agent](https://smith.langchain.com/hub/hwchase17/openai-functions-agent)"
]
},
{
"cell_type": "code",
- "execution_count": 13,
+ "execution_count": 20,
"id": "af83d3e3",
"metadata": {},
"outputs": [
@@ -307,7 +443,7 @@
" MessagesPlaceholder(variable_name='agent_scratchpad')]"
]
},
- "execution_count": 13,
+ "execution_count": 20,
"metadata": {},
"output_type": "execute_result"
}
@@ -325,19 +461,21 @@
"id": "f8014c9d",
"metadata": {},
"source": [
- "Now, we can initalize the agent with the LLM, the prompt, and the tools. The agent is responsible for taking in input and deciding what actions to take. Crucially, the Agent does not execute those actions - that is done by the AgentExecutor (next step). For more information about how to think about these components, see our [conceptual guide](/docs/modules/agents/concepts/)."
+ "Now, we can initalize the agent with the LLM, the prompt, and the tools. The agent is responsible for taking in input and deciding what actions to take. Crucially, the Agent does not execute those actions - that is done by the AgentExecutor (next step). For more information about how to think about these components, see our [conceptual guide](/docs/concepts/#agents).\n",
+ "\n",
+ "Note that we are passing in the `model`, not `model_with_tools`. That is because `create_tool_calling_agent` will call `.bind_tools` for us under the hood."
]
},
{
"cell_type": "code",
- "execution_count": 14,
+ "execution_count": 23,
"id": "89cf72b4-6046-4b47-8f27-5522d8cb8036",
"metadata": {},
"outputs": [],
"source": [
"from langchain.agents import create_tool_calling_agent\n",
"\n",
- "agent = create_tool_calling_agent(llm, tools, prompt)"
+ "agent = create_tool_calling_agent(model, tools, prompt)"
]
},
{
@@ -350,7 +488,7 @@
},
{
"cell_type": "code",
- "execution_count": 27,
+ "execution_count": 24,
"id": "ce33904a",
"metadata": {},
"outputs": [],
@@ -367,12 +505,14 @@
"source": [
"## Run the agent\n",
"\n",
- "We can now run the agent on a few queries! Note that for now, these are all **stateless** queries (it won't remember previous interactions)."
+ "We can now run the agent on a few queries! Note that for now, these are all **stateless** queries (it won't remember previous interactions).\n",
+ "\n",
+ "First up, let's how it responds when there's no need to call a tool:"
]
},
{
"cell_type": "code",
- "execution_count": 28,
+ "execution_count": 25,
"id": "114ba50d",
"metadata": {},
"outputs": [
@@ -382,7 +522,7 @@
"{'input': 'hi!', 'output': 'Hello! How can I assist you today?'}"
]
},
- "execution_count": 28,
+ "execution_count": 25,
"metadata": {},
"output_type": "execute_result"
}
@@ -391,9 +531,19 @@
"agent_executor.invoke({\"input\": \"hi!\"})"
]
},
+ {
+ "cell_type": "markdown",
+ "id": "71493a42",
+ "metadata": {},
+ "source": [
+ "In order to see exactly what is happening under the hood (and to make sure it's not calling a tool) we can take a look at the [LangSmith trace](https://smith.langchain.com/public/8441812b-94ce-4832-93ec-e1114214553a/r)\n",
+ "\n",
+ "Let's now try it out on an example where it should be invoking the retriever"
+ ]
+ },
{
"cell_type": "code",
- "execution_count": 29,
+ "execution_count": 26,
"id": "3fa4780a",
"metadata": {
"scrolled": true
@@ -403,10 +553,10 @@
"data": {
"text/plain": [
"{'input': 'how can langsmith help with testing?',\n",
- " 'output': 'LangSmith is a platform for building production-grade LLM applications that can help with testing by closely monitoring and evaluating your application. It allows you to ship quickly and with confidence. LangSmith provides tracing capabilities, evaluation features, production monitoring, and automations to streamline the testing process. Additionally, LangSmith offers a User Guide, Pricing information, Self-Hosting options, Proxy capabilities, and a Prompt Hub for managing prompts effectively. If you have sensitive data that cannot be logged, LangSmith offers private deployment options and self-hosting solutions.'}"
+ " 'output': 'LangSmith is a platform that aids in building production-grade Language Learning Model (LLM) applications. It can assist with testing in several ways:\\n\\n1. **Monitoring and Evaluation**: LangSmith allows close monitoring and evaluation of your application. This helps you to ensure the quality of your application and deploy it with confidence.\\n\\n2. **Tracing**: LangSmith has tracing capabilities that can be beneficial for debugging and understanding the behavior of your application.\\n\\n3. **Evaluation Capabilities**: LangSmith has built-in tools for evaluating the performance of your LLM. \\n\\n4. **Prompt Hub**: This is a prompt management tool built into LangSmith that can help in testing different prompts and their responses.\\n\\nPlease note that to use LangSmith, you would need to install it and create an API key. The platform offers Python and Typescript SDKs for utilization. It works independently and does not require the use of LangChain.'}"
]
},
- "execution_count": 29,
+ "execution_count": 26,
"metadata": {},
"output_type": "execute_result"
}
@@ -420,12 +570,14 @@
"id": "f2d94242",
"metadata": {},
"source": [
- "Example LangSmith trace: https://smith.langchain.com/public/e14254af-0cc1-4a99-bb24-15c50c559dd7/r"
+ "Let's take a look at the [LangSmith trace](https://smith.langchain.com/public/762153f6-14d4-4c98-8659-82650f860c62/r) to make sure it's actually calling that.\n",
+ "\n",
+ "Now let's try one where it needs to call the search tool:"
]
},
{
"cell_type": "code",
- "execution_count": 30,
+ "execution_count": 27,
"id": "77c2f769",
"metadata": {},
"outputs": [
@@ -433,10 +585,10 @@
"data": {
"text/plain": [
"{'input': 'whats the weather in sf?',\n",
- " 'output': 'The current weather in San Francisco is overcast with a temperature of 61.0°F (16.1°C). The wind speed is 25.9 kph coming from the south-southwest direction. The humidity is at 58%, and there is no precipitation at the moment.'}"
+ " 'output': 'The current weather in San Francisco is partly cloudy with a temperature of 16.1°C (61.0°F). The wind is coming from the WNW at a speed of 10.5 mph. The humidity is at 67%. [source](https://www.weatherapi.com/)'}"
]
},
- "execution_count": 30,
+ "execution_count": 27,
"metadata": {},
"output_type": "execute_result"
}
@@ -450,7 +602,7 @@
"id": "c174f838",
"metadata": {},
"source": [
- "Example LangSmith trace: https://smith.langchain.com/public/a04a067b-3bf4-47b3-acec-4055ff33a87a/r"
+ "We can check out the [LangSmith trace](https://smith.langchain.com/public/36df5b1a-9a0b-4185-bae2-964e1d53c665/r) to make sure it's calling the search tool effectively."
]
},
{
@@ -465,7 +617,7 @@
},
{
"cell_type": "code",
- "execution_count": 31,
+ "execution_count": 28,
"id": "c4073e35",
"metadata": {},
"outputs": [
@@ -477,7 +629,7 @@
" 'output': 'Hello Bob! How can I assist you today?'}"
]
},
- "execution_count": 31,
+ "execution_count": 28,
"metadata": {},
"output_type": "execute_result"
}
@@ -489,7 +641,7 @@
},
{
"cell_type": "code",
- "execution_count": 32,
+ "execution_count": 29,
"id": "9dc5ed68",
"metadata": {},
"outputs": [],
@@ -499,7 +651,7 @@
},
{
"cell_type": "code",
- "execution_count": 33,
+ "execution_count": 30,
"id": "550e0c6e",
"metadata": {},
"outputs": [
@@ -509,10 +661,10 @@
"{'chat_history': [HumanMessage(content='hi! my name is bob'),\n",
" AIMessage(content='Hello Bob! How can I assist you today?')],\n",
" 'input': \"what's my name?\",\n",
- " 'output': 'Your name is Bob. How can I assist you further, Bob?'}"
+ " 'output': 'Your name is Bob. How can I assist you further?'}"
]
},
- "execution_count": 33,
+ "execution_count": 30,
"metadata": {},
"output_type": "execute_result"
}
@@ -534,42 +686,50 @@
"id": "07b3bcf2",
"metadata": {},
"source": [
- "If we want to keep track of these messages automatically, we can wrap this in a RunnableWithMessageHistory. For more information on how to use this, see [this guide](/docs/expression_language/how_to/message_history/)."
+ "If we want to keep track of these messages automatically, we can wrap this in a RunnableWithMessageHistory. For more information on how to use this, see [this guide](/docs/expression_language/how_to/message_history/). "
]
},
{
"cell_type": "code",
- "execution_count": 34,
+ "execution_count": 36,
"id": "8edd96e6",
"metadata": {},
"outputs": [],
"source": [
"from langchain_community.chat_message_histories import ChatMessageHistory\n",
- "from langchain_core.runnables.history import RunnableWithMessageHistory"
+ "from langchain_core.chat_history import BaseChatMessageHistory\n",
+ "from langchain_core.runnables.history import RunnableWithMessageHistory\n",
+ "\n",
+ "store = {}\n",
+ "\n",
+ "\n",
+ "def get_session_history(session_id: str) -> BaseChatMessageHistory:\n",
+ " if session_id not in store:\n",
+ " store[session_id] = ChatMessageHistory()\n",
+ " return store[session_id]"
]
},
{
- "cell_type": "code",
- "execution_count": 35,
- "id": "6e76552a",
+ "cell_type": "markdown",
+ "id": "c450d6a5",
"metadata": {},
- "outputs": [],
"source": [
- "message_history = ChatMessageHistory()"
+ "Because we have multiple inputs, we need to specify two things:\n",
+ "\n",
+ "- `input_messages_key`: The input key to use to add to the conversation history.\n",
+ "- `history_messages_key`: The key to add the loaded messages into."
]
},
{
"cell_type": "code",
- "execution_count": 36,
+ "execution_count": 37,
"id": "828d1e95",
"metadata": {},
"outputs": [],
"source": [
"agent_with_chat_history = RunnableWithMessageHistory(\n",
" agent_executor,\n",
- " # This is needed because in most real world scenarios, a session id is needed\n",
- " # It isn't really used here because we are using a simple in memory ChatMessageHistory\n",
- " lambda session_id: message_history,\n",
+ " get_session_history,\n",
" input_messages_key=\"input\",\n",
" history_messages_key=\"chat_history\",\n",
")"
@@ -577,7 +737,7 @@
},
{
"cell_type": "code",
- "execution_count": 37,
+ "execution_count": 38,
"id": "1f5932b6",
"metadata": {},
"outputs": [
@@ -589,7 +749,7 @@
" 'output': 'Hello Bob! How can I assist you today?'}"
]
},
- "execution_count": 37,
+ "execution_count": 38,
"metadata": {},
"output_type": "execute_result"
}
@@ -597,15 +757,13 @@
"source": [
"agent_with_chat_history.invoke(\n",
" {\"input\": \"hi! I'm bob\"},\n",
- " # This is needed because in most real world scenarios, a session id is needed\n",
- " # It isn't really used here because we are using a simple in memory ChatMessageHistory\n",
" config={\"configurable\": {\"session_id\": \"\"}},\n",
")"
]
},
{
"cell_type": "code",
- "execution_count": 38,
+ "execution_count": 39,
"id": "ae627966",
"metadata": {},
"outputs": [
@@ -615,10 +773,10 @@
"{'input': \"what's my name?\",\n",
" 'chat_history': [HumanMessage(content=\"hi! I'm bob\"),\n",
" AIMessage(content='Hello Bob! How can I assist you today?')],\n",
- " 'output': 'Your name is Bob! How can I help you, Bob?'}"
+ " 'output': 'Your name is Bob.'}"
]
},
- "execution_count": 38,
+ "execution_count": 39,
"metadata": {},
"output_type": "execute_result"
}
@@ -626,8 +784,6 @@
"source": [
"agent_with_chat_history.invoke(\n",
" {\"input\": \"what's my name?\"},\n",
- " # This is needed because in most real world scenarios, a session id is needed\n",
- " # It isn't really used here because we are using a simple in memory ChatMessageHistory\n",
" config={\"configurable\": {\"session_id\": \"\"}},\n",
")"
]
diff --git a/docs/versioned_docs/version-0.2.x/tutorials/chatbot.ipynb b/docs/versioned_docs/version-0.2.x/tutorials/chatbot.ipynb
index 82aa2a2a61325..9ce8739dc6065 100644
--- a/docs/versioned_docs/version-0.2.x/tutorials/chatbot.ipynb
+++ b/docs/versioned_docs/version-0.2.x/tutorials/chatbot.ipynb
@@ -42,6 +42,7 @@
"- [`Chat Models`](/docs/concepts/#chat-models). The chatbot interface is based around messages rather than raw text, and therefore is best suited to Chat Models rather than text LLMs.\n",
"- [`Prompt Templates`](/docs/concepts/#prompt-templates), which simplify the process of assembling prompts that combine default messages, user input, chat history, and (optionally) additional retrieved context.\n",
"- [`Chat History`](/docs/concepts/#chat-history), which allows a chatbot to \"remember\" past interactions and take them into account when responding to followup questions. \n",
+ "- Debugging and tracing your application using [LangSmith](/docs/concepts/#langsmith)\n",
"\n",
"We'll cover how to fit the above components together to create a powerful conversational chatbot.\n",
"\n",
@@ -186,7 +187,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "Let's take a look at the example LangSmith trace here: https://smith.langchain.com/public/5c21cb92-2814-4119-bae9-d02b8db577ac/r\n",
+ "Let's take a look at the example [LangSmith trace](https://smith.langchain.com/public/5c21cb92-2814-4119-bae9-d02b8db577ac/r)\n",
"\n",
"We can see that it doesn't take the previous conversation turn into context, and cannot answer the question.\n",
"This makes for a terrible chatbot experience!\n",
@@ -717,6 +718,209 @@
"To help you understand what's happening internally, check out [this LangSmith trace](https://smith.langchain.com/public/f48fabb6-6502-43ec-8242-afc352b769ed/r)"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Managing Conversation History\n",
+ "\n",
+ "One important concept to understand when building chatbots is how to manage conversation history. If left unmanaged, the list of messages will grow unbounded and potentially overflow the context window of the LLM. Therefore, it is important to add a step that limits the size of the messages you are passing in.\n",
+ "\n",
+ "**Importantly, you will want to do this BEFORE the prompt template but AFTER you load previous messages from Message History.**\n",
+ "\n",
+ "We can do this by adding a simple step in front of the prompt that modifies the `messages` key appropriately, and then wrap that new chain in the Message History class. First, let's define a function that will modify the messages passed in. Let's make it so that it selects the `k` most recent messages. We can then create a new chain by adding that at the start."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 42,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from langchain_core.runnables import RunnablePassthrough\n",
+ "\n",
+ "\n",
+ "def filter_messages(messages, k=10):\n",
+ " return messages[-k:]\n",
+ "\n",
+ "\n",
+ "chain = RunnablePassthrough.assign(\n",
+ " messages=lambda x: filter_messages(x['messages'])\n",
+ ") | prompt | model"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let's now try it out! If we create a list of messages more than 10 messages long, we can see what it no longer remembers information in the early messages."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 43,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "messages = [\n",
+ " HumanMessage(content=\"hi! I'm bob\"),\n",
+ " AIMessage(content=\"hi!\"),\n",
+ " HumanMessage(content=\"I like vanilla ice cream\"),\n",
+ " AIMessage(content=\"nice\"),\n",
+ " HumanMessage(content=\"whats 2 + 2\"),\n",
+ " AIMessage(content=\"4\"),\n",
+ " HumanMessage(content=\"thanks\"),\n",
+ " AIMessage(content=\"no problem!\"),\n",
+ " HumanMessage(content=\"having fun?\"),\n",
+ " AIMessage(content=\"yes!\"),\n",
+ "]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 47,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "\"I'm sorry, I don’t have access to your name. Can I help you with anything else?\""
+ ]
+ },
+ "execution_count": 47,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "response = chain.invoke({\n",
+ " \"messages\": messages + [HumanMessage(content=\"what's my name?\")],\n",
+ " \"language\": \"English\"\n",
+ "})\n",
+ "response.content"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "But if we ask about information that is within the last ten messages, it still remembers it"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 48,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "'You mentioned that you like vanilla ice cream.'"
+ ]
+ },
+ "execution_count": 48,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "response = chain.invoke({\n",
+ " \"messages\": messages + [HumanMessage(content=\"what's my fav ice cream\")],\n",
+ " \"language\": \"English\"\n",
+ "})\n",
+ "response.content"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let's now wrap this in the Message History"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 55,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "with_message_history = RunnableWithMessageHistory(\n",
+ " chain,\n",
+ " get_session_history,\n",
+ " input_messages_key=\"messages\",\n",
+ ")\n",
+ "\n",
+ "config = {\"configurable\": {\"session_id\": \"abc20\"}}"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 57,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "\"I'm sorry, I don't know your name.\""
+ ]
+ },
+ "execution_count": 57,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "response = with_message_history.invoke({\n",
+ " \"messages\": messages + [HumanMessage(content=\"whats my name?\")],\n",
+ " \"language\": \"English\"\n",
+ " },\n",
+ " config=config,\n",
+ ")\n",
+ "\n",
+ "response.content"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "There's now two new messages in the chat history. This means that even more information that used to be accessible in our conversation history is no longer available!"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 58,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "\"I'm sorry, I don't know your favorite ice cream flavor.\""
+ ]
+ },
+ "execution_count": 58,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "response = with_message_history.invoke({\n",
+ " \"messages\": [HumanMessage(content=\"whats my favorite ice cream?\")],\n",
+ " \"language\": \"English\"\n",
+ " },\n",
+ " config=config,\n",
+ ")\n",
+ "\n",
+ "response.content"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "If you take a look at LangSmith, you can see exactly what is happening under the hood in the [LangSmith trace](https://smith.langchain.com/public/fa6b00da-bcd8-4c1c-a799-6b32a3d62964/r)"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
diff --git a/docs/versioned_docs/version-0.2.x/tutorials/llm_chain.ipynb b/docs/versioned_docs/version-0.2.x/tutorials/llm_chain.ipynb
index ab0ba7d765da3..6bc0770def8f0 100644
--- a/docs/versioned_docs/version-0.2.x/tutorials/llm_chain.ipynb
+++ b/docs/versioned_docs/version-0.2.x/tutorials/llm_chain.ipynb
@@ -167,7 +167,7 @@
"id": "f83373db",
"metadata": {},
"source": [
- "If we've enable LangSmith, we can see that this run is logged to LangSmith. The trace should look something like: https://smith.langchain.com/public/88baa0b2-7c1a-4d09-ba30-a47985dde2ea/r"
+ "If we've enable LangSmith, we can see that this run is logged to LangSmith, and can see the [LangSmith trace](https://smith.langchain.com/public/88baa0b2-7c1a-4d09-ba30-a47985dde2ea/r)"
]
},
{
@@ -279,7 +279,7 @@
"id": "dd009096",
"metadata": {},
"source": [
- "If we now look at LangSmith, we can see that the chain has two steps: first the language model is called, then the result of that is passed to the output parser. We can see an example trace here: https://smith.langchain.com/public/f1bdf656-2739-42f7-ac7f-0f1dd712322f/r"
+ "If we now look at LangSmith, we can see that the chain has two steps: first the language model is called, then the result of that is passed to the output parser. We can see the [LangSmith trace]( https://smith.langchain.com/public/f1bdf656-2739-42f7-ac7f-0f1dd712322f/r)"
]
},
{
@@ -452,7 +452,7 @@
"id": "0b19cecb",
"metadata": {},
"source": [
- "If we take a look at the LangSmith trace, we can see all three components show up: https://smith.langchain.com/public/bc49bec0-6b13-4726-967f-dbd3448b786d/r"
+ "If we take a look at the LangSmith trace, we can see all three components show up in the [LangSmith trace](https://smith.langchain.com/public/bc49bec0-6b13-4726-967f-dbd3448b786d/r)"
]
},
{