> 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/evaluation.md).

# Evaluaciones

Evaluación de la salida de tu sistema agéntico

Esta guía cubre enfoques para evaluar agentes. Una evaluación eficaz es esencial para medir el rendimiento del agente, seguir las mejoras y garantizar que tu sistema agéntico cumpla con los estándares de calidad.

Al construir aplicaciones de IA, evaluar la consistencia de su salida es crucial, no solo para el mantenimiento del agente, sino también para evaluar diferentes arquitecturas o enfoques de prompting en la fase inicial de diseño.

Es importante considerar varios factores cualitativos y cuantitativos, incluyendo la sintaxis de la respuesta, la finalización de la tarea, el éxito y las inexactitudes o alucinaciones. En las evaluaciones, también es importante considerar la comparación de diferentes configuraciones para optimizar resultados específicos deseados. Dada la naturaleza dinámica y no determinista de los LLM, también es importante realizar evaluaciones rigurosas y frecuentes para asegurar una línea base consistente para seguir mejoras o regresiones.

### Configuración de tu aplicación

Al igual que en las pruebas unitarias, podría ser mejor recopilar los evaluadores de tu sistema de IA en un directorio dedicado. Así, puedes añadir la siguiente configuración a tu aplicación `composer.json` archivo para indicarle a composer cómo incluir tus evaluadores en los espacios de nombres de la aplicación:

```json
"autoload-dev": {
    "psr-4": {
        ...,
        "App\\Evaluators\\": "evaluators/"
    }
},
```

A continuación crea el `evaluators` directorio en la carpeta raíz de tu proyecto. Mantener el código de evaluación separado del código de producción crea un límite claro entre lo que se despliega a producción y lo que existe únicamente para desarrollo y aseguramiento de la calidad.

### Creación de Evaluadores

Usa el siguiente comando para crear la `AgentEvaluator` clase dentro de la carpeta evaluators:

{% tabs %}
{% tab title="Unix" %}

```bash
vendor/bin/neuron make:evaluator App\\Neuron\\Evaluators\\AgentEvaluator
```

{% endtab %}

{% tab title="Windows" %}

```powershell
.\vendor\bin\neuron make:evaluators App\Neuron\Evaluators\AgentEvaluator
```

{% endtab %}
{% endtabs %}

La clase que se creará tendrá la siguiente estructura:

```php
namespace App\Neuron\Evaluators;

use NeuronAI\Evaluation\Assertions\StringContains;
use NeuronAI\Evaluation\BaseEvaluator;
use NeuronAI\Evaluation\Contracts\DatasetInterface;
use NeuronAI\Evaluation\Dataset\JsonDataset;

class AgentEvaluator extends BaseEvaluator
{
    /**
     * 1. Obtener el conjunto de datos con el que evaluar
     */
    public function getDataset(): DatasetInterface
    {
        return new JsonDataset(__DIR__ . '/datasets/dataset.json');
    }

    /**
     * 2. Ejecutar la lógica del agente que se está probando
     */
    public function run(array $datasetItem): mixed
    {
        $response = MyAgent::make()->chat(
            new UserMessage($datasetItem['input'])
        )->getMessage();
        
        return $response->getContent();
    }

    /**
     * 3. Evaluar la salida frente a los resultados esperados, con aserciones
     */
    public function evaluate(mixed $output, array $datasetItem): void
    {
        $this->assert(
            new StringContains($datasetItem['reference']),
            $output,
        );
    }
} 
```

La lógica es bastante sencilla. El evaluador primero carga el conjunto de datos y luego ejecuta la evaluación para cada elemento del conjunto de datos.

En el `run` método puedes ejecutar tus entidades agénticas con la entrada de ejemplo y devolver la salida. Luego, la salida se pasa al `evaluate` método, donde puedes realizar aserciones comparando la salida con un valor de referencia o cualquier otra lógica que desees.

### Cargador de conjuntos de datos

Puedes usar lo que quieras como conjunto de datos. No hay un formato predefinido. La clase evaluadora simplemente te permite cargar una lista de casos de prueba y ejecutar los evaluadores contra ellos. Tienes dos cargadores de conjuntos de datos.

#### ArrayDataset

```php
class AgentEvaluator extends BaseEvaluator
{
    public function getDataset(): DatasetInterface
    {
        return new ArrayDataset([
            [
                'input' => 'Hola',
                'reference' => 'help'
            ]
        ]);
    }
    
    ...
}
```

#### JsonDataset

```php
class AgentEvaluator extends BaseEvaluator
{
    public function getDataset(): DatasetInterface
    {
        return new JsonDataset(__DIR__ . '/datasets/dataset.json');
    }
    
    ...
}
```

Eventualmente puedes crear un cargador de conjunto de datos personalizado implementando `NeuronAI\Evaluation\Contracts\DatasetInterface`.

### Ejecución de evaluaciones

Si has configurado correctamente tu archivo composer, puedes usar la CLI de Neuron para lanzar los evaluadores:

{% tabs %}
{% tab title="Unix" %}

```bash
vendor/bin/neuron evaluations --path=evaluators
```

{% endtab %}

{% tab title="Windows" %}

```powershell
.\vendor\bin\neuron evaluations --path=evaluators
```

{% endtab %}
{% endtabs %}

### Aserciones

Proporcionamos un conjunto de aserciones integradas para los casos de uso más comunes. También puedes implementar tu propia aserción para diseñar sistemas de puntuación personalizados. Consulta la siguiente sección.

**StringContains**

```php
$this->assert(new StringContains('positive'), $output);
```

**StringContainsAll**

Comprueba si la salida contiene todas las palabras clave:

```php
$this->assert(new StringContainsAll(['hello', 'world']), $output);
```

**StringContainsAny**

Comprueba si la salida contiene alguna de las palabras clave:

```php
$this->assert(new StringContainsAny(['success', 'completed']), $output);
```

**StringStartsWith**

Comprueba si la salida comienza con un prefijo:

```php
$this->assert(new StringStartsWith('Hello'), $output);
```

**StringEndsWith**

Comprueba si la salida termina con un sufijo:

```php
$this->assert(new StringEndsWith('!'), $output);
```

**StringLengthBetween**

Comprueba si la longitud de la cadena está dentro del rango:

```php
$this->assert(new StringLengthBetween(10, 100), $output);
```

**StringDistance**

Comprueba la similitud de cadenas usando la distancia de Levenshtein:

```php
$this->assert(new StringDistance(
    reference: 'expected text',
    threshold: 0.5, // Puntuación mínima de similitud
    maxDistance: 50 // Número máximo de ediciones permitidas
), $output);
```

**StringSimilarity**

Comprueba la similitud de cadenas usando embeddings:

```php
use NeuronAI\Evaluation\Assertions\StringSimilarity;
use NeuronAI\RAG\Embeddings\OpenAI\OpenAIEmbeddings;

$this->assert(new StringSimilarity(
    reference: 'The quick brown fox',
    embeddingsProvider: new OpenAIEmbeddings(key: 'YOUR_KEY'),
    threshold: 0.6
), $output);
```

**MatchesRegex**

Coincide con una expresión regular:

```php
$this->assert(new MatchesRegex('/^\d{3}-\d{2}-\d{4}$/'), $output);
```

**IsValidJson**

Comprueba si la salida es JSON válido:

```php
$this->assert(new IsValidJson(), $output);
```

### IA como juez

Usa un agente de IA para evaluar salidas con criterios personalizados. Neuron te proporciona la clase primitiva AgentJudge para definir tus criterios personalizados; de lo contrario, puedes usar una de las aserciones de juez integradas.

```php
use NeuronAI\Evaluation\Assertions\AgentJudge;

class AgentJudgeEvaluator extends BaseEvaluator
{
    protected AgentInterface $judge;

    public function setUp(): void
    {
        $this->judge = Agent::make()
            ->setAiProvider(
                new Antrhopic(...)
            )
            ->setInstructions('Eres un evaluador experto de respuestas de atención al cliente.');
    }

    public function getDataset(): DatasetInterface
    {
        return new JsonDataset(...);
    }

    public function run(array $datasetItem): mixed
    {
        $response = MyAgent::make()->chat(
            new UserMessage($datasetItem['input'])
        )->getMessage();
        
        return $response->getContent();
    }
    
    public function evaluate(mixed $output, array $datasetItem): void
    {
        $this->assert(new AgentJudge(
            judge: $this->judge,
            criteria: 'La respuesta debe ser útil, educada y abordar directamente la pregunta del cliente',
            threshold: $datasetItem['threshold']
        ), $output);
    }
}
```

#### Juez de fidelidad

Comprueba si la salida está fundamentada en el contexto (sin alucinaciones):

```php
$this->assert(new FaithfulnessJudge(
    judge: $this->judge,
    context: $retrievedDocuments,
    threshold: 0.7
), $output);
```

#### Juez de corrección

Comparar con la respuesta esperada:

```php
$this->assert(new CorrectnessJudge(
    judge: $judge,
    expected: $datasetItem['expected_answer'],
    threshold: 0.7
), $output);
```

#### Juez de relevancia

Comprueba si la salida responde a la pregunta:

```php
$this->assert(new RelevanceJudge(
    judge: $judge,
    question: $datasetItem['question'],
    threshold: 0.7
), $output);
```

#### Juez de utilidad

Evalúa la utilidad y la capacidad de acción:

```php
$this->assert(new HelpfulnessJudge(
    judge: $judge,
    threshold: 0.7
), $output);
```

### Creación de aserciones personalizadas

```php
use NeuronAI\Evaluation\Assertions\AbstractAssertion;
use NeuronAI\Evaluation\AssertionResult;

class GreaterThanAssertion extends AbstractAssertion
{
    public function __construct(
        private readonly float $threshold
    ) {}

    public function evaluate(mixed $actual): AssertionResult
    {
        if (!is_numeric($actual)) {
            return AssertionResult::fail(
                0.0,
                'Se esperaba un valor numérico, se obtuvo ' . gettype($actual),
            );
        }

        if ($actual > $this->threshold) {
            return AssertionResult::pass(1.0);
        }

        return AssertionResult::fail(
            0.0,
            "Se esperaba que {$actual} fuera mayor que {$this->threshold}",
        );
    }
}
```

### Salida

El módulo de evaluación usa un archivo de configuración PHP para controlar cómo se muestran los resultados de la evaluación. El sistema de configuración admite múltiples controladores de salida, lo que permite enviar los resultados a la consola, archivos, bases de datos o API externas simultáneamente.

#### **Archivo de configuración**

Crea el `evaluation.php` archivo en la raíz de tu proyecto:

```php
<?php

use NeuronAI\Evaluation\OutputDrivers\ConsoleDriver;
use NeuronAI\Evaluation\OutputDrivers\JsonDriver;

return [
    'output' => [
        // Mostrar los resultados en la consola
        ConsoleDriver::class => ['verbose' => true],

        // Guardar los resultados en un archivo json
        JsonDriver::class => ['path' => 'evaluation-results.json'],
    ],
];
```

Puedes declarar un arreglo de opciones para cada clase de salida. Estas configuraciones se pasarán como argumentos al constructor de la implementación de la clase de salida.

**Si no existe ningún archivo de configuración**, el sistema usa por defecto `ConsoleOutputDriver` con salida estándar.

#### Creación de salida personalizada

Implementa `EvaluationOutputInterface` para crear controladores de salida personalizados:

```php
namespace App\Neuron\Evaluations;

use NeuronAI\Evaluation\Contracts\EvaluationOutputInterface;
use NeuronAI\Evaluation\Runner\EvaluatorSummary;

class DatabaseOutput implements EvaluationOutputInterface
{
    public function __construct(
        private readonly \PDO $pdo,
        private readonly string $table = 'evaluations'
    ) {}

    public function output(EvaluatorSummary $summary): void
    {
        $stmt = $this->pdo->prepare(
            "INSERT INTO {$this->table} (passed, failed, success_rate, total_time, created_at, updated_at) VALUES (?, ?, ?, ?, NOW(), NOW())"
        );
        $stmt->execute([
            $summary->getPassedCount(),
            $summary->getFailedCount(),
            $summary->getSuccessRate(),
            $summary->getTotalExecutionTime(),
        ]);
    }
}
```

Una vez que hayas creado tu clase de salida, puedes registrarla en el archivo de configuración para que se use la próxima vez que ejecutes las evaluaciones.

```php
<?php

use NeuronAI\Evaluation\OutputDrivers\ConsoleDriver;
use NeuronAI\Evaluation\OutputDrivers\JsonDriver;

return [
    'output' => [
        // Mostrar los resultados en la consola
        ConsoleDriver::class => ['verbose' => true],

        // Guardar los resultados en un archivo json
        //JsonDriver::class => ['path' => 'evaluation-results.json'],
        
        // Guardar los resultados en la base de datos
        DatabaseOutput::class => [
            'pdo' => new \PDO(...),
            'table' => 'evaluations',
        ]
    ],
];
```

### Ejecución en paralelo

De forma predeterminada, el comando de evaluación procesa los elementos del conjunto de datos uno a la vez. Como la mayoría de los evaluadores pasan su tiempo esperando respuestas del proveedor de IA, puedes reducir drásticamente el tiempo total de ejecución procesando varios elementos del conjunto de datos en paralelo con la `--concurrency` opción:

```bash
vendor/bin/neuron evaluation path/to/evaluators --concurrency=3
```

Con `--concurrency=3`, se evalúan hasta 3 elementos del conjunto de datos al mismo tiempo, cada uno en su propio proceso hijo de PHP. Una evaluación que hace una llamada de LLM de 2 segundos por elemento sobre un conjunto de datos de 100 elementos pasa de \~200 segundos a \~66 segundos.

#### Requisitos

La ejecución en paralelo se basa en la bifurcación de procesos, lo que requiere:

* La [pcntl](https://www.php.net/manual/en/book.pcntl.php) extensión de PHP (disponible en Linux y macOS; no en Windows)
* La [paquete spatie/fork:](https://github.com/spatie/fork) paquete:

```bash
composer require --dev spatie/fork
```

Si falta alguno de los dos, el comando muestra un aviso y automáticamente vuelve a la ejecución secuencial, por lo que el mismo comando funciona en cualquier entorno.

#### Elegir un nivel de concurrencia

Cada elemento en curso es una solicitud activa a tu proveedor de IA. Empieza con un valor moderado (3–5) y auméntalo mientras no alcances los límites de velocidad del proveedor. Si ves que aparecen errores de límite de velocidad como fallos de prueba, reduce el valor.

#### Cómo funciona y en qué hay que fijarse

Cada elemento del conjunto de datos se ejecuta en una copia bifurcada de tu evaluador, y su resultado se devuelve al proceso padre. Esto tiene algunas implicaciones prácticas:

* **Los resultados no se ven afectados.** Los elementos se evalúan de forma independiente, los resultados conservan el orden de su conjunto de datos y el informe final es idéntico al de una ejecución secuencial.
* **El estado no se comparte entre elementos.** Cada elemento ve el estado del evaluador tal como estaba después de `setUp()`. Los efectos secundarios realizados al procesar un elemento (incrementar una propiedad, añadir a un archivo) no son visibles para otros elementos. Si tu evaluador depende de acumular estado entre elementos, ejecútalo de forma secuencial.
* **Las salidas deben ser serializables.** El valor devuelto por `run()` cruza un límite de proceso a través de `serialize()`. Si no puede serializarse (por ejemplo, contiene un cierre o una conexión abierta), los resultados de las aserciones se conservan, pero la salida mostrada en los informes se reemplaza por una cadena de marcador de posición.

#### Informe del tiempo de ejecución

El tiempo total informado es la duración real de reloj del sistema de la ejecución, mientras que el tiempo promedio por prueba refleja la duración real de cada elemento individual; por lo tanto, en ejecución paralela el promedio por prueba puede ser mayor que el total dividido entre el número de pruebas.

### Archivo bootstrap personalizado

De forma predeterminada, `neuron` la CLI solo carga el autoloader de Composer de tu proyecto (`vendor/autoload.php`). Eso basta cuando tus clases son PHP normal y resoluble por Composer, pero a menudo no lo son: un evaluador puede leer la configuración mediante los helpers de tu framework, necesitar variables de entorno cargadas desde `.env`, depender de constantes o requerir que se inicialice un contenedor de servicios. En esos casos, el comando fallaría con errores de "clase no encontrada" o de configuración faltante, porque el código que normalmente prepara ese entorno —el bootstrap de tu framework— nunca se ejecuta.

La `--autoload-file` opción resuelve esto permitiéndote indicar a la CLI un archivo PHP que se ejecutará *antes* de que comience el comando, además del autoloader predeterminado de Composer:

```bash
vendor/bin/neuron evaluation /path/to/evaluators --autoload-file=bootstrap.php
```

El archivo puede hacer cualquier cosa que haga un bootstrap normal: registrar autoloaders adicionales, cargar variables de entorno, definir constantes o arrancar tu framework. Por ejemplo, para ejecutar evaluadores que dependen de una aplicación Laravel:

```php
<?php
// bootstrap.php

require __DIR__.'/vendor/autoload.php';

$app = require_once __DIR__.'/bootstrap/app.php';
$app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();
```
