Skip to content

Cómo Construir una Carga Útil Compatible para la API de Mensajes de Anthropic: Evite Errores

Introducción

Cuando se trabaja con la API de Anthropic, específicamente con el endpoint de mensajes, los desarrolladores a menudo encuentran el error invalid_request_error. Este error puede ocurrir debido a varias razones relacionadas con la estructura de la carga útil enviada a la API. En este artículo, exploraremos cómo construir una carga útil compatible para evitar este error y asegurar una comunicación fluida con la API de Anthropic.

Entendiendo los Requisitos de la API de Anthropic

La API de Anthropic tiene requisitos específicos para la estructura de las cargas útiles de mensajes. Estos requisitos aseguran que el modelo de IA pueda procesar correctamente el contexto de la conversación. Los puntos principales a considerar son:

  1. Los mensajes deben alternar entre roles de usuario y asistente.
  2. El primer y último mensaje deben ser del usuario.
  3. Las llamadas a funciones son mensajes del asistente y las respuestas son mensajes del usuario.

Solución

Para abordar este problema, utilizaremos una clase auxiliar llamada LLMProvider. Esta clase contiene un método getStandardMessagesStructure que procesa el array de mensajes para asegurar que cumpla con los requisitos de Anthropic.

Nuestro esquema de array $messages se ve así:

php
class MessageSchema
{
    const string table = 'messages';

    const string id = 'id';

    const string message = 'message';

    const string caption = 'caption';

    const string role = 'role'; // admin, contact, bot, function

    const string function_id = 'function_id';

    const string function_name = 'function_name';

    const string function_arguments = 'function_arguments';

    const string message_type = 'message_type'; // text, image, function_call, function_response

    const string created_at = 'created_at';

    const string updated_at = 'updated_at';

    const string deleted_at = 'deleted_at';
}

El objetivo de este esquema es ser lo suficientemente flexible para que pueda usarse como una base de datos de mensajes de chatbot y, al mismo tiempo, compatible con múltiples LLMs como OpenAI y Google Gemini, pero para el alcance de este artículo, nos centraremos en abordar los errores de solicitud con la API de Anthropic.

Siéntase libre de adaptar el código a continuación a la forma en que estructura sus mensajes en la base de datos antes de enviarlos a un proveedor externo.

Paso 1: Implementar el Método getStandardMessagesStructure

php
private static function getStandardMessagesStructure(array $messages): array
{
    // Eliminar mensajes desde el principio hasta encontrar el primer mensaje con rol 'contact'
    $messages = self::removeMessagesUntilFirstContact($messages);

    // Asegurar que el rol 'contact' siempre esté precedido por un rol 'bot' a menos que sea el primer elemento
    $messages = self::ensureContactPrecededByBot($messages);

    // Eliminar mensajes desde el final hasta que el último mensaje sea:
    // - rol 'contact' con message_type 'text' o 'image'
    // - rol 'function' con message_type 'function_response'
    return self::removeMessagesFromEndUntilValid($messages);
}

Este método llama a tres funciones auxiliares para procesar el array de mensajes:

Paso 2: Implementar removeMessagesUntilFirstContact

php
private static function removeMessagesUntilFirstContact(array $messages): array
{
    while (!empty($messages) && $messages[0][MessageSchema::role] !== 'contact') {
        array_shift($messages);
    }
    return $messages;
}

Esta función asegura que el primer mensaje en el array sea del usuario (rol 'contact').

Paso 3: Implementar ensureContactPrecededByBot

php
private static function ensureContactPrecededByBot(array $messages): array
{
    for ($i = 1; $i < count($messages); $i++) {
        if ($messages[$i][MessageSchema::role] === 'contact' && $messages[$i - 1][MessageSchema::role] !== 'bot') {
            while ($i > 0 && $messages[$i - 1][MessageSchema::role] !== 'bot') {
                array_splice($messages, $i - 1, 1);
                $i--;
            }
        }
    }
    return $messages;
}

Esta función asegura que los mensajes alternen entre roles de usuario y asistente.

Paso 4: Implementar removeMessagesFromEndUntilValid

php
private static function removeMessagesFromEndUntilValid(array $messages): array
{
    while (!empty($messages)) {
        $last_message = $messages[count($messages) - 1];
        $role = $last_message[MessageSchema::role];
        $message_type = $last_message[MessageSchema::message_type] ?? 'text';
        $is_contact_with_valid_type = $role === 'contact' && in_array($message_type, ['text', 'image']);
        $is_function_with_valid_type = $role === 'function' && $message_type === 'function_response';

        if ($is_contact_with_valid_type || $is_function_with_valid_type) {
            break;
        }

        array_pop($messages);
    }
    return $messages;
}

Esta función asegura que el último mensaje sea del usuario o una respuesta de función válida.

Paso 5: Usar getStandardMessagesStructure en Su Llamada a la API

Al hacer una llamada a la API de Anthropic, use el método getStandardMessagesStructure para procesar su array de mensajes:

php
$messages = self::getStandardMessagesStructure($messages);

$response = AnthropicProvider::chat(
    model: AnthropicProvider::CLAUDE_3_SONNET,
    messages: $messages,
    max_tokens: $max_tokens,
    system: $system,
    temperature: $temperature,
    tools: $tools
);

Dentro de la clase AnthropicProvider, mapeamos el array $messages a una estructura de mensajes compatible con Anthropic en una función llamada getParsedMessages. Aquí hay una sección de eso como referencia:

php
    private static function getParsedMessages(array $messages): array
    {
        return array_map(function ($m) {
            $role = $m[MessageSchema::role];

            switch ($role) {
                case 'contact':
                    $role = 'user';
                    break;
                case 'admin':
                case 'bot':
                case 'function':
                    $role = 'assistant';
                    break;
            }

            $message = $m[MessageSchema::message];

            ... // otros mapeos

            return [
                'content' => $message,
                'role' => $role,
            ];

Ejemplo: Antes y Después del Procesamiento

Veamos un ejemplo de cómo cambia la estructura de carga útil del mensaje después del procesamiento:

Antes del procesamiento:

php
$messages = [
    ['role' => 'bot', 'message' => 'Hello!'],
    ['role' => 'contact', 'message' => 'Hi there!'],
    ['role' => 'contact', 'message' => 'How are you?'],
    ['role' => 'bot', 'message' => 'I'm doing well, thank you!'],
    ['role' => 'bot', 'message' => 'How can I assist you today?'],
];

Después del procesamiento:

php
$messages = [
    ['role' => 'contact', 'message' => 'Hi there!'],
    ['role' => 'bot', 'message' => 'I'm doing well, thank you!'],
    ['role' => 'contact', 'message' => 'How are you?'],
];

Mejores Prácticas para la Estructuración de Mensajes

Para minimizar la necesidad de un procesamiento extenso, considere estas mejores prácticas al estructurar sus mensajes:

  1. Siempre comience las conversaciones con un mensaje del usuario.
  2. Alterne entre mensajes del usuario y del asistente.
  3. Termine las conversaciones con un mensaje del usuario o una respuesta de función válida.
  4. Maneje apropiadamente las llamadas a funciones en conversaciones de IA dentro del flujo de la conversación.

Manejo de Errores

Cuando encuentre un invalid_request_error, es crucial registrar los detalles del error y la carga útil que lo causó. Esto puede ayudar en la depuración y mejora de su lógica de procesamiento de mensajes. Considere implementar un bloque try-catch:

php
use Illuminate\Support\Facades\Log;

...

try {
    $response = AnthropicProvider::chat(/* ... */);
} catch (Exception $e) {
    if (str_contains($e->getMessage(), 'invalid_request_error')) {
        // Registrar el error y la carga útil
        Log::error('Error de solicitud inválida: ' . json_encode($messages));
    }
    throw $e;
}

Conclusión

Al implementar estos métodos y usarlos para procesar su array de mensajes antes de enviarlo a la API de Anthropic, puede evitar el invalid_request_error y asegurar que su carga útil cumpla con los requisitos de la API. Este enfoque maneja la alternancia de mensajes de usuario y asistente, asegura una estructura adecuada para llamadas a funciones y respuestas, y mantiene el orden correcto de los mensajes.

Recuerde adaptar las constantes de MessageSchema y otros detalles específicos para que coincidan con la estructura de su proyecto. Con esta solución implementada, debería poder comunicarse con el modelo Claude de Anthropic de manera confiable.

Aunque este enfoque es efectivo, es importante tener en cuenta que puede eliminar algunos mensajes del historial de la conversación. En escenarios donde preservar toda la conversación es crucial, debería implementar un sistema más sofisticado que asegure el cumplimiento sin perder contexto.