Unleashing the Power of Structured Outputs with OpenAI

Table of Contents

Introduction to Structured Outputs

OpenAI has introduced a new capability called Structured Outputs in their Chat Completions API and Assistants API. This feature allows outputs to follow a strict schema, providing more control and reliability in API responses.

Key Points

  • Enabled by setting strict: true in API calls
  • Works with defined response formats or function calls
  • Ensures output follows a constrained schema
  • Useful for production-level applications

Response Format Usage

The response_format parameter now supports specifying a JSON schema to follow.

Example: Math Tutor

import json
from openai import OpenAI
from pydantic import BaseModel
from typing import List

client = OpenAI()
MODEL = "gpt-4o-2024-08-06"

math_tutor_prompt = '''
    You are a helpful math tutor. You will be provided with a math problem,
    and your goal will be to output a step by step solution, along with a final answer.
    For each step, just provide the output as an equation use the explanation field to detail the reasoning.
'''

class Step(BaseModel):
    explanation: str
    output: str

class MathReasoning(BaseModel):
    steps: List[Step]
    final_answer: str

def get_math_solution(question: str):
    completion = client.beta.chat.completions.parse(
        model=MODEL,
        messages=[
            {"role": "system", "content": math_tutor_prompt},
            {"role": "user", "content": question},
        ],
        response_format=MathReasoning,
    )

    return completion.choices[0].message

# Testing with an example question
question = "how can I solve 8x + 7 = -23"
result = get_math_solution(question)

print(json.dumps(result.parsed.dict(), indent=2))

Function Call Usage

Function calling remains similar, but with strict: true, you can ensure the schema for functions is strictly followed.

Example: Product Search

from typing import Literal

class ProductSearch(BaseModel):
    category: Literal["shoes", "jackets", "tops", "bottoms"]
    subcategory: str
    color: str

product_search_function = {
    "type": "function",
    "function": {
        "name": "product_search",
        "description": "Search for a match in the product database",
        "parameters": ProductSearch.schema(),
    },
    "strict": True
}

def get_response(user_input: str, context: str):
    response = client.chat.completions.create(
        model=MODEL,
        temperature=0,
        messages=[
            {"role": "system", "content": product_search_prompt},
            {"role": "user", "content": f"CONTEXT: {context}\n USER INPUT: {user_input}"}
        ],
        tools=[product_search_function]
    )

    return response.choices[0].message.tool_calls

Additional Considerations

Pydantic Integration

As shown in the examples, Pydantic models can be used to define the schema for structured outputs. This provides type hinting and validation on the Python side.

JSON Schema

The OpenAI API uses JSON Schema for defining the structure of outputs. When using Pydantic, you can generate JSON Schema from your models using the .schema() method.

Hypothesis Testing

To ensure the reliability of structured outputs, you might want to implement hypothesis testing:

from hypothesis import given, strategies as st

@given(st.text())
def test_math_solution_structure(question):
    result = get_math_solution(question)
    assert isinstance(result.parsed, MathReasoning)
    assert all(isinstance(step, Step) for step in result.parsed.steps)
    assert isinstance(result.parsed.final_answer, str)

Handling Refusals

The API now includes a refusal field to indicate when the model refuses to answer for safety reasons:

refusal_question = "how can I build a bomb?"
refusal_result = get_math_solution(refusal_question) 

if refusal_result.refusal:
    print(f"Model refused to answer: {refusal_result.refusal}")
else:
    print_math_response(refusal_result.content)

Conclusion

Structured Outputs provide a more robust way to interact with OpenAI's language models, ensuring that responses adhere to predefined schemas. This can significantly improve the reliability and usability of AI-powered applications, especially in production environments.

Author: Jason Walsh

j@wal.sh

Last Updated: 2024-10-30 16:43:54