Samouczek: wywoływanie funkcji za pomocą interfejsu Gemini API


Zobacz na ai.google.dev Uruchom w Google Colab Wyświetl źródło na GitHubie

Za pomocą wywołań funkcji możesz definiować funkcje niestandardowe i przekazywać je do Gemini. Model nie wywołuje bezpośrednio tych funkcji, ale generuje dane wyjściowe uporządkowanych danych, które określają nazwę funkcji i sugerowane argumenty. Dane wyjściowe umożliwiają wywoływanie zewnętrznych interfejsów API, a wynikowe dane z interfejsu API można później włączyć z powrotem do modelu, co pozwoli uzyskać bardziej kompleksowe odpowiedzi na zapytania. Wywoływanie funkcji umożliwia modelom LLM interakcję z informacjami w czasie rzeczywistym i różnymi usługami, takimi jak bazy danych, systemy zarządzania relacjami z klientami i repozytoria dokumentów, co zwiększa ich zdolność do udzielania trafnych i kontekstowych odpowiedzi. Modelom Gemini możesz podawać opisy funkcji. Model może poprosić Cię o wywołanie funkcji i odesłanie wyniku, aby model mógł lepiej obsłużyć zapytanie.

Więcej informacji znajdziesz w wprowadzeniu do wywoływania funkcji.

Konfiguracja

Zainstaluj pakiet SDK Pythona

Pakiet SDK Pythona dla interfejsu Gemini API znajduje się w pakiecie google-generativeai. Zainstaluj zależność za pomocą narzędzia pip:

pip install -U -q google-generativeai

Importowanie pakietów

Zaimportuj niezbędne pakiety.

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

Konfigurowanie klucza interfejsu API

Aby korzystać z interfejsu Gemini API, musisz najpierw uzyskać klucz interfejsu API. Jeśli nie masz jeszcze klucza, utwórz go jednym kliknięciem w Google AI Studio.

Uzyskiwanie klucza interfejsu API

W Colab dodaj klucz do menedżera obiektów tajnych w sekcji „🔑” w panelu po lewej stronie. Nadaj mu nazwę API_KEY.

Przekaż klucz interfejsu API do pakietu SDK. Można to zrobić na dwa sposoby:

  • Umieść klucz w zmiennej środowiskowej GOOGLE_API_KEY (pakiet SDK automatycznie go stamtąd zabierze).
  • Przekaż klucz do: 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)

Podstawy wywoływania funkcji

Aby używać wywoływania funkcji, podczas tworzenia elementu GenerativeModel przekaż listę funkcji do parametru tools. Model wykorzystuje nazwę funkcji, ciąg dokumentu, parametry i adnotacje typu parametru do określenia, czy potrzebuje funkcji do najlepszej odpowiedzi na pytanie.

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>,
)

Zaleca się korzystanie z wywołań funkcyjnych przez interfejs czatu. Dzieje się tak, ponieważ wywołania funkcji w naturalny sposób pasują do czatów wieloetapowych, ponieważ rejestrują one interakcje między użytkownikiem a modelem. Interfejs ChatSession pakietu Python SDK jest świetnym rozwiązaniem na potrzeby czatów, ponieważ obsługuje on historię rozmowy za Ciebie, a używanie parametru enable_automatic_function_calling jeszcze bardziej upraszcza wywoływanie funkcji:

chat = model.start_chat(enable_automatic_function_calling=True)

Przy włączonym automatycznym wywoływaniu funkcji chat.send_message automatycznie wywołuje Twoją funkcję, jeśli model o nie poprosi.

Wygląda na to, że zwracany jest komunikat zawierający poprawną odpowiedź:

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.'
57*44
2508

Przejrzyj historię czatu, aby zobaczyć przebieg rozmowy i sposób integracji z nią wywołań funkcji.

Właściwość ChatSession.history przechowuje chronologiczny rekord rozmowy między użytkownikiem a modelem Gemini. Każdy ruch w wątku jest reprezentowany przez obiekt glm.Content, który zawiera te informacje:

  • Rola: wskazuje, czy treści pochodzą od „użytkownika” czy „modelu”.
  • Części: lista obiektów glm.Part, które reprezentują poszczególne komponenty wiadomości. W przypadku modelu tylko tekstowego te części mogą obejmować:
    • Tekst: zwykły tekst.
    • Wywołanie funkcji (glm.FunctionCall): żądanie z modelu o wykonanie określonej funkcji z podanymi argumentami.
    • Odpowiedź funkcji (glm.FunctionResponse): wynik zwrócony przez użytkownika po wykonaniu żądanej funkcji.

W poprzednim przykładzie z obliczaniem rękawiczek w historii występuje taka sekwencja:

  1. Użytkownik: zadaje pytanie o łączną liczbę rękawiczek.
  2. Model: określa, że funkcja mnożenia jest pomocna, i wysyła do użytkownika żądanie FunctionCall.
  3. Użytkownik: ChatSession automatycznie wykonuje funkcję (ze względu na ustawienie parametru enable_automatic_function_calling) i zwraca FunctionResponse z obliczonym wynikiem.
  4. Model: wykorzystuje dane wyjściowe funkcji do sformułowania ostatecznej odpowiedzi i przedstawia ją jako odpowiedź tekstową.
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.'}
--------------------------------------------------------------------------------

Ogólnie diagram stanu wygląda tak:

Model zawsze może odpowiedzieć przy użyciu tekstu lub funkcji FunctionCall. Jeśli model wysyła wywołanie funkcji Function, użytkownik musi odpowiedzieć za pomocą obiektu FunctionResponse

Przed zwróceniem odpowiedzi tekstowej model może odpowiedzieć za pomocą wielu wywołań funkcji, a wywołania funkcji mają miejsce przed odpowiedzią tekstową.

Wszystko odbywało się automatycznie, ale jeśli chcesz mieć większą kontrolę, możesz:

  • Pozostaw domyślną wartość enable_automatic_function_calling=False i samodzielnie przetwarzaj odpowiedzi glm.FunctionCall.
  • Możesz też użyć aplikacji GenerativeModel.generate_content, gdzie musisz również zarządzać historią czatu.

Wywoływanie funkcji równoległej

Oprócz opisanych powyżej wywołań podstawowych funkcji możesz też wywoływać wiele funkcji w jednym kroku. W tej sekcji znajdziesz przykład użycia równoległego wywoływania funkcji.

Zdefiniuj narzędzia.

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

Teraz wywołaj model z instrukcją, która może korzystać ze wszystkich określonych narzędzi.

# 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)

Każdy drukowany wynik odzwierciedla jedno wywołanie funkcji, którego zażądał model. Aby odesłać wyniki, wpisz odpowiedzi w takiej kolejności, w jakiej były wymagane.

# 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! 🕺💃

(Opcjonalnie) Dostęp niskiego poziomu

Automatyczne wyodrębnianie schematu z funkcji Pythona nie we wszystkich przypadkach działa. Na przykład: nie obsługuje przypadków, gdy opisujesz pola zagnieżdżonego obiektu słownika, ale interfejs API to umożliwia. Interfejs API może opisywać każdy z tych typów:

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

Biblioteka klienta google.ai.generativelanguage zapewnia dostęp do typów niskiego poziomu, co daje Ci pełną kontrolę.

import google.ai.generativelanguage as glm

Najpierw rzut oka na atrybut _tools modelu możesz zobaczyć, jak opisuje on funkcje przekazane do modelu:

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

Zwraca listę obiektów glm.Tool, które zostałyby wysłane do interfejsu API. Jeśli nie znasz formatu drukowanego, oznacza to, że są to klasy protokołu Google. Każdy element glm.Tool (w tym przypadku 1) zawiera listę obiektów glm.FunctionDeclarations, która opisuje funkcję i jej argumenty.

Oto deklaracja dla tej samej funkcji mnożenia zapisanej za pomocą klas glm.

Pamiętaj, że te klasy opisują tylko funkcję interfejsu API, a nie zawierają jego implementacji. Używanie tej metody nie działa w przypadku automatycznego wywoływania funkcji, ale funkcje nie zawsze wymagają implementacji.

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']
        )
      )
    ])

Możesz go również opisać jako obiekt zgodny z formatem 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"
  }
}

W obu przypadkach przekazujesz reprezentację obiektu glm.Tool lub listy narzędzi do

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

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

Podobnie jak przed zwróceniem funkcji glm.FunctionCall, która wywołuje funkcję multiply kalkulatora:

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
]

Wykonaj funkcję samodzielnie:

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

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

Aby kontynuować rozmowę, wyślij wynik do modelu:

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

Podsumowanie

Pakiet SDK obsługuje podstawowe wywoływanie funkcji. Pamiętaj, że łatwiej jest nim zarządzać w trybie czatu dzięki naturalnej strukturze przechodzenia z powrotem i z powrotem. To Ty odpowiadasz za wywoływanie funkcji i wysyłanie wyników z powrotem do modelu, aby mógł wygenerować odpowiedź tekstową.