diff --git a/docs/_scripts/copy_notebooks.py b/docs/_scripts/copy_notebooks.py index 60e6fde5f..684bac1d2 100644 --- a/docs/_scripts/copy_notebooks.py +++ b/docs/_scripts/copy_notebooks.py @@ -38,6 +38,7 @@ _MANUAL = { "subgraph.ipynb", "force-calling-a-tool-first.ipynb", "pass-run-time-values-to-tools.ipynb", + "tool-calling.ipynb", "tool-calling-errors.ipynb", "dynamic-returning-direct.ipynb", "managing-agent-steps.ipynb", diff --git a/docs/docs/how-tos/index.md b/docs/docs/how-tos/index.md index b4c4bdbf1..3f10c4499 100644 --- a/docs/docs/how-tos/index.md +++ b/docs/docs/how-tos/index.md @@ -54,12 +54,17 @@ These guides show how to use different streaming modes. - [How to stream events from within a tool without LangChain models](streaming-events-from-within-tools-without-langchain.ipynb) - [How to stream events from the final node](streaming-from-final-node.ipynb) +## Tool calling + +- [How to call tools using ToolNode](tool-calling.ipynb) +- [How to handle tool calling errors](tool-calling-errors.ipynb) + ## Other + - [How to run graph asynchronously](async.ipynb) - [How to visualize your graph](visualization.ipynb) - [How to add runtime configuration to your graph](configuration.ipynb) - [How to pass runtime values to tools](pass-run-time-values-to-tools.ipynb) -- [How to handle tool calling errors](tool-calling-errors.ipynb) - [How to use a Pydantic model as your state](state-model.ipynb) - [How to use a context object in state](state-context-key.ipynb) diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 86cc27b6b..ba725707c 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -152,12 +152,14 @@ nav: - Stream events from within tools: how-tos/streaming-events-from-within-tools.ipynb - Stream events from within tools without LangChain models: how-tos/streaming-events-from-within-tools-without-langchain.ipynb - Stream events from the final node: how-tos/streaming-from-final-node.ipynb + - Tool calling: + - Call tools using ToolNode: how-tos/tool-calling.ipynb + - Handle tool calling errors: how-tos/tool-calling-errors.ipynb - Other: - Run graph asynchronously: how-tos/async.ipynb - Visualize your graph: how-tos/visualization.ipynb - Add runtime configuration: how-tos/configuration.ipynb - Pass runtime values to tools: how-tos/pass-run-time-values-to-tools.ipynb - - Handle tool calling errors: how-tos/tool-calling-errors.ipynb - Use Pydantic model as state: how-tos/state-model.ipynb - Use a context object in state: how-tos/state-context-key.ipynb - Prebuilt ReAct Agent: diff --git a/examples/tool-calling-errors.ipynb b/examples/tool-calling-errors.ipynb index d294b0eec..677468309 100644 --- a/examples/tool-calling-errors.ipynb +++ b/examples/tool-calling-errors.ipynb @@ -27,11 +27,14 @@ "\n", "LLMs aren't perfect at calling tools. The model may try to call a tool that doesn't exist or fail to return arguments that match the requested schema. Strategies like keeping schemas simple, reducing the number of tools you pass at once, and having good names and descriptions can help mitigate this risk, but aren't foolproof.\n", "\n", - "This guide covers some ways to build error handling into your graphs to mitigate these failure modes.\n", - "\n", - "## Using the prebuilt `ToolNode`\n", - "\n", - "To start, define a mock weather tool that has some hidden restrictions on input queries. The intent here is to simulate a real-world case where a model fails to call a tool correctly:" + "This guide covers some ways to build error handling into your graphs to mitigate these failure modes." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Setup" ] }, { @@ -48,6 +51,41 @@ "cell_type": "code", "execution_count": 2, "metadata": {}, + "outputs": [ + { + "name": "stdin", + "output_type": "stream", + "text": [ + "ANTHROPIC_API_KEY: ········\n" + ] + } + ], + "source": [ + "import getpass\n", + "import os\n", + "\n", + "\n", + "def _set_env(var: str):\n", + " if not os.environ.get(var):\n", + " os.environ[var] = getpass.getpass(f\"{var}: \")\n", + "\n", + "\n", + "_set_env(\"ANTHROPIC_API_KEY\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Using the prebuilt `ToolNode`\n", + "\n", + "To start, define a mock weather tool that has some hidden restrictions on input queries. The intent here is to simulate a real-world case where a model fails to call a tool correctly:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, "outputs": [], "source": [ "from langchain_core.tools import tool\n", @@ -59,7 +97,7 @@ " if location == \"san francisco\":\n", " raise ValueError(\"Input queries must be proper nouns\")\n", " elif location == \"San Francisco\":\n", - " return [\"It's 60 degrees and foggy.\"]\n", + " return \"It's 60 degrees and foggy.\"\n", " else:\n", " raise ValueError(\"Invalid input.\")" ] @@ -73,7 +111,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -122,7 +160,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 5, "metadata": {}, "outputs": [ { @@ -155,7 +193,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 6, "metadata": {}, "outputs": [ { @@ -164,14 +202,14 @@ "text": [ "HUMAN: what is the weather in san francisco?\n", "\n", - "AI: [{'id': 'toolu_01GDtNZG4sNYveWJgHiESTfZ', 'input': {'location': 'san francisco'}, 'name': 'get_weather', 'type': 'tool_use'}]\n", + "AI: [{'id': 'toolu_01UagsLm5GKtdtJ6nZdADFSa', 'input': {'location': 'san francisco'}, 'name': 'get_weather', 'type': 'tool_use'}]\n", "\n", "TOOL: Error: ValueError('Input queries must be proper nouns')\n", " Please fix your mistakes.\n", "\n", - "AI: [{'text': 'Apologies, it looks like there was an issue with the weather lookup. Let me try that again with the proper format:', 'type': 'text'}, {'id': 'toolu_01QXcGRkbeZz6hgPvPqJg83D', 'input': {'location': 'San Francisco'}, 'name': 'get_weather', 'type': 'tool_use'}]\n", + "AI: [{'text': 'Apologies, it looks like there was an issue with the weather lookup. Let me try that again with the proper format:', 'type': 'text'}, {'id': 'toolu_01PwRKYxhbgW8pHnWbyubp94', 'input': {'location': 'San Francisco'}, 'name': 'get_weather', 'type': 'tool_use'}]\n", "\n", - "TOOL: [\"It's 60 degrees and foggy.\"]\n", + "TOOL: It's 60 degrees and foggy.\n", "\n", "AI: The current weather in San Francisco is 60 degrees and foggy.\n", "\n" @@ -201,7 +239,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 7, "metadata": {}, "outputs": [ { @@ -210,17 +248,17 @@ "text": [ "HUMAN: Write me an incredible haiku about water.\n", "\n", - "AI: [{'text': 'Here is a haiku about water:', 'type': 'text'}, {'id': 'toolu_0119DG2EyJUeSorhckUJfFFg', 'input': {'topic': ['water']}, 'name': 'master_haiku_generator', 'type': 'tool_use'}]\n", + "AI: [{'text': 'Here is a haiku about water:', 'type': 'text'}, {'id': 'toolu_018KUdKbJEiprjJdBGxDs4Zq', 'input': {'topic': ['water']}, 'name': 'master_haiku_generator', 'type': 'tool_use'}]\n", "\n", "TOOL: Error: ValidationError(model='master_haiku_generatorSchema', errors=[{'loc': ('request',), 'msg': 'field required', 'type': 'value_error.missing'}])\n", " Please fix your mistakes.\n", "\n", - "AI: [{'text': 'Oops, looks like I forgot to include all the required parameters. Let me try that again:', 'type': 'text'}, {'id': 'toolu_015QsViGoXd9UojDAkYdYYbZ', 'input': {'request': {'topic': ['water']}}, 'name': 'master_haiku_generator', 'type': 'tool_use'}]\n", + "AI: [{'text': 'Oops, let me try that again with the required parameters:', 'type': 'text'}, {'id': 'toolu_015ENNFtemedbmJdvkzT7PU1', 'input': {'request': {'topic': ['water']}}, 'name': 'master_haiku_generator', 'type': 'tool_use'}]\n", "\n", "TOOL: Error: ValidationError(model='master_haiku_generatorSchema', errors=[{'loc': ('request', 'topic'), 'msg': 'ensure this value has at least 3 items', 'type': 'value_error.list.min_items', 'ctx': {'limit_value': 3}}])\n", " Please fix your mistakes.\n", "\n", - "AI: [{'text': 'Hmm, it looks like the haiku generator requires at least 3 topics. Let me provide 3 related topics:', 'type': 'text'}, {'id': 'toolu_01SwQTZKgsKtTrVpxS7csVYk', 'input': {'request': {'topic': ['water', 'ocean', 'waves']}}, 'name': 'master_haiku_generator', 'type': 'tool_use'}]\n", + "AI: [{'text': 'Hmm, it looks like the haiku generator requires at least 3 topics. Let me provide 3 related topics:', 'type': 'text'}, {'id': 'toolu_015vKAc67QwAgoJxigueiyLi', 'input': {'request': {'topic': ['water', 'ocean', 'waves']}}, 'name': 'master_haiku_generator', 'type': 'tool_use'}]\n", "\n", "TOOL: Here is a haiku about water, ocean, and waves:\n", "\n", @@ -308,7 +346,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -446,12 +484,12 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 9, "metadata": {}, "outputs": [ { "data": { - "image/jpeg": "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", + "image/jpeg": "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", "text/plain": [ "" ] @@ -484,12 +522,26 @@ "name": "stdout", "output_type": "stream", "text": [ - "{'agent': {'messages': [AIMessage(content=[{'text': 'Here is a haiku about water:', 'type': 'text'}, {'id': 'toolu_0168U37RJ9bZyutRiXKwJ4ok', 'input': {'topic': ['water']}, 'name': 'master_haiku_generator', 'type': 'tool_use'}], response_metadata={'id': 'msg_01SWs7QU7xWh4jkPQWFnprvn', 'model': 'claude-3-haiku-20240307', 'stop_reason': 'tool_use', 'stop_sequence': None, 'usage': {'input_tokens': 384, 'output_tokens': 67}}, id='run-ce6f4997-f0a8-41e5-95de-d6d6a3503374-0', tool_calls=[{'name': 'master_haiku_generator', 'args': {'topic': ['water']}, 'id': 'toolu_0168U37RJ9bZyutRiXKwJ4ok', 'type': 'tool_call'}], usage_metadata={'input_tokens': 384, 'output_tokens': 67, 'total_tokens': 451})]}}\n", - "{'tools': {'messages': [ToolMessage(content='', additional_kwargs={'error': ValidationError(model='master_haiku_generatorSchema', errors=[{'loc': ('request',), 'msg': 'field required', 'type': 'value_error.missing'}])}, name='master_haiku_generator', id='bbf8da22-c68d-44e4-bf00-9588e6e0a460', tool_call_id='toolu_0168U37RJ9bZyutRiXKwJ4ok')]}}\n", - "{'remove_failed_tool_call_attempt': {'messages': [RemoveMessage(content='', id='run-ce6f4997-f0a8-41e5-95de-d6d6a3503374-0'), RemoveMessage(content='', id='bbf8da22-c68d-44e4-bf00-9588e6e0a460')]}}\n", - "{'fallback_agent': {'messages': [AIMessage(content=[{'text': 'Certainly! I\\'d be happy to help you create an incredible haiku about water. To do this, we\\'ll use the master_haiku_generator function, which requires three topics. Since you\\'ve specified water as the main theme, I\\'ll add two related concepts to create a more vivid and interesting haiku. Let\\'s use \"water,\" \"flow,\" and \"reflection\" as our three topics.\\n\\nHere\\'s the function call to generate your haiku:', 'type': 'text'}, {'id': 'toolu_01FyyeqbMJX2KpL2VEiPXz4x', 'input': {'request': {'topic': ['water', 'flow', 'reflection']}}, 'name': 'master_haiku_generator', 'type': 'tool_use'}], response_metadata={'id': 'msg_016dFzfnjfbVYwzq6styhXRx', 'model': 'claude-3-5-sonnet-20240620', 'stop_reason': 'tool_use', 'stop_sequence': None, 'usage': {'input_tokens': 414, 'output_tokens': 163}}, id='run-cfcc6636-ac20-487c-a8c0-a6c52c7c54c9-0', tool_calls=[{'name': 'master_haiku_generator', 'args': {'request': {'topic': ['water', 'flow', 'reflection']}}, 'id': 'toolu_01FyyeqbMJX2KpL2VEiPXz4x', 'type': 'tool_call'}], usage_metadata={'input_tokens': 414, 'output_tokens': 163, 'total_tokens': 577})]}}\n", - "{'tools': {'messages': [ToolMessage(content='\"Here is a haiku about water, flow, and reflection:\\\\n\\\\nRippling waters flow,\\\\nMirroring the sky above,\\\\nTranquil reflection.\"', name='master_haiku_generator', id='bafc723f-f9dc-417f-8e4b-72904e9ec382', tool_call_id='toolu_01FyyeqbMJX2KpL2VEiPXz4x')]}}\n", - "{'agent': {'messages': [AIMessage(content='I hope you enjoy this haiku about the beauty and serenity of water. Please let me know if you would like me to generate another one.', response_metadata={'id': 'msg_016Viv8MvnAw6GEZyjwxPGVt', 'model': 'claude-3-haiku-20240307', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 599, 'output_tokens': 35}}, id='run-cf42682e-be5b-4201-88b5-e7d51c4fcd5b-0', usage_metadata={'input_tokens': 599, 'output_tokens': 35, 'total_tokens': 634})]}}\n" + "{'agent': {'messages': [AIMessage(content=[{'text': 'Here is a haiku about water:', 'type': 'text'}, {'id': 'toolu_01DHSAkgSaCR1hrpAx1cKbJs', 'input': {'topic': ['water']}, 'name': 'master_haiku_generator', 'type': 'tool_use'}], response_metadata={'id': 'msg_015dn3iAZDnBxPCzDo8eWSub', 'model': 'claude-3-haiku-20240307', 'stop_reason': 'tool_use', 'stop_sequence': None, 'usage': {'input_tokens': 384, 'output_tokens': 67}}, id='run-8b01a16f-1dcd-4d4b-b183-725f8e7a2865-0', tool_calls=[{'name': 'master_haiku_generator', 'args': {'topic': ['water']}, 'id': 'toolu_01DHSAkgSaCR1hrpAx1cKbJs', 'type': 'tool_call'}], usage_metadata={'input_tokens': 384, 'output_tokens': 67, 'total_tokens': 451})]}}\n", + "{'tools': {'messages': [ToolMessage(content='', additional_kwargs={'error': ValidationError(model='master_haiku_generatorSchema', errors=[{'loc': ('request',), 'msg': 'field required', 'type': 'value_error.missing'}])}, name='master_haiku_generator', id='b4761ee1-4d73-482b-85a0-6c114463ab1d', tool_call_id='toolu_01DHSAkgSaCR1hrpAx1cKbJs')]}}\n", + "{'remove_failed_tool_call_attempt': {'messages': [RemoveMessage(content='', id='run-8b01a16f-1dcd-4d4b-b183-725f8e7a2865-0'), RemoveMessage(content='', id='b4761ee1-4d73-482b-85a0-6c114463ab1d')]}}\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/vadymbarda/.virtualenvs/langgraph/lib/python3.11/site-packages/langchain_core/_api/beta_decorator.py:87: LangChainBetaWarning: The class `RemoveMessage` is in beta. It is actively being worked on, so the API may change.\n", + " warn_beta(\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'fallback_agent': {'messages': [AIMessage(content=[{'text': 'Certainly! I\\'d be happy to help you create an incredible haiku about water. To do this, I\\'ll use the master_haiku_generator function, which requires three topics. Since you\\'ve specified water as the main theme, I\\'ll add two related concepts to create a more vivid and interesting haiku. Let\\'s use \"water,\" \"flow,\" and \"reflection\" as our three topics.', 'type': 'text'}, {'id': 'toolu_01THSivCtMnx6P7oVy4eqywy', 'input': {'request': {'topic': ['water', 'flow', 'reflection']}}, 'name': 'master_haiku_generator', 'type': 'tool_use'}], response_metadata={'id': 'msg_01HQQbQ8YjSKn37kQYSwKn8D', 'model': 'claude-3-5-sonnet-20240620', 'stop_reason': 'tool_use', 'stop_sequence': None, 'usage': {'input_tokens': 414, 'output_tokens': 158}}, id='run-c0ceb996-d5f4-4d28-9844-095c83b74ebe-0', tool_calls=[{'name': 'master_haiku_generator', 'args': {'request': {'topic': ['water', 'flow', 'reflection']}}, 'id': 'toolu_01THSivCtMnx6P7oVy4eqywy', 'type': 'tool_call'}], usage_metadata={'input_tokens': 414, 'output_tokens': 158, 'total_tokens': 572})]}}\n", + "{'tools': {'messages': [ToolMessage(content='\"Here is a haiku about water, flow, and reflection:\\\\n\\\\nRippling waters flow,\\\\nMirroring the sky above,\\\\nTranquil reflection.\"', name='master_haiku_generator', id='b7da21e3-bc09-4f4a-a25b-3df29db69589', tool_call_id='toolu_01THSivCtMnx6P7oVy4eqywy')]}}\n", + "{'agent': {'messages': [AIMessage(content='I hope you enjoy this haiku about the beauty and serenity of water. Please let me know if you would like me to generate another one.', response_metadata={'id': 'msg_01KZc2GPbh7xVHUQVpJoWkMK', 'model': 'claude-3-haiku-20240307', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 587, 'output_tokens': 35}}, id='run-89d4dfce-ae04-471f-894e-7d632feeb5cb-0', usage_metadata={'input_tokens': 587, 'output_tokens': 35, 'total_tokens': 622})]}}\n" ] } ], @@ -521,9 +573,9 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "langgraph", "language": "python", - "name": "python3" + "name": "langgraph" }, "language_info": { "codemirror_mode": { @@ -535,9 +587,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.3" + "version": "3.11.9" } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } diff --git a/examples/tool-calling.ipynb b/examples/tool-calling.ipynb new file mode 100644 index 000000000..e6b088ed5 --- /dev/null +++ b/examples/tool-calling.ipynb @@ -0,0 +1,491 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# How to call tools using ToolNode\n", + "\n", + "This guide covers how to use LangGraph's prebuilt [`ToolNode`](https://langchain-ai.github.io/langgraph/reference/prebuilt/#toolnode) for tool calling.\n", + "\n", + "`ToolNode` is a LangChain Runnable that takes graph state (with a list of messages) as input and outputs state update with the result of tool calls. It is designed to work well out-of-box with LangGraph's prebuilt [ReAct agent](https://langchain-ai.github.io/langgraph/how-tos/create-react-agent/), but can also work with any `StateGraph` as long as its state has a `messages` key with an appropriate reducer (see [`MessagesState`](https://github.com/langchain-ai/langgraph/blob/e3ef9adac7395e5c0943c22bbc8a4a856b103aa3/libs/langgraph/langgraph/graph/message.py#L150))." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Setup" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "%%capture --no-stderr\n", + "%pip install --quiet -U langgraph langchain_anthropic" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdin", + "output_type": "stream", + "text": [ + "ANTHROPIC_API_KEY: ········\n" + ] + } + ], + "source": [ + "import getpass\n", + "import os\n", + "\n", + "\n", + "def _set_env(var: str):\n", + " if not os.environ.get(var):\n", + " os.environ[var] = getpass.getpass(f\"{var}: \")\n", + "\n", + "\n", + "_set_env(\"ANTHROPIC_API_KEY\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Define tools" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "from langchain_core.messages import AIMessage\n", + "from langchain_core.tools import tool\n", + "\n", + "from langgraph.prebuilt import ToolNode" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "@tool\n", + "def get_weather(location: str):\n", + " \"\"\"Call to get the current weather.\"\"\"\n", + " if location.lower() in [\"sf\", \"san francisco\"]:\n", + " return \"It's 60 degrees and foggy.\"\n", + " else:\n", + " return \"It's 90 degrees and sunny.\"\n", + "\n", + "\n", + "@tool\n", + "def get_coolest_cities():\n", + " \"\"\"Get a list of coolest cities\"\"\"\n", + " return \"nyc, sf\"" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "tools = [get_weather, get_coolest_cities]\n", + "tool_node = ToolNode(tools)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Manually call `ToolNode`" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`ToolNode` operates on graph state with a list of messages. It expects the last message in the list to be an `AIMessage` with `tool_calls` parameter. \n", + "\n", + "Let's first see how to invoke the tool node manually:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'messages': [ToolMessage(content=\"It's 60 degrees and foggy.\", name='get_weather', tool_call_id='tool_call_id')]}" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "message_with_single_tool_call = AIMessage(\n", + " content=\"\",\n", + " tool_calls=[{'name': 'get_weather', 'args': {'location': 'sf'}, 'id': 'tool_call_id', 'type': 'tool_call'}]\n", + ")\n", + "\n", + "tool_node.invoke({\"messages\": [message_with_single_tool_call]})" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that typically you don't need to create `AIMessage` manually, and it will be automatically generated by any LangChain chat model that supports tool calling.\n", + "\n", + "You can also do parallel tool calling using `ToolNode` if you pass multiple tool calls to `AIMessage`'s `tool_calls` parameter:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'messages': [ToolMessage(content='nyc, sf', name='get_coolest_cities', tool_call_id='tool_call_id_1'),\n", + " ToolMessage(content=\"It's 60 degrees and foggy.\", name='get_weather', tool_call_id='tool_call_id_2')]}" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "message_with_multiple_tool_calls = AIMessage(\n", + " content=\"\",\n", + " tool_calls=[\n", + " {'name': 'get_coolest_cities', 'args': {}, 'id': 'tool_call_id_1', 'type': 'tool_call'},\n", + " {'name': 'get_weather', 'args': {'location': 'sf'}, 'id': 'tool_call_id_2', 'type': 'tool_call'}\n", + " ]\n", + ")\n", + "\n", + "tool_node.invoke({\"messages\": [message_with_multiple_tool_calls]})" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Using with chat models" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We'll be using a small chat model from Anthropic in our example. To use chat models with tool calling, we need to first ensure that the model is aware of the available tools. We do this by calling `.bind_tools` method on `ChatAnthropic` moodel" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "from typing import Literal\n", + "\n", + "from langchain_anthropic import ChatAnthropic\n", + "from langgraph.graph import StateGraph, MessagesState\n", + "from langgraph.prebuilt import ToolNode\n", + "\n", + "\n", + "\n", + "model_with_tools = ChatAnthropic(\n", + " model=\"claude-3-haiku-20240307\", temperature=0\n", + ").bind_tools(tools)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'name': 'get_weather',\n", + " 'args': {'location': 'San Francisco'},\n", + " 'id': 'toolu_01Fwm7dg1mcJU43Fkx2pqgm8',\n", + " 'type': 'tool_call'}]" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "model_with_tools.invoke(\"what's the weather in sf?\").tool_calls" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As you can see, the AI message generated by the chat model already has `tool_calls` populated, so we can just pass it directly to `ToolNode`" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'messages': [ToolMessage(content=\"It's 60 degrees and foggy.\", name='get_weather', tool_call_id='toolu_01LFvAVT3xJMeZS6kbWwBGZK')]}" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tool_node.invoke({\"messages\": [model_with_tools.invoke(\"what's the weather in sf?\")]})" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## ReAct Agent" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next, let's see how to use `ToolNode` inside a LangGraph graph. Let's set up a graph implementation of the [ReAct agent](https://langchain-ai.github.io/langgraph/concepts/agentic_concepts/#react-agent). This agent takes some query as input, then repeatedly call tools until it has enough information to resolve the query. We'll be using `ToolNode` and the Anthropic model with tools we just defined" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "from typing import Literal\n", + "\n", + "from langgraph.graph import StateGraph, MessagesState\n", + "\n", + "\n", + "def should_continue(state: MessagesState) -> Literal[\"tools\", \"__end__\"]:\n", + " messages = state[\"messages\"]\n", + " last_message = messages[-1]\n", + " if last_message.tool_calls:\n", + " return \"tools\"\n", + " return \"__end__\"\n", + "\n", + "\n", + "def call_model(state: MessagesState):\n", + " messages = state[\"messages\"]\n", + " response = model_with_tools.invoke(messages)\n", + " return {\"messages\": [response]}\n", + "\n", + "\n", + "workflow = StateGraph(MessagesState)\n", + "\n", + "# Define the two nodes we will cycle between\n", + "workflow.add_node(\"agent\", call_model)\n", + "workflow.add_node(\"tools\", tool_node)\n", + "\n", + "workflow.add_edge(\"__start__\", \"agent\")\n", + "workflow.add_conditional_edges(\n", + " \"agent\",\n", + " should_continue,\n", + ")\n", + "workflow.add_edge(\"tools\", \"agent\")\n", + "\n", + "app = workflow.compile()" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "image/jpeg": "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", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from IPython.display import Image, display\n", + "\n", + "try:\n", + " display(Image(app.get_graph().draw_mermaid_png()))\n", + "except Exception:\n", + " # This requires some extra dependencies and is optional\n", + " pass" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's try it out!" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "================================\u001b[1m Human Message \u001b[0m=================================\n", + "\n", + "what's the weather in sf?\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "[{'text': \"Okay, let's check the weather in San Francisco:\", 'type': 'text'}, {'id': 'toolu_01LdmBXYeccWKdPrhZSwFCDX', 'input': {'location': 'San Francisco'}, 'name': 'get_weather', 'type': 'tool_use'}]\n", + "Tool Calls:\n", + " get_weather (toolu_01LdmBXYeccWKdPrhZSwFCDX)\n", + " Call ID: toolu_01LdmBXYeccWKdPrhZSwFCDX\n", + " Args:\n", + " location: San Francisco\n", + "=================================\u001b[1m Tool Message \u001b[0m=================================\n", + "Name: get_weather\n", + "\n", + "It's 60 degrees and foggy.\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "The weather in San Francisco is currently 60 degrees with foggy conditions.\n" + ] + } + ], + "source": [ + "# example with a single tool call\n", + "for chunk in app.stream(\n", + " {\"messages\": [(\"human\", \"what's the weather in sf?\")]}, stream_mode=\"values\"\n", + "):\n", + " chunk[\"messages\"][-1].pretty_print()" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "================================\u001b[1m Human Message \u001b[0m=================================\n", + "\n", + "what's the weather in the coolest cities?\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "[{'text': \"Okay, let's find out the weather in the coolest cities:\", 'type': 'text'}, {'id': 'toolu_01LFZUWTccyveBdaSAisMi95', 'input': {}, 'name': 'get_coolest_cities', 'type': 'tool_use'}]\n", + "Tool Calls:\n", + " get_coolest_cities (toolu_01LFZUWTccyveBdaSAisMi95)\n", + " Call ID: toolu_01LFZUWTccyveBdaSAisMi95\n", + " Args:\n", + "=================================\u001b[1m Tool Message \u001b[0m=================================\n", + "Name: get_coolest_cities\n", + "\n", + "nyc, sf\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "[{'text': \"Now let's get the weather for those cities:\", 'type': 'text'}, {'id': 'toolu_01RHPQBhT1u6eDnPqqkGUpsV', 'input': {'location': 'nyc'}, 'name': 'get_weather', 'type': 'tool_use'}]\n", + "Tool Calls:\n", + " get_weather (toolu_01RHPQBhT1u6eDnPqqkGUpsV)\n", + " Call ID: toolu_01RHPQBhT1u6eDnPqqkGUpsV\n", + " Args:\n", + " location: nyc\n", + "=================================\u001b[1m Tool Message \u001b[0m=================================\n", + "Name: get_weather\n", + "\n", + "It's 90 degrees and sunny.\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "[{'id': 'toolu_01W5sFGF8PfgYzdY4CqT5c6e', 'input': {'location': 'sf'}, 'name': 'get_weather', 'type': 'tool_use'}]\n", + "Tool Calls:\n", + " get_weather (toolu_01W5sFGF8PfgYzdY4CqT5c6e)\n", + " Call ID: toolu_01W5sFGF8PfgYzdY4CqT5c6e\n", + " Args:\n", + " location: sf\n", + "=================================\u001b[1m Tool Message \u001b[0m=================================\n", + "Name: get_weather\n", + "\n", + "It's 60 degrees and foggy.\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "Based on the results, it looks like the weather in the coolest cities is:\n", + "- New York City: 90 degrees and sunny\n", + "- San Francisco: 60 degrees and foggy\n", + "\n", + "So the weather in the coolest cities is a mix of warm and cool temperatures, with some sunny and some foggy conditions.\n" + ] + } + ], + "source": [ + "# example with a multiple tool calls in succession\n", + "\n", + "for chunk in app.stream(\n", + " {\"messages\": [(\"human\", \"what's the weather in the coolest cities?\")]}, stream_mode=\"values\"\n", + "):\n", + " chunk[\"messages\"][-1].pretty_print()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`ToolNode` can also handle errors during tool execution. You can enable / disable this by setting `handle_tool_errors=True` (enabled by default). See our guide on handling errors in `ToolNode` [here](https://langchain-ai.github.io/langgraph/how-tos/tool-calling-errors/)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "langgraph", + "language": "python", + "name": "langgraph" + }, + "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.9" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +}