Anleitung: Funktionsaufruf mit der Gemini API


Auf ai.google.dev ansehen In Google Colab ausführen Quelle auf GitHub ansehen

Verwenden Sie Funktionsaufrufe, um benutzerdefinierte Funktionen zu definieren und an Gemini zu übergeben. Das Modell ruft diese Funktionen nicht direkt auf, sondern generiert eine strukturierte Datenausgabe, die den Funktionsnamen und vorgeschlagene Argumente angibt. Diese Ausgabe ermöglicht das Aufrufen externer APIs. Die resultierende API-Ausgabe kann dann wieder in das Modell integriert werden, was umfassendere Abfrageantworten ermöglicht. Durch Funktionsaufrufe können LLMs mit Echtzeitinformationen und verschiedenen Diensten wie Datenbanken, Customer-Relationship-Management-Systemen und Dokumenten-Repositories interagieren und so relevante und kontextabhängige Antworten bereitstellen. Sie können Gemini-Modelle mit Funktionsbeschreibungen bereitstellen. Möglicherweise fordert das Modell Sie auf, eine Funktion aufzurufen und das Ergebnis zurückzusenden, um das Modell bei der Verarbeitung Ihrer Abfrage zu unterstützen.

Weitere Informationen finden Sie unter Einführung in Funktionsaufrufe.

Einrichtung

Python SDK installieren

Das Python SDK für die Gemini API ist im Paket google-generativeai enthalten. Installieren Sie die Abhängigkeit mit „Pip“:

pip install -U -q google-generativeai

Pakete importieren

Importieren Sie die erforderlichen Pakete.

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-Schlüssel einrichten

Bevor Sie die Gemini API verwenden können, müssen Sie zuerst einen API-Schlüssel abrufen. Falls Sie noch keinen Schlüssel haben, können Sie mit einem Klick in Google AI Studio einen Schlüssel erstellen.

API-Schlüssel anfordern

Fügen Sie den Schlüssel in Colab im linken Bereich unter „🚀“ zum Secret-Manager hinzu. Geben Sie ihr den Namen API_KEY.

Sobald Sie den API-Schlüssel haben, übergeben Sie ihn an das SDK. Dafür haben Sie die beiden folgenden Möglichkeiten:

  • Fügen Sie den Schlüssel in die Umgebungsvariable GOOGLE_API_KEY ein. Das SDK übernimmt ihn dort automatisch.
  • Schlüssel an genai.configure(api_key=...) übergeben
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)

Grundlagen des Funktionsaufrufs

Wenn Sie Funktionsaufrufe verwenden möchten, übergeben Sie beim Erstellen einer GenerativeModel eine Liste von Funktionen an den Parameter tools. Das Modell verwendet den Funktionsnamen, den docstring, die Parameter und die Annotationen des Parametertyps, um zu entscheiden, ob die Funktion am besten auf eine Aufforderung antworten muss.

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

Es wird empfohlen, Funktionsaufrufe über die Chat-Oberfläche zu verwenden. Das liegt daran, dass Funktionsaufrufe automatisch in Multi-Turn-Chats passen, da sie die Hin- und Her-Interaktion zwischen Nutzer und Modell erfassen. Das Python SDK ChatSession ist eine hervorragende Schnittstelle für Chats, da es den Unterhaltungsverlauf für Sie verwaltet und die Verwendung des Parameters enable_automatic_function_calling den Funktionsaufruf noch weiter vereinfacht:

chat = model.start_chat(enable_automatic_function_calling=True)

Wenn automatische Funktionsaufrufe aktiviert sind, ruft chat.send_message die Funktion automatisch auf, wenn das Modell dies anfordert.

Es scheint einfach eine Textantwort zurückzugeben, die die richtige Antwort enthält:

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

Sehen Sie sich das Chat-Protokoll an, um den Ablauf der Unterhaltung und die Integration von Funktionsaufrufen zu sehen.

In der Property ChatSession.history wird ein chronologischer Datensatz der Unterhaltung zwischen dem Nutzer und dem Gemini-Modell gespeichert. Jede Runde in der Unterhaltung wird durch ein glm.Content-Objekt dargestellt, das die folgenden Informationen enthält:

  • Rolle: Gibt an, ob der Inhalt vom „Nutzer“ oder dem „Modell“ stammt.
  • Parts (Teile): Eine Liste von glm.Part-Objekten, die einzelne Komponenten der Nachricht darstellen. Bei einem reinen Textmodell können diese Teile so aussehen:
    • Text: Nur Textnachrichten.
    • Funktionsaufruf (glm.FunctionCall): Eine Anfrage vom Modell zur Ausführung einer bestimmten Funktion mit angegebenen Argumenten.
    • Funktionsantwort (glm.FunctionResponse): Das Ergebnis, das vom Nutzer nach Ausführung der angeforderten Funktion zurückgegeben wird.

Im vorherigen Beispiel mit der Berechnung der Fäustlinge zeigt der Verlauf die folgende Reihenfolge:

  1. Nutzer: Stellt die Frage zur Gesamtzahl der Fausthandschuhe.
  2. Modell: Bestimmt, dass die Multiplikationsfunktion hilfreich ist, und sendet eine „FunctionCall“-Anfrage an den Nutzer.
  3. Nutzer: ChatSession führt die Funktion automatisch aus, weil enable_automatic_function_calling festgelegt wurde, und sendet eine FunctionResponse mit dem berechneten Ergebnis zurück.
  4. Modell: Verwendet die Ausgabe der Funktion, um die endgültige Antwort zu formulieren und als Textantwort zu präsentieren.
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.'}
--------------------------------------------------------------------------------

Im Allgemeinen sieht das Zustandsdiagramm so aus:

Das Modell kann immer mit Text oder einem Funktionsaufruf antworten. Wenn das Modell einen FunctionsCall sendet, muss der Nutzer mit einer FunctionResponse antworten

Das Modell kann mit mehreren Funktionsaufrufen antworten, bevor eine Textantwort zurückgegeben wird, und Funktionsaufrufe vor der Textantwort.

Dies wurde zwar automatisch abgewickelt, aber wenn Sie mehr Kontrolle benötigen, haben Sie folgende Möglichkeiten:

  • Behalten Sie den standardmäßigen enable_automatic_function_calling=False bei und verarbeiten Sie die glm.FunctionCall-Antworten selbst.
  • Alternativ können Sie GenerativeModel.generate_content verwenden. Dort müssen Sie auch das Chatprotokoll verwalten.

Parallele Funktionsaufrufe

Neben den oben beschriebenen grundlegenden Funktionsaufrufen können Sie auch mehrere Funktionen in einer einzigen Abfolge aufrufen. Dieser Abschnitt enthält ein Beispiel dafür, wie Sie parallele Funktionsaufrufe verwenden können.

Tools definieren

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

Rufen Sie nun das Modell mit einer Anweisung auf, die alle angegebenen Tools verwenden könnte.

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

Jedes der gedruckten Ergebnisse steht für einen einzelnen Funktionsaufruf, den das Modell angefordert hat. Wenn Sie die Ergebnisse zurücksenden möchten, geben Sie die Antworten in der Reihenfolge an, in der sie angefordert wurden.

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

Optional: Low-Level-Zugriff

Die automatische Extraktion des Schemas aus Python-Funktionen funktioniert nicht in allen Fällen. Beispiel: Sie ist nicht für Fälle geeignet, in denen Sie die Felder eines verschachtelten Wörterbuchobjekts beschreiben, aber die API unterstützt dies. Die API kann folgende Typen beschreiben:

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

Die google.ai.generativelanguage-Clientbibliothek bietet Zugriff auf die untergeordneten Typen, sodass Sie vollständige Kontrolle haben.

import google.ai.generativelanguage as glm

Werfen Sie zuerst einen Blick auf das Attribut _tools des Modells. Hier sehen Sie, wie es die Funktion(en) beschreibt, die Sie an das Modell übergeben haben:

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

Dadurch wird die Liste der glm.Tool-Objekte zurückgegeben, die an die API gesendet werden würden. Wenn das gedruckte Format nicht vertraut ist, liegt das daran, dass dies die protobuf-Klassen von Google sind. Jedes glm.Tool (in diesem Fall 1) enthält eine Liste von glm.FunctionDeclarations, die eine Funktion und ihre Argumente beschreiben.

Hier ist eine Deklaration für dieselbe Multiplikationsfunktion, die mithilfe der glm-Klassen geschrieben wurde.

Beachten Sie, dass diese Klassen nur die Funktion für das API beschreiben, sie keine Implementierung davon enthalten. Die Verwendung dieser Methode funktioniert also nicht mit automatischen Funktionsaufrufen, Funktionen erfordern jedoch nicht immer eine Implementierung.

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

Entsprechend können Sie dies auch als JSON-kompatibles Objekt beschreiben:

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

In beiden Fällen übergeben Sie eine Darstellung von glm.Tool oder eine Liste von Tools an

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

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

Wie zuvor, gibt das Modell einen glm.FunctionCall zurück, der die multiply-Funktion des Rechners aufruft:

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
]

Führen Sie die Funktion selbst aus:

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

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

Senden Sie das Ergebnis an das Modell, um die Unterhaltung fortzusetzen:

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

Zusammenfassung

Im SDK werden grundlegende Funktionsaufrufe unterstützt. Denken Sie daran, dass der Chat-Modus aufgrund der natürlichen Hin- und Her-Struktur einfacher zu verwalten ist. Sie sind dafür verantwortlich, die Funktionen aufzurufen und Ergebnisse zurück an das Modell zu senden, damit es eine Textantwort erzeugen kann.