[Docs] Update notebooks to use START (#902)

This commit is contained in:
William FH
2024-07-01 21:36:34 -07:00
committed by GitHub
parent 267f5e5234
commit 727e63c01e
67 changed files with 1059 additions and 20258 deletions
+16 -218
View File
@@ -45,10 +45,7 @@
"id": "b451b58a-89bd-424f-8c06-0d9fe325e01b",
"metadata": {},
"outputs": [],
"source": [
"%%capture --no-stderr\n",
"%pip install --quiet -U langgraph langchain-community langchain-openai tavily-python"
]
"source": ["%%capture --no-stderr\n%pip install --quiet -U langgraph langchain-community langchain-openai tavily-python"]
},
{
"cell_type": "markdown",
@@ -64,19 +61,7 @@
"id": "ce438281-08d5-4804-afe7-e4089f7b016b",
"metadata": {},
"outputs": [],
"source": [
"import getpass\n",
"import os\n",
"\n",
"\n",
"def _set_env(var: str):\n",
" if not os.environ.get(var):\n",
" os.environ[var] = getpass.getpass(f\"{var}: \")\n",
"\n",
"\n",
"_set_env(\"OPENAI_API_KEY\")\n",
"_set_env(\"TAVILY_API_KEY\")"
]
"source": ["import getpass\nimport os\n\n\ndef _set_env(var: str):\n if not os.environ.get(var):\n os.environ[var] = getpass.getpass(f\"{var}: \")\n\n\n_set_env(\"OPENAI_API_KEY\")\n_set_env(\"TAVILY_API_KEY\")"]
},
{
"cell_type": "markdown",
@@ -92,11 +77,7 @@
"id": "01f460d1-f26f-47d1-ae76-de74d5d851de",
"metadata": {},
"outputs": [],
"source": [
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
"_set_env(\"LANGCHAIN_API_KEY\")\n",
"os.environ[\"LANGCHAIN_PROJECT\"] = \"Plan-and-execute\""
]
"source": ["os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n_set_env(\"LANGCHAIN_API_KEY\")\nos.environ[\"LANGCHAIN_PROJECT\"] = \"Plan-and-execute\""]
},
{
"cell_type": "markdown",
@@ -114,11 +95,7 @@
"id": "25b9ec62-0675-4715-811c-9b32c635b22f",
"metadata": {},
"outputs": [],
"source": [
"from langchain_community.tools.tavily_search import TavilySearchResults\n",
"\n",
"tools = [TavilySearchResults(max_results=3)]"
]
"source": ["from langchain_community.tools.tavily_search import TavilySearchResults\n\ntools = [TavilySearchResults(max_results=3)]"]
},
{
"cell_type": "markdown",
@@ -151,20 +128,7 @@
]
}
],
"source": [
"from langchain import hub\n",
"from langchain_openai import ChatOpenAI\n",
"\n",
"from langgraph.prebuilt import create_react_agent\n",
"\n",
"# Get the prompt to use - you can modify this!\n",
"prompt = hub.pull(\"wfh/react-agent-executor\")\n",
"prompt.pretty_print()\n",
"\n",
"# Choose the LLM that will drive the agent\n",
"llm = ChatOpenAI(model=\"gpt-4-turbo-preview\")\n",
"agent_executor = create_react_agent(llm, tools, messages_modifier=prompt)"
]
"source": ["from langchain import hub\nfrom langchain_openai import ChatOpenAI\n\nfrom langgraph.prebuilt import create_react_agent\n\n# Get the prompt to use - you can modify this!\nprompt = hub.pull(\"wfh/react-agent-executor\")\nprompt.pretty_print()\n\n# Choose the LLM that will drive the agent\nllm = ChatOpenAI(model=\"gpt-4-turbo-preview\")\nagent_executor = create_react_agent(llm, tools, messages_modifier=prompt)"]
},
{
"cell_type": "code",
@@ -186,9 +150,7 @@
"output_type": "execute_result"
}
],
"source": [
"agent_executor.invoke({\"messages\": [(\"user\", \"who is the winnner of the us open\")]})"
]
"source": ["agent_executor.invoke({\"messages\": [(\"user\", \"who is the winnner of the us open\")]})"]
},
{
"cell_type": "markdown",
@@ -212,17 +174,7 @@
"id": "8eeeaeea-8f10-4fbe-8e24-4e1a2381a009",
"metadata": {},
"outputs": [],
"source": [
"import operator\n",
"from typing import Annotated, List, Tuple, TypedDict\n",
"\n",
"\n",
"class PlanExecute(TypedDict):\n",
" input: str\n",
" plan: List[str]\n",
" past_steps: Annotated[List[Tuple], operator.add]\n",
" response: str"
]
"source": ["import operator\nfrom typing import Annotated, List, Tuple, TypedDict\n\n\nclass PlanExecute(TypedDict):\n input: str\n plan: List[str]\n past_steps: Annotated[List[Tuple], operator.add]\n response: str"]
},
{
"cell_type": "markdown",
@@ -240,17 +192,7 @@
"id": "4a88626d-6dfd-4488-87f0-a9a0dd6da44c",
"metadata": {},
"outputs": [],
"source": [
"from langchain_core.pydantic_v1 import BaseModel, Field\n",
"\n",
"\n",
"class Plan(BaseModel):\n",
" \"\"\"Plan to follow in future\"\"\"\n",
"\n",
" steps: List[str] = Field(\n",
" description=\"different steps to follow, should be in sorted order\"\n",
" )"
]
"source": ["from langchain_core.pydantic_v1 import BaseModel, Field\n\n\nclass Plan(BaseModel):\n \"\"\"Plan to follow in future\"\"\"\n\n steps: List[str] = Field(\n description=\"different steps to follow, should be in sorted order\"\n )"]
},
{
"cell_type": "code",
@@ -258,24 +200,7 @@
"id": "ec7b1867-1ea3-4df3-9a98-992a1c32ec49",
"metadata": {},
"outputs": [],
"source": [
"from langchain_core.prompts import ChatPromptTemplate\n",
"\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",
"planner = planner_prompt | ChatOpenAI(\n",
" model=\"gpt-4o\", temperature=0\n",
").with_structured_output(Plan)"
]
"source": ["from langchain_core.prompts import ChatPromptTemplate\n\nplanner_prompt = ChatPromptTemplate.from_messages(\n [\n (\n \"system\",\n \"\"\"For the given objective, come up with a simple step by step plan. \\\nThis plan should involve individual tasks, that if executed correctly will yield the correct answer. Do not add any superfluous steps. \\\nThe 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)\nplanner = planner_prompt | ChatOpenAI(\n model=\"gpt-4o\", temperature=0\n).with_structured_output(Plan)"]
},
{
"cell_type": "code",
@@ -294,15 +219,7 @@
"output_type": "execute_result"
}
],
"source": [
"planner.invoke(\n",
" {\n",
" \"messages\": [\n",
" (\"user\", \"what is the hometown of the current Australia open winner?\")\n",
" ]\n",
" }\n",
")"
]
"source": ["planner.invoke(\n {\n \"messages\": [\n (\"user\", \"what is the hometown of the current Australia open winner?\")\n ]\n }\n)"]
},
{
"cell_type": "markdown",
@@ -320,47 +237,7 @@
"id": "ec2d12cc-016a-44d1-aa08-4c5ce1e8fe2a",
"metadata": {},
"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",
" 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",
"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",
"replanner = replanner_prompt | ChatOpenAI(\n",
" model=\"gpt-4o\", temperature=0\n",
").with_structured_output(Act)"
]
"source": ["from typing import Union\n\n\nclass Response(BaseModel):\n \"\"\"Response to user.\"\"\"\n\n response: str\n\n\nclass Act(BaseModel):\n \"\"\"Action to perform.\"\"\"\n\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\nreplanner_prompt = ChatPromptTemplate.from_template(\n \"\"\"For the given objective, come up with a simple step by step plan. \\\nThis plan should involve individual tasks, that if executed correctly will yield the correct answer. Do not add any superfluous steps. \\\nThe 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\nYour objective was this:\n{input}\n\nYour original plan was this:\n{plan}\n\nYou have currently done the follow steps:\n{past_steps}\n\nUpdate 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\nreplanner = replanner_prompt | ChatOpenAI(\n model=\"gpt-4o\", temperature=0\n).with_structured_output(Act)"]
},
{
"cell_type": "markdown",
@@ -378,43 +255,7 @@
"id": "6c8e0dad-bcea-4c9a-8922-0d820892e2d0",
"metadata": {},
"outputs": [],
"source": [
"from typing import Literal\n",
"\n",
"\n",
"async def execute_step(state: PlanExecute):\n",
" plan = state[\"plan\"]\n",
" plan_str = \"\\n\".join(f\"{i+1}. {step}\" for i, step in enumerate(plan))\n",
" task = plan[0]\n",
" task_formatted = f\"\"\"For the following plan:\n",
"{plan_str}\\n\\nYou are tasked with executing step {1}, {task}.\"\"\"\n",
" agent_response = await agent_executor.ainvoke(\n",
" {\"messages\": [(\"user\", task_formatted)]}\n",
" )\n",
" return {\n",
" \"past_steps\": (task, agent_response[\"messages\"][-1].content),\n",
" }\n",
"\n",
"\n",
"async def plan_step(state: PlanExecute):\n",
" plan = await planner.ainvoke({\"messages\": [(\"user\", state[\"input\"])]})\n",
" return {\"plan\": plan.steps}\n",
"\n",
"\n",
"async def replan_step(state: PlanExecute):\n",
" output = await replanner.ainvoke(state)\n",
" if isinstance(output.action, Response):\n",
" return {\"response\": output.action.response}\n",
" else:\n",
" return {\"plan\": output.action.steps}\n",
"\n",
"\n",
"def should_end(state: PlanExecute) -> Literal[\"agent\", \"__end__\"]:\n",
" if \"response\" in state and state[\"response\"]:\n",
" return \"__end__\"\n",
" else:\n",
" return \"agent\""
]
"source": ["from typing import Literal\n\n\nasync def execute_step(state: PlanExecute):\n plan = state[\"plan\"]\n plan_str = \"\\n\".join(f\"{i+1}. {step}\" for i, step in enumerate(plan))\n task = plan[0]\n task_formatted = f\"\"\"For the following plan:\n{plan_str}\\n\\nYou are tasked with executing step {1}, {task}.\"\"\"\n agent_response = await agent_executor.ainvoke(\n {\"messages\": [(\"user\", task_formatted)]}\n )\n return {\n \"past_steps\": (task, agent_response[\"messages\"][-1].content),\n }\n\n\nasync def plan_step(state: PlanExecute):\n plan = await planner.ainvoke({\"messages\": [(\"user\", state[\"input\"])]})\n return {\"plan\": plan.steps}\n\n\nasync def replan_step(state: PlanExecute):\n output = await replanner.ainvoke(state)\n if isinstance(output.action, Response):\n return {\"response\": output.action.response}\n else:\n return {\"plan\": output.action.steps}\n\n\ndef should_end(state: PlanExecute) -> Literal[\"agent\", \"__end__\"]:\n if \"response\" in state and state[\"response\"]:\n return \"__end__\"\n else:\n return \"agent\""]
},
{
"cell_type": "code",
@@ -422,39 +263,7 @@
"id": "e954cea0-5ccc-46c2-a27b-f5b7185b597d",
"metadata": {},
"outputs": [],
"source": [
"from langgraph.graph import StateGraph\n",
"\n",
"workflow = StateGraph(PlanExecute)\n",
"\n",
"# Add the plan node\n",
"workflow.add_node(\"planner\", plan_step)\n",
"\n",
"# Add the execution step\n",
"workflow.add_node(\"agent\", execute_step)\n",
"\n",
"# Add a replan node\n",
"workflow.add_node(\"replan\", replan_step)\n",
"\n",
"workflow.set_entry_point(\"planner\")\n",
"\n",
"# From plan we go to agent\n",
"workflow.add_edge(\"planner\", \"agent\")\n",
"\n",
"# From agent, we replan\n",
"workflow.add_edge(\"agent\", \"replan\")\n",
"\n",
"workflow.add_conditional_edges(\n",
" \"replan\",\n",
" # Next, we pass in the function that will determine which node is called next.\n",
" should_end,\n",
")\n",
"\n",
"# Finally, we compile it!\n",
"# This compiles it into a LangChain Runnable,\n",
"# meaning you can use it as you would any other runnable\n",
"app = workflow.compile()"
]
"source": ["from langgraph.graph import StateGraph, START\n\nworkflow = StateGraph(PlanExecute)\n\n# Add the plan node\nworkflow.add_node(\"planner\", plan_step)\n\n# Add the execution step\nworkflow.add_node(\"agent\", execute_step)\n\n# Add a replan node\nworkflow.add_node(\"replan\", replan_step)\n\nworkflow.add_edge(START, \"planner\")\n\n# From plan we go to agent\nworkflow.add_edge(\"planner\", \"agent\")\n\n# From agent, we replan\nworkflow.add_edge(\"agent\", \"replan\")\n\nworkflow.add_conditional_edges(\n \"replan\",\n # Next, we pass in the function that will determine which node is called next.\n should_end,\n)\n\n# Finally, we compile it!\n# This compiles it into a LangChain Runnable,\n# meaning you can use it as you would any other runnable\napp = workflow.compile()"]
},
{
"cell_type": "code",
@@ -473,11 +282,7 @@
"output_type": "display_data"
}
],
"source": [
"from IPython.display import Image, display\n",
"\n",
"display(Image(app.get_graph(xray=True).draw_mermaid_png()))"
]
"source": ["from IPython.display import Image, display\n\ndisplay(Image(app.get_graph(xray=True).draw_mermaid_png()))"]
},
{
"cell_type": "code",
@@ -505,14 +310,7 @@
]
}
],
"source": [
"config = {\"recursion_limit\": 50}\n",
"inputs = {\"input\": \"what is the hometown of the 2024 Australia open winner?\"}\n",
"async for event in app.astream(inputs, config=config):\n",
" for k, v in event.items():\n",
" if k != \"__end__\":\n",
" print(v)"
]
"source": ["config = {\"recursion_limit\": 50}\ninputs = {\"input\": \"what is the hometown of the 2024 Australia open winner?\"}\nasync 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",
@@ -530,7 +328,7 @@
"id": "ad8f7955-2cc9-4ebb-8c41-13abb3351a24",
"metadata": {},
"outputs": [],
"source": []
"source": [""]
}
],
"metadata": {