Ver en ai.google.dev | Ejecutar en Google Colab | Ver código fuente en GitHub |
Usar llamadas a funciones para definir funciones personalizadas y pasarlas a Gemini El modelo no invoca directamente estas funciones, sino que genera una salida de datos estructurados que especifica el nombre de la función y los argumentos sugeridos. Este resultado habilita la llamada a APIs externas y el resultado de la API resultante puede incorporarse de nuevo al modelo, lo que permite respuestas a consultas más integrales. La llamada de funciones permite a los LLM interactuar con información en tiempo real y diversos servicios, como bases de datos, sistemas de administración de relaciones con clientes y repositorios de documentos, lo que mejora su capacidad para brindar respuestas relevantes y contextuales. Puedes proporcionar descripciones de funciones a los modelos de Gemini. El modelo puede pedirte que llames a una función y envíes el resultado para ayudar al modelo a manejar tu consulta.
Si aún no lo has hecho, consulta la Introducción a las llamadas a funciones para obtener más información.
Configuración
Instala el SDK de Python
El SDK de Python para la API de Gemini se encuentra en el paquete google-generativeai
. Instala la dependencia con pip:
pip install -U -q google-generativeai
Importa paquetes
Importa los paquetes necesarios.
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))
Cómo configurar tu clave de API
Para poder usar la API de Gemini, primero debes obtener una clave de API. Si aún no tienes una, crea una con un clic en Google AI Studio.
En Colab, agrega la clave al administrador de Secrets en la "automated" del panel izquierdo. Asígnale el nombre API_KEY
.
Una vez que tengas la clave de API, pásala al SDK. Puedes hacerlo de dos maneras:
- Coloca la clave en la variable de entorno
GOOGLE_API_KEY
(el SDK la recogerá automáticamente desde allí). - Pasa la llave a
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)
Conceptos básicos de la llamada a funciones
Para usar la llamada de funciones, pasa una lista de funciones al parámetro tools
cuando crees un GenerativeModel
. El modelo usa el nombre de la función, la docstring, los parámetros y las anotaciones de tipo de parámetro para decidir si necesita que la función responda mejor a una instrucción.
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>, )
Se recomienda usar llamadas a funciones a través de la interfaz de chat. Esto se debe a que las llamadas a funciones se ajustan naturalmente a los chats de varios turnos, ya que capturan la interacción de ida y vuelta entre el usuario y el modelo. El ChatSession
del SDK de Python es una gran interfaz para chats porque controla el historial de conversaciones por ti, y el uso del parámetro enable_automatic_function_calling
simplifica aún más las llamadas a funciones:
chat = model.start_chat(enable_automatic_function_calling=True)
Con la llamada automática a funciones habilitada, chat.send_message
llama automáticamente a tu función si el modelo lo solicita.
Parece que solo muestra una respuesta de texto que contiene la respuesta correcta:
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
Examina el historial de chat para ver el flujo de la conversación y cómo se integran las llamadas a funciones en ella.
La propiedad ChatSession.history
almacena un registro cronológico de la conversación entre el usuario y el modelo Gemini. Cada turno de la conversación se representa con un objeto glm.Content
, que contiene la siguiente información:
- Rol: Identifica si el contenido se originó del “usuario” o el “modelo”.
- Partes: Una lista de objetos
glm.Part
que representan componentes individuales del mensaje Con un modelo de solo texto, estas partes pueden ser las siguientes:- Texto: Mensajes de texto sin formato.
- Llamada a función (
glm.FunctionCall
): Es una solicitud del modelo para ejecutar una función específica con los argumentos proporcionados. - Respuesta de función (
glm.FunctionResponse
): Es el resultado que muestra el usuario después de ejecutar la función solicitada.
En el ejemplo anterior con el cálculo de guantes, el historial muestra la siguiente secuencia:
- Usuario: Hace la pregunta sobre la cantidad total de guantes.
- Model: Determina que la función de multiplicación es útil y envía una solicitud de FunctionCall al usuario.
- Usuario:
ChatSession
ejecuta automáticamente la función (debido a que se estableceenable_automatic_function_calling
) y devuelve un objetoFunctionResponse
con el resultado calculado. - Modelo: Usa el resultado de la función para formular la respuesta final y la presenta como una respuesta de texto.
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 general, el diagrama de estados es el siguiente:
El modelo puede responder con múltiples llamadas a función antes de mostrar una respuesta de texto, y las llamadas a funciones vienen antes de la respuesta de texto.
Aunque todo esto se controló automáticamente, si necesitas más control, puedes hacer lo siguiente:
- Deja el
enable_automatic_function_calling=False
predeterminado y procesa las respuestas de laglm.FunctionCall
por tu cuenta. - También puedes usar
GenerativeModel.generate_content
, donde también debes administrar el historial de chat.
Llamada de funciones paralelas
Además de la llamada a funciones básica descrita anteriormente, también puedes llamar a varias funciones en un solo turno. En esta sección, se muestra un ejemplo de cómo puedes usar la llamada de funciones paralelas.
Definir las herramientas
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
Ahora llama al modelo con una instrucción que pueda usar todas las herramientas especificadas.
# 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)
Cada uno de los resultados impresos refleja una única llamada a función que el modelo solicitó. Para devolver los resultados, incluye las respuestas en el mismo orden en que se solicitaron.
# 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! 🕺💃
Acceso de bajo nivel (opcional)
La extracción automática del esquema de las funciones de Python no funciona en todos los casos. Por ejemplo: no se manejan casos en los que describes los campos de un objeto de diccionario anidado, pero la API sí lo admite. La API puede describir cualquiera de los siguientes tipos:
AllowedType = (int | float | bool | str | list['AllowedType'] | dict[str, AllowedType]
La biblioteca cliente de google.ai.generativelanguage
proporciona acceso a los tipos de nivel inferior con el control total.
import google.ai.generativelanguage as glm
Primero, revisa el atributo _tools
del modelo. Puedes ver cómo describe las funciones que le pasaste al 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" } }]
Esto muestra la lista de objetos glm.Tool
que se enviarían a la API. Si el formato impreso no te resulta conocido, es porque estas son clases de protobuf de Google. Cada glm.Tool
(1 en este caso) contiene una lista de glm.FunctionDeclarations
, que describen una función y sus argumentos.
A continuación, se muestra una declaración para la misma función de multiplicación escrita con las clases glm
.
Ten en cuenta que estas clases solo describen la función para la API, no incluyen una implementación de ella. Por lo tanto, el uso de esto no funciona con las llamadas automáticas a funciones, pero las funciones no siempre necesitan una implementación.
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 forma equivalente, puedes describirlo como un objeto compatible con 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 cualquier manera, pasas una representación de un glm.Tool
o una lista de herramientas a
model = genai.GenerativeModel('gemini-pro', tools=calculator)
chat = model.start_chat()
response = chat.send_message(
f"What's 234551 X 325552 ?",
)
Al igual que antes, el modelo muestra un glm.FunctionCall
invocando la función multiply
de la 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 ]
Ejecuta la función tú mismo:
fc = response.candidates[0].content.parts[0].function_call
assert fc.name == 'multiply'
result = fc.args['a'] * fc.args['b']
result
76358547152.0
Envía el resultado al modelo para continuar la conversación:
response = chat.send_message(
glm.Content(
parts=[glm.Part(
function_response = glm.FunctionResponse(
name='multiply',
response={'result': result}))]))
Resumen
El SDK admite llamadas a funciones básicas. Recuerda que es más fácil administrarlo con el modo chat debido a la estructura natural de ida y vuelta. Estás a cargo de llamar a las funciones y enviar los resultados al modelo para que pueda producir una respuesta de texto.