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

1. מבוא

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

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

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

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

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

מה תפַתחו

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

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

3. דרישות

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

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

  1. ב-מסוף Google Cloud, בדף לבחירת הפרויקט, בוחרים או יוצרים פרויקט ב-Google Cloud.
  2. מוודאים שהחיוב מופעל בפרויקט בענן שלכם ב-Google 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. מפעילים את Cloud APIs הנדרשים של Google Cloud באמצעות הפקודות הבאות ב-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, לוחצים על Extensions (תוספים) ואז מתקינים את התוסף Gemini + 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 ממאגר GitHub של הפרויקט הזה. מעתיקים את הקובץ pom.xml משם לקובץ pom.xml של הפרויקט הנוכחי שאתם עורכים.
  2. מעתיקים את הכיתה מהקישור HelloWorld.java class from the GeminiFunctionCalling github. צריך לעדכן את API_KEY ואת project_id במפתח ה-API של Geocoding ובמזהה הפרויקט ב-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"
       }
     }
   }
 }
}

מעבירים את הפרמטר הבא אל Reverse Geocoding API: "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 מכילה את התגובה מ-Reverse 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 פועלות בתרחיש השימוש של סטנדרטיזציה של כתובות, אתם יכולים להמשיך ולפרוס את Cloud Function.

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

  1. אם כבר יצרתם את הפרויקט GeminiFunctionCalling והטמעתם את Cloud Function, אתם יכולים להמשיך לשלב 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 Function. כדי לעשות זאת, עוברים אל Cloud Functions, מסמנים את הפונקציה שרוצים למחוק מתוך רשימת הפונקציות ולוחצים על DELETE (מחיקה).

9. מזל טוב

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