Gemini API: קריאת פונקציות באמצעות Python

לצפייה ב-ai.google.dev הפעלה ב-Google Colab הצגת המקור ב-GitHub

אפשר לספק מודלים של Gemini עם תיאורים של פונקציות. יכול להיות שהמודל יבקש מכם לקרוא לפונקציה ולשלוח בחזרה את התוצאה כדי לעזור למודל לטפל בשאילתה.

הגדרה

התקנת Python SDK.

ה-Python SDK ל-Gemini API נכלל בחבילה 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

כדי להשתמש ב-Gemini API, עליך לקבל מפתח 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)

מידע בסיסי על פונקציות

אפשר להעביר רשימת פונקציות לארגומנט tools כשיוצרים 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>,
)

הדרך המומלצת לשימוש בקריאות פונקציה היא באמצעות ממשק הצ'אט. הסיבה העיקרית היא ש-FunctionCalls מתאים מאוד למבנה מרובה הפניות של הצ'אט.

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, תוכלו לראות את רצף האירועים:

  1. שלחת את השאלה.
  2. המודל השיב עם glm.FunctionCall.
  3. genai.ChatSession הפעיל את הפונקציה באופן מקומי ושלח את המודל בחזרה glm.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, שם צריך לנהל גם את היסטוריית הצ'אט.

[אופציונלי] גישה ברמה נמוכה

החילוץ האוטומטי של הסכימה מפונקציות 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"
   }
 }]

הפעולה הזו מחזירה רשימה של glm.Tool אובייקטים שיישלחו ל-API. אם הפורמט המודפס לא מוכר, הסיבה לכך היא שמדובר במחלקות של 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 ?",
)

למשל, לפני שהמודל מחזיר glm.FunctionCall, עם הפונקציה multiply של המחשבון:

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 יש תמיכה בהפעלת פונקציות בסיסיות. חשוב לזכור שקל יותר לנהל אותו במצב צ'אט כי המבנה שלו הוא טבעי וטבעי. אתם אחראים על הקריאה של הפונקציות בפועל ועל שליחת התוצאות חזרה למודל כדי שהוא יוכל להניב תשובה טקסט.