En esta guía, se proporcionan instrucciones y prácticas recomendadas para los ingenieros que crean experiencias de pedidos de comida con el método de RPC FoodOrderingService.BidiProcessOrder.
Esta API de transmisión bidireccional en tiempo real es el núcleo del agente de IA para pedidos de comida, ya que permite tomar pedidos de forma dinámica y conversacional en diversas aplicaciones, como apps para dispositivos móviles, asistentes de voz, servicios de pedidos desde el automóvil y quioscos.
Descripción general de BidiProcessOrder
El método BidiProcessOrder establece un canal de comunicación bidireccional persistente entre tu aplicación cliente y el agente de IA de pedidos de comida. A diferencia de las RPCs unarias estándar de solicitud y respuesta, este enfoque de transmisión permite lo siguiente:
- Interacción de baja latencia: Intercambio continuo de información sin la sobrecarga de solicitudes HTTP repetidas.
- Entrada multimodal: Control de transmisiones de audio (para pedidos por voz), entradas de texto y eventos del cliente.
- Respuestas en tiempo real: El agente puede enviar audio, texto, actualizaciones de pedidos y otros indicadores a medida que se desarrolla la conversación.
BidiProcessOrder no se puede invocar con REST. Las integraciones deben usar un protocolo orientado a la conexión:
- gRPC (recomendado): Proporciona un framework sólido y eficiente para la transmisión bidireccional.
- WebSocket: Adecuado para clientes o entornos en los que gRPC no es adecuado debido a limitaciones de lenguaje de programación o de red.
Consulta la referencia de la API de BidiProcessOrder para obtener definiciones de tipos detalladas. Las integraciones de WebSocket usan representaciones JSON de estos tipos, como se describe en la sección de WebSocket.
Requisitos previos
Antes de realizar la integración con BidiProcessOrder, haz lo siguiente:
Habilita la API: Asegúrate de que la API de Food Ordering AI Agent esté habilitada en tu proyecto Google Cloud.
bash gcloud services enable foodorderingaiagent.googleapis.com --project=PROJECT_IDAutenticación: Decide tu enfoque de autenticación y configura las cuentas de servicio y los roles de IAM necesarios, como se describe en Autenticación.
Ingesta de menú: Se debe ingerir un menú válido y asociarlo a un
Store. Consulta Cómo integrar datos de menú para obtener más detalles.
Autenticación
Para conectarte de forma segura a la RPC de BidiProcessOrder, tu aplicación debe autenticarse con una cuenta de servicio de Google Cloud .
1. Configura una cuenta de servicio
- Crea una cuenta de servicio: En tu proyecto Google Cloud , crea una cuenta de servicio que tu aplicación usará para autenticarse en la API del agente de IA para pedidos de comida. Consulta Crea y administra cuentas de servicio.
Otorga roles de IAM: Otorga los roles de IAM necesarios a esta cuenta de servicio. El rol principal requerido para llamar a
BidiProcessOrderes el siguiente:- Usuario del agente de pedidos de comida (
roles/foodorderingaiagent.agentUser): Permite que la cuenta de servicio se conecte al servicio de pedidos y procese sesiones.
Puedes otorgar este rol con la consola de Google Cloud o
gcloud:bash gcloud projects add-iam-policy-binding PROJECT_ID \ --member="serviceAccount:SERVICE_ACCOUNT_EMAIL" \ --role="roles/foodorderingaiagent.agentUser"- Usuario del agente de pedidos de comida (
2. Flujo de autenticación de la aplicación
El flujo de autenticación exacto depende de la arquitectura de tu aplicación, en especial si la aplicación cliente (p.ej., una app para dispositivos móviles o un software de quiosco) se conecta directamente o a través de tu propio backend.
Situación común: Autenticación de una aplicación cliente orientada al consumidor
Este es un patrón típico para las aplicaciones web o para dispositivos móviles:
- Client-to-YourAuth: La app cliente del usuario final (para dispositivos móviles o web) se autentica con tu sistema de autenticación de usuarios existente (podría ser Firebase Authentication, tu propio servidor de OAuth, etcétera).
- Intercambio de tokens: Después de autenticar al usuario, la app cliente solicita un token de corta duración a un servicio de backend seguro que tú controlas (p.ej., un "servicio de tokens de API").
Generación de tokens de acceso: Tu servicio de backend, con las credenciales de la principal de la cuenta de servicio Google Cloud configurada en el paso 1, genera un token de acceso estándar de OAuth 2.0 para el alcance
https://www.googleapis.com/auth/cloud-platform. Esto se puede hacer con las bibliotecas cliente de autenticación deGoogle Cloud .- Seguridad: Las claves o credenciales de la cuenta de servicio que se usan para generar estos tokens deben almacenarse y administrarse de forma segura en tu backend. Nunca expongas las claves privadas de las cuentas de servicio directamente a las aplicaciones cliente de los usuarios finales. Consulta las prácticas recomendadas para administrar claves de cuentas de servicio.
Token para el cliente: Tu servicio de backend devuelve el token de acceso de Google generado a la app cliente.
Llamada a la API: La app cliente usa este token de acceso de Google para autenticar su conexión de gRPC o WebSocket a la RPC de
BidiProcessOrder.
3. Cómo usar el token
- gRPC: Las bibliotecas cliente de gRPC de Google suelen controlar la actualización de tokens y la inclusión en los metadatos de la llamada cuando se proporcionan credenciales de la cuenta de servicio.
- WebSocket (no navegador): Incluye el token en el encabezado
Authorization: Bearer TOKEN. - WebSocket (navegador): Como se indicó en la sección de WebSocket, las conexiones directas de WebSocket del navegador no pueden usar encabezados de autorización. Se necesita un proxy de transmisión del servidor para autenticar la conexión de tus clientes a Google Cloud.
Conexión a la API
Puedes establecer una transmisión con las bibliotecas cliente de gRPC o una conexión de WebSocket.
gRPC
Se recomienda usar gRPC. Usarás las bibliotecas cliente para el lenguaje que elijas (p.ej., Node.js), que se basan en la referencia de la API de BidiProcessOrder.
Los pasos básicos son los siguientes:
- Crea un canal de gRPC para el extremo de API del agente de IA de pedidos de comida (p.ej.,
foodorderingaiagent.googleapis.com). - Obtén un stub de cliente para
FoodOrderingService. - Invoca el método
BidiProcessOrder, que devuelve un objeto de transmisión para enviar solicitudes y recibir respuestas. - Implementa la lógica empresarial según tu caso de uso, que, de forma simultánea, haga lo siguiente:
- Envía audio, texto y entrada de eventos del usuario final.
- Controla los mensajes del agente, incluidos el audio, el texto y los 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 las conexiones WebSocket, la ruta de URL es la siguiente:
wss://foodorderingaiagent.googleapis.com/ws/google.cloud.foodorderingaiagent.v1beta.FoodOrderingService/BidiProcessOrder/locations/LOCATION
LOCATION: p.ej.,us
Encabezados obligatorios:
Authorization:Bearer TOKEN, dondeTOKENes un token de acceso de OAuth 2.0 que se obtuvo para tu cuenta de servicio.
Formato del mensaje:
- Cliente a servidor: Los mensajes enviados a la API (p.ej.,
Config,AudioInput,TextInput,EventInput) deben ser representaciones JSON del protoBidiProcessOrderRequest, que se envían comowebsocket.TextMessage. - Del servidor al cliente: Los mensajes que se reciben de la API (
BidiProcessOrderResponse) se enviarán comowebsocket.BinaryMessage, pero el contenido de estos mensajes binarios es una carga útil JSON. - Datos binarios: Los datos binarios dentro de las cargas útiles de JSON (p.ej.,
customerAudioenAudioInput,agentAudioenAgentAudio) deben estar codificados en base64.
Ejemplo de WebSocket de Node.js
A continuación, se muestra un ejemplo de cómo conectarse a la API y cómo interactuar con ella usando WebSockets en Node.js con la 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 de la sesión
Cada llamada a BidiProcessOrder inicia una sesión. La sesión permanece activa mientras el flujo esté abierto.
1. Iniciación (mensaje de configuración)
- Una vez que se establece la conexión, el primer mensaje que envía el cliente debe ser un
BidiProcessOrderRequestque contenga el mensajeConfig. - Campos obligatorios en
Config:session: Es un identificador de sesión único generado por el cliente. Formato:projects/PROJECT/locations/LOCATION/sessions/SESSION_ID.store: Es el nombre del recursoStore. Formato:projects/PROJECT/locations/LOCATION/brands/BRAND/stores/STORE.- El agente usa
storepara cargar el menú y la configuración adecuados.
- El agente usa
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. Envío de entradas
- Después del
Configinicial, el cliente puede enviar un flujo de mensajesBidiProcessOrderRequestque contengan una de las siguientes entradas:AudioInput: Datos de audio sin procesar (por lo general, PCM lineal de 16 bits a 16,000 Hz, sin encabezados). Se usa para las interacciones de voz.TextInput: Son los mensajes de texto del usuario.EventInput: Son indicadores de eventos comoDriveOffEvent(para casos de uso de autoservicio cuando el vehículo se va),CrewInterjectionEvent(para cualquier situación en la que una persona asume el rol de tomar el pedido en medio de la conversación) oOrderStateUpdateEvent(si el pedido se modifica del lado del cliente, p.ej., con una interfaz táctil).
Node.js
// Stream user inputs over the active connection
stream.write({textInput: {text: 'Hi, I\'d like to order a cheeseburger.'}});
3. Cómo recibir respuestas
- De forma simultánea, el agente envía un flujo de mensajes de
BidiProcessOrderResponse. Tu cliente debe estar preparado para manejar varios tipos de respuestas dentro del campooneof response:AgentAudio: Son los bytes de audio sintetizado que se reproducirán para el usuario y que se usan para las interacciones por voz.AgentText: Es la versión de texto de la respuesta del agente.SpeechRecognition: Es la transcripción del discurso del usuario reconocido.UpdatedOrderState: Contiene el estado actual completo delOrderdel cliente siempre que el agente lo actualice. Úsalo para actualizar la representación del pedido de tu aplicación. Por lo general, esto debería generar una actualización en una interfaz de usuario o en un sistema de registros para la información del estado del pedido, como un sistema de punto de venta.InterruptionSignal: Indica que el usuario interrumpió el discurso del agente. El cliente debe dejar de reproducir de inmediato cualquierAgentAudiosaliente.AgentEvent: Eventos especiales, comoRestartOrder, que requieren una acción del cliente.SuggestedOptions: Proporciona opciones contextualmente relevantes que un usuario podría seleccionar a continuación, lo que resulta útil para mostrar en una pantalla.EndSession: Indica que el agente finalizó la sesión (p.ej., pedido completado, el usuario se fue o el agente derivó el caso).
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. Cómo cerrar la transmisión
- El cliente o el servidor pueden cerrar la transmisión. Por lo general, el servidor señala el final de una conversación con un mensaje
EndSession. El cliente debe cerrar la transmisión cuando se recibe este mensaje.
Cómo controlar tipos de mensajes específicos
En las siguientes secciones, se describe cómo controlar los tipos de respuestas específicos que recibirá tu cliente cuando llame a BidiProcessOrder.
AudioInput
- Transmite audio en fragmentos a medida que esté disponible.
- Formato: PCM lineal de 16 bits, frecuencia de muestreo de 16,000 Hz
- Los fragmentos de audio no incluyen los encabezados de audio que suelen preceder a un archivo WAV.
- Para situaciones de autoservicio con cancelación de eco habilitada (
enable_echo_cancellationenConfig), proporcionacustomer_audioycrew_audio.
UpdatedOrderState
- Este mensaje proporciona el estado completo del pedido cada vez que se envía.
Reemplaza cualquier caché local del pedido por el contenido del mensaje
Orderrecibido. - Usa
custom_integration_attributesdentro de los elementos y modificadoresOrderpara asignar el contenido deOrdera entidades equivalentes dentro del sistema de registros de tu aplicación.
InterruptionSignal
- Al recibirla, detén de inmediato la reproducción de cualquier
AgentAudioy borra cualquier audio del agente almacenado en búfer. Esto garantiza un flujo de conversación natural cuando el usuario interrumpe el discurso del agente.
EndSession
- Verifica el
EndType(p.ej.,DRIVE_OFF,AGENT_ESCALATION). - Tu aplicación debe cerrar la conexión correctamente y realizar la transición del usuario de forma adecuada (p.ej., notificar a un supervisor humano en el caso de
AGENT_ESCALATIONo realizar la transición a un estado de confirmación del pedido).
Prácticas recomendadas
- Controla los mensajes de forma asíncrona: Minimiza la latencia usando subprocesos o E/S no bloqueantes para enviar solicitudes y procesar respuestas entrantes de forma simultánea.
- Lógica de reconexión: Implementa una lógica de reconexión sólida en caso de problemas de red. Recuerda enviar el mensaje
Configinicial con el mismo ID de sesión para intentar reanudar la conexión. - Control de errores: Supervisa la transmisión en busca de errores. Las bibliotecas de gRPC y WebSocket proporcionan mecanismos para detectar el cierre de la transmisión o errores de transporte. Registra estos eventos y contrólalos con facilidad.
- Almacenamiento en búfer de audio: Administra los búferes de audio con cuidado y, si es necesario, implementa el almacenamiento en búfer para garantizar una reproducción fluida de
AgentAudioy una entrega oportuna deAudioInput. Cuando decidas tu esquema de almacenamiento en búfer, considera cuidadosamente la compensación entre la latencia y la calidad de reproducción. - Administración de IDs de sesión: Asegúrate de que los IDs de sesión sean únicos para cada pedido o conversación distintos.
- Administración de recursos: Cierra las transmisiones y libera los recursos cuando finaliza la sesión o si se producen errores irrecuperables.
- Tiempos de espera: Si bien la transmisión en sí puede ser de larga duración (hasta 15 minutos de forma predeterminada), considera los tiempos de espera a nivel de la aplicación para estados específicos si es necesario.
Ejemplo de flujo de integración (conceptual)
- La app cliente (p.ej., una app para dispositivos móviles) inicia un pedido.
- Establece una conexión gRPC/WebSocket a
BidiProcessOrder. - Envía
BidiProcessOrderRequestconConfig(ID de sesión, ID de tienda). - Recibe el
AgentAudioinicial (p.ej., mensaje de bienvenida) y reprodúcelo. - El usuario habla: Captura el audio y lo transmite en mensajes de
AudioInput. - Recibe
SpeechRecognition(muestra la transcripción),AgentAudio(reproduce la respuesta) y, posiblemente,UpdatedOrderState(actualiza el carrito de la IU). - Si el usuario interrumpe la acción, recibe
InterruptionSignaly detiene la reproducción. - Continuar el intercambio de entradas de audio o texto y respuestas del agente
- El usuario confirma el pedido: El agente envía el
UpdatedOrderStatefinal. - El agente envía
EndSession: El cliente cierra la transmisión y finaliza el pedido en el sistema de POS con los datos del últimoUpdatedOrderState.
Ejemplo de extremo a extremo
Si bien las instrucciones anteriores desglosan los conceptos de transmisión paso a paso, a continuación se muestra cómo se ve un flujo de integración completo de extremo a extremo.
Node.js
Antes de probar este ejemplo, sigue las instrucciones de configuración para Node.js incluidas en la guía de inicio rápido del agente de IA para pedidos de comida sobre cómo usar bibliotecas cliente.
Para autenticarte en el agente de IA de pedidos de comida, configura las credenciales predeterminadas de la aplicación. Para obtener más información, consulta Configura la autenticación para un entorno de desarrollo 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.'}});
}