Skip to content

Spec-Driven Design ​

An agent that calls a tool built by writing whatever shape of data seemed reasonable at the time works fine right up until someone else on your team builds a second tool the same way, with a slightly different shape. Contract-first design, defining the interface before the implementation, is how you keep an agent's tools honest as a system grows past the one you can hold in your head.

What you'll learn

  • The real cost of JSON/YAML parsing at scale (the "serialization tax")
  • Pydantic v2's Rust core and why contract-first design pays off
  • OpenAPI vs. JSON Schema vs. Protobuf vs. GraphQL, compared head-to-head

How it works: what a contract actually buys you ​

In high-performance agentic systems, the transition from Unstructured LLM Output to Structured System State is not free. Every validation layer adds a physical latency cost to your execution pipeline.

The Serialization Cost (YAML vs. JSON) ​

  • YAML: Human-readable and preferred for "Agent Context." However, YAML parsing in Python (via PyYAML) is significantly slower than JSON parsing due to its complex specification and lack of a native C/Rust-optimized parser in the standard library.
  • JSON: The "Machine Standard." When an agent executes a tool, we prefer JSON for the final payload to minimize In-Transit Latency and take advantage of rapid simdjson or ujson implementations.

Schema Parsing Latency ​

When an agent reads a 5,000-line OpenAPI YAML file, it consumes tokens and increases Prompt Processing Latency.

  • The Bottleneck: As your API suite grows, "In-Context Specs" can become a primary bottleneck. Production architectures use Spec Indexing (treating API endpoints like RAG documents) or Dynamic Schema Pruning to only show the agent the endpoints relevant to the current task.

Options & when to use each ​

IDL StandardOptimizationAgent CompatibilitySerialization OverheadPrimary Production Bottleneck
OpenAPI 3.0Ecosystem ReachHighest (Native Support)Moderate (YAML/JSON)Large spec files bloat context windows.
JSON SchemaData ValidationHigh (Structured Output)Low (Direct JSON)Lacks "Action" semantics (Paths/Methods).
ProtobufNetwork LatencyLow (Needs JSON Proxy)Minimal (Binary)Agents cannot natively "read" binary specs.
GraphQLData DensityModerate (Requires Tools)High (Introspection)High LLM hallucination rate on complex schemas.

Build it ​

Pattern: Pydantic v2 (The Rust Core) ​

In Lab 2, we utilize Pydantic v2. Unlike standard Python libraries, Pydantic v2 is built on pydantic-core, a validation engine written in Rust.

  1. Compiled Logic: Schema validation logic is compiled into machine code rather than interpreted Python bytecode.
  2. Zero-Copy Coercion: It safely converts types (e.g., str ➡️ datetime) with minimal memory allocation.
  3. Rationale: This ensures that even if an LLM provides a slightly malformed string, your agent's "Safe Boundary" can correct it or reject it in <1ms.

Pattern: Contract-First Injection ​

In Lab 3, we feed the Raw YAML to Claude.

  • Rationale: By providing the entire OpenAPI spec, we leverage Claude's ability to reason over long-context documentation. This eliminates the need for manual "Action Mappings" and allows the agent to self-discover new features as soon as the YAML file is updated.

What goes wrong ​

Failure ModeError/Log SignatureRoot CauseCode-Level Mitigation
Schema DriftHTTP 422 Unprocessable EntityCode changed but the YAML spec was not updated.Implement CI/CD checks that generate YAML from Pydantic.
Ambiguous OperationOperationId not foundThe LLM "guessed" a method name that doesn't exist.Use strict Enum mapping for operationId in the prompt.
Validation RecursionRecursionError: maximum depthRecursive Pydantic models (e.g. A ➡️ B ➡️ A) without depth limits.Use ForwardRef and explicit max_length constraints.
Payload BloatPrompt length exceededSpec file is too large for the model's active window.Implement "Spec Pruning" based on task classification.

Confirm it worked ​

When executing the labs in your Linux environment, monitor these telemetry signals:

  1. Validation Trace: Intentionally pass an invalid string to your BookMeetingTool.
    • Observation: Watch the console for pydantic_core._pydantic_core.ValidationError. Note the highly specific error map detailing exactly which field failed and why.
  2. YAML vs. JSON Tokens: Run the agent with a YAML spec, then run it with a JSON-minified version of the same spec.
    • Observation: Check the Anthropic token usage logs. You will see that YAML consumes significantly more tokens due to whitespace and structural markers.
  3. Context Loading: Use time python spec_agent.py.
    • Observation: Note the "Real Time" vs "User Time." Most of the delay is "Network Wait" (IO-bound), the same network overhead covered in Calling AI APIs Reliably.

Next Step: proceed to Deploying Your Agent to get this running reliably in production.