Now that we've explored how an agent might select tools and the logic behind sequencing them, it's time to put these ideas into practice. In this hands-on exercise, you'll build a simplified agent that orchestrates multiple tools to fulfill a user request. We'll focus on a common scenario: planning a simple outing. This will involve fetching information, making a suggestion based on that information, and then finding a related amenity.
Our goal is to illustrate how an agent can execute a sequence of tool calls, where the output of one tool often becomes the input for the next. This demonstrates the core concepts of multi-step execution and dependency management discussed earlier in this chapter.
Imagine a user wants help planning a local activity. They might ask something like, "What's a good activity for this Saturday in San Francisco, and where can I eat nearby?" To answer this, our agent will need to:
This clearly requires a sequence of operations, making it a good candidate for multi-tool orchestration.
For this exercise, we'll define three Python functions, each representing a distinct tool. In a real-world application, these tools might interact with external APIs (as covered in Chapter 4), but here we'll use simplified implementations to keep the focus on orchestration.
get_weather_forecast
: Takes a location and date, and returns a dictionary with the weather condition and temperature.suggest_activity
: Takes a weather condition and suggests an activity.find_restaurant
: Takes a city and an activity descriptor (like "near the park"), and returns a restaurant suggestion.Here are the Python implementations for these tools. Notice their input parameters and the structure of their return values. Clear input/output schemas are important for reliable tool use, as discussed in Chapter 1.
# tool_definitions.py
def get_weather_forecast(location: str, date: str) -> dict:
"""
Simulates fetching a weather forecast.
In a real tool, this would call a weather API.
"""
print(f"TOOL CALLED: get_weather_forecast(location='{location}', date='{date}')")
if location.lower() == "san francisco":
if "saturday" in date.lower() or "sunday" in date.lower(): # Simulate weekend weather
return {"city": location, "condition": "Sunny", "temperature_celsius": 22}
else:
return {"city": location, "condition": "Foggy", "temperature_celsius": 18}
elif location.lower() == "london":
return {"city": location, "condition": "Cloudy", "temperature_celsius": 15}
else:
return {"city": location, "condition": "Partly Cloudy", "temperature_celsius": 20}
def suggest_activity(weather_condition: str) -> dict:
"""
Suggests an activity based on the weather condition.
"""
print(f"TOOL CALLED: suggest_activity(weather_condition='{weather_condition}')")
if weather_condition == "Sunny":
return {"activity_type": "Outdoor", "suggestion": "Go for a hike in a nearby park"}
elif weather_condition == "Cloudy" or weather_condition == "Partly Cloudy":
return {"activity_type": "Indoor/Outdoor", "suggestion": "Visit a local museum or a covered market"}
elif weather_condition == "Foggy":
return {"activity_type": "Indoor", "suggestion": "Explore an art gallery or enjoy a cozy cafe"}
else: # Default for other conditions
return {"activity_type": "Flexible", "suggestion": "Check out local events"}
def find_restaurant(city: str, activity_type: str, preference: str = "near activity") -> dict:
"""
Simulates finding a restaurant based on city, activity type, and preference.
'preference' could be used to find something 'near activity' or a specific cuisine.
"""
print(f"TOOL CALLED: find_restaurant(city='{city}', activity_type='{activity_type}', preference='{preference}')")
if city.lower() == "san francisco":
if activity_type == "Outdoor":
return {"name": "The Green Eatery", "cuisine": "Californian", "vicinity": "near Golden Gate Park"}
elif activity_type == "Indoor":
return {"name": "City Lights Cafe", "cuisine": "Italian", "vicinity": "Downtown"}
else:
return {"name": "The Bay Diner", "cuisine": "American", "vicinity": "Fisherman's Wharf"}
elif city.lower() == "london":
return {"name": "The Thames Bistro", "cuisine": "British", "vicinity": "near South Bank"}
else:
return {"name": "Local Gem Restaurant", "cuisine": "Varied", "vicinity": "city center"}
With our tools defined, we can now outline the agent's logic. The agent will receive an initial query, then call the tools in a specific sequence, using the output from one tool as input for the next.
Here's a visual representation of the planned tool execution flow:
This diagram illustrates the flow of information. The user's request initiates the process. The output of
get_weather_forecast
(e.g., "Sunny") is used bysuggest_activity
. Bothget_weather_forecast
(for the city) andsuggest_activity
(for the type of activity) provide inputs tofind_restaurant
. Finally, the agent combines all gathered information to create a response.
Now, let's implement this orchestration logic in a Python function that simulates our agent.
# agent_orchestrator.py
from tool_definitions import get_weather_forecast, suggest_activity, find_restaurant
def trip_planner_agent(query_location: str, query_date: str):
"""
Orchestrates tool calls to plan a trip based on user query.
"""
print(f"\nAGENT: Received request to plan for {query_location} on {query_date}.")
# Step 1: Get the weather forecast
print("AGENT: Step 1 - Fetching weather forecast...")
weather_info = get_weather_forecast(location=query_location, date=query_date)
if not weather_info:
print("AGENT: Could not retrieve weather information. Aborting plan.")
return "I'm sorry, I couldn't get the weather information for your request."
print(f"AGENT: Weather in {weather_info.get('city', 'N/A')} is {weather_info.get('condition', 'N/A')}, {weather_info.get('temperature_celsius', 'N/A')}°C.")
# Step 2: Suggest an activity based on the weather
# This step depends on the output of get_weather_forecast
print("\nAGENT: Step 2 - Suggesting an activity...")
activity_info = suggest_activity(weather_condition=weather_info.get('condition'))
if not activity_info:
print("AGENT: Could not suggest an activity. Aborting plan.")
return "I'm sorry, I couldn't come up with an activity suggestion based on the weather."
print(f"AGENT: Suggested activity type: {activity_info.get('activity_type', 'N/A')}, Suggestion: {activity_info.get('suggestion', 'N/A')}.")
# Step 3: Find a restaurant
# This step depends on the city (from weather_info) and activity_type (from activity_info)
print("\nAGENT: Step 3 - Finding a restaurant...")
restaurant_info = find_restaurant(city=weather_info.get('city'), activity_type=activity_info.get('activity_type'))
if not restaurant_info:
print("AGENT: Could not find a restaurant. Proceeding with partial plan.")
# We could decide to return a partial plan or fail. Here, we continue.
restaurant_suggestion_text = "I couldn't find a specific restaurant suggestion at this time."
else:
restaurant_suggestion_text = (
f"For a meal, you could try {restaurant_info.get('name', 'a local spot')} "
f"({restaurant_info.get('cuisine', 'various cuisines')}) "
f"located {restaurant_info.get('vicinity', 'nearby')}."
)
print(f"AGENT: Restaurant found: {restaurant_info.get('name', 'N/A')}.")
# Step 4: Synthesize the final response for the user
print("\nAGENT: Step 4 - Synthesizing the final plan...")
final_plan = (
f"Okay, here's a plan for your {query_date} in {query_location}:\n"
f"- The weather is expected to be {weather_info.get('condition')} with a temperature of around {weather_info.get('temperature_celsius')}°C.\n"
f"- Based on that, I suggest you: {activity_info.get('suggestion')}.\n"
f"- {restaurant_suggestion_text}"
)
print("\nAGENT: Final plan generated:")
print(final_plan)
return final_plan
# Let's run our agent
if __name__ == "__main__":
user_location = "San Francisco"
user_date = "Saturday"
plan = trip_planner_agent(query_location=user_location, query_date=user_date)
print("\n--- Example with another location ---")
user_location_2 = "London"
user_date_2 = "next Tuesday"
plan_2 = trip_planner_agent(query_location=user_location_2, query_date=user_date_2)
When you run agent_orchestrator.py
, you'll see output logs showing each tool being called and the agent's internal reasoning at each step:
AGENT: Received request to plan for San Francisco on Saturday.
AGENT: Step 1 - Fetching weather forecast...
TOOL CALLED: get_weather_forecast(location='San Francisco', date='Saturday')
AGENT: Weather in San Francisco is Sunny, 22°C.
AGENT: Step 2 - Suggesting an activity...
TOOL CALLED: suggest_activity(weather_condition='Sunny')
AGENT: Suggested activity type: Outdoor, Suggestion: Go for a hike in a nearby park.
AGENT: Step 3 - Finding a restaurant...
TOOL CALLED: find_restaurant(city='San Francisco', activity_type='Outdoor', preference='near activity')
AGENT: Restaurant found: The Green Eatery.
AGENT: Step 4 - Synthesizing the final plan...
AGENT: Final plan generated:
Okay, here's a plan for your Saturday in San Francisco:
- The weather is expected to be Sunny with a temperature of around 22°C.
- Based on that, I suggest you: Go for a hike in a nearby park.
- For a meal, you could try The Green Eatery (Californian) located near Golden Gate Park.
--- Example with another location ---
AGENT: Received request to plan for London on next Tuesday.
AGENT: Step 1 - Fetching weather forecast...
TOOL CALLED: get_weather_forecast(location='London', date='next Tuesday')
AGENT: Weather in London is Cloudy, 15°C.
AGENT: Step 2 - Suggesting an activity...
TOOL CALLED: suggest_activity(weather_condition='Cloudy')
AGENT: Suggested activity type: Indoor/Outdoor, Suggestion: Visit a local museum or a covered market.
AGENT: Step 3 - Finding a restaurant...
TOOL CALLED: find_restaurant(city='London', activity_type='Indoor/Outdoor', preference='near activity')
AGENT: Restaurant found: The Thames Bistro.
AGENT: Step 4 - Synthesizing the final plan...
AGENT: Final plan generated:
Okay, here's a plan for your next Tuesday in London:
- The weather is expected to be Cloudy with a temperature of around 15°C.
- Based on that, I suggest you: Visit a local museum or a covered market.
- For a meal, you could try The Thames Bistro (British) located near South Bank.
This hands-on exercise demonstrates several key aspects of tool orchestration:
suggest_activity
tool required the condition
from get_weather_forecast
. The find_restaurant
tool used the city
from get_weather_forecast
and the activity_type
from suggest_activity
. This chaining of inputs and outputs is fundamental to complex tool use.trip_planner_agent
function. In more advanced agents, an LLM would dynamically decide which tool to call next based on the conversation history, the current goal, and the descriptions of available tools (as discussed in "Agent-Driven Tool Selection Mechanisms"). Our explicit sequence serves to clearly illustrate the orchestration flow.weather_info.get('condition')
) to use as inputs for subsequent tools or for constructing the final response.While this example is simplified, it forms the basis for more complex orchestrations. Here are a few points to consider for more advanced scenarios:
This hands-on exercise has given you a practical look at how an agent can coordinate multiple tools to achieve a complex goal. By defining clear tools and orchestrating their execution, you can significantly expand the capabilities of your LLM agents. As you build more sophisticated agents, the principles of sequential execution, dependency management, and (eventually) dynamic tool selection will be central to their design. Try modifying the existing tools, or adding a new one (e.g., a get_event_listings
tool), and think about how you would integrate it into the agent's orchestration logic.
Was this section helpful?
© 2025 ApX Machine Learning