Questa guida fornisce istruzioni e best practice per gli ingegneri che creano esperienze di ordinazione di cibo con il metodo RPC FoodOrderingService.BidiProcessOrder.
Questa API di streaming bidirezionale in tempo reale è il cuore di Food Ordering AI Agent e consente l'acquisizione di ordini dinamici e conversazionali in varie applicazioni, come app mobile, assistenti vocali, drive-through e chioschi.
Panoramica di BidiProcessOrder
Il metodo BidiProcessOrder stabilisce un canale di comunicazione bidirezionale persistente tra l'applicazione client e l'agente AI per l'ordinazione di cibo. A differenza delle RPC standard di richiesta e risposta unarie, questo approccio di streaming consente di:
- Interazione a bassa latenza: scambio continuo di informazioni senza l'overhead delle richieste HTTP ripetute.
- Input multimodale: gestione di stream audio (per l'ordinazione vocale), input di testo ed eventi lato client.
- Risposte in tempo reale: l'agente può inviare audio, testo, aggiornamenti degli ordini e altri segnali man mano che la conversazione si svolge.
BidiProcessOrder non può essere chiamato utilizzando REST. Le integrazioni devono utilizzare un protocollo orientato alla connessione:
- gRPC (consigliato): fornisce un framework robusto ed efficiente per lo streaming bidirezionale.
- WebSocket: adatto a client o ambienti in cui gRPC non è adatto a causa di vincoli di linguaggio di programmazione o di rete.
Per definizioni dettagliate dei tipi, consulta il riferimento API BidiProcessOrder Reference. Le integrazioni WebSocket utilizzano rappresentazioni JSON di questi tipi, come descritto nella sezione WebSocket.
Prerequisiti
Prima di eseguire l'integrazione con BidiProcessOrder:
Abilita l'API: assicurati che l'API Food Ordering AI Agent sia abilitata nel tuo Google Cloud progetto.
bash gcloud services enable foodorderingaiagent.googleapis.com --project=PROJECT_IDAutenticazione: decidi l'approccio di autenticazione e configura tutti i service account e i ruoli IAM necessari, come descritto in Autenticazione.
Importazione del menu: è necessario importare un menu valido e associarlo a un
Store. Per maggiori dettagli, consulta Integrare i dati del menu.
Autenticazione
Per connettersi in modo sicuro alla RPC BidiProcessOrder, l'applicazione deve
eseguire l'autenticazione utilizzando un Google Cloud service account.
1. Configura un service account
- Crea un service account: nel tuo Google Cloud progetto, crea un service account che la tua applicazione utilizzerà per l'autenticazione all'API Agente AI per l'Ordinazione di cibo. Consulta la pagina Creazione e gestione dei service account.
Concedi ruoli IAM: concedi i ruoli IAM necessari a questo account di servizio. Il ruolo principale richiesto per chiamare
BidiProcessOrderè:- Utente agente di ordinazione di cibo (
roles/foodorderingaiagent.agentUser): consente al account di servizio di connettersi al servizio di ordinazione ed elaborare le sessioni.
Puoi concedere questo ruolo utilizzando la Google Cloud console o
gcloud:bash gcloud projects add-iam-policy-binding PROJECT_ID \ --member="serviceAccount:SERVICE_ACCOUNT_EMAIL" \ --role="roles/foodorderingaiagent.agentUser"- Utente agente di ordinazione di cibo (
2. Flusso di autenticazione dell'applicazione
Il flusso di autenticazione esatto dipende dall'architettura dell'applicazione, in particolare se l'applicazione client (ad es. app mobile, software per chioschi) si connette direttamente o tramite il tuo backend.
Scenario comune: autenticazione di un'applicazione client rivolta ai consumatori
Questo è un pattern tipico per le applicazioni web o mobile:
- Client-to-YourAuth: l'app client dell'utente finale (mobile, web) esegue l'autenticazione con il tuo sistema di autenticazione utente esistente (potrebbe essere l'autenticazione Firebase, il tuo server OAuth e così via).
- Scambio di token: dopo aver autenticato l'utente, l'app client richiede un token di breve durata da un servizio di backend sicuro che controlli (ad es. un "servizio token API").
Generazione del token di accesso: il tuo servizio di backend, utilizzando le credenziali dell' entità del service account configurata nel passaggio 1, genera un token di accesso OAuth 2.0 standard per l'
https://www.googleapis.com/auth/cloud-platformambito. Google Cloud Questa operazione può essere eseguita utilizzando le Google Cloud librerie client di autenticazione.- Sicurezza: le chiavi o le credenziali del service account utilizzate per generare questi token devono essere archiviate e gestite in modo sicuro nel backend. Non esporre mai le chiavi private del account di servizio direttamente alle applicazioni client dell'utente finale. Consulta le best practice per la gestione delle chiavi account di servizio .
Token al client: il tuo servizio di backend restituisce il token di accesso Google generato all'app client.
Chiamata API: l'app client utilizza questo token di accesso Google per autenticare la connessione gRPC o WebSocket alla RPC
BidiProcessOrder.
3. Utilizzo del token
- gRPC: le librerie client gRPC di Google in genere gestiscono l'aggiornamento e l'inclusione dei token nei metadati delle chiamate quando vengono fornite le credenziali del account di servizio.
- WebSocket (non browser): includi il token nell'intestazione
Authorization: Bearer TOKEN. - WebSocket (browser): come indicato nella sezione WebSocket, le connessioni WebSocket dirette del browser non possono utilizzare le intestazioni di autorizzazione. È necessario un proxy di streaming lato server per autenticare la connessione dei client a Google Cloud.
Connessione all'API
Puoi stabilire uno stream utilizzando le librerie client gRPC o una connessione WebSocket.
gRPC
L'approccio consigliato è l'utilizzo di gRPC. Utilizzerai le librerie client per la tua lingua che preferisci (ad es. Node.js), basate sul riferimento API BidiProcessOrder.
I passaggi di base sono:
- Crea un canale gRPC per l'endpoint API Food Ordering AI Agent (ad es.
foodorderingaiagent.googleapis.com). - Ottieni uno stub client per
FoodOrderingService. - Richiama il metodo
BidiProcessOrder, che restituisce un oggetto stream per l'invio di richieste e la ricezione di risposte. - Implementa la logica di business in base al tuo caso d'uso, che contemporaneamente:
- Invia input audio, di testo ed eventi dall'utente finale.
- Gestisce i messaggi dell'agente, inclusi audio, testo ed eventi.
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
Per le connessioni WebSocket, il percorso dell'URL è:
wss://foodorderingaiagent.googleapis.com/ws/google.cloud.foodorderingaiagent.v1beta.FoodOrderingService/BidiProcessOrder/locations/LOCATION
LOCATION: ad es.us
Intestazioni obbligatorie:
Authorization:Bearer TOKEN- DoveTOKENè un token di accesso OAuth 2.0 ottenuto per il tuo account di servizio.
Formato dei messaggi:
- Client-server: i messaggi inviati all'API (ad es.
Config,AudioInput,TextInput,EventInput) devono essere rappresentazioni JSON del protoBidiProcessOrderRequest, inviate comewebsocket.TextMessage. - Server-client: i messaggi ricevuti dall'API (
BidiProcessOrderResponse) verranno inviati comewebsocket.BinaryMessage, ma il contenuto di questi messaggi binari è un payload JSON. - Dati binari: i dati binari all'interno dei payload JSON (ad es.
customerAudioinAudioInput,agentAudioinAgentAudio) devono essere codificati in base64.
Esempio di WebSocket Node.js
Ecco un esempio di come connettersi e interagire con l'API utilizzando WebSocket in Node.js con la libreria 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 di vita della sessione
Ogni chiamata a BidiProcessOrder avvia una sessione. La sessione rimane attiva finché lo stream è aperto.
1. Inizializzazione (messaggio di configurazione)
- Dopo aver stabilito la connessione, il primo messaggio inviato dal client
deve essere un
BidiProcessOrderRequestcontenente ilConfigmessaggio. - Campi obbligatori in
Config:session: un identificatore di sessione univoco generato dal client. Formato:projects/PROJECT/locations/LOCATION/sessions/SESSION_ID.store: il nome della risorsa delloStore. Formato:projects/PROJECT/locations/LOCATION/brands/BRAND/stores/STORE.- L'agente utilizza
storeper caricare il menu e la configurazione appropriati.
- L'agente utilizza
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. Invio di input
- Dopo la
Configiniziale, il client può inviare uno stream di messaggiBidiProcessOrderRequestcontenenti uno dei seguenti input:AudioInput: dati audio non elaborati (in genere PCM lineare a 16 bit a 16000 Hz, senza intestazioni). Utilizzato per le interazioni vocali.TextInput: messaggi di testo dell'utente.EventInput: segnali per eventi comeDriveOffEvent(per i casi d'uso drive-through quando il veicolo parte),CrewInterjectionEvent(per qualsiasi situazione in cui un essere umano assume il ruolo di acquisizione dell'ordine a metà conversazione) oOrderStateUpdateEvent(se l'ordine viene modificato lato client, ad es. utilizzando un'interfaccia touch).
Node.js
// Stream user inputs over the active connection
stream.write({textInput: {text: 'Hi, I\'d like to order a cheeseburger.'}});
3. Ricezione delle risposte
- Contemporaneamente, l'agente invia uno stream di messaggi
BidiProcessOrderResponse. Il client deve essere pronto a gestire vari tipi di risposta all'interno del campooneof response:AgentAudio: byte audio sintetizzati da riprodurre all'utente, utilizzati per le interazioni vocali.AgentText: versione di testo della risposta dell'agente.SpeechRecognition: trascrizione del discorso dell'utente riconosciuto.UpdatedOrderState: Contiene lo stato corrente completo dell'Orderogni volta che viene aggiornato dall'agente. Utilizza questo campo per aggiornare la rappresentazione dell'ordine dell'applicazione. In genere, questa operazione comporta un aggiornamento di un'interfaccia utente o di un sistema di registrazione per le informazioni sullo stato dell'ordine, ad esempio un sistema point of sale.InterruptionSignal: indica che l'utente ha interrotto il discorso dell'agente. Il client deve interrompere immediatamente la riproduzione di qualsiasiAgentAudioin uscita.AgentEvent: eventi speciali, comeRestartOrder, che richiedono l'intervento del client.SuggestedOptions: fornisce opzioni contestualmente pertinenti che un utente potrebbe selezionare in seguito, utili per la visualizzazione su uno schermo.EndSession: segnala che la sessione è stata terminata dall'agente (ad es. ordine completato, utente che ha lasciato il drive-through o escalation dell'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. Chiusura dello stream
- Lo stream può essere chiuso dal client o dal server. In genere, il server segnala la fine di una conversazione utilizzando un messaggio
EndSession. Il client deve chiudere lo stream quando riceve questo messaggio.
Gestione di tipi di messaggi specifici
Nelle sezioni seguenti viene descritto come gestire tipi di risposta specifici che il client riceverà quando chiama BidiProcessOrder.
AudioInput
- Trasmetti in streaming l'audio in blocchi man mano che diventa disponibile.
- Formato: PCM lineare a 16 bit, frequenza di campionamento di 16000 Hz.
- I blocchi audio non includono le intestazioni audio che in genere precedono un file WAV.
- Per gli scenari drive-through con cancellazione eco attivata (
enable_echo_cancellationinConfig), fornisci siacustomer_audiochecrew_audio.
UpdatedOrderState
- Questo messaggio fornisce lo stato completo dell'ordine ogni volta che viene inviato.
Sostituisci qualsiasi cache locale dell'ordine con i contenuti del messaggio
Orderricevuto. - Utilizza
custom_integration_attributesall'interno degli articoli e dei modificatoriOrderper mappare i contenutiOrderin entità equivalenti all'interno del sistema di registrazione dell'applicazione.
InterruptionSignal
- Al momento della ricezione, interrompi immediatamente la riproduzione di qualsiasi
AgentAudioe cancella l'audio dell'agente memorizzato nel buffer. In questo modo si garantisce un flusso conversazionale naturale quando l'utente interrompe il discorso dell'agente.
EndSession
- Controlla
EndType(ad es.DRIVE_OFF,AGENT_ESCALATION). - L'applicazione deve chiudere correttamente la connessione e passare l'utente in modo appropriato (ad es. notificare a un supervisore umano in caso di
AGENT_ESCALATIONo passare a uno stato di conferma dell'ordine).
Best practice
- Gestisci i messaggi in modo asincrono: riduci al minimo la latenza utilizzando thread o I/O non bloccanti per inviare contemporaneamente le richieste ed elaborare le risposte in entrata.
- Logica di riconnessione: implementa una logica di riconnessione robusta in caso di problemi di rete, ricordando di inviare il messaggio
Configiniziale con lo stesso ID sessione per tentare di riprendere la sessione. - Gestione degli errori: monitora lo stream per rilevare eventuali errori. Le librerie gRPC e WebSocket forniscono meccanismi per rilevare la chiusura dello stream o gli errori di trasporto. Registra questi eventi e gestiscili in modo appropriato.
- Buffer audio: gestisci attentamente i buffer audio, implementando il buffering, se necessario, per garantire una riproduzione fluida di
AgentAudioe la consegna tempestiva diAudioInput. Valuta attentamente il compromesso tra latenza e qualità di riproduzione quando decidi lo schema di buffering. - Gestione degli ID sessione: assicurati che gli ID sessione siano univoci per ogni ordine/conversazione distinta.
- Gestione delle risorse: chiudi gli stream e rilascia le risorse al termine della sessione o se si verificano errori non recuperabili.
- Timeout: anche se lo stream stesso può essere di lunga durata (fino a 15 minuti per impostazione predefinita), valuta la possibilità di utilizzare timeout a livello di applicazione per stati specifici, se necessario.
Flusso di integrazione di esempio (concettuale)
- L'app client (ad es. app mobile) avvia un ordine.
- Stabilisci la connessione gRPC/WebSocket a
BidiProcessOrder. - Invia
BidiProcessOrderRequestconConfig(ID sessione, ID negozio). - Ricevi l'audio iniziale dell'agente (
AgentAudio, ad es. messaggio di benvenuto) e riproducilo. - L'utente parla: acquisisci l'audio e trasmettilo in streaming nei messaggi
AudioInput. - Ricevi
SpeechRecognition(visualizza la trascrizione),AgentAudio(riproduci la risposta) e, potenzialmente,UpdatedOrderState(aggiorna il carrello dell'interfaccia utente). - Se l'utente interrompe, ricevi
InterruptionSignale interrompi la riproduzione. - Continua lo scambio di input audio o di testo e le risposte dell'agente.
- L'utente conferma l'ordine: l'agente invia l'ultimo
UpdatedOrderState. - L'agente invia
EndSession: il client chiude lo stream e finalizza l'ordine nel sistema POS utilizzando i dati dell'ultimoUpdatedOrderState.
Esempio end-to-end
Sebbene le istruzioni riportate sopra suddividano i concetti di streaming passo dopo passo, ecco come appare un flusso di integrazione end-to-end completo.
Node.js
Prima di provare questo esempio, segui le istruzioni di configurazione Node.js nella guida rapida di Ordinazione di cibo AI Agent per l'utilizzo delle librerie client.
Per eseguire l'autenticazione in Agente AI per l'ordinazione di cibo, configura le Credenziali predefinite dell'applicazione. Per saperne di più, consulta Configura l'autenticazione per un ambiente di sviluppo locale.
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.'}});
}