使用 Java 中的 Gemini 函数调用确定性生成式 AI

1. 简介

生成式 AI 模型在理解和响应自然语言方面表现出色。但是,如果您需要针对地址标准化等关键任务的精确、可预测的输出,该怎么办?传统的生成模型有时可能会在不同的时间针对相同的提示提供不同的回答,这可能会导致不一致。这正是 Gemini 的函数调用功能大显身手,它让您能够确定性地控制 AI 响应的元素。

此 Codelab 通过地址补全和标准化用例说明了此概念。为此,我们将构建一个 Java Cloud Functions 函数,用于执行以下任务:

  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. 使用 Gemini 编排 API:当您向 Gemini 发送提示时,Gemini 可以分析您的请求,并识别可以在哪些地方使用您提供的工具。然后,Gemini 会执行以下任务,充当智能编排系统:
  • 生成必要的 API 参数,以调用您定义的函数。Gemini 不会代表您调用该 API。您必须根据 Gemini 函数调用为您生成的参数和签名来调用该 API。
  • Gemini 处理结果的方式是将 API 调用的结果反馈回它的世代,并将结构化信息整合到最终响应中。您可以根据您的应用所需的方式处理此信息。

下图显示了数据流、实现中涉及的步骤以及每个步骤(例如应用、LLM 或 API)的所有者:

b9a39f55567072d3.png

构建内容

您将创建并部署一个 Java Cloud Functions 函数,用于执行以下操作:

  • 获取纬度和经度坐标。
  • 调用 Google Maps Geocoding API 以获取相应的地址。
  • 使用 Gemini 1.0 Pro 函数调用功能,以特定格式确定性地对这些地址进行标准化和汇总。

3. 要求

  • 一个浏览器,例如 ChromeFirefox
  • 启用了结算功能的 Google Cloud 项目。

4. 准备工作

  1. Google Cloud Console 的项目选择器页面上,选择或创建一个 Google Cloud 项目
  2. 确保您的 Google Cloud 项目已启用结算功能。了解如何检查项目是否已启用结算功能
  3. 通过 Google Cloud 控制台激活 Cloud Shell。如需了解详情,请参阅使用 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 Shell 中执行以下命令,启用必要的 Google Cloud API:
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. pom.xml 文件的 <dependencies>... </dependencies> 标记内添加必要的依赖项。您可以从此项目的 GitHub 代码库访问整个 pom.xml。将 pom.xml 从该位置复制到您正在修改的当前项目的 pom.xml 文件中。
  2. 复制 GeminiFunctionCalling GitHub 链接中的 HelloWorld.java 类。您必须分别使用地理编码 API 密钥和 Google Cloud 项目 ID 更新 API_KEYproject_id

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

它的响应是指向 API 的已编排参数 JSON。输出示例如下:

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 包含来自反向 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 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

以下是部署后的网址格式: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 控制台中,前往管理资源页面。
  2. 在项目列表中,选择要删除的项目,然后点击“删除”。
  3. 在对话框中输入项目 ID,然后点击“关停”以删除项目。
  4. 如果您想保留项目,请跳过上述步骤,并删除 Cloud Functions 函数。为此,请前往 Cloud Functions,在函数列表中选中要删除的函数,然后点击“删除”。

9. 恭喜

恭喜!您已成功在 Java 应用中使用 Gemini 函数调用功能,并将生成式 AI 任务转换为确定性、可靠的流程。如需详细了解可用的模型,请参阅 Vertex AI LLM 产品文档