\n",
"Full Code
\n",
" \n",
"\n",
"```python\n",
"from typing import Annotated\n",
"\n",
"from langchain_anthropic import ChatAnthropic\n",
"from langchain_community.tools.tavily_search import TavilySearchResults\n",
"from langchain_core.tools import tool\n",
"from typing_extensions import TypedDict\n",
"\n",
"from langgraph.checkpoint.memory import MemorySaver\n",
"from langgraph.graph import StateGraph, START, END\n",
"from langgraph.graph.message import add_messages\n",
"from langgraph.prebuilt import ToolNode, tools_condition\n",
"from langgraph.types import Command, interrupt\n",
"\n",
"\n",
"class State(TypedDict):\n",
" messages: Annotated[list, add_messages]\n",
"\n",
"\n",
"graph_builder = StateGraph(State)\n",
"\n",
"\n",
"@tool\n",
"def human_assistance(query: str) -> str:\n",
" \"\"\"Request assistance from a human.\"\"\"\n",
" human_response = interrupt({\"query\": query})\n",
" return human_response[\"data\"]\n",
"\n",
"\n",
"tool = TavilySearchResults(max_results=2)\n",
"tools = [tool, human_assistance]\n",
"llm = ChatAnthropic(model=\"claude-3-5-sonnet-20240620\")\n",
"llm_with_tools = llm.bind_tools(tools)\n",
"\n",
"\n",
"def chatbot(state: State):\n",
" message = llm_with_tools.invoke(state[\"messages\"])\n",
" assert(len(message.tool_calls) <= 1)\n",
" return {\"messages\": [message]}\n",
"\n",
"\n",
"graph_builder.add_node(\"chatbot\", chatbot)\n",
"\n",
"tool_node = ToolNode(tools=tools)\n",
"graph_builder.add_node(\"tools\", tool_node)\n",
"\n",
"graph_builder.add_conditional_edges(\n",
" \"chatbot\",\n",
" tools_condition,\n",
")\n",
"graph_builder.add_edge(\"tools\", \"chatbot\")\n",
"graph_builder.add_edge(START, \"chatbot\")\n",
"\n",
"memory = MemorySaver()\n",
"graph = graph_builder.compile(checkpointer=memory)\n",
"```\n",
"\n",
" "
]
},
{
"cell_type": "markdown",
"id": "0d12578b-ff19-48b5-b1ad-67d9bf7f710e",
"metadata": {},
"source": [
"## Part 5: Customizing State\n",
"\n",
"So far, we've relied on a simple state with one entry-- a list of messages. You can go far with this simple state, but if you want to define complex behavior without relying on the message list, you can add additional fields to the state. Here we will demonstrate a new scenario, in which the chatbot is using its search tool to find specific information, and forwarding them to a human for review. Let's have the chatbot research the birthday of an entity. We will add `name` and `birthday` keys to the state:"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "84627103-bcc8-4645-bd37-b93209fa09dd",
"metadata": {},
"outputs": [],
"source": [
"from typing import Annotated\n",
"\n",
"from typing_extensions import TypedDict\n",
"\n",
"from langgraph.graph.message import add_messages\n",
"\n",
"\n",
"class State(TypedDict):\n",
" messages: Annotated[list, add_messages]\n",
" # highlight-next-line\n",
" name: str\n",
" # highlight-next-line\n",
" birthday: str"
]
},
{
"cell_type": "markdown",
"id": "c0057133-3dfd-4208-a11f-9cf7c0b82587",
"metadata": {},
"source": [
"Adding this information to the state makes it easily accessible by other graph nodes (e.g., a downstream node that stores or processes the information), as well as the graph's persistence layer.\n",
"\n",
"Here, we will populate the state keys inside of our `human_assistance` tool. This allows a human to review the information before it is stored in the state. We will again use `Command`, this time to issue a state update from inside our tool. Read more about use cases for `Command` [here](../../concepts/low_level/#using-inside-tools)."
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "c4b65504-92c7-4c82-a6ee-824885d1a8a4",
"metadata": {},
"outputs": [],
"source": [
"from langchain_core.messages import ToolMessage\n",
"from langchain_core.tools import InjectedToolCallId, tool\n",
"\n",
"from langgraph.types import Command, interrupt\n",
"\n",
"\n",
"@tool\n",
"# Note that because we are generating a ToolMessage for a state update, we\n",
"# generally require the ID of the corresponding tool call. We can use\n",
"# LangChain's InjectedToolCallId to signal that this argument should not\n",
"# be revealed to the model in the tool's schema.\n",
"def human_assistance(\n",
" name: str, birthday: str, tool_call_id: Annotated[str, InjectedToolCallId]\n",
") -> str:\n",
" \"\"\"Request assistance from a human.\"\"\"\n",
" human_response = interrupt(\n",
" {\n",
" \"question\": \"Is this correct?\",\n",
" \"name\": name,\n",
" \"birthday\": birthday,\n",
" },\n",
" )\n",
" # If the information is correct, update the state as-is.\n",
" if human_response.get(\"correct\", \"\").lower().startswith(\"y\"):\n",
" verified_name = name\n",
" verified_birthday = birthday\n",
" response = \"Correct\"\n",
" # Otherwise, receive information from the human reviewer.\n",
" else:\n",
" verified_name = human_response.get(\"name\", name)\n",
" verified_birthday = human_response.get(\"birthday\", birthday)\n",
" response = f\"Made a correction: {human_response}\"\n",
"\n",
" # This time we explicitly update the state with a ToolMessage inside\n",
" # the tool.\n",
" state_update = {\n",
" \"name\": verified_name,\n",
" \"birthday\": verified_birthday,\n",
" \"messages\": [ToolMessage(response, tool_call_id=tool_call_id)],\n",
" }\n",
" # We return a Command object in the tool to update our state.\n",
" return Command(update=state_update)"
]
},
{
"cell_type": "markdown",
"id": "268757ca-4b72-4fc1-a482-d878dd1bc0d9",
"metadata": {},
"source": [
"Otherwise, the rest of our graph is the same:"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "e256fa2e-6c13-42dd-a0a5-d206ee4139bf",
"metadata": {},
"outputs": [],
"source": [
"from langchain_anthropic import ChatAnthropic\n",
"from langchain_community.tools.tavily_search import TavilySearchResults\n",
"\n",
"from langgraph.checkpoint.memory import MemorySaver\n",
"from langgraph.graph import StateGraph, START, END\n",
"from langgraph.prebuilt import ToolNode, tools_condition\n",
"\n",
"\n",
"tool = TavilySearchResults(max_results=2)\n",
"tools = [tool, human_assistance]\n",
"llm = ChatAnthropic(model=\"claude-3-5-sonnet-20240620\")\n",
"llm_with_tools = llm.bind_tools(tools)\n",
"\n",
"\n",
"def chatbot(state: State):\n",
" message = llm_with_tools.invoke(state[\"messages\"])\n",
" assert len(message.tool_calls) <= 1\n",
" return {\"messages\": [message]}\n",
"\n",
"\n",
"graph_builder = StateGraph(State)\n",
"graph_builder.add_node(\"chatbot\", chatbot)\n",
"\n",
"tool_node = ToolNode(tools=tools)\n",
"graph_builder.add_node(\"tools\", tool_node)\n",
"\n",
"graph_builder.add_conditional_edges(\n",
" \"chatbot\",\n",
" tools_condition,\n",
")\n",
"graph_builder.add_edge(\"tools\", \"chatbot\")\n",
"graph_builder.add_edge(START, \"chatbot\")\n",
"\n",
"memory = MemorySaver()\n",
"graph = graph_builder.compile(checkpointer=memory)"
]
},
{
"cell_type": "markdown",
"id": "e9f77638-f957-4e1b-abcf-e9132c039be5",
"metadata": {},
"source": [
"Let's prompt our application to look up the \"birthday\" of the LangGraph library. We will direct the chatbot to reach out to the `human_assistance` tool once it has the required information. Note that setting `name` and `birthday` in the arguments for the tool, we force the chatbot to generate proposals for these fields."
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "10701e49-d4b4-46db-bb30-f895fdf4411d",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"================================\u001b[1m Human Message \u001b[0m=================================\n",
"\n",
"Can you look up when LangGraph was released? When you have the answer, use the human_assistance tool for review.\n",
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
"\n",
"[{'text': \"Certainly! I'll start by searching for information about LangGraph's release date using the Tavily search function. Then, I'll use the human_assistance tool for review.\", 'type': 'text'}, {'id': 'toolu_01JoXQPgTVJXiuma8xMVwqAi', 'input': {'query': 'LangGraph release date'}, 'name': 'tavily_search_results_json', 'type': 'tool_use'}]\n",
"Tool Calls:\n",
" tavily_search_results_json (toolu_01JoXQPgTVJXiuma8xMVwqAi)\n",
" Call ID: toolu_01JoXQPgTVJXiuma8xMVwqAi\n",
" Args:\n",
" query: LangGraph release date\n",
"=================================\u001b[1m Tool Message \u001b[0m=================================\n",
"Name: tavily_search_results_json\n",
"\n",
"[{\"url\": \"https://blog.langchain.dev/langgraph-cloud/\", \"content\": \"We also have a new stable release of LangGraph. By LangChain 6 min read Jun 27, 2024 (Oct '24) Edit: Since the launch of LangGraph Cloud, we now have multiple deployment options alongside LangGraph Studio - which now fall under LangGraph Platform. LangGraph Cloud is synonymous with our Cloud SaaS deployment option.\"}, {\"url\": \"https://changelog.langchain.com/announcements/langgraph-cloud-deploy-at-scale-monitor-carefully-iterate-boldly\", \"content\": \"LangChain - Changelog | ☁ 🚀 LangGraph Cloud: Deploy at scale, monitor LangChain LangSmith LangGraph LangChain LangSmith LangGraph LangChain LangSmith LangGraph LangChain Changelog Sign up for our newsletter to stay up to date DATE: The LangChain Team LangGraph LangGraph Cloud ☁ 🚀 LangGraph Cloud: Deploy at scale, monitor carefully, iterate boldly DATE: June 27, 2024 AUTHOR: The LangChain Team LangGraph Cloud is now in closed beta, offering scalable, fault-tolerant deployment for LangGraph agents. LangGraph Cloud also includes a new playground-like studio for debugging agent failure modes and quick iteration: Join the waitlist today for LangGraph Cloud. And to learn more, read our blog post announcement or check out our docs. Subscribe By clicking subscribe, you accept our privacy policy and terms and conditions.\"}]\n",
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
"\n",
"[{'text': \"Based on the search results, it appears that LangGraph was already in existence before June 27, 2024, when LangGraph Cloud was announced. However, the search results don't provide a specific release date for the original LangGraph. \\n\\nGiven this information, I'll use the human_assistance tool to review and potentially provide more accurate information about LangGraph's initial release date.\", 'type': 'text'}, {'id': 'toolu_01JDQAV7nPqMkHHhNs3j3XoN', 'input': {'name': 'Assistant', 'birthday': '2023-01-01'}, 'name': 'human_assistance', 'type': 'tool_use'}]\n",
"Tool Calls:\n",
" human_assistance (toolu_01JDQAV7nPqMkHHhNs3j3XoN)\n",
" Call ID: toolu_01JDQAV7nPqMkHHhNs3j3XoN\n",
" Args:\n",
" name: Assistant\n",
" birthday: 2023-01-01\n"
]
}
],
"source": [
"user_input = (\n",
" \"Can you look up when LangGraph was released? \"\n",
" \"When you have the answer, use the human_assistance tool for review.\"\n",
")\n",
"config = {\"configurable\": {\"thread_id\": \"1\"}}\n",
"\n",
"events = graph.stream(\n",
" {\"messages\": [{\"role\": \"user\", \"content\": user_input}]},\n",
" config,\n",
" stream_mode=\"values\",\n",
")\n",
"for event in events:\n",
" if \"messages\" in event:\n",
" event[\"messages\"][-1].pretty_print()"
]
},
{
"cell_type": "markdown",
"id": "99dcaf45-e4d1-4597-b669-13a5330740ac",
"metadata": {},
"source": [
"We've hit the `interrupt` in the `human_assistance` tool again. In this case, the chatbot failed to identify the correct date, so we can supply it:"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "7df33d9e-cc76-4a0f-8307-01e619483b3e",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
"\n",
"[{'text': \"Based on the search results, it appears that LangGraph was already in existence before June 27, 2024, when LangGraph Cloud was announced. However, the search results don't provide a specific release date for the original LangGraph. \\n\\nGiven this information, I'll use the human_assistance tool to review and potentially provide more accurate information about LangGraph's initial release date.\", 'type': 'text'}, {'id': 'toolu_01JDQAV7nPqMkHHhNs3j3XoN', 'input': {'name': 'Assistant', 'birthday': '2023-01-01'}, 'name': 'human_assistance', 'type': 'tool_use'}]\n",
"Tool Calls:\n",
" human_assistance (toolu_01JDQAV7nPqMkHHhNs3j3XoN)\n",
" Call ID: toolu_01JDQAV7nPqMkHHhNs3j3XoN\n",
" Args:\n",
" name: Assistant\n",
" birthday: 2023-01-01\n",
"=================================\u001b[1m Tool Message \u001b[0m=================================\n",
"Name: human_assistance\n",
"\n",
"Made a correction: {'name': 'LangGraph', 'birthday': 'Jan 17, 2024'}\n",
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
"\n",
"Thank you for the human assistance. I can now provide you with the correct information about LangGraph's release date.\n",
"\n",
"LangGraph was initially released on January 17, 2024. This information comes from the human assistance correction, which is more accurate than the search results I initially found.\n",
"\n",
"To summarize:\n",
"1. LangGraph's original release date: January 17, 2024\n",
"2. LangGraph Cloud announcement: June 27, 2024\n",
"\n",
"It's worth noting that LangGraph had been in development and use for some time before the LangGraph Cloud announcement, but the official initial release of LangGraph itself was on January 17, 2024.\n"
]
}
],
"source": [
"human_command = Command(\n",
" resume={\n",
" \"name\": \"LangGraph\",\n",
" \"birthday\": \"Jan 17, 2024\",\n",
" },\n",
")\n",
"\n",
"events = graph.stream(human_command, config, stream_mode=\"values\")\n",
"for event in events:\n",
" if \"messages\" in event:\n",
" event[\"messages\"][-1].pretty_print()"
]
},
{
"cell_type": "markdown",
"id": "31581965-8d7d-4378-82b2-f5c2a84a54b1",
"metadata": {},
"source": [
"Note that these fields are now reflected in the state:"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "b9fe6275-489d-4a6f-b19f-f1000d4133a0",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'name': 'LangGraph', 'birthday': 'Jan 17, 2024'}"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"snapshot = graph.get_state(config)\n",
"\n",
"{k: v for k, v in snapshot.values.items() if k in (\"name\", \"birthday\")}"
]
},
{
"cell_type": "markdown",
"id": "14593643-aa81-4b40-9c2d-9408bcaf88cb",
"metadata": {},
"source": [
"This makes them easily accessible to downstream nodes (e.g., a node that further processes or stores the information)."
]
},
{
"cell_type": "markdown",
"id": "238c359a-24ca-4fbf-8f6c-28a347fee2f2",
"metadata": {},
"source": [
"### Manually updating state\n",
"\n",
"LangGraph gives a high degree of control over the application state. For instance, at any point (including when interrupted), we can manually override a key using `graph.update_state`:"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "b206e600-91f0-4f46-9587-ab00c05899de",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'configurable': {'thread_id': '1',\n",
" 'checkpoint_ns': '',\n",
" 'checkpoint_id': '1efd4ec5-cf69-6352-8006-9278f1730162'}}"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"graph.update_state(config, {\"name\": \"LangGraph (library)\"})"
]
},
{
"cell_type": "markdown",
"id": "3dfb8268-8c5a-4022-9189-6edd231436ae",
"metadata": {},
"source": [
"If we call `graph.get_state`, we can see the new value is reflected:"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "10adcb89-55ab-4076-bc81-4488eff9e6b3",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'name': 'LangGraph (library)', 'birthday': 'Jan 17, 2024'}"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"snapshot = graph.get_state(config)\n",
"\n",
"{k: v for k, v in snapshot.values.items() if k in (\"name\", \"birthday\")}"
]
},
{
"cell_type": "markdown",
"id": "ab34ef46-a836-4dbd-b1ae-56c5e8dc75af",
"metadata": {},
"source": [
"Manual state updates will even [generate a trace](https://smith.langchain.com/public/7ebb7827-378d-49fe-9f6c-5df0e90086c8/r) in LangSmith. If desired, they can also be used to control human-in-the-loop workflows, as described in [this guide](../../how-tos/human_in_the_loop/edit-graph-state/). Use of the `interrupt` function is generally recommended instead, as it allows data to be transmitted in a human-in-the-loop interaction independently of state updates.\n",
"\n",
"**Congratulations!** You've added custom keys to the state to facilitate a more complex workflow, and learned how to generate state updates from inside tools.\n",
"\n",
"We're almost done with the tutorial, but there is one more concept we'd like to review before finishing that connects `checkpointing` and `state updates`. \n",
"\n",
"This section's code is reproduced below for your reference.\n",
"\n",
"\n",
"\n",
"Full Code
\n",
" \n",
"\n",
"```python\n",
"from typing import Annotated\n",
"\n",
"from langchain_anthropic import ChatAnthropic\n",
"from langchain_community.tools.tavily_search import TavilySearchResults\n",
"from langchain_core.messages import ToolMessage\n",
"from langchain_core.tools import InjectedToolCallId, tool\n",
"from typing_extensions import TypedDict\n",
"\n",
"from langgraph.checkpoint.memory import MemorySaver\n",
"from langgraph.graph import StateGraph, START, END\n",
"from langgraph.graph.message import add_messages\n",
"from langgraph.prebuilt import ToolNode, tools_condition\n",
"from langgraph.types import Command, interrupt\n",
"\n",
"\n",
"\n",
"class State(TypedDict):\n",
" messages: Annotated[list, add_messages]\n",
" name: str\n",
" birthday: str\n",
"\n",
"\n",
"@tool\n",
"def human_assistance(\n",
" name: str, birthday: str, tool_call_id: Annotated[str, InjectedToolCallId]\n",
") -> str:\n",
" \"\"\"Request assistance from a human.\"\"\"\n",
" human_response = interrupt(\n",
" {\n",
" \"question\": \"Is this correct?\",\n",
" \"name\": name,\n",
" \"birthday\": birthday,\n",
" },\n",
" )\n",
" if human_response.get(\"correct\", \"\").lower().startswith(\"y\"):\n",
" verified_name = name\n",
" verified_birthday = birthday\n",
" response = \"Correct\"\n",
" else:\n",
" verified_name = human_response.get(\"name\", name)\n",
" verified_birthday = human_response.get(\"birthday\", birthday)\n",
" response = f\"Made a correction: {human_response}\"\n",
"\n",
" state_update = {\n",
" \"name\": verified_name,\n",
" \"birthday\": verified_birthday,\n",
" \"messages\": [ToolMessage(response, tool_call_id=tool_call_id)],\n",
" }\n",
" return Command(update=state_update)\n",
"\n",
"\n",
"tool = TavilySearchResults(max_results=2)\n",
"tools = [tool, human_assistance]\n",
"llm = ChatAnthropic(model=\"claude-3-5-sonnet-20240620\")\n",
"llm_with_tools = llm.bind_tools(tools)\n",
"\n",
"\n",
"def chatbot(state: State):\n",
" message = llm_with_tools.invoke(state[\"messages\"])\n",
" assert(len(message.tool_calls) <= 1)\n",
" return {\"messages\": [message]}\n",
"\n",
"\n",
"graph_builder = StateGraph(State)\n",
"graph_builder.add_node(\"chatbot\", chatbot)\n",
"\n",
"tool_node = ToolNode(tools=tools)\n",
"graph_builder.add_node(\"tools\", tool_node)\n",
"\n",
"graph_builder.add_conditional_edges(\n",
" \"chatbot\",\n",
" tools_condition,\n",
")\n",
"graph_builder.add_edge(\"tools\", \"chatbot\")\n",
"graph_builder.add_edge(START, \"chatbot\")\n",
"\n",
"memory = MemorySaver()\n",
"graph = graph_builder.compile(checkpointer=memory)\n",
"```\n",
"\n",
" "
]
},
{
"cell_type": "markdown",
"id": "05283db2-2f26-4800-8eda-78a4468a3d8f",
"metadata": {},
"source": [
"## Part 6: Time Travel\n",
"\n",
"In a typical chat bot workflow, the user interacts with the bot 1 or more times to accomplish a task. In the previous sections, we saw how to add memory and a human-in-the-loop to be able to checkpoint our graph state and control future responses.\n",
"\n",
"But what if you want to let your user start from a previous response and \"branch off\" to explore a separate outcome? Or what if you want users to be able to \"rewind\" your assistant's work to fix some mistakes or try a different strategy (common in applications like autonomous software engineers)?\n",
"\n",
"You can create both of these experiences and more using LangGraph's built-in \"time travel\" functionality. \n",
"\n",
"In this section, you will \"rewind\" your graph by fetching a checkpoint using the graph's `get_state_history` method. You can then resume execution at this previous point in time.\n",
"\n",
"For this, let's use the simple chatbot with tools from [Part 3](#part-3-adding-memory-to-the-chatbot):"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "bb8a02de-a21b-4ef6-a714-7d6e44435e3a",
"metadata": {},
"outputs": [],
"source": [
"from typing import Annotated\n",
"\n",
"from langchain_anthropic import ChatAnthropic\n",
"from langchain_community.tools.tavily_search import TavilySearchResults\n",
"from langchain_core.messages import BaseMessage\n",
"from typing_extensions import TypedDict\n",
"\n",
"from langgraph.checkpoint.memory import MemorySaver\n",
"from langgraph.graph import StateGraph, START, END\n",
"from langgraph.graph.message import add_messages\n",
"from langgraph.prebuilt import ToolNode, tools_condition\n",
"\n",
"\n",
"class State(TypedDict):\n",
" messages: Annotated[list, add_messages]\n",
"\n",
"\n",
"graph_builder = StateGraph(State)\n",
"\n",
"\n",
"tool = TavilySearchResults(max_results=2)\n",
"tools = [tool]\n",
"llm = ChatAnthropic(model=\"claude-3-5-sonnet-20240620\")\n",
"llm_with_tools = llm.bind_tools(tools)\n",
"\n",
"\n",
"def chatbot(state: State):\n",
" return {\"messages\": [llm_with_tools.invoke(state[\"messages\"])]}\n",
"\n",
"\n",
"graph_builder.add_node(\"chatbot\", chatbot)\n",
"\n",
"tool_node = ToolNode(tools=[tool])\n",
"graph_builder.add_node(\"tools\", tool_node)\n",
"\n",
"graph_builder.add_conditional_edges(\n",
" \"chatbot\",\n",
" tools_condition,\n",
")\n",
"graph_builder.add_edge(\"tools\", \"chatbot\")\n",
"graph_builder.add_edge(START, \"chatbot\")\n",
"\n",
"memory = MemorySaver()\n",
"graph = graph_builder.compile(checkpointer=memory)"
]
},
{
"cell_type": "markdown",
"id": "5414c482-215e-4cc0-9eef-4a8722d2f468",
"metadata": {},
"source": [
"Let's have our graph take a couple steps. Every step will be checkpointed in its state history:"
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "69071b02-c011-4b7f-90b1-8e89e032322d",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"================================\u001b[1m Human Message \u001b[0m=================================\n",
"\n",
"I'm learning LangGraph. Could you do some research on it for me?\n",
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
"\n",
"[{'text': \"Certainly! I'd be happy to research LangGraph for you. To get the most up-to-date and accurate information, I'll use the Tavily search engine to look this up. Let me do that for you now.\", 'type': 'text'}, {'id': 'toolu_01BscbfJJB9EWJFqGrN6E54e', 'input': {'query': 'LangGraph latest information and features'}, 'name': 'tavily_search_results_json', 'type': 'tool_use'}]\n",
"Tool Calls:\n",
" tavily_search_results_json (toolu_01BscbfJJB9EWJFqGrN6E54e)\n",
" Call ID: toolu_01BscbfJJB9EWJFqGrN6E54e\n",
" Args:\n",
" query: LangGraph latest information and features\n",
"=================================\u001b[1m Tool Message \u001b[0m=================================\n",
"Name: tavily_search_results_json\n",
"\n",
"[{\"url\": \"https://blockchain.news/news/langchain-new-features-upcoming-events-update\", \"content\": \"LangChain, a leading platform in the AI development space, has released its latest updates, showcasing new use cases and enhancements across its ecosystem. According to the LangChain Blog, the updates cover advancements in LangGraph Cloud, LangSmith's self-improving evaluators, and revamped documentation for LangGraph.\"}, {\"url\": \"https://blog.langchain.dev/langgraph-platform-announce/\", \"content\": \"With these learnings under our belt, we decided to couple some of our latest offerings under LangGraph Platform. LangGraph Platform today includes LangGraph Server, LangGraph Studio, plus the CLI and SDK. ... we added features in LangGraph Server to deliver on a few key value areas. Below, we'll focus on these aspects of LangGraph Platform.\"}]\n",
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
"\n",
"Thank you for your patience. I've found some recent information about LangGraph for you. Let me summarize the key points:\n",
"\n",
"1. LangGraph is part of the LangChain ecosystem, which is a leading platform in AI development.\n",
"\n",
"2. Recent updates and features of LangGraph include:\n",
"\n",
" a. LangGraph Cloud: This seems to be a cloud-based version of LangGraph, though specific details weren't provided in the search results.\n",
"\n",
" b. LangGraph Platform: This is a newly introduced concept that combines several offerings:\n",
" - LangGraph Server\n",
" - LangGraph Studio\n",
" - CLI (Command Line Interface)\n",
" - SDK (Software Development Kit)\n",
"\n",
"3. LangGraph Server: This component has received new features to enhance its value proposition, though the specific features weren't detailed in the search results.\n",
"\n",
"4. LangGraph Studio: This appears to be a new tool in the LangGraph ecosystem, likely providing a graphical interface for working with LangGraph.\n",
"\n",
"5. Documentation: The LangGraph documentation has been revamped, which should make it easier for learners like yourself to understand and use the tool.\n",
"\n",
"6. Integration with LangSmith: While not directly part of LangGraph, LangSmith (another tool in the LangChain ecosystem) now features self-improving evaluators, which might be relevant if you're using LangGraph as part of a larger LangChain project.\n",
"\n",
"As you're learning LangGraph, it would be beneficial to:\n",
"\n",
"1. Check out the official LangChain documentation, especially the newly revamped LangGraph sections.\n",
"2. Explore the different components of the LangGraph Platform (Server, Studio, CLI, and SDK) to see which best fits your learning needs.\n",
"3. Keep an eye on LangGraph Cloud developments, as cloud-based solutions often provide an easier starting point for learners.\n",
"4. Consider how LangGraph fits into the broader LangChain ecosystem, especially its interaction with tools like LangSmith.\n",
"\n",
"Is there any specific aspect of LangGraph you'd like to know more about? I'd be happy to do a more focused search on particular features or use cases.\n"
]
}
],
"source": [
"config = {\"configurable\": {\"thread_id\": \"1\"}}\n",
"events = graph.stream(\n",
" {\n",
" \"messages\": [\n",
" {\n",
" \"role\": \"user\",\n",
" \"content\": (\n",
" \"I'm learning LangGraph. \"\n",
" \"Could you do some research on it for me?\"\n",
" ),\n",
" },\n",
" ],\n",
" },\n",
" config,\n",
" stream_mode=\"values\",\n",
")\n",
"for event in events:\n",
" if \"messages\" in event:\n",
" event[\"messages\"][-1].pretty_print()"
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "acbec099-e5d2-497f-929e-c548d7bcbf77",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"================================\u001b[1m Human Message \u001b[0m=================================\n",
"\n",
"Ya that's helpful. Maybe I'll build an autonomous agent with it!\n",
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
"\n",
"[{'text': \"That's an exciting idea! Building an autonomous agent with LangGraph is indeed a great application of this technology. LangGraph is particularly well-suited for creating complex, multi-step AI workflows, which is perfect for autonomous agents. Let me gather some more specific information about using LangGraph for building autonomous agents.\", 'type': 'text'}, {'id': 'toolu_01QWNHhUaeeWcGXvA4eHT7Zo', 'input': {'query': 'Building autonomous agents with LangGraph examples and tutorials'}, 'name': 'tavily_search_results_json', 'type': 'tool_use'}]\n",
"Tool Calls:\n",
" tavily_search_results_json (toolu_01QWNHhUaeeWcGXvA4eHT7Zo)\n",
" Call ID: toolu_01QWNHhUaeeWcGXvA4eHT7Zo\n",
" Args:\n",
" query: Building autonomous agents with LangGraph examples and tutorials\n",
"=================================\u001b[1m Tool Message \u001b[0m=================================\n",
"Name: tavily_search_results_json\n",
"\n",
"[{\"url\": \"https://towardsdatascience.com/building-autonomous-multi-tool-agents-with-gemini-2-0-and-langgraph-ad3d7bd5e79d\", \"content\": \"Building Autonomous Multi-Tool Agents with Gemini 2.0 and LangGraph | by Youness Mansar | Jan, 2025 | Towards Data Science Building Autonomous Multi-Tool Agents with Gemini 2.0 and LangGraph A practical tutorial with full code examples for building and running multi-tool agents Towards Data Science LLMs are remarkable — they can memorize vast amounts of information, answer general knowledge questions, write code, generate stories, and even fix your grammar. In this tutorial, we are going to build a simple LLM agent that is equipped with four tools that it can use to answer a user’s question. This Agent will have the following specifications: Follow Published in Towards Data Science --------------------------------- Your home for data science and AI. Follow Follow Follow\"}, {\"url\": \"https://github.com/anmolaman20/Tools_and_Agents\", \"content\": \"GitHub - anmolaman20/Tools_and_Agents: This repository provides resources for building AI agents using Langchain and Langgraph. This repository provides resources for building AI agents using Langchain and Langgraph. This repository provides resources for building AI agents using Langchain and Langgraph. This repository serves as a comprehensive guide for building AI-powered agents using Langchain and Langgraph. It provides hands-on examples, practical tutorials, and resources for developers and AI enthusiasts to master building intelligent systems and workflows. AI Agent Development: Gain insights into creating intelligent systems that think, reason, and adapt in real time. This repository is ideal for AI practitioners, developers exploring language models, or anyone interested in building intelligent systems. This repository provides resources for building AI agents using Langchain and Langgraph.\"}]\n",
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
"\n",
"Great idea! Building an autonomous agent with LangGraph is definitely an exciting project. Based on the latest information I've found, here are some insights and tips for building autonomous agents with LangGraph:\n",
"\n",
"1. Multi-Tool Agents: LangGraph is particularly well-suited for creating autonomous agents that can use multiple tools. This allows your agent to have a diverse set of capabilities and choose the right tool for each task.\n",
"\n",
"2. Integration with Large Language Models (LLMs): You can combine LangGraph with powerful LLMs like Gemini 2.0 to create more intelligent and capable agents. The LLM can serve as the \"brain\" of your agent, making decisions and generating responses.\n",
"\n",
"3. Workflow Management: LangGraph excels at managing complex, multi-step AI workflows. This is crucial for autonomous agents that need to break down tasks into smaller steps and execute them in the right order.\n",
"\n",
"4. Practical Tutorials Available: There are tutorials available that provide full code examples for building and running multi-tool agents. These can be incredibly helpful as you start your project.\n",
"\n",
"5. Langchain Integration: LangGraph is often used in conjunction with Langchain. This combination provides a powerful framework for building AI agents, offering features like memory management, tool integration, and prompt management.\n",
"\n",
"6. GitHub Resources: There are repositories available (like the one by anmolaman20) that provide comprehensive resources for building AI agents using Langchain and LangGraph. These can be valuable references as you develop your agent.\n",
"\n",
"7. Real-time Adaptation: LangGraph allows you to create agents that can think, reason, and adapt in real-time, which is crucial for truly autonomous behavior.\n",
"\n",
"8. Customization: You can equip your agent with specific tools tailored to your use case. For example, you might include tools for web searching, data analysis, or interacting with specific APIs.\n",
"\n",
"To get started with your autonomous agent project:\n",
"\n",
"1. Familiarize yourself with LangGraph's documentation and basic concepts.\n",
"2. Look into tutorials that specifically deal with building autonomous agents, like the one mentioned from Towards Data Science.\n",
"3. Decide on the specific capabilities you want your agent to have and identify the tools it will need.\n",
"4. Start with a simple agent and gradually add complexity as you become more comfortable with the framework.\n",
"5. Experiment with different LLMs to find the one that works best for your use case.\n",
"6. Pay attention to how you structure the agent's decision-making process and workflow.\n",
"7. Don't forget to implement proper error handling and safety measures, especially if your agent will be interacting with external systems or making important decisions.\n",
"\n",
"Building an autonomous agent is an iterative process, so be prepared to refine and improve your agent over time. Good luck with your project! If you need any more specific information as you progress, feel free to ask.\n"
]
}
],
"source": [
"events = graph.stream(\n",
" {\n",
" \"messages\": [\n",
" {\n",
" \"role\": \"user\",\n",
" \"content\": (\n",
" \"Ya that's helpful. Maybe I'll \"\n",
" \"build an autonomous agent with it!\"\n",
" ),\n",
" },\n",
" ],\n",
" },\n",
" config,\n",
" stream_mode=\"values\",\n",
")\n",
"for event in events:\n",
" if \"messages\" in event:\n",
" event[\"messages\"][-1].pretty_print()"
]
},
{
"cell_type": "markdown",
"id": "b2e48c77-65f3-4075-8030-ebf943a281f1",
"metadata": {},
"source": [
"Now that we've had the agent take a couple steps, we can `replay` the full state history to see everything that occurred."
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "40953570-66bd-45b9-9469-1d018230d88a",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Num Messages: 8 Next: ()\n",
"--------------------------------------------------------------------------------\n",
"Num Messages: 7 Next: ('chatbot',)\n",
"--------------------------------------------------------------------------------\n",
"Num Messages: 6 Next: ('tools',)\n",
"--------------------------------------------------------------------------------\n",
"Num Messages: 5 Next: ('chatbot',)\n",
"--------------------------------------------------------------------------------\n",
"Num Messages: 4 Next: ('__start__',)\n",
"--------------------------------------------------------------------------------\n",
"Num Messages: 4 Next: ()\n",
"--------------------------------------------------------------------------------\n",
"Num Messages: 3 Next: ('chatbot',)\n",
"--------------------------------------------------------------------------------\n",
"Num Messages: 2 Next: ('tools',)\n",
"--------------------------------------------------------------------------------\n",
"Num Messages: 1 Next: ('chatbot',)\n",
"--------------------------------------------------------------------------------\n",
"Num Messages: 0 Next: ('__start__',)\n",
"--------------------------------------------------------------------------------\n"
]
}
],
"source": [
"to_replay = None\n",
"for state in graph.get_state_history(config):\n",
" print(\"Num Messages: \", len(state.values[\"messages\"]), \"Next: \", state.next)\n",
" print(\"-\" * 80)\n",
" if len(state.values[\"messages\"]) == 6:\n",
" # We are somewhat arbitrarily selecting a specific state based on the number of chat messages in the state.\n",
" to_replay = state"
]
},
{
"cell_type": "markdown",
"id": "b182019e-bae3-4616-ba1b-f845c0ab6636",
"metadata": {},
"source": [
"**Notice** that checkpoints are saved for every step of the graph. This __spans invocations__ so you can rewind across a full thread's history. We've picked out `to_replay` as a state to resume from. This is the state after the `chatbot` node in the second graph invocation above.\n",
"\n",
"Resuming from this point should call the **action** node next."
]
},
{
"cell_type": "code",
"execution_count": 14,
"id": "fdcf00af-8459-4132-85cc-742199391d4f",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"('tools',)\n",
"{'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1efd43e3-0c1f-6c4e-8006-891877d65740'}}\n"
]
}
],
"source": [
"print(to_replay.next)\n",
"print(to_replay.config)"
]
},
{
"cell_type": "markdown",
"id": "7e8c61f5-3a4a-4cce-b81b-43fe1dcc971f",
"metadata": {},
"source": [
"**Notice** that the checkpoint's config (`to_replay.config`) contains a `checkpoint_id` **timestamp**. Providing this `checkpoint_id` value tells LangGraph's checkpointer to **load** the state from that moment in time. Let's try it below:"
]
},
{
"cell_type": "code",
"execution_count": 15,
"id": "c5382e81-bfcd-4508-b02a-099e3d9627fd",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
"\n",
"[{'text': \"That's an exciting idea! Building an autonomous agent with LangGraph is indeed a great application of this technology. LangGraph is particularly well-suited for creating complex, multi-step AI workflows, which is perfect for autonomous agents. Let me gather some more specific information about using LangGraph for building autonomous agents.\", 'type': 'text'}, {'id': 'toolu_01QWNHhUaeeWcGXvA4eHT7Zo', 'input': {'query': 'Building autonomous agents with LangGraph examples and tutorials'}, 'name': 'tavily_search_results_json', 'type': 'tool_use'}]\n",
"Tool Calls:\n",
" tavily_search_results_json (toolu_01QWNHhUaeeWcGXvA4eHT7Zo)\n",
" Call ID: toolu_01QWNHhUaeeWcGXvA4eHT7Zo\n",
" Args:\n",
" query: Building autonomous agents with LangGraph examples and tutorials\n",
"=================================\u001b[1m Tool Message \u001b[0m=================================\n",
"Name: tavily_search_results_json\n",
"\n",
"[{\"url\": \"https://towardsdatascience.com/building-autonomous-multi-tool-agents-with-gemini-2-0-and-langgraph-ad3d7bd5e79d\", \"content\": \"Building Autonomous Multi-Tool Agents with Gemini 2.0 and LangGraph | by Youness Mansar | Jan, 2025 | Towards Data Science Building Autonomous Multi-Tool Agents with Gemini 2.0 and LangGraph A practical tutorial with full code examples for building and running multi-tool agents Towards Data Science LLMs are remarkable — they can memorize vast amounts of information, answer general knowledge questions, write code, generate stories, and even fix your grammar. In this tutorial, we are going to build a simple LLM agent that is equipped with four tools that it can use to answer a user’s question. This Agent will have the following specifications: Follow Published in Towards Data Science --------------------------------- Your home for data science and AI. Follow Follow Follow\"}, {\"url\": \"https://github.com/anmolaman20/Tools_and_Agents\", \"content\": \"GitHub - anmolaman20/Tools_and_Agents: This repository provides resources for building AI agents using Langchain and Langgraph. This repository provides resources for building AI agents using Langchain and Langgraph. This repository provides resources for building AI agents using Langchain and Langgraph. This repository serves as a comprehensive guide for building AI-powered agents using Langchain and Langgraph. It provides hands-on examples, practical tutorials, and resources for developers and AI enthusiasts to master building intelligent systems and workflows. AI Agent Development: Gain insights into creating intelligent systems that think, reason, and adapt in real time. This repository is ideal for AI practitioners, developers exploring language models, or anyone interested in building intelligent systems. This repository provides resources for building AI agents using Langchain and Langgraph.\"}]\n",
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
"\n",
"Great idea! Building an autonomous agent with LangGraph is indeed an excellent way to apply and deepen your understanding of the technology. Based on the search results, I can provide you with some insights and resources to help you get started:\n",
"\n",
"1. Multi-Tool Agents:\n",
" LangGraph is well-suited for building autonomous agents that can use multiple tools. This allows your agent to have a variety of capabilities and choose the appropriate tool based on the task at hand.\n",
"\n",
"2. Integration with Large Language Models (LLMs):\n",
" There's a tutorial that specifically mentions using Gemini 2.0 (Google's LLM) with LangGraph to build autonomous agents. This suggests that LangGraph can be integrated with various LLMs, giving you flexibility in choosing the language model that best fits your needs.\n",
"\n",
"3. Practical Tutorials:\n",
" There are tutorials available that provide full code examples for building and running multi-tool agents. These can be invaluable as you start your project, giving you a concrete starting point and demonstrating best practices.\n",
"\n",
"4. GitHub Resources:\n",
" There's a GitHub repository (github.com/anmolaman20/Tools_and_Agents) that provides resources for building AI agents using both Langchain and Langgraph. This could be a great resource for code examples, tutorials, and understanding how LangGraph fits into the broader LangChain ecosystem.\n",
"\n",
"5. Real-Time Adaptation:\n",
" The resources mention creating intelligent systems that can think, reason, and adapt in real-time. This is a key feature of advanced autonomous agents and something you can aim for in your project.\n",
"\n",
"6. Diverse Applications:\n",
" The materials suggest that these techniques can be applied to various tasks, from answering questions to potentially more complex decision-making processes.\n",
"\n",
"To get started with your autonomous agent project using LangGraph, you might want to:\n",
"\n",
"1. Review the tutorials mentioned, especially those with full code examples.\n",
"2. Explore the GitHub repository for hands-on examples and resources.\n",
"3. Decide on the specific tasks or capabilities you want your agent to have.\n",
"4. Choose an LLM to integrate with LangGraph (like GPT, Gemini, or others).\n",
"5. Start with a simple agent that uses one or two tools, then gradually expand its capabilities.\n",
"6. Implement decision-making logic to help your agent choose between different tools or actions.\n",
"7. Test your agent thoroughly with various inputs and scenarios to ensure robust performance.\n",
"\n",
"Remember, building an autonomous agent is an iterative process. Start simple and gradually increase complexity as you become more comfortable with LangGraph and its capabilities.\n",
"\n",
"Would you like more information on any specific aspect of building your autonomous agent with LangGraph?\n"
]
}
],
"source": [
"# The `checkpoint_id` in the `to_replay.config` corresponds to a state we've persisted to our checkpointer.\n",
"for event in graph.stream(None, to_replay.config, stream_mode=\"values\"):\n",
" if \"messages\" in event:\n",
" event[\"messages\"][-1].pretty_print()"
]
},
{
"cell_type": "markdown",
"id": "c2501fed-2591-420d-98e0-4a3836fb99a8",
"metadata": {},
"source": [
"Notice that the graph resumed execution from the `**action**` node. You can tell this is the case since the first value printed above is the response from our search engine tool.\n",
"\n",
"**Congratulations!** You've now used time-travel checkpoint traversal in LangGraph. Being able to rewind and explore alternative paths opens up a world of possibilities for debugging, experimentation, and interactive applications."
]
},
{
"cell_type": "markdown",
"id": "e584d57f-5aad-4507-815f-0b2e4b64b791",
"metadata": {},
"source": [
"## Next Steps\n",
"\n",
"Take your journey further by exploring deployment and advanced features:\n",
"\n",
"### Server Quickstart\n",
"\n",
"- **[LangGraph Server Quickstart](../langgraph-platform/local-server)**: Launch a LangGraph server locally and interact with it using the REST API and LangGraph Studio Web UI.\n",
"\n",
"### LangGraph Cloud\n",
"\n",
"- **[LangGraph Cloud QuickStart](../../cloud/quick_start)**: Deploy your LangGraph app using LangGraph Cloud.\n",
"\n",
"### LangGraph Framework\n",
"\n",
"- **[LangGraph Concepts](../../concepts)**: Learn the foundational concepts of LangGraph. \n",
"- **[LangGraph How-to Guides](../../how-tos)**: Guides for common tasks with LangGraph.\n",
"\n",
"### LangGraph Platform\n",
"\n",
"Expand your knowledge with these resources:\n",
"\n",
"- **[LangGraph Platform Concepts](../../concepts#langgraph-platform)**: Understand the foundational concepts of the LangGraph Platform. \n",
"- **[LangGraph Platform How-to Guides](../../how-tos#langgraph-platform)**: Guides for common tasks with LangGraph Platform. "
]
}
],
"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.10.4"
}
},
"nbformat": 4,
"nbformat_minor": 5
}