Building a Multi-Agent Chatbot with LangGraph: A Collaborative AI Approach

Introduction
In this rapidly progressing world of Generative AI and LLMs, we have seen models showcase broad capability and knowledge on every aspect of human life from healthcare to finance, by enabling faster decision-making, automating complex processes, and providing intelligent insights, ultimately redefining how humans interact with technology and access information.
Despite their broad knowledge , these models still have limitations in autonomy, specialization, and decision-making. In AI development, we often think of a single powerful model handling all tasks—whether answering questions, making recommendations, or generating content. However, as AI applications grow more complex, this “one-size-fits-all” approach becomes inefficient. Instead, we need multi-agent systems, where different specialized AI agents collaborate to solve tasks more effectively.
Now what are AI agents? An AI agent is an autonomous system that perceives its environment, processes information, and takes actions to achieve specific goals. Unlike traditional AI models that simply respond to user queries, agents are designed to make decisions, interact with tools or other agents, and execute complex tasks.
What Are Multi-Agent Systems?
A multi-agent system (MAS) consists of multiple AI agents that communicate, collaborate, or compete to achieve a goal. These agents can be independent or interdependent, working together in structured workflows. In the context of LLM-based AI applications, multi-agent systems improve:
- Task Specialization – Each agent focuses on a specific function, leading to more accurate and efficient responses.
- Parallel Processing – Multiple agents can operate simultaneously, reducing response times.
- Dynamic Adaptation – Agents can communicate and decide which one is best suited to handle a task.
- Scalability – As new tasks emerge, new agents can be introduced without redesigning the entire system.
Imagine planning a trip: You need an AI to find flights, another to create an itinerary, and maybe even one to suggest hotels or restaurants. If a single model tries to handle everything, it may struggle with task specialization, structured outputs, and decision-making. Instead, multi-agent systems break down complex problems into smaller, more manageable components, allowing each agent to focus on a specific role while working together as a team.
How does LangGraph fit into this?
Building a multi-agent system from scratch is complex—it requires orchestration, state management, and routing logic. LangGraph, an extension of LangChain, simplifies this by providing a framework for defining structured agent workflows using StateGraph.
LangGraph allows you to:
- Define multiple AI agents, each with specialized responsibilities.
- Create decision-making flows, so user inputs dynamically route to the right agent.
- Maintain state and conversation memory, ensuring agents work coherently.
- Design interruptible and flexible systems, allowing human intervention when needed.
Prerequisites
- Familiarity with python, LLM and prompt engineering.
- A OpenAI API key with access to GPT-4o
- A SerpAPI API key
Installing Required Libraries
Before we begin, create a python virtual environment and install the following required libraries.
python -m venv multi-env | |
.\multi-env\Scripts\activate | |
pip install langgraph langchain langchain-core serpapi langchain-openai google-search-result |
Overview of the Multi-Agent Chatbot
We will create a trip planning chatbot that can help users plan an itinerary for their trip and suggest the best available flights to them. In our trip planner, we use LangGraph to orchestrate a multi-agent workflow, ensuring the right agent handles the right task at the right time. Instead of a single AI attempting to answer everything, our system intelligently routes user queries to specialized agents, making the system more scalable, efficient, and modular. This ensures if we want to add additional functionality like car rentals, or hotel bookings, we can simply add agents specialized for their tasks.
Key components
- Chatbot : This agent acts as a supervisor of all the agents, as well as the main assistant the user will interact with to explain their needs and be responsible for collecting information from the user to disperse to the appropriate agents.
- Itinerary agent : This agent specializes in creating structured travel itineraries based on user preferences. It generates a day-by-day plan, including activities, accommodations, meal suggestions, and transportation details.
- Flight agent : This agent extracts all relevant information relevant information from the chat, uses an external API (SerpAPI) to fetch real-time flight information, and provides the best available flights to the user
This multi-agent framework follows a supervisor architecture. Here is a diagram to explain the flow better –
Here is a workflow details of this chatbot –
The user starts the conversation, and the Chatbot determines the nature of the request.
If the user needs an itinerary, the Itinerary Agent generates a personalized plan.
If the user asks for flight details, the Flight Agent extracts and processes the information.
If any clarifications are required, the conversation is routed to the Human Node for human feedback, ensuring a smooth user experience.
Once all details are collected and confirmed, the chatbot presents the final structured itinerary and flight details.
Let’s dive into the implementation of this chatbot.
Diving into the Code
1. Initialize a LLM
We first initialize a LLM that will be used by our chatbot and agents. For this we will use OpenAI’s GPT-4o. According to our testing it has the best performance for agentic tasks.
from langchain_openai import ChatOpenAI | |
llm = ChatOpenAI(model_name="gpt-4o", api_key = "") |
2. Creating the agents
First we create a state class that will be used by the agents to keep track of all the conversation flows. When defining a graph, the first step is to define its State. The State includes the graph’s schema and reducer functions that handle state updates.The schema of the State will be the input schema to all Nodes and Edges in the graph, and can be either a TypedDict or a Pydantic model. All Nodes will emit updates to the State which are then applied using the specified reducer function. We use MessagesState to keep track of messages in our state.
from langgraph.graph import MessagesState | |
class State(MessagesState): | |
next: str |
- Itinerary Agent
Now we create the itinerary agent. This agent directly calls the LLM with the system prompt for itinerary planning and user-provided travel details. It returns a single response from the LLM that contains the generated itinerary.
from typing import Annotated | |
itinerary_agent_prompt = """ | |
You are an expert travel planner. Your task is to create a detailed itinerary for a trip based on the information provided by the user. The itinerary should include the following details: | |
- Day-by-day schedule with activities and locations | |
- Suggested times for each activity | |
- Recommendations for meals and restaurants | |
- Transportation options between locations | |
- Any special events or local attractions to consider | |
- Accommodation details if provided | |
Ensure the itinerary is well-organized, practical, and tailored to the user's preferences and constraints. Use a friendly and informative tone. | |
""" | |
def itinerary_agent(itinary_info: Annotated[str, "All the itinerary information in a plain text format"]): | |
messages = [ | |
("system", itinerary_agent_prompt), | |
("human", {itinary_info}) | |
] | |
return llm.invoke(messages) |
- Flight Agent
Next we create a flight information agent. This is a ReAct-style agent (Reason + Act), meaning it can decide whether it needs to use a tool to answer the user’s query about flights.
First we set up a tool for this agent to use, which can extract relevant information from user query and structure it in a way that we can use an external API (SerpAPI) to retrieve real-time flight information.
This process involves creating a system prompt explaining what all parameters to extract to give to the API; setting up a class which inherits TypingDict to use for LLM structured outputs to make sure we get consistent JSON outputs of these parameters; initializing the LLM with this structured output; and creating a function that invokes this LLM, extracts the parameters and calls the flight API. We use the tool decorator on this function so we can use it as a ReAct agent.
from typing import Annotated, Literal, Optional | |
from typing_extensions import TypedDict | |
from serpapi import GoogleSearch | |
from langchain.prompts import ChatPromptTemplate | |
from langchain_core.tools import tool | |
flight_agent_prompt = """ | |
You are an expert at extracting information out of plain text. The response should be in JSON format. | |
You need to extract the following parameters: | |
- departure_id - city/airport IATA code from which the traveler will depart, e.g. BOS for Boston | |
- arrival_id - city/airport IATA code from which the traveler is going, e.g. BOS for Boston | |
- outbound_date - the date on which the traveler will depart from the origin to go to the destination. Dates are specified in the ISO 8601 YYYY-MM-DD format, e.g. 2017-12-25 | |
- return_date - the date on which the traveller will return from the destination back to the origin. Date is specified in the ISO 8601 YYYY-MM-DD format | |
""" | |
class Flight_parameters(TypedDict): | |
departure_id: Annotated[str, ..., "city/airport IATA code from which the traveler will depart, e.g. BOS for Boston"] | |
arrival_id: Annotated[str, ..., "city/airport IATA code from which the traveler is going, e.g. BOS for Boston"] | |
outbound_date: Annotated[str, ..., "the date on which the traveler will depart from the origin to go to the destination. Dates are specified in the ISO 8601 YYYY-MM-DD format, e.g. 2017-12-25"] | |
return_date: Annotated[Optional[str], None, "the date on which the traveller will return from the destination back to the origin. Date is specified in the ISO 8601 YYYY-MM-DD format"] | |
flight_prompt = ChatPromptTemplate.from_messages([('system', flight_agent_prompt), ('human', '{input}')]) | |
structured_llm_flight = flight_prompt | llm.with_structured_output(Flight_parameters) | |
@tool | |
def flight_tool(parameters: Annotated[str,"All the flight parameters in a plain text format"]): | |
""" | |
Get the list of available flights based on users' requirements. | |
Parameters: | |
query (str): User's flight requirements as natural language input. | |
Returns: | |
str: A human-readable response with flight offers or an error message. | |
""" | |
flight_parameters = structured_llm_flight.invoke({'input': parameters}) | |
flight_parameters['engine'] = 'google_flights' | |
flight_parameters['currencyCode'] = 'USD' | |
flight_parameters['hl'] = 'en' | |
flight_parameters['api_key'] = '' | |
search = GoogleSearch(flight_parameters) | |
results = search.get_dict() | |
return json.dumps(results) |
Now unlike the itinerary agent, this is a ReAct agent with the ability to execute tool calls. For this we will use LangGraphs pre-built ReAct agent function to initialize our agent. This function takes in a LLM, the tool, and a prompt to guide the response
from langgraph.graph import create_react_agent | |
flight_agent = create_react_agent( | |
llm, | |
tools=[flight_tool], | |
prompt=""" | |
You are a flight agent. You are responsible for finding the best flights for the user. Analyze the user's requirements and the tool response and return a few of the best flight information. | |
""" | |
) |
3. Creating the Supervising Chatbot
This is our brain of the multi-agent system. It not only is the interaction point of our user, but also decides whether to route the conversation to itinerary_agent, flight_agent, human user or to “finish” the conversation.
It takes the current conversation plus the chatbot instructions and requests a structured response from the LLM using the Router schema. This schema ensures that the LLM always responds with both the user message and next agent to route to.
from langgraph.graph import END | |
from langgraph.types import Command | |
from typing_extensions import TypedDict | |
from typing import Literal | |
chatbot_prompt = """ | |
You are a dedicated travel planning chatbot designed to create personalized and well-organized trip itineraries based on user preferences and constraints. | |
Your Responsibilities: | |
1. Greeting & Introduction: Start by greeting the user and explaining that you're here to help plan their trip. Inform them what task you can help them with. | |
Currently you can help them with 1. flight information and 2. itinerary planning | |
2. Gather Preferences & Constraints: Ask the user about their travel preferences, such as destinations, budget, duration, interests (e.g., adventure, relaxation, culture), and any constraints (e.g., dietary restrictions, mobility issues). | |
3. Information Collection: Inquire about specific details that is required for each agent as follows - | |
- flight_agent: Departure and arrival city/airport IATA code, departure and return date. | |
- itinerary_agent: preferred activities, types of accommodations, transportation preferences, and meal preferences. | |
If any information is missing, ask the user for the missing information. Only move on to routing once the user has provided all the information. | |
4. Agent Routing: Based on the collected information, determine which agent to route the user to: | |
- itinerary_agent: If sufficient trip-related information is provided or if the user asks you to come up with the rest of the information. | |
- flight_agent: If the user asks for flight information. | |
- human_interrupt: This is the user itself and not an expert agent. Only give response that are crafted by you or any request for additional information. | |
5. Response Handling: | |
- Structured Output: Ensure all responses are in JSON format with the keys: | |
- `next`: The next agent to route to (`itinerary_agent`, `human_interrupt`, or `FINISH`). | |
- `messages`: Any messages to send to the user. | |
- Information Validation: If any required information is missing or incomplete, gather more information from the user. | |
6. Finalization: Once the itinerary is complete, ask the user if they would like to add anything else. If not, respond with `"FINISH"` in the next key to conclude the interaction. NEVER END THE CONVERSATION WITH THE USER BEFORE THEY EXPLICITLY SAY TO END THE CONVERSATION. | |
Communication Style: | |
- Use a friendly, clear, and informative tone. | |
- Ensure all questions are concise and easy to understand. | |
- Make the user feel supported and assured throughout the planning process. | |
- When you are done with the itinerary, always route to human_interrupt agent. | |
Example Workflow: | |
1. Chatbot: "Hello! I'm here to help you plan your perfect trip. Could you please share your preferred destinations and travel dates?" | |
2. User: [Provides information] | |
3. Chatbot: [Routes to `itinerary_agent` with collected information] | |
4. itinerary_agent: [Creates itinerary] | |
5. Chatbot: "Here's your personalized itinerary! Here is the itinerary. Would you like to add anything else?" | |
6. User: "No, that's all. Thank you!" | |
7. Chatbot: `"FINISH"` | |
Ensure seamless and natural interactions while effectively gathering all necessary information to create a comprehensive trip itinerary. | |
""" | |
class Router(TypedDict): | |
"""Worker to route to next. If no workers needed, route to FINISH.""" | |
next: Literal['itinerary_agent', 'human_interrupt', 'FINISH'] | |
messages: str | |
def chatbot_node(state: State) -> Command[Literal['itinerary_agent', 'human_interrupt', 'flight_agent', "__end__"]]: | |
messages = [ | |
{"role": "system", "content": chatbot_prompt}, | |
] + state["messages"] | |
response = llm.with_structured_output(Router).invoke(messages) | |
goto = response["next"] | |
if goto == "FINISH": | |
goto = END | |
return Command(goto=goto, update={"next": goto, "messages": response["messages"]}) | |
This chatbot is set up as a Node of a graph. Nodes represent units of work. They are typically regular python functions. You will also notice that we return a Command. Command object gives us control in managing states of an agent as well as routing. It also helps in sending information/messages between each agent.
4. Creating the Agent Nodes
Now as above we will create nodes using our previously created agents.
from langgraph.graph import Command | |
from langchain_core.prompts.chat import HumanMessage | |
from typing_extensions import Literal | |
def itinerary_node(state: State) -> Command[Literal['chatbot']]: | |
# Convert the state messages to a list of strings | |
human_messages = [msg.content for msg in state['messages'] if isinstance(msg, HumanMessage)] | |
messages = [ | |
("system", itinerary_agent_prompt), | |
("human", " ".join(human_messages)) # Join all human messages into a single string | |
] | |
response = llm.invoke(messages) | |
return Command(goto='chatbot', update={"messages": [response.content]}) | |
def flight_node(state: State) -> Command[Literal['chatbot']]: | |
result = flight_agent.invoke(state) | |
return Command( | |
update={ | |
"messages": [result["messages"][-1].content] | |
}, | |
goto="chatbot", | |
) |
Same as the chatbot, it returns a Command object, that directs the message back to the chatbot node for validation and response generation.
5. Setting up Human Input
We will set up another node called human_interrupt. This will be used to temporarily return control to a human user for clarification or additional input. After receiving the user input, it routes back to the chatbot node with the updated messages.
from langgraph.graph import Command | |
from typing import Literal | |
def human_interrupt(state: State) -> Command[Literal['chatbot']]: | |
query = state['messages'][-1].content | |
user_input = input("user: ") | |
return Command(goto='chatbot', update={"messages": [user_input]}) |
In practice, we will have to set up these user inputs in a different way based on our frontend.
6. Building the StateGraph
With this our graph is all set up with all nodes defined. Now we will use the StateGraph to combine all these elements together into a multi-agent system. A StateGraph object defines the structure of our chatbot as a “state machine”. We’ll add nodes to represent the Agents and functions our chatbot can call and edges to specify how the bot should transition between these functions.
from langgraph.graph import StateGraph, START | |
if __name__ == "__main__": | |
builder = StateGraph(State) | |
builder.add_edge(START, "chatbot") | |
builder.add_node("chatbot", chatbot_node) | |
builder.add_node("itinerary_agent", itinerary_node) | |
builder.add_node("flight_agent", flight_node) | |
builder.add_node("human_interrupt", human_interrupt) | |
graph = builder.compile() |
We define the chatbot as the starting point of our conversation.
7. Running the Chatbot
We now run this chatbot. We set up a simple user input to begin with, this will make the flow of conversation very natural. We only print out the output of the chatbot, flight agent and itinerary agent as we do not want the user to see the inner routing and api response jargon.
for s in graph.stream({"messages": [("user", "Hi")]}, subgraphs=True): | |
for key, value in s[1].items(): | |
if key in ['chatbot', 'itinerary_agent', 'flight_agent']: | |
print(value['messages']) | |
print("----") |
Here is an example conversation with a chatbot helping the user plan a 5 day trip to Paris.
chatbot: Hello! I'm here to help you plan your perfect trip. I can assist you with gathering flight information or creating a personalized trip itinerary. What can I help you with today? | |
---- | |
user: I want help planning an itinerary | |
---- | |
chatbot: Great! Let's get started on planning your itinerary. Could you please share some details about your trip, such as your preferred destinations, total budget, and duration of stay? Also, let me know if you have any specific interests like adventure, relaxation, or culture, and any important constraints like dietary restrictions or mobility issues. Once I have that information, we can dive into preferred activities, types of accommodations, transportation, and meal preferences. | |
---- | |
user: I want to go on a 5 day trip to Paris to experience the culture there. I have no other specific preference so assume the rest of the information | |
---- | |
chatbot: Thank you for providing your preferences! I'll proceed with creating a cultural itinerary in Paris for your 5-day trip. We’ll assume standard accommodations and dining experiences and incorporate cultural highlights. I'll get back to you shortly with your personalized itinerary. | |
---- | |
itinerary_agent: ["**Your 5-Day Cultural Trip to Paris Itinerary**\n\n**Day 1: Arrival & Montmartre Exploration**\n- **Morning:**\n - Arrive in Paris and check into your hotel (suggested accommodation: Hôtel des Arts Montmartre).\n - Take a leisurely stroll around Montmartre, starting at the iconic Sacré-Cœur Basilica for spectacular views | |
of Paris.\n- **Lunch:**\n - Café des Deux Moulins (from the movie Amélie), famous for its charming atmosphere and delicious French classics.\n- **Afternoon:**\n - Visit the Musée de l'Orangerie to admire Monet’s Water Lilies.\n- **Evening:**\n - Dinner at Le Bouillon Pigalle, known for traditional French cuisine.\n- **Transportation:** \n | |
- Take advantage of Paris’s Metro system; buy a day pass for ease of travel.\n\n**Day 2: Iconic Sites and Seine River Cruise**\n- **Morning:**\n - Explore the extensive collections of the Louvre Museum (consider joining a guided tour to avoid the lines).\n- **Lunch:**\n - Le Fumoir, adjacent to the Louvre, for a sophisticated Parisian dining experience.\n- **Afternoon:**\n - Stroll through the Tuileries Garden toward the Champs-Élysées, ending at the Arc de Triomphe.\n- **Evening:**\n - Seine River Cruise | |
to see historic sites illuminated at night.\n - Dinner at Les Ombres, with stunning views of the Eiffel Tower.\n- **Transportation:** \n - Metro or walking, especially enjoyable along the Seine.\n\n**Day 3: Latin Quarter and Notre-Dame**\n- **Morning:**\n - Visit the historic Notre-Dame Cathedral (note: check current restoration status).\n - Tour the nearby Ile de la Cité.\n- **Lunch:**\n - Try Café Panis for a cozy meal with a view of Notre-Dame.\n- **Afternoon:**\n - Wander through the Latin Quarter, visiting the Panthéon and the Shakespeare and Company bookstore.\n- **Evening:**\n - Dinner at Le Procope, the oldest café in Paris.\n- **Transportation:** \n - Walking recommended to fully enjoy the ambiance of the area; Metro options for longer distances.\n\n**Day 4: Art and Shopping in Le Marais**\n- **Morning:**\n - Discover art at the Centre Pompidou with its modern architecture and collections.\n- **Lunch:**\n - Café des Musees, a hidden gem with traditional French meals.\n- **Afternoon:**\n - Shop and explore the charming streets in Le Marais, including the Picasso Museum.\n- **Evening:**\n - Enjoy dinner at Breizh Café for authentic Breton crepes.\n- **Transportation:** \n - Use of Metro or walking to explore the compact district.\n\n**Day 5: Versailles Day Trip**\n- **Morning & Afternoon:**\n - Day trip to the Palace of Versailles; explore the opulent palace rooms and the beautiful gardens.\n - Transportation by RER train from Paris (around 30-40 minutes).\n- **Lunch:** \n - At La Petite Venise, located within the grounds.\n- **Evening:**\n - Return to Paris for a farewell dinner at Bistrot Paul Bert, famous for its steak-frites.\n- **Transportation:** \n - RER train for a comfortable and direct journey.\n\n**Special Tips:**\n- Consider getting a Paris Museum Pass for entry to numerous attractions.\n- Keep an eye on special events or temporary exhibitions during your visit area.\n\nEnjoy your cultural journey through the heart of Paris! Feel free to ask for adjustments or additional recommendations tailored to specific interests. Bon voyage!"] | |
---- | |
chatbot: Here's your personalized 5-day cultural itinerary for Paris. Would you like me to add anything else or make any adjustments to your plan? | |
---- | |
user: No this is great. Can you now help me plan my flights | |
---- | |
chatbot: Great to hear you loved the itinerary! To help with your flights, I just need a bit more information. Please provide your departure city or airport IATA code and your preferred departure and return dates. Once I have these, I'll assist you with flight information. | |
---- | |
---- | |
flight_agent: ["Please provide your departure city or airport code, along with your desired departure and return dates for the trip to Paris. Once I have this information, | |
I'll proceed with finding the best flight options for you."] | |
---- | |
chatbot: Could you please provide your departure city or airport IATA code, and the dates you'd like to depart and return from your trip to Paris? Once I have those details, I'll be able to assist you with flight information. | |
---- | |
user: I want to depart from Ahmedabad. I am planning from the 1st Feb 2025 to the 6th Feb | |
---- | |
chatbot: Thank you for providing your travel details. I'll now proceed to find the best flight options for your trip from Ahmedabad (AMD) to Paris (CDG), departing on February 1, 2025, and returning on February 6, 2025. I'll have some flight options ready for you shortly. | |
---- | |
---- | |
---- | |
---- | |
flight_agent: ["Here are some of the best flight options for your trip from Ahmedabad (AMD) to Paris (CDG), departing on February 1, 2025, and returning on February 6, 2025:\n\n1. **Etihad Airways**\n - **Departure:** \n - Ahmedabad (AMD) at 05:00 AM\n - Arrival in Abu Dhabi (AUH) at 06:50 AM (Flight EY 247)\n - **Layover:** 7 hours and 30 minutes in Abu Dhabi\n - **Destination:**\n - Departure from Abu Dhabi (AUH) at 02:20 PM\n - Arrival in Paris (CDG) at 07:10 PM (Flight EY 33)\n - **Return:**\n - Paris (CDG) at 09:40 PM on February 6, 2025\n - Arrival in Abu Dhabi (AUH) at 06:30 AM on February 7, 2025\n - Departure from Abu Dhabi (AUH) at | |
09:45 AM\n - Arrival in Ahmedabad (AMD) at 02:15 PM (Flight EY 246)\n - **Total Price:** $978\n - **Features:** Checked baggage not included, free changes with a possible fare difference, above average legroom, in-seat power & USB.\n\n2. **Emirates**\n - **Departure:** \n - Ahmedabad (AMD) at 04:25 AM\n - Arrival in Dubai (DXB) at 06:15 AM (Flight EK 539)\n - **Layover:** 1 hour and 35 minutes in Dubai\n - **Destination:**\n - Departure from Dubai (DXB) at 07:50 AM\n - Arrival in Paris (CDG) at 12:25 PM (Flight EK 73)\n - **Return:**\n - Paris (CDG) at 09:50 PM on February 6, 2025\n - Arrival in Dubai (DXB) at 07:40 AM on February 7, 2025\n - Departure from Dubai (DXB) at 10:15 AM\n - Arrival in Ahmedabad (AMD) at 02:50 PM (Flight EK 538)\n - **Total Price:** $1,225\n - **Features:** 1 checked bag up to 30 kg included, free changes with a possible fare difference, above average legroom, Wi-Fi available for a fee.\n\n3. **Qatar Airways**\n - **Departure:** \n | |
- Ahmedabad (AMD) at 03:50 AM\n - Arrival in Doha (DOH) at 05:05 AM (Flight QR 535)\n - **Layover:** 3 hours and 10 minutes in Doha\n - **Destination:**\n - | |
Departure from Doha (DOH) at 08:15 AM\n - Arrival in Paris (CDG) at 01:25 PM (Flight QR 39)\n - **Return:**\n - Paris (CDG) at 09:40 PM on February 6, 2025\n | |
- Arrival in Doha (DOH) at 06:10 AM on February 7, 2025\n - Departure from Doha (DOH) at 08:45 AM\n - Arrival in Ahmedabad (AMD) at 02:10 PM (Flight QR 534)\n - **Total Price:** $1,433\n - **Features:** 1 checked bag up to 25 kg included, free changes with a possible fare difference, average legroom, Wi-Fi available for a fee.\n\n### [View More Flight Options](https://www.google.com/travel/flights?hl=en&gl=us&tfs=CBwQAhoeEgoyMDI1LTAyLTAxagcIARIDQU1EcgcIARIDQ0RHGh4SCjIwMjUtMDItMDZqBwgBEgNDREdyBwgBEgNBTURCAQFIAXABmAEB&tfu=EgIIAQ)\n\nThese options reflect flights with good legroom, favorable layovers, and amenities such as in-flight entertainment and charging points. Please let me know if you need additional information or if you'd like help with the booking process!"] | |
---- | |
chatbot: Here's the flight information for your trip from Ahmedabad to Paris. If you have any questions, need further assistance, or want to make a booking, feel free to let me know. | |
---- | |
user: No that was it. Thank you for your help | |
---- | |
chatbot: You're welcome! I'm glad I could assist you. Have a wonderful trip to Paris and enjoy experiencing the culture. If you have any more questions or need assistance in the future, feel free to reach out. Safe travels! | |
---- |
Conclusion
The above structure forms a robust, multi-agent chatbot using LangGraph. Each agent focuses on a specialized task—planning itineraries, fetching flight information, or simply passing control back to a human. The chatbot_node acts as the conversation orchestrator, making high-level decisions on where to route user requests. By leveraging LangGraph’s StateGraph, we maintain a clean separation of responsibilities and have a clear, visualizable workflow for development and debugging.
This modular design makes the system easy to extend:
- Adding a Hotel Agent? Just introduce a new node and route to it from chatbot_node.
- Human-in-the-loop checks? The human_interrupt node already demonstrates how to re-inject human input.
We now see how by using multi-agent architectures, we move beyond the limitations of a single LLM and create AI ecosystems where specialized agents collaborate to solve tasks efficiently. Agents don’t just generate responses—they perceive, reason, take actions, and interact with external tools and other agents. This agentic approach makes AI more dynamic, structured, and capable of handling real-world complexities.