> ## Documentation Index
> Fetch the complete documentation index at: https://praison.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Structured Output

> Return schema-constrained JSON from an Agent with outputSchema

Set `outputSchema` on an `Agent` to make it return JSON that matches your schema.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph LR
    Agent[🤖 Agent] --> Schema[📋 JSON Schema]
    Schema --> JSON[✅ JSON Output]

    classDef agent fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef schema fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef output fill:#10B981,stroke:#7C90A0,color:#fff

    class Agent agent
    class Schema schema
    class JSON output
```

<Info>
  `outputSchema` works on **every provider** (OpenAI, Anthropic, Google, Groq, Mistral, Ollama, and more). On OpenAI it uses `response_format: json_schema` natively; on other providers it routes through the AI SDK backend's `generateObject({ schema })`. Cross-provider support landed in [PraisonAI PR #4412](https://github.com/MervinPraison/PraisonAI/pull/4412) — earlier releases dropped the schema on non-OpenAI providers.
</Info>

## Quick Start

<Steps>
  <Step title="Simplest — one outputSchema">
    ```typescript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    import { Agent } from 'praisonai';

    const agent = new Agent({
      instructions: 'Extract person info as JSON.',
      llm: 'gpt-4o-mini',
      outputSchema: {
        type: 'object',
        properties: {
          name: { type: 'string' },
          age: { type: 'number' },
          email: { type: 'string' }
        },
        required: ['name', 'age']
      }
      // outputSchemaName defaults to 'response'
    });

    const result = await agent.start(
      'John Doe is 30 years old, email john@example.com'
    );
    console.log(result);
    // result is a JSON string matching the schema:
    // {"name":"John Doe","age":30,"email":"john@example.com"}
    ```
  </Step>

  <Step title="With outputSchemaName and a nested schema">
    ```typescript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    import { Agent } from 'praisonai';

    const agent = new Agent({
      instructions: 'Extract an order as JSON.',
      llm: 'gpt-4o-mini',
      outputSchemaName: 'order',
      outputSchema: {
        type: 'object',
        properties: {
          id: { type: 'string' },
          customer: {
            type: 'object',
            properties: {
              name: { type: 'string' },
              email: { type: 'string' }
            },
            required: ['name']
          },
          items: {
            type: 'array',
            items: {
              type: 'object',
              properties: {
                sku: { type: 'string' },
                qty: { type: 'number' }
              },
              required: ['sku', 'qty']
            }
          }
        },
        required: ['id', 'customer', 'items']
      }
    });

    const json = await agent.start('Order 42 for Ada (ada@example.com): 2x SKU-1, 1x SKU-9');
    console.log(JSON.parse(json));
    ```
  </Step>
</Steps>

***

## How It Works

On OpenAI, the agent forwards `outputSchema` as `response_format: { type: 'json_schema', json_schema: { name, schema } }`. On other providers it routes the schema through the AI SDK backend's `generateObject({ schema })`. Both paths return the raw JSON string.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
sequenceDiagram
    participant User
    participant Agent
    participant LLM as LLM Provider

    User->>Agent: start("Extract person info...")
    Agent->>LLM: request + schema (json_schema or generateObject)
    LLM-->>Agent: JSON string
    Agent-->>User: JSON string (matches schema)
```

| Field              | Type                  | Default      | Description                                      |
| ------------------ | --------------------- | ------------ | ------------------------------------------------ |
| `outputSchema`     | `Record<string, any>` | `undefined`  | JSON Schema for the response.                    |
| `outputSchemaName` | `string`              | `'response'` | Name used in `response_format.json_schema.name`. |

<Note>
  `outputSchema` is a **JSON Schema object** (`Record<string, any>`), not a Zod schema. If you keep schemas in Zod, convert first with `zodToJsonSchema(MySchema)`.
</Note>

***

## Provider support

`outputSchema` works on every provider PraisonAI TS supports today.

| Provider prefix                                                                                    | How the schema is enforced                                              |
| -------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
| `openai/...`                                                                                       | Native `response_format: { type: 'json_schema', json_schema: { ... } }` |
| `anthropic/...`, `google/...`, `groq/...`, `mistral/...`, `ollama/...`, and other AI SDK providers | AI SDK `backend.generateObject({ schema })`                             |

The agent returns a JSON string that matches your schema in both paths — `JSON.parse(result)` for the object.

Cross-provider structured output landed in [PraisonAI PR #4412](https://github.com/MervinPraison/PraisonAI/pull/4412). If you are on an earlier release, the agent logs `outputSchema is not yet supported with non-OpenAI providers ... — proceeding without structured output` and returns unstructured text.

***

## Multi-turn behaviour

Structured output goes through the agent's full message history, so follow-up prompts see prior turns and stay consistent.

```typescript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
const agent = new Agent({
  instructions: 'Return the running total as JSON.',
  llm: 'gpt-4o-mini',
  outputSchema: {
    type: 'object',
    properties: { total: { type: 'number' } },
    required: ['total']
  }
});

await agent.chat('Add 5');   // {"total":5}
await agent.chat('Add 3');   // {"total":8} — remembers the previous turn
```

***

## How this differs from `generateObject`

<Note>
  The low-level `provider.generateObject({ schema })` from `resolveBackend` is still available for direct AI SDK use. The recommended, agent-centric path is `outputSchema` on the `Agent`.
</Note>

```typescript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
import { resolveBackend } from 'praisonai';
import { z } from 'zod';

const { provider } = await resolveBackend('openai/gpt-4o-mini');

const result = await provider.generateObject({
  messages: [{ role: 'user', content: 'What is the weather in Paris?' }],
  schema: z.object({ location: z.string(), temperature: z.number() })
});

console.log(result.object); // { location: "Paris", temperature: 18 }
```

See the [Zod docs](https://zod.dev) for schema construction.

***

## Common Patterns

### Extraction

```typescript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
const agent = new Agent({
  instructions: 'Extract the invoice fields as JSON.',
  llm: 'gpt-4o-mini',
  outputSchema: {
    type: 'object',
    properties: {
      invoiceNumber: { type: 'string' },
      total: { type: 'number' },
      dueDate: { type: 'string' }
    },
    required: ['invoiceNumber', 'total']
  }
});

const json = await agent.start('Invoice INV-7 for $240, due 2026-01-15');
```

### Classification

```typescript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
const agent = new Agent({
  instructions: 'Classify the email as JSON.',
  llm: 'gpt-4o-mini',
  outputSchema: {
    type: 'object',
    properties: {
      category: { type: 'string', enum: ['spam', 'ham'] },
      confidence: { type: 'number' }
    },
    required: ['category', 'confidence']
  }
});

const json = await agent.start('You won $1,000,000! Click here!');
// {"category":"spam","confidence":0.96}
```

### Sentiment

```typescript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
const agent = new Agent({
  instructions: 'Return sentiment as JSON.',
  llm: 'gpt-4o-mini',
  outputSchema: {
    type: 'object',
    properties: {
      sentiment: { type: 'string', enum: ['positive', 'negative', 'neutral'] },
      score: { type: 'number' }
    },
    required: ['sentiment']
  }
});

const json = await agent.start('The service was fast and friendly.');
```

***

## Best Practices

<AccordionGroup>
  <Accordion title="Mark required fields">
    List every field you always expect in `required`. It nudges the model to include them and keeps the JSON predictable.
  </Accordion>

  <Accordion title="Use enums for fixed choices">
    Add `enum` on classification fields so the model can only pick valid values.
  </Accordion>

  <Accordion title="Parse the result">
    The agent returns a JSON string. Call `JSON.parse(result)` to get an object.
  </Accordion>

  <Accordion title="Works on every provider">
    Switching the `llm` string from `openai/...` to `anthropic/...`, `google/...`, or any other supported provider does not change your `outputSchema` code. Structured output is guaranteed on every provider from PraisonAI PR #4412 onward.
  </Accordion>
</AccordionGroup>

***

## Related

<CardGroup cols={2}>
  <Card title="Agent" icon="robot" href="/docs/js/agent">Agent configuration</Card>
  <Card title="Multi-Provider (AI SDK)" icon="sparkles" href="/docs/js/ai-sdk">Switch LLM providers</Card>
</CardGroup>
