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

1. Introduzione

I modelli di AI generativa sono straordinari per la comprensione e la risposta al linguaggio naturale. Ma cosa succede 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 portare a incoerenze. È qui che entra in gioco la funzionalità di chiamata di funzione di Gemini, che ti consente di controllare in modo deterministico gli elementi della risposta dell'AI.

Questo codelab illustra questo concetto con il caso d'uso del completamento e della standardizzazione degli indirizzi. A questo scopo, creeremo una funzione Cloud Functions in 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.

Diamo un'occhiata più da vicino.

2. Chiamata di funzione di Gemini

La chiamata di funzione di Gemini si distingue nell'era dell'AI 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 chiaramente le funzioni. 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 restituito dalla funzione, ad esempio un elenco di stringhe di indirizzi.
  1. Crea strumenti per Gemini: impacchetta le descrizioni delle funzioni sotto forma di specifica API negli strumenti. Pensa a uno strumento come a una cassetta degli attrezzi specializzata 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 funge quindi da orchestratore intelligente eseguendo 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 reinserendoli nella sua generazione e incorpora informazioni strutturate nella risposta finale. Puoi elaborare queste informazioni nel modo che preferisci per la tua applicazione.

L'immagine seguente mostra il flusso di dati, i passaggi coinvolti nell'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 in Java che esegue le seguenti operazioni:

  • Accetta 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 di 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 la fatturazione abilitata.

4. Prima di iniziare

  1. Nella console Google Cloud, nella pagina di selezione del progetto, seleziona o crea un progetto Google Cloud.
  2. Verifica che la fatturazione sia attivata per il tuo progetto Google Cloud. Scopri come verificare se la fatturazione è abilitata per un progetto.
  3. Attiva Cloud Shell dalla console Google Cloud. Per ulteriori informazioni, consulta Utilizzare Cloud Shell.
  4. Se il progetto non è impostato, utilizza il seguente comando per impostarlo:
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 Cloud necessarie eseguendo i seguenti 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 poi installa l'estensione Gemini + Google Cloud Code.

5. Implementa la 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. Specifica un nome per il progetto nel percorso del progetto, ad esempio GeminiFunctionCalling.
  6. Fai clic su Explorer per visualizzare la struttura del progetto e poi apri il file pom.xml. L'immagine seguente mostra la struttura del progetto:

bdf07515f413dd9e.png

  1. Aggiungi le dipendenze necessarie all'interno del <dependencies>... </dependencies> tag nel pom.xml file. 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 GitHub GeminiFunctionCalling. Devi aggiornare API_KEY e project_id rispettivamente con la chiave API Geocoding e l'ID progetto Google Cloud.

6. Comprendi la chiamata di funzione utilizzando la classe HelloWorld.java

Input del prompt

In questo esempio, l'input del prompt è il seguente: Qual è l'indirizzo per il valore latlng 40.714224,-73.961452.

Di seguito è riportato lo snippet di codice corrispondente all'input del prompt nel file:

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

Specifiche dell'API

In questo esempio viene utilizzata l'API Geocoding inversa. Di seguito sono riportate le specifiche 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 le specifiche 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 è il JSON dei parametri orchestrati per l'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".

Richiama 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;..."

Elabora la risposta dell'API e prepara il prompt

Il seguente codice elabora la risposta dell'API e prepara il prompt con le 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 contiene 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 della standardizzazione degli indirizzi, puoi procedere ed eseguire 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 di Cloud Shell e clona questo repository: git clone https://github.com/AbiramiSukumaran/GeminiFunctionCalling
  2. Vai alla cartella del progetto: cd GeminiFunctionCalling
  3. Esegui la seguente 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 dell'URL dopo il deployment: https://us-central1-YOUR_PROJECT_ID.cloudfunctions.net/gemini-fn-calling

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

Di seguito è riportata una risposta per 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. Libera spazio

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. Per farlo, vai a Cloud Functions, seleziona la funzione che vuoi eliminare dall'elenco 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 AI generativa in un processo deterministico e affidabile. Per saperne di più sui modelli disponibili, consulta la documentazione del prodotto LLM di Vertex AI .