Processadores de Transformação LLM

Cada provedor tem seu próprio formato para os payloads de solicitação, resposta e erro. Por causa disso, o provedor de LLM e o Oracle Digital Assistant não podem se comunicar diretamente, portanto, para facilitar a troca entre a habilidade e seus provedores de LLM, você precisa transformar esses payloads na Interface LLM Comum do Oracle Digital Assistant e voltar novamente.

Você ativa essa transformação criando um handler de transformação LLM, um script cujos métodos transformRequestPayload, transformResponsePayload e transformErrorResponsePayload executam as transformações de payload. Esses métodos de transformação têm duas assinaturas:
  • event: As propriedades usadas para esse objeto dependem do tipo de evento (transformRequestPayload, transformResponsePayload, transformErrorResponsePayload).
  • context: Faz referência à classe LlmTransformationContext, que fornece acesso a métodos de conveniência que você pode usar para criar sua lógica do handler de eventos.

Criar um Handler de Transformação LLM

Para criar um Handler de Evento de Transformação LLM:
  1. Clique em Componentes na barra de navegação esquerda.
  2. Clique em +New Serviço.
  3. Preencha a caixa de diálogo Criar Serviço:
    • Nome: Informe o nome do serviço.
    • Tipo de Serviço: Contêiner Incorporado
    • Tipo de Pacote de Serviço do Componente: Novo Componente
    • Tipo de Componente: Transformação de LLM
    • Nome do Componente: Informe um nome facilmente identificável para o handler de eventos de entidade. Você fará referência a esse nome ao criar o serviço LLM para a habilidade.
    • Modelo: Fornecemos modelos para habilidades que chamam Cohere diretamente ou por meio do serviço Oracle Generative AI. Não é necessário editar esses modelos. Se sua habilidade chamar um modelo de IA Generativa não Cohere/Oracle, como o Azure Open AI, você precisará adicionar o código apropriado.

      Os modelos para o Oracle Generative AI Cohere (geração e resumo de texto) e Llama (resumo de texto) são classificados em Generative AI no menu da lista Template. O modelo para acessar Cohere diretamente está localizado em Other. Para acessar o modelo que contém o código inicial de outros modelos, escolha Personalizado (que também está localizado em Outro).

  4. Clique em Criar para gerar o código do processador de eventos.
  5. Após a conclusão da implantação, expanda o serviço e selecione o handler de transformação para abrir sua página de propriedades, que lista os três métodos de transformação CLMI do provedor LLM (transformRequestPayload, transformResponsePayload e transformErrorResponsePayload).

    Se você estiver criando um serviço Cohere ou um serviço do Oracle Generative AI hospedado na mesma tenancy da instância do Digital Assistant, o código do handler será concluído.

  6. Se você estiver criando um serviço do Oracle Generative AI hospedado em outra tenancy do OCI, clique em Editar para abrir a caixa de diálogo Editar Componente e substituir a variável event.compartmentId pelo ID do compartimento da tenancy do OCI na qual o serviço Generative AI está inscrito.
  7. Se você estiver usando o modelo Personalizado, clique em Editar para abrir a caixa de diálogo Editar Componente e atualize o seguinte código de espaço reservado com o código específico do provedor:
    Método Local no Editor (Modelo Personalizado) Código do Espaço Reservado (Modelo Personalizado)
    transformRequestPayload Linhas 23-25
    transformRequestPayload: async (event, context) => {
          return event.payload;
        },
    transformResponsePayload Linhas 33-35
        transformResponsePayload: async (event, context) => {
          return event.payload;
        },
    transformErrorResponsePayload Linhas 44-46
        transformErrorResponsePayload: async (event, context) => {
          return event.payload;
        }
  8. Verifique a sincronização de suas atualizações clicando em Validar. Corrija os erros de validação (se houver) e clique em Salvar. Em seguida, clique em Fechar.

Amostras de Código de Transformação do Provedor LLM

OpenAI do Azure

Método Código de Transformação do Processador de Eventos
Solicitação
transformRequestPayload: async (event, context) => {
  let payload = { "model": "gpt-4-0314",
                    "messages": event.payload.messages.map(m => { return {"role": m.role, "content": m.content}; }),                     "max_tokens": event.payload.maxTokens,
                    "temperature": event.payload.temperature,
                    "stream": event.payload.streamResponse
                  };
  return payload;
},
Resposta (Não Streaming)
transformResponsePayload: async (event, context) => {
     let llmPayload = {};      
     if (event.payload.responseItems) {
       // streaming case
       llmPayload.responseItems = [];
       event.payload.responseItems
           .filter(item => item.choices.length > 0)
           .forEach(item => {
         llmPayload.responseItems.push({"candidates": item.choices.map( c => {return {"content": c.delta.content || "" };})});
       });
     } else {
        // non-streaming case
        llmPayload.candidates = event.payload.choices.map( c => {return {"content": c.message.content || "" };});
     } 
     return llmPayload;
   }
Quando o streaming está ativado, o handler de eventos de transformação de resposta é chamado em batches de 20 mensagens transmitidas. Esse array em batch de respostas transmitidas é armazenado na chave responseItems.
Erro
transformErrorResponsePayload: async (event, context) => {
  let errorCode = 'unknown';
  if (event.payload.error) {
    if ( 'context_length_exceeded' === event.payload.error.code) {
      errorCode = 'modelLengthExceeded';
    }  else if ('content_filter' === event.payload.error.code) {
      errorCode = 'flagged'; 
    } 
    return {"errorCode" : errorCode, "errorMessage": event.payload.error.message};
  } else {
    return {"errorCode" : errorCode, "errorMessage": JSON.stringify(event.payload)};
  }   
}

Oracle Generative AI Service - Cohere

Observação

Os modelos de comando foram retirados. Recomendamos que você migre para o ponto final /chat, que envolve declarar um modelo de chat mais recente e substituir os parâmetros específicos do comando por parâmetros específicos do chat.
Método Código do Handler de Eventos
Solicitação
transformRequestPayload: async (event, context) => {
      // Cohere doesn't support chat completions, so we first print the system prompt, and if there
      // are additional chat entries, we add these to the system prompt under the heading CONVERSATION HISTORY
      let prompt = event.payload.messages[0].content;
      if (event.payload.messages.length > 1) {
         let history = event.payload.messages.slice(1).reduce((acc, cur) => `${acc}\n${cur.role}: ${cur.content}` , '');
         prompt += `\n\nCONVERSATION HISTORY:${history}\nassistant:`
      }
      // using Cohere
      let modelId = "cohere.command"
      let runtimeType = "COHERE";
       return {
        "compartmentId": event.compartmentId,
        "servingMode": {
          "servingType": "ON_DEMAND",
            "modelId": modelId
        },
        "inferenceRequest": {
          "runtimeType": runtimeType,
          "prompt": prompt,
          "isStream": event.payload.streamResponse,
          "maxTokens": event.payload.maxTokens,
          "temperature": event.payload.temperature,
          // parameters set to default values
          "frequencyPenalty": 0,
          "isEcho": false,
          "numGenerations": 1,
          "presencePenalty": 0,
          "returnLikelihoods": "NONE",
          "topK": 0,
          "topP": 0.75,
          "truncate": "NONE"
        }
      };
}
Resposta
transformResponsePayload: async (event, context) => {      
    let llmPayload = {};
    if (event.payload.responseItems) {
        // streaming case
        llmPayload.responseItems = [];
        event.payload.responseItems.forEach(item => {
          llmPayload.responseItems.push({"candidates": [{"content": item.text || "" }]});
        });
      } else {
        // non-streaming
        llmPayload.candidates = event.payload.inferenceResponse.generatedTexts.map( item => {return {"content": item.text || "" };});
      }
      return llmPayload;
 }
Erro
transformErrorResponsePayload: async (event, context) => {      
      const error = event.payload.message || 'unknown error';
      if (error.startsWith('invalid request: total number of tokens')) {
        // returning modelLengthExceeded error code will cause a retry with reduced chat history
        return {"errorCode" : "modelLengthExceeded", "errorMessage": error};
      } else {
        return {"errorCode" : "unknown", "errorMessage": error};
      }
}

Oracle Generative AI - Llama

Observação

Os modelos de comando foram retirados. Recomendamos que você migre para o ponto final /chat, que envolve declarar o modelo de chat e substituir os parâmetros específicos do comando por parâmetros específicos do chat.
Método Código do Handler de Eventos
Solicitação
transformRequestPayload: async (event, context) => {
      // Cohere doesn't support chat completions, so we first print the system prompt, and if there
      // are additional chat entries, we add these to the system prompt under the heading CONVERSATION HISTORY
      let prompt = event.payload.messages[0].content;
      if (event.payload.messages.length > 1) {
         let history = event.payload.messages.slice(1).reduce((acc, cur) => `${acc}\n${cur.role}: ${cur.content}` , '');
         prompt += `\n\nCONVERSATION HISTORY:${history}\nassistant:`
      }
      // using Llama
      let modelId = "meta.llama-2-70b-chat"
      let runtimeType = "LLAMA";
       return {
        "compartmentId": event.compartmentId,
        "servingMode": {
          "servingType": "ON_DEMAND",
            "modelId": modelId
        },
        "inferenceRequest": {
          "runtimeType": runtimeType,
          "prompt": prompt,
          "isStream": event.payload.streamResponse,
          "maxTokens": event.payload.maxTokens,
          "temperature": event.payload.temperature,
          // parameters set to default values
          "frequencyPenalty": 0,
          "isEcho": false,
          "numGenerations": 1,
          "presencePenalty": 0,
          "returnLikelihoods": "NONE",
          "topK": 0,
          "topP": 0.75,
          "truncate": "NONE"
        }
      };
}
Resposta
transformResponsePayload: async (event, context) => {      
    let llmPayload = {};
    if (event.payload.responseItems) {
        // streaming case
        llmPayload.responseItems = [];
        event.payload.responseItems.forEach(item => {
          llmPayload.responseItems.push({"candidates": [{"content": item.text || "" }]});
        });
      } else {
        // non-streaming
        llmPayload.candidates = event.payload.inferenceResponse.choices.map( item => {return {"content": item.text || "" };});
      }
      return llmPayload;
 }
Erro
transformErrorResponsePayload: async (event, context) => {      
      const error = event.payload.message || 'unknown error';
      if (error.startsWith('invalid request: total number of tokens')) {
        // returning modelLengthExceeded error code will cause a retry with reduced chat history
        return {"errorCode" : "modelLengthExceeded", "errorMessage": error};
      } else {
        return {"errorCode" : "unknown", "errorMessage": error};
      }
}

Oracle Generative AI - Resumir

Observação

Os modelos de comando foram retirados. Recomendamos que você migre para o ponto final /chat, que envolve declarar um modelo de chat mais recente e substituir os parâmetros específicos do comando por parâmetros específicos do chat.
Método Código do Handler de Eventos
Solicitação
transformRequestPayload: async (event, context) => {
      // Cohere doesn't support chat completions, so we first print the system prompt, and if there
      // are additional chat entries, we add these to the system prompt under the heading CONVERSATION HISTORY
      let prompt = event.payload.messages[0].content;
      if (event.payload.messages.length > 1) {
         let history = event.payload.messages.slice(1).reduce((acc, cur) => `${acc}\n${cur.role}: ${cur.content}` , '');
         prompt += `\n\nCONVERSATION HISTORY:${history}\nassistant:`
      }
      let modelId = "cohere.command"
      return {
        "compartmentId": event.compartmentId,
        "servingMode": {
          "servingType": "ON_DEMAND",
          "modelId": modelId
        },
        "input" : prompt,
        "temperature": event.payload.temperature,
        // parameters set to default values
        "length": "AUTO",
        "extractiveness": "AUTO",
        "format": "PARAGRAPH",
        // natural language instructions
        "additionalCommand": "write in a conversational style"
      };
}
Resposta
transformResponsePayload: async (event, context) => {
      let llmPayload = {};
      // non-streaming only: streaming is not supported
      llmPayload.candidates = [{"content": event.payload.summary}];
      return llmPayload;
}
Erro
transformErrorResponsePayload: async (event, context) => {             const error = event.payload.message ||
          'unknown error';      if(error.startsWith('invalid request:
          total number of tokens')) {        // returning modelLengthExceeded error
          code will cause a retry with reduced chat history        return{"errorCode": "modelLengthExceeded", "errorMessage": error};      }
          else{        return{"errorCode": "unknown", "errorMessage": error};      }}

Cohere (Modelo de Comando) - Acesso Direto a Cohere

Os handlers neste código de transformação suportam a API /generate e o modelo Cohere.command associado, não a API /chat usada para o modelo cohere.command.R. Se você migrar para o ponto final /chat, precisará atualizar manualmente os payloads de solicitação e resposta e o modelo de código gerado.
Método Código do Handler de Eventos
Solicitação
transformRequestPayload: async (event, context) => {            
      // Cohere doesn't support chat completions, so we first print the system prompt, and if there
      // are additional chat entries, we add these to the system prompt under the heading CONVERSATION HISTORY
      let prompt = event.payload.messages[0].content;
      if (event.payload.messages.length > 1) {
         let history = event.payload.messages.slice(1).reduce((acc, cur) => `${acc}\n${cur.role}: ${cur.content}` , '');
         prompt += `\n\nCONVERSATION HISTORY:${history}\nassistant:`
      }
      return {
        "max_tokens": event.payload.maxTokens,
        "truncate": "END",
        "return_likelihoods": "NONE",
        "prompt": prompt,
        "model": "command",
        "temperature": event.payload.temperature,
        "stream": event.payload.streamResponse
      };
 }
Esse handler gerencia o histórico de conversas para manter o contexto da conversa.
Resposta
transformResponsePayload: async (event, context) => {
  let llmPayload = {};      
  if (event.payload.responseItems) {
        // streaming case
        llmPayload.responseItems = [];
        event.payload.responseItems.forEach(item => {
          llmPayload.responseItems.push({"candidates": [{"content": item.text || "" }]});
        });
      } else {
        // non-streaming
        llmPayload.candidates = event.payload.generations.map( item => {return {"content": item.text || "" };});
      }
   return llmPayload;
}
Erro
transformErrorResponsePayload: async (event, context) => {      
    // NOTE: Depending on the Cohere version, this code might need to be updated
      const error = event.payload.message || 'unknown error';
      if (error.startsWith('invalid request: total number of tokens')) {
        // returning modelLengthExceeded error code will cause a retry with reduced chat history
        return {"errorCode" : "modelLengthExceeded", "errorMessage": error};
      } else {
        return {"errorCode" : "unknown", "errorMessage": error};
      }
 
}

A Interface LLM Comum

Cada provedor de LLM tem seu próprio formato para seus payloads de solicitação e resposta. A Interface LLM Comum, ou CLMI, permite que o componente invokeLLM trate esses payloads de solicitação e resposta proprietários.

O CMLI consiste no seguinte:
  • Uma especificação do corpo da solicitação.
  • Uma especificação de corpo de resposta bem-sucedida, aplicável quando a chamada REST LLM retorna um status HTTP 200.
  • Uma especificação de corpo de resposta de erro, aplicável quando a chamada REST LLM retorna um status HTTP diferente de 200, mas a chamada do serviço LLM ainda foi bem-sucedida.
    Observação

    Para chamadas malsucedidas, o componente invokeLLM trata os erros 401 (não autorizado) ou 500 (erro interno do servidor).

CLMI - Especificação do Corpo da Solicitação

O corpo da solicitação JSON CLMI contém as seguintes propriedades:
Propriedade Tipo Padrão Descrição 0brigatório?
messages Um array de objetos de mensagem N/A Uma lista de mensagens. A primeira mensagem é o prompt com a propriedade role definida como system. Se o LLM suportar uma conversa de várias voltas para que a resposta do LLM possa ser refinada ou aprimorada, as mensagens subsequentes serão pares de mensagens das atribuições user e assistant. A mensagem do usuário contém as instruções de acompanhamento ou a pergunta do LLM. A mensagem do assistente contém a resposta LLM para a mensagem do usuário (a conclusão). Se o LLM não suportar conversas de várias voltas, o array messages conterá apenas uma única mensagem do sistema que contém o prompt. Sim
streamResponse booleano false Determina se a resposta do LLM será transmitida de volta para o componente LLM. A definição dessa propriedade como true aprimora a experiência do usuário, porque o streaming permite que o componente LLM envie mensagens de resposta parciais de volta aos usuários para que eles não tenham que esperar que o LLM conclua a resposta.

Defina streamResponse como false quando a validação da resposta for usada. Como toda a mensagem é necessária para que a validação possa ocorrer, a mensagem pode ser renderizada para usuários várias vezes: primeiro transmitida, depois validada e depois transmitida novamente.

No
maxTokens inteiro 1024 O modelo gera tokens para as palavras em seus resultados. Tokens podem ser pensados como pedaços de palavras. 100 tokens equivale a cerca de 75 palavras em inglês, por exemplo. Essa propriedade limita o tamanho do conteúdo gerado pelo modelo definindo o número máximo de tokens que ele gera para a resposta. No
temperature número 0 O modelo usa a temperatura para medir a aleatoriedade - e, portanto, a criatividade - de suas respostas. Você define isso como um valor que varia de 0 (resultados previsíveis) a 1 (resultados mais randomizados). 0 significa que o modelo enviará os mesmos resultados para um determinado prompt. 1 significa que os resultados do modelo para uma resposta fornecida podem variar muito. Use um handler de eventos para aplicar um multiplicador se o provedor LLM suportar um intervalo diferente de 0 a 1. No
user string N/A Um identificador exclusivo que representa seu usuário final, que pode ser usado para monitorar e detectar linguagem abusiva. No
providerExtension objeto N/A Ativa opções de configuração específicas do provedor LLM que não são definidas como parte do CLMI. No

A Estrutura do Objeto de Mensagem

Propriedade Tipo Descrição 0brigatório?
role string O criador da mensagem. Os valores são system, user e assistant. Sim
content string O conteúdo da mensagem Sim
turn inteiro Um número que indica o giro de refinamento atual da troca de mensagens de chat. Quando o primeiro prompt é enviado para o LLM, o turno é 1. Sim
retry booleano Um flag que indica se a mensagem é enviada ao LLM para corrigir um erro na resposta Não (o padrão é false)
tag string Uma tag personalizada que marca um prompt específico. Se você estiver melhorando a resposta LLM usando RCI (Recursive Criticism and Improvement), poderá ativar a lógica personalizada no handler validateResponsePayload para detectar a etapa atual do processo RCI definindo a tag como "criticize" ou "improve". No

Especificação do Corpo de Resposta com Êxito

Propriedade Tipo Descrição 0brigatório?
candidates Um array de objetos candidate Uma lista de mensagens candidatas retornadas pelo LLM Sim

Objetos Candidatos

O corpo da solicitação JSON CLMI contém as seguintes propriedades:
Propriedade Tipo Descrição 0brigatório?
content string O conteúdo da mensagem Sim

Especificação do Corpo da Resposta do Erro

O corpo da resposta de erro do JSON CLMI contém as seguintes propriedades:
Propriedade Tipo Descrição 0brigatório?
errorCode String
  • notAuthorized: Indica que a solicitação LLM não tem a chave de autorização adequada.
  • modelLengthExceeded: Indica que a combinação de mensagens de solicitação (o prompt do sistema junto com as mensagens de refinamento do usuário e do assistente) e o número máximo de tokens excedem o limite de token do modelo.
  • requestFlagged: Indica que o LLM não pode atender à solicitação porque viola as políticas de moderação do provedor LLM. Por exemplo, solicitações que contenham conteúdo racista ou sexualmente abusivo seriam sinalizadas.
  • responseFlagged: Indica que a resposta LLM viola as políticas de moderação do provedor LLM. Por exemplo, respostas que contêm conteúdo tóxico, como linguagem racista ou sexualmente abusiva, seriam sinalizadas.
  • requestInvalid: Indica que a solicitação para o LLM é inválida. Por exemplo, a solicitação não é válida porque falhou em algumas das regras de validação definidas em um handler de eventos ou está em um formato que não é reconhecido pelo LLM.
  • responseInvalid: Indica que a resposta gerada pelo LLM é inválida. Por exemplo, a resposta não é válida porque falhou em algumas das regras de validação definidas em um processador de eventos de validação.
  • unknown: Quando outros erros ocorrem.
Sim
errorMessage String A mensagem de erro específica do provedor LLM. Pode ser um objeto JSON stringificado. Sim