教學課程:使用 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 中,請在左側面板的「🔑?」下方,將密鑰新增至密鑰管理工具。輸入名稱 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 參數。模型會使用函式名稱、docstring、參數和參數類型註解,判斷是否需要函式以最佳方式回答提示。

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 物件表示,其中包含下列資訊:

  • 角色:指出內容是否來自「使用者」或「模型」。
  • 零件:代表訊息個別元件的 glm.Part 物件清單。在純文字模型中,這些部分可以是:
    • 文字:純文字訊息。
    • 函式呼叫 (glm.FunctionCall):模型以提供的引數執行特定函式的要求。
    • 函式回應 (glm.FunctionResponse):使用者執行要求的函式後傳回的結果。

在之前使用斜張計算的範例中,記錄會顯示下列順序:

  1. 使用者:詢問有關十進機總數的問題。
  2. Model:判斷倍數函式實用,並傳送 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 支援基本函式呼叫。請記得,使用即時通訊模式能更輕鬆地管理,因為這是自然的往返架構。您必須負責實際呼叫函式,並將結果傳回模型,以便產生文字回應。