docs: update how-to to remove agent wrapper (#2721)

This commit is contained in:
Vadym Barda
2024-12-12 08:15:17 -05:00
committed by GitHub
parent e4db5c2ca4
commit 083a14c2c5
5 changed files with 181 additions and 99 deletions
@@ -139,71 +139,117 @@
"from typing_extensions import TypedDict, Literal\n",
"\n",
"from langchain_openai import ChatOpenAI\n",
"from langchain_core.messages import HumanMessage\n",
"from langchain_core.messages import AnyMessage\n",
"from langchain_openai import ChatOpenAI\n",
"from langgraph.graph import MessagesState, StateGraph, START, END\n",
"from langgraph.types import Command, interrupt\n",
"from langgraph.checkpoint.memory import MemorySaver\n",
"from langgraph.prebuilt import create_react_agent\n",
"\n",
"model = ChatOpenAI(model=\"gpt-4o\")\n",
"\n",
"\n",
"def make_agent_node(*, name: str, destinations: list[str], system_prompt: str):\n",
" def agent_node(state: MessagesState) -> Command[Literal[*destinations, \"human\"]]:\n",
" # define schema for the structured output:\n",
" # - model's text response (`response`)\n",
" # - name of the node to go to next (or 'finish')\n",
" class Response(TypedDict):\n",
" response: str\n",
" goto: Literal[*destinations, \"finish\"]\n",
"# Define a helper for each of the agent nodes to call\n",
"def call_llm(messages: list[AnyMessage], target_agent_nodes: list[str]):\n",
" \"\"\"Call LLM with structured output to get a natural language response as well as a target agent (node) to go to next.\n",
"\n",
" messages = [{\"role\": \"system\", \"content\": system_prompt}] + state[\"messages\"]\n",
" response = model.with_structured_output(Response).invoke(messages)\n",
" goto = response[\"goto\"]\n",
" if goto == \"finish\":\n",
" # When the agent is done, we should go to the\n",
" goto = \"human\"\n",
"\n",
" # Handoff to another agent or halt\n",
" ai_msg = {\"role\": \"ai\", \"content\": response[\"response\"], \"name\": name}\n",
" return Command(goto=goto, update={\"messages\": [ai_msg]})\n",
"\n",
" return agent_node\n",
" Args:\n",
" messages: list of messages to pass to the LLM\n",
" target_agents: list of the node names of the target agents to navigate to\n",
" \"\"\"\n",
" # define JSON schema for the structured output:\n",
" # - model's text response (`response`)\n",
" # - name of the node to go to next (or 'finish')\n",
" # see more on structured output here https://python.langchain.com/docs/concepts/structured_outputs\n",
" json_schema = {\n",
" \"name\": \"Response\",\n",
" \"parameters\": {\n",
" \"type\": \"object\",\n",
" \"properties\": {\n",
" \"response\": {\n",
" \"type\": \"string\",\n",
" \"description\": \"A human readable response to the original question. Does not need to be a final response. Will be streamed back to the user.\",\n",
" },\n",
" \"goto\": {\n",
" \"enum\": [*target_agent_nodes, \"finish\"],\n",
" \"type\": \"string\",\n",
" \"description\": \"The next agent to call, or 'finish' if the user's query has been resolved. Must be one of the specified values.\",\n",
" },\n",
" },\n",
" \"required\": [\"response\", \"goto\"],\n",
" },\n",
" }\n",
" response = model.with_structured_output(json_schema).invoke(messages)\n",
" return response\n",
"\n",
"\n",
"travel_advisor = make_agent_node(\n",
" name=\"travel_advisor\",\n",
" destinations=[\"sightseeing_advisor\", \"hotel_advisor\", \"human\"],\n",
" system_prompt=(\n",
"def travel_advisor(\n",
" state: MessagesState,\n",
") -> Command[Literal[\"sightseeing_advisor\", \"hotel_advisor\", \"__end__\"]]:\n",
" system_prompt = (\n",
" \"You are a general travel expert that can recommend travel destinations (e.g. countries, cities, etc). \"\n",
" \"If you need specific sightseeing recommendations, ask 'sightseeing_advisor' for help. \"\n",
" \"If you need hotel recommendations, ask 'hotel_advisor' for help. \"\n",
" \"If you have enough information to respond to the user, return 'finish'. \"\n",
" \"Never mention other agents by name.\"\n",
" ),\n",
")\n",
"sightseeing_advisor = make_agent_node(\n",
" name=\"sightseeing_advisor\",\n",
" destinations=[\"travel_advisor\", \"hotel_advisor\", \"human\"],\n",
" system_prompt=(\n",
" )\n",
" messages = [{\"role\": \"system\", \"content\": system_prompt}] + state[\"messages\"]\n",
" target_agent_nodes = [\"sightseeing_advisor\", \"hotel_advisor\"]\n",
" response = call_llm(messages, target_agent_nodes)\n",
" ai_msg = {\"role\": \"ai\", \"content\": response[\"response\"], \"name\": \"travel_advisor\"}\n",
" # handoff to another agent or go to the human when agent is done\n",
" goto = response[\"goto\"]\n",
" if goto == \"finish\":\n",
" goto = \"human\"\n",
"\n",
" return Command(goto=goto, update={\"messages\": [ai_msg]})\n",
"\n",
"\n",
"def sightseeing_advisor(\n",
" state: MessagesState,\n",
") -> Command[Literal[\"travel_advisor\", \"hotel_advisor\", \"__end__\"]]:\n",
" system_prompt = (\n",
" \"You are a travel expert that can provide specific sightseeing recommendations for a given destination. \"\n",
" \"If you need general travel help, go to 'travel_advisor' for help. \"\n",
" \"If you need hotel recommendations, go to 'hotel_advisor' for help. \"\n",
" \"If you have enough information to respond to the user, return 'finish'. \"\n",
" \"Never mention other agents by name.\"\n",
" ),\n",
")\n",
"hotel_advisor = make_agent_node(\n",
" name=\"hotel_advisor\",\n",
" destinations=[\"travel_advisor\", \"sightseeing_advisor\", \"human\"],\n",
" system_prompt=(\n",
" )\n",
" messages = [{\"role\": \"system\", \"content\": system_prompt}] + state[\"messages\"]\n",
" target_agent_nodes = [\"travel_advisor\", \"hotel_advisor\"]\n",
" response = call_llm(messages, target_agent_nodes)\n",
" ai_msg = {\n",
" \"role\": \"ai\",\n",
" \"content\": response[\"response\"],\n",
" \"name\": \"sightseeing_advisor\",\n",
" }\n",
" # handoff to another agent or go to the human when agent is done\n",
" goto = response[\"goto\"]\n",
" if goto == \"finish\":\n",
" goto = \"human\"\n",
"\n",
" return Command(goto=goto, update={\"messages\": [ai_msg]})\n",
"\n",
"\n",
"def hotel_advisor(\n",
" state: MessagesState,\n",
") -> Command[Literal[\"travel_advisor\", \"sightseeing_advisor\", \"__end__\"]]:\n",
" system_prompt = (\n",
" \"You are a travel expert that can provide hotel recommendations for a given destination. \"\n",
" \"If you need general travel help, ask 'travel_advisor' for help. \"\n",
" \"If you need specific sightseeing recommendations, ask 'sightseeing_advisor' for help. \"\n",
" \"If you have enough information to respond to the user, return 'finish'. \"\n",
" \"Never mention other agents by name.\"\n",
" ),\n",
")\n",
" )\n",
" messages = [{\"role\": \"system\", \"content\": system_prompt}] + state[\"messages\"]\n",
" target_agent_nodes = [\"travel_advisor\", \"sightseeing_advisor\"]\n",
" response = call_llm(messages, target_agent_nodes)\n",
" ai_msg = {\"role\": \"ai\", \"content\": response[\"response\"], \"name\": \"hotel_advisor\"}\n",
" # handoff to another agent or go to the human when agent is done\n",
" goto = response[\"goto\"]\n",
" if goto == \"finish\":\n",
" goto = \"human\"\n",
"\n",
" return Command(goto=goto, update={\"messages\": [ai_msg]})\n",
"\n",
"\n",
"def human_node(\n",
@@ -376,7 +422,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.4"
"version": "3.12.3"
}
},
"nbformat": 4,
+83 -47
View File
@@ -133,74 +133,110 @@
{
"cell_type": "code",
"execution_count": 3,
"id": "aa4bdbff-9461-46cc-aee9-8a22d3c3d9ec",
"id": "29e2a824-dbeb-4944-9df2-63fdbd883252",
"metadata": {},
"outputs": [],
"source": [
"from typing_extensions import TypedDict, Literal\n",
"\n",
"from langchain_openai import ChatOpenAI\n",
"from langchain_core.messages import AnyMessage\n",
"from langgraph.graph import MessagesState, StateGraph, START, END\n",
"from langgraph.types import Command\n",
"\n",
"model = ChatOpenAI(model=\"gpt-4o\")\n",
"\n",
"\n",
"def make_agent_node(*, name: str, destinations: list[str], system_prompt: str):\n",
" def agent_node(state: MessagesState) -> Command[Literal[*destinations, END]]:\n",
" # define schema for the structured output:\n",
" # - model's text response (`response`)\n",
" # - name of the node to go to next (or 'finish')\n",
" class Response(TypedDict):\n",
" response: str\n",
" goto: Literal[*destinations, \"finish\"]\n",
"# Define a helper for each of the agent nodes to call\n",
"def call_llm(messages: list[AnyMessage], target_agent_nodes: list[str]):\n",
" \"\"\"Call LLM with structured output to get a natural language response as well as a target agent (node) to go to next.\n",
"\n",
" messages = [{\"role\": \"system\", \"content\": system_prompt}] + state[\"messages\"]\n",
" response = model.with_structured_output(Response).invoke(messages)\n",
" goto = response[\"goto\"]\n",
" if goto == \"finish\":\n",
" goto = END\n",
"\n",
" # handoff to another agent or halt\n",
" ai_msg = {\"role\": \"ai\", \"content\": response[\"response\"], \"name\": name}\n",
" return Command(goto=goto, update={\"messages\": ai_msg})\n",
"\n",
" return agent_node\n",
" Args:\n",
" messages: list of messages to pass to the LLM\n",
" target_agents: list of the node names of the target agents to navigate to\n",
" \"\"\"\n",
" # define JSON schema for the structured output:\n",
" # - model's text response (`response`)\n",
" # - name of the node to go to next (or 'finish')\n",
" # see more on structured output here https://python.langchain.com/docs/concepts/structured_outputs\n",
" json_schema = {\n",
" \"name\": \"Response\",\n",
" \"parameters\": {\n",
" \"type\": \"object\",\n",
" \"properties\": {\n",
" \"response\": {\n",
" \"type\": \"string\",\n",
" \"description\": \"A human readable response to the original question. Does not need to be a final response. Will be streamed back to the user.\",\n",
" },\n",
" \"goto\": {\n",
" \"enum\": [*target_agent_nodes, \"__end__\"],\n",
" \"type\": \"string\",\n",
" \"description\": \"The next agent to call, or __end__ if the user's query has been resolved. Must be one of the specified values.\",\n",
" },\n",
" },\n",
" \"required\": [\"response\", \"goto\"],\n",
" },\n",
" }\n",
" response = model.with_structured_output(json_schema).invoke(messages)\n",
" return response\n",
"\n",
"\n",
"travel_advisor = make_agent_node(\n",
" name=\"travel_advisor\",\n",
" destinations=[\"sightseeing_advisor\", \"hotel_advisor\"],\n",
" system_prompt=(\n",
"def travel_advisor(\n",
" state: MessagesState,\n",
") -> Command[Literal[\"sightseeing_advisor\", \"hotel_advisor\", \"__end__\"]]:\n",
" system_prompt = (\n",
" \"You are a general travel expert that can recommend travel destinations (e.g. countries, cities, etc). \"\n",
" \"If you need specific sightseeing recommendations, ask 'sightseeing_advisor' for help. \"\n",
" \"If you need hotel recommendations, ask 'hotel_advisor' for help. \"\n",
" \"If you have enough information to respond to the user, return 'finish'. \"\n",
" \"Never mention other agents by name.\"\n",
" ),\n",
")\n",
"sightseeing_advisor = make_agent_node(\n",
" name=\"sightseeing_advisor\",\n",
" destinations=[\"travel_advisor\", \"hotel_advisor\"],\n",
" system_prompt=(\n",
" )\n",
" messages = [{\"role\": \"system\", \"content\": system_prompt}] + state[\"messages\"]\n",
" target_agent_nodes = [\"sightseeing_advisor\", \"hotel_advisor\"]\n",
" response = call_llm(messages, target_agent_nodes)\n",
" ai_msg = {\"role\": \"ai\", \"content\": response[\"response\"], \"name\": \"travel_advisor\"}\n",
" # handoff to another agent or halt\n",
" return Command(goto=response[\"goto\"], update={\"messages\": ai_msg})\n",
"\n",
"\n",
"def sightseeing_advisor(\n",
" state: MessagesState,\n",
") -> Command[Literal[\"travel_advisor\", \"hotel_advisor\", \"__end__\"]]:\n",
" system_prompt = (\n",
" \"You are a travel expert that can provide specific sightseeing recommendations for a given destination. \"\n",
" \"If you need general travel help, go to 'travel_advisor' for help. \"\n",
" \"If you need hotel recommendations, go to 'hotel_advisor' for help. \"\n",
" \"If you have enough information to respond to the user, return 'finish'. \"\n",
" \"Never mention other agents by name.\"\n",
" ),\n",
")\n",
"hotel_advisor = make_agent_node(\n",
" name=\"hotel_advisor\",\n",
" destinations=[\"travel_advisor\", \"sightseeing_advisor\"],\n",
" system_prompt=(\n",
" )\n",
" messages = [{\"role\": \"system\", \"content\": system_prompt}] + state[\"messages\"]\n",
" target_agent_nodes = [\"travel_advisor\", \"hotel_advisor\"]\n",
" response = call_llm(messages, target_agent_nodes)\n",
" ai_msg = {\n",
" \"role\": \"ai\",\n",
" \"content\": response[\"response\"],\n",
" \"name\": \"sightseeing_advisor\",\n",
" }\n",
" # handoff to another agent or halt\n",
" return Command(goto=response[\"goto\"], update={\"messages\": ai_msg})\n",
"\n",
"\n",
"def hotel_advisor(\n",
" state: MessagesState,\n",
") -> Command[Literal[\"travel_advisor\", \"sightseeing_advisor\", \"__end__\"]]:\n",
" system_prompt = (\n",
" \"You are a travel expert that can provide hotel recommendations for a given destination. \"\n",
" \"If you need general travel help, ask 'travel_advisor' for help. \"\n",
" \"If you need specific sightseeing recommendations, ask 'sightseeing_advisor' for help. \"\n",
" \"If you have enough information to respond to the user, return 'finish'. \"\n",
" \"Never mention other agents by name.\"\n",
" ),\n",
")\n",
" )\n",
" messages = [{\"role\": \"system\", \"content\": system_prompt}] + state[\"messages\"]\n",
" target_agent_nodes = [\"travel_advisor\", \"sightseeing_advisor\"]\n",
" response = call_llm(messages, target_agent_nodes)\n",
" ai_msg = {\"role\": \"ai\", \"content\": response[\"response\"], \"name\": \"hotel_advisor\"}\n",
" # handoff to another agent or halt\n",
" return Command(goto=response[\"goto\"], update={\"messages\": ai_msg})\n",
"\n",
"\n",
"builder = StateGraph(MessagesState)\n",
@@ -254,7 +290,7 @@
"name": "stdout",
"output_type": "stream",
"text": [
"{'travel_advisor': {'messages': {'role': 'ai', 'content': 'The Caribbean offers many warm destinations perfect for a relaxing getaway. Consider visiting Jamaica for its beautiful beaches and vibrant culture, the Bahamas for its stunning islands and clear waters, or the Dominican Republic for its all-inclusive resorts and rich history. Let me know if you need more information on sightseeing or hotel recommendations!', 'name': 'travel_advisor'}}}\n",
"{'travel_advisor': {'messages': {'role': 'ai', 'content': 'The Caribbean offers many warm and beautiful destinations. Some popular ones include:\\n\\n1. **Jamaica** - Known for its beautiful beaches, reggae music, and vibrant culture.\\n2. **Bahamas** - Offers stunning beaches and clear turquoise waters, perfect for relaxation and water activities.\\n3. **Barbados** - Known for its friendly locals, delicious cuisine, and beautiful beaches.\\n4. **Dominican Republic** - Offers a mix of beaches, mountains, and historical sites.\\n5. **Aruba** - Known for its dry climate, beautiful beaches, and outdoor activities.\\n\\nWould you like recommendations for sightseeing or hotels in any of these destinations?', 'name': 'travel_advisor'}}}\n",
"\n",
"\n"
]
@@ -286,13 +322,13 @@
"name": "stdout",
"output_type": "stream",
"text": [
"{'travel_advisor': {'messages': {'role': 'ai', 'content': 'I recommend visiting Jamaica, a beautiful Caribbean island known for its warm climate, stunning beaches, and vibrant culture.', 'name': 'travel_advisor'}}}\n",
"{'travel_advisor': {'messages': {'role': 'ai', 'content': 'I recommend visiting Jamaica, a vibrant and warm destination in the Caribbean known for its beautiful beaches, rich culture, and exciting activities.', 'name': 'travel_advisor'}}}\n",
"\n",
"\n",
"{'sightseeing_advisor': {'messages': {'role': 'ai', 'content': \"Jamaica is a fantastic choice for a warm Caribbean getaway. Here are some top things to do while you're there:\\n\\n1. **Dunn's River Falls**: Located near Ocho Rios, this is one of Jamaica's most famous waterfalls. You can climb the falls, swim in the refreshing pools, or simply enjoy the beautiful surroundings.\\n\\n2. **Seven Mile Beach**: Located in Negril, this is one of the most beautiful beaches in the Caribbean. It's perfect for sunbathing, swimming, and enjoying water sports.\\n\\n3. **Bob Marley Museum**: Situated in Kingston, this museum is dedicated to the life and legacy of the reggae legend Bob Marley and is a must-visit for music lovers.\\n\\n4. **Blue Mountains**: Go hiking or take a tour to explore the Blue Mountains, where you can enjoy breathtaking views and taste some of the world's best coffee.\\n\\n5. **Luminous Lagoon**: Experience the natural wonder of the Luminous Lagoon in Falmouth, where the water glows at night due to bioluminescent microorganisms.\\n\\nFor hotel recommendations, I suggest checking with a hotel advisor for the best options that suit your budget and preferences.\", 'name': 'sightseeing_advisor'}}}\n",
"{'sightseeing_advisor': {'messages': {'role': 'ai', 'content': \"For a warm and vibrant Caribbean destination, I recommend Jamaica. It's renowned for its stunning beaches, lively culture, and exciting activities.\\n\\n### Things to Do in Jamaica:\\n1. **Explore Dunn’s River Falls**: A famous waterfall near Ocho Rios where you can climb the terraced steps and enjoy the natural pools.\\n2. **Visit Bob Marley Museum**: Located in Kingston, this museum offers a deep dive into the life of the reggae legend.\\n3. **Relax at Seven Mile Beach**: Known for its beautiful white sand and clear blue waters, it’s perfect for sunbathing and swimming.\\n4. **Experience Negril Cliffs**: Enjoy breathtaking views and adventurous cliff diving.\\n5. **Discover Blue Hole**: A hidden gem near Ocho Rios, offering a refreshing swim in stunning turquoise waters.\\n\\nI will now find some hotel recommendations for you in Jamaica.\", 'name': 'sightseeing_advisor'}}}\n",
"\n",
"\n",
"{'hotel_advisor': {'messages': {'role': 'ai', 'content': 'For hotel recommendations in Jamaica, here are a few options across different areas: \\n\\n1. **Sandals Montego Bay** (Montego Bay): A luxurious all-inclusive resort ideal for couples, offering beautiful beachfront views and a variety of dining options.\\n\\n2. **Half Moon Resort** (Montego Bay): A family-friendly resort with a private beach, golf course, and various activities for all ages.\\n\\n3. **Jamaica Inn** (Ocho Rios): A charming boutique hotel known for its excellent service and tranquil atmosphere.\\n\\n4. **The Caves** (Negril): A unique and romantic cliff-side resort offering stunning ocean views and intimate dining experiences.\\n\\n5. **Trident Hotel** (Port Antonio): A luxurious and contemporary hotel offering privacy, elegance, and beautiful views of the Caribbean Sea.\\n\\nThese options cater to different tastes and budgets, ensuring a comfortable and enjoyable stay in Jamaica.', 'name': 'hotel_advisor'}}}\n",
"{'hotel_advisor': {'messages': {'role': 'ai', 'content': 'For your stay in Jamaica, here are some hotel recommendations:\\n\\n1. **Sandals Montego Bay**: An all-inclusive resort offering luxury accommodations, private beaches, and various dining options.\\n\\n2. **Jamaica Inn**: Located in Ocho Rios, this boutique hotel offers a more intimate experience with stunning beach views and personalized service.\\n\\n3. **Half Moon Resort**: Situated in Montego Bay, this resort features a private beach, golf course, and a world-class spa.\\n\\n4. **The Caves Hotel**: In Negril, offering unique cliffside cottages and a romantic atmosphere.\\n\\n5. **Round Hill Hotel and Villas**: A classic resort near Montego Bay with luxurious villas and an elegant ambiance.\\n\\nEnjoy your trip to Jamaica, where warm weather and vibrant culture await!', 'name': 'hotel_advisor'}}}\n",
"\n",
"\n"
]
@@ -374,7 +410,7 @@
" guard_on_duty: bool\n",
"\n",
"\n",
"def villager(state: GameState) -> Command[Literal[\"villager\", END]]:\n",
"def villager(state: GameState) -> Command[Literal[\"villager\", \"__end__\"]]:\n",
" \"\"\"Villager NPC that gathers wood and food.\"\"\"\n",
" current_resources = state[\"wood\"] + state[\"food\"]\n",
" if current_resources < 15: # Continue gathering until we have enough resources\n",
@@ -387,7 +423,7 @@
" return Command(goto=END)\n",
"\n",
"\n",
"def guard(state: GameState) -> Command[Literal[\"guard\", END]]:\n",
"def guard(state: GameState) -> Command[Literal[\"guard\", \"__end__\"]]:\n",
" \"\"\"Guard NPC that protects gold and consumes food.\"\"\"\n",
" if not state[\"guard_on_duty\"]:\n",
" return Command(goto=END)\n",
@@ -404,7 +440,7 @@
" return Command(goto=END, update={\"guard_on_duty\": False}) # Leave to get food\n",
"\n",
"\n",
"def merchant(state: GameState) -> Command[Literal[\"merchant\", END]]:\n",
"def merchant(state: GameState) -> Command[Literal[\"merchant\", \"__end__\"]]:\n",
" \"\"\"Merchant NPC that trades wood for gold.\"\"\"\n",
" if state[\"wood\"] >= 5: # Trade wood for gold when available\n",
" print(\"Merchant trading wood for gold.\")\n",
@@ -413,7 +449,7 @@
" return Command(goto=END)\n",
"\n",
"\n",
"def thief(state: GameState) -> Command[Literal[\"thief\", END]]:\n",
"def thief(state: GameState) -> Command[Literal[\"thief\", \"__end__\"]]:\n",
" \"\"\"Thief NPC that steals gold if the guard leaves to get food.\"\"\"\n",
" if not state[\"guard_on_duty\"]:\n",
" print(\"Thief stealing gold.\")\n",
@@ -561,7 +597,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.9"
"version": "3.12.3"
}
},
"nbformat": 4,