From c347ba83f194c30fe8afc283f86bc751aa59d941 Mon Sep 17 00:00:00 2001 From: chester Date: Wed, 22 Jan 2025 21:08:31 +0900 Subject: [PATCH 1/2] =?UTF-8?q?[E-4]=2015-Agent=20/=2011-React-Agent.ipynb?= =?UTF-8?q?\n\n{PR=20Message=20-=20Write=20freely,=20remove=20this=20brack?= =?UTF-8?q?et=20if=20unnecessary}\n\n###=20Author=20Checklist\n-=20[x]=20*?= =?UTF-8?q?*PR=20Title=20Format**:=20I=20have=20confirmed=20that=20the=20P?= =?UTF-8?q?R=20title=20follows=20the=20correct=20format.=20=5F(e.g.,=20[N-?= =?UTF-8?q?2]=2007-Text=20Splitter=20/=2007-RecursiveCharacterTextSplitter?= =?UTF-8?q?)=5F\n-=20[x]=20**Committed=20Files**:=20I=20have=20ensured=20t?= =?UTF-8?q?hat=20no=20unnecessary=20files=20=5F(e.g.,=20.bin,=20.gitignore?= =?UTF-8?q?,=20poetry.lock,=20pyproject.toml)=5F=20are=20included.=20These?= =?UTF-8?q?=20files=20are=20not=20allowed.\n-=20[=20]=20**(Optional)=20Rel?= =?UTF-8?q?ated=20Issue**:=20If=20this=20PR=20is=20linked=20to=20an=20issu?= =?UTF-8?q?e,=20I=20have=20referenced=20the=20issue=20number=20in=20the=20?= =?UTF-8?q?PR=20message.=20=5F(e.g.,=20Fixes=20#123)=5F\n=20=20=20=20=20?= =?UTF-8?q?=20\n-=20=E2=9D=8C=20Do=20not=20include=20unnecessary=20files?= =?UTF-8?q?=20(e.g.,=20.bin,=20.gitignore,=20poetry.lock,=20pyproject.toml?= =?UTF-8?q?)=20or=20other=20people's=20code.=20If=20included,=20close=20th?= =?UTF-8?q?e=20PR=20and=20create=20a=20new=20PR.\n\n###=20Review=20Templat?= =?UTF-8?q?e=20(Intial=20PR)\n\nIf=20no=20one=20reviews=20your=20PR=20with?= =?UTF-8?q?in=20a=20few=20days,=20please=20@-mention=20one=20of=20teddylee?= =?UTF-8?q?777,=20musangk,=20BAEM1N?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../05_LangGraph_Plan_and_Execute.ipynb | 805 ++++++++++++++++++ .../assets/05-langgraph-plan-and-execute.png | Bin 0 -> 24084 bytes 2 files changed, 805 insertions(+) create mode 100644 17-LangGraph/03-Use-Cases/05_LangGraph_Plan_and_Execute.ipynb create mode 100644 17-LangGraph/03-Use-Cases/assets/05-langgraph-plan-and-execute.png diff --git a/17-LangGraph/03-Use-Cases/05_LangGraph_Plan_and_Execute.ipynb b/17-LangGraph/03-Use-Cases/05_LangGraph_Plan_and_Execute.ipynb new file mode 100644 index 000000000..2f08ac190 --- /dev/null +++ b/17-LangGraph/03-Use-Cases/05_LangGraph_Plan_and_Execute.ipynb @@ -0,0 +1,805 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "OqKumYqaNrPC", + "metadata": { + "id": "OqKumYqaNrPC" + }, + "source": [ + "# Plan-and-Execute\n", + "\n", + "- Author: [ranian963](https://github.com/ranian963)\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/17-LangGraph/03-Use-Cases/05-langgraph-plan-and-execute.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/17-LangGraph/03-Use-Cases/05-langgraph-plan-and-execute.ipynb)\n", + "\n", + "## Overview\n", + "This tutorial introduces how to create a `plan-and-execute` style agent and explains, step by step, how to implement it using [LangGraph](https://langchain-ai.github.io/langgraph/). \n", + "The `plan-and-execute` approach is useful for tackling complex tasks by first establishing a long-term plan, then executing each step of that plan, and revising it as needed.\n", + "\n", + "![](./assets/05-langgraph-plan-and-execute.png)\n", + "\n", + "### Table of Contents\n", + "\n", + "- [Overview](#overview)\n", + "- [Environment Setup](#environment-setup)\n", + "- [Plan-and-Execute Definition](#plan-and-execute-definition)\n", + "- [Defining Tools](#defining-tools)\n", + "- [Defining the Execution Agent](#defining-the-execution-agent)\n", + "- [State Definition](#state-definition)\n", + "- [Plan Step](#plan-step)\n", + "- [Re-plan Step](#re-plan-step)\n", + "- [Creating the Graph](#creating-the-graph)\n", + "- [Graph Visualization](#graph-visualization)\n", + "- [Running the Graph](#running-the-graph)\n", + "- [Checking the Final Report](#checking-the-final-report)\n", + "\n", + "### References\n", + "\n", + "- [LangChain](https://blog.langchain.dev/)\n", + "- [LangGraph](https://langchain-ai.github.io/langgraph/) \n", + "- [Plan-and-Solve Paper](https://arxiv.org/abs/2305.04091) \n", + "- [Baby-AGI Project](https://github.com/yoheinakajima/babyagi) \n", + "- [ReAct Paper](https://arxiv.org/abs/2210.03629)\n", + "----" + ] + }, + { + "cell_type": "markdown", + "id": "rhUZI_8lN_7x", + "metadata": { + "id": "rhUZI_8lN_7x" + }, + "source": [ + "## Environment Setup\n", + "\n", + "Setting up your environment is the first step. See the [Environment Setup](https://wikidocs.net/257836) guide for more details.\n", + "\n", + "\n", + "**[Note]**\n", + "\n", + "The langchain-opentutorial is a package of easy-to-use environment setup guidance, useful functions and utilities for tutorials.\n", + "Check out the [`langchain-opentutorial`](https://github.com/LangChain-OpenTutorial/langchain-opentutorial-pypi) for more details." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "Ir6WsaC1OB8y", + "metadata": { + "id": "Ir6WsaC1OB8y" + }, + "outputs": [], + "source": [ + "%%capture --no-stderr\n", + "%pip install langchain-opentutorial" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "WhSXCH7kODHR", + "metadata": { + "id": "WhSXCH7kODHR" + }, + "outputs": [], + "source": [ + "# Install required packages\n", + "from langchain_opentutorial import package\n", + "\n", + "package.install(\n", + " [\n", + " \"langsmith\",\n", + " \"langchain\",\n", + " \"langchain_core\",\n", + " \"langchain-anthropic\",\n", + " \"langchain_community\",\n", + " \"langchain_text_splitters\",\n", + " \"langchain_openai\",\n", + " \"langgraph\",\n", + " ],\n", + " verbose=False,\n", + " upgrade=False,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "vzgjAj2mOKEJ", + "metadata": { + "id": "vzgjAj2mOKEJ" + }, + "source": [ + "You can set API keys in a `.env` file or set them manually.\n", + "\n", + "[Note] If you’re not using the `.env` file, no worries! Just enter the keys directly in the cell below, and you’re good to go." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "RmZFhLKBOMrQ", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "RmZFhLKBOMrQ", + "outputId": "32cb3dd1-d3ad-44a9-e97e-45d3d1c84473" + }, + "outputs": [], + "source": [ + "from dotenv import load_dotenv\n", + "from langchain_opentutorial import set_env\n", + "\n", + "# Attempt to load environment variables from a .env file; if unsuccessful, set them manually.\n", + "if not load_dotenv():\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\": \"05-LangGraph-Plan-and-Execute\",\n", + " \"TAVILY_API_KEY\": \n", + " }\n", + " )" + ] + }, + { + "cell_type": "markdown", + "id": "5c95e9bf", + "metadata": { + "id": "5c95e9bf" + }, + "source": [ + "## Plan-and-Execute Definition\n", + "\n", + "A `plan-and-execute` approach is characterized by:\n", + "- Long-Term Planning: Before performing a complex task, it first establishes a high-level plan.\n", + "- Step-by-Step Execution and Replanning: It carries out the plan in stages, checking at each step whether the plan is still valid and updating it if necessary.\n", + "\n", + "This method is inspired by the [Plan-and-Solve Peper](https://arxiv.org/abs/2305.04091) and the [Baby-AGI Project](https://github.com/yoheinakajima/babyagi). Unlike the more traditional [ReAct Style](https://arxiv.org/abs/2210.03629), which focuses on short-term reasoning one step at a time, `plan-and-execute` explicitly emphasizes long-term planning.\n", + "\n", + "Advantages:\n", + "\n", + "1. **Clear Long-Term Structure**: Even powerful LLMs can struggle to handle extended plans in a single pass. By explicitly defining a long-term plan, the process becomes more robust.\n", + "2. **Efficient Model Usage**: A larger or more powerful model can be used for the planning phase, while a smaller or lighter model can handle the execution phase, optimizing resource utilization.\n", + "\n", + "The sections below explain how to implement a `plan-and-execute` agent in LangGraph, step by step." + ] + }, + { + "cell_type": "markdown", + "id": "c6afbb11", + "metadata": { + "id": "c6afbb11" + }, + "source": [ + "### Defining the Model Name for the Examples\n", + "\n", + "We will define the model name to be used in these demonstrations.\n", + "\n", + "> **Note** \n", + "> 1. Since `MODEL_NAME` appears frequently, we declare it as a separate variable. \n", + "> 2. It is recommended to run this with a model such as `gpt-4o` (or another GPT-4-level model). If you use a smaller model like `gpt-40-mini`, you may encounter frequent replanning.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "ruCr1MZjZBs3", + "metadata": { + "id": "ruCr1MZjZBs3" + }, + "outputs": [], + "source": [ + "MODEL_NAME = \"gpt-4o\"" + ] + }, + { + "cell_type": "markdown", + "id": "0856a092", + "metadata": { + "id": "0856a092" + }, + "source": [ + "## Defining Tools\n", + "\n", + "We first define the tools to be used.\n", + "\n", + "In this simple example, we will use the built-in search tool provided by `Tavily`. Of course, it is equally straightforward to create your own custom tools as needed.\n", + "\n", + "For more details, refer to the [Tools](https://langchain-opentutorial.gitbook.io/langchain-opentutorial/15-agent/01-tools) documentation.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4dc76788", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "4dc76788", + "outputId": "4b0e493e-8618-42e5-8d8b-2c7fd0860286" + }, + "outputs": [], + "source": [ + "from langchain_community.tools.tavily_search import TavilySearchResults\n", + "\n", + "# Create an instance of TavilySearchResults with k=3 for retrieving up to 3 search results\n", + "tavily_web_search = TavilySearchResults(k=3)\n", + "\n", + "tools = [tavily_web_search]\n", + "tools" + ] + }, + { + "cell_type": "markdown", + "id": "9d230b5c", + "metadata": { + "id": "9d230b5c" + }, + "source": [ + "## Defining the Execution Agent\n", + "\n", + "We now create the `execution agent` responsible for performing tasks.\n", + "\n", + "In this example, the same `execution agent` will be used for each task, but that is not mandatory.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "f70cf9fa", + "metadata": { + "id": "f70cf9fa" + }, + "outputs": [], + "source": [ + "from langchain_openai import ChatOpenAI\n", + "from langgraph.prebuilt import create_react_agent\n", + "from langchain_core.prompts import ChatPromptTemplate\n", + "\n", + "# Define the prompt\n", + "prompt = ChatPromptTemplate.from_messages(\n", + " [\n", + " (\n", + " \"system\",\n", + " \"You are a helpful assistant.\",\n", + " ),\n", + " (\"human\", \"{messages}\"),\n", + " ]\n", + ")\n", + "\n", + "\n", + "# Define the LLM\n", + "llm = ChatOpenAI(model_name=MODEL_NAME, temperature=0)\n", + "\n", + "# Create ReAct agent\n", + "agent_executor = create_react_agent(llm, tools, state_modifier=prompt)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ab1307f9", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "ab1307f9", + "outputId": "8342cc09-24ef-488c-be7e-a7b0b302a24c" + }, + "outputs": [], + "source": [ + "# Agent execution\n", + "agent_executor.invoke({\"messages\": [(\"user\", \"Tell me about LangChain\")]})" + ] + }, + { + "cell_type": "markdown", + "id": "05f6efaa", + "metadata": { + "id": "05f6efaa" + }, + "source": [ + "## State Definition\n", + "- `input`: User’s input \n", + "- `plan`: The current plan \n", + "- `past_steps`: The plan and results of previous executions \n", + "- `response`: The final response" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "27c44393", + "metadata": { + "id": "27c44393" + }, + "outputs": [], + "source": [ + "import operator\n", + "from typing import Annotated, List, Tuple\n", + "from typing_extensions import TypedDict\n", + "\n", + "\n", + "# State definition\n", + "class PlanExecute(TypedDict):\n", + " input: Annotated[str, \"User's input\"]\n", + " plan: Annotated[List[str], \"Current plan\"]\n", + " past_steps: Annotated[List[Tuple], operator.add]\n", + " response: Annotated[str, \"Final response\"]" + ] + }, + { + "cell_type": "markdown", + "id": "b8c5d026", + "metadata": { + "id": "b8c5d026" + }, + "source": [ + "## Plan Step\n", + "\n", + "We will generate a long-term plan using **function calling** . Specifically, we define a `Plan` model and a prompt for the planner that instructs the LLM to produce an itemized plan of steps needed to solve the user's request. We keep each step focused and avoid adding unnecessary detail.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "ad37fbe6", + "metadata": { + "id": "ad37fbe6" + }, + "outputs": [], + "source": [ + "from pydantic import BaseModel, Field\n", + "from typing import List\n", + "\n", + "\n", + "# Define Plan model\n", + "class Plan(BaseModel):\n", + " \"\"\"Sorted steps to execute the plan\"\"\"\n", + "\n", + " steps: Annotated[List[str], \"Different steps to follow, should be in sorted order\"]" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "id": "726052a2", + "metadata": { + "id": "726052a2" + }, + "outputs": [], + "source": [ + "from langchain_core.prompts import ChatPromptTemplate\n", + "from langchain_openai import ChatOpenAI\n", + "\n", + "# Create a prompt template for planning\n", + "planner_prompt = ChatPromptTemplate.from_messages(\n", + " [\n", + " (\n", + " \"system\",\n", + " \"\"\"For the given objective, come up with a simple step by step plan. \\\n", + "This plan should involve individual tasks, that if executed correctly will yield the correct answer. Do not add any superfluous steps. \\\n", + "The result of the final step should be the final answer. Make sure that each step has all the information needed - do not skip steps.\"\"\",\n", + " ),\n", + " (\"placeholder\", \"{messages}\"),\n", + " ]\n", + ")\n", + "\n", + "planner = planner_prompt | ChatOpenAI(\n", + " model_name=MODEL_NAME, temperature=0\n", + ").with_structured_output(Plan)" + ] + }, + { + "cell_type": "markdown", + "id": "a0fd64cd", + "metadata": { + "id": "a0fd64cd" + }, + "source": [ + "We will run `planner` to verify the plan generation result." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c782515c", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "c782515c", + "outputId": "417c7798-5234-4a26-f678-fb11b99d3dca" + }, + "outputs": [], + "source": [ + "# Run planner\n", + "planner.invoke(\n", + " {\n", + " \"messages\": [\n", + " (\n", + " \"user\",\n", + " \"What are the main pros and cons of LangGraph, and why should we use it?\",\n", + " )\n", + " ]\n", + " }\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "ef28ddf5", + "metadata": { + "id": "ef28ddf5" + }, + "source": [ + "## Re-plan Step\n", + "\n", + "Based on the results of previous steps, we create a stage that can revise the original plan. If a tool call or execution indicates that additional steps are needed, we update the plan accordingly; otherwise, we finalize the response.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "c80e74e3", + "metadata": { + "id": "c80e74e3" + }, + "outputs": [], + "source": [ + "from typing import Union\n", + "\n", + "\n", + "class Response(BaseModel):\n", + " \"\"\"Response to user.\"\"\"\n", + "\n", + " response: str\n", + "\n", + "\n", + "class Act(BaseModel):\n", + " \"\"\"Action to perform.\"\"\"\n", + "\n", + " # The action to perform: 'Response' or 'Plan'. If you want to respond to the user, use Response.\n", + " # If you need to use additional tools, use Plan.\n", + " action: Union[Response, Plan] = Field(\n", + " description=\"Action to perform. If you want to respond to user, use Response.\"\n", + " \"If you need to further use tools to get the answer, use Plan.\"\n", + " )\n", + "\n", + "\n", + "# Define the prompt for re-planning\n", + "replanner_prompt = ChatPromptTemplate.from_template(\n", + " \"\"\"For the given objective, come up with a simple step by step plan. \\\n", + "This plan should involve individual tasks, that if executed correctly will yield the correct answer. Do not add any superfluous steps. \\\n", + "The result of the final step should be the final answer. Make sure that each step has all the information needed - do not skip steps.\n", + "\n", + "Your objective was this:\n", + "{input}\n", + "\n", + "Your original plan was this:\n", + "{plan}\n", + "\n", + "You have currently done the follow steps:\n", + "{past_steps}\n", + "\n", + "Update your plan accordingly. If no more steps are needed and you can return to the user, then respond with that. Otherwise, fill out the plan. Only add steps to the plan that still NEED to be done. Do not return previously done steps as part of the plan.\"\"\"\n", + ")\n", + "\n", + "\n", + "# Create the replanner\n", + "replanner = replanner_prompt | ChatOpenAI(\n", + " model_name=MODEL_NAME, temperature=0\n", + ").with_structured_output(Act)" + ] + }, + { + "cell_type": "markdown", + "id": "b88a7250", + "metadata": { + "id": "b88a7250" + }, + "source": [ + "## Creating the Graph\n", + "\n", + "We now build the LangGraph workflow by connecting the defined nodes:\n", + "\n", + "1. **planner** : Generates the plan. \n", + "2. **execute** : Uses the `execution agent` to perform the next step. \n", + "3. **replan** : Decides whether to continue with a new plan or provide the final answer. \n", + "4. **final_report** : Summarizes all steps and provides a polished final response.\n", + "\n", + "After defining the nodes and edges, we compile the graph. You can visualize the workflow to better understand how data moves between these steps.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "id": "df12d945", + "metadata": { + "id": "df12d945" + }, + "outputs": [], + "source": [ + "from langchain_core.output_parsers import StrOutputParser\n", + "\n", + "\n", + "# Generate and return a plan based on user input\n", + "def plan_step(state: PlanExecute):\n", + " plan = planner.invoke({\"messages\": [(\"user\", state[\"input\"])]})\n", + " # Return the list of steps from the generated plan\n", + " return {\"plan\": plan.steps}\n", + "\n", + "\n", + "# Use the agent executor to perform the specified task and return the result\n", + "def execute_step(state: PlanExecute):\n", + " plan = state[\"plan\"]\n", + " # Convert the plan to a string, enumerating each step\n", + " plan_str = \"\\n\".join(f\"{i+1}. {step}\" for i, step in enumerate(plan))\n", + " task = plan[0]\n", + " # Format the current task for the agent\n", + " task_formatted = f\"\"\"For the following plan:\n", + "{plan_str}\\n\\nYou are tasked with executing [step 1. {task}].\"\"\"\n", + " # Use the agent executor to perform the task and get the result\n", + " agent_response = agent_executor.invoke({\"messages\": [(\"user\", task_formatted)]})\n", + " # Return a dictionary containing the previous step and its result\n", + " return {\n", + " \"past_steps\": [(task, agent_response[\"messages\"][-1].content)],\n", + " }\n", + "\n", + "\n", + "# Update the plan or return the final response based on the results of the previous step\n", + "def replan_step(state: PlanExecute):\n", + " output = replanner.invoke(state)\n", + " # If the answer should be returned to the user\n", + " if isinstance(output.action, Response):\n", + " return {\"response\": output.action.response}\n", + " # If more steps are needed\n", + " else:\n", + " next_plan = output.action.steps\n", + " if len(next_plan) == 0:\n", + " return {\"response\": \"No more steps needed.\"}\n", + " else:\n", + " return {\"plan\": next_plan}\n", + "\n", + "\n", + "# A function that decides whether to end the agent's execution\n", + "def should_end(state: PlanExecute):\n", + " if \"response\" in state and state[\"response\"]:\n", + " return \"final_report\"\n", + " else:\n", + " return \"execute\"\n", + "\n", + "\n", + "final_report_prompt = ChatPromptTemplate.from_template(\n", + " \"\"\"You are given the objective and the previously done steps. Your task is to generate a final report in markdown format.\n", + "Final report should be written in professional tone.\n", + "\n", + "Your objective was this:\n", + "\n", + "{input}\n", + "\n", + "Your previously done steps(question and answer pairs):\n", + "\n", + "{past_steps}\n", + "\n", + "Generate a final report in markdown format.\"\"\"\n", + ")\n", + "\n", + "final_report = (\n", + " final_report_prompt\n", + " | ChatOpenAI(model_name=MODEL_NAME, temperature=0)\n", + " | StrOutputParser()\n", + ")\n", + "\n", + "\n", + "def generate_final_report(state: PlanExecute):\n", + " past_steps = \"\\n\\n\".join(\n", + " [\n", + " f\"Question: {past_step[0]}\\n\\nAnswer: {past_step[1]}\\n\\n####\"\n", + " for past_step in state[\"past_steps\"]\n", + " ]\n", + " )\n", + " response = final_report.invoke({\"input\": state[\"input\"], \"past_steps\": past_steps})\n", + " return {\"response\": response}" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "id": "16abdcf5", + "metadata": { + "id": "16abdcf5" + }, + "outputs": [], + "source": [ + "from langgraph.graph import StateGraph, START, END\n", + "from langgraph.checkpoint.memory import MemorySaver\n", + "\n", + "\n", + "# Create the workflow graph\n", + "workflow = StateGraph(PlanExecute)\n", + "\n", + "# Define nodes\n", + "workflow.add_node(\"planner\", plan_step)\n", + "workflow.add_node(\"execute\", execute_step)\n", + "workflow.add_node(\"replan\", replan_step)\n", + "workflow.add_node(\"final_report\", generate_final_report)\n", + "\n", + "# Define edges\n", + "workflow.add_edge(START, \"planner\")\n", + "workflow.add_edge(\"planner\", \"execute\")\n", + "workflow.add_edge(\"execute\", \"replan\")\n", + "workflow.add_edge(\"final_report\", END)\n", + "\n", + "# Conditional edges: use should_end function to decide whether to stop\n", + "workflow.add_conditional_edges(\n", + " \"replan\",\n", + " should_end,\n", + " {\"execute\": \"execute\", \"final_report\": \"final_report\"},\n", + ")\n", + "\n", + "# Compile the graph\n", + "app = workflow.compile(checkpointer=MemorySaver())" + ] + }, + { + "cell_type": "markdown", + "id": "bd850b8c", + "metadata": { + "id": "bd850b8c" + }, + "source": [ + "## Graph Visualization" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0ee7d379", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 796 + }, + "id": "0ee7d379", + "outputId": "592609c4-ae94-4fe0-af40-00b99523b774" + }, + "outputs": [], + "source": [ + "from IPython.display import Image, display\n", + "\n", + "display(Image(app.get_graph(xray=True).draw_mermaid_png()))\n", + "\n", + "# Image(app.get_graph(xray=True).draw_mermaid_png(output_file_path=\"05-langgraph-plan-and-execute.png\"))" + ] + }, + { + "cell_type": "markdown", + "id": "b6d266cb", + "metadata": { + "id": "b6d266cb" + }, + "source": [ + "## Running the Graph\n", + "\n", + "Finally, we run the entire workflow by providing user input. The workflow proceeds as follows:\n", + "\n", + "1. The **Planner** step creates an initial plan. \n", + "2. The **Execute** step executes the first item in the plan and returns results. \n", + "3. The **Re-plan** step checks if more actions are needed. If so, it updates the plan and goes back to **Execute** ; otherwise, it proceeds to **Final Report** . \n", + "4. The **Final Report** step generates a comprehensive markdown summary of all executed steps and the final answer.\n", + "\n", + "By following these steps, you can build a `plan-and-execute` agent in LangGraph, enabling structured, multi-step problem-solving with explicit long-term planning and flexible re-planning capabilities.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6323963b", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "6323963b", + "outputId": "a447df4d-1e5a-4174-bdab-ae12367b6ebb" + }, + "outputs": [], + "source": [ + "from langchain_core.runnables import RunnableConfig\n", + "\n", + "config = RunnableConfig(recursion_limit=50, configurable={\"thread_id\": \"1\"})\n", + "inputs = {\"input\": \"Please explain about AI Agents.\"}\n", + "\n", + "async for event in app.astream(inputs, config=config):\n", + " for k, v in event.items():\n", + " if k != \"__end__\":\n", + " print(v)" + ] + }, + { + "cell_type": "markdown", + "id": "QqqgsrSntPd8", + "metadata": { + "id": "QqqgsrSntPd8" + }, + "source": [ + "## Checking the Final Report" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e139d290", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "e139d290", + "outputId": "7dc8f880-d1e6-4fd3-89d1-e99c82fb4a8d" + }, + "outputs": [], + "source": [ + "snapshot = app.get_state(config).values\n", + "print(snapshot[\"response\"])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "611af101", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 1000 + }, + "id": "611af101", + "outputId": "84176145-6e78-4b26-87f3-963791c94df7" + }, + "outputs": [], + "source": [ + "from IPython.display import Markdown\n", + "\n", + "Markdown(snapshot[\"response\"])" + ] + } + ], + "metadata": { + "colab": { + "collapsed_sections": [ + "0856a092", + "9d230b5c", + "05f6efaa", + "b8c5d026", + "ef28ddf5" + ], + "provenance": [] + }, + "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.10" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/17-LangGraph/03-Use-Cases/assets/05-langgraph-plan-and-execute.png b/17-LangGraph/03-Use-Cases/assets/05-langgraph-plan-and-execute.png new file mode 100644 index 0000000000000000000000000000000000000000..95407235821798f5a5a23632cab93a6506853dc6 GIT binary patch literal 24084 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

PoO@DGsCJy$2oHeU_MAN0P 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 Up)36jAh!-tRa93fLYh7QFH~rTV*mgE literal 0 HcmV?d00001 From 8de953e81601af6ce2d660c8d49eb123e05ea05a Mon Sep 17 00:00:00 2001 From: ranian963 Date: Thu, 23 Jan 2025 15:05:58 +0900 Subject: [PATCH 2/2] Update 05_LangGraph_Plan_and_Execute.ipynb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 키 입력에 빈 문자열 빠진것을 추가하였습니다. --- .../03-Use-Cases/05_LangGraph_Plan_and_Execute.ipynb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/17-LangGraph/03-Use-Cases/05_LangGraph_Plan_and_Execute.ipynb b/17-LangGraph/03-Use-Cases/05_LangGraph_Plan_and_Execute.ipynb index 2f08ac190..4feee686c 100644 --- a/17-LangGraph/03-Use-Cases/05_LangGraph_Plan_and_Execute.ipynb +++ b/17-LangGraph/03-Use-Cases/05_LangGraph_Plan_and_Execute.ipynb @@ -137,12 +137,12 @@ "if not load_dotenv():\n", " set_env(\n", " {\n", - " \"OPENAI_API_KEY\": \n", - " \"LANGCHAIN_API_KEY\": \n", + " \"OPENAI_API_KEY\": \"\",\n", + " \"LANGCHAIN_API_KEY\": \"\",\n", " \"LANGCHAIN_TRACING_V2\": \"true\",\n", " \"LANGCHAIN_ENDPOINT\": \"https://api.smith.langchain.com\",\n", " \"LANGCHAIN_PROJECT\": \"05-LangGraph-Plan-and-Execute\",\n", - " \"TAVILY_API_KEY\": \n", + " \"TAVILY_API_KEY\": \"\",\n", " }\n", " )" ]