> For the complete documentation index, see [llms.txt](https://docs.neuron-ai.dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.neuron-ai.dev/neuron-v3-es/agente/structured-output.md).

# Salida estructurada

{% hint style="info" %}

### PRERREQUISITOS

Esta guía supone que ya estás familiarizado con los siguientes conceptos:

* [Agente](/neuron-v3-es/agente/agent.md)
* [Llamada de herramienta y función](/neuron-v3-es/agente/tools.md)
  {% endhint %}

Hay muchos casos de uso en los que necesitamos que los agentes entiendan el lenguaje natural, pero produzcan una *formato estructurado*. Un caso de uso común es extraer datos de texto para insertarlos en una base de datos o usarlos con algún otro sistema posterior. Esta guía cubre cómo Neuron permite imponer salidas estructuradas desde el agente.

<figure><img src="/files/6cdce273e2fe9df64ffeacd2f38f30b38c47dc71" alt=""><figcaption></figcaption></figure>

{% embed url="<https://www.youtube.com/watch?v=T8PM-t_AQ-c>" %}

### Cómo usar la salida estructurada

El concepto central es que la estructura de salida de las respuestas del LLM necesita representarse de alguna manera. El esquema contra el que Neuron valida se define mediante anotaciones de tipos de PHP. Básicamente, tienes que definir una clase con propiedades estrictamente tipadas:

```php
<?php

namespace App\Neuron\Output;

use NeuronAI\StructuredOutput\SchemaProperty;

class Person 
{
    #[SchemaProperty(
        description: 'El nombre de usuario.', 
        required: true
    )]
    public string $name;
    
    #[SchemaProperty(
        description: 'Lo que al usuario le encanta comer.', 
        required: false
    )]
    public string $preference;
}
```

Neuron genera el esquema JSON correspondiente a partir del objeto PHP para instruir al modelo subyacente sobre el formato de datos que necesitas. Luego, el agente analiza la salida del LLM para extraer los datos y devuelve una instancia del objeto rellenada con los valores adecuados:

```php
use NeuronAI\Chat\Messages\UserMessage;

// Hablar con el agente solicitando la salida estructurada
$person = MyAgent::make()->structured(
    new UserMessage("¡Soy John y me gusta la pizza!"),
    Person::class
);

echo $person->name.' le gusta '.$person->preference;
// John le gusta la pizza
```

### Clase de salida predeterminada

También puedes encapsular el formato de salida dentro de la implementación del Agente, de modo que sea el formato de salida estándar del Agente. Siempre necesitas llamar al `structured()` método para exigir una salida estricta.

```php
use NeuronAI\Chat\Messages\UserMessage;

// Encapsular el formato de salida predeterminado 
class MyAgent extends Agent
{
    ...

    protected function getOutputClass(): string
    {
        return Person::class;
    }
}

// Usa siempre el método structured si quieres obtener salida estructurada
$person = MyAgent::make()
    ->structured(new UserMessage("Soy John y me gusta la pizza"));

echo $person->name.' le gusta '.$person->preference;
// John le gusta la pizza
```

### Controlar la generación de salida

Neuron requiere que definas dos capas de reglas para crear la clase de salida estructurada.

La primera es el `SchemaProperty` atributo que te permite controlar el esquema JSON enviado al LLM para entender el formato de datos requerido.

La segunda capa es la validación. Los atributos de validación garantizarán que los datos obtenidos de la respuesta del LLM sean coherentes con tus requisitos.

<figure><img src="/files/e62bf8a43f06a46c7a7849a3f25e975635856de5" alt=""><figcaption></figcaption></figure>

### SchemaProperty

El `SchemaProperty` atributo te permite definir los parámetros del esquema JSON de cada propiedad:

```php
<?php

namespace App\Neuron\Output;

use NeuronAI\StructuredOutput\SchemaProperty;

class Person 
{
    #[SchemaProperty(
        description: 'El nombre de usuario.',
        required: true,
        minLength: 3,
        maxLength: 255,
    )]
    public string $name;
    
    #[SchemaProperty(
        description: 'Lo que al usuario le encanta comer.', 
        required: false,
        min: 18,
        max: 64,
    )]
    public ?int $age = null;
}
```

### Clase anidada

Puedes construir estructuras de salida complejas usando otros objetos PHP como tipo de propiedad. Siguiendo el ejemplo de la `Person` clase, podemos añadir la `dirección` propiedad tipada como otra clase estructurada.

```php
<?php

namespace App\Neuron\Output;

use NeuronAI\StructuredOutput\Property;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\Valid;

class Person 
{
    #[SchemaProperty(
        description: 'El nombre de usuario.', 
        required: true
    )]
    #[NotBlank]
    public string $name;
    
    #[SchemaProperty(
        description: 'Lo que al usuario le encanta comer.', 
        required: true
    )]
    public string $preference;
    
    #[SchemaProperty(
        description: 'La dirección para completar la entrega.', 
        required: true
    )]
    public Address $address;
}
```

En el `Address` En esta definición requerimos solo las propiedades de calle y código postal, y permitimos que la ciudad esté vacía.

```php
<?php

namespace App\Neuron\Output;

use NeuronAI\StructuredOutput\SchemaProperty;
use NeuronAI\StructuredOutput\Validation\Rules\NotBlank;

class Address
{
    #[SchemaProperty(
        description: 'El nombre de la calle.', 
        required: true
    )]
    #[NotBlank]
    public string $street;

    #[SchemaProperty(
        description: 'El nombre de la ciudad.', 
        required: false
    )]
    public string $city;

    #[SchemaProperty(
        description: 'El código postal de la dirección.', 
        required: true
    )]
    #[NotBlank]
    public string $zip;
}
```

Ahora, cuando pidas al agente la salida estructurada, obtendrás de vuelta la instancia rellenada:

```php
use NeuronAI\Chat\Messages\UserMessage;
use NeuronAI\Observability\AgentMonitoring;

// Hablar con el agente solicitando la salida estructurada
$person = MyAgent::make()->structured(
    new UserMessage("¡Soy John y quiero una pizza en st. James Street 00560!"),
    Person::class
);

echo $person->name.' le gusta '.$person->preference.'. Dirección: '.$person->address->street;
// John le gusta la pizza. Dirección: st.James Street
```

## Array

Si declaras una propiedad como un array, Neuron asume que la lista de elementos es una lista de cadenas. Si quieres que el array contenga una lista de otros objetos estructurados, puedes especificarlo usando el `anyOf` argumento:

```php
<?php

namespace App\Neuron\Output;

use NeuronAI\StructuredOutput\SchemaProperty;
use NeuronAI\StructuredOutput\Validation\Rules\NotBlank;

class Person 
{
    #[SchemaProperty(
        description: 'El nombre de usuario.', 
        required: true
    )]
    #[NotBlank]
    public string $name;
    
    #[SchemaProperty(
        description: 'La lista de etiquetas para el perfil del usuario.', 
        required: true,
        anyOf: [Tag::class]
    )]
    public array $tags;
}
```

Y aquí está la implementación hipotética de la `Tag` clase con sus propias reglas de validación e información de propiedades:

```php
<?php

namespace App\Neuron\Output;

use NeuronAI\StructuredOutput\SchemaProperty;
use NeuronAI\StructuredOutput\Validation\Rules\NotBlank;

class Tag
{
    #[SchemaProperty(
        description: 'El nombre de la etiqueta', 
        required: true,
    )]
    #[NotBlank]
    public string $name;
}
```

#### Array con múltiples tipos

Como puedes notar en el ejemplo anterior, el argumento anyOf es un array. Neuron también admite la composición de arrays con múltiples tipos de objetos. Solo enumera los objetos estructurados que el array puede contener y Neuron incluirá todas sus especificaciones en el esquema JSON para el LLM.

```php
class Report
{
    #[SchemaProperty(
        description: 'El contenido del informe', 
        required: true,
        anyOf: [TextBlock::class, TableBlock::class, ImageBlock::class]
    )]
    public array $content;
}
```

## Máximo de reintentos

Dado que los LLM no son perfectamente deterministas, es obligatorio contar con un mecanismo de reintento si falta algo en la respuesta del LLM.

Por defecto, Neuron extrae y valida los datos de la respuesta del LLM y, si hay uno o más errores de validación, reintenta automáticamente la solicitud una sola vez más, informando al LLM de lo que salió mal y para qué propiedades.

Eventualmente puedes personalizar cuántas veces el agente debe reintentar para obtener una respuesta correcta del LLM:

```php
$person = MyAgent::make()->structured(
    messages: new UserMessage("Soy John y me gusta la pizza!"),
    class: Person::class,
    maxRetries: 3
);
```

Si trabajas con un LLM menos capaz, considera usar un número de reintentos que equilibre la probabilidad de obtener una respuesta válida y el consumo potencial de tokens.

Puedes desactivar los reintentos simplemente pasando cero. Será un intento de una sola vez:

```php
$person = MyAgent::make()->structured(
    messages: new UserMessage("Soy John y me gusta la pizza!"),
    class: Person::class,
    maxRetries: 0
);
```

## Monitorización y depuración

Muchas de las aplicaciones que construyas con Neuron contendrán varios pasos con múltiples invocaciones de llamadas a LLM. A medida que estas aplicaciones se vuelven cada vez más complejas, resulta crucial poder inspeccionar exactamente qué está ocurriendo dentro de tu sistema agéntico. La mejor manera de hacerlo es con [Inspector](https://inspector.dev/).

{% embed url="<https://docs.inspector.dev/guides/neuron-ai>" %}

<figure><img src="/files/cce3d2227b9344d72e488aa69ca54450140e8012" alt=""><figcaption></figcaption></figure>

Cada segmento aporta su propia información de depuración para seguir la ejecución del agente en tiempo real:

<figure><img src="/files/fd7bad0a4ddd7aeafb68ce3b0d96656094ff483f" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
Aprende cómo habilitar [**la observabilidad**](/neuron-v3-es/agente/observability.md) en la siguiente sección.
{% endhint %}

## Reglas de validación

Dado que los LLM son sistemas no deterministas y la coherencia de su salida puede verse muy influida por la calidad del contexto que tienen como entrada, y aún pueden alucinar, proporcionamos un conjunto de reglas de validación que puedes añadir a las propiedades de clases estructuradas para instruir a Neuron a verificar el conjunto final de datos generado por el LLM.

Las reglas de validación permiten a Neuron reenviar la solicitud de generación al LLM varias veces con un informe detallado de lo que estaba mal si una o más propiedades no son válidas, hasta que alcanza el [maxRetries](#max-retries) valor.

Si no defines ninguna regla de validación, los datos extraídos de la respuesta del LLM se rellenarán directamente en la clase de salida estructurada.

### #\[NotBlank]

La propiedad bajo validación no puede estar vacía. Acepta la `allowNull` indicador para tratar explícitamente el valor null como equivalente a vacío o no.

```php
namespace App\Neuron\Output;

use NeuronAI\StructuredOutput\Validation\Rules\NotBlank;

class Person 
{
    #[NotBlank(allowNull: false)]
    public string $name;
}
```

### #\[Length]

Determina si la longitud de una `cadena` coincide con los criterios dados:

```php
namespace App\Neuron\Output;

use NeuronAI\StructuredOutput\Validation\Rules\Length;

class Person 
{
    #[Length(min: 1, max: 10)]
    public string $name;
    
    #[Length(exactly: 5)]
    public string $zip_code;
}
```

### #\[WordsCount]

Determina si el número de palabras en una `cadena` coincide con los criterios dados:

```php
namespace App\Neuron\Output;

use NeuronAI\StructuredOutput\Validation\Rules\WordsCount;

class Person 
{
    #[WordsCount(exactly: 10)]
    public string $title;
    
    #[WordsCount(min: 1, max: 10)]
    public string $content;
}
```

### #\[Count]

Determina si el tamaño de un `array` coincide con los criterios dados:

```php
namespace App\Neuron\Output;

use NeuronAI\StructuredOutput\Validation\Rules\Count;

class Person 
{
    #[Count(min: 1, max: 3)]
    public array $dogs;
    
    #[Count(exactly: 1)]
    public array $children;
}
```

### #\[EqualTo] - #\[NotEqualTo]

Estas reglas tienen la misma estructura y significado, y aceptan un solo argumento para definir el valor con el que comparar. La propiedad bajo validación debe ser estrictamente igual (*#\[EqualTo]*) o diferente (*#\[NotEqualTo]*) que el valor de referencia:

```php
namespace App\Neuron\Output;

use NeuronAI\StructuredOutput\Validation\Rules\EqualTo;
use NeuronAI\StructuredOutput\Validation\Rules\NotEqualTo;

class Person 
{
    #[EqualTo(reference: 'Rome')]
    public string $city;
    
    #[NotEqualTo(reference: '00502')]
    public string $zip_code;
}
```

### #\[GreaterThan] - #\[GreaterThanEqual]

Estas reglas tienen la misma estructura y significado, y aceptan un solo argumento para definir el valor con el que comparar. La propiedad bajo validación debe ser estrictamente mayor (*#\[GreaterThan]*) o igual (*#\[GreaterThanEqual]*) que el valor de referencia:

```php
namespace App\Neuron\Output;

use NeuronAI\StructuredOutput\Validation\Rules\GreaterThan;
use NeuronAI\StructuredOutput\Validation\Rules\GreaterThanEqual;

class Person 
{
    #[GreaterThan(reference: 17)]
    public int $age;
    
    #[GreaterThanEqual(reference: 1)]
    public int $cars;
}
```

### #\[LowerThan] - #\[LowerThanEqual]

Estas reglas tienen la misma estructura y significado, y aceptan un solo argumento para definir el valor con el que comparar. La propiedad bajo validación debe ser estrictamente menor (*#\[LowerThan]*) o igual (*#\[LowerThanEqual]*) que el valor de referencia:

```php
namespace App\Neuron\Output;

use NeuronAI\StructuredOutput\Validation\Rules\LowerThan;
use NeuronAI\StructuredOutput\Validation\Rules\LowerThanEqual;

class Person 
{
    #[LowerThan(reference: 50)]
    public int $age;
    
    #[LowerThanEqual(reference: 1)]
    public int $cars;
}
```

### #\[OutOfRange]

Determina si un `número` está fuera del rango dado:

```php
namespace App\Neuron\Output;

use NeuronAI\StructuredOutput\Validation\Rules\InRange;

class Person 
{
    #[OutOfRange(min: 18, max: 35)]
    public int $age;
    
    // El argumento strict obliga a mantenerse estrictamente fuera de los límites del rango
    #[OutOfRange(min: 48, max: 54, strict: true)]
    public int $size;
}
```

### #\[IsFalse] - #\[IsTrue]

La propiedad bajo validación debe tener exactamente el valor booleano definido por la regla:

```php
namespace App\Neuron\Output;

use NeuronAI\StructuredOutput\Validation\Rules\IsFalse;
use NeuronAI\StructuredOutput\Validation\Rules\IsTrue;

class Phone
{
    #[IsFalse]
    public bool $iphone;
    
    #[IsTrue]
    public bool $refurbed;
}
```

### #\[IsNull] - #\[IsNotNull]

La propiedad bajo validación debe respetar la condición de nullable definida por la regla:

```php
namespace App\Neuron\Output;

use NeuronAI\StructuredOutput\Validation\Rules\IsNotNull;
use NeuronAI\StructuredOutput\Validation\Rules\IsNull;

class Phone
{
    #[IsNotNull]
    public string $brand;
    
    #[IsNull]
    public ?string $test;
}
```

### #\[Json]

La propiedad bajo validación debe contener una cadena JSON válida:

```php
namespace App\Neuron\Output;

use NeuronAI\StructuredOutput\Validation\Rules\Json;

class Person
{
    #[Json]
    public string $address;
}
```

### #\[Url]

La propiedad bajo validación debe contener una URL válida:

```php
namespace App\Neuron\Output;

use NeuronAI\StructuredOutput\Validation\Rules\Url;

class Person
{
    #[Url]
    public string $website;
}
```

### #\[Email]

La propiedad bajo validación debe contener una dirección de correo electrónico válida:

```php
namespace App\Neuron\Output;

use NeuronAI\StructuredOutput\Validation\Rules\Email;

class Person
{
    #[Email]
    public string $email;
}
```

### #\[IpAddress]

La propiedad bajo validación debe contener una dirección IP válida:

```php
namespace App\Neuron\Output;

use NeuronAI\StructuredOutput\Validation\Rules\IpAddress;

class Spec
{
    #[IpAddress]
    public string $ip;
}
```

### #\[ArrayOf]

La propiedad bajo validación debe ser un array que contenga todos los objetos del tipo dado.

```php
namespace App\Neuron\Output;

use NeuronAI\StructuredOutput\Validation\Rules\ArrayOf;

class Post
{
    #[ArrayOf(Tag::class)]
    public array $tags;
}
```

### #\[Regex]

La propiedad bajo validación debe respetar la expresión regular dada.

```php
namespace App\Neuron\Output;

use NeuronAI\StructuredOutput\Validation\Rules\Regex;

class Coupon
{
    #[Regex('/^[A-Z]{2}\d{4}$/')]
    public string $code;
}
```

## Reglas de validación personalizadas

Las reglas de validación son atributos de PHP, así que para crear una nueva debes extender la clase AbstractValidationRule del framework y marcar la clase como un atributo de PHP:

```php
namespace App\Neuron\Output;

use Attribute;
use NeuronAI\StructuredOutput\Validation\Rules\AbstractValidationRule;

#[Attribute(Attribute::TARGET_PROPERTY)]
class MyFormatRule extends AbstractValidationRule
{
    public function __construct(protected string $format)
    {
    }

    public function validate(string $name, mixed $value, array &$violations): void
    {
        if (!is_string($value)) {
            $violations[] = $this->buildMessage($name, '{name} debe ser una cadena.');
        } else if (!$this->respectFormat($this->format, $value)) {
            $violations[] = $this->buildMessage(
                $name,
                '{name} debe coincidir con el formato {format}',
                ['format' => $this->pattern]
            );
        }
    }
    
    protected function respectFormat(string $value)
    {
        ...
    }
}
```

Ahora puedes usar la regla en tu clase de salida estructurada:

```php
namespace App\Neuron\Output;

class Route
{
    #[MyFormatRule('apps/{id}/show')]
    public string $path;
}
```
