Детерминированный генеративный ИИ с вызовом функции Gemini на Java

1. Введение

Генеративные модели ИИ замечательно справляются с пониманием естественного языка и реагированием на него. Но что делать, если для таких важных задач, как стандартизация адресов, необходимы точные и предсказуемые результаты? Традиционные генеративные модели иногда могут давать разные ответы в разное время на одни и те же запросы, что потенциально может привести к несоответствиям. Именно здесь проявляется преимущество функции вызова функций Gemini, позволяющей детерминированно управлять элементами ответа ИИ.

В этом практическом занятии данная концепция иллюстрируется на примере автозаполнения и стандартизации адресов. Для этого мы создадим облачную функцию на Java, которая будет выполнять следующие задачи:

  1. Принимает координаты широты и долготы.
  2. Вызывает API геокодирования Google Maps для получения соответствующих адресов.
  3. Использует функцию вызова функций Gemini 1.0 Pro для детерминированной стандартизации и суммирования адресов в необходимом нам формате.

Давайте начнём!

2. Вызов функции Gemini

Функция Gemini Function Calling выделяется в эпоху генеративного ИИ тем, что позволяет сочетать гибкость генеративных языковых моделей с точностью традиционного программирования.

Вот задачи, которые необходимо выполнить для реализации вызова функций Gemini:

  1. Определение функций : четко опишите функции. Описание должно включать следующую информацию:
  • Название функции, например, getAddress .
  • Параметры, которые ожидает функция, например, latlng в виде строки.
  • Тип данных, возвращаемых функцией, например, список строковых адресов.
  1. Создайте инструменты для Gemini : упакуйте описания функций в виде спецификаций API в инструменты. Рассматривайте инструмент как специализированный набор инструментов, который Gemini может использовать для понимания функциональности API.
  2. Управление API с помощью Gemini : когда вы отправляете запрос в Gemini, он может проанализировать ваш запрос и определить, где можно использовать предоставленные вами инструменты. Затем Gemini выступает в роли интеллектуального оркестратора, выполняя следующие задачи:
  • Генерирует необходимые параметры API для вызова определенных вами функций. Gemini не вызывает API от вашего имени. Вы должны вызывать API, используя параметры и сигнатуру, сгенерированные для вас функцией вызова Gemini.
  • Gemini обрабатывает результаты, передавая их обратно в процесс генерации, и включает структурированную информацию в свой окончательный ответ. Вы можете обрабатывать эту информацию так, как вам удобно для вашего приложения.

На следующем изображении показан поток данных, этапы реализации и ответственный за каждый этап, например, приложение, LLM или API:

b9a39f55567072d3.png

Что вы построите

Вам предстоит создать и развернуть Java-функцию Cloud Function, которая будет выполнять следующие действия:

  • Принимает координаты широты и долготы.
  • Вызывает API геокодирования Google Maps для получения соответствующих адресов.
  • Использует функцию вызова функций 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. Для активации необходимых API 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 , нажмите «Расширения» , а затем установите расширение Gemini + Google Cloud Code .

5. Внедрить облачные функции.

  1. Запустите редактор Cloud Shell .
  2. Нажмите на Cloud Code , а затем разверните раздел Cloud Functions .
  3. Нажмите на значок «Создать функцию» (+).
  4. В диалоговом окне «Создать новое приложение» выберите параметр «Java: Hello World» .
  5. Укажите имя проекта в пути к проекту, например, GeminiFunctionCalling .
  6. Нажмите «Проводник» , чтобы просмотреть структуру проекта, а затем откройте файл pom.xml. На следующем изображении показана структура проекта:

bdf07515f413dd9e.png

  1. Добавьте необходимые зависимости внутри тега <dependencies>... </dependencies> в файле pom.xml . Полный текст файла pom.xml можно найти в репозитории этого проекта на GitHub. Скопируйте файл pom.xml оттуда в файл pom.xml вашего текущего проекта, который вы редактируете.
  2. Скопируйте класс HelloWorld.java из репозитория GeminiFunctionCalling на GitHub . Необходимо обновить поля API_KEY и project_id , указав в них ваш ключ API для геокодирования и идентификатор проекта Google Cloud соответственно.

6. Разберитесь с вызовом функций, используя класс HelloWorld.java.

Запрос ввода

В этом примере в качестве входных данных используется следующий запрос: Какой адрес соответствует значению широты и долготы 40.714224, -73.961452 ?

Ниже приведён фрагмент кода, соответствующий подсказке ввода в файле:

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

спецификация API

В этом примере используется 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:

// 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"
       }
     }
   }
 }
}

Передайте следующий параметр в API Reverse Geocoding : "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 содержит ответ от 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 в контексте стандартизации адресов, вы можете приступить к развертыванию облачной функции.

7. Развертывание и тестирование

  1. Если вы уже создали проект GeminiFunctionCalling и реализовали облачную функцию, перейдите к шагу 2. Если вы еще не создали проект, перейдите в терминал Cloud Shell и клонируйте этот репозиторий: git clone https://github.com/AbiramiSukumaran/GeminiFunctionCalling
  2. Перейдите в папку проекта: cd GeminiFunctionCalling
  3. Выполните следующую команду для создания и развертывания облачной функции:
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. Протестируйте облачную функцию, выполнив следующую команду в терминале:
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. В диалоговом окне введите идентификатор проекта, а затем нажмите «Завершить», чтобы удалить проект.
  4. Если вы хотите сохранить свой проект, пропустите описанные выше шаги и удалите облачную функцию, перейдя в раздел «Облачные функции», выбрав в списке функций ту, которую хотите удалить, и нажав кнопку «УДАЛИТЬ».

9. Поздравляем!

Поздравляем! Вы успешно использовали функцию вызова функций Gemini в Java-приложении и преобразовали задачу генеративного ИИ в детерминированный, надежный процесс. Чтобы узнать больше о доступных моделях, см. документацию по продукту Vertex AI LLM .