الدرس 02 · أساسيات

الحلقة لم تتغير، فقط زادت الأدوات

«الحلقة لم تتغير قيد أنملة، أضفت فحسب عناصر إلى مصفوفة TOOLS.» — s02_tool_use.py

⏱ ~10 د · 📝 3 مكونات تفاعلية · 🧑‍💻 مبني على shareAI-lab · s02_tool_use.py

ما الذي يجب تعديله لإضافة أداة؟

وكيل S01 يعرف فقط bash. كيف تُمكّنه أيضاً من read_file / write_file / edit_file؟

الردة الأولى لكثيرين: تعديل الحلقة. خطأ. لا تمسّ الحلقة ولا سطراً واحداً منها. ثلاثة أشياء فقط مطلوبة:

  1. اكتب دالة handler بايثون (run_read(path, limit)).
  2. سجّلها في جدول التعيين TOOL_HANDLERS ("read_file": lambda **kw: run_read(...)).
  3. أضف تعريف JSON schema في مصفوفة TOOLS (لتُعلم النموذج باسم الأداة والمعاملات التي تقبلها).

حين ترى الحلقة كتلة tool_use، تبحث بـ block.name في dispatch map عن الدالة، تُنفذها، وتُعيد الإخراج إلى tool_result. المسار طابق تماماً مسار bash.

# Dispatch map: name → handler lambda
TOOL_HANDLERS = {
    "bash":       lambda **kw: run_bash(kw["command"]),
    "read_file":  lambda **kw: run_read(kw["path"], kw.get("limit")),
    "write_file": lambda **kw: run_write(kw["path"], kw["content"]),
    "edit_file":  lambda **kw: run_edit(kw["path"], kw["old_text"], kw["new_text"]),
}

شاهد كيف يعمل التوجيه

المكوّن التالي يتيح لك النقر على طلب tool_use قد يُرسله النموذج، ومشاهدة كيف يُوجَّه إلى دالة Python محددة. تذكر: block.name هو الذي يحدد المسار.

safe_path: خط الدفاع الذي لا يمكن تجاهله

حين تمنح الوكيل صلاحية الوصول للملفات، أخطر ثغرة أمنية هي هروب المسار: النموذج المفروض أن يعمل في /home/user/project/، لكنه يُرسل read_file("../../etc/passwd").

في s02 دالة صغيرة تُعالج هذا:

def safe_path(p: str) -> Path:
    path = (WORKDIR / p).resolve()  # 规范化,解析 .. 和软链
    if not path.is_relative_to(WORKDIR):
        raise ValueError(f"Path escapes workspace: {p}")
    return path

المفتاح في خطوتين: .resolve() + .is_relative_to() — تحليل إلى مسار مطلق ثم التحقق من بقائه داخل sandbox. بدون الأولى، يمكن لـ foo/../../etc العبور؛ بدون الثانية، لا يوجد تحقق أصلاً.

تمييز أمان المسار

المسارات الخمسة التالية مُعاملات read_file قد يُرسلها النموذج. أيها يسمح به safe_path، وأيها يرفضه؟ افترض WORKDIR = /home/user/project.

لا تُضف أدوات خطيرة إلى الوكيل

إضافة الأدوات سهلة، لكن تذكر: كل أداة تُضيفها هي حدود قدرة جديدة للنموذج. في s02 يحتوي bash على قائمة سوداء (rm -rf /، sudo، shutdown)، وwrite_file مقيد بـ safe_path. قبل إضافة أدوات للإنتاج، اسأل نفسك ثلاثة أسئلة:

  • هل يمكن لهذه الأداة أن تُمكّن النموذج من أفعال لا رجعة فيها؟ (rm، إرسال بريد إلكتروني، git push)
  • ما الذي يمكن أن يتسرب عبرها؟ (متغيرات البيئة، .ssh، ملفات تعريف الارتباط)
  • هل يمكن أن تُعامَل مخرجات الخطأ كتعليمات؟ (حقن Prompt عبر مخرجات الأداة)

الدرس s07 سيُضيف «طبقة صلاحيات» لسحب هذه القرارات من الكود وجعلها تصريحية.

تفاعلي

Widget 1 · Tool Dispatch · كيف يُوجَّه tool_use إلى الـ handler

اضغط على طلب tool_use وشاهد كيف تجد الحلقة بواسطة block.name الدالة المقابلة وتُنفذها وتُعيد الإخراج إلى كتلة tool_result.

كتلة tool_use الواردة من النموذج
جدول TOOL_HANDLERS

        
نتيجة التنفيذ · كتلة tool_result
(اضغط على أي tool_use أعلاه لتفعيل التوجيه)
تفاعلي

Widget 2 · Safe Path · كشف الهروب في 5 مسارات

كل مسار ستُعالجه safe_path(). افترض WORKDIR = /home/user/project، اضغط «سماح» أو «رفض» — واختبر حدسك في هروب المسار.

أصبت في 0 / 5
تفاعلي

Widget 3 · Add a Tool · ما الذي يجب تغييره لإضافة أداة glob

افترض أنك تريد إضافة أداة glob(pattern) جديدة تتيح للوكيل البحث عن ملفات بنمط. أكمل الفراغات: ثلاثة أماكن يجب تغييرها كلها دون استثناء.

الموضع 1 · كتابة دالة handler
def run_glob(pattern: str) -> str:
    import glob
    return "\n".join(glob.glob(pattern))
الموضع 2 · التسجيل في dispatch map (اختر السطر الصحيح)
الموضع 3 · إضافة schema في مصفوفة TOOLS (اختر المجموعة الصحيحة)