Orchestration
Building multi-agent systems allow you to route requests across multiple specialized agents, each designed to handle specific types of tasks. Plano makes it easy to build and scale these systems by managing the orchestration layer—deciding which agent(s) should handle each request—while you focus on implementing individual agent logic.
This guide shows you how to configure and implement multi-agent orchestration in Plano using a real-world example: a Travel Booking Assistant that routes queries to specialized agents for weather and flights.
How It Works
Plano’s orchestration layer analyzes incoming prompts and routes them to the most appropriate agent based on user intent and conversation context. The workflow is:
User submits a prompt: The request arrives at Plano’s agent listener.
Agent selection: Plano uses an LLM to analyze the prompt and determine user intent and complexity. By default, this uses Plano-Orchestrator-30B-A3B, which offers performance of foundation models at 1/10th the cost. The LLM routes the request to the most suitable agent configured in your system—such as a weather agent or flight agent.
Agent handles request: Once the selected agent receives the request object from Plano, it manages its own inner loop until the task is complete. This means the agent autonomously calls models, invokes tools, processes data, and reasons about next steps—all within its specialized domain—before returning the final response.
Seamless handoffs: For multi-turn conversations, Plano repeats the intent analysis for each follow-up query, enabling smooth handoffs between agents as the conversation evolves.
Example: Travel Booking Assistant
Let’s walk through a complete multi-agent system: a Travel Booking Assistant that helps users plan trips by providing weather forecasts and flight information. This system uses two specialized agents:
Weather Agent: Provides real-time weather conditions and multi-day forecasts
Flight Agent: Searches for flights between airports with real-time tracking
Configuration
Configure your agents in the listeners section of your plano_config.yaml:
1version: v0.3.0
2
3agents:
4 - id: weather_agent
5 url: http://host.docker.internal:10510
6 - id: flight_agent
7 url: http://host.docker.internal:10520
8
9model_providers:
10 - model: openai/gpt-4o
11 access_key: $OPENAI_API_KEY
12 default: true
13 - model: openai/gpt-4o-mini
14 access_key: $OPENAI_API_KEY # smaller, faster, cheaper model for extracting entities like location
15
16listeners:
17 - type: agent
18 name: travel_booking_service
19 port: 8001
20 router: plano_orchestrator_v1
21 agents:
22 - id: weather_agent
23 description: |
24
25 WeatherAgent is a specialized AI assistant for real-time weather information and forecasts. It provides accurate weather data for any city worldwide using the Open-Meteo API, helping travelers plan their trips with up-to-date weather conditions.
26
27 Capabilities:
28 * Get real-time weather conditions and multi-day forecasts for any city worldwide using Open-Meteo API (free, no API key needed)
29 * Provides current temperature
30 * Provides multi-day forecasts
31 * Provides weather conditions
32 * Provides sunrise/sunset times
33 * Provides detailed weather information
34 * Understands conversation context to resolve location references from previous messages
35 * Handles weather-related questions including "What's the weather in [city]?", "What's the forecast for [city]?", "How's the weather in [city]?"
36 * When queries include both weather and other travel questions (e.g., flights, currency), this agent answers ONLY the weather part
37
38 - id: flight_agent
39 description: |
40
41 FlightAgent is an AI-powered tool specialized in providing live flight information between airports. It leverages the FlightAware AeroAPI to deliver real-time flight status, gate information, and delay updates.
42
43 Capabilities:
44 * Get live flight information between airports using FlightAware AeroAPI
45 * Shows real-time flight status
46 * Shows scheduled/estimated/actual departure and arrival times
47 * Shows gate and terminal information
48 * Shows delays
49 * Shows aircraft type
50 * Shows flight status
51 * Automatically resolves city names to airport codes (IATA/ICAO)
52 * Understands conversation context to infer origin/destination from follow-up questions
53 * Handles flight-related questions including "What flights go from [city] to [city]?", "Do flights go to [city]?", "Are there direct flights from [city]?"
54 * When queries include both flight and other travel questions (e.g., weather, currency), this agent answers ONLY the flight part
55
56tracing:
57 random_sampling: 100
Key Configuration Elements:
agent listener: A listener of
type: agenttells Plano to perform intent analysis and routing for incoming requests.agents list: Define each agent with an
id,description(used for routing decisions)router: The
plano_orchestrator_v1router uses Plano-Orchestrator to analyze user intent and select the appropriate agent.filter_chain: Optionally attach filter chains to agents for guardrails, query rewriting, or context enrichment.
Writing Effective Agent Descriptions
Agent descriptions are critical—they’re used by Plano-Orchestrator to make routing decisions. Effective descriptions should include:
Clear introduction: A concise statement explaining what the agent is and its primary purpose
Capabilities section: A bulleted list of specific capabilities, including:
What APIs or data sources it uses (e.g., “Open-Meteo API”, “FlightAware AeroAPI”)
What information it provides (e.g., “current temperature”, “multi-day forecasts”, “gate information”)
How it handles context (e.g., “Understands conversation context to resolve location references”)
What question patterns it handles (e.g., “What’s the weather in [city]?”)
How it handles multi-part queries (e.g., “When queries include both weather and flights, this agent answers ONLY the weather part”)
Here’s an example of a well-structured agent description:
- id: weather_agent
description: |
WeatherAgent is a specialized AI assistant for real-time weather information
and forecasts. It provides accurate weather data for any city worldwide using
the Open-Meteo API, helping travelers plan their trips with up-to-date weather
conditions.
Capabilities:
* Get real-time weather conditions and multi-day forecasts for any city worldwide
* Provides current temperature, weather conditions, sunrise/sunset times
* Provides detailed weather information including multi-day forecasts
* Understands conversation context to resolve location references from previous messages
* Handles weather-related questions including "What's the weather in [city]?"
* When queries include both weather and other travel questions (e.g., flights),
this agent answers ONLY the weather part
Note
We will soon support “Agents as Tools” via Model Context Protocol (MCP), enabling agents to dynamically discover and invoke other agents as tools. Track progress on GitHub Issue #646.
Implementation
Agents are HTTP services that receive routed requests from Plano. Each agent implements the OpenAI Chat Completions API format, making them compatible with standard LLM clients.
Agent Structure
Let’s examine the Weather Agent implementation:
1@app.post("/v1/chat/completions")
2async def handle_request(request: Request):
3 """HTTP endpoint for chat completions with streaming support."""
4
5 request_body = await request.json()
6 messages = request_body.get("messages", [])
7 logger.info(
8 "messages detail json dumps: %s",
9 json.dumps(messages, indent=2),
10 )
11
12 traceparent_header = request.headers.get("traceparent")
13 return StreamingResponse(
14 invoke_weather_agent(request, request_body, traceparent_header),
15 media_type="text/plain",
16 headers={
17 "content-type": "text/event-stream",
18 },
19 )
20
21
22async def invoke_weather_agent(
Key Points:
Agents expose a
/v1/chat/completionsendpoint that matches OpenAI’s API formatThey use Plano’s LLM gateway (via
LLM_GATEWAY_ENDPOINT) for all LLM callsThey receive the full conversation history in
request_body.messages
Information Extraction with LLMs
Agents use LLMs to extract structured information from natural language queries. This enables them to understand user intent and extract parameters needed for API calls.
The Weather Agent extracts location information:
1
2 instructions = """Extract the location for WEATHER queries. Return just the city name.
3
4 Rules:
5 1. For multi-part queries, extract ONLY the location mentioned with weather keywords ("weather in [location]")
6 2. If user says "there" or "that city", it typically refers to the DESTINATION city in travel contexts (not the origin)
7 3. For flight queries with weather, "there" means the destination city where they're traveling TO
8 4. Return plain text (e.g., "London", "New York", "Paris, France")
9 5. If no weather location found, return "NOT_FOUND"
10
11 Examples:
12 - "What's the weather in London?" -> "London"
13 - "Flights from Seattle to Atlanta, and show me the weather there" -> "Atlanta"
14 - "Can you get me flights from Seattle to Atlanta tomorrow, and also please show me the weather there" -> "Atlanta"
15 - "What's the weather in Seattle, and what is one flight that goes direct to Atlanta?" -> "Seattle"
16 - User asked about flights to Atlanta, then "what's the weather like there?" -> "Atlanta"
17 - "I'm going to Seattle" -> "Seattle"
18 - "What's happening?" -> "NOT_FOUND"
19
20 Extract location:"""
21
22 try:
23 user_messages = [
24 msg.get("content") for msg in messages if msg.get("role") == "user"
25 ]
26
27 if not user_messages:
28 location = "New York"
29 else:
30 ctx = extract(request.headers)
31 extra_headers = {}
32 inject(extra_headers, context=ctx)
33
34 # For location extraction, pass full conversation for context (e.g., "there" = previous destination)
35 response = await openai_client_via_plano.chat.completions.create(
36 model=LOCATION_MODEL,
37 messages=[
38 {"role": "system", "content": instructions},
39 *[
40 {"role": msg.get("role"), "content": msg.get("content")}
41 for msg in messages
42 ],
43 ],
44 temperature=0.1,
45 max_tokens=50,
46 extra_headers=extra_headers if extra_headers else None,
47 )
The Flight Agent extracts more complex information—origin, destination, and dates:
1async def extract_flight_route(messages: list, request: Request) -> dict:
2 """Extract origin, destination, and date from conversation using LLM."""
3
4 extraction_prompt = """Extract flight origin, destination cities, and travel date from the conversation.
5
6 Rules:
7 1. Look for patterns: "flight from X to Y", "flights to Y", "fly from X"
8 2. Extract dates like "tomorrow", "next week", "December 25", "12/25", "on Monday"
9 3. Use conversation context to fill in missing details
10 4. Return JSON: {"origin": "City" or null, "destination": "City" or null, "date": "YYYY-MM-DD" or null}
11
12 Examples:
13 - "Flight from Seattle to Atlanta tomorrow" -> {"origin": "Seattle", "destination": "Atlanta", "date": "2025-12-24"}
14 - "What flights go to New York?" -> {"origin": null, "destination": "New York", "date": null}
15 - "Flights to Miami on Christmas" -> {"origin": null, "destination": "Miami", "date": "2025-12-25"}
16 - "Show me flights from LA to NYC next Monday" -> {"origin": "LA", "destination": "NYC", "date": "2025-12-30"}
17
18 Today is December 23, 2025. Extract flight route and date:"""
19
20 try:
21 ctx = extract(request.headers)
22 extra_headers = {}
23 inject(extra_headers, context=ctx)
24
25 response = await openai_client_via_plano.chat.completions.create(
26 model=EXTRACTION_MODEL,
27 messages=[
28 {"role": "system", "content": extraction_prompt},
29 *[
30 {"role": msg.get("role"), "content": msg.get("content")}
31 for msg in messages[-5:]
32 ],
33 ],
34 temperature=0.1,
35 max_tokens=100,
36 extra_headers=extra_headers if extra_headers else None,
37 )
38
39 result = response.choices[0].message.content.strip()
40 if "```json" in result:
41 result = result.split("```json")[1].split("```")[0].strip()
42 elif "```" in result:
43 result = result.split("```")[1].split("```")[0].strip()
44
45 route = json.loads(result)
46 return {
47 "origin": route.get("origin"),
48 "destination": route.get("destination"),
49 "date": route.get("date"),
50 }
51 except Exception as e:
52 logger.error(f"Error extracting flight route: {e}")
Key Points:
Use smaller, faster models (like
gpt-4o-mini) for extraction tasksInclude conversation context to handle follow-up questions and pronouns
Use structured prompts with clear output formats (JSON)
Handle edge cases with fallback values
Calling External APIs
After extracting information, agents call external APIs to fetch real-time data:
1 # Geocode city to get coordinates
2 geocode_url = f"https://geocoding-api.open-meteo.com/v1/search?name={quote(location)}&count=1&language=en&format=json"
3 geocode_response = await http_client.get(geocode_url)
4
5 if geocode_response.status_code != 200 or not geocode_response.json().get(
6 "results"
7 ):
8 logger.warning(f"Could not geocode {location}, using New York")
9 location = "New York"
10 geocode_url = f"https://geocoding-api.open-meteo.com/v1/search?name={quote(location)}&count=1&language=en&format=json"
11 geocode_response = await http_client.get(geocode_url)
12
13 geocode_data = geocode_response.json()
14 if not geocode_data.get("results"):
15 return {
16 "location": location,
17 "weather": {
18 "date": datetime.now().strftime("%Y-%m-%d"),
19 "day_name": datetime.now().strftime("%A"),
20 "temperature_c": None,
21 "temperature_f": None,
22 "weather_code": None,
23 "error": "Could not retrieve weather data",
24 },
25 }
26
27 result = geocode_data["results"][0]
28 location_name = result.get("name", location)
29 latitude = result["latitude"]
30 longitude = result["longitude"]
31
32 logger.info(
33 f"Geocoded '{location}' to {location_name} ({latitude}, {longitude})"
34 )
35
36 # Get weather forecast
37 weather_url = (
38 f"https://api.open-meteo.com/v1/forecast?"
39 f"latitude={latitude}&longitude={longitude}&"
40 f"current=temperature_2m&"
41 f"daily=sunrise,sunset,temperature_2m_max,temperature_2m_min,weather_code&"
42 f"forecast_days={days}&timezone=auto"
43 )
44
45 weather_response = await http_client.get(weather_url)
46 if weather_response.status_code != 200:
47 return {
48 "location": location_name,
49 "weather": {
50 "date": datetime.now().strftime("%Y-%m-%d"),
51 "day_name": datetime.now().strftime("%A"),
52 "temperature_c": None,
53 "temperature_f": None,
54 "weather_code": None,
55 "error": "Could not retrieve weather data",
56 },
57 }
58
59 weather_data = weather_response.json()
60 current_temp = weather_data.get("current", {}).get("temperature_2m")
61 daily = weather_data.get("daily", {})
62
The Flight Agent calls FlightAware’s AeroAPI:
1async def get_flights(
2 origin_code: str, dest_code: str, travel_date: Optional[str] = None
3) -> Optional[dict]:
4 """Get flights between two airports using FlightAware API.
5
6 Args:
7 origin_code: Origin airport IATA code
8 dest_code: Destination airport IATA code
9 travel_date: Travel date in YYYY-MM-DD format, defaults to today
10
11 Note: FlightAware API limits searches to 2 days in the future.
12 """
13 try:
14 # Use provided date or default to today
15 if travel_date:
16 search_date = travel_date
17 else:
18 search_date = datetime.now().strftime("%Y-%m-%d")
19
20 # Validate date is not too far in the future (FlightAware limit: 2 days)
21 search_date_obj = datetime.strptime(search_date, "%Y-%m-%d")
22 today = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
23 days_ahead = (search_date_obj - today).days
24
25 if days_ahead > 2:
26 logger.warning(
27 f"Requested date {search_date} is {days_ahead} days ahead, exceeds FlightAware 2-day limit"
28 )
29 return {
30 "origin_code": origin_code,
31 "destination_code": dest_code,
32 "flights": [],
33 "count": 0,
34 "error": f"FlightAware API only provides flight data up to 2 days in the future. The requested date ({search_date}) is {days_ahead} days ahead. Please search for today, tomorrow, or the day after.",
35 }
36
37 url = f"{AEROAPI_BASE_URL}/airports/{origin_code}/flights/to/{dest_code}"
38 headers = {"x-apikey": AEROAPI_KEY}
39 params = {
40 "start": f"{search_date}T00:00:00Z",
41 "end": f"{search_date}T23:59:59Z",
42 "connection": "nonstop",
43 "max_pages": 1,
44 }
45
46 response = await http_client.get(url, headers=headers, params=params)
47
48 if response.status_code != 200:
49 logger.error(
50 f"FlightAware API error {response.status_code}: {response.text}"
51 )
52 return None
53
54 data = response.json()
55 flights = []
56
57 # Log raw API response for debugging
58 logger.info(f"FlightAware API returned {len(data.get('flights', []))} flights")
59
60 for idx, flight_group in enumerate(
61 data.get("flights", [])[:5]
62 ): # Limit to 5 flights
63 # FlightAware API nests data in segments array
64 segments = flight_group.get("segments", [])
65 if not segments:
66 continue
67
68 flight = segments[0] # Get first segment (direct flights only have one)
69
70 # Extract airport codes from nested objects
71 flight_origin = None
72 flight_dest = None
73
74 if isinstance(flight.get("origin"), dict):
75 flight_origin = flight["origin"].get("code_iata")
76
77 if isinstance(flight.get("destination"), dict):
78 flight_dest = flight["destination"].get("code_iata")
79
80 # Build flight object
81 flights.append(
82 {
83 "airline": flight.get("operator"),
84 "flight_number": flight.get("ident_iata") or flight.get("ident"),
85 "departure_time": flight.get("scheduled_out"),
86 "arrival_time": flight.get("scheduled_in"),
87 "origin": flight_origin,
88 "destination": flight_dest,
89 "aircraft_type": flight.get("aircraft_type"),
90 "status": flight.get("status"),
91 "terminal_origin": flight.get("terminal_origin"),
92 "gate_origin": flight.get("gate_origin"),
93 }
94 )
95
96 return {
97 "origin_code": origin_code,
98 "destination_code": dest_code,
99 "flights": flights,
100 "count": len(flights),
101 }
102 except Exception as e:
103 logger.error(f"Error fetching flights: {e}")
104 return None
105
Key Points:
Use async HTTP clients (like
httpx.AsyncClient) for non-blocking API callsTransform external API responses into consistent, structured formats
Handle errors gracefully with fallback values
Cache or validate data when appropriate (e.g., airport code validation)
Preparing Context and Generating Responses
Agents combine extracted information, API data, and conversation history to generate responses:
1 last_user_msg = get_last_user_content(messages)
2 days = 1
3
4 if "forecast" in last_user_msg or "week" in last_user_msg:
5 days = 7
6 elif "tomorrow" in last_user_msg:
7 days = 2
8
9 # Extract specific number of days if mentioned (e.g., "5 day forecast")
10 import re
11
12 day_match = re.search(r"(\d{1,2})\s+day", last_user_msg)
13 if day_match:
14 requested_days = int(day_match.group(1))
15 days = min(requested_days, 16) # API supports max 16 days
16
17 # Get live weather data (location extraction happens inside this function)
18 weather_data = await get_weather_data(request, messages, days)
19
20 # Create weather context to append to user message
21 forecast_type = "forecast" if days > 1 else "current weather"
22 weather_context = f"""
23
24Weather data for {weather_data['location']} ({forecast_type}):
25{json.dumps(weather_data, indent=2)}"""
26
27 # System prompt for weather agent
28 instructions = """You are a weather assistant in a multi-agent system. You will receive weather data in JSON format with these fields:
29
30 - "location": City name
31 - "forecast": Array of weather objects, each with date, day_name, temperature_c, temperature_f, temperature_max_c, temperature_min_c, weather_code, sunrise, sunset
32 - weather_code: WMO code (0=clear, 1-3=partly cloudy, 45-48=fog, 51-67=rain, 71-86=snow, 95-99=thunderstorm)
33
34 Your task:
35 1. Present the weather/forecast clearly for the location
36 2. For single day: show current conditions
37 3. For multi-day: show each day with date and conditions
38 4. Include temperature in both Celsius and Fahrenheit
39 5. Describe conditions naturally based on weather_code
40 6. Use conversational language
41
42 Important: If the conversation includes information from other agents (like flight details), acknowledge and build upon that context naturally. Your primary focus is weather, but maintain awareness of the full conversation.
43
44 Remember: Only use the provided data. If fields are null, mention data is unavailable."""
45
46 # Build message history with weather data appended to the last user message
47 response_messages = [{"role": "system", "content": instructions}]
48
49 for i, msg in enumerate(messages):
50 # Append weather data to the last user message
51 if i == len(messages) - 1 and msg.get("role") == "user":
52 response_messages.append(
53 {"role": "user", "content": msg.get("content") + weather_context}
54 )
55 else:
56 response_messages.append(
57 {"role": msg.get("role"), "content": msg.get("content")}
58 )
59
60 try:
61 ctx = extract(request.headers)
62 extra_headers = {"x-envoy-max-retries": "3"}
63 inject(extra_headers, context=ctx)
64
65 stream = await openai_client_via_plano.chat.completions.create(
66 model=WEATHER_MODEL,
67 messages=response_messages,
68 temperature=request_body.get("temperature", 0.7),
69 max_tokens=request_body.get("max_tokens", 1000),
70 stream=True,
71 extra_headers=extra_headers,
72 )
73
74 async for chunk in stream:
75 if chunk.choices:
76 yield f"data: {chunk.model_dump_json()}\n\n"
77
78 yield "data: [DONE]\n\n"
79
80 except Exception as e:
81 logger.error(f"Error generating weather response: {e}")
Key Points:
Use system messages to provide structured data to the LLM
Include full conversation history for context-aware responses
Stream responses for better user experience
Route all LLM calls through Plano’s gateway for consistent behavior and observability
Best Practices
Write Clear Agent Descriptions
Agent descriptions are used by Plano-Orchestrator to make routing decisions. Be specific about what each agent handles:
# Good - specific and actionable
- id: flight_agent
description: Get live flight information between airports using FlightAware AeroAPI. Shows real-time flight status, scheduled/estimated/actual departure and arrival times, gate and terminal information, delays, aircraft type, and flight status. Automatically resolves city names to airport codes (IATA/ICAO). Understands conversation context to infer origin/destination from follow-up questions.
# Less ideal - too vague
- id: flight_agent
description: Handles flight queries
Use Conversation Context Effectively
Include conversation history in your extraction and response generation:
# Include conversation context for extraction
conversation_context = []
for msg in messages:
conversation_context.append({"role": msg.role, "content": msg.content})
# Use recent context (last 10 messages)
context_messages = conversation_context[-10:] if len(conversation_context) > 10 else conversation_context
Route LLM Calls Through Plano’s Model Proxy
Always route LLM calls through Plano’s Model Proxy for consistent responses, smart routing, and rich observability:
openai_client_via_plano = AsyncOpenAI(
base_url=LLM_GATEWAY_ENDPOINT, # Plano's LLM gateway
api_key="EMPTY",
)
response = await openai_client_via_plano.chat.completions.create(
model="openai/gpt-4o",
messages=messages,
stream=True,
)
Handle Errors Gracefully
Provide fallback values and clear error messages:
async def get_weather_data(request: Request, messages: list, days: int = 1):
try:
# ... extraction and API logic ...
location = response.choices[0].message.content.strip().strip("\"'`.,!?")
if not location or location.upper() == "NOT_FOUND":
location = "New York" # Fallback to default
return weather_data
except Exception as e:
logger.error(f"Error getting weather data: {e}")
return {"location": "New York", "weather": {"error": "Could not retrieve weather data"}}
Use Appropriate Models for Tasks
Use smaller, faster models for extraction tasks and larger models for final responses:
# Extraction: Use smaller, faster model
LOCATION_MODEL = "openai/gpt-4o-mini"
# Final response: Use larger, more capable model
WEATHER_MODEL = "openai/gpt-4o"
Stream Responses
Stream responses for better user experience:
async def invoke_weather_agent(request: Request, request_body: dict, traceparent_header: str = None):
# ... prepare messages with weather data ...
stream = await openai_client_via_plano.chat.completions.create(
model=WEATHER_MODEL,
messages=response_messages,
temperature=request_body.get("temperature", 0.7),
max_tokens=request_body.get("max_tokens", 1000),
stream=True,
extra_headers=extra_headers,
)
async for chunk in stream:
if chunk.choices:
yield f"data: {chunk.model_dump_json()}\n\n"
yield "data: [DONE]\n\n"
Common Use Cases
Multi-agent orchestration is particularly powerful for:
Travel and Booking Systems
Route queries to specialized agents for weather and flights:
agents:
- id: weather_agent
description: Get real-time weather conditions and forecasts
- id: flight_agent
description: Search for flights and provide flight status
Customer Support
Route common queries to automated support agents while escalating complex issues:
agents:
- id: tier1_support
description: Handles common FAQs, password resets, and basic troubleshooting
- id: tier2_support
description: Handles complex technical issues requiring deep product knowledge
- id: human_escalation
description: Escalates sensitive issues or unresolved problems to human agents
Sales and Marketing
Direct leads and inquiries to specialized sales agents:
agents:
- id: product_recommendation
description: Recommends products based on user needs and preferences
- id: pricing_agent
description: Provides pricing information and quotes
- id: sales_closer
description: Handles final negotiations and closes deals
Technical Documentation and Support
Combine RAG agents for documentation lookup with specialized troubleshooting agents:
agents:
- id: docs_agent
description: Retrieves relevant documentation and guides
filter_chain:
- query_rewriter
- context_builder
- id: troubleshoot_agent
description: Diagnoses and resolves technical issues step by step
Next Steps
Learn more about agents and the inner vs. outer loop model
Explore filter chains for adding guardrails and context enrichment
See observability for monitoring multi-agent workflows
Review the LLM Providers guide for model routing within agents
Check out the complete Travel Booking demo on GitHub
Note
To observe traffic to and from agents, please read more about observability in Plano.
By carefully configuring and managing your Agent routing and hand off, you can significantly improve your application’s responsiveness, performance, and overall user satisfaction.