Structured output

Trust: ★★★☆☆ (0.90) · 0 validations · developer_reference

Published: 2026-05-10 · Source: crawler_authoritative

Tình huống

Mastra Agent SDK guide for configuring agents to return structured data objects matching defined schemas instead of plain text responses.

Insight

Structured output enables agents to return typed data objects defined by schemas rather than natural language text. Supported schema formats include Standard JSON Schema libraries (Zod, Valibot, ArkType) and JSON Schema. Zod is recommended for TypeScript type inference and runtime validation. The structuredOutput option accepts a schema property that defines the expected output shape. Response data is accessed via response.object. Streaming structured output uses stream.fullStream for chunks with chunk.type === 'object-result' and stream.object after completion, while stream.textStream emits natural language text. The model property under structuredOutput invokes a separate internal agent to handle structuring when the main agent isn’t proficient at it, making two LLM calls. The jsonPromptInjection: true option injects schema into system prompts instead of using the provider’s response_format API parameter, which is required for Gemini 2.5 models when combining structured output with tools. Error handling supports errorStrategy values: strict (throws on validation failure, default), warn (logs warning and continues), and fallback (returns predefined fallbackValue). The prepareStep callback enables multi-step handling where step 0 uses tools and subsequent steps use structured output, useful for models that don’t support both features simultaneously. The useAgent: true flag alongside model allows the structuring model to access conversation history from threads.

Hành động

Define a schema using Zod, Valibot, ArkType, or JSON Schema, then pass it to the structuredOutput option in .generate() or .stream(). Access results via response.object. For streaming, iterate stream.fullStream checking for object-result chunks, then access stream.object after completion. For complex schemas or models that don’t support direct structured output, provide a model parameter under structuredOutput to use a secondary LLM agent. To combine tools with structured output on models that don’t support both, use jsonPromptInjection: true or prepareStep to handle them in separate steps. Configure errorStrategy to ‘strict’, ‘warn’, or ‘fallback’ to control validation failure behavior, providing fallbackValue when using ‘fallback’.

Kết quả

Returns a structured data object matching the defined schema, accessible via response.object for standard generation or stream.object after streaming completes.

Điều kiện áp dụng

Applies to Mastra agents using .generate() or .stream() methods. Gemini 2.5 models require jsonPromptInjection: true when combining structured output with tools. Some models don’t support combining tools and structured output; use prepareStep workaround.


Nội dung gốc (Original)

Structured output

Structured output lets an agent return an object that matches the shape defined by a schema instead of returning text. The schema tells the model what fields to produce, and the model ensures the final result fits that shape.

When to use structured output

Use structured output when you need an agent to return a data object rather than text. Having well defined fields can make it simpler to pull out the values you need for API calls, UI rendering, or application logic.

Define schemas

Agents can return structured data by defining the expected output with either Standard JSON Schema (Zod, Valibot, ArkType, etc.) or JSON Schema. Libraries like Zod are recommended because they provide TypeScript type inference and runtime validation, while JSON Schema is useful when you need a language agnostic format.

Zod:

Define the output shape using Zod:

import { z } from 'zod'
 
const response = await testAgent.generate('Help me plan my day.', {
  structuredOutput: {
    schema: z.array(
      z.object({
        name: z.string(),
        activities: z.array(z.string()),
      }),
    ),
  },
})
 
console.log(response.object)

Valibot:

Define the output shape using Valibot:

import * as v from 'valibot'
import { toStandardJsonSchema } from '@valibot/to-json-schema'
 
const response = await testAgent.generate('Help me plan my day.', {
  structuredOutput: {
    schema: toStandardJsonSchema(
      v.array(
        v.object({
          name: v.string(),
          activities: v.array(v.string()),
        }),
      ),
    ),
  },
})
 
console.log(response.object)

ArkType:

Define the output shape using ArkType:

import { type } from 'arktype'
 
const response = await testAgent.generate('Help me plan my day.', {
  structuredOutput: {
    schema: type({
      name: 'string',
      activities: 'string[]',
    }).array(),
  },
})
 
console.log(response.object)

JSON Schema:

You can also use JSON Schema to define your output structure:

const response = await testAgent.generate('Help me plan my day.', {
  structuredOutput: {
    schema: {
      type: 'array',
      items: {
        type: 'object',
        properties: {
          name: { type: 'string' },
          activities: {
            type: 'array',
            items: { type: 'string' },
          },
        },
        required: ['name', 'activities'],
      },
    },
  },
})
 
console.log(response.object)

Note: Visit .generate() for a full list of configuration options.

Example output: The response.object will contain the structured data as defined by the schema.

[
  {
    "name": "Morning Routine",
    "activities": ["Wake up at 7am", "Exercise", "Shower", "Breakfast"]
  },
  {
    "name": "Work",
    "activities": ["Check emails", "Team meeting", "Lunch break"]
  },
  {
    "name": "Evening",
    "activities": ["Dinner", "Relax", "Read a book", "Sleep by 10pm"]
  }
]

Stream structured output

Streaming also supports structured output. The final structured object is available on stream.fullStream and after the stream completes on stream.object. Text stream chunks are still emitted, but they contain natural language text rather than structured data.

import { z } from 'zod'
 
const stream = await testAgent.stream('Help me plan my day.', {
  structuredOutput: {
    schema: z.array(
      z.object({
        name: z.string(),
        activities: z.array(z.string()),
      }),
    ),
  },
})
 
for await (const chunk of stream.fullStream) {
  if (chunk.type === 'object-result') {
    console.log('\n', JSON.stringify(chunk, null, 2))
  }
  process.stdout.write(JSON.stringify(chunk))
}
 
console.log(await stream.object)
 
for await (const chunk of stream.textStream) {
  process.stdout.write(chunk)
}

Structuring agent

When your main agent isn’t proficient at creating structured output you can provide a model to structuredOutput. In this case, Mastra uses a second agent under the hood to extract structured data from the main agent’s natural language response. This makes two LLM calls, one to generate the response and another to turn that response into the structured object, which adds some latency and cost but can improve accuracy for complex structuring tasks.

import { z } from 'zod'
 
const response = await testAgent.generate('Analyze the TypeScript programming language.', {
  structuredOutput: {
    schema: z.object({
      overview: z.string(),
      strengths: z.array(z.string()),
      weaknesses: z.array(z.string()),
      useCases: z.array(
        z.object({
          scenario: z.string(),
          reasoning: z.string(),
        }),
      ),
      comparison: z.object({
        similarTo: z.array(z.string()),
        differentiators: z.array(z.string()),
      }),
    }),
    model: 'openai/gpt-5.4',
  },
})
 
console.log(response.object)

Combine tools and structured output

When an agent has both tools and structured output configured, some models may not support using both features together. This is a limitation of the underlying model APIs, not Mastra itself.

If your tools aren’t being called when structured output is enabled, or you receive an error when combining both features, try one of the workarounds below.

Workaround options

When your model doesn’t support tools and structured output together, you have three options:

  1. Use jsonPromptInjection: true - Injects the schema into the prompt instead of using the API’s response_format parameter
  2. Use a separate structuring model - Pass a model to structuredOutput to use a second LLM for structuring
  3. Use prepareStep - Handle tools and structured output in separate steps

Each approach is detailed in the sections below.

LLM structured output support

Structured output support varies across LLMs due to differences in their APIs. The sections below cover workarounds for models that don’t fully support structured output or combining it with tools.

jsonPromptInjection

By default, Mastra passes the schema to the model provider using the response_format API parameter. Most model providers have built-in support for this, which reliably enforces the schema.

If your model provider doesn’t support response_format, you’ll get an error from the API. When this happens, set jsonPromptInjection: true. This adds the schema to the system prompt instead, instructing the model to output JSON. This is less reliable than the API parameter approach.

import { z } from 'zod'
 
const response = await testAgent.generate('Help me plan my day.', {
  structuredOutput: {
    schema: z.array(
      z.object({
        name: z.string(),
        activities: z.array(z.string()),
      }),
    ),
    jsonPromptInjection: true,
  },
})
 
console.log(response.object)

Gemini 2.5 with tools: Gemini 2.5 models don’t support combining response_format (structured output) with function calling (tools) in the same API call. If your agent has tools and you’re using structuredOutput with a Gemini 2.5 model, you must set jsonPromptInjection: true to avoid the error Function calling with a response mime type: 'application/json' is unsupported.

const response = await agentWithTools.generate('Your prompt', {
  structuredOutput: {
    schema: yourSchema,
    jsonPromptInjection: true, // Required for Gemini 2.5 when tools are present
  },
})

Use a separate structuring model

When model is provided to the structuredOutput property, Mastra uses a separate internal agent to handle the structured output. The main agent will handle all of the steps (including tool calling) and the structured output model will handle only the generation of structured output.

const response = await testAgent.generate('Tell me about TypeScript.', {
  structuredOutput: {
    schema: yourSchema,
    model: 'openai/gpt-5.4',
  },
})

If you want that structuring model to also see the current conversation history, set useAgent: true alongside model. Mastra will reuse the parent agent with the separate structuring model and attach read-only memory context when a thread is available.

const response = await testAgent.generate('Return my profile as structured data.', {
  memory: {
    thread: 'thread-123',
    resource: 'user-123',
  },
  structuredOutput: {
    schema: z.object({
      favoriteColor: z.string(),
      hometown: z.string(),
      petName: z.string(),
    }),
    model: 'openai/gpt-5.4',
    useAgent: true,
  },
})

Leave useAgent unset if you want the separate structuring model to work only from the current response and not inherit prior conversation memory.

Multi-step approach with prepareStep

For models that don’t support tools and structured outputs together, you can use prepareStep to handle them in separate steps.

const result = await agent.stream('weather in vancouver?', {
  prepareStep: async ({ stepNumber }) => {
    if (stepNumber === 0) {
      return {
        model: 'openai/gpt-5.4',
        tools: {
          weatherTool,
        },
        toolChoice: 'required',
      }
    }
    return {
      model: 'openai/gpt-5.4',
      tools: undefined,
      structuredOutput: {
        schema: z.object({
          temperature: z.number(),
          humidity: z.number(),
          windSpeed: z.number(),
        }),
      },
    }
  },
})

Handle errors

When schema validation fails, you can control how errors are handled using errorStrategy. The default strict strategy throws an error, while warn logs a warning and continues. The fallback strategy returns the values provided using fallbackValue.

const response = await testAgent.generate('Tell me about TypeScript.', {
  structuredOutput: {
    schema: z.object({
      summary: z.string(),
      keyFeatures: z.array(z.string()),
    }),
    errorStrategy: 'fallback',
    fallbackValue: {
      summary: 'TypeScript is a typed superset of JavaScript',
      keyFeatures: ['Static typing', 'Compiles to JavaScript', 'Better tooling'],
    },
  },
})
 
console.log(response.object)

Liên kết

Xem thêm: