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

# Structured Output

> Generate validated, structured data from your agents for seamless integration with databases, APIs, and downstream systems

Structured output enables your agents to return data in a predefined schema instead of free-form text. This is essential for integrating agents with databases, APIs, or any system that requires consistent data formats.

## Overview

When you enable structured output, your agent will:

1. Process the input normally
2. Extract information according to your schema
3. Return validated, typed data matching your specification
4. Ensure type safety and consistency

> [!NOTE]
>
> Structured output uses runtime schema generation and validation to ensure your agents return properly formatted data.

> [!WARNING]
>
> **Important**: Structured output is **NOT** included in the conversation history array. This means structured output from previous agents is only accessible through template variables in prompts (e.g., `{{ agent_name.output.field }}`) and will not appear in the `{{ history }}` or `{{ history[N] }}` variables.

## When to Use Structured Output

> **Data Extraction**
>
> Extract structured information from unstructured text (resumes, invoices, documents)

> **API Integration**
>
> Generate responses that match your API schema requirements

> **Database Inserts**
>
> Create records with validated fields for database operations

> **Multi-Agent Pipelines**
>
> Pass typed data between agents in complex workflows

## Basic Configuration

Add the `structured_output` field to your agent configuration:

```yaml theme={null}
agents:
  - name: "data_extractor"
    agent_type: "llm_agent"
    prompt_config:
      system_prompt: "Extract contact information from the provided text."
    
    structured_output:
      enabled: true
      schema:
        name:
          type: "str"
          description: "Full name of the person"
        email:
          type: "str"
          description: "Email address"
        phone:
          type: "str"
          description: "Phone number"
```

## Type Notation

Define field types using our type notation syntax. The common types are `str`, `int`, `float`, `bool`, `list[T]`, `dict[K, V]`, and their nullable forms `T | None` / `Optional[T]`:

```yaml theme={null}
schema:
  name:
    type: "str"
    description: "Person's full name"
  age:
    type: "int"
    description: "Age in years"
  skills:
    type: "list[str]"
    description: "List of technical skills"
  metadata:
    type: "dict[str, str]"
    description: "Additional metadata fields"
  middle_name:
    type: "str | None"
    description: "Middle name if available, null otherwise"
```

See [Type Notation](https://docs.artaios.ai/amas/advanced/type-notation.md) for the full reference — every supported scalar and collection form, nesting, coercion rules, and the nullability interaction between structured output fields and variables.

> [!NOTE]
>
> **Enum types are not currently supported.** Use `str` type with clear descriptions for fields that should have restricted values. Guide the LLM via your system prompt to output specific allowed values.

> [!WARNING]
>
> **Any field can come back `null`.** Every field is marked optional at the schema-building layer regardless of the type you declare, so an agent that omits a field — or answers `null` for a field you declared `str` — does not crash the run. This is deliberate: without it a single `null` would fail the whole generation call with *"Expected string, got null"* and take the agent turn down with it.
>
> Consequences to design for:
>
> * Treat every field as potentially absent in downstream prompts and conditional edges.
> * A `null` is **not** written into a non-nullable variable by `variable_assignments` — the assignment is skipped and the previous value kept. Declare the variable `T | None` if storing `null` is what you want. See [Type Notation](https://docs.artaios.ai/amas/advanced/type-notation.md).
> * Declaring the field itself `T | None` is good practice for documenting intent, but it is not what makes the parse succeed.

## Root Schemas

Normally a schema defines named fields and the agent returns a JSON object. Use the reserved field name `__root__` when you want the agent to return a **bare value** instead — most usefully a bare array:

```yaml theme={null}
agents:
  - name: "topic_extractor"
    agent_type: "llm_agent"
    prompt_config:
      system_prompt: "List the key topics discussed in the conversation."

    structured_output:
      schema:
        __root__:
          type: "list[str]"
          description: "Key topics mentioned, most significant first"
```

The agent's output is then the list itself — `["billing", "refunds"]` — not `{"topics": [...]}`.

> [!WARNING]
>
> `__root__` must be the **only** key in the schema. Mixing it with named fields fails validation:
>
> ```
> '__root__' must be the only key in the schema (got ['__root__', 'summary'])
>  - a root schema has no named fields alongside it
> ```

Any type notation works as the root type, not just lists:

```yaml theme={null}
# A bare list of objects
schema:
  __root__:
    type: "list[dict[str, str]]"

# A bare scalar
schema:
  __root__:
    type: "float"
    description: "Confidence score between 0 and 1"
```

### Referencing a root schema downstream

A root-schema output is stored as a bare list or value rather than a field map, so there is no field name to address. Reference the whole output:

```yaml theme={null}
# ✅ The entire root value
variable_assignments:
  key_topics: "topic_extractor.output"

# ❌ No named fields exist on a root schema
variable_assignments:
  key_topics: "topic_extractor.output.topics"
```

The same applies in prompts — use `{{ topic_extractor.output }}`, not `{{ topic_extractor.output.field }}`.

> [!TIP]
>
> Prefer a named field when you are unsure. A root schema buys you a cleaner payload, but it gives up per-field addressing in prompts, variable assignments and conditional edges.

## Quick Start Example

Extract contact information from text:

```yaml theme={null}
agents:
  - name: "contact_extractor"
    agent_type: "llm_agent"
    prompt_config:
      system_prompt: "Extract contact information from the provided text."
    
    structured_output:
      enabled: true
      schema:
        name:
          type: "str"
          description: "Person's full name"
        
        email:
          type: "str"
          description: "Email address"
        
        phone:
          type: "str"
          description: "Phone number"
        
        company:
          type: "str"
          description: "Company name"
```

**Input:**

```
John Smith from Acme Corp can be reached at john@acme.com or 555-1234
```

**Output:**

```json theme={null}
{
  "name": "John Smith",
  "email": "john@acme.com",
  "phone": "555-1234",
  "company": "Acme Corp"
}
```

## Multi-Agent Data Passing

Use structured output to pass typed data between agents in workflows. Downstream agents can access structured outputs from previous agents using template variables in their system prompts.

### Accessing Structured Output in Prompts

Use the `{{ agent_name.output.field }}` syntax to inject structured output into agent prompts:

```yaml theme={null}
agents:
  - name: "data_extractor"
    agent_type: "llm_agent"
    structured_output:
      enabled: true
      schema:
        company_name:
          type: "str"
          description: "Company name"
        revenue:
          type: "float"
          description: "Annual revenue in millions"
  
  - name: "analyzer"
    agent_type: "llm_agent"
    prompt_config:
      system_prompt: |
        Analyze the company data:
        - Company: {{ data_extractor.output.company_name }}
        - Revenue: ${{ data_extractor.output.revenue }}M
        
        Provide insights on market position and growth potential.
```

**Supported template variables:**

* `{{ agent_name.output.field }}` — Access specific field from structured output
* `{{ agent_name.output.nested.field }}` — Access nested fields (dot-separated path)
* `{{ agent_name.output }}` — Access entire structured output as JSON string
* `{{ variables.name }}` — Access custom variables (see [Variables System](https://docs.artaios.ai/amas/advanced/variables-system.md))
* `{{ user_input }}` — Latest user message
* `{{ history[N] }}` — Last N messages from conversation (structured output NOT included)

> [!WARNING]
>
> **Critical**: Structured output is excluded from history variables. Use the specific `{{ agent_name.output.field }}` template variables to access structured data from previous agents.

> [!TIP]
>
> **Complex data types** (lists, dicts) are automatically serialized to JSON when injected into prompts.

### Multi-Agent Example

Pass structured data between agents:

```yaml theme={null}
name: "Data Pipeline"
architecture: "workflow"

agents:
  - name: "extractor"
    agent_type: "llm_agent"
    prompt_config:
      system_prompt: "Extract key metrics from the report."
    
    structured_output:
      enabled: true
      schema:
        revenue:
          type: "float"
          description: "Annual revenue in millions"
        growth_rate:
          type: "float"
          description: "Year-over-year growth percentage"
  
  - name: "analyzer"
    agent_type: "llm_agent"
    prompt_config:
      system_prompt: |
        Analyze the company metrics:
        Revenue: ${{ extractor.output.revenue }}M
        Growth: {{ extractor.output.growth_rate }}%
        
        Provide insights on performance and recommendations.

edges:
  - from: "__start__"
    to: "extractor"
  - from: "extractor"
    to: "analyzer"
  - from: "analyzer"
    to: "__end__"

entry_point: "extractor"
```

> [!TIP]
>
> **Template variables**: Use `{{ agent_name.output.field }}` to access structured output from previous agents

## Best Practices

> [!TIP]
>
> **Clear Descriptions**: Always provide detailed descriptions for each field. The agent uses these to understand what to extract.

> [!TIP]
>
> **Appropriate Types**: Choose the most specific type possible. For fields with restricted values, use `str` type and specify allowed values in the description and system prompt.

> [!TIP]
>
> **System Prompt Alignment**: Ensure your system prompt explicitly mentions the structured output requirements.

> [!WARNING]
>
> **Type Validation**: All output is validated against your schema. Invalid data will cause errors. Test your schemas thoroughly.

> [!TIP]
>
> **Nested Structures**: For complex nested objects, consider breaking them into multiple agents or using `dict` types with clear descriptions.

## Configuration Reference

### Schema Structure

The `structured_output` field has the following structure:

**structured\_output** `object`

Configuration for structured output generation

**Properties**

**enabled** `boolean`

Enable or disable structured output for this agent

When `true`, the agent will return data matching the defined schema instead of free-form text.

**Default**: `true`

**schema** `object` *required*

Schema definition mapping field names to field specifications

Each key is a field name, and each value is an object with `type` and optional `description`.

**Required when** `enabled: true`

The single reserved key `__root__` makes the agent return a bare value instead of an object — see [Root Schemas](#root-schemas).

> [!NOTE]
>
> `enabled` defaults to `true`, so providing a `schema` is enough to turn structured output on. Set `enabled: false` to keep a schema in the config while temporarily reverting the agent to free-form text.
>
> No other keys are accepted under `structured_output` — a typo fails validation rather than being ignored.

### Field Specification

Each field in the schema must have:

**type** `string` *required*

Type notation for the field. Common types: `str`, `int`, `float`, `bool`, `list[T]`, `dict[K, V]`, `T | None`, `Optional[T]`, `Any`.

See [Type Notation](https://docs.artaios.ai/amas/advanced/type-notation.md) for the full reference.

**description** `string`

Human-readable description of what this field represents (optional but strongly recommended).

The agent uses this description to understand what data to extract. Be specific and clear. Fields without descriptions may produce less accurate results.

### Agent Type Support

> [!TIP]
>
> **llm\_agent**: ✅ Fully supported - LLM agents can generate structured output for any schema

> [!TIP]
>
> **react\_agent**: ✅ Fully supported - ReAct agents generate structured output after completing their tool-calling iterations

> [!WARNING]
>
> **supervisor**: ❌ Not supported - Supervisor agents focus on coordination and do not produce structured output

## Validation and Error Handling

The system automatically validates structured output:

> [!TIP]
>
> **Type Checking**: Values must match the declared type

> [!TIP]
>
> **Required Fields**: All fields in the schema must be present in the output

> [!TIP]
>
> **Format Validation**: Basic format checking for strings, numbers, etc.

### Common Errors

| Error | Cause | Solution |
| - | - | - |
| `Invalid type notation` | Unsupported type syntax | Check [Type Notation](https://docs.artaios.ai/amas/advanced/type-notation.md) |
| `Missing required field` | Agent didn't provide all fields | Improve system prompt to mention all fields |
| `Type validation failed` | Value doesn't match expected type | Ensure agent outputs correct type for the field |
| `Type mismatch` | Wrong data type | Ensure agent outputs correct type (e.g., number not string) |

### Example Error Response

If validation fails, you'll receive a detailed error:

```json theme={null}
{
  "error": {
    "type": "StructuredOutputValidationError",
    "message": "Field 'age' has invalid value 'thirty'. Expected type: int",
    "field": "age",
    "expected": "int",
    "received": "thirty"
  }
}
```

## Reading structured output over REST

`POST /api/v1/runtime/{session_id}/execute/` has no dedicated field for the validated object. It arrives as **JSON text in `content`** of an `assistant` entry in the top-level `messages` array (there is no `response.content` wrapper). Parse `content` yourself:

```python theme={null}
data = response.json()
msg = next(
    m for m in reversed(data["messages"])
    if m["role"] == "assistant" and m.get("agent_name") == "researcher"
)
result = json.loads(msg["content"])  # the validated object
```

Schematic response:

```json theme={null}
{
  "status": "completed",
  "messages": [
    {
      "role": "assistant",
      "agent_name": "researcher",
      "content": "{\n  \"field_a\": \"value\",\n  \"field_b\": 42\n}",
      "metadata": {}
    }
  ]
}
```

* Same shape inside `architecture: workflow`; every agent's messages land in one flat array, so filter by `agent_name`.
* A `__root__` schema yields the bare JSON value as the string.
* `show_output_to_user: false` on the agent removes its output from `messages`. Keep the default `true`.
* Message `metadata` is empty over REST; do not look for a `structured_output` flag.
* See [Execute Message](https://docs.artaios.ai/api-reference/runtime/execute.md#structured-output).

## Variable Assignments

Structured output fields can be automatically mapped to system variables using `variable_assignments`. This creates a data pipeline: structured output → variables → downstream prompt substitution.

```yaml theme={null}
variables:
  customer_name:
    type: "str"
    description: "Extracted customer name"
  priority:
    type: "str"
    default: "medium"

agents:
  - name: "extractor"
    agent_type: "llm_agent"
    structured_output:
      enabled: true
      schema:
        name:
          type: "str"
          description: "Customer name"
        urgency:
          type: "str"
          description: "Priority level"
    variable_assignments:
      customer_name: "extractor.output.name"
      priority: "extractor.output.urgency"
  
  - name: "responder"
    agent_type: "llm_agent"
    prompt_config:
      system_prompt: |
        Customer: {{ variables.customer_name }}
        Priority: {{ variables.priority }}
        Respond appropriately.
```

Variable assignments also support static values and non-structured output. The system applies **automatic type coercion** during variable assignment, converting strings to types like integers, booleans, or JSON arrays to ensure they match the variable's defined `type`. See [Variables System](https://docs.artaios.ai/amas/advanced/variables-system.md) for details.

## Limitations

> [!NOTE]
>
> * Structured output is currently supported for `llm_agent` and `react_agent` types
> * Complex nested objects may require careful prompt engineering
> * Very large schemas (>20 fields) may impact performance
> * Every field is effectively optional — any field can return `null` regardless of its declared type
> * Output must be valid JSON
> * Enum types are not supported
> * A `__root__` schema gives up per-field addressing in prompts, variable assignments and conditional edges

## Next Steps

> **[Conditional Edges](https://docs.artaios.ai/amas/advanced/conditional-edges.md)**
>
> Learn how to route agents based on structured output values


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.