API Gemini: appeler une fonction avec Python

Voir sur ai.google.dev Exécuter dans Google Colab Afficher la source sur GitHub

Vous pouvez fournir aux modèles Gemini des descriptions de fonctions. Le modèle peut vous demander d'appeler une fonction et de renvoyer le résultat pour l'aider à gérer votre requête.

Préparation

Installer le SDK Python

Le SDK Python pour l'API Gemini est contenu dans le package google-generativeai. Installez la dépendance à l'aide de pip:

pip install -U -q google-generativeai

Importer des packages

Importez les packages nécessaires.

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

Configurer votre clé API

Pour pouvoir utiliser l'API Gemini, vous devez d'abord obtenir une clé API. Si vous ne possédez pas encore de clé, créez-en une en un clic dans Google AI Studio.

Obtenir une clé API

Dans Colab, ajoutez la clé au gestionnaire de secrets sous le bouton "EIDR" du panneau de gauche. Donnez-lui le nom API_KEY.

Une fois la clé API obtenue, transmettez-la au SDK. Pour cela, vous avez le choix entre deux méthodes :

  • Placez la clé dans la variable d'environnement GOOGLE_API_KEY (le SDK la récupérera automatiquement à partir de là).
  • Transmettre la clé à 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)

Principes de base des fonctions

Vous pouvez transmettre une liste de fonctions à l'argument tools lorsque vous créez un genai.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>,
)

Il est recommandé d'utiliser les appels de fonction via l'interface de chat. La raison principale est que FunctionCalls s'intègre parfaitement à la structure multitours du chat.

chat = model.start_chat(enable_automatic_function_calling=True)

Lorsque l'appel de fonction automatique est activé, chat.send_message appelle automatiquement votre fonction si le modèle le demande.

Il semble simplement renvoyer une réponse textuelle contenant la bonne réponse:

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

Si vous regardez dans la ChatSession.history, vous pouvez voir la séquence d'événements:

  1. Vous avez envoyé la question.
  2. Le modèle a renvoyé une réponse glm.FunctionCall.
  3. genai.ChatSession a exécuté la fonction localement et renvoyé au modèle une glm.FunctionResponse.
  4. Le modèle a utilisé la sortie de la fonction dans sa réponse.
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.'}
--------------------------------------------------------------------------------

En général, le diagramme d'état est le suivant:

Le modèle peut toujours répondre avec du texte ou un appel de fonction. Si le modèle envoie un appel de fonction (FunctionCall), l&#39;utilisateur doit répondre avec une réponse FunctionResponse.

Le modèle peut répondre avec plusieurs appels de fonction avant de renvoyer une réponse textuelle, et les appels de fonction avant la réponse textuelle.

Bien que tout ait été géré automatiquement, si vous avez besoin de plus de contrôle, vous pouvez:

  • Conservez la valeur par défaut enable_automatic_function_calling=False et traitez vous-même les réponses glm.FunctionCall.
  • Vous pouvez également utiliser GenerativeModel.generate_content, qui vous permet également de gérer l'historique des discussions.

[Facultatif] Accès de bas niveau

L'extraction automatique du schéma à partir des fonctions Python ne fonctionne pas dans tous les cas. Par exemple, elle ne gère pas les cas où vous décrivez les champs d'un objet de dictionnaire imbriqué, mais l'API le permet. L'API est capable de décrire les différents types suivants:

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

La bibliothèque cliente google.ai.generativelanguage donne accès aux types de niveau inférieur, ce qui vous offre un contrôle total.

import google.ai.generativelanguage as glm

Tout d'abord, dans l'attribut _tools du modèle, vous pouvez voir comment il décrit la ou les fonctions que vous lui avez transmises:

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

Cela renvoie la liste des objets glm.Tool qui seraient envoyés à l'API. Si le format imprimé ne vous est pas familier, il s'agit de classes de tampons de protocole Google. Chaque glm.Tool (1 dans ce cas) contient une liste de glm.FunctionDeclarations, qui décrivent une fonction et ses arguments.

Voici une déclaration pour la même fonction de multiplication écrite à l'aide des classes glm.

Notez que ces classes ne font que décrire la fonction de l'API, elles n'incluent pas d'implémentation. L'utilisation de cette option ne fonctionne donc pas avec l'appel automatique de fonctions, mais les fonctions n'ont pas toujours besoin d'être implémentées.

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

De manière équivalente, vous pouvez décrire cela comme un objet compatible 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"
  }
}

Dans tous les cas, vous transmettez une représentation d'un glm.Tool ou d'une liste d'outils à

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

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

Comme avant que le modèle ne renvoie un glm.FunctionCall en appelant la fonction multiply du calculateur:

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
]

Exécutez la fonction vous-même:

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

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

Envoyez le résultat au modèle pour poursuivre la conversation:

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

Résumé

Le SDK prend en charge les appels de fonctions de base. N'oubliez pas qu'il est plus facile à gérer en mode chat, en raison de la structure naturelle des échanges. Vous êtes en charge d'appeler les fonctions et de renvoyer les résultats au modèle afin qu'il puisse générer une réponse textuelle.