We are going to build a command-line environmental analyst on Oxlo.ai that fetches live air quality data and returns a structured risk assessment. It is useful for researchers, policy tool builders, or anyone who needs to turn raw sensor readings into actionable summaries without managing token-based billing surprises.
What you'll need
- Python 3.10+
- The OpenAI SDK:
pip install openai - The
requestslibrary:pip install requests - An Oxlo.ai API key from https://portal.oxlo.ai
Step 1: Initialize the Oxlo.ai client and data fetcher
We start by importing the dependencies and creating an OpenAI-compatible client pointed at Oxlo.ai. We also add a helper that calls the Open-Meteo Air Quality API for current PM2.5, PM10, ozone, and UV index values.
import requests
from openai import OpenAI
client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")
def fetch_air_quality(lat: float, lon: float):
url = "https://air-quality-api.open-meteo.com/v1/air-quality"
params = {
"latitude": lat,
"longitude": lon,
"current": "pm10,pm2_5,ozone,uv_index",
"timezone": "auto",
}
resp = requests.get(url, params=params, timeout=30)
resp.raise_for_status()
return resp.json()["current"]
Step 2: Resolve city names to coordinates
Hard-coding coordinates is brittle, so we use Open-Meteo's free geocoding endpoint to translate a city name into latitude and longitude.
def geocode_city(name: str):
url = "https://geocoding-api.open-meteo.com/v1/search"
params = {"name": name, "count": 1}
resp = requests.get(url, params=params, timeout=30)
resp.raise_for_status()
data = resp.json()
results = data.get("results")
if not results:
raise ValueError(f"City not found: {name}")
r = results[0]
return r["latitude"], r["longitude"], r["name"]
Step 3: Define the agent system prompt
The system prompt encodes the evaluation rubric and the exact JSON schema we want back. Keeping the prompt explicit keeps the model deterministic.
SYSTEM_PROMPT = """
You are an environmental science analyst. A user will provide current air quality readings for a location. Evaluate overall health risk as low, moderate, or high. Return a JSON object with exactly these keys: summary (one sentence), risk_level (low | moderate | high), pm2_5_note (string), ozone_note (string), uv_note (string), recommendation (one practical recommendation). Be concise. Use EPA category thresholds where PM2.5 0-12 is low, 12.1-35.4 moderate, 35.5+ high. Ozone 0-50 low, 51-100 moderate, 101+ high.
"""
Step 4: Wire fetch and inference together
This function geocodes the city, pulls the latest readings, and sends them to Oxlo.ai for structured analysis. We use JSON mode so the response parses cleanly into a Python dictionary.
import json
def analyze_city(city_name: str):
lat, lon, display_name = geocode_city(city_name)
data = fetch_air_quality(lat, lon)
user_message = (
f"Location: {display_name} ({lat}, {lon})\n"
f"Current readings:\n"
f"- PM2.5: {data['pm2_5']} µg/m³\n"
f"- PM10: {data['pm10']} µg/m³\n"
f"- Ozone: {data['ozone']} µg/m³\n"
f"- UV Index: {data['uv_index']}\n"
)
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": user_message},
],
response_format={"type": "json_object"},
temperature=0.2,
)
return json.loads(response.choices[0].message.content)
Run it
Call the agent from the command line and print the structured report.
if __name__ == "__main__":
report = analyze_city("Berlin")
print(json.dumps(report, indent=2))
Example output:
{
"summary": "Air quality in Berlin is currently moderate due to elevated PM2.5 levels.",
"risk_level": "moderate",
"pm2_5_note": "PM2.5 at 18.4 µg/m³ falls in the moderate range.",
"ozone_note": "Ozone at 42 µg/m³ is low.",
"uv_note": "UV index is 2, minimal risk.",
"recommendation": "Sensitive individuals should limit prolonged outdoor exertion."
}
Next steps
Try extending the tool to accept a date range and fetch historical time-series from Open-Meteo. Because Oxlo.ai uses flat per-request pricing, you can pass long context windows filled with multi-day sensor logs without the cost scaling with input length. See https://oxlo.ai/pricing for plan details.
Another practical upgrade is wrapping the function in a FastAPI endpoint so a frontend or a cron job can query it on demand. The OpenAI-compatible SDK makes that migration trivial.
Top comments (0)