IA generativa deterministica con le chiamate di funzione di Gemini in Java

1. Introduzione

I modelli di IA generativa si distinguono per comprendere e rispondere al linguaggio naturale. Ma cosa succederebbe se avessi bisogno di output precisi e prevedibili per attività critiche come la standardizzazione degli indirizzi? I modelli generativi tradizionali a volte possono fornire risposte diverse in momenti diversi per gli stessi prompt, il che potrebbe causare incoerenze. È qui che spicca la funzionalità di chiamata di funzione di Gemini, che ti consente di controllare in modo deterministico gli elementi della risposta dell'IA.

Questo codelab illustra questo concetto con il caso d'uso di completamento degli indirizzi e standardizzazione. A questo scopo, creeremo una funzione Cloud Functions Java che esegue le seguenti attività:

  1. Accetta le coordinate di latitudine e longitudine
  2. Chiama l'API Geocoding di Google Maps per ottenere gli indirizzi corrispondenti
  3. Utilizza la funzionalità di chiamata di funzione di Gemini 1.0 Pro per standardizzare e riassumere in modo deterministico questi indirizzi in un formato specifico di cui abbiamo bisogno

Iniziamo.

2. Chiamate di funzione di Gemini

La chiamata di funzione di Gemini si distingue nell'era dell'IA generativa perché ti consente di combinare la flessibilità dei modelli linguistici generativi con la precisione della programmazione tradizionale.

Ecco le attività che devi completare per implementare la chiamata di funzione di Gemini:

  1. Definisci le funzioni: descrivi le funzioni in modo chiaro. Le descrizioni devono includere le seguenti informazioni:
  • Il nome della funzione, ad esempio getAddress.
  • I parametri previsti dalla funzione, ad esempio latlng come stringa.
  • Il tipo di dati restituiti dalla funzione, ad esempio un elenco di stringhe di indirizzi.
  1. Creare strumenti per Gemini: descrizioni delle funzioni dei pacchetti sotto forma di specifica API in strumenti. Pensa a uno strumento come a uno strumento specializzato che Gemini può utilizzare per comprendere la funzionalità dell'API.
  2. Orchestra le API utilizzando Gemini: quando invii un prompt a Gemini, questo può analizzare la tua richiesta e riconoscere dove può utilizzare gli strumenti che hai fornito. Gemini agisce quindi da agente di orchestrazione intelligente svolgendo le seguenti attività:
  • Genera i parametri API necessari per chiamare le funzioni definite. Gemini non chiama l'API per tuo conto. Devi chiamare l'API in base ai parametri e alla firma che la chiamata di funzione di Gemini ha generato per te.
  • Gemini elabora i risultati fornendo i risultati delle chiamate API alla loro generazione e incorpora informazioni strutturate nella risposta finale. Puoi trattare queste informazioni come preferisci per la tua domanda.

L'immagine seguente mostra il flusso di dati, i passaggi previsti dall'implementazione e il proprietario di ogni passaggio, ad esempio applicazione, LLM o API:

b9a39f55567072d3.png

Cosa creerai

Creerai ed eseguirai il deployment di una funzione Cloud Functions Java che fa quanto segue:

  • Indica le coordinate di latitudine e longitudine.
  • Chiama l'API Geocoding di Google Maps per ottenere gli indirizzi corrispondenti.
  • Utilizza la funzionalità di chiamata di funzione Gemini 1.0 Pro per standardizzare e riassumere in modo deterministico questi indirizzi in un formato specifico.

3. Requisiti

  • Un browser, ad esempio Chrome o Firefox.
  • Un progetto Google Cloud con fatturazione abilitata.

4. Prima di iniziare

  1. Nella pagina del selettore dei progetti della console Google Cloud, seleziona o crea un progetto Google Cloud.
  2. Assicurati che la fatturazione sia abilitata per il tuo progetto Google Cloud. Scopri come controllare se la fatturazione è abilitata per un progetto.
  3. Attiva Cloud Shell dalla console Google Cloud. Per ulteriori informazioni, vedi Utilizzare Cloud Shell.
  4. Se il progetto non è configurato, utilizza il comando seguente per configurarlo:
gcloud config set project <YOUR_PROJECT_ID>
  1. In Cloud Shell, imposta le seguenti variabili di ambiente:
export GCP_PROJECT=<YOUR_PROJECT_ID>
export GCP_REGION=us-central1
  1. Abilita le API Google Cloud necessarie eseguendo questi comandi in 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. Apri l'editor di Cloud Shell, fai clic su Estensioni e installa l'estensione Gemini + Google Cloud Code.

5. Implementazione della funzione Cloud Functions

  1. Avvia l'editor di Cloud Shell.
  2. Fai clic su Cloud Code ed espandi la sezione Cloud Functions.
  3. Fai clic sull'icona Crea funzione (+).
  4. Nella finestra di dialogo Crea nuova applicazione, seleziona l'opzione Java: Hello World.
  5. Fornisci un nome per il progetto nel relativo percorso, ad esempio GeminiFunctionCalling.
  6. Fai clic su Explorer per visualizzare la struttura del progetto, quindi apri il file pom.xml. La seguente immagine mostra la struttura del progetto:

bdf07515f413dd9e.png

  1. Aggiungi le dipendenze necessarie all'interno del tag <dependencies>... </dependencies> nel file pom.xml. Puoi accedere all'intero pom.xml dal repository GitHub di questo progetto. Copia il file pom.xml da lì nel file pom.xml del progetto corrente che stai modificando.
  2. Copia la classe HelloWorld.java dal link GeminiFunctionCalling GitHub. Devi aggiornare API_KEY e project_id rispettivamente con la tua chiave API di geocodifica e con l'ID progetto Google Cloud.

6. Comprendere le chiamate di funzione utilizzando la classe HelloWorld.java

Input del prompt

In questo esempio, il prompt di input è il seguente: Qual è l'indirizzo per il valore latitudine/longitudine 40.714224,-73.961452.

Di seguito è riportato lo snippet di codice corrispondente al prompt di input nel file:

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

Specifica API

In questo esempio viene utilizzata l'API Reverse Geocoding. Di seguito è riportata la specifica dell'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();

Orchestra il prompt con Gemini

L'input del prompt e la specifica dell'API vengono inviati a 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();

La risposta da questo è il file JSON dei parametri orchestrati nell'API. Di seguito è riportato un output di esempio:

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

Passa il seguente parametro all'API Reverse Geocoding: "latlng=40.714224,-73.961452"

Abbina il risultato orchestrato al formato "latlng=VALUE".

Richiamare l'API

Di seguito è riportata la sezione del codice che richiama l'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();

La stringa jsonResult contiene la risposta dell'API Geocoding inversa. Di seguito è riportata una versione formattata dell'output:

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

Elaborare la risposta dell'API e preparare il prompt

Il seguente codice elabora la risposta dall'API e prepara il prompt con istruzioni su come elaborare la risposta:

// 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()
   );

Richiama Gemini e restituisci l'indirizzo standardizzato

Il seguente codice passa l'output elaborato del passaggio precedente come prompt a 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);

La variabile finalAnswer ha l'indirizzo standardizzato in formato JSON. Di seguito è riportato un output di esempio:

{"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}"]}

Ora che hai capito come funziona la chiamata di funzione di Gemini con il caso d'uso di standardizzazione degli indirizzi, puoi procedere con il deployment della funzione Cloud Functions.

7. Deployment e test

  1. Se hai già creato il progetto GeminiFunctionCalling e implementato la funzione Cloud Functions, vai al passaggio 2. Se non hai creato il progetto, vai al terminale Cloud Shell e clona questo repository: git clone https://github.com/AbiramiSukumaran/GeminiFunctionCalling
  2. Vai alla cartella del progetto: cd GeminiFunctionCalling
  3. Esegui questa istruzione per creare ed eseguire il deployment della funzione Cloud Functions:
gcloud functions deploy gemini-fn-calling --gen2 --region=us-central1 --runtime=java11 --source=. --entry-point=cloudcode.helloworld.HelloWorld --trigger-http

Di seguito è riportato il formato URL dopo il deployment: https://us-central1-YOUR_PROJECT_ID.cloudfunctions.net/gemini-fn-calling

  1. Testa la funzione Cloud Functions eseguendo questo comando dal terminale:
gcloud functions call gemini-fn-calling --region=us-central1 --gen2 --data '{"calls":[["40.714224,-73.961452"]]}'

Di seguito è riportata una risposta a un prompt di esempio casuale: '{"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. Esegui la pulizia

Per evitare che al tuo account Google Cloud vengano addebitati costi relativi alle risorse utilizzate in questo post, segui questi passaggi:

  1. Nella console Google Cloud, vai alla pagina Gestisci risorse.
  2. Nell'elenco dei progetti, seleziona il progetto che vuoi eliminare, quindi fai clic su Elimina.
  3. Nella finestra di dialogo, digita l'ID progetto, quindi fai clic su Chiudi per eliminare il progetto.
  4. Se vuoi conservare il progetto, salta i passaggi precedenti ed elimina la funzione Cloud Functions accedendo a Cloud Functions e, nell'elenco delle funzioni, seleziona la funzione che vuoi eliminare e fai clic su ELIMINA.

9. Complimenti

Complimenti Hai utilizzato con successo la funzionalità di chiamata di funzione di Gemini in un'applicazione Java e hai trasformato un'attività di IA generativa in un processo deterministico e affidabile. Per saperne di più sui modelli disponibili, consulta la documentazione del prodotto LLM Vertex AI.