教程:使用 Gemini API 进行函数调用


在 ai.google.dev 上查看 在 Google Colab 中运行 查看 GitHub 上的源代码

使用函数调用定义自定义函数,并将其传递给 Gemini。模型不会直接调用这些函数,而是生成指定函数名称和建议的参数的结构化数据输出。此输出支持调用外部 API,然后生成的 API 输出可以重新合并到模型中,从而实现更全面的查询响应。函数调用使 LLM 能够与实时信息和各种服务(如数据库、客户关系管理系统和文档存储库)进行交互,从而增强其提供相关且符合情境的回答的能力。您可以为 Gemini 模型提供函数说明。模型可能会要求您调用一个函数并发回相应结果,以帮助模型处理您的查询。

请参阅函数调用简介了解详情(如果您尚未这样做)。

初始设置

安装 Python SDK

适用于 Gemini API 的 Python SDK 包含在 google-generativeai 软件包中。使用 pip 安装依赖项:

pip install -U -q google-generativeai

导入软件包

导入必要的软件包。

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 密钥

您必须先获取 API 密钥,然后才能使用 Gemini API。如果您还没有密钥,请在 Google AI Studio 中一键创建。

获取 API 密钥

在 Colab 中,将密钥添加到 Secret 管理器中左侧面板中的“🔑?”下。将其命名为 API_KEY

获得 API 密钥后,将其传递给 SDK。可以通过以下两种方法实现此目的:

  • 将密钥放在 GOOGLE_API_KEY 环境变量中(SDK 会自动从该变量中获取密钥)。
  • 将密钥传递给 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)

函数调用基础知识

如需使用函数调用,请在创建 GenerativeModel 时将函数列表传递给 tools 形参。模型使用函数名称、文档字符串、参数和参数类型注解来确定它是否需要该函数来最好地回答提示。

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

建议通过聊天界面使用函数调用。这是因为函数调用自然适合多轮聊天,因为它们捕获了用户和模型之间的来回互动。Python SDK 的 ChatSession 是一个很棒的聊天接口,因为它可以为您处理对话记录,并且使用 enable_automatic_function_calling 参数可以进一步简化函数调用:

chat = model.start_chat(enable_automatic_function_calling=True)

启用自动函数调用后,如果模型要求,chat.send_message 会自动调用您的函数。

它似乎只会返回包含正确答案的文本响应:

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

检查聊天记录,了解对话流程以及函数调用在其中的集成方式。

ChatSession.history 属性会按时间顺序存储用户与 Gemini 模型之间的对话记录。对话中的每一回合都由一个 glm.Content 对象表示,该对象包含以下信息:

  • 角色:标识内容是来自“用户”还是“模型”。
  • Parts:一系列 glm.Part 对象,表示消息的各个组件。使用纯文本模型时,这些部分可以是:
    • 文字:纯文本消息。
    • 函数调用 (glm.FunctionCall):模型请求使用提供的参数执行特定函数。
    • 函数响应 (glm.FunctionResponse):用户执行请求的函数后返回的结果。

在上一个计算手套的示例中,历史记录显示以下顺序:

  1. 用户:询问关于连指手套总数的问题。
  2. 模型:确定乘法函数有用并向用户发送 FunctionCall 请求。
  3. 用户ChatSession 会自动执行该函数(由于设置了 enable_automatic_function_calling),并发回包含计算结果的 FunctionResponse
  4. 模型:使用函数的输出得出最终答案,并将其呈现为文本回复。
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.'}
--------------------------------------------------------------------------------

通常,状态图如下:

模型始终可以使用文本或 FunctionCall 进行回复。如果模型发送了 FunctionCall,用户必须使用 FunctionResponse 进行回复

模型可以在返回文本响应之前使用多个函数调用进行响应,并且函数调用先于文本响应。

虽然所有操作都是自动处理的,但如果您需要更多控制权,可以执行以下操作:

  • 保留默认的 enable_automatic_function_calling=False,并自行处理 glm.FunctionCall 响应。
  • 或者使用 GenerativeModel.generate_content,您还需要在其中管理聊天记录。

并行函数调用

除了上述基本函数调用之外,您还可以在单轮中调用多个函数。本部分举例说明了如何使用并行函数调用。

定义工具。

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

现在,使用一条可以使用所有指定工具的指令来调用模型。

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

输出的每条结果反映了模型请求的单个函数调用。要发回结果,请按照请求顺序加入响应。

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

(可选)低级别访问权限

从 Python 函数中自动提取架构并不适用于所有情况。例如:它不会处理您描述嵌套字典对象的字段的情况,但该 API 支持这一点。此 API 能够描述以下任意类型:

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

google.ai.generativelanguage 客户端库提供对低级别类型的访问权限,让您可以完全控制。

import google.ai.generativelanguage as glm

先了解一下模型的 _tools 属性,您可以看到它如何描述您传递给模型的函数:

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

这将返回将发送到 API 的 glm.Tool 对象的列表。如果您对输出的格式不熟悉,则是因为这些是 Google protobuf 类。每个 glm.Tool(在本例中为 1)都包含一个 glm.FunctionDeclarations 列表,该列表描述了函数及其实参。

以下是使用 glm 类编写的同一乘法函数的声明。

请注意,这些类只描述 API 的函数,不包含函数的实现。因此,这不适用于自动函数调用,但函数并不总是需要实现。

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

同样,您可以将其描述为一个与 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"
  }
}

无论采用哪种方式,您都需要将 glm.Tool 的表示法或工具列表传递给

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

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

与之前一样,模型会返回调用计算器的 multiply 函数的 glm.FunctionCall

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
]

自行执行该函数:

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

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

将结果发送给模型,以继续对话:

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

摘要

SDK 支持基本的函数调用。请注意,使用聊天模式更易于管理,因为它可以自然地来回切换。您负责实际调用函数并将结果发送回模型,以便模型生成文本响应。