Deterministische generative KI mit Gemini-Funktionsaufrufen in Java

1. Einführung

Generative KI-Modelle sind bemerkenswert gut darin, natürliche Sprache zu verstehen und darauf zu reagieren. Aber was ist, wenn Sie für kritische Aufgaben wie die Adressstandardisierung präzise, vorhersagbare Ausgaben benötigen? Traditionelle generative Modelle können für dieselben Prompts zu unterschiedlichen Zeiten unterschiedliche Antworten liefern, was möglicherweise zu Inkonsistenzen führt. Hier kommt die Funktion für Funktionsaufrufe von Gemini ins Spiel, mit der Sie Elemente der KI-Antwort deterministisch steuern können.

In diesem Codelab wird dieses Konzept anhand des Anwendungsfalls der Adressvervollständigung und ‑standardisierung veranschaulicht. Dazu erstellen wir eine Java-Cloud Functions-Funktion, die die folgenden Aufgaben ausführt:

  1. Breiten- und Längenkoordinaten verwenden
  2. Die Google Maps Geocoding API aufrufen, um entsprechende Adressen zu erhalten
  3. Die Funktion für Funktionsaufrufe von Gemini 1.0 Pro verwenden, um diese Adressen deterministisch in einem bestimmten Format zu standardisieren und zusammenzufassen

Sehen wir uns das genauer an.

2. Funktionsaufrufe von Gemini

Die Funktion für Funktionsaufrufe von Gemini zeichnet sich im Zeitalter der generativen KI dadurch aus, dass Sie die Flexibilität generativer Sprachmodelle mit der Präzision der traditionellen Programmierung kombinieren können.

Folgende Aufgaben müssen Sie ausführen, um Funktionsaufrufe von Gemini zu implementieren:

  1. Funktionen definieren: Beschreiben Sie die Funktionen klar. Die Beschreibungen müssen die folgenden Informationen enthalten:
  • Der Name der Funktion, z. B. getAddress.
  • Die Parameter, die die Funktion erwartet, z. B. latlng als String.
  • Der Datentyp, den die Funktion zurückgibt, z. B. eine Liste von Adressstrings.
  1. Tools für Gemini erstellen: Packen Sie Funktionsbeschreibungen in Form einer API-Spezifikation in Tools. Ein Tool ist wie ein spezieller Werkzeugkasten, mit dem Gemini die Funktionalität der API verstehen kann.
  2. APIs mit Gemini orchestrieren: Wenn Sie einen Prompt an Gemini senden, kann das Tool Ihre Anfrage analysieren und erkennen, wo es die von Ihnen bereitgestellten Tools verwenden kann. Gemini fungiert dann als intelligenter Orchestrator und führt die folgenden Aufgaben aus:
  • Die erforderlichen API-Parameter zum Aufrufen der von Ihnen definierten Funktionen generieren. Gemini ruft die API nicht in Ihrem Namen auf. Sie müssen die API anhand der Parameter und der Signatur aufrufen, die von der Funktion für Funktionsaufrufe von Gemini für Sie generiert wurden.
  • Gemini verarbeitet die Ergebnisse, indem die Ergebnisse Ihrer API-Aufrufe wieder in die Generierung einfließen und strukturierte Informationen in die endgültige Antwort aufgenommen werden. Sie können diese Informationen nach Bedarf für Ihre Anwendung verarbeiten.

Die folgende Abbildung zeigt den Datenfluss, die Schritte bei der Implementierung und den Inhaber der einzelnen Schritte, z. B. Anwendung, LLM oder API:

b9a39f55567072d3.png

Aufgaben

Sie erstellen und stellen eine Java-Cloud Functions-Funktion bereit, die Folgendes ausführt:

  • Breiten- und Längenkoordinaten verwenden
  • Die Google Maps Geocoding API aufrufen, um entsprechende Adressen zu erhalten
  • Die Funktion für Funktionsaufrufe von Gemini 1.0 Pro verwenden, um diese Adressen deterministisch in einem bestimmten Format zu standardisieren und zusammenzufassen

3. Voraussetzungen

  • Ein Browser, z. B. Chrome oder Firefox.
  • Ein Google Cloud-Projekt mit aktivierter Abrechnungsfunktion.

4. Hinweis

  1. Wählen Sie in der Google Cloud Console auf der Seite der Projektauswahl ein Google Cloud-Projekt aus oder erstellen Sie eines.
  2. Die Abrechnung für das Google Cloud-Projekt muss aktiviert sein. So prüfen Sie, ob die Abrechnung für ein Projekt aktiviert ist
  3. Aktivieren Sie Cloud Shell über die Google Cloud Console. Weitere Informationen finden Sie unter Cloud Shell verwenden.
  4. Wenn Ihr Projekt nicht festgelegt ist, verwenden Sie den folgenden Befehl, um es festzulegen:
gcloud config set project <YOUR_PROJECT_ID>
  1. Legen Sie in Cloud Shell die folgenden Umgebungsvariablen fest:
export GCP_PROJECT=<YOUR_PROJECT_ID>
export GCP_REGION=us-central1
  1. Aktivieren Sie die erforderlichen Google Cloud APIs, indem Sie die folgenden Befehle in Cloud Shell ausführen:
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. Öffnen Sie den Cloud Shell-Editor, klicken Sie auf Erweiterungen und installieren Sie die Erweiterung „Gemini + Google Cloud Code“.

5. Cloud Functions-Funktion implementieren

  1. Starten Sie den Cloud Shell-Editor.
  2. Klicken Sie auf Cloud Code und maximieren Sie den Bereich Cloud Functions.
  3. Klicken Sie auf das Symbol Funktion erstellen (+).
  4. Wählen Sie im Dialogfeld Neue Anwendung erstellen die Option Java: Hello World aus.
  5. Geben Sie im Projektpfad einen Namen für das Projekt an, z. B. GeminiFunctionCalling.
  6. Klicken Sie auf Explorer , um die Projektstruktur aufzurufen, und öffnen Sie dann die Datei „pom.xml“. Die folgende Abbildung zeigt die Projektstruktur:

bdf07515f413dd9e.png

  1. Fügen Sie die erforderlichen Abhängigkeiten im Tag <dependencies>... </dependencies> in der Datei pom.xml hinzu. Sie können über das GitHub-Repository dieses Projekts auf die gesamte Datei pom.xml zugreifen. Kopieren Sie die Datei „pom.xml“ von dort in die Datei pom.xml Ihres aktuellen Projekts, die Sie gerade bearbeiten.
  2. Kopieren Sie die Klasse HelloWorld.java über den GitHub-Link GeminiFunctionCalling. Sie müssen API_KEY und project_id durch Ihren Geocoding API-Schlüssel bzw. Ihre Google Cloud-Projekt-ID ersetzen.

6. Funktionsaufrufe mit der Klasse „HelloWorld.java“ verstehen

Prompt-Eingabe

In diesem Beispiel lautet der Eingabe-Prompt: What's the address for the latlong value 40.714224,-73.961452.

Das folgende Code-Snippet entspricht dem Eingabe-Prompt in der Datei:

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

API-Spezifikation

In diesem Beispiel wird die Reverse Geocoding API verwendet. Die API-Spezifikation lautet:

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

Prompt mit Gemini orchestrieren

Die Prompt-Eingabe und die API-Spezifikation werden an Gemini gesendet:

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

Die Antwort darauf ist das orchestrierte Parameter-JSON für die API. Hier ein Beispiel für die Ausgabe:

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

Übergeben Sie den folgenden Parameter an die Reverse Geocoding API: "latlng=40.714224,-73.961452"

Das orchestrierte Ergebnis muss dem Format "latlng=VALUE" entsprechen.

API aufrufen

Der folgende Codeabschnitt ruft die API auf:

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

Der String jsonResult enthält die Antwort von der Reverse Geocoding API. Hier eine formatierte Version der Ausgabe:

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

API-Antwort verarbeiten und Prompt vorbereiten

Der folgende Code verarbeitet die Antwort von der API und bereitet den Prompt mit Anweisungen zur Verarbeitung der Antwort vor:

// 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 aufrufen und standardisierte Adresse zurückgeben

Der folgende Code übergibt die verarbeitete Ausgabe aus dem vorherigen Schritt als Prompt an 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);

Die Variable finalAnswer enthält die standardisierte Adresse im JSON-Format. Hier ein Beispiel für die Ausgabe:

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

Nachdem Sie nun verstanden haben, wie Funktionsaufrufe von Gemini mit dem Anwendungsfall der Adressstandardisierung funktionieren, können Sie die Cloud Functions-Funktion bereitstellen.

7. Bereitstellen und testen

  1. Wenn Sie das Projekt GeminiFunctionCalling bereits erstellt und die Cloud Functions-Funktion implementiert haben, fahren Sie mit Schritt 2 fort. Wenn Sie das Projekt noch nicht erstellt haben, rufen Sie das Cloud Shell-Terminal auf und klonen Sie dieses Repository: git clone https://github.com/AbiramiSukumaran/GeminiFunctionCalling
  2. Rufen Sie den Projektordner auf: cd GeminiFunctionCalling
  3. Führen Sie die folgende Anweisung aus, um die Cloud Functions-Funktion zu erstellen und bereitzustellen:
gcloud functions deploy gemini-fn-calling --gen2 --region=us-central1 --runtime=java11 --source=. --entry-point=cloudcode.helloworld.HelloWorld --trigger-http

Das URL-Format nach der Bereitstellung lautet: https://us-central1-YOUR_PROJECT_ID.cloudfunctions.net/gemini-fn-calling

  1. Testen Sie die Cloud Functions-Funktion, indem Sie den folgenden Befehl im Terminal ausführen:
gcloud functions call gemini-fn-calling --region=us-central1 --gen2 --data '{"calls":[["40.714224,-73.961452"]]}'

Hier eine Antwort für einen zufälligen Beispiel-Prompt: '{"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. Bereinigen

Mit den folgenden Schritten vermeiden Sie, dass Ihrem Google Cloud-Konto die in diesem Beitrag verwendeten Ressourcen in Rechnung gestellt werden:

  1. Wechseln Sie in der Google Cloud Console zur Seite Ressourcen verwalten.
  2. Wählen Sie in der Projektliste das Projekt aus, das Sie löschen möchten, und klicken Sie auf Löschen.
  3. Geben Sie im Dialogfeld die Projekt-ID ein und klicken Sie auf Shut down (Beenden), um das Projekt zu löschen.
  4. Wenn Sie Ihr Projekt behalten möchten, überspringen Sie die obigen Schritte und löschen Sie die Cloud Functions-Funktion. Rufen Sie dazu Cloud Functions auf, wählen Sie in der Liste der Funktionen die Funktion aus, die Sie löschen möchten, und klicken Sie auf LÖSCHEN.

9. Glückwunsch

Glückwunsch! Sie haben die Funktion für Funktionsaufrufe von Gemini erfolgreich in einer Java-Anwendung verwendet und eine generative KI-Aufgabe in einen deterministischen, zuverlässigen Prozess umgewandelt. Weitere Informationen zu verfügbaren Modellen finden Sie in der Vertex AI LLM-Produktdokumentation.