Back to Articles

Progressive Disclosure for AI Agents: How pydantic-ai-skills Cuts Token Costs

[ View on GitHub ]

Progressive Disclosure for AI Agents: How pydantic-ai-skills Cuts Token Costs

Hook

Your AI agent doesn't need to know how to book flights until it actually needs to book a flight—yet most frameworks force you to load every capability upfront, burning tokens and money with each API call.

Context

The evolution of AI agent frameworks has revealed a critical tension: agents need access to diverse capabilities, but stuffing all available tool documentation into every conversation context creates massive token overhead. Early LangChain and AutoGPT implementations would frontload dozens of tool descriptions, consuming thousands of tokens before the agent even started thinking. This approach doesn't scale—not technically, and certainly not economically.

Anthropic identified this pattern and proposed the Agent Skills specification, a standardized format for defining agent capabilities with progressive disclosure. The idea: agents should discover capabilities exist, then load detailed instructions only when needed. Doug Trajano's pydantic-ai-skills brings this specification to Pydantic AI, combining Anthropic's vision with Pydantic's type-safe ecosystem. The package offers both filesystem-based skills (following a directory convention) and programmatic skills (via Python decorators), giving developers flexibility while maintaining compatibility with an emerging industry standard.

Technical Insight

The architecture leverages Pydantic AI's tool-calling mechanism to expose three core tools to agents: load_skill fetches full skill instructions, read_skill_resource loads supplementary documentation, and run_skill_script executes associated Python scripts. This creates a lazy-loading pattern where agents first see lightweight skill metadata, then pull complete context only when relevant.

Here's how you define a filesystem-based skill following the Agent Skills spec:

from pydantic_ai_skills import SkillsCapability, FilesystemSkillSource
from pydantic_ai import Agent

# Create skill source pointing to directory structure
skills_source = FilesystemSkillSource("./skills")

# Initialize capability with skill source
skills_capability = SkillsCapability(skill_source=skills_source)

# Attach to agent
agent = Agent(
    "openai:gpt-4",
    capabilities=[skills_capability]
)

The ./skills directory contains subdirectories for each skill, with each requiring a skill.md file describing the capability and optionally a main.py script. For example, a weather skill might have:

skills/
  weather/
    skill.md       # Instructions for when/how to use weather API
    main.py        # Script that makes actual API calls
    resources/
      api-docs.md  # Additional reference documentation

The skill.md file follows a specific structure that agents can parse:

# Weather Lookup

Provides current weather data for any city worldwide.

## When to use
Use this skill when the user asks about current weather conditions, temperature, or forecasts.

## Usage
1. Load this skill using load_skill("weather")
2. Run the script with run_skill_script("weather", {"city": "city_name"})
3. The script returns JSON with temperature, conditions, and forecast

For programmatic skills, the package offers a decorator-based approach that's more ergonomic for Python-native workflows:

from pydantic_ai_skills import skill, ProgrammaticSkillSource
from pydantic import BaseModel

class WeatherInput(BaseModel):
    city: str
    units: str = "metric"

@skill(
    name="weather",
    description="Get current weather for a city",
    instructions="""
    Use this skill when users ask about current weather.
    Provide city name and optionally temperature units.
    """
)
def get_weather(input: WeatherInput) -> dict:
    # Your weather API logic here
    return {"temp": 72, "conditions": "sunny"}

# Create source from decorated functions
skills_source = ProgrammaticSkillSource.from_skills([get_weather])
skills_capability = SkillsCapability(skill_source=skills_source)

The package offers two integration patterns: SkillsCapability (using Pydantic AI's Capabilities API) and SkillsToolset (traditional toolset). The Capability approach is cleaner for new projects, while Toolset provides backward compatibility:

# Toolset approach for existing codebases
from pydantic_ai_skills import SkillsToolset

skills_toolset = SkillsToolset(skill_source=skills_source)
agent = Agent(
    "openai:gpt-4",
    tools=skills_toolset.get_tools()
)

The progressive disclosure happens automatically. When an agent encounters a user request, it first sees lightweight skill listings—just names and brief descriptions. If the agent determines a skill is relevant, it calls load_skill("weather") to fetch complete instructions. Only then does the full skill documentation enter the context window. For complex skills with extensive documentation, the agent can selectively call read_skill_resource("weather", "api-docs.md") to load specific reference material.

Security considerations are built into the script execution model. The package prevents path traversal attacks in skill loading and executes scripts in subprocess isolation rather than eval() or exec(). However, this remains a trust boundary—filesystem skills require vetting, especially in multi-tenant environments.

Gotcha

The filesystem-based approach requires discipline. You must maintain proper directory structure, and skill documentation must be well-formatted Markdown that agents can parse effectively. Poorly written skill.md files will confuse agents, leading to either skill misuse or avoidance. There's no validation framework to ensure your skill descriptions are agent-friendly—you discover problems through runtime agent behavior.

Script execution via subprocess introduces latency and limits interoperability. Skills can't easily share state or maintain persistent connections. Each run_skill_script call spawns a new process, making skills unsuitable for scenarios requiring connection pooling, stateful sessions, or sub-second response times. The package also inherits Pydantic AI's ecosystem constraints—you're locked into Pydantic AI's architecture patterns. If you need to port skills to LangChain or CrewAI, you'll need to rewrite integration code despite the underlying Agent Skills spec being framework-agnostic in principle. The spec's youth means tooling and community patterns are still emerging, and breaking changes to the specification could require migration work.

Verdict

Use if: You're building Pydantic AI agents with 5+ capabilities where context window optimization matters, you want to share skills across multiple agent projects or teams, you're committed to the Agent Skills specification as a long-term standard, or you need a clean separation between skill definition (product/domain) and agent logic (engineering). The progressive disclosure pattern genuinely reduces token costs in complex agents. Skip if: You're prototyping with 2-3 simple tools where upfront context loading is negligible, you need sub-100ms tool execution (subprocess overhead hurts), you require mature production tooling with extensive community support (284 stars signals early adoption), or you're working in non-Pydantic AI frameworks where the integration overhead outweighs spec compatibility benefits. For straightforward single-purpose agents, writing tools directly in Pydantic AI is simpler and more transparent.

// ADD TO YOUR README
[![Featured on Starlog](https://starlog.is/api/badge/ai-agents/dougtrajano-pydantic-ai-skills.svg)](https://starlog.is/api/badge-click/ai-agents/dougtrajano-pydantic-ai-skills)