Este guia fornece instruções e práticas recomendadas para engenheiros que criam experiências de
pedido de comida com o método RPC FoodOrderingService.BidiProcessOrder.
Essa API de streaming bidirecional em tempo real é o núcleo do agente de IA para pedidos de comida, permitindo a coleta de pedidos dinâmica e conversacional em vários aplicativos, como apps para dispositivos móveis, assistentes de voz, drive-thrus e quiosques.
Visão geral de BidiProcessOrder
O método BidiProcessOrder estabelece um canal de comunicação bidirecional
persistente entre o aplicativo cliente e o agente de IA para pedidos de comida. Ao contrário das RPCs unárias padrão de solicitação e resposta, essa abordagem de streaming permite:
- Interação de baixa latência:troca contínua de informações sem a sobrecarga de solicitações HTTP repetidas.
- Entrada multimodal:processamento de fluxos de áudio (para pedidos por voz), entradas de texto e eventos do lado do cliente.
- Respostas em tempo real:o agente pode enviar áudio, texto, atualizações de pedidos e outros sinais conforme a conversa acontece.
BidiProcessOrder não pode ser invocado usando REST. As integrações precisam usar um
protocolo orientado a conexão:
- gRPC (recomendado): oferece uma estrutura robusta e eficiente para streaming bidirecional.
- WebSocket:adequado para clientes ou ambientes em que o gRPC não é adequado devido a restrições de linguagem de programação ou de rede.
Consulte a referência da API BidiProcessOrder para definições de tipo detalhadas. As integrações do WebSocket usam representações JSON desses tipos, conforme descrito na seção do WebSocket.
Pré-requisitos
Antes de fazer a integração com o BidiProcessOrder:
Ative a API:verifique se a API do agente de IA para pedidos de comida está ativada no seu projeto Google Cloud.
bash gcloud services enable foodorderingaiagent.googleapis.com --project=PROJECT_IDAutenticação:decida sua abordagem de autenticação e configure as contas de serviço e os papéis do IAM necessários, conforme descrito em Autenticação.
Ingestão de cardápio:um Menu válido precisa ser ingerido e associado a um
Store. Consulte Como integrar dados de menu para mais detalhes.
Autenticação
Para se conectar com segurança ao RPC BidiProcessOrder, seu aplicativo precisa
fazer a autenticação usando uma conta de serviço do Google Cloud .
1. Configurar uma conta de serviço
- Criar uma conta de serviço:no seu projeto Google Cloud , crie uma conta de serviço que seu aplicativo vai usar para autenticar a API do agente de IA para pedidos de comida. Consulte Como criar e gerenciar contas de serviço.
Conceda papéis do IAM:conceda os papéis do IAM necessários a essa conta de serviço. O papel principal necessário para chamar
BidiProcessOrderé:- Usuário do agente de pedidos de comida (
roles/foodorderingaiagent.agentUser): permite que a conta de serviço se conecte ao serviço de pedidos e processe sessões.
É possível conceder esse papel usando o console do Google Cloud ou o
gcloud:bash gcloud projects add-iam-policy-binding PROJECT_ID \ --member="serviceAccount:SERVICE_ACCOUNT_EMAIL" \ --role="roles/foodorderingaiagent.agentUser"- Usuário do agente de pedidos de comida (
2. Fluxo de autenticação de aplicativos
O fluxo de autenticação exato depende da arquitetura do aplicativo, especialmente se o aplicativo cliente (por exemplo, app para dispositivos móveis, software de quiosque) se conecta diretamente ou pelo seu próprio back-end.
Cenário comum: autenticar um aplicativo cliente voltado ao consumidor
Este é um padrão típico para aplicativos móveis ou da Web:
- Client-to-YourAuth::o app cliente do usuário final (dispositivo móvel, Web) faz a autenticação com seu sistema de autenticação de usuário atual (pode ser o Firebase Authentication, seu próprio servidor OAuth etc.).
- Troca de token:depois de autenticar o usuário, o app cliente solicita um token de curta duração de um serviço de back-end seguro que você controla (por exemplo, um "serviço de token de API").
Geração de token de acesso:seu serviço de back-end, usando as credenciais do principal da conta de serviço Google Cloud configurado na etapa 1, gera um token de acesso padrão do OAuth 2.0 para o escopo
https://www.googleapis.com/auth/cloud-platform. Isso pode ser feito usando as bibliotecas de cliente de autenticação Google Cloud .- Segurança:as chaves ou credenciais da conta de serviço usadas para gerar esses tokens precisam ser armazenadas e gerenciadas com segurança no seu back-end. Nunca exponha as chaves privadas da conta de serviço diretamente aos aplicativos cliente do usuário final. Consulte Práticas recomendadas para gerenciar chaves de conta de serviço.
Token para o cliente:seu serviço de back-end retorna o token de acesso do Google gerado para o app cliente.
Chamada de API:o app cliente usa esse token de acesso do Google para autenticar a conexão gRPC ou WebSocket com o RPC
BidiProcessOrder.
3. Como usar o token
- gRPC:as bibliotecas de cliente gRPC do Google geralmente processam a atualização e a inclusão de tokens nos metadados da chamada quando recebem credenciais da conta de serviço.
- WebSocket (não navegador): inclua o token no cabeçalho
Authorization: Bearer TOKEN. - WebSocket (navegador): conforme observado na seção WebSocket, conexões WebSocket diretas do navegador não podem usar cabeçalhos de autorização. Um proxy de streaming do lado do servidor é necessário para autenticar a conexão dos clientes com Google Cloud.
Como se conectar à API
É possível estabelecer um fluxo usando bibliotecas de cliente gRPC ou uma conexão WebSocket.
gRPC
Usar gRPC é a abordagem recomendada. Você vai usar as bibliotecas de cliente para a linguagem de sua escolha (por exemplo, Node.js), que são baseadas na Referência da API BidiProcessOrder.
As etapas básicas envolvem:
- Crie um canal gRPC para o endpoint de API do agente de IA para pedidos de comida (por exemplo,
foodorderingaiagent.googleapis.com). - Receba um stub de cliente para
FoodOrderingService. - Invoque o método
BidiProcessOrder, que retorna um objeto de fluxo para envio de solicitações e recebimento de respostas. - Implemente a lógica de negócios de acordo com seu caso de uso, que simultaneamente:
- Envia áudio, texto e entrada de eventos do usuário final.
- Processa mensagens do agente, incluindo áudio, texto e eventos.
Node.js
const {FoodOrderingServiceClient} = require('@google-cloud/foodorderingaiagent');
const client = new FoodOrderingServiceClient();
// The stream is initialized immediately. You can now write commands and attach listeners.
const stream = client.bidiProcessOrder();
WebSocket
Para conexões WebSocket, o caminho do URL é:
wss://foodorderingaiagent.googleapis.com/ws/google.cloud.foodorderingaiagent.v1beta.FoodOrderingService/BidiProcessOrder/locations/LOCATION
LOCATION: por exemplo,us
Cabeçalhos obrigatórios:
Authorization:Bearer TOKEN, em queTOKENé um token de acesso do OAuth 2.0 obtido para sua conta de serviço.
Formato da mensagem:
- Cliente para servidor:as mensagens enviadas à API (por exemplo,
Config,AudioInput,TextInput,EventInput) precisam ser representações JSON do protoBidiProcessOrderRequest, enviadas comowebsocket.TextMessage. - Do servidor para o cliente:as mensagens recebidas da API
(
BidiProcessOrderResponse) serão enviadas comowebsocket.BinaryMessage, mas o conteúdo dessas mensagens binárias é um payload JSON. - Dados binários:os dados binários nos payloads JSON (por exemplo,
customerAudioemAudioInput,agentAudioemAgentAudio) precisam ser codificados em base64.
Exemplo de WebSocket do Node.js
Confira um exemplo de como se conectar e interagir com a API usando WebSockets
no Node.js com a biblioteca ws:
const WebSocket = require('ws');
// Replace with your actual values
const location = 'LOCATION';
const projectId = 'PROJECT_ID';
const sessionId = 'SESSION_ID';
const brandId = 'BRAND_ID';
const storeId = 'STORE_ID';
const token = 'OAUTH_TOKEN';
const wsUrl = `wss://foodorderingaiagent.googleapis.com/ws/google.cloud.foodorderingaiagent.v1beta.FoodOrderingService/BidiProcessOrder/locations/${location}`;
const ws = new WebSocket(wsUrl, {
headers: {
'Authorization': `Bearer ${token}`
}
});
ws.on('open', () => {
console.log('Connected to WebSocket');
// 1. Send the required initial Config message
const configRequest = {
config: {
session: `projects/${projectId}/locations/${location}/sessions/${sessionId}`,
store: `projects/${projectId}/locations/${location}/brands/${brandId}/stores/${storeId}`
}
};
// Client-to-server messages are sent as TextMessage
ws.send(JSON.stringify(configRequest));
console.log('Sent Config message');
});
ws.on('message', (data, isBinary) => {
// The documentation specifies that server-to-client messages
// are sent as BinaryMessage containing a JSON payload.
if (isBinary) {
try {
const response = JSON.parse(data.toString('utf8'));
console.log('Received response:', response);
if (response.agentText) {
console.log(`Agent: ${response.agentText.text}`);
}
if (response.agentAudio) {
const audioBytes = Buffer.from(response.agentAudio.agentAudio, 'base64');
console.log(`Received ${audioBytes.length} bytes of agent audio.`);
// Play or process the audio bytes here
}
if (response.endSession) {
console.log('Session ended by agent.');
ws.close();
}
} catch (e) {
console.error('Failed to parse JSON response:', e);
}
}
});
ws.on('close', () => {
console.log('Connection closed');
});
Ciclo de vida da sessão
Cada chamada para BidiProcessOrder inicia uma sessão. A sessão permanece ativa
enquanto o fluxo estiver aberto.
1. Iniciação (mensagem de configuração)
- Ao estabelecer a conexão, a primeira mensagem enviada pelo cliente precisa ser um
BidiProcessOrderRequestque contenha a mensagemConfig. - Campos obrigatórios em
Config:session: um identificador de sessão exclusivo gerado pelo cliente. Formato:projects/PROJECT/locations/LOCATION/sessions/SESSION_ID.store: o nome do recurso doStore. Formato:projects/PROJECT/locations/LOCATION/brands/BRAND/stores/STORE.- O agente usa o
storepara carregar o menu e a configuração adequados.
- O agente usa o
Node.js
// Send the first message containing Config
stream.write({
config: {
session: client.sessionPath(projectId, location, sessionId),
store: client.storePath(projectId, location, brandId, storeId),
}
});
2. Envio de entradas
- Depois do
Configinicial, o cliente pode enviar um fluxo de mensagensBidiProcessOrderRequestcontendo uma das seguintes entradas:AudioInput: dados de áudio brutos (normalmente PCM linear de 16 bits a 16.000 Hz, sem cabeçalhos). Usado para interações de voz.TextInput: Mensagens de texto do usuário.EventInput: sinais para eventos comoDriveOffEvent(para casos de uso de drive-thru quando o veículo sai),CrewInterjectionEvent(para qualquer situação em que um humano assume a função de receber pedidos no meio da conversa) ouOrderStateUpdateEvent(se o pedido for modificado no lado do cliente, por exemplo, usando uma interface de toque).
Node.js
// Stream user inputs over the active connection
stream.write({textInput: {text: 'Hi, I\'d like to order a cheeseburger.'}});
3. Recebimento das respostas
- Ao mesmo tempo, o agente envia de volta um stream de mensagens
BidiProcessOrderResponse. Seu cliente precisa estar preparado para lidar com vários tipos de resposta no campooneof response:AgentAudio: bytes de áudio sintetizados para serem reproduzidos para o usuário, usados em interações por voz.AgentText: versão em texto da resposta do agente.SpeechRecognition: transcrição da fala reconhecida do usuário.UpdatedOrderState: contém o estado atual completo doOrdersempre que ele é atualizado pelo agente. Use isso para atualizar a representação de pedido do seu aplicativo. Isso geralmente resulta em uma atualização de uma interface do usuário ou de um sistema de registro de informações de estado do pedido, como um sistema de ponto de venda.InterruptionSignal: indica que o usuário interrompeu a fala do agente. O cliente precisa parar imediatamente de tocar qualquerAgentAudiode saída.AgentEvent: eventos especiais, comoRestartOrder, que exigem ação do cliente.SuggestedOptions: oferece opções contextualmente relevantes que um usuário pode selecionar em seguida, útil para exibição em uma tela.EndSession: Indica que a sessão foi encerrada pelo agente (por exemplo, pedido concluído, saída do usuário ou escalonamento do agente).
Node.js
// Attach event listeners to handle responses sequentially
stream.on('data', (response) => {
if (response.agentAudio) {
console.log(`Received ${response.agentAudio.agentAudio.length} bytes of agent audio.`);
} else if (response.agentText) {
console.log(`Agent: ${response.agentText.text}`);
} else if (response.speechRecognition) {
console.log(`Recognized User Speech: ${response.speechRecognition.transcript}`);
} else if (response.updatedOrderState) {
console.log('Order updated.');
} else if (response.interruptionSignal) {
console.log('User interrupted the agent. Stop playing audio!');
} else if (response.endSession) {
console.log(`Session ended. Type: ${response.endSession.type}, Reason: ${response.endSession.reason}`);
stream.end();
}
});
stream.on('error', (err) => {
console.error('Stream error:', err);
});
4. Encerrar o stream
- O fluxo pode ser fechado pelo cliente ou pelo servidor. Normalmente, o servidor
sinaliza o fim de uma conversa usando uma mensagem
EndSession. O cliente precisa fechar o stream quando essa mensagem é recebida.
Como processar tipos específicos de mensagens
As seções a seguir descrevem como processar tipos de respostas específicos que seu
cliente vai receber ao chamar BidiProcessOrder.
AudioInput
- Transmita o áudio em partes à medida que ele fica disponível.
- Formato: PCM linear de 16 bits, taxa de amostragem de 16.000 Hz.
- Os trechos de áudio não incluem os cabeçalhos que geralmente precedem um arquivo WAV.
- Para cenários de drive-thru com cancelamento de eco ativado
(
enable_echo_cancellationemConfig), forneçacustomer_audioecrew_audio.
UpdatedOrderState
- Essa mensagem fornece o estado completo do pedido sempre que é enviada.
Substitua qualquer cache local do pedido pelo conteúdo da mensagem
Orderrecebida. - Use o
custom_integration_attributesnos itens e modificadoresOrderpara mapear o conteúdoOrderem entidades equivalentes no sistema de registro do seu aplicativo.
InterruptionSignal
- Ao receber, interrompa imediatamente a reprodução de qualquer
AgentAudioe limpe o áudio do agente em buffer. Isso garante um fluxo de conversa natural quando o usuário interrompe a fala do agente.
EndSession
- Verifique o
EndType(por exemplo,DRIVE_OFF,AGENT_ESCALATION). - O aplicativo precisa fechar a conexão normalmente e fazer a transição do
usuário de maneira adequada (por exemplo, notificar um supervisor humano no caso de
AGENT_ESCALATIONou fazer a transição para um estado de confirmação de pedido).
Práticas recomendadas
- Processar mensagens de forma assíncrona:minimize a latência usando linhas de execução ou E/S não bloqueadora para enviar solicitações e processar respostas simultaneamente.
- Lógica de reconexão:implemente uma lógica de reconexão robusta em caso de
problemas de rede. Não se esqueça de enviar a mensagem inicial
Configcom o mesmo ID de sessão para tentar retomar. - Tratamento de erros:monitore o stream em busca de erros. As bibliotecas gRPC e WebSocket fornecem mecanismos para detectar o fechamento do stream ou erros de transporte. Registre esses eventos e lide com eles sem estresse.
- Buffer de áudio:gerencie os buffers de áudio com cuidado, implementando o buffer
se necessário, para garantir a reprodução tranquila de
AgentAudioe a entrega pontual deAudioInput. Considere cuidadosamente a compensação entre latência e qualidade de reprodução ao decidir seu esquema de buffer. - Gerenciamento de ID da sessão:garanta que os IDs de sessão sejam exclusivos para cada pedido/conversa diferente.
- Gerenciamento de recursos:feche streams e libere recursos quando a sessão for concluída ou se ocorrerem erros irrecuperáveis.
- Tempos limite:embora o fluxo em si possa ser de longa duração (até 15 minutos por padrão), considere tempos limite no nível do aplicativo para estados específicos, se necessário.
Exemplo de fluxo de integração (conceitual)
- O app cliente (por exemplo, um app para dispositivos móveis) inicia um pedido.
- Estabelece uma conexão gRPC/WebSocket com
BidiProcessOrder. - Envie
BidiProcessOrderRequestcomConfig(ID da sessão, ID da loja). - Receber e reproduzir a
AgentAudioinicial (por exemplo, mensagem de boas-vindas). - O usuário fala: capture o áudio e faça streaming dele em mensagens
AudioInput. - Receba
SpeechRecognition(exibir transcrição),AgentAudio(tocar resposta) e possivelmenteUpdatedOrderState(atualizar carrinho da UI). - Se o usuário interromper, receba
InterruptionSignale pare a reprodução. - Continue a troca de entradas de áudio ou texto e respostas do agente.
- O usuário confirma o pedido: o agente envia o
UpdatedOrderStatefinal. - O agente envia
EndSession: o cliente fecha o fluxo e finaliza o pedido no sistema de PDV usando dados do últimoUpdatedOrderState.
Exemplo completo
Embora as instruções acima detalhem os conceitos de streaming parte por parte, aqui está um exemplo de fluxo de integração completo de ponta a ponta.
Node.js
Antes de testar esse exemplo, siga as instruções de configuração para Node.js no Guia de início rápido do agente de IA para pedidos de comida: como usar bibliotecas de cliente.
Para autenticar no agente de IA para pedidos de comida, configure o Application Default Credentials. Para mais informações, consulte Configurar a autenticação para um ambiente de desenvolvimento local.
const {FoodOrderingServiceClient} = require('@google-cloud/foodorderingaiagent');
async function bidiProcessOrderSample(projectId, location, brand, store, sessionId) {
const client = new FoodOrderingServiceClient();
// Create the resource names
const sessionPath = client.sessionPath(projectId, location, sessionId);
const storePath = client.storePath(projectId, location, brand, store);
// Initialize the stream using gRPC. See the WebSocket section for the equivalent WebSocket implementation.
const stream = client.bidiProcessOrder();
// Attach event listeners to handle responses sequentially
stream.on('data', (response) => {
if (response.agentAudio) {
console.log(`Received ${response.agentAudio.agentAudio.length} bytes of agent audio.`);
} else if (response.agentText) {
console.log(`Agent: ${response.agentText.text}`);
} else if (response.speechRecognition) {
console.log(`Recognized User Speech: ${response.speechRecognition.transcript}`);
} else if (response.updatedOrderState) {
console.log('Order updated.');
} else if (response.interruptionSignal) {
console.log('User interrupted the agent. Stop playing audio!');
} else if (response.endSession) {
console.log(`Session ended. Type: ${response.endSession.type}, Reason: ${response.endSession.reason}`);
stream.end();
}
});
stream.on('error', (err) => {
console.error('Stream error:', err);
});
// 1. Send the first message containing Config
stream.write({
config: {
session: sessionPath,
store: storePath,
}
});
// 2. Stream user inputs over the active connection
stream.write({textInput: {text: 'Hi, I\'d like to order a cheeseburger.'}});
}