Skip to content

Como Construir um Payload Compatível para a API de Mensagens da Anthropic: Evite Erros

Introdução

Ao trabalhar com a API da Anthropic, especificamente o endpoint de mensagens, os desenvolvedores frequentemente encontram o erro invalid_request_error. Este erro pode ocorrer devido a várias razões relacionadas à estrutura do payload enviado para a API. Neste artigo, exploraremos como construir um payload compatível para evitar este erro e garantir uma comunicação suave com a API da Anthropic.

Entendendo os Requisitos da API da Anthropic

A API da Anthropic tem requisitos específicos para a estrutura dos payloads de mensagens. Esses requisitos garantem que o modelo de IA possa processar o contexto da conversa corretamente. Os principais pontos a considerar são:

  1. As mensagens devem alternar entre os papéis de usuário e assistente.
  2. A primeira e a última mensagem devem ser do usuário.
  3. Chamadas de função são mensagens do assistente e as respostas são mensagens do usuário.

Solução

Para abordar esta questão, usaremos uma classe auxiliar chamada LLMProvider. Esta classe contém um método getStandardMessagesStructure que processa o array de mensagens para garantir que esteja em conformidade com os requisitos da Anthropic.

Nosso esquema de array $messages se parece com isto:

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';
}

O objetivo deste esquema é ser flexível o suficiente para que possa ser usado como um banco de dados de mensagens de chatbot e, ao mesmo tempo, compatível com múltiplos LLMs como OpenAI e Google Gemini, mas para o escopo deste artigo, vamos nos concentrar em abordar erros de solicitação com a API da Anthropic.

Sinta-se à vontade para adaptar o código abaixo à forma como você estrutura suas mensagens no banco de dados antes de enviá-las a um provedor terceirizado.

Passo 1: Implementar o Método getStandardMessagesStructure

php
private static function getStandardMessagesStructure(array $messages): array
{
    // Remove mensagens do início até que a primeira mensagem com papel 'contact' seja encontrada
    $messages = self::removeMessagesUntilFirstContact($messages);

    // Garante que o papel 'contact' seja sempre precedido por um papel 'bot', a menos que seja o primeiro elemento
    $messages = self::ensureContactPrecededByBot($messages);

    // Remove mensagens do final até que a última mensagem seja:
    // - papel 'contact' com message_type 'text' ou 'image'
    // - papel 'function' com message_type 'function_response'
    return self::removeMessagesFromEndUntilValid($messages);
}

Este método chama três funções auxiliares para processar o array de mensagens:

Passo 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 função garante que a primeira mensagem no array seja do usuário (papel 'contact').

Passo 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 função garante que as mensagens alternem entre os papéis de usuário e assistente.

Passo 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 função garante que a última mensagem seja do usuário ou uma resposta de função válida.

Passo 5: Usar o getStandardMessagesStructure em Sua Chamada de API

Ao fazer uma chamada para a API da Anthropic, use o método getStandardMessagesStructure para processar seu array de mensagens:

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 da classe AnthropicProvider, mapeamos o array $messages para uma estrutura de mensagem compatível com a Anthropic em uma função chamada getParsedMessages. Aqui está uma seção disso para referência:

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];

            ... // outros mapeamentos

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

Exemplo: Antes e Depois do Processamento

Vamos ver um exemplo de como a estrutura do payload de mensagens muda após o processamento:

Antes do processamento:

php
$messages = [
    ['role' => 'bot', 'message' => 'Olá!'],
    ['role' => 'contact', 'message' => 'Oi!'],
    ['role' => 'contact', 'message' => 'Como você está?'],
    ['role' => 'bot', 'message' => 'Estou bem, obrigado!'],
    ['role' => 'bot', 'message' => 'Como posso ajudar você hoje?'],
];

Depois do processamento:

php
$messages = [
    ['role' => 'contact', 'message' => 'Oi!'],
    ['role' => 'bot', 'message' => 'Estou bem, obrigado!'],
    ['role' => 'contact', 'message' => 'Como você está?'],
];

Melhores Práticas para Estruturação de Mensagens

Para minimizar a necessidade de processamento extensivo, considere estas melhores práticas ao estruturar suas mensagens:

  1. Sempre inicie as conversas com uma mensagem do usuário.
  2. Alterne entre mensagens do usuário e do assistente.
  3. Termine as conversas com uma mensagem do usuário ou uma resposta de função válida.
  4. Lide com chamadas de função em conversas de IA apropriadamente dentro do fluxo da conversa.

Tratamento de Erros

Quando você encontrar um invalid_request_error, é crucial registrar os detalhes do erro e o payload que o causou. Isso pode ajudar na depuração e na melhoria da sua lógica de processamento de mensagens. Considere implementar um bloco try-catch:

php
use Illuminate\Support\Facades\Log;

...

try {
    $response = AnthropicProvider::chat(/* ... */);
} catch (Exception $e) {
    if (str_contains($e->getMessage(), 'invalid_request_error')) {
        // Registra o erro e o payload
        Log::error('Erro de solicitação inválida: ' . json_encode($messages));
    }
    throw $e;
}

Conclusão

Ao implementar esses métodos e usá-los para processar seu array de mensagens antes de enviá-lo para a API da Anthropic, você pode evitar o invalid_request_error e garantir que seu payload esteja em conformidade com os requisitos da API. Esta abordagem lida com a alternância de mensagens do usuário e do assistente, garante a estrutura adequada para chamadas de função e respostas, e mantém a ordem correta das mensagens.

Lembre-se de adaptar as constantes MessageSchema e outros detalhes específicos para corresponder à estrutura do seu projeto. Com esta solução implementada, você deve ser capaz de se comunicar com o modelo Claude da Anthropic de forma confiável.

Embora esta abordagem seja eficaz, é importante notar que ela pode remover algumas mensagens do histórico da conversa. Em cenários onde preservar toda a conversa é crucial, você deve implementar um sistema mais sofisticado que garanta a conformidade sem perder o contexto.