Pydantic AI: Dict Arguments Invisible To OpenAI Agents
Hey there, fellow developers! Have you ever been working with Pydantic AI and noticed something a little... off when using OpenAI models? Specifically, you might have encountered a peculiar behavior where arguments typed as dict within your tools seem to vanish into thin air, completely invisible to the OpenAI model. This can be a real head-scratcher, especially when you expect your agents to understand and utilize all the parameters you've so carefully defined. In this article, we're going to dive deep into this intriguing issue, explore why it happens, and what we can do about it. We'll walk through a clear example that demonstrates the problem and then discuss how other models, like Anthropic's Claude, handle it differently, offering a glimpse into potential solutions and best practices for working with complex data structures in your AI agents.
The Mystery of the Missing dict Arguments
Let's set the stage with a common scenario. You're building an agent using Pydantic AI, and you've defined a couple of handy tools. One tool, let's call it tool_w_dict, is designed to accept a string name and a dictionary data where keys and values are strings. Another tool, tool_w_list, accepts a name and a list of strings. You're using a powerful OpenAI model, like gpt-4o, to power your agent. You then ask your agent to list its available tools and their parameters, expecting a comprehensive overview. However, when the agent responds, you'll notice something peculiar: the data argument for tool_w_dict is simply gone. It's as if the OpenAI model never saw it! This behavior is consistent across multiple OpenAI models we've tested, including gpt-4o, gpt-4o-mini, and even gpt-5. It's a significant hurdle when you need your AI to understand and work with structured dictionary data, which is quite common in many applications. This invisibility means the model can't effectively use the dict parameter, limiting the functionality of your tools and the overall capability of your agent. We're talking about a core aspect of how your agent interacts with its environment – its tools – being compromised. This isn't just a minor bug; it's a fundamental limitation that can impact how you design your AI systems.
A Concrete Example: Visualizing the Problem
To really hammer home this point, let's look at a practical example using Pydantic AI. Imagine we've set up an agent powered by openai:gpt-4o. We define two tools:
from pydantic_ai import Agent
openai_agent = Agent("openai:gpt-4o")
@openai_agent.tool_plain
def tool_w_dict(name: str, data: dict[str, str]) -> str:
"""A tool with a dict argument.
Args:
name: A name string
data: A dictionary with arbitrary data
"""
return f"Got {name} with {data}"
@openai_agent.tool_plain
def tool_w_list(name: str, data: list[str]) -> str:
"""A tool with a list argument.
Args:
name: A name string
data: A list with arbitrary data
"""
return f"Got {name} with {data}"
Now, when we instruct this agent to list its tools and parameters, like so:
openai_result = openai_agent.run_sync("List all tools you have and their parameters with types")
print(openai_result.output)
The output we receive is quite revealing:
I have the following tools available:
1. **functions.tool_w_dict**
- **Parameters**:
- `name` (string): A name string.
2. **functions.tool_w_list**
- **Parameters**:
- `name` (string): A name string.
- `data` (string[]): A list with arbitrary data.
These tools can handle dictionary-based or list-based inputs, respectively.
Notice how tool_w_dict only shows the name parameter. The data: dict[str, str] argument is conspicuously absent. In stark contrast, tool_w_list, which uses a list[str] for its data argument, correctly lists data as string[]. This disparity highlights a specific issue with how OpenAI models, when integrated with Pydantic AI, interpret and expose dictionary types for tool parameters. It's a critical difference that can lead to unexpected behavior and limitations in tool usage. The model isn't aware of the full signature of the tool_w_dict function, meaning it cannot generate valid calls to it that include the data argument, even if you, the developer, know it exists and is intended to be used. This fundamental disconnect between the tool's definition and the model's understanding is at the heart of the problem.
The Anthropic Contrast: A Beacon of Clarity
To further illuminate the problem with OpenAI's behavior, let's compare it with an agent powered by a different provider: Anthropic's Claude. When we configure an agent with anthropic:claude-sonnet-4-20250514 and run the exact same code to list the tools, the output is remarkably different and, frankly, exactly what we'd expect:
I have access to 2 tools:
## 1. tool_w_dict
**Description:** A tool with a dict argument.
**Parameters:**
- `name` (string, required): A name string
- `data` (object, required): A dictionary with arbitrary data
- Additional properties: string values only
## 2. tool_w_list
**Description:** A tool with a list argument.
**Parameters:**
- `name` (string, required): A name string
- `data` (array, required): A list with arbitrary data
- Items: string type only
See the difference? The Anthropic agent correctly identifies and describes both the name and the data arguments for tool_w_dict. It accurately categorizes data as an object (which is how dictionaries are typically represented in JSON schemas used by LLMs) and even provides details about its expected structure (additional properties with string values only). Similarly, it correctly identifies data in tool_w_list as an array. This stark contrast between OpenAI and Anthropic demonstrates that the issue isn't necessarily with Pydantic AI's ability to define complex types, but rather with how the OpenAI API, or perhaps its integration layer within Pydantic AI, handles the serialization and exposure of dict types when generating tool schemas for the model. This capability in Anthropic's model suggests that the underlying mechanisms for exposing tool signatures to LLMs can handle dictionary types effectively, providing a valuable benchmark and a potential roadmap for resolving the OpenAI-specific issue. It also underscores the importance of choosing the right model provider for specific use cases, especially when dealing with structured data.
Digging Deeper: Why the Discrepancy?
The core of the issue appears to stem from how different language model providers format the tool definitions they send to the LLM. Pydantic AI, much like other frameworks that enable LLMs to use tools, relies on a structured format (often JSON Schema) to describe available functions to the model. The OpenAI API, when receiving these schemas, seems to have a peculiar blind spot regarding dict types when they are used as direct parameters for tools. While list types are generally well-represented as array, dict types are not consistently translated into an object type in a way that OpenAI's models readily recognize or prioritize when inspecting tool signatures. This could be due to how the OpenAI API's function calling or tool usage system parses these schemas, or it might be an intentional design choice that prioritizes simpler, more primitive types when describing tools to the model. The Anthropic model, on the other hand, seems to interpret the schema more comprehensively, recognizing dict as object and list as array consistently, allowing it to present a complete picture of the tool's parameters. This suggests that the problem isn't with Pydantic's schema generation itself, but with the interpretation layer of the OpenAI API. When the OpenAI model receives the tool definitions, it's not seeing the full picture for dictionary arguments, hence it omits them from its understanding and its responses. This makes it impossible for the model to generate arguments for those dict parameters, even if the developer has clearly defined them and expects them to be used. Understanding this difference is crucial for developers who rely on structured data inputs for their AI agents.
Potential Causes and Workarounds
Several factors could contribute to this behavior. One possibility is that the underlying API that translates Pydantic models into the format OpenAI expects for tool descriptions might be stripping or misinterpreting dict types. Another is that OpenAI's own model architecture or its function-calling interface has a limitation in how it processes complex nested types like dictionaries when they are exposed as direct tool parameters. Workarounds, though not ideal, might involve flattening the dictionary structure into separate arguments if the number of keys is predictable and small. However, this defeats the purpose of using a dict for arbitrary data. A more robust workaround could be to represent the dictionary as a JSON string within a string argument. The model would then see a string argument, and you would need to parse the JSON string within your tool function. For example:
@openai_agent.tool_plain
def tool_w_dict_as_string(name: str, data_json: str) -> str:
"""A tool with a dict argument represented as a JSON string.
Args:
name: A name string
data_json: A JSON string representing arbitrary data
"""
import json
try:
data = json.loads(data_json)
return f"Got {name} with {data}"
except json.JSONDecodeError:
return "Error decoding JSON data."
This approach ensures the argument is visible to the model (as a string), and you handle the serialization/deserialization within your tool. While this works, it adds complexity and requires manual parsing, which Pydantic AI is designed to abstract away. Another avenue to explore is whether Pydantic AI might offer alternative ways to define tool schemas or specific configurations for different providers that could circumvent this issue. For instance, some libraries allow for explicit schema generation or customization, which might enable developers to force a more complete representation of dict types.
The Path Forward: Ensuring Comprehensive Tool Visibility
This issue with dict arguments being invisible to OpenAI agents in Pydantic AI highlights a critical area for improvement in the integration between powerful AI models and structured data. While the current workaround of using JSON strings is functional, it sacrifices some of the elegance and type safety that Pydantic is known for. The ideal solution would involve the Pydantic AI library or the OpenAI API itself to correctly interpret and expose dict types as object in tool schemas, just as Anthropic's model does. This would enable developers to leverage the full power of structured data within their AI agents without cumbersome workarounds. We encourage the developers of Pydantic AI and OpenAI to collaborate on resolving this discrepancy. Ensuring that all argument types are accurately represented in tool definitions is fundamental for building robust and reliable AI applications. By addressing this, we can unlock new possibilities for how AI agents interact with complex data, leading to more sophisticated and capable systems. The future of AI development hinges on seamless integration and accurate representation of data structures, and resolving this dict visibility issue is a significant step in that direction. Keep an eye on Pydantic AI updates and OpenAI's API documentation for potential improvements in this area!
Where to Learn More
For further insights into Pydantic AI and its capabilities, you can refer to the official Pydantic AI Documentation. To understand more about how LLMs interact with tools and function calling, the OpenAI API documentation on Function Calling is an invaluable resource.