This week, OpenAI released Structured Outputs, a significant upgrade to the JSON mode that allows us to define response formats more strictly. If you’ve wrestled with unpredictable JSON responses, you know how much time this will save us!
But here’s the real gem: Structured Outputs let you define prompts
for each specific output attribute—an incredible feature that’s not getting enough attention. Let me show you why this is a game-changer.
Take a look at this slightly modified math reasoning example from the OpenAI Cookbook:
import json
from openai import OpenAI
client = OpenAI()
from pydantic import BaseModel, Field
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 MathReasoning(BaseModel):
class Step(BaseModel):
explanation: str
output: str
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
question = "how can I solve 8x + 7 = -23"
result = get_math_solution(question).parsed
print(result.steps[0].explanation + "\n" + result.steps[0].output)
This example shows how Structured Outputs can enforce a specific step-by-step format. The output?
Start by isolating the term with the variable by subtracting 7 from both sides of the equation.
8x + 7 - 7 = -23 - 7
Cool, right? But it gets better. With the ability to add descriptions for each output attribute, you can tailor responses even more finely. For example, let’s make the explanation sound like a pirate and the output in Polish notation:
class MathReasoning(BaseModel):
class Step(BaseModel):
explanation: str = Field(..., description="The step's explanation, but said like a pirate would say it")
output: str = Field(..., description="The step's output, but in Polish notation")
steps: list[Step]
final_answer: str
Run the code, and you’ll get:
First, matey, we be movin’ that 7 to the other side by subtractin’ it from both sides. Aye!
- = (+ (-23) 7) (+ 7 (* 8 x))
Previously, you could add this information to your system prompt, but attribute-specific prompting lets you fine-tune the output even better. Try changing the explanation to Spanish and the output to reverse Polish notation:
class MathReasoning(BaseModel):
class Step(BaseModel):
explanation: str = Field(..., description="The step's explanation, but said in Spanish")
output: str = Field(..., description="The step's output, but in reverse Polish notation")
steps: list[Step]
final_answer: str
And you’ll see:
Primero, restamos 7 de ambos lados de la ecuación para aislar el término con x.
8x - 23 7 -
That’s all for today! Hope you find this feature as powerful and fun as I do!