هناك فرق شاسع بين بناء عميل يعمل وآخر لا يعمل. كيف يمكننا بناء وكلاء يندرجون ضمن الفئة الأخيرة؟ في هذا الدليل، سنرى في هذا الدليل أفضل الممارسات لبناء الوكلاء.
إذا كنت جديدًا في بناء الوكلاء، تأكد أولاً من قراءة مقدمة للوكلاء و جولة إرشادية في smolagents.
أفضل الأنظمة التوكيلية هي الأبسط: تبسيط سير العمل قدر الإمكان #
إن منحك لـ LLM بعض الوكالة في سير عملك يُدخل بعض مخاطر الأخطاء.
تمتلك الأنظمة الوكيلة المبرمجة جيدًا آليات جيدة لتسجيل الأخطاء وإعادة المحاولة على أي حال، لذا فإن محرك LLM لديه فرصة لتصحيح الخطأ ذاتيًا. ولكن لتقليل مخاطر خطأ LLM إلى أقصى حد، يجب عليك تبسيط سير عملك!
دعنا نعيد النظر في المثال من [مقدمة_الوكلاء]: روبوت يجيب على استفسارات المستخدم لشركة رحلات ركوب الأمواج. بدلًا من السماح للوكيل بإجراء مكالمتين مختلفتين لـ "واجهة برمجة تطبيقات مسافة السفر" و"واجهة برمجة تطبيقات الطقس" في كل مرة يُسأل فيها عن مكان جديد لركوب الأمواج، يمكنك فقط إنشاء أداة واحدة موحدة "return_spot_information"، وهي دالة تستدعي كلا واجهة برمجة التطبيقات في آن واحد وتعيد مخرجاتهما المتسلسلة إلى المستخدم.
سيؤدي ذلك إلى تقليل التكاليف والوقت المستغرق ومخاطر الخطأ!
المبدأ التوجيهي الرئيسي هو: قلل عدد مكالمات LLM قدر الإمكان.
وهذا يقودنا إلى بعض الاستنتاجات:
- كلما كان ذلك ممكنًا، اجمع أداتين في أداة واحدة، كما في مثالنا لواجهتي برمجة التطبيقات.
- حيثما أمكن، يجب أن يعتمد المنطق على الدوال الحتمية بدلاً من القرارات الحتمية.
تحسين تدفق المعلومات إلى محرك LLM #
تذكر أن محرك LLM الخاص بك يشبه روبوتًا ذكيًا ~~، تم ربطه في غرفة مع التواصل الوحيد مع العالم الخارجي هو الملاحظات التي تمر من تحت الباب.
لن يعرف أي شيء حدث إذا لم تضع ذلك صراحةً في مطالبته.
لذا ابدأ أولاً بجعل مهمتك واضحة جداً! نظرًا لأن الوكيل مدعوم من قبل LLM، فقد تؤدي الاختلافات الطفيفة في صياغة مهمتك إلى نتائج مختلفة تمامًا.
بعد ذلك، قم بتحسين تدفق المعلومات نحو وكيلك في استخدام الأدوات.
إرشادات خاصة يجب اتباعها:
- يجب أن تسجل كل أداة (ببساطة باستخدام
طباعة
داخل الأداةإلى الأمام
طريقة) كل ما يمكن أن يكون مفيدًا لمحرك LLM.- على وجه الخصوص، فإن تسجيل التفاصيل المتعلقة بأخطاء تنفيذ الأداة سيساعد كثيرًا!
على سبيل المثال، إليك أداة تسترجع بيانات الطقس استناداً إلى الموقع الجغرافي والتاريخ والوقت:
أولاً، هذه نسخة رديئة
تم النسخ
استيراد التاريخ والوقت من أداة استيراد smolagents smolagents def get_weather_report_at_coordinates(إحداثيات، تاريخ_وقت): # دالة وهمية، تُرجع قائمة ب [درجة الحرارة بالدرجة المئوية، خطر هطول الأمطار على مقياس 0-1، ارتفاع الموج بالمتر] العائد [28.0، 0.35، 0.85] تعريف get_coordinates_from_location(الموقع): # إرجاع إحداثيات وهمية العائد [3.3، -42.0] @أداة def get_weather_api(الموقع: str, date_time: str) -> str: """ إرجاع تقرير الطقس. الأوامر: الموقع: اسم المكان الذي تريد معرفة حالة الطقس فيه. التاريخ والوقت: التاريخ والوقت الذي تريد التقرير الخاص به. """ طول، عرض = تحويل_الموقع إلى إحداثيات (الموقع) date_time = datetime.strptime(date_time) الإرجاع str(get_weather_report_at_coordinates((lon, lat), date_time))
لماذا هو سيء؟
- لا توجد دقة في التنسيق الذي يجب استخدامه لـ
التاريخ_الوقت
- لا توجد تفاصيل حول كيفية تحديد الموقع.
- لا توجد آلية تسجيل مرتبطة بحالات الفشل الصريحة مثل عدم تنسيق الموقع بتنسيق مناسب، أو عدم تنسيق التاريخ_الوقت بشكل صحيح.
- من الصعب فهم تنسيق الإخراج
في حالة فشل استدعاء الأداة، يمكن أن يساعد تتبع الخطأ المسجل في الذاكرة أداة LLM في إجراء هندسة عكسية للأداة لإصلاح الأخطاء. لكن لماذا يترك لها الكثير من العمل الشاق لتقوم به؟
كان من الأفضل بناء هذه الأداة على النحو التالي:
تم النسخ
@أداة def get_weather_api(الموقع: str, date_time: str) -> str: """ إرجاع تقرير الطقس. الأوامر: الموقع: اسم المكان الذي تريد تقرير الطقس له. يجب أن يكون اسم مكان، متبوعًا ربما باسم مدينة، ثم اسم بلد، مثل "نقطة المرساة، تغازوت، المغرب". التاريخ_التوقيت: التاريخ والوقت الذي تريد التقرير الخاص به، منسقًا على شكل "%m/%dM/%y %H:%M:%S". """ lon, lat = convert_location_to_coordinates(location) حاول date_time = datetime.strptime(date_time) باستثناء الاستثناء ك e: رفع ValueError ("فشل تحويل 'date_time' إلى تنسيق التاريخ والوقت، تأكد من توفير سلسلة بالتنسيق '%m/%dM/%y %H:%M:%S'. التتبع الكامل:" + str(e))) درجة الحرارة_المئوية, خطر_المطر, ارتفاع_الموجة = get_weather_report_at_coordinates((lon, lat), date_time) الإرجاع f"تقرير حالة الطقس لـ {الموقع}، {التاريخ_التوقيت}: درجة الحرارة ستكون {درجة الحرارة_المئوية} درجة مئوية، خطر هطول الأمطار هو {خطر_المطر*100:.0f} %، ارتفاع الموجة هو {ارتفاع_الموجة} م."
بشكل عام، لتخفيف العبء عن كاهل أداة LLM، فإن السؤال الجيد الذي يجب أن تطرحه على نفسك هو: "ما مدى سهولة برمجة هذه الأداة وتصحيح أخطائي إذا كنت غبيًا وأستخدمها لأول مرة على الإطلاق؟
إعطاء المزيد من الحجج للوكيل #
لتمرير بعض الكائنات الإضافية إلى وكيلك بخلاف السلسلة البسيطة التي تصف المهمة، يمكنك استخدام الإضافات_الإضافية
لتمرير أي نوع من الكائنات:
تم النسخ
من smolagents smolagents استيراد CodeAgent, HfApiModel model_id = "meta-llama/Llama-3.3-70B-Instruct" الوكيل = CodeAgent(Tools=[]، model=HfApiModel(model_id=model_id)، add_base_tools=True) agent.run( "لماذا لا يعرف مايك الكثير من الأشخاص في نيويورك؟ additional_args={"mp3_sound_sound_file_url":'https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/recording.mp3'} )
على سبيل المثال، يمكنك استخدام هذا الإضافات_الإضافية
وسيطة لتمرير الصور أو السلاسل التي تريد أن يستفيد منها وكيلك.
كيفية تصحيح أخطاء الوكيل #
1. استخدام LLM أقوى #
في سير عمل وكيلي، بعض الأخطاء هي أخطاء فعلية، وبعضها الآخر هو خطأ محرك LLM الخاص بك لا يستنتج بشكل صحيح. على سبيل المثال، تأمل هذا التتبع لـ CodeAgent
التي طلبتُ منها إنشاء صورة سيارة:
تم النسخ
==================================================================================================== New task ==================================================================================================== اصنع لي صورة سيارة رائعة ──────────────────────────────────────────────────────────────────────────────────────────────────── خطوة جديدة ──────────────────────────────────────────────────────────────────────────────────────────────────── يقوم الوكيل بتنفيذ الرمز أدناه ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── Image_generator(موجه="سيارة رياضية مستقبلية رائعة بمصابيح أمامية LED، وتصميم ديناميكي هوائي، وألوان نابضة بالحياة، عالية الدقة، وواقعية") ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── آخر إخراج من مقتطف الكود: ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── /var/folders/6m/9b1tts6d5w960j80wbw9tx3m0000gn/T/tmpx09qfsdd/652f0007-3ee9-44e2-94ac-90dae6bb89a4.png الخطوة 1: - الوقت المستغرق 16.35 ثانية - رموز الإدخال: 1,383 - رموز الإخراج: 77 ──────────────────────────────────────────────────────────────────────────────────────────────────── خطوة جديدة ──────────────────────────────────────────────────────────────────────────────────────────────────── يقوم الوكيل بتنفيذ الرمز أدناه: ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── final_answer("/var/folders/6m/9b1tts6d5w960j80wbw9tx3m0000gn/T/tmpx09qfsdd/652f0007-3ee9-44e2-94ac-90dae6bb89a4.png") ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── مخرجات الطباعة: آخر إخراج من مقتطف الشفرة: ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── /var/folders/6m/9b1tts6d5w960j80wbw9tx3m0000gn/T/tmpx09qfsdd/652f0007-3ee9-44e2-94ac-90dae6bb89a4.png الإجابة النهائية /var/folders/6m/9b1tts6d5w960j80wbw9tx3m0000gn/T/tmpx09qfsdd/652f0007-3ee9-44e2-94ac-90dae6bb89a4.png
يرى المستخدم، بدلاً من إرجاع الصورة، مسارًا يتم إرجاعه إليه. قد يبدو الأمر وكأنه خطأ من النظام، ولكن في الواقع لم يتسبب النظام الوكيل في حدوث الخطأ: كل ما في الأمر أن عقل LLM أخطأ في عدم حفظ إخراج الصورة في متغير. وبالتالي لا يمكنه الوصول إلى الصورة مرة أخرى إلا من خلال الاستفادة من المسار الذي تم تسجيله أثناء حفظ الصورة، لذا فإنه يعيد المسار بدلاً من الصورة.
وبالتالي، فإن الخطوة الأولى لتصحيح أخطاء وكيلك هي "استخدام LLM أكثر قوة". بدائل مثل Qwen2/5-72B-تعليمات كوين2/5-72B-تعليمات
لم يكن ليرتكب هذا الخطأ.
2. تقديم المزيد من الإرشادات/مزيد من المعلومات #
يمكنك أيضاً استخدام نماذج أقل قوة، شريطة أن تقوم بتوجيهها بشكل أكثر فعالية.
ضع نفسك مكان النموذج الخاص بك: لو كنت أنت النموذج الذي يحل المهمة، هل كنت ستعاني من المعلومات المتاحة لك (من موجه النظام + صياغة المهمة + وصف الأداة)؟
هل تحتاج إلى بعض التوضيحات الإضافية؟
لتوفير معلومات إضافية، لا نوصي بتغيير موجه النظام على الفور: يحتوي موجه النظام الافتراضي على العديد من التعديلات التي لا تريد إفسادها إلا إذا كنت تفهم الموجه جيدًا. أفضل الطرق لتوجيه محرك LLM الخاص بك هي:
- إذا كان الأمر يتعلق بالمهمة المطلوب حلها: أضف كل هذه التفاصيل إلى المهمة. يمكن أن تكون المهمة بطول 100 صفحة.
- إذا كان الأمر يتعلق بكيفية استخدام الأدوات: سمة الوصف لأدواتك.
3. تغيير موجه النظام (لا ينصح به بشكل عام) #
إذا لم تكن التوضيحات المذكورة أعلاه غير كافية، يمكنك تغيير مطالبة النظام.
دعنا نرى كيف يعمل. على سبيل المثال، دعنا نتحقق من موجه النظام الافتراضي لـ CodeAgent (تم اختصار النسخة أدناه بتخطي أمثلة اللقطة الصفرية).
تم النسخ
طباعة(agent.system_prompt_template)
إليك ما ستحصل عليه:
تم النسخ
أنت مساعد خبير يمكنه حل أي مهمة باستخدام النقط البرمجية. سيتم إعطاؤك مهمة لحلها بأفضل ما يمكنك. وللقيام بذلك، سيُتاح لك الوصول إلى قائمة من الأدوات: هذه الأدوات هي في الأساس دوال بايثون التي يمكنك استدعاؤها باستخدام التعليمات البرمجية. لحل المهمة، يجب أن تخطط للمضي قدمًا في سلسلة من الخطوات، في دورة من تسلسلات "الفكر:" و"البرمجة:" و"الملاحظة:". في كل خطوة، في تسلسل "فكر:"، يجب عليك أولاً أن تشرح أولاً منطقك لحل المهمة والأدوات التي تريد استخدامها. ثم في تسلسل "البرمجة:" يجب أن تكتب الكود بلغة بايثون البسيطة. يجب أن ينتهي تسلسل التعليمات البرمجية بتسلسل "". خلال كل خطوة وسيطة، يمكنك استخدام 'print()' لحفظ أي معلومات مهمة ستحتاجها بعد ذلك. ستظهر مخرجات الطباعة هذه بعد ذلك في حقل "الملاحظة:" والتي ستكون متاحة كمدخلات للخطوة التالية. في النهاية عليك إرجاع إجابة نهائية باستخدام أداة "الإجابة النهائية". فيما يلي بعض الأمثلة باستخدام الأدوات الافتراضية: --- {أمثلة} الأمثلة أعلاه كانت تستخدم أدوات افتراضية قد لا تكون موجودة بالنسبة لك. بالإضافة إلى إجراء العمليات الحسابية في مقتطفات شيفرة Python التي تنشئها، يمكنك الوصول إلى هذه الأدوات فقط: {{{أداة_أوصاف}} {{{managed_agents_dagents_descriptions}} فيما يلي القواعد التي يجب عليك اتباعها دائمًا لحل مهمتك: 1. 2. استخدم فقط المتغيرات التي قمت بتعريفها! 3. استخدم دائمًا الوسيطات الصحيحة للأدوات. لا تمرر الوسيطات على شكل إملاء كما في 'answer = wiki({'query': "ما هو المكان الذي يعيش فيه جيمس بوند؟"})، بل استخدم الوسيطات مباشرةً كما في 'answer = wiki(query="ما هو المكان الذي يعيش فيه جيمس بوند؟")'. 4. احرص على عدم تسلسل الكثير من استدعاءات الأدوات المتسلسلة في نفس الكتلة البرمجية، خاصةً عندما يكون تنسيق الإخراج غير متوقع. على سبيل المثال، استدعاء البحث له صيغة إرجاع لا يمكن التنبؤ بها، لذا لا تقم باستدعاء أداة أخرى تعتمد على مخرجاتها في نفس الكتلة البرمجية: بل قم بإخراج النتائج باستخدام print() لاستخدامها في الكتلة التالية. 5. لا تستدعي الأداة إلا عند الحاجة فقط، ولا تعيد أبدًا استدعاء أداة قمتَ باستدعائها سابقًا بنفس المعلمات بالضبط. 6. لا تسمي أي متغير جديد بنفس اسم الأداة: على سبيل المثال لا تسمي المتغير "إجابة_نهائية". 7. لا تنشئ أبدًا أي متغيرات افتراضية في الشيفرة البرمجية؛ لأن وجودها في سجلاتك قد يصرفك عن المتغيرات الحقيقية. 8. يمكنك استخدام الواردات في شيفرتك، ولكن فقط من قائمة الوحدات التالية: {{{authorized_imports}}} 9. تستمر الحالة بين عمليات تنفيذ التعليمات البرمجية: لذا إذا قمت في خطوة واحدة بإنشاء متغيرات أو وحدات مستوردة، فستستمر هذه كلها. 10. لا تستسلم! أنت مسؤول عن حل المهمة، وليس تقديم توجيهات لحلها. ابدأ الآن! إذا قمت بحل المهمة بشكل صحيح، ستحصل على مكافأة قدرها $1,000,000.
كما ترى، هناك عناصر نائبة مثل "{{tool_descriptions}}"
:: سيتم استخدامها عند تهيئة الوكيل لإدراج بعض الأوصاف التي تم إنشاؤها تلقائيًا للأدوات أو الوكلاء المُدارة.
لذلك بينما يمكنك الكتابة فوق قالب مطالبة النظام هذا عن طريق تمرير المطالبة المخصصة كوسيطة إلى نظام_برومبت
يجب أن تحتوي معلمة النظام الجديد على العناصر النائبة التالية:
"{{tool_descriptions}}"
لإدراج أوصاف الأداة."{{managed_agents_description_description}}"
لإدراج الوصف الخاص بالوكلاء المُدارين، إن وُجدوا.- بالنسبة لـ
CodeAgent
فقط:"{{authorized_imports}}"
لإدراج قائمة الواردات المصرح بها.
ثم يمكنك تغيير مطالبة النظام على النحو التالي:
تم النسخ
من smolagents.prompts.prompts استيراد CODE_SYSTEM_PROMPT موجه_النظام_المعدّل = CODE_SYSTEM_PROMPT + "\nها أنت ذا!" # تغيير موجه النظام هنا الوكيل = CodeAgent( الأدوات=[], model=HfApiModel(), System_prompt=modified_system_prompt )
يعمل هذا أيضًا مع وكيل استدعاء الأدوات.
4. التخطيط الإضافي #
نحن نقدم نموذجًا لخطوة تخطيط تكميلية يمكن للوكيل تشغيلها بانتظام بين خطوات العمل العادية. في هذه الخطوة، لا يوجد استدعاء للأدوات، حيث يُطلب من الوكيل ببساطة تحديث قائمة بالحقائق التي يعرفها والتفكير في الخطوات التالية التي يجب أن يتخذها بناءً على تلك الحقائق.
تم النسخ
من smolagents استيراد smolagents load_tool, CodeAgent, HfApiModel, DuckDuckGoSearchTool من dotenv استيراد load_dotenv تحميل_دوتينف() # أداة الاستيراد من المحور أداة_إنشاء_الصورة_أداة_توليد_الصور = تحميل_أداة("m-ric/النص_إلى_صورة"، رمز_الثقة_بالتصميم_المتبادل=صحيح) أداة_البحث_أداة البحث = DuckDuckGoSearchTool() وكيل = CodeAgent( الأدوات=[أداة_البحث], model=HfApiModel("Qwen/Qwen2.5-72B-Instruct"), فترة_التخطيط=3 # هذا هو المكان الذي تقوم فيه بتفعيل التخطيط! ) # قم بتشغيله! النتيجة = agent.run( ") "كم من الوقت يستغرق الفهد بأقصى سرعة للركض بطول جسر ألكسندر الثالث؟ )