diff --git a/docs/cassettes/agent-handoffs_62ba5113-e972-41e6-8392-cc970d4eea72.msgpack.zlib b/docs/cassettes/agent-handoffs_62ba5113-e972-41e6-8392-cc970d4eea72.msgpack.zlib new file mode 100644 index 000000000..4bd93ef2e --- /dev/null +++ b/docs/cassettes/agent-handoffs_62ba5113-e972-41e6-8392-cc970d4eea72.msgpack.zlib @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/docs/cassettes/agent-handoffs_637d188c-e0d0-4c05-bb41-f007b4e17fb7.msgpack.zlib b/docs/cassettes/agent-handoffs_637d188c-e0d0-4c05-bb41-f007b4e17fb7.msgpack.zlib new file mode 100644 index 000000000..220e60417 --- /dev/null +++ b/docs/cassettes/agent-handoffs_637d188c-e0d0-4c05-bb41-f007b4e17fb7.msgpack.zlib @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/docs/cassettes/agent-handoffs_c4ccd402-a90d-4c94-906d-6d364c274192.msgpack.zlib b/docs/cassettes/agent-handoffs_c4ccd402-a90d-4c94-906d-6d364c274192.msgpack.zlib new file mode 100644 index 000000000..4b0efc892 --- /dev/null +++ b/docs/cassettes/agent-handoffs_c4ccd402-a90d-4c94-906d-6d364c274192.msgpack.zlib @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/docs/cassettes/agent-handoffs_f39f99b0-95c7-422f-96a6-e612fde186df.msgpack.zlib b/docs/cassettes/agent-handoffs_f39f99b0-95c7-422f-96a6-e612fde186df.msgpack.zlib new file mode 100644 index 000000000..7593980a3 --- /dev/null +++ b/docs/cassettes/agent-handoffs_f39f99b0-95c7-422f-96a6-e612fde186df.msgpack.zlib @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/docs/cassettes/multi-agent-multi-turn-convo_161e0cf1-d13a-4026-8f89-bdab67d1ad4d.msgpack.zlib b/docs/cassettes/multi-agent-multi-turn-convo_161e0cf1-d13a-4026-8f89-bdab67d1ad4d.msgpack.zlib index 25b20565b..4e2e0614d 100644 --- a/docs/cassettes/multi-agent-multi-turn-convo_161e0cf1-d13a-4026-8f89-bdab67d1ad4d.msgpack.zlib +++ b/docs/cassettes/multi-agent-multi-turn-convo_161e0cf1-d13a-4026-8f89-bdab67d1ad4d.msgpack.zlib @@ -1 +1 @@ -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 \ No newline at end of file +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 \ No newline at end of file diff --git a/docs/cassettes/multi-agent-network_26a0d4df-ff99-40f0-92a8-0b3f2c591040.msgpack.zlib b/docs/cassettes/multi-agent-network_26a0d4df-ff99-40f0-92a8-0b3f2c591040.msgpack.zlib index eb0081989..e225c1250 100644 --- a/docs/cassettes/multi-agent-network_26a0d4df-ff99-40f0-92a8-0b3f2c591040.msgpack.zlib +++ b/docs/cassettes/multi-agent-network_26a0d4df-ff99-40f0-92a8-0b3f2c591040.msgpack.zlib @@ -1 +1 @@ -eNqdVg1wFOUZDhKYULRR2sIwlPJ5lkZqdnN3e0kuiQeGuwAH+SGXSAgBz73d7+422dtv2d275JIeyt/IqIMcWNtRamtIchIjIUDFgqgU8KeIHQXUMAWrQsGCgFqQWsb03b07uAxMO+3O3O3u973/z/O+365IRLCiCkQa0SdIGlZYToMXNb4ioeClYaxqq3pCWAsSvmt+TV39prAiDP48qGmyWlpQwMoCTWQssQLNkVBBxFLABVmtAJ5lERtmunyEjx7L3txhCmFVZQNYNZWipg4TR8CXpMGLqZGEEatgxKIAlsC/iDSFjWAR4TYZKxrSwCTiWAkpGAyHsMSnBXiITpBYwxG6G9MBGnEkLGmKgNV8xAmacccaN41Gbj+KgiMJYx6pMuYEv8AhVQgENRVjQQpct560l49YtQXlZUh4WT4iqETJQ36ioCAW5eFWg0SDmG5uxtj7DwaCkA/CEgkHgkiQYDtkqCONgEFVJnrOBCqBUVjFSj4samFFQnl+QRLUYB6NqjGgiMCvoUZAUkFQbUlTkS+KJDaEaVM+MilExHrN1aiq4ZAplo+GQSGgVlaSAAiCVBLCrWAFw5ISgqAM7xyrCD4fZqVMY3pIptgSWAkRHov6UkDWKBvRhSR4tcBdZgFZEYqgESJ6OXjWmeBnRRXDrqopmA1lLEB0AD4LWeouzHSxvmZoBonA6WsdJi0qG/79YcngrO7u2rMuoKetC3iMEqrYFIulzKRY+L9bSGUCHQFNMywK4mvGnGYIKEQnroCTAkpaN1MaEgZK6dJAYk4R5JRLUzkKhkMG2Vme9YkYpdXTBCCKEADSi8hoTtCikYtgFUlES/IQ5Hx6N/kNqbQ6jRoEUdR3krUGQR/LtWTSitb5YAoQjRihYimsI9JkukkP6IEP47S+4PUC7b1ek86E/5pnPTiVcJuWZKkehs6JfEgPpewgwX8ttDxVT1eJQqMAoTHW66MSMYJ5GlWFVU3Pi0jwS6qkGhxyjLAilIlOIq8PNEHBvJGVkgGpkfOSWCy2JJYIQuEB2ye6gkTV4luGj7h+luMwcBtLHOEhsfiLgXZBzodJ5BdZDfdCL0nYoE+8twVjmWJFIYJ7klrxrawsiwJntHZBs0qkvlTvUXq5btzu1VOnjALFd9RAEOXugvlRmMUSstCFxbR1axulaqwgiTBbKZGFeHpkY3935oYMOIMRKjXn4z1J5S2ZMkSNd1exXE3dMJOswgXj3dD/RbbtmesKDFkhhOMJ5/wb3aU2r7tjaIuFLhkYZliNSly82+j2ncOUsaZEKWOKx58zb0nXR8RSQAvGuyyWEub5NHIre0BPC6srugAM/M5bidQR01kzL43iiawJXS4AJr6nAfP5yGJBLswhq9lqg7/SQmupxYxmV9X3OVN+6m+Kw0C9wkqqH7CoSOOe4IJhqQXzvc6bIr5HRxzS0eOHMUnBUUZUTKWiivctpDzJw5Vyu7Yn6UURJcBKQrvhNr5ZRxMOU0HakdqGqaKbBOdUSIVKWC3MltRWutK9kJiZspgps2VXGwXTE4tCSIDqGf+p4xyAtpjhevlGCY20YDj5n2fMyevVTBEFhyAa3f01Q10lcL1yc6G0LasuY7ebdw0XU3FGQJuKQurLN+6nTHSa1b62tDAl8PHBn8KLl7PbrPaiEsz4OF8RZv08z9nNXLEVFu0lZhv+gz56OLCiYycTRaNUzMHXixaND+aH2Da9rxyMpZApgkzL4IDjxDCP68I+F9FzUMuQrGCRsHy/cxblZLkgpuoMusUTrsbq8iq3s7cOgnQS0iLg9cdGjPR6Ob/XF3LcHy30Y7o4upC45xPR7Y8yfMjaUOj08PWN3ipW4ISZsuYtItKcCspSzDAlhWaGAdhoM22hLZTXU9/SRker5gTaIpEo42FxhJltxQGzfwHd4poznzS2+pVFvrZWWVjqdVr85c3VDXNbl9Y4q3xig1jR7olwxd55rMW1aE5rmMzy+6JewVUO2bBa0FFQpn9VwQxUHamGoKAhKL0dLKXWdDuUId6ogYMePv3K0Bz48KuRxGgZqtOLieEOR0mdoGFHNczfwSehBuGIwDui9c7KULPPrTJuz8JKvrJi5oJ5tda57vm2WrNYos1uVapqZtYV2r3l5owiMDbgbqoORWab3WDh9dD/z6heWkhl9jdVIye/cBMSUSXB7++pwwq0ULyXE0mYh0Gu4B7A3FPeGN9h95vthb5CG8OXlNiY4iKqosGzNW3t2jTo0k+BBCsCxyJcfHuQcZhKbTbGVIZCrMNeZDObje/g5T3J4/DALfumPJaTZVwjH6+vIpOKb99ztqEy+sDEbfzsl6Twp86m13M+/fH7C/b6ntp4vHtfwbp9d/d+MvSA3PDBn9aO1XLH+k6808Hsmzj93axVC3wjZ15+6/3RzND9x7+YIh09frQZn4r1zCD9i9t2Xj19+eAjX0+q/WXOx/euO3n6eebzkHn/sd13PBVft3nrs/0NL5w9eKA56sj64B7r1L6x97528etTvqjFu/jQhAvfX77nw/XrP3r9Yybr4Tc/8Xq0R5etuS2/N/BY95Nb7xooKR2x5kzVQE6lO2/Wm51P3/f55A33NO4W3xnz+olnFnVaNly84nh2Y2L6c5ZLkwtbp/TPGDp1ZMqh9p1vXfjysz1HZkTy2u+6NP3WMb8ffJt7qPriptjXc2+9z77DeS5v7t7drn7fiOw36CZl8Vfjn2i6I/fZpoHBR//s2Nvx+C4hZ/KZ7Byq9I39i/74UTAYWzV51tiymeen3VI66jbWf9xfV70toY3+5sS47Cnb9uY8Oq7lkalfZB9zb/jR3gvB2nl9D3esyD3RnJhaesV1Z/Gsg+8tW+lYVHkUqSsHyk8dnviXznN7dx6Yekf2yvXjrrzU/Wu0NFK9+3zJr7736tmfdXLrBuW1tvWjnGu/WZ099eyFpwunPph78idXv7t1WQt7gbGbRk1bd+TFp253t7e8vdk5rXuiu9WR19l55q7f5dPf/bV2+Z2j51089M/jBU21oYrTg7nHt7QovVt2fup+5eXuxGDT6f68wxM2HxpziM3PvZz4bOOkj1efeeYfzWLFC7+pjk0ec9g83TSQuyny3sQfupYNmEwuMvLDc6/F71l18IBy8geHR9ec+fZq1raALa/27P2XRntfXFObW6Ytbmo8X1n13OFvfQuO/TZr+4beh979G3V+QPli15BzxgiP50FPeE3Xjl80fnly/OZ75aFTkXNfrds651/7+bMLdq/1rWlv41yT/h6Jhd2HcMfA+O2JwdeArUNDI7NQ2+oT4qisrH8DKyiG7A== \ No newline at end of file +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 \ No newline at end of file diff --git a/docs/cassettes/multi-agent-network_29b47c57-ad05-4f10-83bf-c3ff6ff8eb93.msgpack.zlib b/docs/cassettes/multi-agent-network_29b47c57-ad05-4f10-83bf-c3ff6ff8eb93.msgpack.zlib new file mode 100644 index 000000000..b0d9e9264 --- /dev/null +++ b/docs/cassettes/multi-agent-network_29b47c57-ad05-4f10-83bf-c3ff6ff8eb93.msgpack.zlib @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/docs/cassettes/multi-agent-network_68a547d4-0a15-43bd-aeed-c9ba1dfe388f.msgpack.zlib b/docs/cassettes/multi-agent-network_68a547d4-0a15-43bd-aeed-c9ba1dfe388f.msgpack.zlib index 02061cf68..a10bdaf46 100644 --- a/docs/cassettes/multi-agent-network_68a547d4-0a15-43bd-aeed-c9ba1dfe388f.msgpack.zlib +++ b/docs/cassettes/multi-agent-network_68a547d4-0a15-43bd-aeed-c9ba1dfe388f.msgpack.zlib @@ -1 +1 @@ -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 \ No newline at end of file +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 \ No newline at end of file diff --git a/docs/docs/how-tos/agent-handoffs.ipynb b/docs/docs/how-tos/agent-handoffs.ipynb new file mode 100644 index 000000000..d17e12de9 --- /dev/null +++ b/docs/docs/how-tos/agent-handoffs.ipynb @@ -0,0 +1,1042 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "34d3d54e-9a2b-481e-bccd-74aca7a53f9a", + "metadata": {}, + "source": [ + "# How to implement handoffs between agents" + ] + }, + { + "cell_type": "markdown", + "id": "ef16392a-56de-4cda-9ae8-dff078b2ed87", + "metadata": {}, + "source": [ + "!!! info \"Prerequisites\"\n", + " This guide assumes familiarity with the following:\n", + "\n", + " - [Multi-agent systems](../../concepts/multi_agent)\n", + " - [Command](../../concepts/low_level/#command)\n", + " - [LangGraph Glossary](../../concepts/low_level/)\n", + " \n", + "\n", + "In multi-agent architectures, agents can be represented as graph nodes. Each agent node executes its step(s) and decides whether to finish execution or route to another agent, including potentially routing to itself (e.g., running in a loop). A natural pattern in multi-agent interactions is [handoffs](../../concepts/multi_agent#handoffs), where one agent hands off control to another. Handoffs allow you to specify:\n", + "\n", + "- **destination**: target agent to navigate to - node name in LangGraph\n", + "- **payload**: information to pass to that agent - state update in LangGraph\n", + "\n", + "To implement handoffs in LangGraph, agent nodes can return `Command` object that allows you to [combine both control flow and state updates](../command):\n", + "\n", + "```python\n", + "def agent(state) -> Command[Literal[\"agent\", \"another_agent\"]]:\n", + " # the condition for routing/halting can be anything, e.g. LLM tool call / structured output, etc.\n", + " goto = get_next_agent(...) # 'agent' / 'another_agent'\n", + " return Command(\n", + " # Specify which agent to call next\n", + " goto=goto,\n", + " # Update the graph state\n", + " update={\"my_state_key\": \"my_state_value\"}\n", + " )\n", + "```\n", + "\n", + "One of the most common agent types is a tool-calling agent. For those types of agents, one pattern is wrapping a handoff in a tool call, e.g.:\n", + "\n", + "```python\n", + "@tool\n", + "def transfer_to_bob(state):\n", + " \"\"\"Transfer to bob.\"\"\"\n", + " return Command(\n", + " goto=\"bob\",\n", + " update={\"my_state_key\": \"my_state_value\"},\n", + " # Each tool-calling agent is implemented as a subgraph.\n", + " # As a result, to navigate to another agent (a sibling sub-graph), \n", + " # we need to specify that navigation is w/ respect to the parent graph.\n", + " graph=Command.PARENT,\n", + " )\n", + "```\n", + "\n", + "This guide shows how you can:\n", + "\n", + "- implement handoffs using `Command`: agent node makes some decision (usually LLM-based), and explicitly returns a handoff via `Command`. These are useful when you need fine-grained control over how an agent routes to another agent. It could be well suited for implementing a supervisor agent in a supervisor architecture.\n", + "- implement handoffs using tools: a tool-calling agent has access to tools that can return a handoff via `Command`. The tool-executing node in the agent recognizes `Command` objects returned by the tools and routes accordingly. Handoff tool a general-purpose primitive that is useful in any multi-agent systems that contain tool-calling agents." + ] + }, + { + "cell_type": "markdown", + "id": "7a4274c8-204f-41e4-b7ef-c8d1bb8de02e", + "metadata": {}, + "source": [ + "## Setup" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "e060e7a2-e339-49a6-bfd0-071dba8a3131", + "metadata": {}, + "outputs": [], + "source": [ + "%%capture --no-stderr\n", + "%pip install -U langgraph langchain-anthropic" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "b4864843-00a1-4c88-9a7c-c34e6c31c548", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "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", + "id": "230aec8a-ed82-4b97-a52e-2131f6c295ed", + "metadata": {}, + "source": [ + "
\n", + "

Set up LangSmith for LangGraph development

\n", + "

\n", + " Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started here. \n", + "

\n", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "4157f016-ccce-4f3a-877c-d3b3cfd77ffe", + "metadata": {}, + "source": [ + "## Implement handoffs using `Command`" + ] + }, + { + "cell_type": "markdown", + "id": "43a75f43-cf79-4e5d-b2b2-fc8982bc84a8", + "metadata": {}, + "source": [ + "Let's implement a system with two agents:\n", + "\n", + "- an addition expert (can only add numbers)\n", + "- a multiplication expert (can only multiply numbers).\n", + "\n", + "In this example the agents will be relying on the LLM for doing math. In a more realistic [follow-up example](#using-with-a-custom-agent), we will give the agents tools for doing math.\n", + "\n", + "When the addition expert needs help with multiplication, it hands off to the multiplication expert and vice-versa. This is an example of a simple multi-agent network.\n", + "\n", + "Each agent will have a corresponding node function that can conditionally return a `Command` object (e.g. our handoff). The node function will use an LLM with a system prompt and a tool that lets it signal when it needs to hand off to another agent. If the LLM responds with the tool calls, we will return a `Command(goto=)`.\n", + "\n", + "> **Note**: while we're using tools for the LLM to signal that it needs a handoff, the condition for the handoff can be anything: a specific response text from the LLM, structured output from the LLM, any other custom logic, etc." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "4e184beb-b9b2-4bd0-ac35-0356a8da46bc", + "metadata": {}, + "outputs": [], + "source": [ + "from typing_extensions import Literal\n", + "from langchain_core.messages import ToolMessage\n", + "from langchain_core.tools import tool\n", + "from langchain_anthropic import ChatAnthropic\n", + "from langgraph.graph import MessagesState, StateGraph, START\n", + "from langgraph.types import Command\n", + "\n", + "model = ChatAnthropic(model=\"claude-3-5-sonnet-latest\")\n", + "\n", + "\n", + "@tool\n", + "def transfer_to_multiplication_expert():\n", + " \"\"\"Ask multiplication agent for help.\"\"\"\n", + " # This tool is not returning anything: we're just using it\n", + " # as a way for LLM to signal that it needs to hand off to another agent\n", + " # (See the paragraph above)\n", + " return\n", + "\n", + "\n", + "@tool\n", + "def transfer_to_addition_expert():\n", + " \"\"\"Ask addition agent for help.\"\"\"\n", + " return\n", + "\n", + "\n", + "def addition_expert(\n", + " state: MessagesState,\n", + ") -> Command[Literal[\"multiplication_expert\", \"__end__\"]]:\n", + " system_prompt = (\n", + " \"You are an addition expert, you can ask the multiplication expert for help with multiplication. \"\n", + " \"Always do your portion of calculation before the handoff.\"\n", + " )\n", + " messages = [{\"role\": \"system\", \"content\": system_prompt}] + state[\"messages\"]\n", + " ai_msg = model.bind_tools([transfer_to_multiplication_expert]).invoke(messages)\n", + " # If there are tool calls, the LLM needs to hand off to another agent\n", + " if len(ai_msg.tool_calls) > 0:\n", + " tool_call_id = ai_msg.tool_calls[-1][\"id\"]\n", + " # NOTE: it's important to insert a tool message here because LLM providers are expecting\n", + " # all AI messages to be followed by a corresponding tool result message\n", + " tool_msg = {\n", + " \"role\": \"tool\",\n", + " \"content\": \"Successfully transferred\",\n", + " \"tool_call_id\": tool_call_id,\n", + " }\n", + " return Command(\n", + " goto=\"multiplication_expert\", update={\"messages\": [ai_msg, tool_msg]}\n", + " )\n", + "\n", + " # If the expert has an answer, return it directly to the user\n", + " return {\"messages\": [ai_msg]}\n", + "\n", + "\n", + "def multiplication_expert(\n", + " state: MessagesState,\n", + ") -> Command[Literal[\"addition_expert\", \"__end__\"]]:\n", + " system_prompt = (\n", + " \"You are a multiplication expert, you can ask an addition expert for help with addition. \"\n", + " \"Always do your portion of calculation before the handoff.\"\n", + " )\n", + " messages = [{\"role\": \"system\", \"content\": system_prompt}] + state[\"messages\"]\n", + " ai_msg = model.bind_tools([transfer_to_addition_expert]).invoke(messages)\n", + " if len(ai_msg.tool_calls) > 0:\n", + " tool_call_id = ai_msg.tool_calls[-1][\"id\"]\n", + " tool_msg = {\n", + " \"role\": \"tool\",\n", + " \"content\": \"Successfully transferred\",\n", + " \"tool_call_id\": tool_call_id,\n", + " }\n", + " return Command(goto=\"addition_expert\", update={\"messages\": [ai_msg, tool_msg]})\n", + "\n", + " return {\"messages\": [ai_msg]}" + ] + }, + { + "cell_type": "markdown", + "id": "921dc7bb-0c5b-410e-b143-601a549d529d", + "metadata": {}, + "source": [ + "Let's now combine both of these nodes into a single graph. Note that there are no edges between the agents! If the expert has an answer, it will return it directly to the user, otherwise it will route to the other expert for help." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "f56b6617-4226-4a7f-8234-59ebeaf53447", + "metadata": {}, + "outputs": [], + "source": [ + "builder = StateGraph(MessagesState)\n", + "builder.add_node(\"addition_expert\", addition_expert)\n", + "builder.add_node(\"multiplication_expert\", multiplication_expert)\n", + "# we'll always start with the addition expert\n", + "builder.add_edge(START, \"addition_expert\")\n", + "graph = builder.compile()" + ] + }, + { + "cell_type": "markdown", + "id": "d36574f3-7990-4ef2-b556-3bbd3625ec17", + "metadata": {}, + "source": [ + "Finally, let's define a helper function to render the streamed outputs nicely:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "3f8e4b2b-c761-445c-909b-3d206ac475c7", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain_core.messages import convert_to_messages\n", + "\n", + "\n", + "def pretty_print_messages(update):\n", + " if isinstance(update, tuple):\n", + " ns, update = update\n", + " # skip parent graph updates in the printouts\n", + " if len(ns) == 0:\n", + " return\n", + "\n", + " graph_id = ns[-1].split(\":\")[0]\n", + " print(f\"Update from subgraph {graph_id}:\")\n", + " print(\"\\n\")\n", + "\n", + " for node_name, node_update in update.items():\n", + " print(f\"Update from node {node_name}:\")\n", + " print(\"\\n\")\n", + "\n", + " for m in convert_to_messages(node_update[\"messages\"]):\n", + " m.pretty_print()\n", + " print(\"\\n\")" + ] + }, + { + "cell_type": "markdown", + "id": "01ff98c2-81ea-4679-8569-fed4750d5954", + "metadata": {}, + "source": [ + "Let's run the graph with an expression that requires both addition and multiplication:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "62ba5113-e972-41e6-8392-cc970d4eea72", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Update from node addition_expert:\n", + "\n", + "\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "[{'text': \"Let me help break this down:\\n\\nFirst, I'll handle the addition part since I'm the addition expert:\\n3 + 5 = 8\\n\\nNow, for the multiplication of 8 * 12, I'll need to ask the multiplication expert for help.\", 'type': 'text'}, {'id': 'toolu_015LCrsomHbeoQPtCzuff78Y', 'input': {}, 'name': 'transfer_to_multiplication_expert', 'type': 'tool_use'}]\n", + "Tool Calls:\n", + " transfer_to_multiplication_expert (toolu_015LCrsomHbeoQPtCzuff78Y)\n", + " Call ID: toolu_015LCrsomHbeoQPtCzuff78Y\n", + " Args:\n", + "=================================\u001b[1m Tool Message \u001b[0m=================================\n", + "\n", + "Successfully transferred\n", + "\n", + "\n", + "Update from node multiplication_expert:\n", + "\n", + "\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "[{'text': 'I see there was an error in my approach. I am actually the multiplication expert, and I need to ask the addition expert for help with (3 + 5) first.', 'type': 'text'}, {'id': 'toolu_01HFcB8WesPfDyrdgxoXApZk', 'input': {}, 'name': 'transfer_to_addition_expert', 'type': 'tool_use'}]\n", + "Tool Calls:\n", + " transfer_to_addition_expert (toolu_01HFcB8WesPfDyrdgxoXApZk)\n", + " Call ID: toolu_01HFcB8WesPfDyrdgxoXApZk\n", + " Args:\n", + "=================================\u001b[1m Tool Message \u001b[0m=================================\n", + "\n", + "Successfully transferred\n", + "\n", + "\n", + "Update from node addition_expert:\n", + "\n", + "\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "Now that I have the result of 3 + 5 = 8 from the addition expert, I can multiply 8 * 12:\n", + "\n", + "8 * 12 = 96\n", + "\n", + "So, (3 + 5) * 12 = 96\n", + "\n", + "\n" + ] + } + ], + "source": [ + "for chunk in graph.stream(\n", + " {\"messages\": [(\"user\", \"what's (3 + 5) * 12\")]},\n", + "):\n", + " pretty_print_messages(chunk)" + ] + }, + { + "cell_type": "markdown", + "id": "0088d791-1e03-49fb-b640-0ea01a7ef61d", + "metadata": {}, + "source": [ + "You can see that the addition expert first handled the expression in the parentheses, and then handed off to the multiplication expert to finish the calculation.\n", + "\n", + "Now let's see how we can implement this same system using special handoff tools and give our agents actual math tools." + ] + }, + { + "cell_type": "markdown", + "id": "c7a5d161-9f74-47a2-83ce-0cbe6073edcc", + "metadata": {}, + "source": [ + "## Implement handoffs using tools" + ] + }, + { + "cell_type": "markdown", + "id": "6ccfbd12-0a27-4e34-b6f7-701c2e7a6ffb", + "metadata": {}, + "source": [ + "### Implement a handoff tool" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "acaed37a-ddd3-4bf9-ac30-a9c5cc1ea3fe", + "metadata": {}, + "source": [ + "In the previous example we explicitly defined custom handoffs in each of the agent nodes. Another pattern is to create special **handoff tools** that directly return `Command` objects. When an agent calls a tool like this, it hands the control off to a different agent. Specifically, the tool-executing node in the agent recognizes the `Command` objects returned by the tools and routes control flow accordingly. **Note**: unlike the previous example, a tool-calling agent is not a single node but another graph that can be added to the multi-agent graph as a subgraph node.\n", + "\n", + "There are a few important considerations when implementing handoff tools:\n", + "\n", + "- since each agent is a __subgraph__ node in another graph, and the tools will be called in one of the agent subgraph nodes (e.g. tool executor), we need to specify `graph=Command.PARENT` in the `Command`, so that LangGraph knows to navigate outside of the agent subgraph\n", + "- we can optionally specify a state update that will be applied to the parent graph state before the next agent is called\n", + " - these state updates can be used to control [how much of the chat message history](../../concepts/multi_agent#shared-message-list) the target agent sees. For example, you might choose to just share the last AI messages from the current agent, or its full internal chat history, etc. In the examples below we'll be sharing the full internal chat history.\n", + "\n", + "- we can optionally provide the following to the tool (in the tool function signature):\n", + " - graph state (using [`InjectedState`][langgraph.prebuilt.tool_node.InjectedState])\n", + " - graph long-term memory (using [`InjectedStore`][langgraph.prebuilt.tool_node.InjectedStore])\n", + " - the current tool call ID (using [`InjectedToolCallId`](https://python.langchain.com/api_reference/core/tools/langchain_core.tools.base.InjectedToolCallId.html))\n", + " \n", + " These are not necessary but are useful for creating the state update passed to the next agent." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "d022072b-39bf-4133-aa62-e20f22bb4b17", + "metadata": {}, + "outputs": [], + "source": [ + "from typing import Annotated\n", + "\n", + "from langchain_core.tools import tool\n", + "from langchain_core.tools.base import InjectedToolCallId\n", + "from langgraph.prebuilt import InjectedState\n", + "\n", + "\n", + "def make_handoff_tool(*, agent_name: str):\n", + " \"\"\"Create a tool that can return handoff via a Command\"\"\"\n", + " tool_name = f\"transfer_to_{agent_name}\"\n", + "\n", + " @tool(tool_name)\n", + " def handoff_to_agent(\n", + " # # optionally pass current graph state to the tool (will be ignored by the LLM)\n", + " state: Annotated[dict, InjectedState],\n", + " # optionally pass the current tool call ID (will be ignored by the LLM)\n", + " tool_call_id: Annotated[str, InjectedToolCallId],\n", + " ):\n", + " \"\"\"Ask another agent for help.\"\"\"\n", + " tool_message = {\n", + " \"role\": \"tool\",\n", + " \"content\": f\"Successfully transferred to {agent_name}\",\n", + " \"name\": tool_name,\n", + " \"tool_call_id\": tool_call_id,\n", + " }\n", + " return Command(\n", + " # navigate to another agent node in the PARENT graph\n", + " goto=agent_name,\n", + " graph=Command.PARENT,\n", + " # This is the state update that the agent `agent_name` will see when it is invoked.\n", + " # We're passing agent's FULL internal message history AND adding a tool message to make sure\n", + " # the resulting chat history is valid. See the paragraph above for more information.\n", + " update={\"messages\": state[\"messages\"] + [tool_message]},\n", + " )\n", + "\n", + " return handoff_to_agent" + ] + }, + { + "cell_type": "markdown", + "id": "ba83e85a-576a-4e3f-8086-ce5e2c149c60", + "metadata": {}, + "source": [ + "### Using with a custom agent" + ] + }, + { + "cell_type": "markdown", + "id": "4d263776-3b2d-4ab0-9cc8-c7cbef22e2d5", + "metadata": {}, + "source": [ + "To demonstrate how to use handoff tools, let's first implement a simple version of the prebuilt [create_react_agent][langgraph.prebuilt.chat_agent_executor.create_react_agent]. This is useful in case you want to have a custom tool-calling agent implementation and want to leverage handoff tools." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "e5cd13c5-7dac-4dd7-9ffc-9f3467e28a44", + "metadata": {}, + "outputs": [], + "source": [ + "from typing_extensions import Literal\n", + "from langchain_core.messages import ToolMessage\n", + "from langchain_core.tools import tool\n", + "from langgraph.graph import MessagesState, StateGraph, START\n", + "from langgraph.types import Command\n", + "\n", + "\n", + "def make_agent(model, tools, system_prompt=None):\n", + " model_with_tools = model.bind_tools(tools)\n", + " tools_by_name = {tool.name: tool for tool in tools}\n", + "\n", + " def call_model(state: MessagesState) -> Command[Literal[\"call_tools\", \"__end__\"]]:\n", + " messages = state[\"messages\"]\n", + " if system_prompt:\n", + " messages = [{\"role\": \"system\", \"content\": system_prompt}] + messages\n", + "\n", + " response = model_with_tools.invoke(messages)\n", + " if len(response.tool_calls) > 0:\n", + " return Command(goto=\"call_tools\", update={\"messages\": [response]})\n", + "\n", + " return {\"messages\": [response]}\n", + "\n", + " # NOTE: this is a simplified version of the prebuilt ToolNode\n", + " # If you want to have a tool node that has full feature parity, please refer to the source code\n", + " def call_tools(state: MessagesState) -> Command[Literal[\"call_model\"]]:\n", + " tool_calls = state[\"messages\"][-1].tool_calls\n", + " results = []\n", + " for tool_call in tool_calls:\n", + " tool_ = tools_by_name[tool_call[\"name\"]]\n", + " tool_input_fields = tool_.get_input_schema().model_json_schema()[\n", + " \"properties\"\n", + " ]\n", + "\n", + " # this is simplified for demonstration purposes and\n", + " # is different from the ToolNode implementation\n", + " if \"state\" in tool_input_fields:\n", + " # inject state\n", + " tool_call = {**tool_call, \"args\": {**tool_call[\"args\"], \"state\": state}}\n", + "\n", + " tool_response = tool_.invoke(tool_call)\n", + " if isinstance(tool_response, ToolMessage):\n", + " results.append(\n", + " Command(goto=\"call_model\", update={\"messages\": [tool_response]})\n", + " )\n", + "\n", + " # handle tools that return Command directly\n", + " elif isinstance(tool_response, Command):\n", + " results.append(tool_response)\n", + "\n", + " # NOTE: nodes in LangGraph allow you to return list of updates, including Command objects\n", + " return results\n", + "\n", + " graph = StateGraph(MessagesState)\n", + " graph.add_node(call_model)\n", + " graph.add_node(call_tools)\n", + " graph.add_edge(START, \"call_model\")\n", + "\n", + " return graph.compile()" + ] + }, + { + "cell_type": "markdown", + "id": "8b7231d5-1e01-41a7-b260-d0495323d552", + "metadata": {}, + "source": [ + "Let's also define math tools that we'll give our agents:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "b9f8e553-8894-4d59-a069-1baa05d23289", + "metadata": {}, + "outputs": [], + "source": [ + "@tool\n", + "def add(a: int, b: int) -> int:\n", + " \"\"\"Adds two numbers.\"\"\"\n", + " return a + b\n", + "\n", + "\n", + "@tool\n", + "def multiply(a: int, b: int) -> int:\n", + " \"\"\"Multiplies two numbers.\"\"\"\n", + " return a * b" + ] + }, + { + "cell_type": "markdown", + "id": "e3f76999-2364-4e08-90be-62d4d138a8f4", + "metadata": {}, + "source": [ + "Let's test the agent implementation out to make sure it's working as expected:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "f39f99b0-95c7-422f-96a6-e612fde186df", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Update from node call_model:\n", + "\n", + "\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "[{'text': \"I'll help break this down into two steps:\\n1. First calculate 3 + 5\\n2. Then multiply that result by 12\\n\\nLet me make these calculations:\\n\\n1. Adding 3 and 5:\", 'type': 'text'}, {'id': 'toolu_01DUAzgWFqq6XZtj1hzHTka9', 'input': {'a': 3, 'b': 5}, 'name': 'add', 'type': 'tool_use'}]\n", + "Tool Calls:\n", + " add (toolu_01DUAzgWFqq6XZtj1hzHTka9)\n", + " Call ID: toolu_01DUAzgWFqq6XZtj1hzHTka9\n", + " Args:\n", + " a: 3\n", + " b: 5\n", + "\n", + "\n", + "Update from node call_tools:\n", + "\n", + "\n", + "=================================\u001b[1m Tool Message \u001b[0m=================================\n", + "Name: add\n", + "\n", + "8\n", + "\n", + "\n", + "Update from node call_model:\n", + "\n", + "\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "[{'text': '2. Multiplying the result (8) by 12:', 'type': 'text'}, {'id': 'toolu_01QXi1prSN4etgJ1QCuFJsgN', 'input': {'a': 8, 'b': 12}, 'name': 'multiply', 'type': 'tool_use'}]\n", + "Tool Calls:\n", + " multiply (toolu_01QXi1prSN4etgJ1QCuFJsgN)\n", + " Call ID: toolu_01QXi1prSN4etgJ1QCuFJsgN\n", + " Args:\n", + " a: 8\n", + " b: 12\n", + "\n", + "\n", + "Update from node call_tools:\n", + "\n", + "\n", + "=================================\u001b[1m Tool Message \u001b[0m=================================\n", + "Name: multiply\n", + "\n", + "96\n", + "\n", + "\n", + "Update from node call_model:\n", + "\n", + "\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "The result of (3 + 5) * 12 = 96\n", + "\n", + "\n" + ] + } + ], + "source": [ + "agent = make_agent(model, [add, multiply])\n", + "\n", + "for chunk in agent.stream({\"messages\": [(\"user\", \"what's (3 + 5) * 12\")]}):\n", + " pretty_print_messages(chunk)" + ] + }, + { + "cell_type": "markdown", + "id": "09689fde-7a54-4725-859f-b9e7d2725434", + "metadata": {}, + "source": [ + "Now, we can implement our multi-agent system with the multiplication and addition expert agents. This time we'll give them the tools for doing math, as well as our special handoff tools:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "af9e540a-e847-4ee3-b896-2b4dd93ecb34", + "metadata": {}, + "outputs": [], + "source": [ + "addition_expert = make_agent(\n", + " model,\n", + " [add, make_handoff_tool(agent_name=\"multiplication_expert\")],\n", + " system_prompt=\"You are an addition expert, you can ask the multiplication expert for help with multiplication.\",\n", + ")\n", + "multiplication_expert = make_agent(\n", + " model,\n", + " [multiply, make_handoff_tool(agent_name=\"addition_expert\")],\n", + " system_prompt=\"You are a multiplication expert, you can ask an addition expert for help with addition.\",\n", + ")\n", + "\n", + "builder = StateGraph(MessagesState)\n", + "builder.add_node(\"addition_expert\", addition_expert)\n", + "builder.add_node(\"multiplication_expert\", multiplication_expert)\n", + "builder.add_edge(START, \"addition_expert\")\n", + "graph = builder.compile()" + ] + }, + { + "cell_type": "markdown", + "id": "039ff31e-6559-437b-a500-f739b29c003b", + "metadata": {}, + "source": [ + "Let's run the graph with the same multi-step calculation input as before:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "c4ccd402-a90d-4c94-906d-6d364c274192", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Update from subgraph addition_expert:\n", + "\n", + "\n", + "Update from node call_model:\n", + "\n", + "\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "[{'text': \"I can help with the addition part (3 + 5), but I'll need to ask the multiplication expert for help with multiplying the result by 12. Let me break this down:\\n\\n1. First, let me calculate 3 + 5:\", 'type': 'text'}, {'id': 'toolu_01McaW4XWczLGKaetg88fxQ5', 'input': {'a': 3, 'b': 5}, 'name': 'add', 'type': 'tool_use'}]\n", + "Tool Calls:\n", + " add (toolu_01McaW4XWczLGKaetg88fxQ5)\n", + " Call ID: toolu_01McaW4XWczLGKaetg88fxQ5\n", + " Args:\n", + " a: 3\n", + " b: 5\n", + "\n", + "\n", + "Update from subgraph addition_expert:\n", + "\n", + "\n", + "Update from node call_tools:\n", + "\n", + "\n", + "=================================\u001b[1m Tool Message \u001b[0m=================================\n", + "Name: add\n", + "\n", + "8\n", + "\n", + "\n", + "Update from subgraph addition_expert:\n", + "\n", + "\n", + "Update from node call_model:\n", + "\n", + "\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "[{'text': \"Now that we have 8, we need to multiply it by 12. I'll ask the multiplication expert for help with this:\", 'type': 'text'}, {'id': 'toolu_01KpdUhHuyrmha62z5SduKRc', 'input': {}, 'name': 'transfer_to_multiplication_expert', 'type': 'tool_use'}]\n", + "Tool Calls:\n", + " transfer_to_multiplication_expert (toolu_01KpdUhHuyrmha62z5SduKRc)\n", + " Call ID: toolu_01KpdUhHuyrmha62z5SduKRc\n", + " Args:\n", + "\n", + "\n", + "Update from subgraph multiplication_expert:\n", + "\n", + "\n", + "Update from node call_model:\n", + "\n", + "\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "[{'text': 'Now that we have 8 as the result of the addition, I can help with the multiplication by 12:', 'type': 'text'}, {'id': 'toolu_01Vnp4k3TE87siad3BNJgRKb', 'input': {'a': 8, 'b': 12}, 'name': 'multiply', 'type': 'tool_use'}]\n", + "Tool Calls:\n", + " multiply (toolu_01Vnp4k3TE87siad3BNJgRKb)\n", + " Call ID: toolu_01Vnp4k3TE87siad3BNJgRKb\n", + " Args:\n", + " a: 8\n", + " b: 12\n", + "\n", + "\n", + "Update from subgraph multiplication_expert:\n", + "\n", + "\n", + "Update from node call_tools:\n", + "\n", + "\n", + "=================================\u001b[1m Tool Message \u001b[0m=================================\n", + "Name: multiply\n", + "\n", + "96\n", + "\n", + "\n", + "Update from subgraph multiplication_expert:\n", + "\n", + "\n", + "Update from node call_model:\n", + "\n", + "\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "The final result is 96.\n", + "\n", + "To break down the steps:\n", + "1. 3 + 5 = 8\n", + "2. 8 * 12 = 96\n", + "\n", + "\n" + ] + } + ], + "source": [ + "for chunk in graph.stream(\n", + " {\"messages\": [(\"user\", \"what's (3 + 5) * 12\")]}, subgraphs=True\n", + "):\n", + " pretty_print_messages(chunk)" + ] + }, + { + "cell_type": "markdown", + "id": "dd98eeae-5abd-45d9-aae7-4e714b7999dc", + "metadata": {}, + "source": [ + "We can see that after the addition expert is done with the first part of the calculation (after calling the `add` tool), it decides to hand off to the multiplication expert, which computes the final result." + ] + }, + { + "cell_type": "markdown", + "id": "102b116d-62f1-4570-afba-be9a96ce721f", + "metadata": {}, + "source": [ + "## Using with a prebuilt ReAct agent" + ] + }, + { + "cell_type": "markdown", + "id": "c46194ad-768d-4f29-85ce-91869a220107", + "metadata": {}, + "source": [ + "If you don't need extra customization, you can use the prebuilt [`create_react_agent`][langgraph.prebuilt.chat_agent_executor.create_react_agent], which includes built-in support for handoff tools through [`ToolNode`][langgraph.prebuilt.tool_node.ToolNode]." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "fe91541c-4c6f-42ef-858a-336bbbb96728", + "metadata": {}, + "outputs": [], + "source": [ + "from langgraph.prebuilt import create_react_agent\n", + "\n", + "addition_expert = create_react_agent(\n", + " model,\n", + " [add, make_handoff_tool(agent_name=\"multiplication_expert\")],\n", + " state_modifier=\"You are an addition expert, you can ask the multiplication expert for help with multiplication.\",\n", + ")\n", + "\n", + "multiplication_expert = create_react_agent(\n", + " model,\n", + " [multiply, make_handoff_tool(agent_name=\"addition_expert\")],\n", + " state_modifier=\"You are a multiplication expert, you can ask an addition expert for help with addition.\",\n", + ")\n", + "\n", + "builder = StateGraph(MessagesState)\n", + "builder.add_node(\"addition_expert\", addition_expert)\n", + "builder.add_node(\"multiplication_expert\", multiplication_expert)\n", + "builder.add_edge(START, \"addition_expert\")\n", + "graph = builder.compile()" + ] + }, + { + "cell_type": "markdown", + "id": "762fbd94-0e54-45cd-84fb-05a241bae679", + "metadata": {}, + "source": [ + "We can now verify that the prebuilt ReAct agent works exactly the same as the custom agent above:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "637d188c-e0d0-4c05-bb41-f007b4e17fb7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Update from subgraph addition_expert:\n", + "\n", + "\n", + "Update from node agent:\n", + "\n", + "\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "[{'text': \"I can help with the addition part of this calculation (3 + 5), and then I'll need to ask the multiplication expert for help with multiplying the result by 12.\\n\\nLet me first calculate 3 + 5:\", 'type': 'text'}, {'id': 'toolu_01GUasumGGJVXDV7TJEqEfmY', 'input': {'a': 3, 'b': 5}, 'name': 'add', 'type': 'tool_use'}]\n", + "Tool Calls:\n", + " add (toolu_01GUasumGGJVXDV7TJEqEfmY)\n", + " Call ID: toolu_01GUasumGGJVXDV7TJEqEfmY\n", + " Args:\n", + " a: 3\n", + " b: 5\n", + "\n", + "\n", + "Update from subgraph addition_expert:\n", + "\n", + "\n", + "Update from node tools:\n", + "\n", + "\n", + "=================================\u001b[1m Tool Message \u001b[0m=================================\n", + "Name: add\n", + "\n", + "8\n", + "\n", + "\n", + "Update from subgraph addition_expert:\n", + "\n", + "\n", + "Update from node agent:\n", + "\n", + "\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "[{'text': \"Now that we have 8, we need to multiply it by 12. Since I'm an addition expert, I'll transfer this to the multiplication expert to complete the calculation:\", 'type': 'text'}, {'id': 'toolu_014HEbwiH2jVno8r1Pc6t9Qh', 'input': {}, 'name': 'transfer_to_multiplication_expert', 'type': 'tool_use'}]\n", + "Tool Calls:\n", + " transfer_to_multiplication_expert (toolu_014HEbwiH2jVno8r1Pc6t9Qh)\n", + " Call ID: toolu_014HEbwiH2jVno8r1Pc6t9Qh\n", + " Args:\n", + "\n", + "\n", + "Update from subgraph multiplication_expert:\n", + "\n", + "\n", + "Update from node agent:\n", + "\n", + "\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "[{'text': 'I notice I made a mistake - I actually don\\'t have access to the \"add\" function or \"transfer_to_multiplication_expert\". Instead, I am the multiplication expert and I should ask the addition expert for help with the first part. Let me correct this:', 'type': 'text'}, {'id': 'toolu_01VAGpmr4ysHjvvuZp3q5Dzj', 'input': {}, 'name': 'transfer_to_addition_expert', 'type': 'tool_use'}]\n", + "Tool Calls:\n", + " transfer_to_addition_expert (toolu_01VAGpmr4ysHjvvuZp3q5Dzj)\n", + " Call ID: toolu_01VAGpmr4ysHjvvuZp3q5Dzj\n", + " Args:\n", + "\n", + "\n", + "Update from subgraph addition_expert:\n", + "\n", + "\n", + "Update from node agent:\n", + "\n", + "\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "[{'text': \"I'll help you with the addition part of (3 + 5) * 12. First, let me calculate 3 + 5:\", 'type': 'text'}, {'id': 'toolu_01RE16cRGVo4CC4wwHFB6gaE', 'input': {'a': 3, 'b': 5}, 'name': 'add', 'type': 'tool_use'}]\n", + "Tool Calls:\n", + " add (toolu_01RE16cRGVo4CC4wwHFB6gaE)\n", + " Call ID: toolu_01RE16cRGVo4CC4wwHFB6gaE\n", + " Args:\n", + " a: 3\n", + " b: 5\n", + "\n", + "\n", + "Update from subgraph addition_expert:\n", + "\n", + "\n", + "Update from node tools:\n", + "\n", + "\n", + "=================================\u001b[1m Tool Message \u001b[0m=================================\n", + "Name: add\n", + "\n", + "8\n", + "\n", + "\n", + "Update from subgraph addition_expert:\n", + "\n", + "\n", + "Update from node agent:\n", + "\n", + "\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "[{'text': \"Now that we have 8, we need to multiply it by 12. Since I'm an addition expert, I'll need to transfer this to the multiplication expert to complete the calculation:\", 'type': 'text'}, {'id': 'toolu_01HBDRh64SzGcCp7EX1u3MFa', 'input': {}, 'name': 'transfer_to_multiplication_expert', 'type': 'tool_use'}]\n", + "Tool Calls:\n", + " transfer_to_multiplication_expert (toolu_01HBDRh64SzGcCp7EX1u3MFa)\n", + " Call ID: toolu_01HBDRh64SzGcCp7EX1u3MFa\n", + " Args:\n", + "\n", + "\n", + "Update from subgraph multiplication_expert:\n", + "\n", + "\n", + "Update from node agent:\n", + "\n", + "\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "[{'text': 'Now that I have the result of 3 + 5 = 8, I can help with multiplying by 12:', 'type': 'text'}, {'id': 'toolu_014Ay95rsKvvbWWJV4CcZSPY', 'input': {'a': 8, 'b': 12}, 'name': 'multiply', 'type': 'tool_use'}]\n", + "Tool Calls:\n", + " multiply (toolu_014Ay95rsKvvbWWJV4CcZSPY)\n", + " Call ID: toolu_014Ay95rsKvvbWWJV4CcZSPY\n", + " Args:\n", + " a: 8\n", + " b: 12\n", + "\n", + "\n", + "Update from subgraph multiplication_expert:\n", + "\n", + "\n", + "Update from node tools:\n", + "\n", + "\n", + "=================================\u001b[1m Tool Message \u001b[0m=================================\n", + "Name: multiply\n", + "\n", + "96\n", + "\n", + "\n", + "Update from subgraph multiplication_expert:\n", + "\n", + "\n", + "Update from node agent:\n", + "\n", + "\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "The final result is 96. Here's the complete calculation:\n", + "(3 + 5) * 12 = 8 * 12 = 96\n", + "\n", + "\n" + ] + } + ], + "source": [ + "for chunk in graph.stream(\n", + " {\"messages\": [(\"user\", \"what's (3 + 5) * 12\")]}, subgraphs=True\n", + "):\n", + " pretty_print_messages(chunk)" + ] + } + ], + "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.12.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/docs/how-tos/index.md b/docs/docs/how-tos/index.md index f33d97340..cd4910aca 100644 --- a/docs/docs/how-tos/index.md +++ b/docs/docs/how-tos/index.md @@ -108,6 +108,7 @@ These how-to guides show common patterns for tool calling with LangGraph: [Multi-agent systems](../concepts/multi_agent.md) are useful to break down complex LLM applications into multiple agents, each responsible for a different part of the application. These how-to guides show how to implement multi-agent systems in LangGraph: +- [How to implement handoffs between agents](agent-handoffs.ipynb) - [How to build a multi-agent network](multi-agent-network.ipynb) - [How to add multi-turn conversation in a multi-agent application](multi-agent-multi-turn-convo.ipynb) diff --git a/docs/docs/how-tos/local-studio.md b/docs/docs/how-tos/local-studio.md index 8e4e29f89..270bd80c0 100644 --- a/docs/docs/how-tos/local-studio.md +++ b/docs/docs/how-tos/local-studio.md @@ -22,8 +22,14 @@ See [this guide](../concepts/application_structure.md) for information on how to You will need to install [`langgraph-cli`](../cloud/reference/cli.md#langgraph-cli) (version `0.1.55` or higher). You will need to make sure to install the `inmem` extras. +???+ note "Minimum version" + + The minimum version to use the `inmem` extra with `langgraph-cli` is `0.1.55`. + Python 3.11 or higher is required. + + ```shell -pip install "langgraph-cli[inmem]==0.1.55" +pip install -U "langgraph-cli[inmem]" ``` ## Run the development server diff --git a/docs/docs/how-tos/multi-agent-multi-turn-convo.ipynb b/docs/docs/how-tos/multi-agent-multi-turn-convo.ipynb index 7ffc7a759..fb73d3151 100644 --- a/docs/docs/how-tos/multi-agent-multi-turn-convo.ipynb +++ b/docs/docs/how-tos/multi-agent-multi-turn-convo.ipynb @@ -11,10 +11,11 @@ "!!! info \"Prerequisites\"\n", " This guide assumes familiarity with the following:\n", "\n", - " - [Node](../../concepts/low_level/#nodes)\n", - " - [Command](../../concepts/low_level/#command)\n", + " - [How to implement handoffs between agents](../agent-handoffs)\n", " - [Multi-agent systems](../../concepts/multi_agent)\n", " - [Human-in-the-loop](../../concepts/human_in_the_loop)\n", + " - [Command](../../concepts/low_level/#command)\n", + " - [LangGraph Glossary](../../concepts/low_level/)\n", "\n", "\n", "In this how-to guide, we’ll build an application that allows an end-user to engage in a *multi-turn conversation* with one or more agents. We'll create a node that uses an [`interrupt`](../../reference/types/#langgraph.types.interrupt) to collect user input and routes back to the **active** agent.\n", @@ -40,7 +41,8 @@ " \"content\": user_input,\n", " }]\n", " },\n", - " goto=active_agent,)\n", + " goto=active_agent\n", + " )\n", "\n", "def agent(state) -> Command[Literal[\"agent\", \"another_agent\", \"human\"]]:\n", " # The condition for routing/halting can be anything, e.g. LLM tool call / structured output, etc.\n", @@ -49,7 +51,6 @@ " return Command(goto=goto, update={\"my_state_key\": \"my_state_value\"})\n", " else:\n", " return Command(goto=\"human\") # Go to human node\n", - " )\n", "```" ] }, @@ -71,15 +72,23 @@ "outputs": [], "source": [ "%%capture --no-stderr\n", - "%pip install -U langgraph langchain-openai" + "%pip install -U langgraph langchain-anthropic" ] }, { "cell_type": "code", - "execution_count": 106, + "execution_count": 2, "id": "0bcff5d4-130e-426d-9285-40d0f72c7cd3", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdin", + "output_type": "stream", + "text": [ + "ANTHROPIC_API_KEY: ········\n" + ] + } + ], "source": [ "import getpass\n", "import os\n", @@ -90,7 +99,7 @@ " os.environ[var] = getpass.getpass(f\"{var}: \")\n", "\n", "\n", - "_set_env(\"OPENAI_API_KEY\")" + "_set_env(\"ANTHROPIC_API_KEY\")" ] }, { @@ -109,165 +118,171 @@ { "attachments": {}, "cell_type": "markdown", - "id": "6696b398-559d-4250-bb76-ebb7c97ce5f3", + "id": "c217c3fe-ca50-45a1-be91-912bc83ed8b3", "metadata": {}, "source": [ - "## Travel Recommendations Example\n", + "## Define agents\n", "\n", "In this example, we will build a team of travel assistant agents that can communicate with each other via handoffs.\n", "\n", - "We will create 3 agents:\n", + "We will create 2 agents:\n", + "\n", + "* `travel_advisor`: can help with travel destination recommendations. Can ask `hotel_advisor` for help.\n", + "* `hotel_advisor`: can help with hotel recommendations. Can ask `travel_advisor` for help.\n", + "\n", + "We will be using prebuilt [`create_react_agent`][langgraph.prebuilt.chat_agent_executor.create_react_agent] for the agents - each agent will have tools specific to its area of expertise as well as a special [tool for handoffs](../agent-handoffs#implementing-handoffs-using-tools) to another agent.\n", + "\n", + "First, let's define the tools we'll be using:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "eb51463a-4425-44ad-91d5-f21fd5b4e3b3", + "metadata": {}, + "outputs": [], + "source": [ + "import random\n", + "from typing import Annotated, Literal\n", + "\n", + "from langchain_core.tools import tool\n", + "from langchain_core.tools.base import InjectedToolCallId\n", + "from langgraph.prebuilt import InjectedState\n", + "\n", "\n", - "* `travel_advisor`: can help with general travel destination recommendations. Can ask `sightseeing_advisor` and `hotel_advisor` for help.\n", - "* `sightseeing_advisor`: can help with sightseeing recommendations. Can ask `travel_advisor` and `hotel_advisor` for help.\n", - "* `hotel_advisor`: can help with hotel recommendations. Can ask `sightseeing_advisor` and `hotel_advisor` for help.\n", + "@tool\n", + "def get_travel_recommendations():\n", + " \"\"\"Get recommendation for travel destinations\"\"\"\n", + " return random.choice([\"aruba\", \"turks and caicos\"])\n", "\n", - "This is a fully-connected network - every agent can talk to any other agent. \n", "\n", - "To implement the handoffs between the agents we'll be using LLMs with structured output. Each agent's LLM will return an output with both its text response (`response`) as well as which agent to route to next (`goto`). If the agent has enough information to respond to the user, the `goto` will be set to `human` to route back and collect information from a human.\n", + "@tool\n", + "def get_hotel_recommendations(location: Literal[\"aruba\", \"turks and caicos\"]):\n", + " \"\"\"Get hotel recommendations for a given destination.\"\"\"\n", + " return {\n", + " \"aruba\": [\n", + " \"The Ritz-Carlton, Aruba (Palm Beach)\"\n", + " \"Bucuti & Tara Beach Resort (Eagle Beach)\"\n", + " ],\n", + " \"turks and caicos\": [\"Grace Bay Club\", \"COMO Parrot Cay\"],\n", + " }[location]\n", "\n", - "Now, let's define our agent nodes and graph!" + "\n", + "def make_handoff_tool(*, agent_name: str):\n", + " \"\"\"Create a tool that can return handoff via a Command\"\"\"\n", + " tool_name = f\"transfer_to_{agent_name}\"\n", + "\n", + " @tool(tool_name)\n", + " def handoff_to_agent(\n", + " state: Annotated[dict, InjectedState],\n", + " tool_call_id: Annotated[str, InjectedToolCallId],\n", + " ):\n", + " \"\"\"Ask another agent for help.\"\"\"\n", + " tool_message = {\n", + " \"role\": \"tool\",\n", + " \"content\": f\"Successfully transferred to {agent_name}\",\n", + " \"name\": tool_name,\n", + " \"tool_call_id\": tool_call_id,\n", + " }\n", + " return Command(\n", + " # navigate to another agent node in the PARENT graph\n", + " goto=agent_name,\n", + " graph=Command.PARENT,\n", + " # This is the state update that the agent `agent_name` will see when it is invoked.\n", + " # We're passing agent's FULL internal message history AND adding a tool message to make sure\n", + " # the resulting chat history is valid.\n", + " update={\"messages\": state[\"messages\"] + [tool_message]},\n", + " )\n", + "\n", + " return handoff_to_agent" + ] + }, + { + "cell_type": "markdown", + "id": "213d661e-6ba4-42b9-bc7f-6c8c423e3419", + "metadata": {}, + "source": [ + "Let's now create our agents using the the prebuilt [`create_react_agent`][langgraph.prebuilt.chat_agent_executor.create_react_agent]. We'll also define a dedicated `human` node with an [`interrupt`][langgraph.types.interrupt] -- we will route to this node after the final response from the agents. Note that to do so we're wrapping each agent invocation in a separate node function that returns `Command(goto=\"human\", ...)`." ] }, { "cell_type": "code", - "execution_count": 110, + "execution_count": 4, "id": "aa4bdbff-9461-46cc-aee9-8a22d3c3d9ec", "metadata": {}, "outputs": [], "source": [ - "from typing_extensions import TypedDict, Literal\n", - "\n", - "from langchain_openai import ChatOpenAI\n", - "from langchain_core.messages import AnyMessage\n", - "from langgraph.graph import MessagesState, StateGraph, START, END\n", + "from langchain_anthropic import ChatAnthropic\n", + "from langgraph.graph import MessagesState, StateGraph, START\n", + "from langgraph.prebuilt import create_react_agent, InjectedState\n", "from langgraph.types import Command, interrupt\n", "from langgraph.checkpoint.memory import MemorySaver\n", "\n", - "model = ChatOpenAI(model=\"gpt-4o\")\n", - "\n", - "\n", - "# Define a helper for each of the agent nodes to call\n", - "def call_llm(messages: list[AnyMessage], target_agent_nodes: list[str]):\n", - " \"\"\"Call LLM with structured output to get a natural language response as well as a target agent (node) to go to next.\n", - "\n", - " Args:\n", - " messages: list of messages to pass to the LLM\n", - " target_agents: list of the node names of the target agents to navigate to\n", - " \"\"\"\n", - " # define JSON schema for the structured output:\n", - " # - model's text response (`response`)\n", - " # - name of the node to go to next (or 'finish')\n", - " # see more on structured output here https://python.langchain.com/docs/concepts/structured_outputs\n", - " json_schema = {\n", - " \"name\": \"Response\",\n", - " \"parameters\": {\n", - " \"type\": \"object\",\n", - " \"properties\": {\n", - " \"response\": {\n", - " \"type\": \"string\",\n", - " \"description\": \"A human readable response to the original question. Does not need to be a final response. Will be streamed back to the user.\",\n", - " },\n", - " \"goto\": {\n", - " \"enum\": [*target_agent_nodes, \"finish\"],\n", - " \"type\": \"string\",\n", - " \"description\": \"The next agent to call, or 'finish' if the user's query has been resolved. Must be one of the specified values.\",\n", - " },\n", - " },\n", - " \"required\": [\"response\", \"goto\"],\n", - " },\n", - " }\n", - " response = model.with_structured_output(json_schema).invoke(messages)\n", - " return response\n", "\n", + "model = ChatAnthropic(model=\"claude-3-5-sonnet-latest\")\n", "\n", - "def travel_advisor(\n", - " state: MessagesState,\n", - ") -> Command[Literal[\"sightseeing_advisor\", \"hotel_advisor\", \"human\"]]:\n", - " system_prompt = (\n", + "# Define travel advisor tools and ReAct agent\n", + "travel_advisor_tools = [\n", + " get_travel_recommendations,\n", + " make_handoff_tool(agent_name=\"hotel_advisor\"),\n", + "]\n", + "travel_advisor = create_react_agent(\n", + " model,\n", + " travel_advisor_tools,\n", + " state_modifier=(\n", " \"You are a general travel expert that can recommend travel destinations (e.g. countries, cities, etc). \"\n", - " \"If you need specific sightseeing recommendations, ask 'sightseeing_advisor' for help. \"\n", " \"If you need hotel recommendations, ask 'hotel_advisor' for help. \"\n", - " \"If you have enough information to respond to the user, return 'finish'. \"\n", - " \"Never mention other agents by name.\"\n", - " )\n", - " messages = [{\"role\": \"system\", \"content\": system_prompt}] + state[\"messages\"]\n", - " target_agent_nodes = [\"sightseeing_advisor\", \"hotel_advisor\"]\n", - " response = call_llm(messages, target_agent_nodes)\n", - " ai_msg = {\"role\": \"ai\", \"content\": response[\"response\"], \"name\": \"travel_advisor\"}\n", - " # handoff to another agent or go to the human when agent is done\n", - " goto = response[\"goto\"]\n", - " if goto == \"finish\":\n", - " goto = \"human\"\n", - "\n", - " return Command(goto=goto, update={\"messages\": [ai_msg]})\n", + " \"You MUST include human-readable response before transferring to another agent.\"\n", + " ),\n", + ")\n", "\n", "\n", - "def sightseeing_advisor(\n", + "def call_travel_advisor(\n", " state: MessagesState,\n", - ") -> Command[Literal[\"travel_advisor\", \"hotel_advisor\", \"human\"]]:\n", - " system_prompt = (\n", - " \"You are a travel expert that can provide specific sightseeing recommendations for a given destination. \"\n", - " \"If you need general travel help, go to 'travel_advisor' for help. \"\n", - " \"If you need hotel recommendations, go to 'hotel_advisor' for help. \"\n", - " \"If you have enough information to respond to the user, return 'finish'. \"\n", - " \"Never mention other agents by name.\"\n", - " )\n", - " messages = [{\"role\": \"system\", \"content\": system_prompt}] + state[\"messages\"]\n", - " target_agent_nodes = [\"travel_advisor\", \"hotel_advisor\"]\n", - " response = call_llm(messages, target_agent_nodes)\n", - " ai_msg = {\n", - " \"role\": \"ai\",\n", - " \"content\": response[\"response\"],\n", - " \"name\": \"sightseeing_advisor\",\n", - " }\n", - " # handoff to another agent or go to the human when agent is done\n", - " goto = response[\"goto\"]\n", - " if goto == \"finish\":\n", - " goto = \"human\"\n", - "\n", - " return Command(goto=goto, update={\"messages\": [ai_msg]})\n", - "\n", - "\n", - "def hotel_advisor(\n", - " state: MessagesState,\n", - ") -> Command[Literal[\"travel_advisor\", \"sightseeing_advisor\", \"human\"]]:\n", - " system_prompt = (\n", - " \"You are a travel expert that can provide hotel recommendations for a given destination. \"\n", - " \"If you need general travel help, ask 'travel_advisor' for help. \"\n", - " \"If you need specific sightseeing recommendations, ask 'sightseeing_advisor' for help. \"\n", - " \"If you have enough information to respond to the user, return 'finish'. \"\n", - " \"Never mention other agents by name.\"\n", - " )\n", - " messages = [{\"role\": \"system\", \"content\": system_prompt}] + state[\"messages\"]\n", - " target_agent_nodes = [\"travel_advisor\", \"sightseeing_advisor\"]\n", - " response = call_llm(messages, target_agent_nodes)\n", - " ai_msg = {\"role\": \"ai\", \"content\": response[\"response\"], \"name\": \"hotel_advisor\"}\n", - " # handoff to another agent or go to the human when agent is done\n", - " goto = response[\"goto\"]\n", - " if goto == \"finish\":\n", - " goto = \"human\"\n", + ") -> Command[Literal[\"hotel_advisor\", \"human\"]]:\n", + " # You can also add additional logic like changing the input to the agent / output from the agent, etc.\n", + " # NOTE: we're invoking the ReAct agent with the full history of messages in the state\n", + " response = travel_advisor.invoke(state)\n", + " return Command(update=response, goto=\"human\")\n", + "\n", "\n", - " return Command(goto=goto, update={\"messages\": [ai_msg]})\n", + "# Define hotel advisor tools and ReAct agent\n", + "hotel_advisor_tools = [\n", + " get_hotel_recommendations,\n", + " make_handoff_tool(agent_name=\"travel_advisor\"),\n", + "]\n", + "hotel_advisor = create_react_agent(\n", + " model,\n", + " hotel_advisor_tools,\n", + " state_modifier=(\n", + " \"You are a hotel expert that can provide hotel recommendations for a given destination. \"\n", + " \"If you need help picking travel destinations, ask 'travel_advisor' for help.\"\n", + " \"You MUST include human-readable response before transferring to another agent.\"\n", + " ),\n", + ")\n", "\n", "\n", - "def human_node(\n", + "def call_hotel_advisor(\n", " state: MessagesState,\n", - ") -> Command[\n", - " Literal[\"hotel_advisor\", \"sightseeing_advisor\", \"travel_advisor\", \"human\"]\n", - "]:\n", + ") -> Command[Literal[\"travel_advisor\", \"human\"]]:\n", + " response = hotel_advisor.invoke(state)\n", + " return Command(update=response, goto=\"human\")\n", + "\n", + "\n", + "def human_node(\n", + " state: MessagesState, config\n", + ") -> Command[Literal[\"hotel_advisor\", \"travel_advisor\", \"human\"]]:\n", " \"\"\"A node for collecting user input.\"\"\"\n", + "\n", " user_input = interrupt(value=\"Ready for user input.\")\n", "\n", - " active_agent = None\n", + " # identify the last active agent\n", + " # (the last active node before returning to human)\n", + " langgraph_triggers = config[\"metadata\"][\"langgraph_triggers\"]\n", + " if len(langgraph_triggers) != 1:\n", + " raise AssertionError(\"Expected exactly 1 trigger in human node\")\n", "\n", - " # This will look up the active agent.\n", - " for message in state[\"messages\"][::-1]:\n", - " if message.name:\n", - " active_agent = message.name\n", - " break\n", - " else:\n", - " raise AssertionError(\"Could not determine the active agent.\")\n", + " active_agent = langgraph_triggers[0].split(\":\")[1]\n", "\n", " return Command(\n", " update={\n", @@ -283,9 +298,8 @@ "\n", "\n", "builder = StateGraph(MessagesState)\n", - "builder.add_node(\"travel_advisor\", travel_advisor)\n", - "builder.add_node(\"sightseeing_advisor\", sightseeing_advisor)\n", - "builder.add_node(\"hotel_advisor\", hotel_advisor)\n", + "builder.add_node(\"travel_advisor\", call_travel_advisor)\n", + "builder.add_node(\"hotel_advisor\", call_hotel_advisor)\n", "\n", "# This adds a node to collect human input, which will route\n", "# back to the active agent.\n", @@ -301,13 +315,13 @@ }, { "cell_type": "code", - "execution_count": 111, + "execution_count": 5, "id": "d77921f6-599d-443f-8b15-56b1adafd3a8", "metadata": {}, "outputs": [ { "data": { - "image/png": "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", + "image/png": "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", "text/plain": [ "" ] @@ -327,14 +341,14 @@ "id": "af856e1b-41fc-4041-8cbf-3818a60088e0", "metadata": {}, "source": [ - "### Test multi-turn conversation\n", + "## Test multi-turn conversation\n", "\n", "Let's test a multi turn conversation with this application." ] }, { "cell_type": "code", - "execution_count": 112, + "execution_count": 7, "id": "161e0cf1-d13a-4026-8f89-bdab67d1ad4d", "metadata": {}, "outputs": [ @@ -347,20 +361,61 @@ "\n", "User: {'messages': [{'role': 'user', 'content': 'i wanna go somewhere warm in the caribbean'}]}\n", "\n", - "travel_advisor: The Caribbean is full of warm and beautiful destinations. Some popular options include Jamaica, the Bahamas, the Dominican Republic, and Aruba. Each of these places offers stunning beaches, vibrant culture, and plenty of activities to enjoy. Would you like recommendations on sightseeing or accommodations in any specific location?\n", + "travel_advisor: Based on the recommendations, I suggest considering Aruba! It's a fantastic Caribbean destination known for its perfect warm weather year-round, with average temperatures around 82°F (28°C). Aruba is famous for its pristine white-sand beaches, crystal-clear waters, and constant cooling trade winds.\n", + "\n", + "Some highlights of Aruba include:\n", + "1. Beautiful Eagle Beach and Palm Beach\n", + "2. Excellent snorkeling and diving opportunities\n", + "3. Vibrant culture and dining scene\n", + "4. Consistent sunny weather (it's outside the hurricane belt!)\n", + "5. Great shopping and nightlife in Oranjestad\n", + "\n", + "Would you like me to help you explore more specific aspects of visiting Aruba? Or if you're interested in finding a hotel there, I can connect you with our hotel advisor who can provide detailed accommodation recommendations.\n", "\n", "--- Conversation Turn 2 ---\n", "\n", "User: Command(resume='could you recommend a nice hotel in one of the areas and tell me which area it is.')\n", "\n", - "travel_advisor: I'll get a hotel recommendation for you.\n", - "hotel_advisor: I recommend the \"Half Moon Resort\" located in Montego Bay, Jamaica. It's a luxurious resort known for its beautiful private beaches, excellent service, and a variety of amenities including golf, spas, and fine dining. Montego Bay is a vibrant area offering plenty of activities, from snorkeling and diving to exploring local culture and nightlife.\n", + "hotel_advisor: Based on the recommendations, I can suggest two excellent options in different areas:\n", + "\n", + "1. The Ritz-Carlton, Aruba - Located in Palm Beach\n", + "This luxury resort is situated in the bustling Palm Beach area, known for its high-rise hotels and vibrant atmosphere. The Ritz offers world-class amenities, including a luxurious spa, multiple restaurants, and a casino. The location is perfect if you want to be close to shopping, dining, and nightlife.\n", + "\n", + "2. Bucuti & Tara Beach Resort - Located in Eagle Beach\n", + "This adults-only boutique resort is situated on the stunning Eagle Beach, which is wider and generally quieter than Palm Beach. It's perfect for those seeking a more peaceful, romantic atmosphere. The resort is known for its exceptional service and sustainability practices.\n", + "\n", + "Would you like more specific information about either of these hotels or their locations?\n", "\n", "--- Conversation Turn 3 ---\n", "\n", - "User: Command(resume='could you recommend something to do near the hotel?')\n", + "User: Command(resume='i like the first one. could you recommend something to do near the hotel?')\n", + "\n", + "travel_advisor: Near The Ritz-Carlton in Palm Beach, there are several excellent activities you can enjoy:\n", + "\n", + "1. Palm Beach Strip - Right outside the hotel, you can walk along this vibrant strip featuring:\n", + " - High-end shopping at luxury boutiques\n", + " - Various restaurants and bars\n", + " - The Paseo Herencia Shopping & Entertainment Center\n", + "\n", + "2. Water Activities (within walking distance):\n", + " - Snorkeling at the artificial reef\n", + " - Parasailing\n", + " - Jet ski rentals\n", + " - Catamaran sailing trips\n", + " - Paddleboarding\n", "\n", - "hotel_advisor: I recommend visiting the Rose Hall Great House, a historic plantation house located near Montego Bay. It's known for its intriguing history and beautiful architecture, offering guided tours that include tales of the White Witch of Rose Hall. Additionally, you could explore Dunn's River Falls, a stunning natural waterfall that you can climb, located a bit further but well worth the trip. For a more relaxing day, you might enjoy a catamaran cruise along the coast, which often includes snorkeling stops and beautiful sunset views.\n" + "3. Nearby Attractions:\n", + " - Bubali Bird Sanctuary (5-minute drive)\n", + " - Butterfly Farm (10-minute walk)\n", + " - California Lighthouse (short drive)\n", + " - Visit the famous Stellaris Casino (located within the Ritz-Carlton)\n", + "\n", + "4. Local Culture:\n", + " - Visit the nearby fishing pier\n", + " - Take a short trip to local craft markets\n", + " - Evening sunset watching on the beach\n", + "\n", + "Would you like more specific information about any of these activities? I can also recommend some specific restaurants or shopping venues in the area!\n" ] } ], @@ -382,7 +437,9 @@ " resume=\"could you recommend a nice hotel in one of the areas and tell me which area it is.\"\n", " ),\n", " # 3rd round of conversation,\n", - " Command(resume=\"could you recommend something to do near the hotel?\"),\n", + " Command(\n", + " resume=\"i like the first one. could you recommend something to do near the hotel?\"\n", + " ),\n", "]\n", "\n", "for idx, user_input in enumerate(inputs):\n", @@ -399,9 +456,9 @@ " for node_id, value in update.items():\n", " if isinstance(value, dict) and value.get(\"messages\", []):\n", " last_message = value[\"messages\"][-1]\n", - " if last_message[\"role\"] != \"ai\":\n", + " if isinstance(last_message, dict) or last_message.type != \"ai\":\n", " continue\n", - " print(f\"{last_message['name']}: {last_message['content']}\")" + " print(f\"{node_id}: {last_message.content}\")" ] } ], diff --git a/docs/docs/how-tos/multi-agent-network.ipynb b/docs/docs/how-tos/multi-agent-network.ipynb index e1c48b98f..f1be9ad17 100644 --- a/docs/docs/how-tos/multi-agent-network.ipynb +++ b/docs/docs/how-tos/multi-agent-network.ipynb @@ -14,30 +14,28 @@ "id": "2c65639c-9705-49f1-840a-370718852e98", "metadata": {}, "source": [ - "!!! info \"Prerequisites\"\n", + "!!! info \"Prerequisites\" \n", " This guide assumes familiarity with the following:\n", "\n", - " - [Node](../../concepts/low_level/#nodes)\n", - " - [Command](../../concepts/low_level/#command)\n", + " - [How to implement handoffs between agents](../agent-handoffs)\n", " - [Multi-agent systems](../../concepts/multi_agent)\n", + " - [Command](../../concepts/low_level/#command)\n", + " - [LangGraph Glossary](../../concepts/low_level/)\n", "\n", + "In this how-to guide we will demonstrate how to implement a [multi-agent network](../../concepts/multi_agent#network) architecture where each agent can communicate with every other agent (many-to-many connections) and can decide which agent to call next. Individual agents will be defined as graph nodes.\n", "\n", - "In this how-to guide we will demonstrate how to implement a [multi-agent network](../../concepts/multi_agent#network) architecture.\n", - "\n", - "Each agent can be represented as a node in the graph that executes agent step(s) and decides what to do next - finish execution or route to another agent (including routing to itself, e.g. running in a loop). A common pattern for routing in multi-agent architectures is handoffs. Handoffs allow you to specify:\n", - "\n", - "1. which agent to navigate to next and (e.g. name of the node to go to)\n", - "2. what information to pass to that agent (e.g. state update)\n", - "\n", - "To implement handoffs, agent nodes can return `Command` object that allows you to [combine both control flow and state updates](../command):\n", + "To implement communication between the agents, we will be using [handoffs](../agent-handoffs):\n", "\n", "```python\n", "def agent(state) -> Command[Literal[\"agent\", \"another_agent\"]]:\n", " # the condition for routing/halting can be anything, e.g. LLM tool call / structured output, etc.\n", " goto = get_next_agent(...) # 'agent' / 'another_agent'\n", - " if goto:\n", - " return Command(goto=goto, update={\"my_state_key\": \"my_state_value\"})\n", - " \n", + " return Command(\n", + " # Specify which agent to call next\n", + " goto=goto,\n", + " # Update the graph state\n", + " update={\"my_state_key\": \"my_state_value\"}\n", + " )\n", "```" ] }, @@ -59,7 +57,7 @@ "outputs": [], "source": [ "%%capture --no-stderr\n", - "%pip install -U langgraph langchain-openai" + "%pip install -U langgraph langchain-anthropic" ] }, { @@ -72,7 +70,7 @@ "name": "stdin", "output_type": "stream", "text": [ - "OPENAI_API_KEY: ········\n" + "ANTHROPIC_API_KEY: ········\n" ] } ], @@ -86,7 +84,7 @@ " os.environ[var] = getpass.getpass(f\"{var}: \")\n", "\n", "\n", - "_set_env(\"OPENAI_API_KEY\")" + "_set_env(\"ANTHROPIC_API_KEY\")" ] }, { @@ -107,7 +105,7 @@ "id": "4a53f304-3709-4df7-8714-1ca61e615743", "metadata": {}, "source": [ - "## Travel Recommendations Example" + "## Using a custom agent implementation" ] }, { @@ -117,156 +115,126 @@ "source": [ "In this example we will build a team of travel assistant agents that can communicate with each other via handoffs.\n", "\n", - "We will create 3 agents:\n", + "We will create 2 agents:\n", "\n", - "* `travel_advisor`: can help with general travel destination recommendations. Can ask `sightseeing_advisor` and `hotel_advisor` for help.\n", - "* `sightseeing_advisor`: can help with sightseeing recommendations. Can ask `travel_advisor` and `hotel_advisor` for help.\n", - "* `hotel_advisor`: can help with hotel recommendations. Can ask `sightseeing_advisor` and `hotel_advisor` for help.\n", + "* `travel_advisor`: can help with travel destination recommendations. Can ask `hotel_advisor` for help.\n", + "* `hotel_advisor`: can help with hotel recommendations. Can ask `travel_advisor` for help.\n", "\n", "This is a fully-connected network - every agent can talk to any other agent. \n", "\n", - "To implement the handoffs between the agents we'll be using LLMs with structured output. Each agent's LLM will return an output with both its text response (`response`) as well as which agent to route to next (`goto`). If the agent has enough information to respond to the user, `goto` will contain `finish`.\n", + "Each agent will have a corresponding node function that can conditionally return a `Command` object (the handoff). The node function will use an LLM with a system prompt and a tool that lets it signal when it needs to hand off to another agent. If the LLM responds with the tool calls, we will return a `Command(goto=)`.\n", + "\n", + "> **Note**: while we're using tools for the LLM to signal that it needs a handoff, the condition for the handoff can be anything: a specific response text from the LLM, structured output from the LLM, any other custom logic, etc.\n", "\n", "Now, let's define our agent nodes and graph!" ] }, { "cell_type": "code", - "execution_count": 3, - "id": "29e2a824-dbeb-4944-9df2-63fdbd883252", + "execution_count": 43, + "id": "8a3f270f-4894-4a2d-98cd-e855353e3e0c", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ - "from typing_extensions import TypedDict, Literal\n", + "from typing_extensions import Literal\n", "\n", - "from langchain_openai import ChatOpenAI\n", - "from langchain_core.messages import AnyMessage\n", - "from langgraph.graph import MessagesState, StateGraph, START, END\n", + "from langchain_core.messages import ToolMessage\n", + "from langchain_core.tools import tool\n", + "from langchain_anthropic import ChatAnthropic\n", + "from langgraph.graph import MessagesState, StateGraph, START\n", "from langgraph.types import Command\n", "\n", - "model = ChatOpenAI(model=\"gpt-4o\")\n", + "\n", + "model = ChatAnthropic(model=\"claude-3-5-sonnet-latest\")\n", "\n", "\n", "# Define a helper for each of the agent nodes to call\n", - "def call_llm(messages: list[AnyMessage], target_agent_nodes: list[str]):\n", - " \"\"\"Call LLM with structured output to get a natural language response as well as a target agent (node) to go to next.\n", - "\n", - " Args:\n", - " messages: list of messages to pass to the LLM\n", - " target_agents: list of the node names of the target agents to navigate to\n", - " \"\"\"\n", - " # define JSON schema for the structured output:\n", - " # - model's text response (`response`)\n", - " # - name of the node to go to next (or 'finish')\n", - " # see more on structured output here https://python.langchain.com/docs/concepts/structured_outputs\n", - " json_schema = {\n", - " \"name\": \"Response\",\n", - " \"parameters\": {\n", - " \"type\": \"object\",\n", - " \"properties\": {\n", - " \"response\": {\n", - " \"type\": \"string\",\n", - " \"description\": \"A human readable response to the original question. Does not need to be a final response. Will be streamed back to the user.\",\n", - " },\n", - " \"goto\": {\n", - " \"enum\": [*target_agent_nodes, \"__end__\"],\n", - " \"type\": \"string\",\n", - " \"description\": \"The next agent to call, or __end__ if the user's query has been resolved. Must be one of the specified values.\",\n", - " },\n", - " },\n", - " \"required\": [\"response\", \"goto\"],\n", - " },\n", - " }\n", - " response = model.with_structured_output(json_schema).invoke(messages)\n", - " return response\n", "\n", "\n", - "def travel_advisor(\n", - " state: MessagesState,\n", - ") -> Command[Literal[\"sightseeing_advisor\", \"hotel_advisor\", \"__end__\"]]:\n", - " system_prompt = (\n", - " \"You are a general travel expert that can recommend travel destinations (e.g. countries, cities, etc). \"\n", - " \"If you need specific sightseeing recommendations, ask 'sightseeing_advisor' for help. \"\n", - " \"If you need hotel recommendations, ask 'hotel_advisor' for help. \"\n", - " \"If you have enough information to respond to the user, return 'finish'. \"\n", - " \"Never mention other agents by name.\"\n", - " )\n", - " messages = [{\"role\": \"system\", \"content\": system_prompt}] + state[\"messages\"]\n", - " target_agent_nodes = [\"sightseeing_advisor\", \"hotel_advisor\"]\n", - " response = call_llm(messages, target_agent_nodes)\n", - " ai_msg = {\"role\": \"ai\", \"content\": response[\"response\"], \"name\": \"travel_advisor\"}\n", - " # handoff to another agent or halt\n", - " return Command(goto=response[\"goto\"], update={\"messages\": ai_msg})\n", + "@tool\n", + "def transfer_to_travel_advisor():\n", + " \"\"\"Ask travel advisor for help.\"\"\"\n", + " # This tool is not returning anything: we're just using it\n", + " # as a way for LLM to signal that it needs to hand off to another agent\n", + " # (See the paragraph above)\n", + " return\n", + "\n", "\n", + "@tool\n", + "def transfer_to_hotel_advisor():\n", + " \"\"\"Ask hotel advisor for help.\"\"\"\n", + " return\n", "\n", - "def sightseeing_advisor(\n", + "\n", + "def travel_advisor(\n", " state: MessagesState,\n", - ") -> Command[Literal[\"travel_advisor\", \"hotel_advisor\", \"__end__\"]]:\n", + ") -> Command[Literal[\"hotel_advisor\", \"__end__\"]]:\n", " system_prompt = (\n", - " \"You are a travel expert that can provide specific sightseeing recommendations for a given destination. \"\n", - " \"If you need general travel help, go to 'travel_advisor' for help. \"\n", - " \"If you need hotel recommendations, go to 'hotel_advisor' for help. \"\n", - " \"If you have enough information to respond to the user, return 'finish'. \"\n", - " \"Never mention other agents by name.\"\n", + " \"You are a general travel expert that can recommend travel destinations (e.g. countries, cities, etc). \"\n", + " \"If you need hotel recommendations, ask 'hotel_advisor' for help.\"\n", " )\n", " messages = [{\"role\": \"system\", \"content\": system_prompt}] + state[\"messages\"]\n", - " target_agent_nodes = [\"travel_advisor\", \"hotel_advisor\"]\n", - " response = call_llm(messages, target_agent_nodes)\n", - " ai_msg = {\n", - " \"role\": \"ai\",\n", - " \"content\": response[\"response\"],\n", - " \"name\": \"sightseeing_advisor\",\n", - " }\n", - " # handoff to another agent or halt\n", - " return Command(goto=response[\"goto\"], update={\"messages\": ai_msg})\n", + " ai_msg = model.bind_tools([transfer_to_hotel_advisor]).invoke(messages)\n", + " # If there are tool calls, the LLM needs to hand off to another agent\n", + " if len(ai_msg.tool_calls) > 0:\n", + " tool_call_id = ai_msg.tool_calls[-1][\"id\"]\n", + " # NOTE: it's important to insert a tool message here because LLM providers are expecting\n", + " # all AI messages to be followed by a corresponding tool result message\n", + " tool_msg = {\n", + " \"role\": \"tool\",\n", + " \"content\": \"Successfully transferred\",\n", + " \"tool_call_id\": tool_call_id,\n", + " }\n", + " return Command(goto=\"hotel_advisor\", update={\"messages\": [ai_msg, tool_msg]})\n", + "\n", + " # If the expert has an answer, return it directly to the user\n", + " return {\"messages\": [ai_msg]}\n", "\n", "\n", "def hotel_advisor(\n", " state: MessagesState,\n", - ") -> Command[Literal[\"travel_advisor\", \"sightseeing_advisor\", \"__end__\"]]:\n", + ") -> Command[Literal[\"travel_advisor\", \"__end__\"]]:\n", " system_prompt = (\n", - " \"You are a travel expert that can provide hotel recommendations for a given destination. \"\n", - " \"If you need general travel help, ask 'travel_advisor' for help. \"\n", - " \"If you need specific sightseeing recommendations, ask 'sightseeing_advisor' for help. \"\n", - " \"If you have enough information to respond to the user, return 'finish'. \"\n", - " \"Never mention other agents by name.\"\n", + " \"You are a hotel expert that can provide hotel recommendations for a given destination. \"\n", + " \"If you need help picking travel destinations, ask 'travel_advisor' for help.\"\n", " )\n", " messages = [{\"role\": \"system\", \"content\": system_prompt}] + state[\"messages\"]\n", - " target_agent_nodes = [\"travel_advisor\", \"sightseeing_advisor\"]\n", - " response = call_llm(messages, target_agent_nodes)\n", - " ai_msg = {\"role\": \"ai\", \"content\": response[\"response\"], \"name\": \"hotel_advisor\"}\n", - " # handoff to another agent or halt\n", - " return Command(goto=response[\"goto\"], update={\"messages\": ai_msg})\n", + " ai_msg = model.bind_tools([transfer_to_travel_advisor]).invoke(messages)\n", + " # If there are tool calls, the LLM needs to hand off to another agent\n", + " if len(ai_msg.tool_calls) > 0:\n", + " tool_call_id = ai_msg.tool_calls[-1][\"id\"]\n", + " # NOTE: it's important to insert a tool message here because LLM providers are expecting\n", + " # all AI messages to be followed by a corresponding tool result message\n", + " tool_msg = {\n", + " \"role\": \"tool\",\n", + " \"content\": \"Successfully transferred\",\n", + " \"tool_call_id\": tool_call_id,\n", + " }\n", + " return Command(goto=\"travel_advisor\", update={\"messages\": [ai_msg, tool_msg]})\n", + "\n", + " # If the expert has an answer, return it directly to the user\n", + " return {\"messages\": [ai_msg]}\n", "\n", "\n", "builder = StateGraph(MessagesState)\n", "builder.add_node(\"travel_advisor\", travel_advisor)\n", - "builder.add_node(\"sightseeing_advisor\", sightseeing_advisor)\n", "builder.add_node(\"hotel_advisor\", hotel_advisor)\n", "# we'll always start with a general travel advisor\n", "builder.add_edge(START, \"travel_advisor\")\n", "\n", - "graph = builder.compile()" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "d77921f6-599d-443f-8b15-56b1adafd3a8", - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ + "graph = builder.compile()\n", + "\n", "from IPython.display import display, Image\n", "\n", "display(Image(graph.get_graph().draw_mermaid_png()))" @@ -282,7 +250,37 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 28, + "id": "058f3d96-534f-4b97-afb3-799ba81224ea", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain_core.messages import convert_to_messages\n", + "\n", + "\n", + "def pretty_print_messages(update):\n", + " if isinstance(update, tuple):\n", + " ns, update = update\n", + " # skip parent graph updates in the printouts\n", + " if len(ns) == 0:\n", + " return\n", + "\n", + " graph_id = ns[-1].split(\":\")[0]\n", + " print(f\"Update from subgraph {graph_id}:\")\n", + " print(\"\\n\")\n", + "\n", + " for node_name, node_update in update.items():\n", + " print(f\"Update from node {node_name}:\")\n", + " print(\"\\n\")\n", + "\n", + " for m in convert_to_messages(node_update[\"messages\"]):\n", + " m.pretty_print()\n", + " print(\"\\n\")" + ] + }, + { + "cell_type": "code", + "execution_count": 29, "id": "26a0d4df-ff99-40f0-92a8-0b3f2c591040", "metadata": {}, "outputs": [ @@ -290,7 +288,39 @@ "name": "stdout", "output_type": "stream", "text": [ - "{'travel_advisor': {'messages': {'role': 'ai', 'content': 'The Caribbean offers many warm and beautiful destinations. Some popular ones include:\\n\\n1. **Jamaica** - Known for its beautiful beaches, reggae music, and vibrant culture.\\n2. **Bahamas** - Offers stunning beaches and clear turquoise waters, perfect for relaxation and water activities.\\n3. **Barbados** - Known for its friendly locals, delicious cuisine, and beautiful beaches.\\n4. **Dominican Republic** - Offers a mix of beaches, mountains, and historical sites.\\n5. **Aruba** - Known for its dry climate, beautiful beaches, and outdoor activities.\\n\\nWould you like recommendations for sightseeing or hotels in any of these destinations?', 'name': 'travel_advisor'}}}\n", + "Update from node travel_advisor:\n", + "\n", + "\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "I'd be happy to help you plan a Caribbean vacation! The Caribbean is perfect for warm weather getaways. Let me suggest some fantastic destinations:\n", + "\n", + "1. Dominican Republic\n", + "- Known for beautiful beaches, all-inclusive resorts, and tropical climate\n", + "- Popular areas include Punta Cana and Puerto Plata\n", + "- Great mix of beaches, culture, and activities\n", + "\n", + "2. Jamaica\n", + "- Famous for its laid-back atmosphere and beautiful beaches\n", + "- Popular spots include Montego Bay, Negril, and Ocho Rios\n", + "- Known for reggae music, delicious cuisine, and water sports\n", + "\n", + "3. Bahamas\n", + "- Crystal clear waters and stunning beaches\n", + "- Perfect for island hopping\n", + "- Great for water activities and swimming with pigs at Pig Beach\n", + "\n", + "4. Turks and Caicos\n", + "- Pristine beaches and luxury resorts\n", + "- Excellent for snorkeling and diving\n", + "- More peaceful and less crowded than some other Caribbean destinations\n", + "\n", + "5. Aruba\n", + "- Known for constant sunny weather and minimal rainfall\n", + "- Beautiful white sand beaches\n", + "- Great shopping and dining options\n", + "\n", + "Would you like me to provide more specific information about any of these destinations? Also, if you'd like hotel recommendations for any of these locations, I can transfer you to our hotel advisor for specific accommodation suggestions. Just let me know which destination interests you most!\n", "\n", "\n" ] @@ -300,8 +330,7 @@ "for chunk in graph.stream(\n", " {\"messages\": [(\"user\", \"i wanna go somewhere warm in the caribbean\")]}\n", "):\n", - " print(chunk)\n", - " print(\"\\n\")" + " pretty_print_messages(chunk)" ] }, { @@ -314,7 +343,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 30, "id": "68a547d4-0a15-43bd-aeed-c9ba1dfe388f", "metadata": {}, "outputs": [ @@ -322,13 +351,46 @@ "name": "stdout", "output_type": "stream", "text": [ - "{'travel_advisor': {'messages': {'role': 'ai', 'content': 'I recommend visiting Jamaica, a vibrant and warm destination in the Caribbean known for its beautiful beaches, rich culture, and exciting activities.', 'name': 'travel_advisor'}}}\n", + "Update from node travel_advisor:\n", + "\n", + "\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "[{'text': \"I'll help you with a Caribbean destination recommendation! Given the vast number of beautiful Caribbean islands, I'll recommend one popular destination: the Dominican Republic, specifically Punta Cana. It offers pristine beaches, warm weather year-round, crystal-clear waters, and excellent resorts.\\n\\nLet me get some hotel recommendations for Punta Cana by consulting our hotel advisor.\", 'type': 'text'}, {'id': 'toolu_01B9djUstpDKHVSy3o3rfzsG', 'input': {}, 'name': 'transfer_to_hotel_advisor', 'type': 'tool_use'}]\n", + "Tool Calls:\n", + " transfer_to_hotel_advisor (toolu_01B9djUstpDKHVSy3o3rfzsG)\n", + " Call ID: toolu_01B9djUstpDKHVSy3o3rfzsG\n", + " Args:\n", + "=================================\u001b[1m Tool Message \u001b[0m=================================\n", + "\n", + "Successfully transferred\n", + "\n", + "\n", + "Update from node hotel_advisor:\n", + "\n", + "\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "For Punta Cana, here are some top hotel recommendations:\n", + "\n", + "1. Hyatt Zilara Cap Cana - Adults-only, all-inclusive luxury resort with pristine beachfront location, multiple pools, and upscale dining options.\n", + "\n", + "2. Hard Rock Hotel & Casino Punta Cana - Perfect for entertainment lovers, featuring 13 pools, 9 restaurants, a casino, and extensive amenities.\n", "\n", + "3. Excellence Punta Cana - Adults-only, all-inclusive resort known for its romantic atmosphere and excellent service.\n", "\n", - "{'sightseeing_advisor': {'messages': {'role': 'ai', 'content': \"For a warm and vibrant Caribbean destination, I recommend Jamaica. It's renowned for its stunning beaches, lively culture, and exciting activities.\\n\\n### Things to Do in Jamaica:\\n1. **Explore Dunn’s River Falls**: A famous waterfall near Ocho Rios where you can climb the terraced steps and enjoy the natural pools.\\n2. **Visit Bob Marley Museum**: Located in Kingston, this museum offers a deep dive into the life of the reggae legend.\\n3. **Relax at Seven Mile Beach**: Known for its beautiful white sand and clear blue waters, it’s perfect for sunbathing and swimming.\\n4. **Experience Negril Cliffs**: Enjoy breathtaking views and adventurous cliff diving.\\n5. **Discover Blue Hole**: A hidden gem near Ocho Rios, offering a refreshing swim in stunning turquoise waters.\\n\\nI will now find some hotel recommendations for you in Jamaica.\", 'name': 'sightseeing_advisor'}}}\n", + "4. Secrets Cap Cana Resort & Spa - Sophisticated adults-only resort with beautiful swim-out suites and gourmet dining options.\n", "\n", + "5. The Reserve at Paradisus Palma Real - Family-friendly luxury resort with dedicated family concierge, kids' activities, and beautiful pools.\n", "\n", - "{'hotel_advisor': {'messages': {'role': 'ai', 'content': 'For your stay in Jamaica, here are some hotel recommendations:\\n\\n1. **Sandals Montego Bay**: An all-inclusive resort offering luxury accommodations, private beaches, and various dining options.\\n\\n2. **Jamaica Inn**: Located in Ocho Rios, this boutique hotel offers a more intimate experience with stunning beach views and personalized service.\\n\\n3. **Half Moon Resort**: Situated in Montego Bay, this resort features a private beach, golf course, and a world-class spa.\\n\\n4. **The Caves Hotel**: In Negril, offering unique cliffside cottages and a romantic atmosphere.\\n\\n5. **Round Hill Hotel and Villas**: A classic resort near Montego Bay with luxurious villas and an elegant ambiance.\\n\\nEnjoy your trip to Jamaica, where warm weather and vibrant culture await!', 'name': 'hotel_advisor'}}}\n", + "These resorts all offer:\n", + "- Direct beach access\n", + "- Multiple restaurants\n", + "- Swimming pools\n", + "- Spa facilities\n", + "- High-quality accommodations\n", + "\n", + "Would you like more specific information about any of these hotels or would you prefer to explore hotels in a different Caribbean destination?\n", "\n", "\n" ] @@ -340,13 +402,12 @@ " \"messages\": [\n", " (\n", " \"user\",\n", - " \"i wanna go somewhere warm in the caribbean. pick one destination, give me some things to do and hotel recommendations\",\n", + " \"i wanna go somewhere warm in the caribbean. pick one destination and give me hotel recommendations\",\n", " )\n", " ]\n", " }\n", "):\n", - " print(chunk)\n", - " print(\"\\n\")" + " pretty_print_messages(chunk)" ] }, { @@ -354,136 +415,122 @@ "id": "c1c66f91-39b0-4ed2-91e8-6daf6d124f47", "metadata": {}, "source": [ - "Voila - `travel_advisor` makes a decision to first get some sightseeing recommendations from `sightseeing_advisor`, and then `sightseeing_advisor` in turn calls `hotel_advisor` for more info. Notice that we never explicitly defined the order in which the agents should be executed!" + "Voila - `travel_advisor` picks a destination and then makes a decision to call `hotel_advisor` for more info!" ] }, { "cell_type": "markdown", - "id": "3f9930b9-16b4-4179-9990-7ddf48cb3ed7", + "id": "5f9ff04a-f2c6-408c-a332-1346a96d7f61", "metadata": {}, "source": [ - "## Game NPCs Example" + "## Using with a prebuilt ReAct agent" ] }, { - "attachments": {}, "cell_type": "markdown", - "id": "3f7b49c5-070e-4289-88aa-afbfae44cc98", + "id": "1b560c3c-fa17-4879-a40f-147fc483c41c", "metadata": {}, "source": [ - "In this example we will create a team of [non-player characters (NPCs)](https://en.wikipedia.org/wiki/Non-player_character) that all run at the same time and share game state (resources). At each step, each NPC will inspect the state and decide whether to halt or continue acting at the next step. If it continues, it will update the shared game state (produce or consume resources).\n", - "\n", - "We will create 4 NPC agents:\n", - "\n", - "- `villager`: produces wood and food until there is enough, then halts\n", - "- `guard`: protects gold and consumes food. When there is not enough food, leaves duty and halts\n", - "- `merchant`: trades wood for gold. When there is not enough wood, halts\n", - "- `thief`: checks if the guard is on duty and steals all of the gold when the guard leaves, then halts\n", - "\n", - "Our NPC agents will be simple node functions (`villager`, `guard`, etc.). At each step of the graph execution, the agent function will inspect the resource values in the state and decide whether it should halt or continue. If it decides to continue, it will update the resource values in the state and loop back to itself to run at the next step.\n", - "\n", - "Now, let's define our agent nodes and graph!" + "Let's now see how we can implement the same team of travel agents, but give each of the agents some tools to call. We'll be using prebuilt [`create_react_agent`][langgraph.prebuilt.chat_agent_executor.create_react_agent] to implement the agents. First, let's create some of the tools that the agents will be using:" ] }, { "cell_type": "code", - "execution_count": 7, - "id": "f15c38c0-c88a-404b-9687-a9ef9ff20ffc", + "execution_count": 31, + "id": "7e31f258-ec28-4020-b86d-c91dfa9a3bfc", "metadata": {}, "outputs": [], "source": [ - "from typing_extensions import Annotated, TypedDict, Literal\n", - "\n", - "from langchain_core.runnables import RunnableConfig\n", - "from langgraph.graph import StateGraph, START, END\n", - "from langgraph.types import Command\n", - "\n", - "import operator\n", - "\n", - "\n", - "class GameState(TypedDict):\n", - " # note that we're defining a reducer (operator.add) here.\n", - " # This will allow all agents to write their updates for resources concurrently.\n", - " wood: Annotated[int, operator.add]\n", - " food: Annotated[int, operator.add]\n", - " gold: Annotated[int, operator.add]\n", - " guard_on_duty: bool\n", - "\n", - "\n", - "def villager(state: GameState) -> Command[Literal[\"villager\", \"__end__\"]]:\n", - " \"\"\"Villager NPC that gathers wood and food.\"\"\"\n", - " current_resources = state[\"wood\"] + state[\"food\"]\n", - " if current_resources < 15: # Continue gathering until we have enough resources\n", - " print(\"Villager gathering resources.\")\n", - " # Loop back to the 'villager' agent\n", - " return Command(goto=\"villager\", update={\"wood\": 3, \"food\": 1})\n", - " # NOTE: Returning Command(goto=END) is not necessary for the graph to run correctly\n", - " # but it's useful for visualization, to show that the agent actually halts\n", - " else:\n", - " return Command(goto=END)\n", - "\n", - "\n", - "def guard(state: GameState) -> Command[Literal[\"guard\", \"__end__\"]]:\n", - " \"\"\"Guard NPC that protects gold and consumes food.\"\"\"\n", - " if not state[\"guard_on_duty\"]:\n", - " return Command(goto=END)\n", - "\n", - " if state[\"food\"] > 0: # Guard needs food to keep patrolling\n", - " print(\"Guard patrolling.\")\n", - " # Loop back to the 'guard' agent\n", + "import random\n", + "from typing_extensions import Literal\n", + "\n", + "\n", + "@tool\n", + "def get_travel_recommendations():\n", + " \"\"\"Get recommendation for travel destinations\"\"\"\n", + " return random.choice([\"aruba\", \"turks and caicos\"])\n", + "\n", + "\n", + "@tool\n", + "def get_hotel_recommendations(location: Literal[\"aruba\", \"turks and caicos\"]):\n", + " \"\"\"Get hotel recommendations for a given destination.\"\"\"\n", + " return {\n", + " \"aruba\": [\n", + " \"The Ritz-Carlton, Aruba (Palm Beach)\"\n", + " \"Bucuti & Tara Beach Resort (Eagle Beach)\"\n", + " ],\n", + " \"turks and caicos\": [\"Grace Bay Club\", \"COMO Parrot Cay\"],\n", + " }[location]" + ] + }, + { + "cell_type": "markdown", + "id": "2b64455a-8b28-42f0-90ac-a0ab92a433a3", + "metadata": {}, + "source": [ + "Let's also write a helper to create a handoff tool. See [this how-to guide](../agent-handoffs#implementing-handoffs-using-tools) for a more in-depth walkthrough of how to make a handoff tool." + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "3b82280f-d171-4338-8ead-d1b3029ad9fb", + "metadata": {}, + "outputs": [], + "source": [ + "from typing import Annotated\n", + "\n", + "from langchain_core.tools import tool\n", + "from langchain_core.tools.base import InjectedToolCallId\n", + "from langgraph.prebuilt import InjectedState\n", + "\n", + "\n", + "def make_handoff_tool(*, agent_name: str):\n", + " \"\"\"Create a tool that can return handoff via a Command\"\"\"\n", + " tool_name = f\"transfer_to_{agent_name}\"\n", + "\n", + " @tool(tool_name)\n", + " def handoff_to_agent(\n", + " state: Annotated[dict, InjectedState],\n", + " tool_call_id: Annotated[str, InjectedToolCallId],\n", + " ):\n", + " \"\"\"Ask another agent for help.\"\"\"\n", + " tool_message = {\n", + " \"role\": \"tool\",\n", + " \"content\": f\"Successfully transferred to {agent_name}\",\n", + " \"name\": tool_name,\n", + " \"tool_call_id\": tool_call_id,\n", + " }\n", " return Command(\n", - " goto=\"guard\",\n", - " update={\"food\": -1}, # Consume food while patrolling\n", + " # navigate to another agent node in the PARENT graph\n", + " goto=agent_name,\n", + " graph=Command.PARENT,\n", + " # This is the state update that the agent `agent_name` will see when it is invoked.\n", + " # We're passing agent's FULL internal message history AND adding a tool message to make sure\n", + " # the resulting chat history is valid.\n", + " update={\"messages\": state[\"messages\"] + [tool_message]},\n", " )\n", - " else:\n", - " print(\"Guard leaving to get food.\")\n", - " return Command(goto=END, update={\"guard_on_duty\": False}) # Leave to get food\n", - "\n", - "\n", - "def merchant(state: GameState) -> Command[Literal[\"merchant\", \"__end__\"]]:\n", - " \"\"\"Merchant NPC that trades wood for gold.\"\"\"\n", - " if state[\"wood\"] >= 5: # Trade wood for gold when available\n", - " print(\"Merchant trading wood for gold.\")\n", - " return Command(goto=\"merchant\", update={\"wood\": -5, \"gold\": 1})\n", - " else:\n", - " return Command(goto=END)\n", - "\n", - "\n", - "def thief(state: GameState) -> Command[Literal[\"thief\", \"__end__\"]]:\n", - " \"\"\"Thief NPC that steals gold if the guard leaves to get food.\"\"\"\n", - " if not state[\"guard_on_duty\"]:\n", - " print(\"Thief stealing gold.\")\n", - " return Command(goto=END, update={\"gold\": -state[\"gold\"]})\n", - " else:\n", - " # keep thief on standby (loop back to the 'thief' agent)\n", - " return Command(goto=\"thief\")\n", - "\n", - "\n", - "builder = StateGraph(GameState)\n", - "\n", - "# Add NPC nodes\n", - "builder.add_node(villager)\n", - "builder.add_node(guard)\n", - "builder.add_node(merchant)\n", - "builder.add_node(thief)\n", - "\n", - "# All NPCs start running in parallel\n", - "builder.add_edge(START, \"villager\")\n", - "builder.add_edge(START, \"guard\")\n", - "builder.add_edge(START, \"merchant\")\n", - "builder.add_edge(START, \"thief\")\n", - "graph = builder.compile()" + "\n", + " return handoff_to_agent" + ] + }, + { + "cell_type": "markdown", + "id": "93dbc3bd-27b9-4d79-b5dd-be592bc50f74", + "metadata": {}, + "source": [ + "Now let's define our agent nodes and combine them into a graph:" ] }, { "cell_type": "code", - "execution_count": 8, - "id": "ab4cc03e-4e25-44ac-88b1-e415fcbce151", + "execution_count": 42, + "id": "b638d6c4-3de6-4921-980c-2df1bd1cc9c7", "metadata": {}, "outputs": [ { "data": { - "image/png": "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", + "image/png": "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", "text/plain": [ "" ] @@ -493,91 +540,190 @@ } ], "source": [ + "from langgraph.graph import MessagesState, StateGraph, START, END\n", + "from langgraph.prebuilt import create_react_agent\n", + "from langgraph.types import Command\n", + "\n", + "\n", + "model = ChatAnthropic(model=\"claude-3-5-sonnet-latest\")\n", + "\n", + "# Define travel advisor ReAct agent\n", + "travel_advisor_tools = [\n", + " get_travel_recommendations,\n", + " make_handoff_tool(agent_name=\"hotel_advisor\"),\n", + "]\n", + "travel_advisor = create_react_agent(\n", + " model,\n", + " travel_advisor_tools,\n", + " state_modifier=(\n", + " \"You are a general travel expert that can recommend travel destinations (e.g. countries, cities, etc). \"\n", + " \"If you need hotel recommendations, ask 'hotel_advisor' for help. \"\n", + " \"You MUST include human-readable response before transferring to another agent.\"\n", + " ),\n", + ")\n", + "\n", + "\n", + "def call_travel_advisor(\n", + " state: MessagesState,\n", + ") -> Command[Literal[\"hotel_advisor\", \"__end__\"]]:\n", + " # You can also add additional logic like changing the input to the agent / output from the agent, etc.\n", + " # NOTE: we're invoking the ReAct agent with the full history of messages in the state\n", + " return travel_advisor.invoke(state)\n", + "\n", + "\n", + "# Define hotel advisor ReAct agent\n", + "hotel_advisor_tools = [\n", + " get_hotel_recommendations,\n", + " make_handoff_tool(agent_name=\"travel_advisor\"),\n", + "]\n", + "hotel_advisor = create_react_agent(\n", + " model,\n", + " hotel_advisor_tools,\n", + " state_modifier=(\n", + " \"You are a hotel expert that can provide hotel recommendations for a given destination. \"\n", + " \"If you need help picking travel destinations, ask 'travel_advisor' for help.\"\n", + " \"You MUST include human-readable response before transferring to another agent.\"\n", + " ),\n", + ")\n", + "\n", + "\n", + "def call_hotel_advisor(\n", + " state: MessagesState,\n", + ") -> Command[Literal[\"travel_advisor\", \"__end__\"]]:\n", + " return hotel_advisor.invoke(state)\n", + "\n", + "\n", + "builder = StateGraph(MessagesState)\n", + "builder.add_node(\"travel_advisor\", call_travel_advisor)\n", + "builder.add_node(\"hotel_advisor\", call_hotel_advisor)\n", + "# we'll always start with a general travel advisor\n", + "builder.add_edge(START, \"travel_advisor\")\n", + "\n", + "graph = builder.compile()\n", "display(Image(graph.get_graph().draw_mermaid_png()))" ] }, { "cell_type": "markdown", - "id": "5a3ea167-c302-41f7-906e-60fd0e5cd004", + "id": "7132e2c0-d937-4325-a30e-e715c5304fe0", "metadata": {}, "source": [ - "Let's run it with some initial state!" + "Let's test it out using the same input as our original multi-agent system:" ] }, { "cell_type": "code", - "execution_count": 9, - "id": "83f50671-9371-46dd-847d-5db824c1141e", + "execution_count": 40, + "id": "29b47c57-ad05-4f10-83bf-c3ff6ff8eb93", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Game state {'wood': 10, 'food': 3, 'gold': 10, 'guard_on_duty': True}\n", + "Update from subgraph travel_advisor:\n", + "\n", + "\n", + "Update from node agent:\n", + "\n", + "\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "[{'text': \"I'll help you find a warm Caribbean destination and get some hotel recommendations for you.\\n\\nLet me first get some travel recommendations for Caribbean destinations.\", 'type': 'text'}, {'id': 'toolu_01GGDP6XSoJZFCYVA9Emhg89', 'input': {}, 'name': 'get_travel_recommendations', 'type': 'tool_use'}]\n", + "Tool Calls:\n", + " get_travel_recommendations (toolu_01GGDP6XSoJZFCYVA9Emhg89)\n", + " Call ID: toolu_01GGDP6XSoJZFCYVA9Emhg89\n", + " Args:\n", + "\n", + "\n", + "Update from subgraph travel_advisor:\n", + "\n", + "\n", + "Update from node tools:\n", "\n", "\n", - "Villager gathering resources.\n", - "Guard patrolling.\n", - "Merchant trading wood for gold.\n", - "Game state {'wood': 8, 'food': 3, 'gold': 11, 'guard_on_duty': True}\n", + "=================================\u001b[1m Tool Message \u001b[0m=================================\n", + "Name: get_travel_recommendations\n", "\n", + "turks and caicos\n", "\n", - "Villager gathering resources.\n", - "Guard patrolling.\n", - "Merchant trading wood for gold.\n", - "Game state {'wood': 6, 'food': 3, 'gold': 12, 'guard_on_duty': True}\n", "\n", + "Update from subgraph travel_advisor:\n", "\n", - "Villager gathering resources.\n", - "Guard patrolling.\n", - "Merchant trading wood for gold.\n", - "Game state {'wood': 4, 'food': 3, 'gold': 13, 'guard_on_duty': True}\n", "\n", + "Update from node agent:\n", "\n", - "Villager gathering resources.\n", - "Guard patrolling.\n", - "Game state {'wood': 7, 'food': 3, 'gold': 13, 'guard_on_duty': True}\n", "\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", "\n", - "Villager gathering resources.\n", - "Guard patrolling.\n", - "Game state {'wood': 10, 'food': 3, 'gold': 13, 'guard_on_duty': True}\n", + "[{'text': 'Based on the recommendations, I suggest Turks and Caicos! This beautiful British Overseas Territory is known for its stunning white-sand beaches, crystal-clear turquoise waters, and perfect warm weather year-round. The main island, Providenciales (often called \"Provo\"), is home to the famous Grace Bay Beach, consistently rated one of the world\\'s best beaches.\\n\\nNow, let me connect you with our hotel advisor to get some specific hotel recommendations for Turks and Caicos.', 'type': 'text'}, {'id': 'toolu_01JbPSSbTdbWSPNPwsKxifKR', 'input': {}, 'name': 'transfer_to_hotel_advisor', 'type': 'tool_use'}]\n", + "Tool Calls:\n", + " transfer_to_hotel_advisor (toolu_01JbPSSbTdbWSPNPwsKxifKR)\n", + " Call ID: toolu_01JbPSSbTdbWSPNPwsKxifKR\n", + " Args:\n", "\n", "\n", - "Villager gathering resources.\n", - "Guard patrolling.\n", - "Game state {'wood': 13, 'food': 3, 'gold': 13, 'guard_on_duty': True}\n", + "Update from subgraph hotel_advisor:\n", "\n", "\n", - "Guard patrolling.\n", - "Game state {'wood': 13, 'food': 2, 'gold': 13, 'guard_on_duty': True}\n", + "Update from node agent:\n", "\n", "\n", - "Guard patrolling.\n", - "Game state {'wood': 13, 'food': 1, 'gold': 13, 'guard_on_duty': True}\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", "\n", + "[{'text': 'Let me get some hotel recommendations for Turks and Caicos:', 'type': 'text'}, {'id': 'toolu_01JfcmUUmpdiYEFXaDFEkh1G', 'input': {'location': 'turks and caicos'}, 'name': 'get_hotel_recommendations', 'type': 'tool_use'}]\n", + "Tool Calls:\n", + " get_hotel_recommendations (toolu_01JfcmUUmpdiYEFXaDFEkh1G)\n", + " Call ID: toolu_01JfcmUUmpdiYEFXaDFEkh1G\n", + " Args:\n", + " location: turks and caicos\n", "\n", - "Guard patrolling.\n", - "Game state {'wood': 13, 'food': 0, 'gold': 13, 'guard_on_duty': True}\n", "\n", + "Update from subgraph hotel_advisor:\n", "\n", - "Guard leaving to get food.\n", - "Game state {'wood': 13, 'food': 0, 'gold': 13, 'guard_on_duty': False}\n", "\n", + "Update from node tools:\n", "\n", - "Thief stealing gold.\n", - "Game state {'wood': 13, 'food': 0, 'gold': 0, 'guard_on_duty': False}\n", + "\n", + "=================================\u001b[1m Tool Message \u001b[0m=================================\n", + "Name: get_hotel_recommendations\n", + "\n", + "[\"Grace Bay Club\", \"COMO Parrot Cay\"]\n", + "\n", + "\n", + "Update from subgraph hotel_advisor:\n", + "\n", + "\n", + "Update from node agent:\n", + "\n", + "\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "Here are two excellent hotel options in Turks and Caicos:\n", + "\n", + "1. Grace Bay Club: This luxury resort is located on the world-famous Grace Bay Beach. It offers elegant accommodations, multiple swimming pools, a spa, and several dining options. The resort is divided into different sections including adults-only and family-friendly areas.\n", + "\n", + "2. COMO Parrot Cay: This exclusive private island resort offers the ultimate luxury escape. It features pristine beaches, world-class spa facilities, and exceptional dining experiences. The resort is known for its serene atmosphere and excellent service.\n", + "\n", + "Would you like more specific information about either of these properties?\n", "\n", "\n" ] } ], "source": [ - "initial_state = {\"wood\": 10, \"food\": 3, \"gold\": 10, \"guard_on_duty\": True}\n", - "for state in graph.stream(initial_state, stream_mode=\"values\"):\n", - " print(\"Game state\", state)\n", - " print(\"\\n\")" + "for chunk in graph.stream(\n", + " {\n", + " \"messages\": [\n", + " (\n", + " \"user\",\n", + " \"i wanna go somewhere warm in the caribbean. pick one destination and give me hotel recommendations\",\n", + " )\n", + " ]\n", + " },\n", + " subgraphs=True,\n", + "):\n", + " pretty_print_messages(chunk)" ] } ], diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 30adba64e..afb72fffe 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -201,7 +201,10 @@ nav: - how-tos/subgraphs-manage-state.ipynb - how-tos/subgraph-transform-state.ipynb - Multi-agent: + - Multi-agent: how-tos#multi-agent + - how-tos/agent-handoffs.ipynb - how-tos/multi-agent-network.ipynb + - how-tos/multi-agent-multi-turn-convo.ipynb - State Management: - State Management: how-tos#state-management - how-tos/state-model.ipynb diff --git a/libs/langgraph/langgraph/graph/message.py b/libs/langgraph/langgraph/graph/message.py index 6575bd10c..e63ebee83 100644 --- a/libs/langgraph/langgraph/graph/message.py +++ b/libs/langgraph/langgraph/graph/message.py @@ -1,8 +1,21 @@ import uuid -from typing import Annotated, TypedDict, Union, cast +import warnings +from functools import partial +from typing import ( + Annotated, + Any, + Callable, + Literal, + Optional, + Sequence, + TypedDict, + Union, + cast, +) from langchain_core.messages import ( AnyMessage, + BaseMessage, BaseMessageChunk, MessageLikeRepresentation, RemoveMessage, @@ -15,7 +28,32 @@ Messages = Union[list[MessageLikeRepresentation], MessageLikeRepresentation] -def add_messages(left: Messages, right: Messages) -> Messages: +def _add_messages_wrapper(func: Callable) -> Callable[[Messages, Messages], Messages]: + def _add_messages( + left: Optional[Messages] = None, right: Optional[Messages] = None, **kwargs: Any + ) -> Union[Messages, Callable[[Messages, Messages], Messages]]: + if left is not None and right is not None: + return func(left, right, **kwargs) + elif left is not None or right is not None: + msg = ( + f"Must specify non-null arguments for both 'left' and 'right'. Only " + f"received: '{'left' if left else 'right'}'." + ) + raise ValueError(msg) + else: + return partial(func, **kwargs) + + _add_messages.__doc__ = func.__doc__ + return cast(Callable[[Messages, Messages], Messages], _add_messages) + + +@_add_messages_wrapper +def add_messages( + left: Messages, + right: Messages, + *, + format: Optional[Literal["langchain-openai"]] = None, +) -> Messages: """Merges two lists of messages, updating existing messages by ID. By default, this ensures the state is "append-only", unless the @@ -25,6 +63,14 @@ def add_messages(left: Messages, right: Messages) -> Messages: left: The base list of messages. right: The list of messages (or single message) to merge into the base list. + format: The format to return messages in. If None then messages will be + returned as is. If 'langchain-openai' then messages will be returned as + BaseMessage objects with their contents formatted to match OpenAI message + format, meaning contents can be string, 'text' blocks, or 'image_url' blocks + and tool responses are returned as their own ToolMessages. + + **REQUIREMENT**: Must have ``langchain-core>=0.3.11`` installed to use this + feature. Returns: A new list of messages with the messages from `right` merged into `left`. @@ -58,8 +104,59 @@ def add_messages(left: Messages, right: Messages) -> Messages: >>> graph = builder.compile() >>> graph.invoke({}) {'messages': [AIMessage(content='Hello', id=...)]} + + >>> from typing import Annotated + >>> from typing_extensions import TypedDict + >>> from langgraph.graph import StateGraph, add_messages + >>> + >>> class State(TypedDict): + ... messages: Annotated[list, add_messages(format='langchain-openai')] + ... + >>> def chatbot_node(state: State) -> list: + ... return {"messages": [ + ... { + ... "role": "user", + ... "content": [ + ... { + ... "type": "text", + ... "text": "Here's an image:", + ... "cache_control": {"type": "ephemeral"}, + ... }, + ... { + ... "type": "image", + ... "source": { + ... "type": "base64", + ... "media_type": "image/jpeg", + ... "data": "1234", + ... }, + ... }, + ... ] + ... }, + ... ]} + >>> builder = StateGraph(State) + >>> builder.add_node("chatbot", chatbot_node) + >>> builder.set_entry_point("chatbot") + >>> builder.set_finish_point("chatbot") + >>> graph = builder.compile() + >>> graph.invoke({"messages": []}) + { + 'messages': [ + HumanMessage( + content=[ + {"type": "text", "text": "Here's an image:"}, + { + "type": "image_url", + "image_url": {"url": "data:image/jpeg;base64,1234"}, + }, + ], + ), + ] + } ``` + ..versionchanged:: 0.2.61 + + Support for 'format="langchain-openai"' flag added. """ # coerce to list if not isinstance(left, list): @@ -100,6 +197,15 @@ def add_messages(left: Messages, right: Messages) -> Messages: merged.append(m) merged = [m for m in merged if m.id not in ids_to_remove] + + if format == "langchain-openai": + merged = _format_messages(merged) + elif format: + msg = f"Unrecognized {format=}. Expected one of 'langchain-openai', None." + raise ValueError(msg) + else: + pass + return merged @@ -156,3 +262,19 @@ def __init__(self) -> None: class MessagesState(TypedDict): messages: Annotated[list[AnyMessage], add_messages] + + +def _format_messages(messages: Sequence[BaseMessage]) -> list[BaseMessage]: + try: + from langchain_core.messages import convert_to_openai_messages + except ImportError: + msg = ( + "Must have langchain-core>=0.3.11 installed to use automatic message " + "formatting (format='langchain-openai'). Please update your langchain-core " + "version or remove the 'format' flag. Returning un-formatted " + "messages." + ) + warnings.warn(msg) + return list(messages) + else: + return convert_to_messages(convert_to_openai_messages(messages)) diff --git a/libs/langgraph/langgraph/graph/state.py b/libs/langgraph/langgraph/graph/state.py index 7a5614f91..e412db2d5 100644 --- a/libs/langgraph/langgraph/graph/state.py +++ b/libs/langgraph/langgraph/graph/state.py @@ -961,8 +961,12 @@ def _is_field_binop(typ: Type[Any]) -> Optional[BinaryOperatorAggregate]: if len(meta) >= 1 and callable(meta[-1]): sig = signature(meta[-1]) params = list(sig.parameters.values()) - if len(params) == 2 and all( - p.kind in (p.POSITIONAL_ONLY, p.POSITIONAL_OR_KEYWORD) for p in params + if ( + sum( + p.kind in (p.POSITIONAL_ONLY, p.POSITIONAL_OR_KEYWORD) + for p in params + ) + == 2 ): return BinaryOperatorAggregate(typ, meta[-1]) else: diff --git a/libs/langgraph/langgraph/prebuilt/chat_agent_executor.py b/libs/langgraph/langgraph/prebuilt/chat_agent_executor.py index 4c8699360..b5207d167 100644 --- a/libs/langgraph/langgraph/prebuilt/chat_agent_executor.py +++ b/libs/langgraph/langgraph/prebuilt/chat_agent_executor.py @@ -382,6 +382,8 @@ class Agent,Tools otherClass ```pycon >>> from typing import TypedDict + >>> + >>> from langgraph.managed import IsLastStep >>> prompt = ChatPromptTemplate.from_messages( ... [ ... ("system", "Today is {today}"), @@ -392,7 +394,7 @@ class Agent,Tools otherClass >>> class CustomState(TypedDict): ... today: str ... messages: Annotated[list[BaseMessage], add_messages] - ... is_last_step: str + ... is_last_step: IsLastStep >>> >>> graph = create_react_agent(model, tools, state_schema=CustomState, state_modifier=prompt) >>> inputs = {"messages": [("user", "What's today's date? And what's the weather in SF?")], "today": "July 16, 2004"} diff --git a/libs/langgraph/langgraph/types.py b/libs/langgraph/langgraph/types.py index 7dc3aeec2..53dc6bd57 100644 --- a/libs/langgraph/langgraph/types.py +++ b/libs/langgraph/langgraph/types.py @@ -271,7 +271,7 @@ class Command(Generic[N], ToolOutputMixin): """ graph: Optional[str] = None - update: Any = () + update: Optional[Any] = None resume: Optional[Union[Any, dict[str, Any]]] = None goto: Union[Send, Sequence[Union[Send, str]], str] = () @@ -292,8 +292,10 @@ def _update_as_tuples(self) -> Sequence[tuple[str, Any]]: for t in self.update ): return self.update - else: + elif self.update is not None: return [("__root__", self.update)] + else: + return [] PARENT: ClassVar[Literal["__parent__"]] = "__parent__" diff --git a/libs/langgraph/poetry.lock b/libs/langgraph/poetry.lock index bdb2a4da6..deace7b65 100644 --- a/libs/langgraph/poetry.lock +++ b/libs/langgraph/poetry.lock @@ -1325,18 +1325,18 @@ files = [ [[package]] name = "langchain-core" -version = "0.3.23" +version = "0.3.25" description = "Building applications with LLMs through composability" optional = false python-versions = "<4.0,>=3.9" files = [ - {file = "langchain_core-0.3.23-py3-none-any.whl", hash = "sha256:550c0b996990830fa6515a71a1192a8a0343367999afc36d4ede14222941e420"}, - {file = "langchain_core-0.3.23.tar.gz", hash = "sha256:f9e175e3b82063cc3b160c2ca2b155832e1c6f915312e1204828f97d4aabf6e1"}, + {file = "langchain_core-0.3.25-py3-none-any.whl", hash = "sha256:e10581c6c74ba16bdc6fdf16b00cced2aa447cc4024ed19746a1232918edde38"}, + {file = "langchain_core-0.3.25.tar.gz", hash = "sha256:fdb8df41e5cdd928c0c2551ebbde1cea770ee3c64598395367ad77ddf9acbae7"}, ] [package.dependencies] jsonpatch = ">=1.33,<2.0" -langsmith = ">=0.1.125,<0.2.0" +langsmith = ">=0.1.125,<0.3" packaging = ">=23.2,<25" pydantic = [ {version = ">=2.5.2,<3.0.0", markers = "python_full_version < \"3.12.4\""}, @@ -1348,7 +1348,7 @@ typing-extensions = ">=4.7" [[package]] name = "langgraph-checkpoint" -version = "2.0.8" +version = "2.0.9" description = "Library with base interfaces for LangGraph checkpoint savers." optional = false python-versions = "^3.9.0,<4.0" @@ -1418,7 +1418,7 @@ url = "../checkpoint-sqlite" [[package]] name = "langgraph-sdk" -version = "0.1.43" +version = "0.1.47" description = "SDK for interacting with LangGraph API" optional = false python-versions = "^3.9.0,<4.0" diff --git a/libs/langgraph/pyproject.toml b/libs/langgraph/pyproject.toml index 35c47866a..4197552bd 100644 --- a/libs/langgraph/pyproject.toml +++ b/libs/langgraph/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "langgraph" -version = "0.2.59" +version = "0.2.60" description = "Building stateful, multi-actor applications with LLMs" authors = [] license = "MIT" diff --git a/libs/langgraph/tests/test_messages_state.py b/libs/langgraph/tests/test_messages_state.py index ff8d064d6..787774baf 100644 --- a/libs/langgraph/tests/test_messages_state.py +++ b/libs/langgraph/tests/test_messages_state.py @@ -1,6 +1,7 @@ from typing import Annotated from uuid import UUID +import langchain_core import pytest from langchain_core.messages import ( AIMessage, @@ -8,9 +9,11 @@ HumanMessage, RemoveMessage, SystemMessage, + ToolMessage, ) from pydantic import BaseModel from pydantic.v1 import BaseModel as BaseModelV1 +from typing_extensions import TypedDict from langgraph.graph import add_messages from langgraph.graph.message import MessagesState @@ -18,6 +21,8 @@ from tests.conftest import IS_LANGCHAIN_CORE_030_OR_GREATER from tests.messages import _AnyIdHumanMessage +_, CORE_MINOR, CORE_PATCH = (int(v) for v in langchain_core.__version__.split(".")) + def test_add_single_message(): left = [HumanMessage(content="Hello", id="1")] @@ -178,3 +183,108 @@ def foo(state): _AnyIdHumanMessage(content="foo"), ] } + + +@pytest.mark.skipif( + condition=not ((CORE_MINOR == 3 and CORE_PATCH >= 11) or CORE_MINOR > 3), + reason="Requires langchain_core>=0.3.11.", +) +def test_messages_state_format_openai(): + class State(TypedDict): + messages: Annotated[list[AnyMessage], add_messages(format="langchain-openai")] + + def foo(state): + messages = [ + HumanMessage( + content=[ + { + "type": "text", + "text": "Here's an image:", + "cache_control": {"type": "ephemeral"}, + }, + { + "type": "image", + "source": { + "type": "base64", + "media_type": "image/jpeg", + "data": "1234", + }, + }, + ] + ), + AIMessage( + content=[ + { + "type": "tool_use", + "name": "foo", + "input": {"bar": "baz"}, + "id": "1", + } + ] + ), + HumanMessage( + content=[ + { + "type": "tool_result", + "tool_use_id": "1", + "is_error": False, + "content": [ + { + "type": "image", + "source": { + "type": "base64", + "media_type": "image/jpeg", + "data": "1234", + }, + }, + ], + } + ] + ), + ] + return {"messages": messages} + + expected = [ + HumanMessage(content="meow"), + HumanMessage( + content=[ + {"type": "text", "text": "Here's an image:"}, + { + "type": "image_url", + "image_url": {"url": "data:image/jpeg;base64,1234"}, + }, + ], + ), + AIMessage( + content="", + tool_calls=[ + { + "name": "foo", + "type": "tool_calls", + "args": {"bar": "baz"}, + "id": "1", + } + ], + ), + ToolMessage( + content=[ + { + "type": "image_url", + "image_url": {"url": "data:image/jpeg;base64,1234"}, + } + ], + tool_call_id="1", + ), + ] + + graph = StateGraph(State) + graph.add_edge(START, "foo") + graph.add_edge("foo", END) + graph.add_node(foo) + + app = graph.compile() + + result = app.invoke({"messages": [("user", "meow")]}) + for m in result["messages"]: + m.id = None + assert result == {"messages": expected} diff --git a/poetry.lock b/poetry.lock index 9b0fe29f8..94f2b3db3 100644 --- a/poetry.lock +++ b/poetry.lock @@ -6748,22 +6748,22 @@ files = [ [[package]] name = "tornado" -version = "6.4.1" +version = "6.4.2" description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." optional = false python-versions = ">=3.8" files = [ - {file = "tornado-6.4.1-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:163b0aafc8e23d8cdc3c9dfb24c5368af84a81e3364745ccb4427669bf84aec8"}, - {file = "tornado-6.4.1-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6d5ce3437e18a2b66fbadb183c1d3364fb03f2be71299e7d10dbeeb69f4b2a14"}, - {file = "tornado-6.4.1-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e20b9113cd7293f164dc46fffb13535266e713cdb87bd2d15ddb336e96cfc4"}, - {file = "tornado-6.4.1-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ae50a504a740365267b2a8d1a90c9fbc86b780a39170feca9bcc1787ff80842"}, - {file = "tornado-6.4.1-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:613bf4ddf5c7a95509218b149b555621497a6cc0d46ac341b30bd9ec19eac7f3"}, - {file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:25486eb223babe3eed4b8aecbac33b37e3dd6d776bc730ca14e1bf93888b979f"}, - {file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:454db8a7ecfcf2ff6042dde58404164d969b6f5d58b926da15e6b23817950fc4"}, - {file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a02a08cc7a9314b006f653ce40483b9b3c12cda222d6a46d4ac63bb6c9057698"}, - {file = "tornado-6.4.1-cp38-abi3-win32.whl", hash = "sha256:d9a566c40b89757c9aa8e6f032bcdb8ca8795d7c1a9762910c722b1635c9de4d"}, - {file = "tornado-6.4.1-cp38-abi3-win_amd64.whl", hash = "sha256:b24b8982ed444378d7f21d563f4180a2de31ced9d8d84443907a0a64da2072e7"}, - {file = "tornado-6.4.1.tar.gz", hash = "sha256:92d3ab53183d8c50f8204a51e6f91d18a15d5ef261e84d452800d4ff6fc504e9"}, + {file = "tornado-6.4.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e828cce1123e9e44ae2a50a9de3055497ab1d0aeb440c5ac23064d9e44880da1"}, + {file = "tornado-6.4.2-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:072ce12ada169c5b00b7d92a99ba089447ccc993ea2143c9ede887e0937aa803"}, + {file = "tornado-6.4.2-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a017d239bd1bb0919f72af256a970624241f070496635784d9bf0db640d3fec"}, + {file = "tornado-6.4.2-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c36e62ce8f63409301537222faffcef7dfc5284f27eec227389f2ad11b09d946"}, + {file = "tornado-6.4.2-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bca9eb02196e789c9cb5c3c7c0f04fb447dc2adffd95265b2c7223a8a615ccbf"}, + {file = "tornado-6.4.2-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:304463bd0772442ff4d0f5149c6f1c2135a1fae045adf070821c6cdc76980634"}, + {file = "tornado-6.4.2-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:c82c46813ba483a385ab2a99caeaedf92585a1f90defb5693351fa7e4ea0bf73"}, + {file = "tornado-6.4.2-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:932d195ca9015956fa502c6b56af9eb06106140d844a335590c1ec7f5277d10c"}, + {file = "tornado-6.4.2-cp38-abi3-win32.whl", hash = "sha256:2876cef82e6c5978fde1e0d5b1f919d756968d5b4282418f3146b79b58556482"}, + {file = "tornado-6.4.2-cp38-abi3-win_amd64.whl", hash = "sha256:908b71bf3ff37d81073356a5fadcc660eb10c1476ee6e2725588626ce7e5ca38"}, + {file = "tornado-6.4.2.tar.gz", hash = "sha256:92bad5b4746e9879fd7bf1eb21dce4e3fc5128d71601f80005afa39237ad620b"}, ] [[package]]