במדריך הזה מפורטות הוראות ושיטות מומלצות למהנדסים שיוצרים חוויות של הזמנת אוכל באמצעות שיטת FoodOrderingService.BidiProcessOrder RPC.
ממשק ה-API הזה, שמאפשר סטרימינג דו-כיווני בזמן אמת, הוא הליבה של סוכן ה-AI להזמנה ממסעדה. הוא מאפשר לקבל הזמנות באופן דינמי ובשיחה באפליקציות שונות, כמו אפליקציות לנייד, עוזרים קוליים, מסעדות דרייב-אין וקיוסקים.
סקירה כללית של BidiProcessOrder
שיטת BidiProcessOrder יוצרת ערוץ תקשורת דו-כיווני ומתמשך בין אפליקציית הלקוח לבין סוכן ה-AI להזמנה ממסעדה. בניגוד ל-RPC רגיל של בקשה ותגובה יחידות, גישת הסטרימינג הזו מאפשרת:
- אינטראקציה עם זמן אחזור נמוך: חילופי מידע רציפים ללא התקורה של בקשות HTTP חוזרות.
- קלט מולטי-מודאלי: טיפול בזרמי אודיו (להזמנה קולית), בקלט טקסט ובאירועים מצד הלקוח.
- תשובות בזמן אמת: הסוכן יכול לשלוח אודיו, טקסט, עדכונים לגבי הזמנות ואותות אחרים במהלך השיחה.
BidiProcessOrder לא ניתן להפעיל באמצעות REST. השילובים צריכים להשתמש בפרוטוקול מוכוון-חיבור:
- gRPC (מומלץ): מספק מסגרת חזקה ויעילה להזרמת נתונים דו-כיוונית.
- WebSocket: מתאים ללקוחות או לסביבות שבהן gRPC לא מתאים בגלל שפת תכנות או מגבלות רשת.
הגדרות מפורטות של סוגים זמינות ב-API Reference של BidiProcessOrder. שילובים של WebSocket משתמשים בייצוגי JSON של הסוגים האלה, כפי שמתואר בקטע על WebSocket.
דרישות מוקדמות
לפני שמשלבים עם BidiProcessOrder:
מפעילים את ה-API: מוודאים ש-AI Agent להזמנה ממסעדה API מופעל ב Google Cloudפרויקט.
bash gcloud services enable foodorderingaiagent.googleapis.com --project=PROJECT_IDאימות: בוחרים את שיטת האימות ומגדירים את חשבונות השירות ותפקידי ה-IAM הנדרשים, כמו שמתואר במאמר בנושא אימות.
העלאת תפריט: צריך להעלות תפריט תקין ולשייך אותו ל-
Store. פרטים נוספים זמינים במאמר בנושא שילוב נתוני תפריט.
אימות
כדי להתחבר בצורה מאובטחת ל-BidiProcessOrder RPC, האפליקציה צריכה לעבור אימות באמצעות Google Cloud חשבון שירות.
1. הגדרת חשבון שירות
- יוצרים חשבון שירות: בפרויקט Google Cloud , יוצרים חשבון שירות שהאפליקציה תשתמש בו כדי לבצע אימות ל-API של סוכן ה-AI להזמנה ממסעדה. איך יוצרים ומנהלים חשבונות שירות
מקצים תפקידים ב-IAM: מקצים לחשבון השירות הזה את התפקידים הנדרשים ב-IAM. התפקיד הראשי שנדרש כדי להתקשר אל
BidiProcessOrderהוא:- Food Ordering Agent User (
roles/foodorderingaiagent.agentUser): מאפשר לחשבון השירות להתחבר לשירות הזמנה ממסעדה ולעבד סשנים.
אפשר להקצות את התפקיד הזה באמצעות Google Cloud המסוף או
gcloud:bash gcloud projects add-iam-policy-binding PROJECT_ID \ --member="serviceAccount:SERVICE_ACCOUNT_EMAIL" \ --role="roles/foodorderingaiagent.agentUser"- Food Ordering Agent User (
2. תהליך האימות של האפליקציה
תהליך האימות המדויק תלוי בארכיטקטורת האפליקציה, במיוחד אם אפליקציית הלקוח (למשל, אפליקציה לנייד, תוכנה לעמדה) מתחברת ישירות או דרך ה-Backend שלכם.
תרחיש נפוץ: אימות של אפליקציית לקוח שפונה לצרכנים
זהו דפוס אופייני לאפליקציות לנייד או לאינטרנט:
- Client-to-YourAuth: אפליקציית הלקוח של משתמש הקצה (לנייד, לאינטרנט) מאומתת באמצעות מערכת אימות המשתמשים הקיימת שלכם (יכול להיות שזו מערכת אימות ב-Firebase, שרת OAuth משלכם וכו').
- החלפת אסימונים: אפליקציית הלקוח, אחרי אימות המשתמש, מבקשת אסימון לטווח קצר משירות מאובטח לקצה העורפי שנמצא בשליטתכם (לדוגמה, 'שירות אסימוני API').
יצירת אסימון גישה: שירות הקצה העורפי שלכם, באמצעות פרטי הכניסה של החשבון הראשי Google Cloud Service Account שהוגדר בשלב 1, יוצר אסימון גישה רגיל מסוג OAuth 2.0 עבור ההיקף
https://www.googleapis.com/auth/cloud-platform. אפשר לעשות זאת באמצעות ספריות הלקוח שלGoogle Cloud Authentication.- אבטחה: צריך לאחסן ולנהל בצורה מאובטחת בשרת העורפי את המפתחות של חשבונות השירות או את פרטי הכניסה שמשמשים ליצירת הטוקנים האלה. לעולם אל תחשפו מפתחות פרטיים של חשבונות שירות ישירות לאפליקציות לקוח של משתמשי קצה. שיטות מומלצות לניהול מפתחות של חשבונות שירות
טוקן ללקוח: שירות הקצה העורפי מחזיר לאפליקציית הלקוח את טוקן הגישה שנוצר ב-Google.
קריאה ל-API: אפליקציית הלקוח משתמשת באסימון הגישה הזה של Google כדי לאמת את החיבור שלה ל-RPC מסוג gRPC או WebSocket אל
BidiProcessOrderRPC.
3. שימוש בטוקן
- gRPC: ספריות הלקוח של Google gRPC בדרך כלל מטפלות ברענון האסימון ובשילוב שלו במטא-נתונים של הקריאה, כשהן מקבלות פרטי כניסה של חשבון שירות.
- WebSocket (לא בדפדפן): צריך לכלול את הטוקן בכותרת
Authorization: Bearer TOKEN. - WebSocket (דפדפן): כמו שצוין בקטע WebSocket, אי אפשר להשתמש בכותרות Authorization בחיבורי WebSocket ישירים בדפדפן. נדרש שרת proxy להזרמה בצד השרת כדי לאמת את החיבור של הלקוחות אל Google Cloud.
התחברות ל-API
אפשר ליצור סטרימינג באמצעות ספריות לקוח של gRPC או חיבור WebSocket.
gRPC
הגישה המומלצת היא להשתמש ב-gRPC. תשתמשו בספריות הלקוח בשפה שבחרתם (למשל, Node.js), שמבוססות על הפניה ל-API של BidiProcessOrder.
השלבים הבסיסיים כוללים:
- יוצרים ערוץ gRPC לנקודת קצה ל-API של סוכן AI להזמנה ממסעדה (לדוגמה,
foodorderingaiagent.googleapis.com). - קבלת stub של לקוח עבור
FoodOrderingService. - מפעילים את השיטה
BidiProcessOrder, שמחזירה אובייקט של זרם גם לשליחת בקשות וגם לקבלת תגובות. - מטמיעים לוגיקה עסקית בהתאם לתרחיש לדוגמה, שבו מתבצעים במקביל הפעולות הבאות:
- שליחת קלט של אודיו, טקסט ואירועים ממשתמש הקצה.
- מטפל בהודעות מהסוכן, כולל אודיו, טקסט ואירועים.
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
בחיבורי WebSocket, נתיב כתובת ה-URL הוא:
wss://foodorderingaiagent.googleapis.com/ws/google.cloud.foodorderingaiagent.v1beta.FoodOrderingService/BidiProcessOrder/locations/LOCATION
LOCATION: לדוגמה,us
כותרות חובה:
-
Authorization:Bearer TOKEN– כאשרTOKENהוא אסימון גישה מסוג OAuth 2.0 שהתקבל עבור חשבון השירות.
פורמט ההודעה:
- לקוח לשרת: הודעות שנשלחות ל-API (למשל,
Config,AudioInput, TextInput, EventInput) צריכות להיות ייצוגים בפורמט JSON שלBidiProcessOrderRequestproto, שנשלחים כ-websocket.TextMessage. - מהשרת ללקוח: הודעות שמתקבלות מה-API (
BidiProcessOrderResponse) יישלחו כ-websocket.BinaryMessage, אבל התוכן של ההודעות הבינאריות האלה הוא מטען ייעודי (payload) בפורמט JSON. - נתונים בינאריים: נתונים בינאריים במטענים הייעודיים (payloads) של JSON (לדוגמה,
customerAudioב-AudioInput, agentAudioב-AgentAudio) צריכים להיות בקידוד base64.
דוגמה ל-WebSocket ב-Node.js
הנה דוגמה לאופן ההתחברות ל-API וליצירת אינטראקציה איתו באמצעות WebSockets ב-Node.js עם הספרייה 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');
});
מחזור החיים של סשן
כל קריאה ל-BidiProcessOrder יוצרת סשן. הסשן נשאר פעיל כל עוד הסטרימינג פתוח.
1. התחלת השיחה (הודעת הגדרה)
- אחרי יצירת החיבור, ההודעה הראשונה שנשלחת מהלקוח חייבת להיות
BidiProcessOrderRequestשמכילה את ההודעהConfig. - שדות חובה ב-
Config:-
session: מזהה סשן ייחודי שנוצר על ידי הלקוח. פורמט:projects/PROJECT/locations/LOCATION/sessions/SESSION_ID.-
store: שם המשאב שלStore. פורמט:projects/PROJECT/locations/LOCATION/brands/BRAND/stores/STORE.- הסוכן משתמש ב-
storeכדי לטעון את התפריט וההגדרות המתאימים.
- הסוכן משתמש ב-
-
-
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. שליחת קלט
- אחרי ההודעה הראשונית
Config, הלקוח יכול לשלוח רצף של הודעותBidiProcessOrderRequestשמכילות אחת מהכניסות הבאות:-
AudioInput: נתוני אודיו גולמיים (בדרך כלל PCM ליניארי של 16 ביט ב-16,000 הרץ, ללא כותרות). משמש לאינטראקציות קוליות. -
TextInput: הודעות טקסט מהמשתמש. -
EventInput: אותות לאירועים כמוDriveOffEvent(לתרחישי שימוש בשירות Drive-Thru כשכלי הרכב יוצא),CrewInterjectionEvent(לכל מצב שבו אדם משתלט על תפקיד קבלת ההזמנה באמצע השיחה) אוOrderStateUpdateEvent(אם ההזמנה משתנה בצד הלקוח, למשל באמצעות ממשק מגע).
-
Node.js
// Stream user inputs over the active connection
stream.write({textInput: {text: 'Hi, I\'d like to order a cheeseburger.'}});
3. קבלת תשובות
- במקביל, הסוכן שולח בחזרה זרם של
BidiProcessOrderResponseהודעות. הלקוח צריך להיות מוכן לטפל בסוגים שונים של תשובות בשדהoneof response:-
AgentAudio: בייטים של אודיו מסונתז שיושמעו למשתמש, לשימוש באינטראקציות קוליות. -
AgentText: גרסת הטקסט של התגובה של הסוכן. -
SpeechRecognition: תמליל של הדיבור של המשתמש שזוהה. -
UpdatedOrderState: מכיל את המצב הנוכחי המלא שלOrderשל הלקוח. בכל פעם שהסוכן מעדכן אותו. אפשר להשתמש במידע הזה כדי לעדכן את ייצוג ההזמנה באפליקציה. בדרך כלל, הפעולה הזו אמורה לגרום לעדכון של ממשק משתמש או של מערכת רישום למידע על סטטוס ההזמנה, כמו מערכת נקודת מכירה. -
InterruptionSignal: מציין שהמשתמש קטע את הדיבור של הסוכן. הלקוח צריך להפסיק מיד את ההשמעה של כלAgentAudioיוצא. -
AgentEvent: אירועים מיוחדים, כמוRestartOrder, שמחייבים פעולה מצד הלקוח. -
SuggestedOptions: מספק אפשרויות רלוונטיות בהקשר מסוים שהמשתמש עשוי לבחור בהן בהמשך. האפשרויות האלה שימושיות להצגה במסך. -
EndSession: הסוכן מציין שהסשן הסתיים (למשל, הזמנה הושלמה, המשתמש עזב או שהסוכן העביר את השיחה לטיפול ברמה גבוהה יותר).
-
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. סגירת השידור
- הלקוח או השרת יכולים לסגור את הזרם. בדרך כלל, השרת מסמן את סוף השיחה באמצעות הודעת
EndSession. הלקוח צריך לסגור את הזרם כשהוא מקבל את ההודעה הזו.
טיפול בסוגים ספציפיים של הודעות
בקטעים הבאים מוסבר איך לטפל בסוגים ספציפיים של תשובות שהלקוח יקבל כשהוא יתקשר אל BidiProcessOrder.
AudioInput
- שידור האודיו בחלקים כשהוא זמין.
- פורמט: PCM ליניארי של 16 ביט, תדירות דגימה של 16,000 הרץ.
- קטעי אודיו לא כוללים את כותרות האודיו שבדרך כלל מופיעות לפני קובץ WAV.
- בתרחישים של הזמנה ואיסוף מהרכב עם ביטול הד (
enable_echo_cancellationב-Config), צריך לספק אתcustomer_audioוגם אתcrew_audio.
UpdatedOrderState
- ההודעה הזו מספקת את הסטטוס המלא של ההזמנה בכל פעם שהיא נשלחת.
החלפת המטמון המקומי של ההזמנה בתוכן של ההודעה שהתקבלה
Order - משתמשים ב-
custom_integration_attributesבתוך הפריטיםOrderובאמצעי השינוי כדי למפות את התוכןOrderלישויות מקבילות במערכת התיעוד של האפליקציה.
InterruptionSignal
- עם קבלת ההודעה, צריך להפסיק מיד את ההפעלה של כל
AgentAudioולנקות את כל האודיו של הסוכן שנמצא בזיכרון המטמון. כך השיחה תזרום בצורה טבעית אם המשתמש יקטע את הדיבור של הנציג.
EndSession
- בודקים את
EndType(לדוגמה,DRIVE_OFF, AGENT_ESCALATION). - האפליקציה צריכה לסגור את החיבור בצורה תקינה ולהעביר את המשתמש למצב המתאים (למשל, להודיע למפקח אנושי במקרה של
AGENT_ESCALATION, או לעבור למצב של אישור הזמנה).
שיטות מומלצות
- טיפול בהודעות באופן אסינכרוני: כדי לצמצם את זמן האחזור, כדאי להשתמש בשרשורים או בקלט/פלט לא חוסם כדי לשלוח בקשות ולעבד תגובות נכנסות במקביל.
- לוגיקה של חיבור מחדש: צריך להטמיע לוגיקה חזקה של חיבור מחדש במקרה של בעיות ברשת, ולא לשכוח לשלוח את ההודעה הראשונית
Configעם אותו מזהה סשן כדי לנסות לחדש את החיבור. - טיפול בשגיאות: מעקב אחרי הזרם כדי לזהות שגיאות. ספריות gRPC ו-WebSocket מספקות מנגנונים לזיהוי סגירת הזרם או שגיאות בהעברה. כדאי לרשום את האירועים האלה ביומן ולטפל בהם בצורה חלקה.
- אגירת נתונים של אודיו: צריך לנהל את מאגרי הנתונים של האודיו בקפידה, ולהטמיע אגירת נתונים אם יש צורך בכך, כדי להבטיח הפעלה חלקה של
AgentAudioואספקה בזמן שלAudioInput. כשמחליטים על תוכנית החציצה, חשוב לשקול את היחס בין זמן האחזור לבין איכות וידאו. - ניהול מזהי סשנים: צריך לוודא שמזהי הסשנים ייחודיים לכל הזמנה או שיחה.
- ניהול משאבים: סגירת הזרמים ושחרור המשאבים בסיום הסשן או אם מתרחשות שגיאות שלא ניתן לתקן.
- זמן קצוב לתפוגה: למרות שהסטרימינג עצמו יכול להיות ארוך (עד 15 דקות כברירת מחדל), כדאי לשקול הגדרת זמן קצוב לתפוגה ברמת האפליקציה למצבים ספציפיים, אם יש צורך בכך.
דוגמה לזרימת שילוב (קונספטואלית)
- אפליקציית לקוח (למשל, אפליקציה לנייד) יוזמת הזמנה.
- יצירת חיבור gRPC/WebSocket אל
BidiProcessOrder. - שליחת
BidiProcessOrderRequestעםConfig(מזהה סשן, מזהה חנות). - לקבל את ההודעה הראשונית
AgentAudio(למשל, הודעת פתיחה) ולהפעיל אותה. - המשתמש מדבר: המערכת מצלמת אודיו ומשדרת אותו בהודעות
AudioInput. - מקבלים
SpeechRecognition(הצגת תמליל),AgentAudio(הפעלת התגובה) ואוליUpdatedOrderState(עדכון עגלת הקניות בממשק המשתמש). - אם המשתמש מפריע, מקבלים
InterruptionSignalומפסיקים את ההפעלה. - להמשיך את חילופי האודיו או הטקסט עם הסוכן.
- המשתמש מאשר את ההזמנה: הסוכן שולח את
UpdatedOrderStateהסופי. - הסוכן שולח
EndSession: הלקוח סוגר את הזרם ומסיים את ההזמנה במערכת ה-POS באמצעות נתונים מהשלב האחרוןUpdatedOrderState.
דוגמה מקצה לקצה
ההוראות שלמעלה מפרטות את המושגים שקשורים לסטרימינג של נתונים, אבל כאן אפשר לראות את התהליך המלא של שילוב נתונים.
Node.js
לפני שמנסים את הדוגמה הזו, צריך לפעול לפי Node.jsהוראות ההגדרה במאמר הפעלה מהירה של סוכן AI להזמנה ממסעדה באמצעות ספריות לקוח.
כדי לבצע אימות לסוכן ה-AI להזמנה ממסעדה, צריך להגדיר את Application Default Credentials. מידע נוסף זמין במאמר הגדרת אימות לסביבת פיתוח מקומית.
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.'}});
}