Skip to main content

Someone wanted to learn this too, so Grasp built them a personal learning path.

You can keep exploring this one or create your own

Structured Output Prompt Engineering

Introduction

In our last lesson, we focused on crafting system prompts to set the overall strategy for an LLM's behavior—defining its persona, rules, and goals. We briefly noted that one of the most powerful uses for these instructions is to enforce a specific output format. Today, we will expand on that idea and take a deep dive into the techniques for reliably generating structured data, a cornerstone of building robust, production-grade AI applications.

Your learning outcome for this lesson is to be able to engineer prompts and API calls that reliably produce structured outputs like JSON or XML. This skill is the critical bridge between the probabilistic, text-based world of LLMs (often called "Software 3.0") and the deterministic, data-driven world of traditional software systems. Mastering this allows you to treat an LLM not just as a chatbot, but as a predictable, parsable component in a larger software architecture.

This diagram illustrates how structured outputs turn the messy, uncontrolled text from an LLM into a predictable format that traditional software can easily parse, validate, and use for downstream processing.

Why and How of Structured Outputs

Before we dive into specific techniques, let's solidify the "why". Your experience in software development has undoubtedly shown you that applications thrive on predictable data formats. Whether you're saving data to a database, passing it to a frontend UI component, or making a call to another service's API, you need data that conforms to a known schema. Relying on an LLM's free-form text and trying to parse it with regular expressions is a brittle and error-prone strategy. Structured outputs solve this by compelling the model to generate data in the exact format your application expects.

The guide to structured outputs and function calling with ...

This article from Agenta provides a great summary of the core problem and the use cases that structured outputs unlock.

Please read the introduction and the section "Why You Need Structured Outputs". This will reinforce the importance of this technique for data storage, UI display, and function calling.

There is a spectrum of methods to achieve this, ranging from simple suggestions to strict enforcement. We can broadly categorize them as:

  1. Prompt-Based Structuring: Instructing the model within the prompt to follow a specific format (e.g., using XML or JSON tags). This relies on the model's ability to follow instructions and is more of a strong suggestion than a guarantee.
  2. API-Native Enforcement: Using dedicated features of an LLM provider's API to force the output to conform to a provided schema. This is the most reliable method as it constrains the model during the generation process itself.

The core idea behind the most robust methods is providing the LLM with a schema that acts as a blueprint for its output.

As shown here, a JSON Schema is provided to the LLM, which then uses it as a guide for "constrained generation," ensuring the output JSONs match the required structure.

Approach 1: Structuring the Prompt Itself

The first and most universally applicable technique is to embed structure directly into your prompt using formats the model is familiar with, like XML or JSON. Since LLMs are trained on vast amounts of code and markup, they are particularly adept at recognizing and replicating these patterns.

This method involves wrapping your instructions, context, and examples in tags. For instance, you might use <instruction>, <context>, and <example> tags. This helps the model disambiguate the different parts of your prompt and often leads to more consistent outputs. This is a powerful technique, especially with models like Anthropic's Claude, which are explicitly designed to respond well to XML-tagged prompts.

Structured Prompting Techniques: XML & JSON ...

This guide from CodeConductor provides an excellent overview of using XML and JSON within your prompts to guide model behavior. It offers clear examples for various tasks.

Please read the following sections to understand this technique: Start with the overview. Then, read about the benefits and examples of JSON prompting in the sections Why JSON Prompting is Used and Examples of JSON Prompting. Finally, review the parallel approach for XML in the sections Why XML Prompting is Used? and Examples of XML Prompting. Pay close attention to how the examples separate instructions from data, which reduces ambiguity for the model.

While this method improves reliability over a simple natural language request, it's still not a foolproof guarantee. The model can still make mistakes, forget a tag, or add extraneous text. For true production reliability, we turn to API-native features.

Approach 2: API-Native Schema Enforcement

The most robust way to get structured output is to use API features specifically designed for this purpose. Modern LLM providers like OpenAI allow you to pass a schema along with your request, and they guarantee that the model's output will conform to it.

This is a significant leap from the older "JSON mode," which only guaranteed valid JSON syntax but not its structure (e.g., key names, value types). With schema enforcement, you get guarantees on the entire data contract.

OpenAI Structured Output - All You Need to Know

This video from Dave Ebbelaar provides an excellent, concise overview of OpenAI's new structured output capabilities and clarifies when to use them versus other features like function calling.

Please watch these two introductory segments: First, get the context on the new update and how it improves on older methods from the introduction. Next, understand the decision-making process of when to use structured output versus function calling from the guidelines.

As the video explains, there are two primary ways to specify the schema: providing a raw JSON Schema or, the recommended approach for Python developers, using a Pydantic model. Given your background, the elegance and power of using Pydantic will be immediately apparent. You define a Python class, and it serves as both the schema for the LLM and the data validation model for the response your application receives.

OpenAI Structured Output - All You Need to Know

Let's continue with the same video to see a practical, code-based demonstration of both methods. Pay close attention to the difference in complexity and readability.

Watch these segments to see the implementation details: First, see how to provide a raw JSON schema. Note the verbosity. Next, watch the far superior approach using Pydantic models. Notice how intuitive it is to define and extend the data structure directly in Python. Finally, review the key takeaways at the end.

The Pydantic approach is a clear winner. It reduces boilerplate, keeps your data definitions in sync with your Python code, and returns a fully parsed and validated object, eliminating an entire step of manual JSON parsing and validation.

This capability isn't unique to OpenAI. Other major providers like Anthropic and Google offer similar functionalities, often integrated into their "tool use" or "function calling" APIs, where the schema you provide for a tool effectively becomes the schema for the structured output.

Under the Hood: How Constrained Generation Works

So, how does the API actually guarantee the output matches the schema? It's not just better prompting; it's a fundamental change to the generation process itself. Given your computer science background, you'll find the underlying mechanism fascinating, as it draws directly from formal language theory.

At each step of generating text, the LLM produces a probability distribution over its entire vocabulary of possible next tokens. In normal generation, it samples from this distribution. In constrained generation, this process is modified:

  1. The provided schema (e.g., from a Pydantic model) is converted into a formal grammar, often represented as a regular expression or a finite state machine (FSM).
  2. At each generation step, the FSM is used to determine the set of all valid next tokens that could possibly lead to a valid final output.
  3. The LLM's probability distribution is "masked" to set the probability of all invalid tokens to zero.
  4. The model then samples only from the remaining valid tokens.

This ensures, token by token, that the model cannot stray from the path of generating a schema-compliant output.

Structured Output from LLMs: Grammars, Regex, and State Machines

This video from the Efficient NLP channel offers a superb explanation of these concepts, connecting them to automata theory.

Please watch these sections carefully: This segment introduces the outlines library and shows how it can convert a schema into a regular expression to guide generation. The next part explains the core theory: how a regular expression corresponds to a Finite State Machine (FSM) and how that FSM is used to mask the LLM's output at each step. Finally, this section discusses the limitations of regular expressions for deeply nested structures (like complex JSON) and introduces the next level of the Chomsky hierarchy: context-free grammars and pushdown automata.

This is a powerful mechanism, but it comes with a crucial trade-off. By forcing the model down a specific structural path, you can sometimes reduce its accuracy on the actual content.

Structured Output from LLMs: Grammars, Regex, and State Machines

Let's watch one final, short clip from the same video that highlights an important research finding.

Watch from this point, where the presenter discusses a paper showing that stricter format constraints can sometimes lead to less accurate answers.

This implies that while you should enforce the structure you need, it's wise to be as flexible as the downstream application allows (e.g., accepting "Thursday" or "thursday") to give the model maximum freedom to generate the correct information.

Conclusion

In this lesson, we've moved from simply asking an LLM for formatted text to programmatically enforcing a data contract. This is a fundamental skill for any developer looking to integrate LLMs into reliable software systems.

Here are the essential takeaways:

  • Structured output is non-negotiable for most production applications, as it provides the predictability required for databases, APIs, and UIs.
  • You can guide the model by embedding structure (like XML tags) directly into your prompts, a useful and model-agnostic technique.
  • The most reliable method is API-native schema enforcement, which constrains the model's output at generation time.
  • For Python developers, using Pydantic models with APIs like OpenAI's is the state-of-the-art, combining readability, validation, and ease of use.
  • The underlying technology leverages concepts from formal language theory, using grammars and state machines to filter the LLM's token choices in real time.
  • There is a practical trade-off between the strictness of the format and the accuracy of the content; be only as restrictive as your application requires.

In our upcoming lessons, we will begin applying these foundational techniques. Starting with the next module on AI-assisted development, you will see how the ability to extract structured data from code, error messages, and natural language requests is the engine that powers advanced developer tools and agents.

Can't find a good explanation? Sign up and we'll make it for you

Sign up