أهلاً بك في هذا الدرس الجديد!
في الدرس السابق، قمنا ببناء جسر مهم بين عالم البرمجة وعالم الذكاء الاصطناعي. تعلمنا كيف نصف "أداة" برمجية (دالة بايثون) باستخدام تنسيق JSON، بحيث يتمكن النموذج اللغوي الكبير (LLM) من فهم وظيفتها ومتطلباتها. لقد جهزنا "دليل المستخدم" لأداتنا count_words.
لكن يظل السؤال قائماً: كيف نستخدم هذا الدليل عملياً؟ هذا هو محور درسنا اليوم، حيث سنحقق هدفنا التعليمي التالي: تنفيذ آلية لاختيار الأداة المناسبة وتنفيذها بناءً على قرار النموذج. سنكتب كود بايثون الذي يعمل كوسيط، يأخذ قرار النموذج ويحوله إلى فعل حقيقي.
1. دورة التفاعل: محادثة من خطوتين مع النموذج
قبل كتابة الكود، من الضروري أن نفهم أن عملية "استدعاء الدوال" (Function Calling) ليست مجرد طلب واحد واستجابة واحدة. إنها محادثة قصيرة من خطوتين بين تطبيقنا والنموذج اللغوي:
-
الخطوة الأولى (قرار الاستدعاء):
- نحن نرسل: طلب المستخدم (مثلاً، "كم عدد الكلمات في هذه الجملة؟") + قائمة أوصاف الأدوات المتاحة (وصف أداة
count_wordsالتي أنشأناها). - النموذج يستجيب: لا يعطي إجابة نهائية، بل يرسل "قراراً" باستخدام أداة معينة، محدداً اسمها والمدخلات التي يجب تمريرها لها.
- نحن نرسل: طلب المستخدم (مثلاً، "كم عدد الكلمات في هذه الجملة؟") + قائمة أوصاف الأدوات المتاحة (وصف أداة
-
الخطوة الثانية (الحصول على الإجابة النهائية):
- نحن نرسل: نتيجة تنفيذ الأداة التي طلبها النموذج في الخطوة السابقة (مثلاً، الرقم 6).
- النموذج يستجيب: يستخدم النتيجة التي أرسلناها لصياغة إجابة نهائية باللغة الطبيعية (مثلاً، "يوجد 6 كلمات في الجملة.").
الرسم التوضيحي التالي يصف هذه الدورة بدقة. ركز على كيفية تبادل الرسائل بين "التطبيق" (الكود الذي سنكتبه) و"نموذج Gemini" (النموذج اللغوي).

مهمتنا اليوم هي برمجة الخطوات 2، 3، و 4 في هذا الرسم.
2. الخطوة العملية الأولى: طلب قرار النموذج
لنبدأ بكتابة كود بايثون الذي ينفذ الخطوة الأولى من المحادثة. سنستخدم مكتبة openai للتواصل مع النموذج. يفترض هذا الكود أنك قمت بالفعل بإعداد مفتاح API الخاص بك كمتغير بيئة (Environment Variable) كما تعلمنا في الوحدة الثانية.
أولاً، لنتذكر دالتنا ووصفها من الدرس السابق:
import openai
import json
# لنفترض أن مفتاح OpenAI API موجود كمتغير بيئة
# client = openai.OpenAI()
# 1. تعريف الأداة (الدالة الفعلية في بايثون)
def count_words(text):
"""تقوم هذه الدالة بحساب عدد الكلمات في نص معين."""
words = text.split()
return len(words)
# 2. وصف الأداة بصيغة JSON
tools = [
{
"type": "function",
"function": {
"name": "count_words",
"description": "تقوم بحساب عدد الكلمات في نص معين.",
"parameters": {
"type": "object",
"properties": {
"text": {
"type": "string",
"description": "النص الذي سيتم حساب عدد الكلمات فيه.",
}
},
"required": ["text"],
},
}
}
]
الآن، سنقوم بإرسال طلب المستخدم مع وصف الأداة إلى النموذج. لاحظ المعاملين الجديدين في استدعاء الـ API: tools و tool_choice.
# 3. إرسال طلب المستخدم مع الأدوات المتاحة
user_prompt = "كم عدد الكلمات في جملة 'أهلاً بك في عالم الذكاء الاصطناعي'؟"
messages = [{"role": "user", "content": user_prompt}]
# هذا هو الاستدعاء الرئيسي
# response = client.chat.completions.create(
# model="gpt-4-turbo", # أو أي موديل يدعم استدعاء الأدوات
# messages=messages,
# tools=tools,
# tool_choice="auto", # 'auto' تعني أن النموذج حر في اختيار أداة أو عدم اختيارها
# )
# response_message = response.choices[0].message
# print(response_message)
عند تشغيل هذا الكود (بعد إزالة التعليقات ووضع الكود في ملف واحد)، لن تحصل على إجابة مباشرة. بدلاً من ذلك، ستحصل على استجابة خاصة من النموذج تبدو كالتالي:
ChatCompletionMessage(
content=None,
role='assistant',
function_call=None,
tool_calls=[
ToolCall(
id='call_abc123',
function=Function(
arguments='{"text":"أهلاً بك في عالم الذكاء الاصطناعي"}',
name='count_words'
),
type='function'
)
]
)
هذه هي "الخطوة الأولى" من محادثتنا. النموذج يقول لنا: "لقد قررت استخدام الأداة count_words مع النص المحدد". الآن، حان دورنا لتنفيذ هذا القرار.
لمشاهدة شرح عملي لهذه الخطوة، وكيفية إضافة أكثر من أداة والسماح للنموذج بالاختيار بينها باستخدام function_call: auto، يمكنك مشاهدة المقطع التالي.
OpenAI Function Calling - Full Beginner Tutorial
يشرح هذا المقطع من قناة Dave Ebbelaar كيف يمكن تزويد النموذج بوصف لعدة أدوات، وكيف تسمح له قيمة 'auto' باختيار الأنسب منها بناءً على طلب المستخدم.
شاهد من الدقيقة 17:00 إلى 19:07. ركز على كيفية بناء قائمة تحتوي على أوصاف لثلاث دوال مختلفة، وكيف أن تحديد function_call إلى auto يمنح النموذج حرية الاختيار.
3. الخطوة العملية الثانية: تحليل الاستجابة وتنفيذ الأداة
الآن بعد أن تلقينا قرار النموذج، نحتاج إلى كتابة كود بايثون لتحليله وتنفيذه. هذه هي الآلية الأساسية التي سيعمل بها وكيلنا.
سنقوم بالخطوات التالية في الكود:
- التحقق مما إذا كان النموذج قد قرر استخدام أداة (
tool_calls). - استخلاص اسم الأداة (
name) والوسيطات (arguments). - الوسيطات تأتي كنص بصيغة JSON، لذا يجب تحويلها إلى قاموس (dictionary) بايثون باستخدام
json.loads(). - استخدام جملة
ifللتحقق من اسم الأداة واستدعاء الدالة الفعلية المطابقة لها في بايثون. - تمرير الوسيطات إلى الدالة باستخدام
**لتمرير القاموس كوسيطات.
لنكمل الكود السابق:
# response_message هو المتغير الذي يحتوي على استجابة النموذج من الخطوة السابقة
tool_calls = response_message.tool_calls
if tool_calls:
# 4. تحليل قرار النموذج
available_functions = {
"count_words": count_words,
}
# قد يقرر النموذج استدعاء عدة أدوات، لذا سنمر عليها في حلقة
for tool_call in tool_calls:
function_name = tool_call.function.name
function_to_call = available_functions[function_name]
# الوسيطات تأتي كنص، لذا نحولها إلى قاموس
function_args = json.loads(tool_call.function.arguments)
# 5. تنفيذ الدالة الفعلية وتخزين النتيجة
print(f"النموذج قرر استدعاء: {function_name}")
print(f"مع الوسيطات: {function_args}")
function_response = function_to_call(
text=function_args.get("text")
)
print(f"نتيجة التنفيذ: {function_response}")
# في الخطوة التالية، سنرسل هذه النتيجة مرة أخرى إلى النموذج
لفهم هذه العملية بشكل أعمق، شاهد المقاطع التالية التي توضح بالتفصيل كيفية استخلاص اسم الدالة ووسيطاتها من استجابة النموذج، ثم استدعاء دالة بايثون الفعلية.
OpenAI Function Calling - Full Beginner Tutorial
يوضح هذا الفيديو من قناة Dave Ebbelaar بالتفصيل كيفية تحليل استجابة النموذج في بايثون، تحويل الوسيطات من نص إلى قاموس، ومن ثم استدعاء الدالة الحقيقية.
شاهد أولاً من الدقيقة 09:14 إلى 10:40 لترى شكل استجابة النموذج التي تحتوي على استدعاء الدالة. ثم شاهد من الدقيقة 12:05 إلى 15:03 لترى الكود الذي يحلل هذه الاستجابة، ويستخدم json.loads، ويستدعي الدالة في النهاية. ركز على كيفية الانتقال من نص غير منظم (طلب المستخدم) إلى قاموس بايثون منظم.
How does function calling with tools really work?
هذا الفيديو من قناة Matt Williams يعزز نفس المفهوم، موضحاً أن هذه الآلية تعمل مع نماذج مختلفة. يركز على منطق 'التبديل' (switch statement)، والذي سنطبقه باستخدام if/elif في بايثون لاختيار الدالة الصحيحة.
شاهد من الدقيقة 06:21 إلى 07:53. لاحظ كيف يستخدم دالة execute_function مع جملة switch (ما يعادل if/elif/else في بايثون) لتوجيه الطلب إلى الدالة الصحيحة بناءً على الاسم الذي يقرره النموذج.
الصورة التالية تلخص ما يقوم به الكود الذي كتبناه. لاحظ كيف "يفكر" الوكيل، ويقرر "الإجراء" (استدعاء دالة analyze_sentiment)، ثم يحصل على "استجابة الإجراء" (النتيجة 0.6). هذا هو بالضبط ما فعلناه مع دالة count_words.

اختبر فهمك!
لنفترض أنك أضفت دالة get_character_count التي أنشأتها في تمرين الدرس السابق. كيف ستعدل الكود الخاص بتنفيذ الدالة (الجزء الذي يحتوي على جملة if أو قاموس available_functions) ليدعم هذه الأداة الجديدة؟
اظهر الإجابة
الطريقة الأولى (باستخدام القاموس available_functions):
ستحتاج أولاً إلى تعريف الدالة get_character_count في كودك. ثم، ستوسع القاموس ليشملها:
def get_character_count(text):
"""تحسب عدد الحروف في النص."""
return len(text)
available_functions = {
"count_words": count_words,
"get_character_count": get_character_count, # الإضافة هنا
}
بهذه الطريقة، عندما يقرر النموذج استدعاء get_character_count، سيجدها الكود تلقائياً في القاموس وينفذها. هذه الطريقة أكثر تنظيماً وقابلية للتوسع.
الطريقة الثانية (باستخدام if/elif):
function_name = tool_call.function.name
function_args = json.loads(tool_call.function.arguments)
if function_name == "count_words":
function_response = count_words(text=function_args.get("text"))
elif function_name == "get_character_count": # الإضافة هنا
function_response = get_character_count(text=function_args.get("text"))
خلاصة الدرس
لقد قمنا اليوم بتجميع القطع معاً وبناء الآلية الأساسية التي تمكن وكيل الذكاء الاصطناعي من استخدام الأدوات. لم يعد النموذج مجرد مولد للنصوص، بل أصبح "عقلاً" يمكنه اتخاذ قرار باستخدام أداة برمجية، وبرنامجنا هو "الأيدي" التي تنفذ هذا القرار.
أهم النقاط:
- استدعاء الأدوات هو عملية من خطوتين: طلب القرار أولاً، ثم إرسال نتيجة التنفيذ للحصول على الإجابة النهائية.
- نستخدم المعامل
toolsفي استدعاء الـ API لتزويد النموذج بقائمة الأدوات المتاحة، وtool_choice="auto"لمنحه حرية الاختيار. - استجابة النموذج التي تطلب استدعاء أداة تحتوي على كائن
tool_callsيتضمن اسم الدالة ووسيطاتها. - نستخدم
json.loads()لتحويل نص الوسيطات إلى قاموس بايثون. - نستخدم قاموساً أو جمل
if/elifلربط اسم الدالة النصي بالدالة الفعلية في كود بايثون وتنفيذها.
ماذا بعد؟
لقد أصبح وكيلنا الآن قادراً على "الفعل" (Act) باستخدام الأدوات. لكن كيف يمكننا أن نجعل قراراته أكثر ذكاءً وموثوقية؟
في الدرس القادم، سنتعمق في كيفية تحسين عملية "التفكير" (Thinking) لدى الوكيل. سندرس استراتيجية 'سلسلة الأفكار' (Chain-of-Thought)، وهي تقنية تجعل النموذج "يفكر بصوت عالٍ" ويضع خطة قبل أن يقرر أي أداة سيستخدمها. هذا سيجعل وكيلنا أكثر منهجية وقدرة على حل المشاكل المعقدة.