Complete Weather Agent
On this page
Build a Complete Weather Agent from Scratch
In this lesson, you will assemble everything you’ve learned to build a complete weather agent. This agent illustrates the full function calling cycle: tool definition, multi-turn handling, error handling, and natural language responses.
Agent Architecture
The weather agent has three tools:
- get_weather — get current weather for a city
- get_forecast — get forecasts for multiple days
- get_alerts — check active weather alerts
import json
from openai import OpenAI
client = OpenAI(
api_key="your-xai-key",
base_url="https://api.x.ai/v1"
)
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get current weather for a city. "
"Returns temperature, humidity, wind, and conditions.",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "City name (e.g., Paris, Lyon, Marseille)"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"default": "celsius"
}
},
"required": ["city"]
}
}
},
{
"type": "function",
"function": {
"name": "get_forecast",
"description": "Get weather forecasts for multiple days. "
"Returns day-by-day conditions.",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "City name"
},
"days": {
"type": "integer",
"description": "Number of forecast days (1-7)",
"default": 3
}
},
"required": ["city"]
}
}
},
{
"type": "function",
"function": {
"name": "get_alerts",
"description": "Check active weather alerts (storms, heatwaves, "
"floods) for a city or region.",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "City or region name"
}
},
"required": ["city"]
}
}
}
]
Function Implementation
In production, these functions would call a real weather API. Here, we simulate the responses:
def get_weather(city, unit="celsius"):
# In production: call OpenWeatherMap, WeatherAPI, etc.
data = {
"Paris": {"temp": 18, "humidity": 65, "wind": 12, "conditions": "Cloudy"},
"Lyon": {"temp": 21, "humidity": 55, "wind": 8, "conditions": "Sunny"},
"Marseille": {"temp": 24, "humidity": 70, "wind": 15, "conditions": "Clear"},
}
result = data.get(city, {"error": f"City {city} not found"})
if "error" not in result:
result["city"] = city
result["unit"] = unit
return result
def get_forecast(city, days=3):
return {
"city": city,
"forecast": [
{"day": f"Day+{i+1}", "temp_max": 20+i, "temp_min": 12+i,
"conditions": "Variable"}
for i in range(days)
]
}
def get_alerts(city):
alerts = {
"Marseille": [{"type": "Strong Wind", "severity": "orange",
"message": "Gusts up to 90 km/h expected"}],
}
return {"city": city, "alerts": alerts.get(city, [])}
The Agent’s Main Loop
def execute_function(name, args):
functions = {
"get_weather": get_weather,
"get_forecast": get_forecast,
"get_alerts": get_alerts,
}
fn = functions.get(name)
if not fn:
return {"error": f"Unknown function {name}"}
try:
return fn(**args)
except Exception as e:
return {"error": str(e)}
def weather_agent(user_message):
messages = [
{
"role": "system",
"content": "You are an expert weather assistant. "
"Use the available tools to provide "
"accurate weather information. Always "
"check alerts when asked about the "
"weather for a city."
},
{"role": "user", "content": user_message}
]
for _ in range(5): # Max 5 turns
response = client.chat.completions.create(
model="grok-4.5",
messages=messages,
tools=tools
)
message = response.choices[0].message
messages.append(message)
if not message.tool_calls:
return message.content
for tc in message.tool_calls:
args = json.loads(tc.function.arguments)
print(f" → Calling: {tc.function.name}({args})")
result = execute_function(tc.function.name, args)
messages.append({
"role": "tool",
"tool_call_id": tc.id,
"content": json.dumps(result, ensure_ascii=False)
})
return "Sorry, too many turns required."
Test the Agent
# Test 1: simple question
print(weather_agent("What's the weather like in Paris?"))
# Test 2: forecast
print(weather_agent("Give me the forecast for Lyon for 5 days"))
# Test 3: multi-city (parallel calls)
print(weather_agent("Compare the weather between Paris, Lyon, and Marseille"))
# Test 4: complex question
print(weather_agent("I'm going to Marseille this weekend, what do you recommend?"))
Test 3 triggers parallel calls: the model calls get_weather three times simultaneously. Test 4 is the most interesting—the model should call get_forecast AND get_alerts for Marseille, then synthesize the results into practical advice.
Possible Improvements
For a production-ready agent, add:
- Cache: avoid calling the weather API for the same city within 10 minutes
- Geocoding: convert city names to coordinates for better accuracy
- History: save user preferences (units, favorite cities)
- Internationalization: support city names in all languages
Key Takeaways
- A complete agent combines multiple tools with distinct responsibilities
- The system prompt guides model behavior (check alerts systematically)
- The multi-turn loop with
max_iterationsprevents infinite loops - The
execute_functiondispatcher centralizes execution and error handling - Test with varied queries: simple, multi-city, complex
- In production, add caching, geocoding, and user history