برنامج تعليمي: استدعاء الوظائف باستخدام واجهة برمجة تطبيقات Gemini


العرض على ai.google.dev التشغيل في Google Colab الاطّلاع على المصدر على GitHub

استخدم استدعاء الدالة لتحديد الدوال المخصصة وتمريرها إلى Gemini. لا يستدعي النموذج هذه الدوال مباشرةً، ولكنه ينشئ بدلاً من ذلك إخراج بيانات منظَّمة يحدِّد اسم الدالة والوسيطات المقترَحة. ويتيح هذا الناتج استدعاء واجهات برمجة التطبيقات الخارجية، ويمكن بعد ذلك دمج ناتج واجهة برمجة التطبيقات الناتج في النموذج، ما يسمح بردود أكثر شمولاً على طلبات البحث. تتيح ميزة "استدعاء الوظائف" للنماذج اللغوية الكبيرة إمكانية التفاعل مع المعلومات في الوقت الفعلي والخدمات المتنوعة، مثل قواعد البيانات وأنظمة إدارة العلاقات مع العملاء ومستودعات المستندات، ما يعزّز قدرتها على توفير إجابات ذات صلة وسياقية. يمكنك تقديم نماذج Gemini مع أوصاف للدوال. قد يطلب منك النموذج استدعاء دالة وإعادة إرسال النتيجة لمساعدة النموذج في التعامل مع استعلامك.

إذا لم يسبق لك إجراء ذلك، يُرجى الاطّلاع على مقدمة حول استدعاء الدوال لمعرفة المزيد.

ضبط إعدادات الجهاز

تثبيت حزمة تطوير البرامج (SDK) للغة Python

تتوفّر حزمة 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))

إعداد مفتاح واجهة برمجة التطبيقات

قبل أن تتمكّن من استخدام واجهة برمجة تطبيقات Gemini، يجب أولاً الحصول على مفتاح واجهة برمجة التطبيقات. أنشِئ مفتاحًا بنقرة واحدة في "استوديو Google AI" إذا لم يكن لديك مفتاح.

الحصول على مفتاح واجهة برمجة التطبيقات

في Colab، أضِف المفتاح إلى أداة إدارة الأسرار ضمن "🔑" في اللوحة اليمنى. أدخِل الاسم "API_KEY".

بعد حصولك على مفتاح واجهة برمجة التطبيقات، أرسِله إلى حزمة تطوير البرامج (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 عند إنشاء 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>,
)

يُنصح باستخدام استدعاء الدوالّ من خلال واجهة المحادثة. ويرجع ذلك إلى أنّ طلبات الوظائف تتناسب بشكل طبيعي مع المحادثات المتعددة الأدوار لأنّها تسجّل التفاعل المتبادل بين المستخدم والنموذج. تُعد ChatSession من حزمة تطوير البرامج (SDK) للغة Python واجهة رائعة للمحادثات لأنّها تعالج سجلّ المحادثات نيابةً عنك، كما أنّ استخدام المَعلمة 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. النموذج: يحدِّد أن دالة الضرب مفيدة ويرسل طلب 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! 🕺💃

(اختياري) الوصول المنخفض المستوى

لا يعمل الاستخراج التلقائي للمخطط من دوال بايثون في جميع الحالات. على سبيل المثال، لا يتعامل مع الحالات التي تصف فيها حقول كائن قاموس متداخل، لكن واجهة برمجة التطبيقات تتيح ذلك. يمكن لواجهة برمجة التطبيقات وصف أي من الأنواع التالية:

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 التي سيتم إرسالها إلى واجهة برمجة التطبيقات. إذا كان التنسيق المطبوع غير مألوف، فذلك لأن هذه فئات النماذج الأوّلية من Google. يحتوي كل glm.Tool (1 في هذه الحالة) على قائمة من glm.FunctionDeclarations تصف دالة ووسيطاتها.

وإليك تعريف لدالة الضرب نفسها المكتوبة باستخدام فئات glm.

تجدر الإشارة إلى أن هذه الفئات تصف وظيفة واجهة برمجة التطبيقات فقط، ولا تتضمن تنفيذًا لها. لذا فإن استخدام هذه الطريقة لا يعمل مع استدعاء الدوال التلقائية، إلا أن الدوال لا تحتاج دائمًا إلى تنفيذ.

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. تذكر أن الإدارة باستخدام وضع الدردشة أسهل من أي وقت مضى، وذلك بفضل البنية المتبادلة بين الحين والآخر. أنت مسؤول عن استدعاء الدوالّ وإعادة إرسال النتائج إلى النموذج كي يتمكّن من إنشاء استجابة نصية.