diff --git a/17-LangGraph/01-Core-Features/06-LangGraph-Human-in-the-loop.ipynb b/17-LangGraph/01-Core-Features/06-LangGraph-Human-in-the-loop.ipynb new file mode 100644 index 000000000..6e4b59770 --- /dev/null +++ b/17-LangGraph/01-Core-Features/06-LangGraph-Human-in-the-loop.ipynb @@ -0,0 +1,776 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "635d8ebb", + "metadata": {}, + "source": [ + "# Human-in-the-loop\n", + "\n", + "- Author: [Jaemin Hong](https://github.com/geminii01)\n", + "- Peer Review: \n", + "- This is a part of [LangChain Open Tutorial](https://github.com/LangChain-OpenTutorial/LangChain-OpenTutorial)\n", + "\n", + "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/LangChain-OpenTutorial/LangChain-OpenTutorial/blob/main/99-TEMPLATE/00-BASE-TEMPLATE-EXAMPLE.ipynb) [![Open in GitHub](https://img.shields.io/badge/Open%20in%20GitHub-181717?style=flat-square&logo=github&logoColor=white)](https://github.com/LangChain-OpenTutorial/LangChain-OpenTutorial/blob/main/99-TEMPLATE/00-BASE-TEMPLATE-EXAMPLE.ipynb)\n", + "\n", + "## Overview\n", + "\n", + "This tutorial covers **Human in the loop** .\n", + "\n", + "Agents are not always reliable, and human intervention may be required to ensure tasks are executed successfully. \n", + "\n", + "In some cases, you might want human approval before proceeding to confirm that everything is functioning as intended. LangGraph supports these **Human-in-the-loop** workflows. \n", + "\n", + "In this tutorial, you'll learn how to use LangGraph's `interrupt_before` feature to pause execution, enabling human oversight and control.\n", + "\n", + "### Table of Contents\n", + "\n", + "- [Overview](#overview)\n", + "- [Environment Setup](#environment-setup)\n", + "- [Function Setup](#function-setup)\n", + "- [Graph Setup](#graph-setup)\n", + "- [Adding Human Feedback](#adding-human-feedback)\n", + "\n", + "### References\n", + "\n", + "- [Tutorials > Quick Start > Human-in-the-loop](https://langchain-ai.github.io/langgraph/tutorials/introduction/#part-4-human-in-the-loop)\n", + "- [Concepts > LangGraph > Human-in-the-loop](https://langchain-ai.github.io/langgraph/concepts/human_in_the_loop/)\n", + "----" + ] + }, + { + "cell_type": "markdown", + "id": "c6c7aba4", + "metadata": {}, + "source": [ + "## Environment Setup\n", + "\n", + "Set up the environment. You may refer to [Environment Setup](https://wikidocs.net/257836) for more details.\n", + "\n", + "**[Note]**\n", + "- `langchain-opentutorial` is a package that provides a set of easy-to-use environment setup, useful functions and utilities for tutorials. \n", + "- You can checkout the [`langchain-opentutorial`](https://github.com/LangChain-OpenTutorial/langchain-opentutorial-pypi) for more details." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "21943adb", + "metadata": {}, + "outputs": [], + "source": [ + "%%capture --no-stderr\n", + "%pip install langchain-opentutorial" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "f25ec196", + "metadata": {}, + "outputs": [], + "source": [ + "# Install required packages\n", + "from langchain_opentutorial import package\n", + "\n", + "package.install(\n", + " [\n", + " \"langsmith\",\n", + " \"langgraph\",\n", + " \"langchain_core\",\n", + " \"langchain_openai\",\n", + " ],\n", + " verbose=False,\n", + " upgrade=False,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "7f9065ea", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Environment variables have been set successfully.\n" + ] + } + ], + "source": [ + "# Set environment variables\n", + "from langchain_opentutorial import set_env\n", + "\n", + "set_env(\n", + " {\n", + " \"OPENAI_API_KEY\": \"\",\n", + " \"LANGCHAIN_API_KEY\": \"\",\n", + " \"LANGCHAIN_TRACING_V2\": \"true\",\n", + " \"LANGCHAIN_ENDPOINT\": \"https://api.smith.langchain.com\",\n", + " \"LANGCHAIN_PROJECT\": \"06-LangGraph-Human-in-the-loop\",\n", + " }\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "690a9ae0", + "metadata": {}, + "source": [ + "You can alternatively set API keys such as `OPENAI_API_KEY` in a `.env` file and load them.\n", + "\n", + "[Note] This is not necessary if you've already set the required API keys in previous steps." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "4f99b5b6", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Load API keys from .env file\n", + "from dotenv import load_dotenv\n", + "\n", + "load_dotenv(override=True)" + ] + }, + { + "cell_type": "markdown", + "id": "c61acea8", + "metadata": {}, + "source": [ + "## Function Setup\n", + "\n", + "Convenient functions for smooth execution." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "323d6d97", + "metadata": {}, + "outputs": [], + "source": [ + "import feedparser\n", + "from urllib.parse import quote\n", + "from typing import List, Dict, Optional\n", + "\n", + "\n", + "class GoogleNews:\n", + " \"\"\"\n", + " A class to search Google News.\n", + " \"\"\"\n", + "\n", + " def __init__(self):\n", + " \"\"\"\n", + " Initializes the base URL.\n", + " \"\"\"\n", + " self.base_url = \"https://news.google.com/rss\"\n", + "\n", + " def _fetch_news(self, url: str, k: int = 3) -> List[Dict[str, str]]:\n", + " \"\"\"\n", + " Fetches news from the given URL.\n", + " \"\"\"\n", + " news_data = feedparser.parse(url)\n", + " return [\n", + " {\"title\": entry.title, \"link\": entry.link}\n", + " for entry in news_data.entries[:k]\n", + " ]\n", + "\n", + " def _collect_news(self, news_list: List[Dict[str, str]]) -> List[Dict[str, str]]:\n", + " \"\"\"\n", + " Organizes and returns the news list.\n", + " \"\"\"\n", + " if not news_list:\n", + " print(\"No news found for the given keyword.\")\n", + " return []\n", + "\n", + " result = []\n", + " for news in news_list:\n", + " result.append({\"url\": news[\"link\"], \"content\": news[\"title\"]})\n", + "\n", + " return result\n", + "\n", + " def search_latest(self, k: int = 3) -> List[Dict[str, str]]:\n", + " \"\"\"\n", + " Searches for the latest news.\n", + " \"\"\"\n", + " url = f\"{self.base_url}?hl=en&gl=US&ceid=US:en\"\n", + " news_list = self._fetch_news(url, k)\n", + " return self._collect_news(news_list)\n", + "\n", + " def search_by_keyword(\n", + " self, keyword: Optional[str] = None, k: int = 3\n", + " ) -> List[Dict[str, str]]:\n", + " \"\"\"\n", + " Searches for news by keyword.\n", + " \"\"\"\n", + " if keyword:\n", + " encoded_keyword = quote(keyword)\n", + " url = f\"{self.base_url}/search?q={encoded_keyword}&hl=en&gl=US&ceid=US:en\"\n", + " else:\n", + " url = f\"{self.base_url}?hl=en&gl=US&ceid=US:en\"\n", + " news_list = self._fetch_news(url, k)\n", + " return self._collect_news(news_list)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "5ee82bae", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain_core.messages import BaseMessage\n", + "\n", + "\n", + "def pretty_print_messages(messages: list[BaseMessage]):\n", + " for message in messages:\n", + " message.pretty_print()" + ] + }, + { + "cell_type": "markdown", + "id": "07f6664a", + "metadata": {}, + "source": [ + "## Graph Setup\n", + "\n", + "This setup initializes a graph-based workflow for a chatbot, defining the state, tools, and nodes while establishing edges and a memory saver for persistent interactions." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "c52807f7", + "metadata": {}, + "outputs": [], + "source": [ + "from typing import Annotated\n", + "from typing_extensions import TypedDict\n", + "\n", + "from langchain_openai import ChatOpenAI\n", + "from langchain_core.tools import tool\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", + "###### Define the State ######\n", + "class State(TypedDict):\n", + " messages: Annotated[list, add_messages]\n", + "\n", + "\n", + "graph_builder = StateGraph(State)\n", + "\n", + "\n", + "###### Define the tool and binding ######\n", + "@tool\n", + "def search_keyword(query: str) -> List[Dict[str, str]]:\n", + " \"\"\"\n", + " Look up news by keyword.\n", + " \"\"\"\n", + " news_tool = GoogleNews()\n", + " return news_tool.search_by_keyword(query, k=5)\n", + "\n", + "\n", + "tools = [search_keyword]\n", + "llm = ChatOpenAI(model=\"gpt-4o-mini\")\n", + "llm_with_tools = llm.bind_tools(tools)\n", + "\n", + "\n", + "###### Add Nodes ######\n", + "def chatbot(state: State):\n", + " \"\"\"\n", + " Invoke the message and return the result.\n", + " \"\"\"\n", + " return {\"messages\": [llm_with_tools.invoke(state[\"messages\"])]}\n", + "\n", + "\n", + "# chatbot node\n", + "graph_builder.add_node(\"chatbot\", chatbot)\n", + "\n", + "# tool node\n", + "tool_node = ToolNode(tools=tools)\n", + "graph_builder.add_node(\"tools\", tool_node)\n", + "\n", + "# conditional edge\n", + "graph_builder.add_conditional_edges(\"chatbot\", tools_condition)\n", + "\n", + "\n", + "###### Add Edges ######\n", + "# tools -> chatbot\n", + "graph_builder.add_edge(\"tools\", \"chatbot\")\n", + "\n", + "# START -> chatbot\n", + "graph_builder.add_edge(START, \"chatbot\")\n", + "\n", + "# chatbot -> END\n", + "graph_builder.add_edge(\"chatbot\", END)\n", + "\n", + "\n", + "###### Add the MemorySaver ######\n", + "memory = MemorySaver()" + ] + }, + { + "cell_type": "markdown", + "id": "8ce03ae7", + "metadata": {}, + "source": [ + "We compile the graph with a checkpointer." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "fdf7759b", + "metadata": {}, + "outputs": [], + "source": [ + "###### Add the interrupt_before ######\n", + "graph = graph_builder.compile(checkpointer=memory)" + ] + }, + { + "cell_type": "markdown", + "id": "41c54ff9", + "metadata": {}, + "source": [ + "Let's visualize the graph!" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "5ae46bba", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "###### Graph Visualization ######\n", + "from IPython.display import Image, display\n", + "\n", + "try:\n", + " display(Image(graph.get_graph().draw_mermaid_png()))\n", + "except Exception:\n", + " pass" + ] + }, + { + "cell_type": "markdown", + "id": "9dc0a002", + "metadata": {}, + "source": [ + "## Adding Human Feedback\n", + "\n", + "Let's now perform **Human-in-the-loop** !" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "3f89a4e8", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "================================\u001b[1m Human Message \u001b[0m=================================\n", + "\n", + "Please provide the latest news related to AI.\n", + "================================\u001b[1m Human Message \u001b[0m=================================\n", + "\n", + "Please provide the latest news related to AI.\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "Tool Calls:\n", + " search_keyword (call_JFu8CvoZ7QlIzVnKky1rMgCn)\n", + " Call ID: call_JFu8CvoZ7QlIzVnKky1rMgCn\n", + " Args:\n", + " query: AI\n" + ] + } + ], + "source": [ + "from langchain_core.runnables import RunnableConfig\n", + "\n", + "# User input\n", + "question = \"Please provide the latest news related to AI.\"\n", + "\n", + "# Define the initial input State\n", + "input = State(messages=[(\"user\", question)])\n", + "\n", + "# Config settings\n", + "config = RunnableConfig(\n", + " # Visit up to 10 nodes; beyond this, a RecursionError will occur\n", + " recursion_limit=10,\n", + " # Set thread id\n", + " configurable={\"thread_id\": \"1\"},\n", + " # Set tags\n", + " tags=[\"06-LangGraph-Human-in-the-loop\"],\n", + ")\n", + "\n", + "# Process events in a loop\n", + "for event in graph.stream(\n", + " input=input,\n", + " config=config,\n", + " stream_mode=\"values\",\n", + " # Interrupt before executing the tools (stop before the tools node execution)\n", + " interrupt_before=[\"tools\"],\n", + "):\n", + " for key, val in event.items():\n", + " # key is the node's name\n", + " # print(f\"\\n[{key}]\\n\")\n", + "\n", + " # val(value) is the node's output\n", + " pretty_print_messages(val)\n", + "\n", + " # State is stored in dict format within val(value)\n", + " if \"messages\" in val:\n", + " print(f\"Number of messages: {len(val['messages'])}\")" + ] + }, + { + "cell_type": "markdown", + "id": "79194529", + "metadata": {}, + "source": [ + "The chatbot invoked the tool, but its execution was interrupted.\n", + "\n", + "Let's check the graph state!" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "86f4bd4d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "('tools',)" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# snapshot of the graph state\n", + "snapshot = graph.get_state(config)\n", + "snapshot.next" + ] + }, + { + "cell_type": "markdown", + "id": "486398fd", + "metadata": {}, + "source": [ + "The reason for the interruption is that we set `tools` in `interrupt_before` while streaming the graph, causing the process to stop before executing the `tools` node. As a result, the next node ( `.next` ) becomes `tools` .\n", + "\n", + "Additionally, *in the previous tutorial* , `.next` was empty because the process had reached the final `END` node. However, with the interrupt set, `.next` is now set to `tools` ." + ] + }, + { + "cell_type": "markdown", + "id": "2d9352b3", + "metadata": {}, + "source": [ + "Now, let's check the last message in the snapshot!" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "59aa479f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'name': 'search_keyword',\n", + " 'args': {'query': 'AI'},\n", + " 'id': 'call_JFu8CvoZ7QlIzVnKky1rMgCn',\n", + " 'type': 'tool_call'}]" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "existing_message = snapshot.values[\"messages\"][-1]\n", + "existing_message.tool_calls" + ] + }, + { + "cell_type": "markdown", + "id": "a36482dd", + "metadata": {}, + "source": [ + "Next, let's resume the graph from the point where it was interrupted.\n", + "\n", + "**LangGraph** makes it easy to resume graph execution.\n", + "\n", + "Simply pass `None` as the `input` ." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "f88e20b3", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "Tool Calls:\n", + " search_keyword (call_JFu8CvoZ7QlIzVnKky1rMgCn)\n", + " Call ID: call_JFu8CvoZ7QlIzVnKky1rMgCn\n", + " Args:\n", + " query: AI\n", + "=================================\u001b[1m Tool Message \u001b[0m=================================\n", + "Name: search_keyword\n", + "\n", + "[{\"url\": \"https://news.google.com/rss/articles/CBMikwFBVV95cUxNcllEYjZmTkNzUGpjenpxZ3N6ZlFOcFAxS2szNm80MWM4dVRSU3YzcGlzU0xrZkI3MEpBR3N2U19lSmdmVnR5R0dhT0U4S1RHR1JWZDlvTXd1MnU1TlNyZlRuYXVpS0cxY1pjckNaQU1URE9IV2kzZm96d2dYRWVUczBrYV9HUklmVDJNOElaTEVNMUHSAYoBQVVfeXFMTkttd0EzSUNJUnR5ak5UYWNfZWhpVHlrZFMxQ095TTVOb2JTX2RLSTZQV2dDNktvbTktR1B4bDBtRlpxUlJhck9mTGgzVDBzMXpTaXR6aEZ2VndnRElvaFNNcGE4VmtwTjdVcXVjYkU2SnlaUGZDSXRRUjZxb3RJSHFFcTZCVWpiaWJB?oc=5\", \"content\": \"Trump announces a $500 billion AI infrastructure investment in the US - CNN\"}, {\"url\": \"https://news.google.com/rss/articles/CBMinAFBVV95cUxOUFlLYjE5VkkyR0F4Vl9JYVVtSDl0Vk5hcHVLZ2h2bUgweE00NFplTFhDLWFBNDNpRlN0OXJiQ1JjSFNadmU2Z1BDaVpPMlYtTnBpS2VnQmR5dE9GZFB1c3hQR0NoNUo3ekpFTmFIWjI4TjdjV1ZrbDJ5LURaZ2s1UDhZSkd4cndaQ2pRRENscjNJc2JXZGZyazRmdDU?oc=5\", \"content\": \"Trump Announces $100 Billion A.I. Initiative - The New York Times\"}, {\"url\": \"https://news.google.com/rss/articles/CBMi1gFBVV95cUxNMXRqZnRNMTk2aGZUdlAydjY3aHktaHhDNkxqZi03MmtqUkp1OXg5YVY5WjdZZ2J0Zk5oY2ZWVW9ZR2tmdElleUhIdnppOHNDdXhYYnJ0RHFHaGJZbnNYTWJOaDJ6c3N0eWpyUTZCUV96NVFEYWlKQVdyZmpfdHFRWGpzNDVuME1XZDE3UnNrX3U1eEVaT0g4YnJFVHNsbFdQSVhpMmFWQmtCT1hib3c4ZHZsa0pxNGNGUTJJZVRSb2tXU3NwdzlCcl93ZnVuUXo3SGhTZ1ZB0gHbAUFVX3lxTFBXUDRJSEh3ZlhVV0xzSzExUFFvTkRlMG1XTmpUeUhHV1lJUUE4ME5tNFR0YXNKd05pc2pSSVZOTm1VbDhMMUxQbnBHbm5QRkRnb0lzWnM2Znk4cFVBendfcVlOdThtNnMyRHpHaXJYUXdxOVBSQXE0OV9QNjN1M2E4QVRaWS1JX2pVcV9qSndMQzFiYXpWVEE1d2s2RFJGZXF4RzFZd192Z2NpQWpfYTlPbENTenBRZm9ZWDV0NUZyYjJQS1d3SFNQQzlNTU1DdjJIUG5ac2RHazgxUQ?oc=5\", \"content\": \"Davos 2025: Trade, tariffs, AI and UN chief Guterres dominate World Economic Forum agenda - The Hill\"}, {\"url\": \"https://news.google.com/rss/articles/CBMilwFBVV95cUxPdXhCRVNEVzZYRGMxdVlwQkc4SXBmUmVUY0drTHQ5ZDk5dUdtdHhkSUJtRWowMG9kZkkzWVBFN2ZoWUFLeEdCUW00aDFmQkNWU0dYR0p4N0RoekxqQ0w3M1pQVU4wNC0yU1JRV3dGeW1hdW5oUEJiVTNKZ2Z5Y3ZSenFldjNpQmVTYjNSSW9tYi14LWx6enZV?oc=5\", \"content\": \"Google rushed to sell AI tools to Israel’s military after Hamas attack - The Washington Post\"}, {\"url\": \"https://news.google.com/rss/articles/CBMiswFBVV95cUxQdG9FV2tjQjZGeVdlQzFkOGZNdkd4bDYzVm1BZnVDT1B1bEc0UUpSWlVYbjdHcnl0bnpJOThhc3NlbGZBZ2tSSGh4RzJtdVVFNmVuLVYtOElxQjRLdnd3R0hoTjdmQmcyd1U4VHFsNWhGY0tTVmJhUkExaWJTQ2ZpVVpXQzd6VGw1TV9MVWxxalMxLWRkWnAwdjV4WG4xMVFTNmR5Nng4WFVxMGZiYkxiU3VVaw?oc=5\", \"content\": \"Trump Pushes to Make US an AI Superpower, With Fewer Guardrails - Bloomberg\"}]\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "Here are the latest news articles related to AI:\n", + "\n", + "1. **Trump announces a $500 billion AI infrastructure investment in the US** - [CNN](https://news.google.com/rss/articles/CBMikwFBVV95cUxNcllEYjZmTkNzUGpjenpxZ3N6ZlFOcFAxS2szNm80MWM4dVRSU3YzcGlzU0xrZkI3MEpBR3N2U19lSmdmVnR5R0dhT0U4S1RHR1JWZDlvTXd1MnU1TlNyZlRuYXVpS0cxY1pjckNaQU1URE9IV2kzZm96d2dYRWVUczBrYV9HUklmVDJNOElaTEVNMUHSAYoBQVVfeXFMTkttd0EzSUNJUnR5ak5UYWNfZWhpVHlrZFMxQ095TTVOb2JTX2RLSTZQV2dDNktvbTktR1B4bDBtRlpxUlJhck9mTGgzVDBzMXpTaXR6aEZ2VndnRElvaFNNcGE4VmtwTjdVcXVjYkU2SnlaUGZDSXRRUjZxb3RJSHFFcTZCVWpiaWJB?oc=5)\n", + "\n", + "2. **Trump Announces $100 Billion A.I. Initiative** - [The New York Times](https://news.google.com/rss/articles/CBMinAFBVV95cUxOUFlLYjE5VkkyR0F4Vl9JYVVtSDl0Vk5hcHVLZ2h2bUgweE00NFplTFhDLWFBNDNpRlN0OXJiQ1JjSFNadmU2Z1BDaVpPMlYtTnBpS2VnQmR5dE9GZFB1c3hQR0NoNUo3ekpFTmFIWjI4TjdjV1ZrbDJ5LURaZ2s1UDhZSkd4cwdaQ2pRRENscjNJc2JXZGZyazRmdDU?oc=5)\n", + "\n", + "3. **Davos 2025: Trade, tariffs, AI and UN chief Guterres dominate World Economic Forum agenda** - [The Hill](https://news.google.com/rss/articles/CBMi1gFBVV95cUxNMXRqZnRNMTk2aGZUdlAydjY3aHktaHhDNkxqZi03MmtqUkp1OXg5YVY5WjdZZ2J0Zk5oY2ZWVW9ZR2tmdElleUhIdnppOHNDdXhYYnJ0RHFHaGJZbnNYTWJOaDJ6c3N0eWpyUTZCUV96NVFEYWlKQVdyZmpfdHFRWGpzNDVuME1XZDE3UnNrX3U1eEVaT0g4YnJFVHNsbFdQSVhpMmFWQmtCT1hib3c4ZHZsa0pxNGNGUTJJZVRSb2tXU3NwdzlCcl93ZnVuUXo3SGhTZ1ZB0gHbAUFVX3lxTFBXUDRJSEh3ZlhVV0xzSzExUFFvTkRlMG1XTmpUeUhHV1lJUUE4ME5tNFR0YXNKd05pc2pSSVZOTm1VbDhMMUxQbnBHbm5QRkRnb0lzWnM2Znk4cFVBendfcVlOdThtNnMyRHpHaXJYUXdxOVBSQXE0OV9QNjN1M2E4QVRaWS1JX2pVcV9qSndMQzFiYXpWVEE1d2s2RFJGZXF4RzFZd192Z2NpQWpfYTlPbENTenBRZm9ZWDV0NUZyYjJQS1d3SFNQQzlNTU1DdjJIUG5ac2RHazgxUQ?oc=5)\n", + "\n", + "4. **Google rushed to sell AI tools to Israel’s military after Hamas attack** - [The Washington Post](https://news.google.com/rss/articles/CBMilwFBVV95cUxPdXhCRVNEVzZYRGMxdVlwQkc4SXBmUmVUY0drTHQ5ZDk5dUdtdHhkSUJtRWowMG9kZkkzWVBFN2ZoWUFLeEdCUW00aDFmQkNWU0dYR0p4N0RoekxqQ0w3M1pQVU4wNC0yU1JRV3dGeW1hdW5oUEJiVTNKZ2Z5Y3ZSenFldjNpQmVTYjNSSW9tYi14LWx6enZV?oc=5)\n", + "\n", + "5. **Trump Pushes to Make US an AI Superpower, With Fewer Guardrails** - [Bloomberg](https://news.google.com/rss/articles/CBMiswFBVV95cUxQdG9FV2tjQjZGeVdlQzFkOGZNdkd4bDYzVm1BZnVDT1B1bEc0UUpSWlVYbjdHcnl0bnpJOThhc3NlbGZBZ2tSSGh4RzJtdVVFNmVuLVYtOElxQjRLdnd3R0hoTjdmQmcyd1U4VHFsNWhGY0tTVmJhUkExaWJTQ2ZpVVpXQzd6VGw1TV9MVWxxalMxLWRkWnAwdjV4WG4xMVFTNmR5Nng4WFVxMGZiYkxiU3VVaw?oc=5)\n", + "\n", + "Feel free to check the articles for more detailed information!\n" + ] + } + ], + "source": [ + "events = graph.stream(\n", + " input=None, # Do not add anything to the current State\n", + " config=config,\n", + " stream_mode=\"values\",\n", + ")\n", + "\n", + "# Process events in a loop\n", + "for event in events:\n", + " if \"messages\" in event:\n", + " # Print the last message\n", + " event[\"messages\"][-1].pretty_print()" + ] + }, + { + "cell_type": "markdown", + "id": "56c4b22e", + "metadata": {}, + "source": [ + "We used **interrupt** to enable **human intervention** in the chatbot's execution." + ] + }, + { + "cell_type": "markdown", + "id": "909696f2", + "metadata": {}, + "source": [ + "Additionally, with the inclusion of a `checkpointer` , the graph can resume execution even after being indefinitely paused.\n", + "\n", + "Below is how you can use `get_state_history` to retrieve the state history.\n", + "\n", + "By specifying the desired state from the history, you can restart execution from that point." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "3a029ca0", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Number of messages: 4 \n", + "Next node: ()\n", + "--------------------------------------------------------------------------------\n", + "Number of messages: 3 \n", + "Next node: ('chatbot',)\n", + "--------------------------------------------------------------------------------\n", + "Number of messages: 2 \n", + "Next node: ('tools',)\n", + "--------------------------------------------------------------------------------\n", + "Number of messages: 1 \n", + "Next node: ('chatbot',)\n", + "--------------------------------------------------------------------------------\n", + "Number of messages: 0 \n", + "Next node: ('__start__',)\n", + "--------------------------------------------------------------------------------\n" + ] + } + ], + "source": [ + "to_replay = None\n", + "\n", + "# Retrieve state history\n", + "for state in graph.get_state_history(config):\n", + " print(\n", + " \"Number of messages:\", len(state.values[\"messages\"]), \"\\nNext node:\", state.next\n", + " )\n", + " print(\"-\" * 80)\n", + " if len(state.values[\"messages\"]) == 3:\n", + " to_replay = state" + ] + }, + { + "cell_type": "markdown", + "id": "ca13bf6d", + "metadata": {}, + "source": [ + "It is important to note that the `checkpointer` saves data at every step of the graph!\n", + "\n", + "The desired point is stored in `to_replay` . This allows you to specify the starting point for resuming execution.\n", + "\n", + "The `checkpoint_id` is stored in `to_replay.config` ." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "78d87058", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "('chatbot',)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "{'configurable': {'thread_id': '1',\n", + " 'checkpoint_ns': '',\n", + " 'checkpoint_id': '1efd8a06-ace9-6516-8002-8731d8cb463e'}}" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "display(to_replay.next)\n", + "display(to_replay.config)" + ] + }, + { + "cell_type": "markdown", + "id": "e580548d", + "metadata": {}, + "source": [ + "Using this `checkpoint_id` , LangGraph's checkpointer can load the state at that specific point. \n", + "\n", + "Note that the `input` must be set to `None` in this case." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "a5fb3c26", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=================================\u001b[1m Tool Message \u001b[0m=================================\n", + "Name: search_keyword\n", + "\n", + "[{\"url\": \"https://news.google.com/rss/articles/CBMikwFBVV95cUxNcllEYjZmTkNzUGpjenpxZ3N6ZlFOcFAxS2szNm80MWM4dVRSU3YzcGlzU0xrZkI3MEpBR3N2U19lSmdmVnR5R0dhT0U4S1RHR1JWZDlvTXd1MnU1TlNyZlRuYXVpS0cxY1pjckNaQU1URE9IV2kzZm96d2dYRWVUczBrYV9HUklmVDJNOElaTEVNMUHSAYoBQVVfeXFMTkttd0EzSUNJUnR5ak5UYWNfZWhpVHlrZFMxQ095TTVOb2JTX2RLSTZQV2dDNktvbTktR1B4bDBtRlpxUlJhck9mTGgzVDBzMXpTaXR6aEZ2VndnRElvaFNNcGE4VmtwTjdVcXVjYkU2SnlaUGZDSXRRUjZxb3RJSHFFcTZCVWpiaWJB?oc=5\", \"content\": \"Trump announces a $500 billion AI infrastructure investment in the US - CNN\"}, {\"url\": \"https://news.google.com/rss/articles/CBMinAFBVV95cUxOUFlLYjE5VkkyR0F4Vl9JYVVtSDl0Vk5hcHVLZ2h2bUgweE00NFplTFhDLWFBNDNpRlN0OXJiQ1JjSFNadmU2Z1BDaVpPMlYtTnBpS2VnQmR5dE9GZFB1c3hQR0NoNUo3ekpFTmFIWjI4TjdjV1ZrbDJ5LURaZ2s1UDhZSkd4cndaQ2pRRENscjNJc2JXZGZyazRmdDU?oc=5\", \"content\": \"Trump Announces $100 Billion A.I. Initiative - The New York Times\"}, {\"url\": \"https://news.google.com/rss/articles/CBMi1gFBVV95cUxNMXRqZnRNMTk2aGZUdlAydjY3aHktaHhDNkxqZi03MmtqUkp1OXg5YVY5WjdZZ2J0Zk5oY2ZWVW9ZR2tmdElleUhIdnppOHNDdXhYYnJ0RHFHaGJZbnNYTWJOaDJ6c3N0eWpyUTZCUV96NVFEYWlKQVdyZmpfdHFRWGpzNDVuME1XZDE3UnNrX3U1eEVaT0g4YnJFVHNsbFdQSVhpMmFWQmtCT1hib3c4ZHZsa0pxNGNGUTJJZVRSb2tXU3NwdzlCcl93ZnVuUXo3SGhTZ1ZB0gHbAUFVX3lxTFBXUDRJSEh3ZlhVV0xzSzExUFFvTkRlMG1XTmpUeUhHV1lJUUE4ME5tNFR0YXNKd05pc2pSSVZOTm1VbDhMMUxQbnBHbm5QRkRnb0lzWnM2Znk4cFVBendfcVlOdThtNnMyRHpHaXJYUXdxOVBSQXE0OV9QNjN1M2E4QVRaWS1JX2pVcV9qSndMQzFiYXpWVEE1d2s2RFJGZXF4RzFZd192Z2NpQWpfYTlPbENTenBRZm9ZWDV0NUZyYjJQS1d3SFNQQzlNTU1DdjJIUG5ac2RHazgxUQ?oc=5\", \"content\": \"Davos 2025: Trade, tariffs, AI and UN chief Guterres dominate World Economic Forum agenda - The Hill\"}, {\"url\": \"https://news.google.com/rss/articles/CBMilwFBVV95cUxPdXhCRVNEVzZYRGMxdVlwQkc4SXBmUmVUY0drTHQ5ZDk5dUdtdHhkSUJtRWowMG9kZkkzWVBFN2ZoWUFLeEdCUW00aDFmQkNWU0dYR0p4N0RoekxqQ0w3M1pQVU4wNC0yU1JRV3dGeW1hdW5oUEJiVTNKZ2Z5Y3ZSenFldjNpQmVTYjNSSW9tYi14LWx6enZV?oc=5\", \"content\": \"Google rushed to sell AI tools to Israel’s military after Hamas attack - The Washington Post\"}, {\"url\": \"https://news.google.com/rss/articles/CBMiswFBVV95cUxQdG9FV2tjQjZGeVdlQzFkOGZNdkd4bDYzVm1BZnVDT1B1bEc0UUpSWlVYbjdHcnl0bnpJOThhc3NlbGZBZ2tSSGh4RzJtdVVFNmVuLVYtOElxQjRLdnd3R0hoTjdmQmcyd1U4VHFsNWhGY0tTVmJhUkExaWJTQ2ZpVVpXQzd6VGw1TV9MVWxxalMxLWRkWnAwdjV4WG4xMVFTNmR5Nng4WFVxMGZiYkxiU3VVaw?oc=5\", \"content\": \"Trump Pushes to Make US an AI Superpower, With Fewer Guardrails - Bloomberg\"}]\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "Here are the latest news articles related to AI:\n", + "\n", + "1. **Trump announces a $500 billion AI infrastructure investment in the US** - [CNN](https://news.google.com/rss/articles/CBMikwFBVV95cUxNcllEYjZmTkNzUGpjenpxZ3N6ZlFOcFAxS2szNm80MWM4dVRSU3YzcGlzU0xrZkI3MEpBR3N2U19lSmdmVnR5R0dhT0U4S1RHR1JWZDlvTXd1MnU1TlNyZlRuYXVpS0cxY1pjckNaQU1URE9IV2kzZm96d2dYRWVUczBrYV9HUklmVDJNOElaTEVNMUHSAYoBQVVfeXFMTkttd0EzSUNJUnR5ak5UYWNfZWhpVHlrZFMxQ095TTVOb2JTX2RLSTZQV2dDNktvbTktR1B4bDBtRlpxUlJhck9mTGgzVDBzMXpTaXR6aEZ2VndnRElvaFNNcGE4VmtwTjdVcXVjYkU2SnlaUGZDSXRRUjZxb3RJSHFFcTZCVWpiaWJB?oc=5)\n", + "\n", + "2. **Trump Announces $100 Billion A.I. Initiative** - [The New York Times](https://news.google.com/rss/articles/CBMinAFBVV95cUxOUFlLYjE5VkkyR0F4Vl9JYVVtSDl0Vk5hcHVLZ2h2bUgweE00NFplTFhDLWFBNDNpRlN0OXJiQ1JjSFNadmU2Z1BDaVpPMlYtTnBpS2VnQmR5dE9GZFB1c3hQR0NoNUo3ekpFTmFIWjI4TjdjV1ZrbDJ5LURaZ2s1UDhZSkd4cndaQ2pRRENscjNJc2JXZGZyazRmdDU?oc=5)\n", + "\n", + "3. **Davos 2025: Trade, tariffs, AI and UN chief Guterres dominate World Economic Forum agenda** - [The Hill](https://news.google.com/rss/articles/CBMi1gFBVV95cUxNMXRqZnRNMTk2aGZUdlAydjY3aHktaHhDNkxqZi03MmtqUkp1OXg5YVY5WjdZZ2J0Zk5oY2ZWVW9ZR2tmdElleUhIdnppOHNDdXhYYnJ0RHFHaGJZbnNYTWJOaDJ6c3N0eWpyUTZCUV96NVFEYWlKQVdyZmpfdHFRWGpzNDVuME1XZDE3UnNrX3U1eEVaT0g4YnJFVHNsbFdQSVhpMmFWQmtCT1hib3c4ZHZsa0pxNGNGUTJJZVRSb2tXU3NwdzlCcl93ZnVuUXo3SGhTZ1ZB0gHbAUFVX3lxTFBXUDRJSEh3ZlhVV0xzSzExUFFvTkRlMG1XTmpUeUhHV1lJUUE4ME5tNFR0YXNKd05pc2pSSVZOTm1VbDhMMUxQbnBHbm5QRkRnb0lzWnM2Znk4cFVBendfcVlOdThtNnMyRHpHaXJYUXdxOVBSQXE0OV9QNjN1M2E4QVRaWS1JX2pVcV9qSndMQzFiYXpWVEE1d2s2RFJGZXF4RzFZd192Z2NpQWpfYTlPbENTenBRZm9ZWDV0NUZyYjJQS1d3SFNQQzlNTU1DdjJIUG5ac2RHazgxUQ?oc=5)\n", + "\n", + "4. **Google rushed to sell AI tools to Israel’s military after Hamas attack** - [The Washington Post](https://news.google.com/rss/articles/CBMilwFBVV95cUxPdXhCRVNEVzZYRGMxdVlwQkc4SXBmUmVUY0drTHQ5ZDk5dUdtdHhkSUJtRWowMG9kZkkzWVBFN2ZoWUFLeEdCUW00aDFmQkNWU0dYR0p4N0RoekxqQ0w3M1pQVU4wNC0yU1JRV3dGeW1hdW5oUEJiVTNKZ2Z5Y3ZSenFldjNpQmVTYjNSSW9tYi14LWx6enZV?oc=5)\n", + "\n", + "5. **Trump Pushes to Make US an AI Superpower, With Fewer Guardrails** - [Bloomberg](https://news.google.com/rss/articles/CBMiswFBVV95cUxQdG9FV2tjQjZGeVdlQzFkOGZNdkd4bDYzVm1BZnVDT1B1bEc0UUpSWlVYbjdHcnl0bnpJOThhc3NlbGZBZ2tSSGh4RzJtdVVFNmVuLVYtOElxQjRLdnd3R0hoTjdmQmcyd1U4VHFsNWhGY0tTVmJhUkExaWJTQ2ZpVVpXQzd6VGw1TV9MVWxxalMxLWRkWnAwdjV4WG4xMVFTNmR5Nng4WFVxMGZiYkxiU3VVaw?oc=5)\n", + "\n", + "Feel free to check the articles for more detailed information!\n" + ] + } + ], + "source": [ + "for event in graph.stream(input=None, config=to_replay.config, stream_mode=\"values\"):\n", + " if \"messages\" in event:\n", + " # Print the last message\n", + " event[\"messages\"][-1].pretty_print()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "langchain-kr-lwwSZlnu-py3.11", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}