בינה מלאכותית גנרטיבית דטרמיניסטית באמצעות קריאה לפונקציה של Gemini ב-Java

1. מבוא

מודלים של בינה מלאכותית גנרטיבית הם יוצאי דופן בהבנה של שפה טבעית ובתגובה אליה. אבל מה קורה אם צריכים פלט מדויק וצפוי למשימות קריטיות כמו סטנדרטיזציה של כתובות? מודלים גנרטיביים מסורתיים יכולים לפעמים לתת תשובות שונות בזמנים שונים לאותן הנחיות, מה שעלול להוביל לחוסר עקביות. כאן נמצאת היכולת להשתמש בפונקציות של Gemini כדי לשלוט בצורה דטרמיניסטית באלמנטים של התשובה של ה-AI.

ה-Codelab הזה מדגים את העיקרון הזה באמצעות תרחיש לדוגמה של השלמת כתובות וסטנדרטיזציה. לצורך כך, נפתח פונקציה של Java Cloud Functions שמבצעת את המשימות הבאות:

  1. מקבלת קואורדינטות של קווי אורך ורוחב
  2. קורא ל-Google Maps Geocoding API כדי לקבל את הכתובות המתאימות
  3. נעשה שימוש בתכונה 'קריאות לפונקציה של Gemini 1.0' כדי ליצור סטנדרטיזציה ולסכם את הכתובות האלה בפורמט ספציפי שדרוש לנו

קדימה, מתחילים!

2. הפעלת פונקציה של Gemini

התכונה 'קריאה לפונקציות של Gemini' בולטת בעידן ה-AI הגנרטיבי, כי היא מאפשרת לשלב את הגמישות של מודלים גנרטיביים של שפה עם הדיוק של התכנות המסורתי.

אלה המשימות שצריך לבצע כדי להטמיע קריאה לפונקציה של Gemini:

  1. הגדרת פונקציות: תיאור ברור של הפונקציות. התיאורים חייבים לכלול את המידע הבא:
  • שם הפונקציה, למשל getAddress.
  • הפרמטרים שהפונקציה מצפה להם, כמו latlng כמחרוזת.
  • סוג הנתונים שהפונקציה מחזירה, למשל רשימת מחרוזות של כתובות.
  1. יוצרים כלים ל-Gemini: מספקים לכלים תיאורים של פונקציות, בצורת מפרט API. הכלי הזה הוא כמו ארגז כלים מיוחד ש-Gemini יכול להשתמש בו כדי להבין את הפונקציונליות של ה-API.
  2. ממשקי API לתזמור באמצעות Gemini: כשאתם שולחים הנחיה ל-Gemini, הוא יכול לנתח את הבקשה שלכם ולזהות איפה הוא יכול להשתמש בכלים שסיפקתם. לאחר מכן, Gemini משמש כמתזמר חכם, על ידי ביצוע המשימות הבאות:
  • יוצרת את הפרמטרים הנדרשים של ה-API כדי לקרוא לפונקציות המוגדרות. Gemini לא קורא ל-API בשמכם. עליך לשלוח קריאה ל-API על סמך הפרמטרים והחתימה שיצרה עבורך הקריאה לפונקציה של Gemini.
  • Gemini מעבד את התוצאות. לשם כך, הוא צריך להזין את התוצאות מהקריאות ל-API כדי ליצור אותן, ומשלב מידע מובנה בתשובה הסופית. ניתן לעבד מידע זה בדרך הרצויה לך עבור הבקשה.

בתמונה הבאה אפשר לראות את זרימת הנתונים, את השלבים בהטמעה ואת הבעלים של כל שלב, כמו אפליקציה, LLM או API:

b9a39f55567072d3.png

מה תפַתחו

יוצרים ופורסים פונקציה של Java Cloud Functions שמבצעת את הפעולות הבאות:

  • מקבלת קואורדינטות של קווי אורך ורוחב.
  • קורא ל-Google Maps Geocoding API כדי לקבל את הכתובות המתאימות.
  • נעשה שימוש בתכונת הקריאה לפונקציה של Gemini 1.0 Pro כדי ליצור סטנדרטיזציה ולסכם את הכתובות האלה בפורמט ספציפי.

3. דרישות

  • דפדפן כמו Chrome או Firefox.
  • פרויקט ב-Google Cloud שמופעל בו חיוב.

4. לפני שמתחילים

  1. במסוף Google Cloud, בדף בורר הפרויקטים, בוחרים או יוצרים פרויקט ב-Google Cloud.
  2. צריך לוודא שהחיוב מופעל בפרויקט שלכם ב-Cloud. איך בודקים אם החיוב מופעל בפרויקט
  3. מפעילים את Cloud Shell ממסוף Google Cloud. מידע נוסף זמין במאמר שימוש ב-Cloud Shell.
  4. אם הפרויקט לא מוגדר, מגדירים את הפרויקט באמצעות הפקודה הבאה:
gcloud config set project <YOUR_PROJECT_ID>
  1. ב-Cloud Shell, מגדירים את משתני הסביבה הבאים:
export GCP_PROJECT=<YOUR_PROJECT_ID>
export GCP_REGION=us-central1
  1. כדי להפעיל את ממשקי Google Cloud API הנחוצים, מריצים את הפקודות הבאות ב-Cloud Shell:
gcloud services enable cloudbuild.googleapis.com cloudfunctions.googleapis.com run.googleapis.com logging.googleapis.com storage-component.googleapis.com cloudaicompanion.googleapis.com aiplatform.googleapis.com
  1. פותחים את Cloud Shell Editor, לוחצים על תוספים ומתקינים את התוסף Google Cloud Code.

5. הטמעת הפונקציה של Cloud Functions

  1. מפעילים את Cloud Shell Editor.
  2. לוחצים על Cloud Code ומרחיבים את הקטע Cloud Functions.
  3. לוחצים על הסמל יצירת פונקציה (+).
  4. בתיבת הדו-שיח Create New Application (יצירת אפליקציה חדשה), בוחרים באפשרות Java: Hello World.
  5. נותנים שם לפרויקט בנתיב הפרויקט, למשל GeminiFunctionCalling.
  6. לוחצים על Explorer כדי להציג את מבנה הפרויקט ואז פותחים את הקובץ pom.xml. בתמונה הבאה מוצג מבנה הפרויקט:

bdf07515f413dd9e.png

  1. מוסיפים את יחסי התלות הנדרשים בתוך התג <dependencies>... </dependencies> בקובץ pom.xml. אפשר לגשת לכל pom.xml מהמאגר של הפרויקט הזה. מעתיקים את הקובץ pom.xml משם לקובץ pom.xml של הפרויקט הנוכחי שאותו אתם עורכים.
  2. מעתיקים את המחלקה HelloWorld.java דרך הקישור GeminiFunctionCalling github. צריך לעדכן את API_KEY ואת project_id באמצעות מפתח ה-API לקידוד גיאוגרפי ומזהה הפרויקט ב-Google Cloud, בהתאמה.

6. הבנת הקריאה של פונקציות באמצעות המחלקה HelloWorld.Java

הקלט של ההנחיה

בדוגמה הזו, זו בקשת הקלט: What's the address for the latlong value 40.714224,-73.961452.

זה קטע הקוד שתואם להנחיית הקלט בקובץ:

String promptText = "What's the address for the latlong value '" + latlngString + "'?"; //40.714224,-73.961452

מפרט ה-API

בדוגמה הזו נעשה שימוש ב-Reverse Geocoding API. זה מפרט ה-API:

/* Declare the function for the API to invoke (Geo coding API) */ 
FunctionDeclaration functionDeclaration =
    FunctionDeclaration.newBuilder()
        .setName("getAddress")
        .setDescription("Get the address for the given latitude and longitude value.")
        .setParameters(
            Schema.newBuilder()
                .setType(Type.OBJECT)
                .putProperties(
                    "latlng",
                    Schema.newBuilder()
                        .setType(Type.STRING)
                        .setDescription("This must be a string of latitude and longitude coordinates separated by comma")
                        .build())
                .addRequired("latlng")
                .build())
        .build();

תזמור ההנחיה באמצעות Gemini

הקלט של ההנחיה והמפרט של ה-API נשלחים ל-Gemini:

// Add the function to a "tool"
Tool tool = Tool.newBuilder()
.addFunctionDeclarations(functionDeclaration)
.build();

// Invoke the Gemini model with the use of the tool to generate the API parameters from the prompt input.
GenerativeModel model = GenerativeModel.newBuilder()
.setModelName(modelName)
.setVertexAi(vertexAI)
.setTools(Arrays.asList(tool))
.build();
GenerateContentResponse response = model.generateContent(promptText);
Content responseJSONCnt = response.getCandidates(0).getContent();

התגובה הזו היא הפרמטרים המתוזמנים של JSON ל-API. לפניכם פלט לדוגמה:

role: "model"
parts {
 function_call {
   name: "getAddress"
   args {
     fields {
       key: "latlng"
       value {
         string_value: "40.714224,-73.961452"
       }
     }
   }
 }
}

מעבירים את הפרמטר הבא אל ה-API של Reverse Geocoding: "latlng=40.714224,-73.961452"

מתאימים את התוצאה המתוזמנת לפורמט "latlng=VALUE".

הפעלת ה-API

זה הקטע בקוד שמפעיל את ה-API:

// Create a request
     String url = API_STRING + "?key=" + API_KEY + params;
     java.net.http.HttpRequest request = java.net.http.HttpRequest.newBuilder()
         .uri(URI.create(url))
         .GET()
         .build();
     // Send the request and get the response
     java.net.http.HttpResponse<String> httpresponse = client.send(request, java.net.http.HttpResponse.BodyHandlers.ofString());
     // Save the response
     String jsonResult =  httpresponse.body().toString();

המחרוזת jsonResult מכילה את התשובה מ-Geocoding API ההפוך. זוהי גרסה בפורמט של הפלט:

"...277 Bedford Ave, Brooklyn, NY 11211, USA; 279 Bedford Ave, Brooklyn, NY 11211, USA; 277 Bedford Ave, Brooklyn, NY 11211, USA;..."

מעבדים את התשובה מה-API ומכינים את ההנחיה

הקוד הבא מעבד את התשובה מה-API ומכינים את ההנחיה עם הוראות לעיבוד התשובה:

// Provide an answer to the model so that it knows what the result
     // of a "function call" is.
     String promptString =
     "You are an AI address standardizer for assisting with standardizing addresses accurately. Your job is to give the accurate address in the standard format as a JSON object containing the fields DOOR_NUMBER, STREET_ADDRESS, AREA, CITY, TOWN, COUNTY, STATE, COUNTRY, ZIPCODE, LANDMARK by leveraging the address string that follows in the end. Remember the response cannot be empty or null. ";

Content content =
         ContentMaker.fromMultiModalData(
             PartMaker.fromFunctionResponse(
                 "getAddress",
                 Collections.singletonMap("address", formattedAddress)));
     String contentString = content.toString();
     String address = contentString.substring(contentString.indexOf("string_value: \"") + "string_value: \"".length(), contentString.indexOf('"', contentString.indexOf("string_value: \"") + "string_value: \"".length()));

     List<SafetySetting> safetySettings = Arrays.asList(
       SafetySetting.newBuilder()
           .setCategory(HarmCategory.HARM_CATEGORY_HATE_SPEECH)
           .setThreshold(SafetySetting.HarmBlockThreshold.BLOCK_ONLY_HIGH)
           .build(),
       SafetySetting.newBuilder()
           .setCategory(HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT)
           .setThreshold(SafetySetting.HarmBlockThreshold.BLOCK_ONLY_HIGH)
           .build()
   );

הפעלת Gemini והחזרת הכתובת הרגילה

הקוד הבא מעביר את הפלט המעובד מהשלב הקודם כהנחיה ל-Gemini:

GenerativeModel modelForFinalResponse = GenerativeModel.newBuilder()
     .setModelName(modelName)
     .setVertexAi(vertexAI)
     .build();
     GenerateContentResponse finalResponse = modelForFinalResponse.generateContent(promptString + ": " + address, safetySettings);
      System.out.println("promptString + content: " + promptString + ": " + address);
       // See what the model replies now
       System.out.println("Print response: ");
       System.out.println(finalResponse.toString());
       String finalAnswer = ResponseHandler.getText(finalResponse);
       System.out.println(finalAnswer);

המשתנה finalAnswer מכיל את הכתובת הסטנדרטית בפורמט JSON. זה פלט לדוגמה:

{"replies":["{ \"DOOR_NUMBER\": null, \"STREET_ADDRESS\": \"277 Bedford Ave\", \"AREA\": \"Brooklyn\", \"CITY\": \"New York\", \"TOWN\": null, \"COUNTY\": null, \"STATE\": \"NY\", \"COUNTRY\": \"USA\", \"ZIPCODE\": \"11211\", \"LANDMARK\": null} null}"]}

עכשיו, אחרי שהבנתם איך הפונקציה של Gemini Functions עובדת עם התרחיש לדוגמה של תקן כתובות, אתם יכולים להתחיל לפרוס את הפונקציה של Cloud Functions.

7. פריסה ובדיקה

  1. אם כבר יצרתם את הפרויקט GeminiFunctionCalling והטמעתם את הפונקציה של Cloud Functions, צריך להמשיך לשלב 2. אם עדיין לא יצרתם את הפרויקט, צריך לעבור לטרמינל של Cloud Shell ולשכפל את המאגר הזה: git clone https://github.com/AbiramiSukumaran/GeminiFunctionCalling
  2. ניווט לתיקיית הפרויקט: cd GeminiFunctionCalling
  3. מריצים את ההצהרה הבאה כדי ליצור ולפרוס את הפונקציה של Cloud Functions:
gcloud functions deploy gemini-fn-calling --gen2 --region=us-central1 --runtime=java11 --source=. --entry-point=cloudcode.helloworld.HelloWorld --trigger-http

זהו הפורמט של כתובת ה-URL אחרי הפריסה: https://us-central1-YOUR_PROJECT_ID.cloudfunctions.net/gemini-fn-calling

  1. כדי לבדוק את הפונקציה של Cloud Functions, מריצים את הפקודה הבאה מהטרמינל:
gcloud functions call gemini-fn-calling --region=us-central1 --gen2 --data '{"calls":[["40.714224,-73.961452"]]}'

זו תשובה להנחיה לדוגמה אקראית: '{"replies":["{ "DOOR_NUMBER": "277", "STREET_ADDRESS": "Bedford Ave", "AREA": null, "CITY": "Brooklyn", "TOWN": null, "COUNTY": "Kings County", "STATE": "NY", "COUNTRY": "USA", "ZIPCODE": "11211", "LANDMARK": null}}```"]}'

8. הסרת המשאבים

כדי לא לצבור חיובים לחשבון Google Cloud עבור המשאבים שבהם השתמשתם בפוסט הזה:

  1. במסוף Google Cloud, נכנסים לדף Manage resources:
  2. ברשימת הפרויקטים, בוחרים את הפרויקט שרוצים למחוק ולוחצים על Delete.
  3. כדי למחוק את הפרויקט, כותבים את מזהה הפרויקט בתיבת הדו-שיח ולוחצים על Shut down.
  4. כדי לשמור את הפרויקט, מדלגים אל Cloud Functions ומוחקים את הפונקציה של Cloud Functions. ברשימת הפונקציות, מסמנים את הפונקציה שרוצים למחוק ולוחצים על DELETE.

9. מזל טוב

מעולה! השתמשת בהצלחה בפיצ'ר של הקריאה לפונקציה של Gemini באפליקציית Java והפכת משימה של AI גנרטיבי לתהליך דטרמיניסטי ואמין. מידע נוסף על המודלים הזמינים זמין במסמכי התיעוד של Vertex AI LLM.