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

# Middleware

Middleware es una característica del componente básico Workflow. Así que también puedes adjuntar middleware personalizado al Agent y al RAG para engancharte a su ciclo de ejecución.

### El workflow del Agent

La clase Agent es una extensión del componente Workflow. Workflow es la pieza fundamental del rompecabezas en Neuron. Muchas funciones de los componentes Agent y RAG heredan su lógica de las capacidades del Workflow subyacente.

Aquí tienes un esquema simple del workflow utilizado para crear la implementación del agente:

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

Teniendo en cuenta esta arquitectura, eres libre de usar middleware para enganchar el workflow del agente, interrupción para mantener a los humanos en el circuito, o mira abajo para ver un conjunto de componentes integrados que proporcionamos para casos de uso comunes.

### Aprobación de herramientas (Human In The Loop)

{% hint style="info" %}
Antes de usar ToolApproval, deberías estar familiarizado con el workflow [persistencia](/neuron-v3-es/flujo-de-trabajo/persistence.md) y [interrupción](/neuron-v3-es/flujo-de-trabajo/human-in-the-loop.md).
{% endhint %}

En Neuron, la entidad Agent se construye sobre el componente Workflow. Eso significa que puede interrumpirse para pedir confirmación antes de realizar acciones críticas. El `ToolApproval` middleware pausa la ejecución del agente para la aprobación o el rechazo humano de las llamadas a herramientas antes de que se ejecuten.

```php
use NeuronAI\Agent\Agent;
use NeuronAI\Agent\Middleware\ToolApproval;
use NeuronAI\Workflow\Middleware\WorkflowMiddleware;
use NeuronAI\Workflow\NodeInterface;

class MyAgent extends Agent
{
    protected function provider(): AIProviderInterface
    {...}
    
    /**
     * Registrar herramientas
     */
    protected function tools(): array
    {
        return [
            BuyTicketTool::make(),
        ];
    }

    /**
     * Adjuntar middleware a los nodos.
     */
    protected function middleware(): array
    {
        return [
            ToolNode::class => [
                new ToolApproval(
                    // Proporciona una lista de clases o nombres de herramientas que necesitan ser aprobados
                    tools: [BuyTicketTool::class]
                )
            ],
        ];
    }
}
```

Una vez que el agente intenta llamar a una de las herramientas enumeradas en el `ToolApproval` middleware, activa la excepción de interrupción del workflow. Tienes que capturar esta excepción y presentar al usuario la interfaz para recopilar su opinión. La excepción de interrupción contendrá una instancia de `ApprovalRequest` con acciones que requieren la opinión del usuario.

```php
use NeuronAI\Workflow\Interrupt\WorkflowInterrupt;
use NeuronAI\Workflow\Persistence\FilePersistence;

$persistence = new FilePersistence(__DIR__);

try {

    $response = new MyAgent($presistence)
        ->chat(new UserMessage("¿Qué tiempo hace en Italia?"))
        ->getMessage();
        
} catch (WorkflowInterrupt $interrupt) {
    $approvalRequest = json_encode($interrupt->getRequest());
    $resumeToken = $interrupt->getResumeToken();
    
    // Guarda la solicitud y el resumeToken para recopilar la opinión del usuario y reiniciar más tarde el workflow del agente
}
```

El token de reanudación se genera automáticamente y está disponible en la excepción de interrupción.

Deberías guardar el `solicitud de aprobación` junto con el `token de reanudación` para reiniciar más tarde el workflow del agente, exactamente donde lo dejó. Puedes usar una base de datos o cualquier otra capa de persistencia que sea conveniente para tu aplicación. La solicitud de aprobación es serializable a JSON, así que puedes guardar fácilmente su estructura en un almacén.

Una vez que el usuario haya aprobado/rechazado las acciones, puedes reanudar el agente alimentándolo con la solicitud editada.

```php
$persistence = new FilePersistence(__DIR__);

// Recupera la solicitud y el token después de la interacción del usuario para reiniciar el workflow
$approvalRequest = ApprovalRequest::fromArray(...);
$resumeToken = ...

$response = new MyAgent($persistence, $resumeToken)
        ->chat(interrupt: $approvalRequest)
        ->getMessage();
```

Para entender mejor cómo gestionar el flujo de interrupción, puedes consultar este ejemplo:

{% embed url="<https://github.com/neuron-core/neuron-ai/blob/main/examples/agent/tool-approval.php>" %}

O consulta la [documentación completa del workflow](/neuron-v3-es/flujo-de-trabajo/human-in-the-loop.md).

### Aprobación condicional

El ejemplo anterior es un flujo clásico de aprobación activada/desactivada. Si una herramienta está incluida en el `ToolApproval` middleware, el agente interrumpirá la ejecución; de lo contrario, la herramienta se ejecutará como de costumbre.

El middleware también acepta una devolución de llamada asociada a las herramientas, con el fin de definir tu condición personalizada de aprobación. La devolución de llamada recibe la instancia de la herramienta y devuelve `true` si la herramienta requiere aprobación, o `false` para omitir la interrupción y ejecutar la herramienta tal cual.

```php
class MyAgent extends Agent
{
    ...
    
    /**
     * Registrar herramientas
     */
    protected function tools(): array
    {
        return [
            BuyTicketTool::make(),
        ];
    }

    /**
     * Adjuntar middleware a los nodos.
     */
    protected function middleware(): array
    {
        return [
            ToolNode::class => [
                new ToolApproval(
                    tools: [
                        // Solicita aprobación si el importe es mayor que 100
                        BuyTicketTool::class => function (array $args): bool {
                            return $args['amount'] > 100;
                        }
                    ]
                )
            ],
        ];
    }
}
```

En el ejemplo anterior requerimos la aprobación humana solo si el boleto cuesta más de 100; de lo contrario, la devolución de llamada devuelve false, lo que significa que no hace falta interrupción.

### Resumen del contexto

Este middleware está diseñado para envolver el nodo donde el agente realmente llama al LLM, con el fin de resumir automáticamente el historial de la conversación cuando se acerca a los límites de tokens. En Neuron hay tres nodos posibles encargados de esta tarea según el tipo de llamada que quieras realizar: `ChatNode`, `StreamingNode`, y `StructuredNode`. Debes adjuntar el middleware a todos estos nodos para asegurarte de que funcione sin importar en qué modo esté ejecutándose el agente.

```php
use NeuronAI\Agent\Agent;
use NeuronAI\Agent\Middleware\Summarization;
use NeuronAI\Agent\Nodes\ChatNode;
use NeuronAI\Agent\Nodes\StreamingNode;
use NeuronAI\Agent\Nodes\StructuredOutputNode;

class MyAgent extends Agent
{
    ...

    /**
     * Adjuntar middleware a los nodos.
     */
    protected function middleware(): array
    {
        $summarization = new Summarization(
            provider: $this->resolveProvider(), // O usa una instancia de proveedor dedicada
            maxTokens: 10000,
            messagesToKeep: 5,
        );
        
        return [
            ChatNode::class => [$summarization],
            StreamingNode::class => [$summarization],
            StructuredOutputNode::class => [$summarization]
        ];
    }
}
```

`maxTokens` y `messagesToKeep` trabajan juntos para definir el umbral a partir del cual debe realizarse el resumen. En el ejemplo anterior, si el contexto alcanza 30K tokens, debe haber al menos 10 mensajes en el historial del chat para que comience el resumen. Añadir nuevos mensajes al historial del chat acabará superando ambos umbrales y activará la resumición.

### Búsqueda de herramientas

De forma predeterminada, cada vez que se invoca al proveedor, todas las herramientas se cargan y se transmiten al LLM del backend. Un agente de producción complejo conectado a correo electrónico, calendario, drive, CRM y múltiples servidores MCP puede alcanzar fácilmente cientos de herramientas, cada una con su nombre, descripción, esquema de parámetros y sugerencias de uso.

Esto consume miles de tokens en cada turno, pero el problema más doloroso es la calidad: cuando un modelo ve demasiadas herramientas a la vez, las descripciones se difuminan entre sí, herramientas con nombres similares compiten por la atención y el agente empieza a tomar decisiones sutilmente erróneas, confundiendo parámetros o inventando argumentos porque intenta mantener demasiadas firmas en la memoria de trabajo al mismo tiempo.

La búsqueda de herramientas replantea el catálogo de herramientas como algo que el agente consulta bajo demanda, en lugar de algo que lleva en cada solicitud.

Puedes usar el middleware global `ToolSearchMiddleware` para activar la selección dinámica de herramientas en tu agente:

```php
class MyAgent extends Agennt
{
    ...
    
    /**
     * Define el middleware global.
     */
    protected function globalMiddleware(): array
    {
        return [
            new ToolSearchMiddleware([
                MyCustomTool::make(),
                ...CalculatorToolkit::make()->tools()
                ...MCPConnector::make([...])->tools()
            ]),
        ];
    }
    
    /**
     * Proporciona herramientas básicas al agente.
     */
    protected function tools(): array
    {
        return [
            // Una lista de herramientas básicas que el modelo siempre tiene disponibles
            TavilySearchTool::make(...),
        ];
    }
}
```

{% embed url="<https://www.youtube.com/watch?v=qYmidHAXEYM>" %}

El middleware inyecta automáticamente la `ToolSearch` herramienta en la lista predeterminada de herramientas disponible para el modelo en cada solicitud, y mantiene la lista de herramientas que proporcionas en un array interno.

El agente comienza un turno con un conjunto mínimo de herramientas, normalmente solo `ToolSearch` él mismo más cualquier herramienta básica que siempre quieras tener disponible, y cuando necesita una capacidad que no tiene en ese momento, llama a `ToolSearch` con una consulta en lenguaje natural que devuelve una lista clasificada de descriptores de herramientas con sus esquemas completos. En este punto, un middleware situado entre el agente y la siguiente llamada de inferencia inspecciona el resultado de la búsqueda, extrae los identificadores de las herramientas, los busca en el registro subyacente y añade sus definiciones completas al array de herramientas que se enviará en la siguiente solicitud al modelo.

Desde la perspectiva del modelo, el siguiente turno simplemente llega con una lista de herramientas más rica, y puede invocar directamente cualquiera de esas herramientas recién expuestas con una validación de esquema adecuada, exactamente como si hubieran estado ahí desde el principio.
