For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to the page URL.
Navegação principal

Conversas em tempo real

Saiba como gerenciar conversas de fala para fala em tempo real.

Depois de se conectar à Realtime API por WebRTC ou WebSocket, você pode chamar um modelo Realtime (como gpt-realtime-2.1) para ter conversas de fala para fala. Para isso, você precisará enviar eventos de cliente para iniciar ações e escutar eventos de servidor para responder às ações da Realtime API.

Este guia apresenta os fluxos de eventos necessários para usar capacidades do modelo, como geração de áudio e texto, entrada de imagens e chamada de função, além de explicar como entender o estado de uma sessão em tempo real.

Se você não precisa conversar com o modelo, ou seja, não espera nenhuma resposta, pode usar a Realtime API no modo de transcrição.

Sessões de fala para fala em tempo real

Uma sessão em tempo real é uma interação que mantém estado entre o modelo e um cliente conectado. Os principais componentes da sessão são:

  • O objeto Sessão , que controla os parâmetros da interação, como o modelo em uso, a voz usada para gerar a saída e outras configurações.
  • Uma Conversa, que representa os itens de entrada do usuário e os itens de saída do modelo gerados durante a sessão atual.
  • Respostas, que são itens de áudio ou texto gerados pelo modelo e adicionados à Conversa.

Buffer de áudio de entrada e WebSockets

Se você usa WebRTC, as APIs de WebRTC ajudam a realizar boa parte do tratamento de mídia necessário para enviar áudio ao modelo e receber áudio dele.


Se você usa WebSockets para áudio, precisará interagir manualmente com o buffer de áudio de entrada , enviando áudio ao servidor em eventos JSON com áudio codificado em base64.

Todos esses componentes juntos formam uma sessão em tempo real. Você usará eventos do cliente para atualizar o estado da sessão e escutará eventos do servidor para reagir às mudanças de estado dentro dela.

Diagrama do estado de uma sessão em tempo real

Eventos do ciclo de vida da sessão

Após iniciar uma sessão por WebRTC ou WebSockets, o servidor enviará um evento session.created indicando que a sessão está pronta. No cliente, você pode atualizar a configuração da sessão atual com o evento session.update. A maioria das propriedades da sessão pode ser atualizada a qualquer momento. A exceção é a propriedade voice, que define a voz usada pelo modelo na saída de áudio: ela não pode ser alterada depois que o modelo responder com áudio pela primeira vez na sessão. A duração máxima de uma sessão em tempo real é de 60 minutos.

O exemplo a seguir mostra como atualizar a sessão com um evento de cliente session.update. Consulte o guia de WebRTC ou de WebSocket para saber mais sobre o envio de eventos de cliente por esses canais.

Atualize as instruções de sistema usadas pelo modelo nesta sessão
const event = {
  type: "session.update",
  session: {
    type: "realtime",
    model: "gpt-realtime-2.1",
    // Lock the output to audio (set to ["text"] if you want text without audio)
    output_modalities: ["audio"],
    audio: {
      input: {
        format: {
          type: "audio/pcm",
          rate: 24000,
        },
        turn_detection: {
          type: "semantic_vad",
        },
      },
      output: {
        format: {
          type: "audio/pcm",
        },
        voice: "marin",
      },
    },
    // Use a server-stored prompt by ID. Optionally pin a version and pass variables.
    prompt: {
      id: "pmpt_123", // your stored prompt ID
      version: "89", // optional: pin a specific version
      variables: {
        city: "Paris", // example variable used by your prompt
      },
    },
    // You can still set direct session fields; these override prompt fields if they overlap:
    instructions:
      "Speak clearly and briefly. Confirm understanding before taking actions.",
  },
};

// WebRTC data channel and WebSocket both have .send()
dataChannel.send(JSON.stringify(event));

Quando a sessão for atualizada, o servidor emitirá um evento session.updated com o novo estado da sessão.

Eventos relacionados do cliente Eventos relacionados do servidor

session.update

session.created

session.updated

Entradas e saídas de texto

Para gerar texto com um modelo Realtime, você pode adicionar entradas de texto à conversa atual, pedir ao modelo que gere uma resposta e escutar os eventos enviados pelo servidor que indicam o progresso da resposta. Para gerar texto, a sessão deve estar configurada com a modalidade text (essa é a configuração padrão).

Crie um novo item de texto na conversa usando o evento do cliente conversation.item.create. Isso é semelhante a enviar uma mensagem do usuário (prompt) em Chat Completions na API REST.

Crie um item de conversa com a entrada do usuário
const event = {
  type: "conversation.item.create",
  item: {
    type: "message",
    role: "user",
    content: [
      {
        type: "input_text",
        text: "What Prince album sold the most copies?",
      },
    ],
  },
};

// WebRTC data channel and WebSocket both have .send()
dataChannel.send(JSON.stringify(event));

Depois de adicionar a mensagem do usuário à conversa, envie o evento response.create para iniciar uma resposta do modelo. Se áudio e texto estiverem habilitados na sessão atual, o modelo responderá com conteúdo em áudio e texto. Se quiser gerar apenas texto, você pode especificar isso ao enviar o evento do cliente response.create, como mostrado abaixo.

Gere uma resposta somente em texto
const event = {
  type: "response.create",
  response: {
    output_modalities: ["text"],
  },
};

// WebRTC data channel and WebSocket both have .send()
dataChannel.send(JSON.stringify(event));

Quando a resposta estiver totalmente concluída, o servidor emitirá o evento response.done. Esse evento conterá o texto completo gerado pelo modelo, como mostrado abaixo.

Escute response.done para ver os resultados finais
function handleEvent(message) {
  const data = "data" in message ? message.data : message.toString();
  const serverEvent = JSON.parse(data);
  if (serverEvent.type === "response.done") {
    console.log(serverEvent.response.output[0]);
  }
}

// Listen for server messages (WebRTC)
dataChannel.addEventListener("message", handleEvent);

// Listen for server messages (WebSocket)
// ws.on("message", handleEvent);

Enquanto a resposta do modelo estiver sendo gerada, o servidor emitirá vários eventos do ciclo de vida. Você pode escutar esses eventos, como response.output_text.delta, para fornecer feedback em tempo real aos usuários durante a geração da resposta. A lista completa dos eventos emitidos pelo servidor está abaixo, em eventos relacionados do servidor. Eles são apresentados na ordem aproximada em que são emitidos, junto com os eventos do cliente relevantes para a geração de texto.

Eventos relacionados do cliente Eventos relacionados do servidor

conversation.item.create

response.create

conversation.item.added

conversation.item.done

response.created

response.output_item.added

response.content_part.added

response.output_text.delta

response.output_text.done

response.content_part.done

response.output_item.done

response.done

rate_limits.updated

Entradas e saídas de áudio

Um dos recursos mais poderosos da Realtime API é a interação de voz para voz com o modelo, sem uma etapa intermediária de conversão de texto em fala ou de fala em texto. Isso permite reduzir a latência das interfaces de voz e fornece ao modelo mais dados sobre o tom e a entonação da entrada de voz.

Opções de voz

As sessões em tempo real podem ser configuradas para usar uma das várias vozes integradas ao produzir saída de áudio. Você pode definir voice na criação da sessão (ou em um evento response.create) para controlar a voz do modelo. As opções de voz atuais são alloy, ash, ballad, coral, echo, sage, shimmer, verse, marin e cedar. Depois que o modelo emitir áudio em uma sessão, voice não poderá ser alterado nessa sessão. Para obter a melhor qualidade, recomendamos usar marin ou cedar.

Como lidar com áudio usando WebRTC

Se você se conecta à Realtime API usando WebRTC, a Realtime API atua como um par em uma conexão ponto a ponto com seu cliente. A saída de áudio do modelo é entregue ao cliente como um fluxo de mídia remoto. A entrada de áudio do modelo é capturada por dispositivos de áudio (getUserMedia), e os fluxos de mídia são adicionados como faixas à conexão ponto a ponto.

O código de exemplo do guia de conexão por WebRTC mostra uma configuração básica de áudio local e remoto usando APIs do navegador:

// Create a peer connection
const pc = new RTCPeerConnection();

// Set up to play remote audio from the model
const audioEl = document.createElement("audio");
audioEl.autoplay = true;
pc.ontrack = (e) => (audioEl.srcObject = e.streams[0]);

// Add local audio track for microphone input in the browser
const ms = await navigator.mediaDevices.getUserMedia({
  audio: true,
});
pc.addTrack(ms.getTracks()[0]);

O trecho de código acima permite interagir com a Realtime API, mas há muito mais que você pode fazer. Para ver mais exemplos de diferentes tipos de interfaces de usuário, confira o repositório de exemplos de WebRTC. Você também pode encontrar aqui demonstrações desses exemplos em funcionamento.

Usar capturas e fluxos de mídia no navegador permite, por exemplo, silenciar e reativar microfones, selecionar o dispositivo de entrada e muito mais.

Eventos do cliente e do servidor para áudio em WebRTC

Por padrão, os clientes WebRTC não precisam enviar nenhum evento do cliente à Realtime API antes de enviar entradas de áudio. Assim que uma faixa de áudio local é adicionada à conexão ponto a ponto, seus usuários já podem começar a falar!

No entanto, os clientes WebRTC ainda recebem vários eventos do ciclo de vida enviados pelo servidor enquanto o áudio trafega entre cliente e servidor pela conexão ponto a ponto. Alguns exemplos:

Usar as APIs de WebRTC para manipular fluxos de mídia pode oferecer todo o controle de que você precisa. No entanto, às vezes pode ser necessário usar interfaces de nível mais baixo para entrada e saída de áudio. Consulte a seção sobre WebSockets abaixo para obter mais informações e uma lista dos eventos necessários para controlar a entrada de áudio com mais precisão.

Como lidar com áudio usando WebSockets

Ao enviar e receber áudio por um WebSocket, você terá um pouco mais de trabalho para enviar mídia do cliente e receber mídia do servidor. A tabela abaixo descreve o fluxo de eventos necessários durante uma sessão WebSocket para enviar e receber áudio pela conexão.

Os eventos abaixo são apresentados na ordem do ciclo de vida, embora alguns deles (como os eventos delta) possam ocorrer simultaneamente.

Etapa do ciclo de vida Eventos do cliente Eventos do servidor
Inicialização da sessão

session.update

session.created

session.updated

Entrada de áudio do usuário

conversation.item.create


  (enviar a mensagem de áudio inteira)

input_audio_buffer.append


  (transmitir áudio em blocos)

input_audio_buffer.commit


  (usado quando a VAD está desativada)

response.create


  (usado quando a VAD está desativada)

input_audio_buffer.speech_started

input_audio_buffer.speech_stopped

input_audio_buffer.committed

Saída de áudio do servidor

input_audio_buffer.clear


  (usado quando a VAD está desativada)

conversation.item.added

conversation.item.done

response.created

response.output_item.added

response.content_part.added

response.output_audio.delta

response.output_audio.done

response.output_audio_transcript.delta

response.output_audio_transcript.done

response.output_text.delta

response.output_text.done

response.content_part.done

response.output_item.done

response.done

rate_limits.updated

Transmissão de áudio de entrada para o servidor

Para transmitir áudio de entrada para o servidor, você pode usar o evento de cliente input_audio_buffer.append. Esse evento exige que você envie blocos de bytes de áudio codificados em Base64 para a Realtime API pelo socket. Cada bloco não pode exceder 15 MB.

O formato dos blocos de entrada pode ser configurado para toda a sessão ou por resposta.

Acrescente bytes de áudio de entrada à conversa
import fs from "fs";
import decodeAudio from "audio-decode";

// Converts Float32Array of audio data to PCM16 ArrayBuffer
function floatTo16BitPCM(float32Array) {
  const buffer = new ArrayBuffer(float32Array.length * 2);
  const view = new DataView(buffer);
  let offset = 0;
  for (let i = 0; i < float32Array.length; i++, offset += 2) {
    let s = Math.max(-1, Math.min(1, float32Array[i]));
    view.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7fff, true);
  }
  return buffer;
}

// Converts a Float32Array to base64-encoded PCM16 data
function base64EncodeAudio(float32Array) {
  const arrayBuffer = floatTo16BitPCM(float32Array);
  let binary = "";
  let bytes = new Uint8Array(arrayBuffer);
  const chunkSize = 0x8000; // 32KB chunk size
  for (let i = 0; i < bytes.length; i += chunkSize) {
    let chunk = bytes.subarray(i, i + chunkSize);
    binary += String.fromCharCode(...chunk);
  }
  return btoa(binary);
}

// Fills the audio buffer with the contents of three files,
// then asks the model to generate a response.
const files = [
  "fixtures/sample1.wav",
  "fixtures/sample2.wav",
  "fixtures/sample3.wav",
];

for (const filename of files) {
  const audioFile = fs.readFileSync(filename);
  const audioBuffer = await decodeAudio(audioFile);
  const channelData = audioBuffer.channelData[0];
  const base64Chunk = base64EncodeAudio(channelData);
  ws.send(
    JSON.stringify({
      type: "input_audio_buffer.append",
      audio: base64Chunk,
    })
  );
}

ws.send(JSON.stringify({ type: "input_audio_buffer.commit" }));
ws.send(JSON.stringify({ type: "response.create" }));

Envie mensagens de áudio completas

Também é possível criar mensagens na conversa que contenham gravações de áudio completas. Use o evento de cliente conversation.item.create para criar mensagens com conteúdo input_audio.

Crie itens de conversa com áudio de entrada completo
const fullAudio = "<a base64-encoded string of audio bytes>";

const event = {
  type: "conversation.item.create",
  item: {
    type: "message",
    role: "user",
    content: [
      {
        type: "input_audio",
        audio: fullAudio,
      },
    ],
  },
};

// WebRTC data channel and WebSocket both have .send()
dataChannel.send(JSON.stringify(event));

Como trabalhar com a saída de áudio de um WebSocket

Para reproduzir o áudio de saída em um dispositivo cliente, como um navegador Web, recomendamos usar WebRTC em vez de WebSockets. O WebRTC oferece mais robustez no envio de mídia a dispositivos clientes em condições de rede imprevisíveis.

Mas, para trabalhar com a saída de áudio em aplicações que se comunicam entre servidores usando um WebSocket, você precisará escutar os eventos response.output_audio.delta que contêm os blocos de dados de áudio do modelo codificados em Base64. Você precisará armazenar esses blocos em um buffer e gravá-los em um arquivo, ou talvez transmiti-los imediatamente para outro destino, como uma chamada telefônica com a Twilio.

Observe que os eventos response.output_audio.done e response.done não contêm dados de áudio, apenas transcrições do conteúdo do áudio. Para obter os bytes em si, você precisará escutar os eventos response.output_audio.delta.

O formato dos blocos de saída pode ser configurado para toda a sessão ou por resposta.

Escute os eventos response.output_audio.delta
function handleEvent(message) {
  const serverEvent = JSON.parse(message.toString());
  if (serverEvent.type === "response.output_audio.delta") {
    // Access Base64-encoded audio chunks
    // console.log(serverEvent.delta);
  }
}

// Listen for server messages (WebSocket)
ws.on("message", handleEvent);

Entradas de imagem

gpt-realtime-2 e gpt-realtime também aceitam entradas de imagem. Você pode anexar uma imagem como parte do conteúdo de uma mensagem do usuário, e o modelo pode incorporar o conteúdo da imagem ao responder.

Adicione uma imagem à conversa
const base64Image = "<a base64-encoded string of image bytes>";

const event = {
  type: "conversation.item.create",
  item: {
    type: "message",
    role: "user",
    content: [
      {
        type: "input_image",
        image_url: `data:image/{format};base64,${base64Image}`,
      },
    ],
  },
};

// WebRTC data channel and WebSocket both have .send()
dataChannel.send(JSON.stringify(event));

Detecção de atividade de voz

Por padrão, as sessões em tempo real têm a detecção de atividade de voz (VAD) ativada. Isso significa que a API determinará quando o usuário começou ou parou de falar e responderá automaticamente.

Saiba mais sobre como configurar a VAD no nosso guia de detecção de atividade de voz.

Desative a VAD

A VAD pode ser desativada definindo turn_detection como null com o evento de cliente session.update. Isso pode ser útil em interfaces nas quais você deseja ter controle granular sobre a entrada de áudio, como interfaces de pressionar para falar.

Quando a VAD está desativada, o cliente precisa emitir manualmente alguns eventos de cliente adicionais para acionar respostas em áudio:

Mantenha a VAD, mas desative as respostas automáticas

Se quiser manter o modo VAD ativado, mas decidir manualmente quando uma resposta será gerada, você pode definir turn_detection.interrupt_response e turn_detection.create_response como false com o evento de cliente session.update. Isso manterá todo o comportamento da VAD, mas não criará novas Respostas automaticamente. Os clientes podem acioná-las manualmente com um evento response.create.

Isso pode ser útil para moderação, validação de entradas ou padrões de RAG, quando você aceita um pouco mais de latência na interação em troca de controle sobre as entradas.

Crie respostas fora da conversa padrão

Por padrão, todas as respostas geradas durante uma sessão são adicionadas ao estado da conversa da sessão (a "conversa padrão"). No entanto, você pode querer gerar respostas do modelo fora do contexto da conversa padrão da sessão ou gerar várias respostas simultaneamente. Também pode querer controlar com mais precisão quais itens da conversa são considerados pelo modelo ao gerar uma resposta (por exemplo, apenas os últimos N turnos).

É possível gerar respostas "fora de banda", que não são adicionadas ao estado da conversa padrão, definindo o campo response.conversation como a string none ao criar uma resposta com o evento de cliente response.create.

Ao criar uma resposta fora de banda, você provavelmente também vai querer uma forma de identificar quais eventos enviados pelo servidor pertencem a essa resposta. Você pode fornecer metadata para a resposta do modelo, o que ajudará a identificar qual resposta está sendo gerada para esse evento enviado pelo cliente.

Crie uma resposta do modelo fora de banda
const prompt = `
Analyze the conversation so far. If it is related to support, output
"support". If it is related to sales, output "sales".
`;

const event = {
  type: "response.create",
  response: {
    // Setting to "none" indicates the response is out of band
    // and will not be added to the default conversation
    conversation: "none",

    // Set metadata to help identify responses sent back from the model
    metadata: { topic: "classification" },

    // Set any other available response fields
    output_modalities: ["text"],
    instructions: prompt,
  },
};

// WebRTC data channel and WebSocket both have .send()
dataChannel.send(JSON.stringify(event));

Agora, ao escutar o evento de servidor response.done, você pode identificar o resultado da sua resposta fora de banda.

Crie uma resposta do modelo fora de banda
function handleEvent(message) {
  const data = "data" in message ? message.data : message.toString();
  const serverEvent = JSON.parse(data);
  if (
    serverEvent.type === "response.done" &&
    serverEvent.response.metadata?.topic === "classification"
  ) {
    // this server event pertained to our OOB model response
    console.log(serverEvent.response.output[0]);
  }
}

// Listen for server messages (WebRTC)
dataChannel.addEventListener("message", handleEvent);

// Listen for server messages (WebSocket)
// ws.on("message", handleEvent);

Crie um contexto personalizado para as respostas

Você também pode construir um contexto personalizado que o modelo usará para gerar uma resposta fora da conversa padrão/atual. Para isso, use o array input em um evento de cliente response.create. Você pode usar novas entradas ou referenciar pelo ID os itens de entrada existentes na conversa.

Escute a resposta do modelo fora de banda com contexto personalizado
const event = {
  type: "response.create",
  response: {
    conversation: "none",
    metadata: { topic: "pizza" },
    output_modalities: ["text"],

    // Create a custom input array for this request with whatever context
    // is appropriate
    input: [
      // potentially include existing conversation items:
      {
        type: "item_reference",
        id: "some_conversation_item_id",
      },
      {
        type: "message",
        role: "user",
        content: [
          {
            type: "input_text",
            text: "Is it okay to put pineapple on pizza?",
          },
        ],
      },
    ],
  },
};

// WebRTC data channel and WebSocket both have .send()
dataChannel.send(JSON.stringify(event));

Crie respostas sem contexto

Você também pode inserir respostas na conversa padrão, ignorando todas as outras instruções e o contexto. Para isso, defina input como um array vazio.

Insira respostas do modelo sem contexto na conversa padrão
const prompt = `
Say exactly the following:
I'm a little teapot, short and stout!
This is my handle, this is my spout!
`;

const event = {
  type: "response.create",
  response: {
    // An empty input array removes existing context
    input: [],
    instructions: prompt,
  },
};

// WebRTC data channel and WebSocket both have .send()
dataChannel.send(JSON.stringify(event));

Chamada de função

Os modelos Realtime também oferecem suporte à chamada de função, que permite executar código personalizado para ampliar as capacidades do modelo. Veja como isso funciona em linhas gerais:

  1. Ao atualizar a sessão ou criar uma resposta, você pode especificar uma lista de funções disponíveis para o modelo chamar.
  2. Se, ao processar a entrada, o modelo determinar que deve fazer uma chamada de função, ele adicionará à conversa itens que representam os argumentos dessa chamada.
  3. Quando o cliente detectar itens da conversa que contenham argumentos de uma chamada de função, ele executará código personalizado usando esses argumentos
  4. Após a execução do código personalizado, o cliente criará novos itens na conversa com a saída da chamada de função e pedirá ao modelo que responda.

Vamos ver como isso funcionaria na prática adicionando uma função que o modelo pode chamar para fornecer o horóscopo do dia aos usuários. Mostraremos a estrutura dos objetos de evento do cliente que precisam ser enviados e o que o servidor emitirá em seguida.

Configure funções que o modelo pode chamar

Primeiro, precisamos fornecer ao modelo um conjunto de funções que ele pode chamar com base na entrada do usuário. As funções disponíveis podem ser configuradas tanto para a sessão quanto para cada resposta individual.

Veja um exemplo de payload de evento do cliente para session.update que configura uma função de geração de horóscopo. Essa função recebe um único argumento: o signo para o qual o horóscopo deve ser gerado.

session.update

{
  "type": "session.update",
  "session": {
    "tools": [
      {
        "type": "function",
        "name": "generate_horoscope",
        "description": "Give today's horoscope for an astrological sign.",
        "parameters": {
          "type": "object",
          "properties": {
            "sign": {
              "type": "string",
              "description": "The sign for the horoscope.",
              "enum": [
                "Aries",
                "Taurus",
                "Gemini",
                "Cancer",
                "Leo",
                "Virgo",
                "Libra",
                "Scorpio",
                "Sagittarius",
                "Capricorn",
                "Aquarius",
                "Pisces"
              ]
            }
          },
          "required": ["sign"]
        }
      }
    ],
    "tool_choice": "auto"
  }
}

Os campos description da função e dos parâmetros ajudam o modelo a decidir se deve chamar a função e quais dados incluir em cada parâmetro. Se o modelo receber uma entrada que indique que o usuário quer saber seu horóscopo, ele chamará essa função com um parâmetro sign.

Detecte quando o modelo quer chamar uma função

Com base nas entradas recebidas, o modelo pode decidir chamar uma função para gerar a melhor resposta. Suponha que nosso aplicativo adicione o seguinte item à conversa com um evento conversation.item.create e depois crie uma resposta:

{
  "type": "conversation.item.create",
  "item": {
    "type": "message",
    "role": "user",
    "content": [
      {
        "type": "input_text",
        "text": "What is my horoscope? I am an aquarius."
      }
    ]
  }
}

Em seguida, um evento do cliente response.create é enviado para gerar uma resposta:

{
  "type": "response.create"
}

Em vez de retornar imediatamente uma resposta em texto ou áudio, o modelo gerará uma resposta contendo os argumentos que devem ser passados a uma função no aplicativo do desenvolvedor. Você pode acompanhar as atualizações dos argumentos da chamada de função em tempo real por meio do evento do servidor response.function_call_arguments.delta, mas response.done também conterá todos os dados necessários para chamar nossa função.

response.done

{
    "type": "response.done",
    "event_id": "event_AeqLA8iR6FK20L4XZs2P6",
    "response": {
        "object": "realtime.response",
        "id": "resp_AeqL8XwMUOri9OhcQJIu9",
        "status": "completed",
        "status_details": null,
        "output": [
            {
                "object": "realtime.item",
                "id": "item_AeqL8gmRWDn9bIsUM2T35",
                "type": "function_call",
                "status": "completed",
                "name": "generate_horoscope",
                "call_id": "call_sHlR7iaFwQ2YQOqm",
                "arguments": "{\"sign\":\"Aquarius\"}"
            }
        ],
        ...
    }
}

No JSON emitido pelo servidor, podemos detectar que o modelo quer chamar uma função personalizada:

PropriedadeFinalidade na chamada de função
response.output[0].typeQuando definida como function_call, indica que esta resposta contém argumentos para a chamada de uma função identificada pelo nome.
response.output[0].nameO nome da função configurada a ser chamada, neste caso, generate_horoscope
response.output[0].argumentsUma string JSON contendo os argumentos da função. No nosso caso, "{\"sign\":\"Aquarius\"}".
response.output[0].call_idUm ID gerado pelo sistema para esta chamada de função. Você precisará desse ID para enviar o resultado de uma chamada de função de volta ao modelo.

Com essas informações, podemos executar código em nosso aplicativo para gerar o horóscopo e depois enviar essas informações de volta ao modelo para que ele gere uma resposta.

Forneça os resultados de uma chamada de função ao modelo

Ao receber uma resposta do modelo com argumentos para uma chamada de função, seu aplicativo pode executar o código que realiza a operação solicitada. Esse código pode fazer o que você quiser, como se comunicar com APIs externas ou acessar bancos de dados.

Quando estiver pronto para fornecer ao modelo os resultados do seu código personalizado, você poderá criar um novo item na conversa contendo o resultado por meio do evento do cliente conversation.item.create.

{
  "type": "conversation.item.create",
  "item": {
    "type": "function_call_output",
    "call_id": "call_sHlR7iaFwQ2YQOqm",
    "output": "{\"horoscope\": \"You will soon meet a new friend.\"}"
  }
}
  • O tipo do item da conversa é function_call_output
  • item.call_id é o mesmo ID que recebemos no evento response.done acima
  • item.output é uma string JSON contendo os resultados da nossa chamada de função

Depois de adicionar o item da conversa contendo os resultados da chamada de função, emitimos novamente o evento response.create a partir do cliente. Isso acionará uma resposta do modelo usando os dados da chamada de função.

{
  "type": "response.create"
}

Tratamento de erros

O evento error é emitido pelo servidor sempre que ocorre um erro no servidor durante a sessão. Às vezes, é possível identificar um evento do cliente emitido pelo seu aplicativo como a origem desses erros.

Diferentemente das requisições e respostas HTTP, em que uma resposta está implicitamente vinculada a uma requisição do cliente, precisamos usar uma propriedade event_id nos eventos do cliente para saber quando um deles causou um erro no servidor. Essa técnica é mostrada no código abaixo, em que o cliente tenta emitir um tipo de evento não suportado.

const event = {
  event_id: "my_awesome_event",
  type: "scooby.dooby.doo",
};

dataChannel.send(JSON.stringify(event));

Esse evento malsucedido enviado pelo cliente resultará na emissão de um evento de erro como o seguinte:

{
  "type": "invalid_request_error",
  "code": "invalid_value",
  "message": "Invalid value: 'scooby.dooby.doo' ...",
  "param": "type",
  "event_id": "my_awesome_event"
}

Interrupção e truncamento

Em muitos aplicativos de voz, o usuário pode interromper o modelo enquanto ele está falando. Quando a VAD está habilitada, a Realtime API lida com interrupções detectando a fala do usuário, cancelando a resposta em andamento e iniciando uma nova. Nesse cenário, porém, é importante que o modelo saiba em que ponto foi interrompido para continuar a conversa naturalmente (por exemplo, se o usuário perguntar "qual foi a última coisa que você disse?"). Chamamos isso de truncar a última resposta do modelo, ou seja, remover da conversa a parte dessa resposta que não foi reproduzida.

Nas conexões WebRTC e SIP, o servidor gerencia um buffer de áudio de saída e, por isso, sabe quanto áudio já foi reproduzido em determinado momento. O servidor truncará automaticamente o áudio não reproduzido quando o usuário interromper.

Em uma conexão WebSocket, o cliente gerencia a reprodução de áudio e, por isso, deve parar a reprodução e lidar com o truncamento. Veja como funciona esse procedimento:

  1. O cliente monitora novos eventos input_audio_buffer.speech_started do servidor, que indicam que o usuário começou a falar. O servidor cancelará automaticamente qualquer resposta do modelo em andamento, e um evento response.cancelled será emitido.
  2. Quando o cliente detectar esse evento, deverá parar imediatamente a reprodução de qualquer áudio do modelo que esteja sendo reproduzido. Ele deverá registrar quanto da última resposta em áudio foi reproduzido antes da interrupção.
  3. O cliente deve enviar um evento conversation.item.truncate para remover da conversa a parte não reproduzida da última resposta do modelo.

Veja um exemplo:

{
    "type": "conversation.item.truncate",
    "item_id": "item_1234", # this is the item ID of the model's last response
    "content_index": 0,
    "audio_end_ms": 1500 # truncate audio after 1.5 seconds
}

E quanto a truncar também a transcrição? O modelo Realtime não tem informações suficientes para alinhar com precisão a transcrição e o áudio. Por isso, conversation.item.truncate cortará o áudio em um determinado ponto e removerá a transcrição em texto da parte não reproduzida. Isso resolve o problema de remover o áudio não reproduzido, mas não fornece uma transcrição truncada.

Pressionar para falar

Por padrão, a Realtime API usa detecção de atividade de voz (VAD), o que significa que as respostas do modelo são acionadas pela entrada de áudio. Você também pode implementar uma interação de pressionar para falar desabilitando a VAD e usando um controle no aplicativo para definir quando a entrada de áudio é enviada ao modelo. Por exemplo, o usuário pode manter a barra de espaço pressionada para capturar áudio e acionar uma resposta ao soltá-la. Em alguns aplicativos, isso funciona surpreendentemente bem: dá aos usuários controle sobre as interações, evita falhas da VAD e transmite uma sensação de agilidade, já que não é preciso aguardar o tempo limite da VAD.

A implementação de pressionar para falar é um pouco diferente em WebSockets e WebRTC. Em uma conexão WebSocket da Realtime API, todos os eventos são enviados no mesmo canal e na mesma ordem, enquanto uma conexão WebRTC tem canais separados para áudio e eventos de controle.

WebSockets

Para implementar pressionar para falar com uma conexão WebSocket, o cliente deverá parar a reprodução de áudio, lidar com interrupções e iniciar uma nova resposta. Veja o procedimento em mais detalhes:

  1. Desabilite a VAD definindo "turn_detection": null em um evento session.update.
  2. Ao pressionar o botão, inicie a gravação de áudio no cliente.
    1. Se houver uma resposta do modelo em andamento, cancele-a enviando um evento response.cancel.
    2. Se o áudio de saída do modelo estiver sendo reproduzido, interrompa a reprodução imediatamente e envie um evento conversation.item.truncate para remover da conversa todo o áudio ainda não reproduzido.
  3. Ao soltar o botão, envie uma mensagem input_audio_buffer.append com o áudio para adicionar o novo áudio ao buffer de entrada.
  4. Envie um evento input_audio_buffer.commit. Isso confirmará o áudio gravado no buffer de entrada e iniciará a transcrição da entrada, se estiver habilitada.
  5. Em seguida, acione uma resposta com um evento response.create.

WebRTC e SIP

A implementação de pressionar para falar com WebRTC é semelhante, mas o buffer de áudio de entrada precisa ser limpo explicitamente. Siga este procedimento:

  1. Desative a VAD definindo "turn_detection": null em um evento session.update.
  2. Ao pressionar o botão, envie um evento input_audio_buffer.clear para limpar qualquer entrada de áudio anterior.
    1. Se houver uma resposta do modelo em andamento, cancele-a enviando um evento response.cancel.
    2. Se o áudio de saída do modelo estiver sendo reproduzido, envie um evento output_audio_buffer.clear para limpar o áudio ainda não reproduzido. Isso também trunca a conversa.
  3. Ao soltar o botão, envie um evento input_audio_buffer.commit. Isso confirma o áudio gravado no buffer de entrada e inicia a transcrição da entrada (se estiver habilitada).
  4. Em seguida, inicie uma resposta com um evento response.create.