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
Theme

Conversas com a Realtime API

Aprenda a gerenciar conversas de fala para fala com a Realtime API.

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

Este guia explica como acompanhar o estado de uma sessão Realtime e quais fluxos de eventos usar para acessar recursos do modelo, como geração de áudio e texto, entrada de imagens e chamada de função.

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

Sessões Realtime de fala para fala

Uma sessão Realtime é uma interação que mantém o estado entre o modelo e um cliente conectado. A sessão tem três componentes principais.

  • O objeto Sessão controla os parâmetros da interação, como o modelo, a voz usada para gerar o áudio de saída e outras configurações.
  • A Conversa representa os itens de entrada do usuário e os itens de saída que o modelo gera durante a sessão atual.
  • As Respostas são itens de áudio ou texto que o modelo gera e que passam a fazer parte da conversa.

Buffer de áudio de entrada e WebSockets

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


Se você usa WebSockets para transmitir áudio, precisa interagir manualmente com o buffer de áudio de entrada. Para isso, envie o áudio codificado em Base64 ao servidor em eventos JSON.

Juntos, esses componentes formam uma sessão Realtime. Use eventos do cliente para atualizar o estado da sessão e escute eventos do servidor para reagir às mudanças de estado.

Diagrama do estado de uma sessão Realtime

Eventos do ciclo de vida da sessão

Depois que você inicia uma sessão via WebRTC ou WebSockets, o servidor envia um evento session.created para indicar 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 da saída de áudio e só pode ser alterada até que o modelo responda com áudio pela primeira vez na sessão. Uma sessão Realtime pode durar até 60 minutos.

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

Atualize as instruções de sistema que o modelo usa 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",
          rate: 24000,
        },
        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));

Após a atualização, o servidor emite um evento session.updated com o novo estado da sessão.

Eventos relacionados do clienteEventos relacionados do servidor

session.update

session.created

session.updated

Entradas e saídas de texto

Para gerar texto com um modelo Realtime, adicione entradas de texto à conversa atual, peça ao modelo que gere uma resposta e escute os eventos que o servidor envia para indicar o progresso da resposta. A geração de texto exige que você configure a sessão com a modalidade text, que já vem habilitada por padrão.

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

Crie um item na 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()
ws.send(JSON.stringify(event));

Depois de adicionar a mensagem do usuário à conversa, envie o evento response.create para que o modelo comece a responder. Escolha uma única modalidade de saída. A modalidade audio gera áudio com a respectiva transcrição, e text gera apenas texto. Para gerar apenas texto, especifique essa opção ao enviar o evento response.create do cliente, como no exemplo abaixo.

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

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

Quando a resposta estiver concluída, o servidor emitirá o evento response.done. Esse evento contém o texto completo gerado pelo modelo, como mostra o exemplo abaixo.

Escute o evento response.done para ver o resultado final
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 o modelo gera a resposta, o servidor emite vários eventos de ciclo de vida. Você pode escutar eventos como response.output_text.delta para mostrar o progresso ao usuário em tempo real. A coluna Eventos relacionados do servidor lista os eventos que o servidor emite, na ordem aproximada de emissão. A tabela também inclui os eventos do cliente relevantes para a geração de texto.

Entradas e saídas de áudio

Um dos principais recursos da Realtime API é a interação direta por voz com o modelo, sem uma etapa intermediária de conversão de texto em fala ou de fala em texto. Isso reduz a latência das interfaces de voz e dá ao modelo mais informações sobre o tom e a entonação da fala do usuário.

Opções de voz

Você pode configurar uma sessão Realtime para usar uma das vozes integradas ao gerar áudio. Defina voice ao criar a sessão (ou em um evento response.create) para escolher a voz do modelo. As opções atuais são alloy, ash, ballad, coral, echo, sage, shimmer, verse, marin e cedar. Depois que o modelo emitir áudio, você não poderá mais alterar voice nessa sessão. Para obter a melhor qualidade, recomendamos marin ou cedar.

Como trabalhar com áudio via WebRTC

Quando você se conecta à Realtime API via WebRTC, a Realtime API participa de uma conexão ponto a ponto com o cliente. O cliente recebe o áudio gerado pelo modelo como um fluxo de mídia remoto. Dispositivos de áudio capturam a entrada para o modelo (getUserMedia), e os fluxos de mídia são adicionados à conexão ponto a ponto como faixas.

O exemplo do guia de conexão via WebRTC mostra uma configuração básica do áudio local e remoto com as 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 acima permite interagir com a Realtime API. Para explorar outras possibilidades de interface, consulte o repositório de exemplos de WebRTC. Você também pode experimentar as demonstrações desses exemplos.

Com a captura e a transmissão de mídia no navegador, você pode, por exemplo, ligar e desligar microfones e selecionar o dispositivo de entrada.

Eventos do cliente e do servidor para áudio via WebRTC

Por padrão, os clientes WebRTC podem enviar áudio de entrada à Realtime API sem precisar enviar antes nenhum evento do cliente. Assim que você adiciona uma faixa de áudio local à conexão ponto a ponto, o usuário já pode começar a falar.

Os clientes WebRTC continuam recebendo eventos de ciclo de vida do servidor enquanto o áudio trafega nos dois sentidos pela conexão ponto a ponto. Veja alguns exemplos.

As APIs do WebRTC para fluxos de mídia podem oferecer todo o controle de que você precisa. Em alguns casos, porém, pode ser necessário usar interfaces de nível mais baixo para a entrada e a saída de áudio. A seção sobre WebSockets a seguir traz mais informações e lista os eventos necessários para controlar a entrada de áudio em detalhes.

Como trabalhar com áudio via WebSockets

Com um WebSocket, enviar mídia pelo cliente e receber mídia do servidor exige algumas etapas adicionais. A tabela abaixo descreve o fluxo de eventos necessário para enviar e receber áudio durante uma sessão via WebSocket.

Os eventos aparecem na ordem do ciclo de vida, embora alguns deles, como os eventos delta, possam ocorrer simultaneamente.

Transmita áudio de entrada ao servidor

Para transmitir áudio de entrada ao servidor, use o evento input_audio_buffer.append do cliente. Esse evento exige que você envie blocos de bytes de áudio codificados em Base64 à Realtime API pelo socket. Cada bloco pode ter no máximo 15 MB.

Defina session.audio.input.format em session.update. Para PCM, use {"type": "audio/pcm", "rate": 24000} e envie áudio mono a 24 kHz.

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) {
    const sample = Math.max(-1, Math.min(1, float32Array[i]));
    view.setInt16(
      offset,
      Math.round(sample * (sample < 0 ? 0x8000 : 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",
];

// Manual commit and response creation require automatic turn detection off.
ws.send(
  JSON.stringify({
    type: "session.update",
    session: {
      type: "realtime",
      audio: { input: { turn_detection: null } },
    },
  })
);

for (const filename of files) {
  const audioFile = fs.readFileSync(filename);
  const audioBuffer = await decodeAudio(audioFile);
  if (
    audioBuffer.sampleRate !== 24000 ||
    audioBuffer.channelData.length !== 1
  ) {
    throw new Error("Expected 24 kHz mono audio: " + filename);
  }
  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",
    response: { output_modalities: ["audio"] },
  })
);

Envie mensagens de áudio completas

Você também pode criar mensagens na conversa a partir de gravações de áudio completas. Use o evento conversation.item.create do cliente para criar mensagens com conteúdo do tipo input_audio.

Crie itens na conversa com gravações de áudio completas
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 no cliente, por exemplo, em um navegador, recomendamos usar WebRTC em vez de WebSockets. O WebRTC transmite mídia aos dispositivos clientes com mais confiabilidade quando as condições da rede variam.

Para trabalhar com saída de áudio em aplicativos que usam um WebSocket na comunicação entre servidores, escute os eventos response.output_audio.delta. Eles contêm blocos de dados de áudio do modelo codificados em Base64. Você pode armazenar esses blocos em um buffer e gravá-los em um arquivo ou transmiti-los imediatamente para outro destino, como uma chamada telefônica via Twilio.

Os eventos response.output_audio.done e response.done contêm apenas a transcrição do áudio. Para obter os bytes de áudio, escute os eventos response.output_audio.delta.

Você pode configurar o formato dos blocos de saída para a sessão inteira ou para cada resposta.

Escute os eventos response.output_audio.delta
function handleEvent(message) {
  const serverEvent = JSON.parse(message.toString());
  if (serverEvent.type === "response.output_audio.delta") {
    const audio = Buffer.from(serverEvent.delta, "base64");
    console.log(`Received ${audio.length} audio bytes`);
  }
}

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

Entradas de imagem

gpt-realtime-2 e gpt-realtime também aceitam imagens como entrada. Você pode anexar uma imagem como parte do conteúdo de uma mensagem do usuário, e o modelo pode levar o conteúdo da imagem em conta 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 Realtime usam detecção de atividade de voz (VAD). Com esse recurso, a API identifica quando o usuário começa e para de falar e responde automaticamente.

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

Desative a VAD

Para desativar a VAD, defina turn_detection como null no evento session.update do cliente. Isso é útil em interfaces que exigem controle detalhado da entrada de áudio, como aquelas em que o usuário pressiona um botão para falar.

Com a VAD desativada, o cliente precisa emitir manualmente alguns eventos adicionais para acionar respostas em áudio.

Mantenha a VAD ativada e desative as respostas automáticas

Para manter a VAD ativada e decidir manualmente quando gerar uma resposta, defina turn_detection.interrupt_response e turn_detection.create_response como false no evento session.update do cliente. A VAD continua funcionando, mas a API deixa de criar respostas automaticamente. Os clientes podem acioná-las manualmente com um evento response.create.

Essa configuração pode ser útil para moderação, validação de entradas ou implementações de RAG quando vale a pena aceitar um pouco mais de latência na interação para ter controle sobre as entradas.

Crie respostas fora da conversa padrão

Por padrão, todas as respostas geradas durante uma sessão passam a fazer parte do estado da conversa da sessão (a “conversa padrão”). Em alguns casos, você pode querer que o modelo gere respostas fora do contexto da conversa padrão ou que gere várias respostas simultaneamente. Você também pode escolher com mais precisão quais itens da conversa o modelo considera ao gerar uma resposta (por exemplo, apenas os últimos N turnos).

Para gerar respostas “fora de banda”, que não são adicionadas ao estado da conversa padrão, defina o campo response.conversation como a string none ao criar a resposta com o evento response.create do cliente.

Ao criar uma resposta fora de banda, você provavelmente também vai querer identificar quais eventos do servidor correspondem a ela. Inclua metadata na resposta do modelo para identificar qual resposta está sendo gerada para o 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()
  ws.send(JSON.stringify(event));

Agora, ao escutar o evento response.done do servidor, você pode identificar o resultado da 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"
  ) {
    const { response } = serverEvent;
    if (response.status !== "completed" || response.output.length === 0) {
      throw new Error(
        "Classification response ended with status " + response.status
      );
    }
    console.log(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 criar um contexto personalizado, fora da conversa padrão atual, para o modelo usar ao gerar uma resposta. Para isso, use o array input em um evento response.create do cliente. Você pode fornecer novas entradas ou referenciar pelo ID os itens de entrada que já estão na conversa.

Escute a resposta fora de banda do modelo com contexto personalizado
const existingItemId = "item_pizza";
ws.send(
  JSON.stringify({
    type: "conversation.item.create",
    item: {
      id: existingItemId,
      type: "message",
      role: "user",
      content: [
        { type: "input_text", text: "We are ordering pizza for dinner." },
      ],
    },
  })
);
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: existingItemId,
      },
      {
        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()
ws.send(JSON.stringify(event));

Crie respostas sem contexto

Você também pode inserir respostas na conversa padrão ignorando todas as demais instruções e todo 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: [],
      output_modalities: ["text"],
      instructions: prompt,
    },
  };

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

Chamada de função

Os modelos Realtime também oferecem o recurso de chamada de função, que permite executar código personalizado para ampliar os recursos do modelo. O processo tem quatro etapas.

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

Para ver como isso funciona na prática, vamos adicionar uma função que o modelo pode chamar para fornecer o horóscopo do dia aos usuários. Os exemplos mostram a estrutura dos eventos que o cliente precisa enviar e o que o servidor emite em seguida.

Configure as funções que o modelo pode chamar

Primeiro, forneça ao modelo as funções que ele pode chamar com base na entrada do usuário. Você pode configurar as funções disponíveis para toda a sessão ou para uma resposta específica.

O exemplo a seguir mostra o conteúdo de um evento session.update do cliente que configura uma função para gerar horóscopos. A função recebe um único argumento, o signo para o qual deve gerar o horóscopo.

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 a entrada indicar que o usuário quer saber seu horóscopo, o modelo chamará a função com o parâmetro sign.

Identifique 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 o aplicativo adicione o item a seguir à 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, o cliente envia um evento response.create para gerar a resposta.

{
  "type": "response.create"
}

Em vez de retornar imediatamente uma resposta em texto ou áudio, o modelo gera uma resposta com os argumentos que o aplicativo do desenvolvedor deve passar à função. Você pode acompanhar as atualizações dos argumentos em tempo real pelo evento response.function_call_arguments.delta do servidor. O evento response.done também contém todos os dados necessários para chamar a 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\"}"
      }
    ]
  }
}

As propriedades do JSON enviado pelo servidor permitem identificar que o modelo quer chamar uma função personalizada.

Propriedade Papel na chamada de função
response.output[0].type Quando tem o valor function_call, indica que a resposta contém argumentos para chamar uma função identificada pelo nome.
response.output[0].name Nome da função configurada que deve ser chamada. Neste caso, generate_horoscope.
response.output[0].arguments String JSON com os argumentos da função. Neste caso, "{\"sign\":\"Aquarius\"}".
response.output[0].call_id ID que o sistema gera para esta chamada de função. Você precisa desse ID para devolver o resultado da chamada ao modelo.

Com essas informações, o aplicativo pode executar o código que gera o horóscopo e devolver o resultado ao modelo para que ele gere uma resposta.

Forneça ao modelo o resultado de uma chamada de função

Ao receber uma resposta do modelo com os argumentos de uma chamada de função, o aplicativo pode executar o código que implementa a função. Esse código pode realizar as operações que você precisar, como consultar APIs externas ou acessar bancos de dados.

Quando o código personalizado produzir o resultado, você pode fornecê-lo ao modelo criando um item na conversa com o evento conversation.item.create do cliente.

{
  "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 recebido no evento response.done acima.
  • item.output é uma string JSON que contém o resultado da chamada de função.

Depois de adicionar à conversa o item com o resultado da chamada de função, envie novamente o evento response.create pelo cliente. Isso faz com que o modelo gere uma resposta usando os dados da chamada de função.

{
  "type": "response.create"
}

Tratamento de erros

O servidor emite o evento error sempre que encontra um erro durante a sessão. Em alguns casos, é possível identificar o evento do cliente enviado pelo aplicativo que causou o erro.

No HTTP, cada resposta está implicitamente associada a uma solicitação do cliente. Na Realtime API, use a propriedade event_id nos eventos do cliente para identificar qual deles causou um erro no servidor. No exemplo abaixo, o cliente tenta emitir um evento de um tipo sem suporte.

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

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

Ao receber esse evento inválido do cliente, o servidor emite um evento de erro como o do exemplo abaixo.

{
  "type": "error",
  "event_id": "event_error_123",
  "error": {
    "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 fala. Quando a VAD está ativada, a Realtime API detecta a fala do usuário, cancela a resposta em andamento e inicia outra. Para continuar a conversa naturalmente, o modelo precisa saber em que ponto foi interrompido, por exemplo, se o usuário perguntar “o que você acabou de dizer?”. Truncar a última resposta do modelo significa remover da conversa o trecho dessa resposta que não foi reproduzido.

Nas conexões WebRTC e SIP, o servidor gerencia um buffer de áudio de saída e sabe quanto áudio já foi reproduzido a cada momento. Quando o usuário interrompe o modelo, o servidor remove automaticamente o áudio que ainda não foi reproduzido.

Nas conexões WebSocket, o cliente gerencia a reprodução do áudio. Por isso, cabe a ele interromper a reprodução e truncar a resposta.

  1. Ao receber input_audio_buffer.speech_started, interrompa imediatamente a reprodução do áudio do modelo e descarte o áudio desse item que ainda estiver na fila. Registre quantos milissegundos de áudio do item o usuário realmente ouviu.
  2. Envie um evento conversation.item.truncate com o ID do item interrompido, o índice do conteúdo de áudio e a duração já reproduzida em audio_end_ms. Isso remove da conversa o trecho que não foi reproduzido.
  3. Quando a VAD interrompe uma resposta em andamento, o servidor cancela a resposta e envia response.done com response.status definido como cancelled. Use esse evento para identificar o término da resposta. Interrompa a reprodução e envie o evento de truncamento sem esperar por ele.

Veja um exemplo.

{
  "type": "conversation.item.truncate",
  "item_id": "item_1234",
  "content_index": 0,
  "audio_end_ms": 1500
}

O exemplo trunca o áudio após 1,5 segundo de reprodução.

E a transcrição? O modelo Realtime não dispõe de informações suficientes para alinhar com precisão a transcrição e o áudio. Por isso, conversation.item.truncate corta o áudio no ponto indicado e remove o texto da transcrição referente ao trecho que não foi reproduzido. Isso elimina o áudio que o usuário não ouviu, mas não fornece uma transcrição truncada.

Pressionar para falar

Por padrão, a Realtime API usa a detecção de atividade de voz (VAD) para acionar respostas do modelo a partir do áudio de entrada. Você também pode implementar a interação de pressionar para falar. Para isso, desative a VAD e controle pelo aplicativo quando enviar o áudio de entrada ao modelo. Por exemplo, o aplicativo pode capturar áudio enquanto o usuário mantém a barra de espaço pressionada e acionar uma resposta quando ele solta a tecla. Em alguns aplicativos, essa abordagem funciona muito bem. Ela dá ao usuário o controle da interação, evita falhas de detecção da VAD e torna a resposta mais rápida porque o aplicativo não precisa aguardar o tempo limite da VAD.

A implementação desse recurso varia entre WebSockets e WebRTC. Em uma conexão WebSocket com a Realtime API, todos os eventos passam pelo mesmo canal, na ordem em que foram enviados. Já uma conexão WebRTC usa canais separados para áudio e eventos de controle.

WebSockets

Para implementar o recurso de pressionar para falar em uma conexão WebSocket, configure o cliente para parar a reprodução de áudio, tratar interrupções e iniciar uma nova resposta.

  1. Desative a VAD definindo "turn_detection": null em um evento session.update.
  2. Quando o usuário pressionar o botão, comece a gravar o áudio no cliente.
    1. Se o modelo estiver gerando uma resposta, cancele-a enviando um evento response.cancel.
    2. Se o áudio do modelo estiver sendo reproduzido, interrompa a reprodução imediatamente e envie um evento conversation.item.truncate para remover da conversa o áudio que ainda não foi reproduzido.
  3. Quando o usuário soltar o botão, envie uma mensagem input_audio_buffer.append com o áudio gravado para adicioná-lo ao buffer de entrada.
  4. Envie um evento input_audio_buffer.commit para confirmar o áudio gravado no buffer de entrada e iniciar a transcrição da entrada, se ela estiver habilitada.
  5. Em seguida, acione uma resposta com um evento response.create.

WebRTC e SIP

A implementação do recurso de pressionar para falar com WebRTC é semelhante, mas exige que você limpe explicitamente o buffer de áudio de entrada.

  1. Desative a VAD definindo "turn_detection": null em um evento session.update.
  2. Quando o usuário pressionar o botão, envie um evento input_audio_buffer.clear para limpar o áudio de entrada anterior.
    1. Se o modelo estiver gerando uma resposta, cancele-a enviando um evento response.cancel.
    2. Se o áudio do modelo estiver sendo reproduzido, envie um evento output_audio_buffer.clear para descartar o áudio que ainda não foi reproduzido. Esse evento também trunca a conversa.
  3. Quando o usuário soltar o botão, envie um evento input_audio_buffer.commit para confirmar o áudio gravado no buffer de entrada e iniciar a transcrição da entrada, se ela estiver habilitada.
  4. Em seguida, acione uma resposta com um evento response.create.