Open In Colab Open on GitHub

In this notebook, we’re building a trip planning swarm which has an objective to create an itinerary together with a customer. The end result will be an itinerary that has route times and distances calculated between activities.

The following diagram outlines the key components of the Swarm, with highlights being:

  • FalkorDB agent using a GraphRAG database of restaurants and attractions
  • Structured Output agent that will enforce a strict format for the accepted itinerary
  • Routing agent that utilises the Google Maps API to calculate distances between activites
  • Swarm orchestration utilising context variables
:::info Requirements
FalkorDB's GraphRAG-SDK is a dependency for this notebook, which can be installed with ag2 via pip:

```bash
pip install ag2[graph-rag-falkor-db]
```

For more information, please refer to the [installation guide](/docs/installation/).
:::

Pydantic

Please ensure you have Pydantic version 2+ installed.

pip install --upgrade pydantic

Running a FalkorDB

Note: You need to have a FalkorDB graph database running. If you are running one in a Docker container, please ensure your Docker network is setup to allow access to it.

In this example, we’ve set the FalkorDB host and port, please adjust them accordingly. For how to set up FalkorDB, please refer to https://docs.falkordb.com/.

Google Maps API Key

To use Google’s API to calculate travel times, you will need to have enabled the Directions API in your Google Maps Platform. You can get an API key and free quota, see here and here for more details.

Once you have your API key, set your environment variable GOOGLE_MAP_API_KEY to the key

Set Configuration and OpenAI API Key

By default, FalkorDB uses OpenAI LLMs and that requires an OpenAI key in your environment variable OPENAI_API_KEY.

You can utilise an OAI_CONFIG_LIST file and extract the OpenAI API key and put it in the environment, as will be shown in the following cell.

Alternatively, you can load the environment variable yourself.

:::tip
Learn more about configuring LLMs for agents [here](/docs/topics/llm_configuration).
:::
import os

import autogen

config_list = autogen.config_list_from_json(env_or_file="OAI_CONFIG_LIST", filter_dict={"model": ["gpt-4o"]})
llm_config = {"config_list": config_list, "timeout": 120}

# Put the OpenAI API key into the environment
os.environ["OPENAI_API_KEY"] = config_list[0]["api_key"]
/usr/local/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html
  from .autonotebook import tqdm as notebook_tqdm

Prepare the FalkorDB GraphRAG database

Using 3 sample JSON data files from our GitHub repository, we will create a specific ontology for our GraphRAG database and then populate it.

Creating a specific ontology that matches with the types of queries makes for a more optimal database and is more cost efficient when populating the knowledge graph.

from autogen.agentchat.contrib.graph_rag.document import Document, DocumentType

# 3 Files (adjust path as necessary)
input_paths = [
    "../test/agentchat/contrib/graph_rag/trip_planner_data/attractions.json",
    "../test/agentchat/contrib/graph_rag/trip_planner_data/cities.json",
    "../test/agentchat/contrib/graph_rag/trip_planner_data/restaurants.json",
]
input_documents = [Document(doctype=DocumentType.TEXT, path_or_url=input_path) for input_path in input_paths]

Create Ontology

Entities: Country, City, Attraction, Restaurant

Relationships: City in Country, Attraction in City, Restaurant in City

from graphrag_sdk import Attribute, AttributeType, Entity, Ontology, Relation

# Attraction + Restaurant + City + Country Ontology
trip_data_ontology = Ontology()

trip_data_ontology.add_entity(
    Entity(
        label="Country",
        attributes=[
            Attribute(
                name="name",
                attr_type=AttributeType.STRING,
                required=True,
                unique=True,
            ),
        ],
    )
)
trip_data_ontology.add_entity(
    Entity(
        label="City",
        attributes=[
            Attribute(
                name="name",
                attr_type=AttributeType.STRING,
                required=True,
                unique=True,
            ),
            Attribute(
                name="weather",
                attr_type=AttributeType.STRING,
                required=False,
                unique=False,
            ),
            Attribute(
                name="population",
                attr_type=AttributeType.NUMBER,
                required=False,
                unique=False,
            ),
        ],
    )
)
trip_data_ontology.add_entity(
    Entity(
        label="Restaurant",
        attributes=[
            Attribute(
                name="name",
                attr_type=AttributeType.STRING,
                required=True,
                unique=True,
            ),
            Attribute(
                name="description",
                attr_type=AttributeType.STRING,
                required=False,
                unique=False,
            ),
            Attribute(
                name="rating",
                attr_type=AttributeType.NUMBER,
                required=False,
                unique=False,
            ),
            Attribute(
                name="food_type",
                attr_type=AttributeType.STRING,
                required=False,
                unique=False,
            ),
        ],
    )
)
trip_data_ontology.add_entity(
    Entity(
        label="Attraction",
        attributes=[
            Attribute(
                name="name",
                attr_type=AttributeType.STRING,
                required=True,
                unique=True,
            ),
            Attribute(
                name="description",
                attr_type=AttributeType.STRING,
                required=False,
                unique=False,
            ),
            Attribute(
                name="type",
                attr_type=AttributeType.STRING,
                required=False,
                unique=False,
            ),
        ],
    )
)
trip_data_ontology.add_relation(
    Relation(
        label="IN_COUNTRY",
        source="City",
        target="Country",
    )
)
trip_data_ontology.add_relation(
    Relation(
        label="IN_CITY",
        source="Restaurant",
        target="City",
    )
)
trip_data_ontology.add_relation(
    Relation(
        label="IN_CITY",
        source="Attraction",
        target="City",
    )
)

Establish FalkorDB and load

Remember: Change your host, port, and preferred OpenAI model if needed (gpt-4o-mini and better is recommended).

from graphrag_sdk.models.openai import OpenAiGenerativeModel

from autogen.agentchat.contrib.graph_rag.falkor_graph_query_engine import FalkorGraphQueryEngine
from autogen.agentchat.contrib.graph_rag.falkor_graph_rag_capability import FalkorGraphRagCapability

# Create FalkorGraphQueryEngine
query_engine = FalkorGraphQueryEngine(
    name="trip_data",
    host="192.168.0.115",  # Change
    port=6379,  # if needed
    ontology=trip_data_ontology,
    model=OpenAiGenerativeModel("gpt-4o"),
)

# Ingest data and initialize the database
# query_engine.init_db(input_doc=input_documents)

# If you have already ingested and created the database, you can use this connect_db instead of init_db
query_engine.connect_db()
# IMPORTS
import copy
import json
import os
from typing import Any, Dict

import requests
from pydantic import BaseModel

from autogen import (
    AFTER_WORK,
    ON_CONDITION,
    AfterWorkOption,
    SwarmAgent,
    SwarmResult,
    UserProxyAgent,
    initiate_swarm_chat,
)

Pydantic model for Structured Output

Utilising OpenAI’s Structured Outputs, our Structured Output agent’s responses will be constrained to this Pydantic model.

The itinerary is structured as: Itinerary has Day(s) has Event(s)

class Event(BaseModel):
    type: str  # Attraction, Restaurant, Travel
    location: str
    city: str
    description: str


class Day(BaseModel):
    events: list[Event]


class Itinerary(BaseModel):
    days: list[Day]

Google Maps Platform

The functions necessary to query the Directions API to get travel times.

def _fetch_travel_time(origin: str, destination: str) -> dict:
    """
    Retrieves route information using Google Maps Directions API.
    API documentation at https://developers.google.com/maps/documentation/directions/get-directions
    """
    endpoint = "https://maps.googleapis.com/maps/api/directions/json"
    params = {
        "origin": origin,
        "destination": destination,
        "mode": "walking",  # driving (default), bicycling, transit
        "key": os.environ.get("GOOGLE_MAP_API_KEY"),
    }

    response = requests.get(endpoint, params=params)
    if response.status_code == 200:
        return response.json()
    else:
        return {"error": "Failed to retrieve the route information", "status_code": response.status_code}


def update_itinerary_with_travel_times(context_variables: dict) -> SwarmResult:
    """Update the complete itinerary with travel times between each event."""

    """
    Retrieves route information using Google Maps Directions API.
    API documentation at https://developers.google.com/maps/documentation/directions/get-directions
    """

    # Ensure that we have a structured itinerary, if not, back to the structured_output_agent to make it
    if context_variables.get("structured_itinerary") is None:
        return SwarmResult(
            agent="structured_output_agent",
            values="Structured itinerary not found, please create the structured output, structured_output_agent.",
        )
    elif "timed_itinerary" in context_variables:
        return SwarmResult(values="Timed itinerary already done, inform the customer that their itinerary is ready!")

    # Process the itinerary, converting it back to an object and working through each event to work out travel time and distance
    itinerary_object = Itinerary.model_validate(json.loads(context_variables["structured_itinerary"]))
    for day in itinerary_object.days:
        events = day.events
        new_events = []
        pre_event, cur_event = None, None
        event_count = len(events)
        index = 0
        while index < event_count:
            if index > 0:
                pre_event = events[index - 1]

            cur_event = events[index]
            if pre_event:
                origin = ", ".join([pre_event.location, pre_event.city])
                destination = ", ".join([cur_event.location, cur_event.city])
                maps_api_response = _fetch_travel_time(origin=origin, destination=destination)
                try:
                    leg = maps_api_response["routes"][0]["legs"][0]
                    travel_time_txt = f"{leg['duration']['text']}, ({leg['distance']['text']})"
                    new_events.append(
                        Event(
                            type="Travel",
                            location=f"walking from {pre_event.location} to {cur_event.location}",
                            city=cur_event.city,
                            description=travel_time_txt,
                        )
                    )
                except Exception:
                    print(f"Note: Unable to get travel time from {origin} to {destination}")
            new_events.append(cur_event)
            index += 1
        day.events = new_events

    context_variables["timed_itinerary"] = itinerary_object.model_dump()

    return SwarmResult(context_variables=context_variables, values="Timed itinerary added to context with travel times")

Swarm

Context Variables

Our swarm agents will have access to a couple of context variables in relation to the itinerary.

trip_context = {
    "itinerary_confirmed": False,
    "itinerary": "",
    "structured_itinerary": None,
}

Agent Functions

We have two functions/tools for our agents.

One for our Planner agent to mark an itinerary as confirmed by the customer and to store the final text itinerary. This will then transfer to our Structured Output agent.

Another for the Structured Output Agent to save the structured itinerary and transfer to the Route Timing agent.

def mark_itinerary_as_complete(final_itinerary: str, context_variables: Dict[str, Any]) -> SwarmResult:
    """Store and mark our itinerary as accepted by the customer."""
    context_variables["itinerary_confirmed"] = True
    context_variables["itinerary"] = final_itinerary

    # This will update the context variables and then transfer to the Structured Output agent
    return SwarmResult(
        agent="structured_output_agent", context_variables=context_variables, values="Itinerary recorded and confirmed."
    )


def create_structured_itinerary(context_variables: Dict[str, Any], structured_itinerary: str) -> SwarmResult:
    """Once a structured itinerary is created, store it and pass on to the Route Timing agent."""

    # Ensure the itinerary is confirmed, if not, back to the Planner agent to confirm it with the customer
    if not context_variables["itinerary_confirmed"]:
        return SwarmResult(
            agent="planner_agent",
            values="Itinerary not confirmed, please confirm the itinerary with the customer first.",
        )

    context_variables["structured_itinerary"] = structured_itinerary

    # This will update the context variables and then transfer to the Route Timing agent
    return SwarmResult(
        agent="route_timing_agent", context_variables=context_variables, values="Structured itinerary stored."
    )

Agents

Our SwarmAgents and a UserProxyAgent (human) which the swarm will interact with.

# Planner agent, interacting with the customer and GraphRag agent, to create an itinerary
planner_agent = SwarmAgent(
    name="planner_agent",
    system_message="You are a trip planner agent. It is important to know where the customer is going, how many days, what they want to do."
    + "You will work with another agent, graphrag_agent, to get information about restaurant and attractions. "
    + "You are also working with the customer, so you must ask the customer what they want to do if you don’t have LOCATION, NUMBER OF DAYS, MEALS, and ATTRACTIONS. "
    + "When you have the customer's requirements, work with graphrag_agent to get information for an itinerary."
    + "You are responsible for creating the itinerary and for each day in the itinerary you MUST HAVE events and EACH EVENT MUST HAVE a 'type' ('Restaurant' or 'Attraction'), 'location' (name of restaurant or attraction), 'city', and 'description'. "
    + "Finally, YOU MUST ask the customer if they are happy with the itinerary before marking the itinerary as complete.",
    functions=[mark_itinerary_as_complete],
    llm_config=llm_config,
)

# FalkorDB GraphRAG agent, utilising the FalkorDB to gather data for the Planner agent
graphrag_agent = SwarmAgent(
    name="graphrag_agent",
    system_message="Return a list of restaurants and/or attractions. List them separately and provide ALL the options in the location. Do not provide travel advice.",
)

# Adding the FalkorDB capability to the agent
graph_rag_capability = FalkorGraphRagCapability(query_engine)
graph_rag_capability.add_to_agent(graphrag_agent)

# Structured Output agent, formatting the itinerary into a structured format through the response_format on the LLM Configuration
structured_config_list = copy.deepcopy(config_list)
for config in structured_config_list:
    config["response_format"] = Itinerary

structured_output_agent = SwarmAgent(
    name="structured_output_agent",
    system_message="You are a data formatting agent, format the provided itinerary in the context below into the provided format.",
    llm_config={"config_list": structured_config_list, "timeout": 120},
    functions=[create_structured_itinerary],
)

# Route Timing agent, adding estimated travel times to the itinerary by utilising the Google Maps Platform
route_timing_agent = SwarmAgent(
    name="route_timing_agent",
    system_message="You are a route timing agent. YOU MUST call the update_itinerary_with_travel_times tool if you do not see the exact phrase 'Timed itinerary added to context with travel times' is seen in this conversation. Only after this please tell the customer 'Your itinerary is ready!'.",
    llm_config=llm_config,
    functions=[update_itinerary_with_travel_times],
)

# Our customer will be a human in the loop
customer = UserProxyAgent(name="customer")

Hand offs and After works

In conjunction with the agent’s associated functions, we establish rules that govern the swarm orchestration through hand offs and After works.

For more details on the swarm orchestration, see the documentation.

planner_agent.register_hand_off(
    hand_to=[
        ON_CONDITION(
            graphrag_agent,
            "Need information on the restaurants and attractions for a location. DO NOT call more than once at a time.",
        ),  # Get info from FalkorDB GraphRAG
        ON_CONDITION(structured_output_agent, "Itinerary is confirmed by the customer"),
        AFTER_WORK(AfterWorkOption.REVERT_TO_USER),  # Revert to the customer for more information on their plans
    ]
)


# Back to the Planner when information has been retrieved
graphrag_agent.register_hand_off(hand_to=[AFTER_WORK(planner_agent)])

# Once we have formatted our itinerary, we can hand off to the route timing agent to add in the travel timings
structured_output_agent.register_hand_off(hand_to=[AFTER_WORK(route_timing_agent)])

# Finally, once the route timing agent has finished, we can terminate the swarm
route_timing_agent.register_hand_off(
    hand_to=[AFTER_WORK(AfterWorkOption.TERMINATE)]  # Once this agent has finished, the swarm can terminate
)

Run the swarm

Let’s get an itinerary for a couple of days in Rome.

# Start the conversation

chat_result, context_variables, last_agent = initiate_swarm_chat(
    initial_agent=planner_agent,
    agents=[planner_agent, graphrag_agent, structured_output_agent, route_timing_agent],
    user_agent=customer,
    context_variables=trip_context,
    messages="I want to go to Rome for a couple of days. Can you help me plan my trip?",
    after_work=AfterWorkOption.TERMINATE,
    max_rounds=100,
)
customer (to chat_manager):

I want to go to Rome for a couple of days. Can you help me plan my trip?

--------------------------------------------------------------------------------

Next speaker: planner_agent

planner_agent (to chat_manager):

Sure! How many days will you be staying in Rome? Additionally, what type of activities are you interested in doing, and do you have any specific preferences for meals (e.g., certain type of cuisine or dining experience)?

--------------------------------------------------------------------------------

Next speaker: customer

customer (to chat_manager):

2 days please, I want lunch and dinner, with an attraction to see in the morning and after lunch.

--------------------------------------------------------------------------------

Next speaker: planner_agent

planner_agent (to chat_manager):

***** Suggested tool call (call_vQMpso8aOomdfq8S2uCRlnzj): transfer_to_graphrag_agent *****
Arguments: 
{}
*******************************************************************************************

--------------------------------------------------------------------------------

Next speaker: Tool_Execution


>>>>>>>> EXECUTING FUNCTION transfer_to_graphrag_agent...
Tool_Execution (to chat_manager):

***** Response from calling tool (call_vQMpso8aOomdfq8S2uCRlnzj) *****
SwarmAgent --> graphrag_agent
**********************************************************************

--------------------------------------------------------------------------------

Next speaker: graphrag_agent

graphrag_agent (to chat_manager):

Restaurants in Rome:
1. Trattoria da Enzo - A cozy trattoria known for its traditional Roman dishes and welcoming atmosphere (Rating: 4.5).
2. Il Pagliaccio - An elegant Michelin-starred restaurant offering contemporary Italian cuisine (Rating: 4.8).
3. Tonnarello - Casual stop for pasta, meatballs & other simple Roman dishes, plus patio seating & acoustic guitar (Rating: 4.7).
4. Osteria delle Commari - Local home style Roman restaurant near Vatican Museums (Rating: 4.3).
5. Armando al Pantheon - Traditional Roman cuisine since 1961 near the Pantheon (Rating: 4.8).
6. Salumeria Roscioli Ristorante - Known for pasta alla gricia, amatriciana, and an impressive wine cellar (Rating: 4.5).
7. L'Arcangelo - Known for some of the best supplì in the city and Thursday gnocchi (Rating: 4.8).

Attractions in Rome:
1. Colosseum - An ancient amphitheater known for gladiatorial contests and public spectacles.
2. Vatican Museums - A complex of museums and galleries showcasing works of art collected by Popes over centuries.
3. Trevi Fountain - A Baroque fountain known for its stunning sculptures and tradition of tossing coins.
4. Basilica di Santa Maria Maggiore - A major basilica known for its rich history and impressive architecture.

--------------------------------------------------------------------------------

Next speaker: planner_agent

planner_agent (to chat_manager):

Great! Here's a suggested itinerary for your 2-day trip to Rome:

### Day 1:

- **Morning Attraction:**
  - **Type:** Attraction
  - **Location:** Colosseum
  - **City:** Rome
  - **Description:** Explore the ancient amphitheater known for gladiatorial contests and public spectacles.

- **Lunch:**
  - **Type:** Restaurant
  - **Location:** Trattoria da Enzo
  - **City:** Rome
  - **Description:** Enjoy traditional Roman dishes in a cozy and welcoming atmosphere.

- **Afternoon Attraction:**
  - **Type:** Attraction
  - **Location:** Vatican Museums
  - **City:** Rome
  - **Description:** Visit a complex of museums and galleries showcasing works of art collected by Popes over centuries.

- **Dinner:**
  - **Type:** Restaurant
  - **Location:** Armando al Pantheon
  - **City:** Rome
  - **Description:** Experience traditional Roman cuisine since 1961, located near the Pantheon.


### Day 2:

- **Morning Attraction:**
  - **Type:** Attraction
  - **Location:** Trevi Fountain
  - **City:** Rome
  - **Description:** Marvel at the stunning sculptures of the Baroque fountain and join the tradition of tossing coins.

- **Lunch:**
  - **Type:** Restaurant
  - **Location:** Tonnarello
  - **City:** Rome
  - **Description:** Relish in simple Roman dishes such as pasta and meatballs in a casual setting with patio seating.

- **Afternoon Attraction:**
  - **Type:** Attraction
  - **Location:** Basilica di Santa Maria Maggiore
  - **City:** Rome
  - **Description:** Visit a major basilica known for its rich history and impressive architecture.

- **Dinner:**
  - **Type:** Restaurant
  - **Location:** L'Arcangelo
  - **City:** Rome
  - **Description:** Savor some of the best supplì in the city and enjoy traditional gnocchi on Thursdays.

Let me know if you are happy with this itinerary or if there are any changes you would like to make!

--------------------------------------------------------------------------------

Next speaker: customer

customer (to chat_manager):

I don't feel like eating at L'Arcangelo, please change that for another option.

--------------------------------------------------------------------------------

Next speaker: planner_agent

planner_agent (to chat_manager):

Certainly! Here's the updated itinerary for Day 2:

### Day 2:

- **Morning Attraction:**
  - **Type:** Attraction
  - **Location:** Trevi Fountain
  - **City:** Rome
  - **Description:** Marvel at the stunning sculptures of the Baroque fountain and join the tradition of tossing coins.

- **Lunch:**
  - **Type:** Restaurant
  - **Location:** Tonnarello
  - **City:** Rome
  - **Description:** Relish in simple Roman dishes such as pasta and meatballs in a casual setting with patio seating.

- **Afternoon Attraction:**
  - **Type:** Attraction
  - **Location:** Basilica di Santa Maria Maggiore
  - **City:** Rome
  - **Description:** Visit a major basilica known for its rich history and impressive architecture.

- **Dinner:**
  - **Type:** Restaurant
  - **Location:** Il Pagliaccio
  - **City:** Rome
  - **Description:** Indulge in elegant, contemporary Italian cuisine at a Michelin-starred restaurant.

Let me know if this itinerary meets your preferences!

--------------------------------------------------------------------------------

Next speaker: customer

customer (to chat_manager):

Can you research and show me all the available restaurant options.

--------------------------------------------------------------------------------

Next speaker: planner_agent

planner_agent (to chat_manager):

Here are the available restaurant options for your trip to Rome:

1. **Trattoria da Enzo**
   - **Description:** A cozy trattoria known for its traditional Roman dishes and welcoming atmosphere.
   - **Rating:** 4.5

2. **Il Pagliaccio**
   - **Description:** An elegant Michelin-starred restaurant offering contemporary Italian cuisine.
   - **Rating:** 4.8

3. **Tonnarello**
   - **Description:** Casual stop for pasta, meatballs & other simple Roman dishes, plus patio seating & acoustic guitar.
   - **Rating:** 4.7

4. **Osteria delle Commari**
   - **Description:** Local home style Roman restaurant near Vatican Museums.
   - **Rating:** 4.3

5. **Armando al Pantheon**
   - **Description:** Traditional Roman cuisine since 1961 near the Pantheon.
   - **Rating:** 4.8

6. **Salumeria Roscioli Ristorante**
   - **Description:** Known for pasta alla gricia, amatriciana, and an impressive wine cellar.
   - **Rating:** 4.5

7. **L'Arcangelo**
   - **Description:** Known for some of the best supplì in the city and Thursday gnocchi.
   - **Rating:** 4.8

Please let me know which restaurants you'd like to include in your itinerary or if there are any changes you'd like to make!

--------------------------------------------------------------------------------

Next speaker: customer

customer (to chat_manager):

Tonnarello's please.

--------------------------------------------------------------------------------

Next speaker: planner_agent

planner_agent (to chat_manager):

***** Suggested tool call (call_sROJ1YfU63SDCRfUbRuGqjVa): mark_itinerary_as_complete *****
Arguments: 
{"final_itinerary": "### Day 1:\n\n- **Morning Attraction:**\n  - **Type:** Attraction\n  - **Location:** Colosseum\n  - **City:** Rome\n  - **Description:** Explore the ancient amphitheater known for gladiatorial contests and public spectacles.\n\n- **Lunch:**\n  - **Type:** Restaurant\n  - **Location:** Trattoria da Enzo\n  - **City:** Rome\n  - **Description:** Enjoy traditional Roman dishes in a cozy and welcoming atmosphere.\n\n- **Afternoon Attraction:**\n  - **Type:** Attraction\n  - **Location:** Vatican Museums\n  - **City:** Rome\n  - **Description:** Visit a complex of museums and galleries showcasing works of art collected by Popes over centuries.\n\n- **Dinner:**\n  - **Type:** Restaurant\n  - **Location:** Armando al Pantheon\n  - **City:** Rome\n  - **Description:** Experience traditional Roman cuisine since 1961, located near the Pantheon.\n\n\n### Day 2:\n\n- **Morning Attraction:**\n  - **Type:** Attraction\n  - **Location:** Trevi Fountain\n  - **City:** Rome\n  - **Description:** Marvel at the stunning sculptures of the Baroque fountain and join the tradition of tossing coins.\n\n- **Lunch:**\n  - **Type:** Restaurant\n  - **Location:** Tonnarello\n  - **City:** Rome\n  - **Description:** Relish in simple Roman dishes such as pasta and meatballs in a casual setting with patio seating.\n\n- **Afternoon Attraction:**\n  - **Type:** Attraction\n  - **Location:** Basilica di Santa Maria Maggiore\n  - **City:** Rome\n  - **Description:** Visit a major basilica known for its rich history and impressive architecture.\n\n- **Dinner:**\n  - **Type:** Restaurant\n  - **Location:** Tonnarello\n  - **City:** Rome\n  - **Description:** Return to Tonnarello for a delightful dining experience with a variety of Roman specialties."}
*******************************************************************************************
***** Suggested tool call (call_NBw71N4pS66h8VLlgu5nvveN): transfer_to_structured_output_agent *****
Arguments: 
{}
****************************************************************************************************

--------------------------------------------------------------------------------

Next speaker: Tool_Execution


>>>>>>>> EXECUTING FUNCTION mark_itinerary_as_complete...

>>>>>>>> EXECUTING FUNCTION transfer_to_structured_output_agent...
Tool_Execution (to chat_manager):

***** Response from calling tool (call_sROJ1YfU63SDCRfUbRuGqjVa) *****
Itinerary recorded and confirmed.
**********************************************************************

--------------------------------------------------------------------------------
***** Response from calling tool (call_NBw71N4pS66h8VLlgu5nvveN) *****
SwarmAgent --> structured_output_agent
**********************************************************************

--------------------------------------------------------------------------------

Next speaker: structured_output_agent

structured_output_agent (to chat_manager):

{"days":[{"events":[{"type":"Attraction","location":"Colosseum","city":"Rome","description":"Explore the ancient amphitheater known for gladiatorial contests and public spectacles."},{"type":"Restaurant","location":"Trattoria da Enzo","city":"Rome","description":"Enjoy traditional Roman dishes in a cozy and welcoming atmosphere."},{"type":"Attraction","location":"Vatican Museums","city":"Rome","description":"Visit a complex of museums and galleries showcasing works of art collected by Popes over centuries."},{"type":"Restaurant","location":"Armando al Pantheon","city":"Rome","description":"Experience traditional Roman cuisine since 1961, located near the Pantheon."}]},{"events":[{"type":"Attraction","location":"Trevi Fountain","city":"Rome","description":"Marvel at the stunning sculptures of the Baroque fountain and join the tradition of tossing coins."},{"type":"Restaurant","location":"Tonnarello","city":"Rome","description":"Relish in simple Roman dishes such as pasta and meatballs in a casual setting with patio seating."},{"type":"Attraction","location":"Basilica di Santa Maria Maggiore","city":"Rome","description":"Visit a major basilica known for its rich history and impressive architecture."},{"type":"Restaurant","location":"Tonnarello","city":"Rome","description":"Return to Tonnarello for a delightful dining experience with a variety of Roman specialties."}]}]}

--------------------------------------------------------------------------------

Next speaker: route_timing_agent

route_timing_agent (to chat_manager):

None
***** Suggested tool call (call_6nnUKnD3VveszMT8OvkqUTCC): update_itinerary_with_travel_times *****
Arguments: 
{}
***************************************************************************************************

--------------------------------------------------------------------------------

Next speaker: Tool_Execution


>>>>>>>> EXECUTING FUNCTION update_itinerary_with_travel_times...
Tool_Execution (to chat_manager):

***** Response from calling tool (call_6nnUKnD3VveszMT8OvkqUTCC) *****
Structured itinerary not found, please create the structured output, structured_output_agent.
**********************************************************************

--------------------------------------------------------------------------------

Next speaker: structured_output_agent

structured_output_agent (to chat_manager):

***** Suggested tool call (call_uTWKi1KdcsaFK9RxAeFUIWbU): create_structured_itinerary *****
Arguments: 
{"structured_itinerary":"{\"days\":[{\"events\":[{\"type\":\"Attraction\",\"location\":\"Colosseum\",\"city\":\"Rome\",\"description\":\"Explore the ancient amphitheater known for gladiatorial contests and public spectacles.\"},{\"type\":\"Restaurant\",\"location\":\"Trattoria da Enzo\",\"city\":\"Rome\",\"description\":\"Enjoy traditional Roman dishes in a cozy and welcoming atmosphere.\"},{\"type\":\"Attraction\",\"location\":\"Vatican Museums\",\"city\":\"Rome\",\"description\":\"Visit a complex of museums and galleries showcasing works of art collected by Popes over centuries.\"},{\"type\":\"Restaurant\",\"location\":\"Armando al Pantheon\",\"city\":\"Rome\",\"description\":\"Experience traditional Roman cuisine since 1961, located near the Pantheon.\"}]},{\"events\":[{\"type\":\"Attraction\",\"location\":\"Trevi Fountain\",\"city\":\"Rome\",\"description\":\"Marvel at the stunning sculptures of the Baroque fountain and join the tradition of tossing coins.\"},{\"type\":\"Restaurant\",\"location\":\"Tonnarello\",\"city\":\"Rome\",\"description\":\"Relish in simple Roman dishes such as pasta and meatballs in a casual setting with patio seating.\"},{\"type\":\"Attraction\",\"location\":\"Basilica di Santa Maria Maggiore\",\"city\":\"Rome\",\"description\":\"Visit a major basilica known for its rich history and impressive architecture.\"},{\"type\":\"Restaurant\",\"location\":\"Tonnarello\",\"city\":\"Rome\",\"description\":\"Return to Tonnarello for a delightful dining experience with a variety of Roman specialties.\"}]}]}"}
********************************************************************************************

--------------------------------------------------------------------------------

Next speaker: Tool_Execution


>>>>>>>> EXECUTING FUNCTION create_structured_itinerary...
Tool_Execution (to chat_manager):

***** Response from calling tool (call_uTWKi1KdcsaFK9RxAeFUIWbU) *****
Structured itinerary stored.
**********************************************************************

--------------------------------------------------------------------------------

Next speaker: route_timing_agent

route_timing_agent (to chat_manager):

***** Suggested tool call (call_7alJoHYs74wsPIz4WhMrj7Zu): update_itinerary_with_travel_times *****
Arguments: 
{}
***************************************************************************************************

--------------------------------------------------------------------------------

Next speaker: Tool_Execution


>>>>>>>> EXECUTING FUNCTION update_itinerary_with_travel_times...
Tool_Execution (to chat_manager):

***** Response from calling tool (call_7alJoHYs74wsPIz4WhMrj7Zu) *****
Timed itinerary added to context with travel times
**********************************************************************

--------------------------------------------------------------------------------

Next speaker: route_timing_agent

route_timing_agent (to chat_manager):

Your itinerary is ready!

--------------------------------------------------------------------------------

Bonus itinerary output

def print_itinerary(itinerary_data):
    header = "█             █\n █           █ \n  █  █████  █  \n   ██     ██   \n  █         █  \n █  ███████  █ \n █ ██ ███ ██ █ \n   █████████   \n\n ██   ███ ███  \n█  █ █       █ \n████ █ ██  ██  \n█  █ █  █ █    \n█  █  ██  ████ \n"
    width = 80
    icons = {"Travel": "🚶", "Restaurant": "🍽️", "Attraction": "🏛️"}

    for line in header.split("\n"):
        print(line.center(width))
    print(f"Itinerary for {itinerary_data['days'][0]['events'][0]['city']}".center(width))
    print("=" * width)

    for day_num, day in enumerate(itinerary_data["days"], 1):
        print(f"\nDay {day_num}".center(width))
        print("-" * width)

        for event in day["events"]:
            event_type = event["type"]
            print(f"\n  {icons[event_type]} {event['location']}")
            if event_type != "Travel":
                words = event["description"].split()
                line = "    "
                for word in words:
                    if len(line) + len(word) + 1 <= 76:
                        line += word + " "
                    else:
                        print(line)
                        line = "    " + word + " "
                if line.strip():
                    print(line)
            else:
                print(f"    {event['description']}")
        print("\n" + "-" * width)


if "timed_itinerary" in context_variables:
    print_itinerary(context_variables["timed_itinerary"])
else:
    print("No itinerary available to print.")
                                █             █                                 
                                 █           █                                  
                                  █  █████  █                                   
                                   ██     ██                                    
                                  █         █                                   
                                 █  ███████  █                                  
                                 █ ██ ███ ██ █                                  
                                   █████████                                    
                                                                                
                                 ██   ███ ███                                   
                                █  █ █       █                                  
                                ████ █ ██  ██                                   
                                █  █ █  █ █                                     
                                █  █  ██  ████                                  
                                                                                
                               Itinerary for Rome                               
================================================================================
                                     
Day 1                                     
--------------------------------------------------------------------------------

  🏛️ Colosseum
    Explore the ancient amphitheater known for gladiatorial contests and 
    public spectacles. 

  🚶 walking from Colosseum to Trattoria da Enzo
    24 mins, (1.8 km)

  🍽️ Trattoria da Enzo
    Enjoy traditional Roman dishes in a cozy and welcoming atmosphere. 

  🚶 walking from Trattoria da Enzo to Vatican Museums
    50 mins, (3.5 km)

  🏛️ Vatican Museums
    Visit a complex of museums and galleries showcasing works of art 
    collected by Popes over centuries. 

  🚶 walking from Vatican Museums to Armando al Pantheon
    36 mins, (2.6 km)

  🍽️ Armando al Pantheon
    Experience traditional Roman cuisine since 1961, located near the 
    Pantheon. 

--------------------------------------------------------------------------------
                                     
Day 2                                     
--------------------------------------------------------------------------------

  🏛️ Trevi Fountain
    Marvel at the stunning sculptures of the Baroque fountain and join the 
    tradition of tossing coins. 

  🚶 walking from Trevi Fountain to Tonnarello
    31 mins, (2.2 km)

  🍽️ Tonnarello
    Relish in simple Roman dishes such as pasta and meatballs in a casual 
    setting with patio seating. 

  🚶 walking from Tonnarello to Basilica di Santa Maria Maggiore
    46 mins, (3.2 km)

  🏛️ Basilica di Santa Maria Maggiore
    Visit a major basilica known for its rich history and impressive 
    architecture. 

  🚶 walking from Basilica di Santa Maria Maggiore to Tonnarello
    44 mins, (3.2 km)

  🍽️ Tonnarello
    Return to Tonnarello for a delightful dining experience with a variety 
    of Roman specialties. 

--------------------------------------------------------------------------------