← Blog
TechnologySeptember 4, 2026 · 9 min read

Weather AI Agent on DeepSeek and Pydantic AI: Same Agent, Different SDK

The same task as in the previous article, but on the framework by the Pydantic team: a native DeepSeek provider out of the box and a response contract the framework validates itself. We compare it with OpenAI Agents SDK on a live run.

The Digital Paragon teamAI agent development
Illustration for the article “Weather AI Agent on DeepSeek and Pydantic AI”

In the previous article we built a weather consultant on OpenAI Agents SDK and ran into a pitfall: DeepSeek does not accept response_format of type json_schema, so the response contract had to be validated by hand. Today we build exactly the same agent — the same tool with Gismeteo, the same contract — but on the framework by the Pydantic team, and see how much code disappears.

Stack and installation

Pydantic AI is an agent framework from the team behind Pydantic itself: typing and validation are first-class citizens here. The version at the time of writing (September 2026) is 2.32.0 — the code in this article was verified on exactly that one.

pip install pydantic-ai

The key, as before, is issued in the platform.deepseek.com console and placed into the DEEPSEEK_API_KEY environment variable — the provider picks it up automatically.

The model and the contract: one line for the provider

No AsyncOpenAI client with a base_url: Pydantic AI has a native provider for DeepSeek, and the model is set with a “provider:model” string. The contract is the same Pydantic model as in the previous article, passed via the output_type parameter. The key difference: the framework itself parses and validates the JSON from the model's reply, retrying on error — no manual text parsing needed.

from pydantic import BaseModel, Field
from pydantic_ai import Agent

class WeatherReport(BaseModel):
    city: str = Field(description="The city the report is about")
    temperature_c: int = Field(description="Current temperature, °C")
    feels_like_c: int = Field(description="Feels-like temperature, °C")
    advice: str = Field(description="Practical advice: what to wear and take")

agent = Agent(
    "deepseek:deepseek-v4-flash",  # native provider: the key is taken from DEEPSEEK_API_KEY
    output_type=WeatherReport,      # response contract; the framework validates it
    system_prompt=(
        "You are a weather consultant. Detect the city from the question, "
        "call get_gismeteo_weather and write a short practical report. "
        "Use exactly the temperatures returned by the tool. No inventing."
    ),
)

Version pitfalls: the framework moves fast

To be honest about the first minutes of porting: in the current version 2.32 the parameter is called output_type, not result_type as in the older tutorials, and the key is not passed to the agent constructor — it is taken from the environment or via an explicit provider. Both times we got a TypeError and fixed the code; if you follow older examples, do not be surprised. The upside of the framework is that errors are loud and instant: the contract does not silently degrade into garbage.

The tool: the same live weather from Gismeteo

The tool is registered with the @agent.tool decorator; the first argument is the run context, followed by regular annotated parameters. The parsing logic is exactly the same as in the previous article.

import re
import httpx
from pydantic_ai import RunContext

CITY_SLUGS = {
    "moscow": "weather-moscow-4368",
    "saint petersburg": "weather-sankt-peterburg-4079",
    "kazan": "weather-kazan-4364",
}

@agent.tool
async def get_gismeteo_weather(ctx: RunContext[None], city: str) -> str:
    """Current temperature and feels-like from the city page on Gismeteo."""
    slug = CITY_SLUGS.get(city.strip().lower())
    if slug is None:
        return "City not found. Available: " + ", ".join(sorted(CITY_SLUGS))
    url = f"https://www.gismeteo.ru/{slug}/"
    async with httpx.AsyncClient(timeout=15, headers={"User-Agent": "Mozilla/5.0"}) as http:
        page = (await http.get(url)).text
    temps = re.findall(r'<temperature-value[^>]*value="(-?\d+)"[^>]*from-unit="c"', page)
    if len(temps) < 2:
        return "Could not parse weather from the Gismeteo page."
    return f"{city}: now {temps[0]}°C, feels like {temps[1]}°C (Gismeteo data, {url})"

The run

import asyncio

async def main() -> None:
    result = await agent.run("Is it warm in Moscow now? What should I wear for a walk?")
    report = result.output  # an already validated Pydantic model
    print(f"{report.city}: {report.temperature_c}°C, feels like {report.feels_like_c}°C")
    print("Advice:", report.advice)

asyncio.run(main())

A real run (deepseek-v4-flash, September 4, 2026):

Moscow: 17°C, feels like 16°C
Advice: It is cool outside but not cold — dress in layers. A light jacket or
a denim jacket, a sweater or a long-sleeve tee, comfortable sneakers will do.
Take a windbreaker or a light scarf in case it gets windy.

Same city, same temperatures from Gismeteo, the same advice in spirit — but there is not a single line of manual JSON parsing in the code. Compare with the previous article: there the contract rested on our own parse function with a retry; here it is the framework's responsibility.

Which SDK to choose

  • OpenAI Agents SDK is the pick if you are in the OpenAI ecosystem and need tracing and handoff escalations between agents. On DeepSeek mind the pitfall from the previous article: structured output via output_type does not work there, so the contract has to be closed manually.
  • Pydantic AI is the pick if you want less code and output validation out of the box: a native “provider:model” provider, a one-line contract, retries on validation errors. The price is a young framework with fast renames.

We ran both implementations live on the same task: the weather numbers matched and the contract was fulfilled in both cases. The choice is a matter of the team's taste, not of capability.

What to improve next

  • retries — a parameter of the agent constructor: how many times to retry on a response validation error.
  • deps_type — typed dependencies (config, clients) that the tools receive through the same context.
  • Streaming and Logfire — streaming replies and observability from the same development team.
  • Weather cache and more cities — exactly as in the previous article: the architecture allows it without changes.

The takeaway of the two-article series: a simple agent is a model, facts and a contract. Either framework will do; what matters is that the contract is checked by code and the data comes from a tool.

Read also
Weather AI Agent on DeepSeek: OpenAI Agents SDK + Pydantic Sep 4, 2026 · 10 min RAG or Fine-Tuning: What to Choose for Your Knowledge Base Aug 14, 2026 · 9 min A Two-Week AI Agent Pilot: What You Can Really Get Done Aug 28, 2026 · 9 min

Need an agent like this in your process?

Discuss your task