العودة للدورة|مقابلات مهندس وكلاء الذكاء الاصطناعي: صمم وابنِ وانشر أنظمة وكلاء إنتاجية
معمل

ابنِ إطار وكيل استدعاء أدوات

35 دقيقة
متقدم
محاولات مجانية غير محدودة

التعليمات

في هذا المختبر، ستبني أساس كل نظام وكلاء: وكيل استدعاء أدوات بلغة Python. وكيلك سيحتفظ بسجل أدوات، ويستخدم LLM لتحديد الأدوات التي يجب استدعاؤها، والتحقق من المعاملات، وتنفيذ الأدوات بأمان، والتعامل مع الأخطاء بسلاسة.

هذا هو النمط المستخدم في كل إطار وكلاء رئيسي (LangGraph، CrewAI، OpenAI Agents SDK). ببنائه من الصفر، ستفهم بالضبط كيف يعمل استدعاء الأدوات تحت الغطاء.

نظرة عامة على البنية

رسالة المستخدم
حلقة الوكيل ←──────────────────┐
     ↓                           │
LLM (يقرر: الرد أو              │
     استدعاء أداة)               │
     ↓                           │
[إذا استدعاء أداة]              │
     ↓                           │
التحقق من المعاملات             │
     ↓                           │
تنفيذ الأداة (مع مهلة زمنية)    │
     ↓                           │
حقن النتيجة → العودة لـ LLM ────┘
[إذا استجابة نهائية]
الإعادة للمستخدم

الخطوة 1: سجل الأدوات

ابنِ فئة ToolRegistry تدير تعريفات الأدوات. كل أداة تحتوي:

  • name فريد (معرّف نصي)
  • description (ما تفعله الأداة — يُرسل هذا لـ LLM)
  • parameters بصيغة JSON Schema (يحدد أنواع المدخلات المتوقعة)
  • دالة handler (التنفيذ الفعلي)

السجل يجب أن يدعم:

  • register(tool) — إضافة أداة للسجل
  • unregister(name) — إزالة أداة بالاسم
  • get(name) — استرجاع أداة بالاسم
  • list_tools() — إعادة جميع تعريفات الأدوات (لإرسالها لـ LLM)

الخطوة 2: التحقق من المعاملات

قبل تنفيذ أي أداة، تحقق من الوسائط المقدمة مقابل JSON Schema الأداة:

  • تحقق من وجود الحقول المطلوبة
  • تحقق من تطابق الأنواع (string، number، boolean، array، object)
  • أعد رسالة خطأ واضحة إذا فشل التحقق

يمكنك تنفيذ محقق بسيط أو استخدام نمط مكتبة jsonschema.

الخطوة 3: منفّذ الأدوات

ابنِ ToolExecutor يشغّل دوال الأدوات بانتظار محدود ونتائج مهيكلة:

  • نفّذ دالة handler الأداة مع الوسائط المحققة
  • طبّق ميزانية انتظار قابلة للتكوين (افتراضي: 30 ثانية) باستخدام ThreadPoolExecutor
  • التقاط الاستثناءات وإعادة نتائج خطأ مهيكلة
  • تتبع بيانات التنفيذ الوصفية: وقت البدء، وقت الانتهاء، المدة، النجاح/الفشل

اقرأ هذا قبل التنفيذ، لأن النسخة البديهية لا تفعل ما يوحي به اسمها. مهلة ThreadPoolExecutor تحدّ مدة انتظارك أنت، وهي لا توقف الـhandler. فبايثون لا يستطيع قتل خيط قيد التشغيل، ولهذا يُرجع future.cancel() القيمة False بمجرد بدء العمل، وتستمر الأداة — ستُكمل، وأي كتابة كانت ستقوم بها تقع فعلاً، بعد أن يكون وكيلك قد سجّل فشلاً ومضى. تحقق من ذلك بنفسك بـhandler ينام ثم يضيف عنصراً إلى قائمة: ستجد أن الإضافة حدثت.

نتيجتان يجب أن يعالجهما تنفيذك:

  1. لا تنشئ الـpool داخل كتلة with مع كل استدعاء. فـThreadPoolExecutor.__exit__ يستدعي shutdown(wait=True) الذي يحجب حتى ينتهي الـhandler — فمهلة 0.2 ثانية حول handler مدته 1.5 ثانية ترجع بعد 1.5 ثانية، ولا تحدّ المهلة شيئاً على الإطلاق. أنشئ pool واحداً طويل العمر في __init__.
  2. أبلغ عن النتيجة بأمانة. المهلة ليست «الأداة لم تعمل»، بل «نتيجة الأداة مجهولة». أضف إلى ExecutionResult حقل side_effects_possible: bool واضبطه على True في مسار المهلة، لتسجّل حلقة الوكيل وسجل التدقيق كلاهما أن كتابة قد تكون ما زالت جارية.

أما الإصلاح الحقيقي لأداة يجب إيقافها فهو حدّ يفرضه نظام التشغيل — عملية فرعية تستطيع قتلها، أو مهلة داخل عميل الأداة نفسه مثل مهلة الطلب في عميل HTTP — لكن ذلك يغيّر واجهة Callable التي بُني عليها هذا المختبر. تسمية القيد هي المهارة المُقيَّمة هنا، وهي أيضاً الإجابة في المقابلة.

الخطوة 4: اختيار الأدوات بواسطة LLM

ابنِ ToolSelector يسأل LLM أي أداة/أدوات يجب استدعاؤها:

  • تنسيق الأدوات المتاحة كأمر مهيكل لـ LLM
  • تحليل استجابة LLM لاستخراج قرارات استدعاء الأدوات
  • دعم قرار LLM بعدم استدعاء أي أدوات (استجابة مباشرة)
  • دعم قرار LLM باستدعاء عدة أدوات بالتسلسل

استخدم تحليل المخرجات المهيكلة: يجب أن يعيد LLM JSON مع اسم الأداة والوسائط.

الخطوة 5: حلقة محادثة الوكيل

ابنِ فئة Agent الرئيسية التي تنسق كل شيء:

  • قبول رسالة المستخدم
  • إرسالها لـ LLM مع تعريفات الأدوات المتاحة
  • إذا قرر LLM استدعاء أداة: تحقق، نفّذ، احقن النتيجة، عُد للحلقة
  • إذا قدم LLM استجابة نهائية: أعدها للمستخدم
  • دعم الاستدلال متعدد الخطوات (قد يستدعي LLM عدة أدوات قبل الرد)
  • فرض حد أقصى لاستدعاءات الأدوات لكل دورة (لمنع الحلقات اللانهائية)

الخطوة 6: سجل التدقيق

أضف AuditLogger يسجل كل إجراء:

  • محاولات استدعاء الأدوات (اسم الأداة، الوسائط، الطابع الزمني)
  • نتائج تنفيذ الأدوات (النجاح/الفشل، المدة، المخرجات)
  • قرارات LLM (استدعاء أداة مقابل استجابة مباشرة)
  • أحداث الأخطاء (فشل التحقق، المهلات الزمنية، الاستثناءات)

ماذا تقدّم

المحرر يحتوي على 6 أقسام ملفات مع تعليقات TODO. استبدل كل TODO بكود Python الخاص بك. سيقوم المقيّم الذكي بتقييم كل قسم وفقاً لمعايير التقييم.

تلميحات

  • لمحقق JSON Schema، ركز على فحوصات type وrequired — لا تحتاج لتنفيذ JSON Schema كامل
  • للمهلة الزمنية، استخدم concurrent.futures.ThreadPoolExecutor مع معامل timeout
  • لاختيار أدوات LLM، حدد تنسيق أمر واضح وهيكل استجابة JSON متوقع
  • حلقة الوكيل يجب أن تحتوي معامل max_iterations (افتراضي: 10) لمنع التنفيذ الجامح

معايير التقييم

ToolRegistry ينفذ register (مع فحص الأسماء المكررة) وunregister وget (كلاهما يرفع أخطاء مناسبة) وlist_tools يعيد قواميس قابلة للتسلسل بدون handler20 نقاط
ParameterValidator يتحقق من الحقول المطلوبة وفحص الأنواع لـ string وnumber وinteger وboolean وarray وobject، يجمع كل الأخطاء، ويرفع ValidationError مع رسائل وصفية15 نقاط
ToolExecutor يشغّل الـhandler على ThreadPoolExecutor طويل العمر يُنشأ في __init__ — لا كتلة `with` مع كل استدعاء، فهي تستدعي shutdown(wait=True) وتجعل المهلة لا تحدّ شيئاً، وتلك النسخة تأخذ صفراً هنا. ويطبّق ميزانية الانتظار عبر future.result(timeout=...)، ويسجّل start_time/end_time/duration_ms، ويعيد ExecutionResult مهيكلاً في حالات النجاح والمهلة والاستثناء. وفي مسار المهلة يضبط side_effects_possible=True ولا يدّعي أن الأداة لم تعمل، لأن الخيط لا يمكن إلغاؤه وسيُكمل الـhandler عمله. والتسليم الذي يذكر هذا القيد في تعليق أو docstring ينال الدرجة كاملة ولو اختلفت صياغته.15 نقاط
ToolSelector ينسق أمراً واضحاً مع الأدوات ورسالة المستخدم، يحلل استجابة LLM بصيغة JSON لإجراءات tool_call وrespond، يتعامل مع JSON المشوه بسلاسة كاستجابة مباشرة20 نقاط
Agent.run ينفذ حلقة المحادثة الكاملة: يحصل على الأدوات، يستدعي المحدد، يعالج استدعاءات الأدوات (تحقق → تنفيذ → حقن النتيجة)، يعود للحلقة متعدد الخطوات، يحترم max_iterations، يتعامل مع الأخطاء بسلاسة15 نقاط
AuditLogger ينفذ log_tool_call وlog_result وlog_decision وlog_error مع إنشاء AuditEntry صحيح (الطابع الزمني، نوع الحدث، البيانات)، وget_entries مع تصفية اختيارية15 نقاط

قائمة التحقق

0/6

حلك

محاولات مجانية غير محدودة