Учебное пособие. Вызов функций с помощью Gemini API


Посмотреть на ai.google.dev Запустить в Google Colab Посмотреть исходный код на GitHub

Используйте вызов функций, чтобы определить пользовательские функции и передать их в Gemini. Модель не вызывает эти функции напрямую, а вместо этого генерирует структурированные выходные данные, в которых указывается имя функции и предлагаемые аргументы. Эти выходные данные позволяют вызывать внешние API, а полученные выходные данные API можно затем включить обратно в модель, что позволяет получать более полные ответы на запросы. Вызов функций позволяет LLM взаимодействовать с информацией в режиме реального времени и различными услугами, такими как базы данных, системы управления взаимоотношениями с клиентами и хранилища документов, повышая их способность предоставлять актуальные и контекстуальные ответы. Вы можете предоставить модели Gemini описания функций. Модель может попросить вас вызвать функцию и отправить результат обратно, чтобы помочь модели обработать ваш запрос.

Если вы еще этого не сделали, ознакомьтесь с разделом «Введение в вызов функций», чтобы узнать больше.

Настраивать

Установите SDK Python

Python SDK для Gemini API содержится в пакете google-generativeai . Установите зависимость с помощью pip:

pip install -U -q google-generativeai

Импортировать пакеты

Импортируйте необходимые пакеты.

import pathlib
import textwrap
import time

import google.generativeai as genai

from IPython import display
from IPython.display import Markdown

def to_markdown(text):
  text = text.replace('•', '  *')
  return Markdown(textwrap.indent(text, '> ', predicate=lambda _: True))

Настройте свой ключ API

Прежде чем вы сможете использовать API Gemini, вам необходимо сначала получить ключ API. Если у вас его еще нет, создайте ключ одним щелчком мыши в Google AI Studio.

Получить ключ API

В Colab добавьте ключ к менеджеру секретов под знаком «🔑» на левой панели. Дайте ему имя API_KEY .

Получив ключ API, передайте его в SDK. Вы можете сделать это двумя способами:

  • Поместите ключ в переменную среды GOOGLE_API_KEY (SDK автоматически подберет его оттуда).
  • Передайте ключ в genai.configure(api_key=...)
try:
    # Used to securely store your API key
    from google.colab import userdata

    # Or use `os.getenv('API_KEY')` to fetch an environment variable.
    GOOGLE_API_KEY=userdata.get('GOOGLE_API_KEY')
except ImportError:
    import os
    GOOGLE_API_KEY = os.environ['GOOGLE_API_KEY']

genai.configure(api_key=GOOGLE_API_KEY)

Основы вызова функций

Чтобы использовать вызов функций, передайте список функций в параметр tools при создании GenerativeModel . Модель использует имя функции, строку документации, параметры и аннотации типов параметров, чтобы решить, нужна ли ей функция для наилучшего ответа на запрос.

def multiply(a:float, b:float):
    """returns a * b."""
    return a*b

model = genai.GenerativeModel(model_name='gemini-1.0-pro',
                              tools=[multiply])

model
genai.GenerativeModel(
    model_name='models/gemini-1.0-pro',
    generation_config={},
    safety_settings={},
    tools=<google.generativeai.types.content_types.FunctionLibrary object at 0x10e73fe90>,
)

Рекомендуется использовать вызовы функций через интерфейс чата. Это связано с тем, что вызовы функций естественным образом вписываются в многоходовые чаты, поскольку они фиксируют двустороннее взаимодействие между пользователем и моделью. ChatSession из Python SDK — отличный интерфейс для чатов, поскольку он обрабатывает историю разговоров, а использование параметра enable_automatic_function_calling еще больше упрощает вызов функций:

chat = model.start_chat(enable_automatic_function_calling=True)

Если включен автоматический вызов функции, chat.send_message автоматически вызывает вашу функцию, если модель об этом попросит.

Похоже, он просто возвращает текстовый ответ, содержащий правильный ответ:

response = chat.send_message('I have 57 cats, each owns 44 mittens, how many mittens is that in total?')
response.text
'The total number of mittens is 2508.'
l10n
57*44
2508

Изучите историю чата, чтобы увидеть ход разговора и то, как в него интегрированы вызовы функций.

Свойство ChatSession.history хранит хронологическую запись разговора между пользователем и моделью Gemini. Каждый ход разговора представлен объектом glm.Content , который содержит следующую информацию:

  • Роль : определяет, исходит ли контент от «пользователя» или «модели».
  • Части : список объектов glm.Part , представляющих отдельные компоненты сообщения. В текстовой модели эти части могут быть следующими:
    • Текст : обычные текстовые сообщения.
    • Вызов функции ( glm.FunctionCall ): запрос модели на выполнение определенной функции с предоставленными аргументами.
    • Ответ функции ( glm.FunctionResponse ): результат, возвращаемый пользователем после выполнения запрошенной функции.

В предыдущем примере с расчетом варежек история показывает следующую последовательность:

  1. Пользователь : Задает вопрос об общем количестве варежек.
  2. Модель : определяет, что функция умножения полезна, и отправляет пользователю запрос FunctionCall.
  3. Пользователь : ChatSession автоматически выполняет функцию (из-за установленного enable_automatic_function_calling ) и отправляет обратно FunctionResponse с вычисленным результатом.
  4. Модель : использует выходные данные функции для формулирования окончательного ответа и представляет его в виде текстового ответа.
for content in chat.history:
    part = content.parts[0]
    print(content.role, "->", type(part).to_dict(part))
    print('-'*80)
user -> {'text': 'I have 57 cats, each owns 44 mittens, how many mittens is that in total?'}
--------------------------------------------------------------------------------
model -> {'function_call': {'name': 'multiply', 'args': {'a': 57.0, 'b': 44.0} } }
--------------------------------------------------------------------------------
user -> {'function_response': {'name': 'multiply', 'response': {'result': 2508.0} } }
--------------------------------------------------------------------------------
model -> {'text': 'The total number of mittens is 2508.'}
--------------------------------------------------------------------------------

В целом диаграмма состояний выглядит следующим образом:

Модель всегда может ответить текстом или вызовом FunctionCall. Если модель отправляет FunctionCall, пользователь должен ответить FunctionResponse.

Модель может отвечать несколькими вызовами функций перед возвратом текстового ответа, а вызовы функций выполняются до текстового ответа.

Хотя все это обрабатывается автоматически, если вам нужен больший контроль, вы можете:

  • Оставьте значение по умолчанию enable_automatic_function_calling=False и обрабатывайте ответы glm.FunctionCall самостоятельно.
  • Или используйте GenerativeModel.generate_content , где вам также нужно управлять историей чата.

Параллельный вызов функций

В дополнение к базовому вызову функций, описанному выше, вы также можете вызывать несколько функций за один ход. В этом разделе показан пример использования параллельного вызова функций.

Определите инструменты.

def power_disco_ball(power: bool) -> bool:
    """Powers the spinning disco ball."""
    print(f"Disco ball is {'spinning!' if power else 'stopped.'}")
    return True


def start_music(energetic: bool, loud: bool, bpm: int) -> str:
    """Play some music matching the specified parameters.

    Args:
      energetic: Whether the music is energetic or not.
      loud: Whether the music is loud or not.
      bpm: The beats per minute of the music.

    Returns: The name of the song being played.
    """
    print(f"Starting music! {energetic=} {loud=}, {bpm=}")
    return "Never gonna give you up."


def dim_lights(brightness: float) -> bool:
    """Dim the lights.

    Args:
      brightness: The brightness of the lights, 0.0 is off, 1.0 is full.
    """
    print(f"Lights are now set to {brightness:.0%}")
    return True

Теперь вызовите модель с инструкцией, которая могла бы использовать все указанные инструменты.

# Set the model up with tools.
house_fns = [power_disco_ball, start_music, dim_lights]

model = genai.GenerativeModel(model_name="gemini-1.5-pro-latest", tools=house_fns)

# Call the API.
chat = model.start_chat()
response = chat.send_message("Turn this place into a party!")

# Print out each of the function calls requested from this single call.
for part in response.parts:
    if fn := part.function_call:
        args = ", ".join(f"{key}={val}" for key, val in fn.args.items())
        print(f"{fn.name}({args})")
power_disco_ball(power=True)
start_music(energetic=True, loud=True, bpm=120.0)
dim_lights(brightness=0.3)

Каждый из напечатанных результатов отражает один вызов функции, запрошенный моделью. Чтобы отправить результаты обратно, включите ответы в том же порядке, в котором они были запрошены.

# Simulate the responses from the specified tools.
responses = {
    "power_disco_ball": True,
    "start_music": "Never gonna give you up.",
    "dim_lights": True,
}

# Build the response parts.
response_parts = [
    glm.Part(function_response=glm.FunctionResponse(name=fn, response={"result": val}))
    for fn, val in responses.items()
]

response = chat.send_message(response_parts)
print(response.text)
Let's get this party started! I've turned on the disco ball, started playing some upbeat music, and dimmed the lights. 🎶✨  Get ready to dance! 🕺💃

(Необязательно) Низкоуровневый доступ

Автоматическое извлечение схемы из функций Python работает не во всех случаях. Например: он не обрабатывает случаи, когда вы описываете поля вложенного объекта-словаря, но API это поддерживает. API способен описывать любой из следующих типов:

AllowedType = (int | float | bool | str | list['AllowedType'] | dict[str, AllowedType]

Клиентская библиотека google.ai.generativelanguage предоставляет доступ к типам низкого уровня, предоставляя вам полный контроль.

import google.ai.generativelanguage as glm

Сначала загляните в атрибут _tools модели, и вы увидите, как он описывает функции, которые вы передали модели:

def multiply(a:float, b:float):
    """returns a * b."""
    return a*b

model = genai.GenerativeModel(model_name='gemini-1.0-pro',
                             tools=[multiply])

model._tools.to_proto()
[function_declarations {
   name: "multiply"
   description: "returns a * b."
   parameters {
     type_: OBJECT
     properties {
       key: "b"
       value {
         type_: NUMBER
       }
     }
     properties {
       key: "a"
       value {
         type_: NUMBER
       }
     }
     required: "a"
     required: "b"
   }
 }]

Это возвращает список объектов glm.Tool , которые будут отправлены в API. Если печатный формат вам не знаком, то это потому, что это классы Google protobuf. Каждый glm.Tool (в данном случае 1) содержит список glm.FunctionDeclarations , которые описывают функцию и ее аргументы.

Вот объявление той же функции умножения, написанное с использованием классов glm .

Обратите внимание, что эти классы просто описывают функцию API, но не включают ее реализацию. Таким образом, использование этого не работает с автоматическим вызовом функций, но функции не всегда нуждаются в реализации.

calculator = glm.Tool(
    function_declarations=[
      glm.FunctionDeclaration(
        name='multiply',
        description="Returns the product of two numbers.",
        parameters=glm.Schema(
            type=glm.Type.OBJECT,
            properties={
                'a':glm.Schema(type=glm.Type.NUMBER),
                'b':glm.Schema(type=glm.Type.NUMBER)
            },
            required=['a','b']
        )
      )
    ])

Аналогично, вы можете описать это как JSON-совместимый объект:

calculator = {'function_declarations': [
      {'name': 'multiply',
       'description': 'Returns the product of two numbers.',
       'parameters': {'type_': 'OBJECT',
       'properties': {
         'a': {'type_': 'NUMBER'},
         'b': {'type_': 'NUMBER'} },
       'required': ['a', 'b']} }]}
glm.Tool(calculator)
function_declarations {
  name: "multiply"
  description: "Returns the product of two numbers."
  parameters {
    type_: OBJECT
    properties {
      key: "b"
      value {
        type_: NUMBER
      }
    }
    properties {
      key: "a"
      value {
        type_: NUMBER
      }
    }
    required: "a"
    required: "b"
  }
}

В любом случае вы передаете представление glm.Tool или список инструментов

model = genai.GenerativeModel('gemini-pro', tools=calculator)
chat = model.start_chat()

response = chat.send_message(
    f"What's 234551 X 325552 ?",
)

Как и раньше, модель возвращает glm.FunctionCall , вызывающую функцию multiply калькулятора:

response.candidates
[index: 0
content {
  parts {
    function_call {
      name: "multiply"
      args {
        fields {
          key: "b"
          value {
            number_value: 325552
          }
        }
        fields {
          key: "a"
          value {
            number_value: 234551
          }
        }
      }
    }
  }
  role: "model"
}
finish_reason: STOP
]

Выполните функцию самостоятельно:

fc = response.candidates[0].content.parts[0].function_call
assert fc.name == 'multiply'

result = fc.args['a'] * fc.args['b']
result
76358547152.0

Отправьте результат модели, чтобы продолжить разговор:

response = chat.send_message(
    glm.Content(
    parts=[glm.Part(
        function_response = glm.FunctionResponse(
          name='multiply',
          response={'result': result}))]))

Краткое содержание

Вызов базовых функций поддерживается в SDK. Помните, что управляться в режиме чата проще из-за естественной структуры обмена сообщениями. Вы отвечаете за вызов функций и отправку результатов обратно в модель, чтобы она могла выдать текстовый ответ.