API Gemini: chamada de funções com Python

Ver em ai.google.dev Executar no Google Colab Consulte o código-fonte no GitHub

É possível fornecer modelos do Gemini com descrições de funções. O modelo pode pedir que você chame uma função e envie o resultado de volta para ajudar o modelo a processar a consulta.

Configuração

Instalar o SDK do Python

O SDK do Python para a API Gemini está incluído no pacote google-generativeai. Instale a dependência usando pip:

pip install -U -q google-generativeai

Importar pacotes

Importe os pacotes necessários.

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

Configurar sua chave de API

Antes de usar a API Gemini, é necessário ter uma chave de API. Se você ainda não tiver uma, crie uma chave com um clique no Google AI Studio.

Gerar uma chave de API

No Colab, adicione a chave ao gerenciador de secrets abaixo do "", no painel à esquerda. Dê o nome API_KEY a ela.

Quando você tiver a chave de API, transmita-a ao SDK. Faça isso de duas maneiras:

  • Coloque a chave na variável de ambiente GOOGLE_API_KEY. O SDK vai selecioná-la automaticamente de lá.
  • Transmita a chave para 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)

Noções básicas de funções

É possível transmitir uma lista de funções para o argumento tools ao criar um 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>,
)

A maneira recomendada de usar a chamada de funções é pela interface de chat. O principal motivo é que o FunctionCalls se encaixa bem na estrutura de várias interações do chat.

chat = model.start_chat(enable_automatic_function_calling=True)

Com a chamada automática de funções ativada, o chat.send_message vai chamar sua função automaticamente se o modelo pedir.

Ele parece simplesmente retornar uma resposta de texto, contendo a resposta correta:

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

No ChatSession.history, você verá a sequência de eventos:

  1. Você enviou a pergunta.
  2. O modelo respondeu com um glm.FunctionCall.
  3. O genai.ChatSession executou a função localmente e enviou o modelo de volta um glm.FunctionResponse.
  4. O modelo usou a saída da função na resposta.
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.'}
--------------------------------------------------------------------------------

Em geral, o diagrama de estado é:

O modelo sempre pode responder com texto ou uma FunctionCall. Se o modelo enviar uma FunctionCall, o usuário deverá responder com um FunctionResponse.

O modelo pode responder com várias chamadas de função antes de retornar uma resposta de texto, e as chamadas de função vêm antes da resposta de texto.

Embora tudo isso seja automático, você pode fazer o seguinte para ter mais controle:

  • Deixe o valor padrão enable_automatic_function_calling=False e processe as respostas glm.FunctionCall por conta própria.
  • Ou use o GenerativeModel.generate_content, onde você também precisa gerenciar o histórico de chat.

[Opcional] Acesso de baixo nível

A extração automática do esquema de funções do Python não funciona em todos os casos. Por exemplo: ela não processa casos em que você descreve os campos de um objeto de dicionário aninhado, mas a API suporta isso. A API é capaz de descrever qualquer um dos seguintes tipos:

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

A biblioteca de cliente google.ai.generativelanguage fornece acesso aos tipos de nível inferior, proporcionando controle total.

import google.ai.generativelanguage as glm

Primeiro, confira o atributo _tools do modelo para saber como ele descreve as funções que você transmitiu ao modelo:

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

Isso retorna a lista de objetos glm.Tool que seriam enviados à API. Se o formato impresso não for familiar, é porque essas são classes protobuf do Google. Cada glm.Tool (1, neste caso) contém uma lista de glm.FunctionDeclarations, que descreve uma função e os argumentos dela.

Veja a seguir uma declaração para a mesma função de multiplicação escrita usando as classes glm.

Essas classes apenas descrevem a função da API, e não incluem uma implementação dela. Portanto, usar isso não funciona com a chamada automática de funções, mas as funções nem sempre precisam de uma implementação.

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

Da mesma forma, é possível descrever esse objeto como um objeto compatível com 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"
  }
}

De qualquer forma, você transmite uma representação de um glm.Tool ou uma lista de ferramentas para

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

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

Como antes, o modelo retorna um glm.FunctionCall invocando a função multiply da calculadora:

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
]

Execute a função:

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

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

Para continuar a conversa, envie o resultado ao modelo:

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

Resumo

A chamada de função básica é compatível com o SDK. Lembre-se de que é mais fácil gerenciar usando o modo de chat devido à estrutura natural de idas e vindas. Você é responsável por chamar as funções e enviar os resultados de volta ao modelo para que ele produza uma resposta de texto.