Building an AI Weather Agent with PydanticAI and Tool Injection A developer built a production-ready AI weather agent using PydanticAI, Pydantic, and tool injection. The agent orchestrates external API calls to Open-Meteo for geocoding and weather data, validating responses with strict Pydantic models. The project demonstrates patterns for building autonomous agents that can handle structured dependencies and real-time data access. Large Language Models LLMs excel at processing natural language but cannot access real-time data natively. To resolve this limitation, developers build autonomous AI agents. An AI agent extends an LLM's capabilities by executing external tools, accessing live APIs, and validating unstructured data into predictable data models. This article details how to build a production-ready AI weather agent using PydanticAI , validate external API data with Pydantic , and handle structured dependencies. This project serves as a foundational step toward constructing specialized, autonomous agents capable of managing automated Bitcoin mining nodes, validating Lightning Network lightning transactions, and orchestrating algorithmic payments. The application architecture separates prompt evaluation from the execution of the external business logic. User Query │ ▼ PydanticAI Agent │ ▼ Weather Tool @agent.tool │ ▼ Weather Service WeatherAgent Dependency │ ▼ Open-Meteo Geocoding & Forecast APIs │ ▼ Pydantic Data Validation Coordinates & CurrentWeather Models │ ▼ Final Structured AI Response Instead of allowing the LLM to access the network directly, the agent acts as an orchestrator. It extracts natural language parameters such as a city name , passes them to a registered validation tool, executes network requests via httpx , and maps the response payload back to a strict schema. To transition from a simple text chatbot to an analytical agent, this project implements several core engineering patterns: deps type . BaseModel classes to prevent data corruption and unexpected structural types. @agent.tool decorator to automatically expose Python functions to the underlying LLM via generated JSON schemas.The implementation is contained within a single executable Python module main.py . The script uses the Open-Meteo Geocoding API to resolve alphanumeric location queries into geographic coordinates before querying weather metrics. python import httpx from pydantic import BaseModel from pydantic ai import Agent, RunContext class Coordinates BaseModel : """Data model to validate geographic coordinates.""" latitude: float longitude: float class CurrentWeather BaseModel : """Data model to validate structured weather conditions.""" city: str temperature: float Corrected typo from original 'tempereture' description: str class WeatherAgent: """Service class handling external API interaction and data retrieval.""" def get coordinates self, city: str - Coordinates: try: response = httpx.get "https://geocoding-api.open-meteo.com/v1/search", params={"name": city, "count": 1}, timeout=5.0 response.raise for status data = response.json results = data.get "results" if not results: raise ValueError f"City '{city}' was not found." result = results 0 return Coordinates latitude=result "latitude" , longitude=result "longitude" except httpx.ConnectError: print " ERROR Connection failure to Geocoding server." raise except httpx.TimeoutException: print " ERROR Geocoding request timed out." raise except httpx.HTTPStatusError as e: print f" ERROR Server returned HTTP {e.response.status code}" raise def describe weather self, code: int - str: """Maps World Meteorological Organization WMO codes to human-readable strings.""" weather codes = { 0: "Clear sky", 1: "Mainly clear", 2: "Partly cloudy", 3: "Overcast", } return weather codes.get code, "Unknown" def get weather self, city: str - CurrentWeather: Step 1: Resolve city name to GPS coordinates coordinates = self.get coordinates city Step 2: Query the forecast API with resolved positions response = httpx.get "https://api.open-meteo.com/v1/forecast", params={ "latitude": coordinates.latitude, "longitude": coordinates.longitude, "current": "temperature 2m,weather code", }, timeout=5.0 response.raise for status data = response.json current = data "current" return CurrentWeather city=city, temperature=current "temperature 2m" , description=self.describe weather current "weather code" , Instantiate the agent with strict instructions and explicit type dependencies agent = Agent "groq:llama-3.3-70b-versatile", instructions= "You are a precise, helpful, and professional Weather Assistant. " "Your primary goal is to provide accurate weather forecasts and current conditions " "by utilizing available weather tools and structuring the data flawlessly.\n\n" " Core Responsibilities:\n" "1. Location Resolution: Extract the location city, country, region from the user's query.\n" "2. Tool Execution: Use the provided weather tools to fetch real-time data. Never make up weather data.\n" "3. Data Mapping: Carefully parse the tool outputs to fulfill the schema requirements.\n\n" " Formatting & Tone Guidelines:\n" "- Units: Default to the metric system Celsius, km/h, mm unless the user requests imperial units.\n" "- Clarity: Summarize complex meteorological data into simple, digestible insights.\n\n" " Behavioral Constraints:\n" "- If a location cannot be found, explain the limitation gracefully.\n" "- Do not hallucinate current dates or times; rely entirely on the tool's timestamps." , deps type=WeatherAgent, @agent.tool def current weather ctx: RunContext WeatherAgent , city: str - CurrentWeather: """Fetches the current weather for a given city. Args: ctx: The runtime context containing the injected WeatherAgent dependency. city: The name of the city to lookup. """ print f" TOOL Fetching weather for {city}" return ctx.deps.get weather city if name == " main ": history = None print "AI Weather Agent Initialized. Type 'exit' to quit." while True: prompt = input "you " if prompt.lower in {"exit", "quit"}: break if not prompt.strip : continue try: Instantiate service dependency per conversation frame weather service = WeatherAgent result = agent.run sync prompt, message history=history, deps=weather service print f"bot {result.output}" history = result.all messages except Exception as e: print f" RUNTIME ERROR {type e . name }: {e}" Building this system highlights the fundamental difference between standard generative text generation and actual agentic engineering: WeatherAgent service instance through the deps argument ensures that tools remain stateless and isolated. This allows for concurrent execution threads without shared-state mutation errors. httpx logic with deliberate catch blocks for ConnectError , TimeoutException , and HTTPStatusError ensures that the Python app handles infrastructure hiccups cleanly before they crash the runtime stack.Mastering structured tools and type-safe dependency execution prepares developers for the next level of intelligent systems: Bitcoin-native AI agents . The long-term roadmap for this architecture shifts from tracking weather metrics to executing autonomous machine-to-machine financial operations on the Bitcoin network: RunContext , agents can autonomously pay for APIs, settle micro-invoices, and rebalance liquidity channels based on demand.