smolagents: کتابخانهای ساده برای ساخت Agentها

در این پست با smolagents آشنا شده و آن را راهاندازی میکنیم، فریمورکی بسیار ساده از هاگینگ فیس که قابلیتهای عاملیت (agentic) را برای مدلهای زبانی فراهم میکند. قبل از شروع، نگاهی به آن بیندازیم:
from smolagents import CodeAgent, DuckDuckGoSearchTool, HfApiModel agent = CodeAgent(tools=[DuckDuckGoSearchTool()], model=HfApiModel()) agent.run("How many seconds would it take for a leopard at full speed to run through Pont des Arts?")
فهرست مطالب
Agentها چیستند؟
هر سیستم کارآمد مبتنی بر هوش مصنوعی نیاز دارد به مدلهای زبانی بزرگ (LLM) نوعی دسترسی به دنیای واقعی را فراهم کند: به عنوان مثال، امکان فراخوانی یک ابزار جستجو برای دریافت اطلاعات خارجی، یا اقدام روی برنامههای خاص برای حل یک وظیفه. به عبارت دیگر، LLMها باید قابلیت عاملیت داشته باشند. برنامههای عاملیتی (Agentic)، دروازهای به دنیای خارج برای LLMها هستند.
Agentهای هوش مصنوعی برنامههایی هستند که مدلهای زبانی یا LLMها در آنها تصمیم میگیرند چه کاری انجام شود. به زبان ساده، این برنامهها به هوش مصنوعی اجازه میدهند خودش تصمیم بگیرد که قدم بعدی چیست و چطور یک مسئله را حل کند.
وقتی از هوش مصنوعی در یک برنامه استفاده میکنیم، پاسخهای آن باید به نوعی در برنامه به کار گرفته شود. هر چقدر به هوش مصنوعی اجازه دهیم بیشتر در تصمیمگیریها و روند اجرای برنامه دخالت کند، میگوییم سطح عاملیت(level of agency) آن بالاتر است. به عبارت سادهتر، سطح عاملیت یعنی هوش مصنوعی چقدر اختیار دارد در برنامه تصمیم بگیرد و کارها را هدایت کند.
توجه داشته باشید که با این تعریف، Agent یک تعریف گسسته، صفر یا یک نیست: در عوض، عاملیت (agency) به اندازه ای که شما قدرت بیشتر یا کمتری به LLM در جریان کار خود میدهید در یک طیف پیوسته تکامل مییابد.
جدول زیر نشان میدهد چگونه عاملیت در سیستمهای مختلف متفاوت است:
سطح عاملیت | توضیحات | نام این روش | الگوی مثال |
---|---|---|---|
☆☆☆ | خروجی LLM تأثیری بر جریان برنامه ندارد | پردازشگر ساده (Simple processor) |
process_llm_output(llm_response) |
★☆☆ | خروجی LLM جریان کنترل اساسی را تعیین میکند | Router | if llm_decision(): path_a() else: path_b() |
★★☆ | خروجی LLM اجرای تابع را تعیین میکند | فراخوانی ابزار (Tool call) |
run_function(llm_chosen_tool, llm_chosen_args) |
★★★ | خروجی LLM تکرار و ادامه برنامه را کنترل میکند | Agent چندمرحلهای (Multi-step Agent) |
while llm_should_continue(): execute_next_step() |
★★★ | یک گردش کار agentic میتواند گردش کار agentic دیگری را شروع کند | چند-Agentی (Multi-Agent) |
if llm_trigger(): execute_agent() |
Agent چندمرحلهای (multi-step agent) این ساختار کد را دارد:
memory = [user_defined_task] while llm_should_continue(memory): # this loop is the multi-step part action = llm_get_next_action(memory) # this is the tool-calling part observations = execute_action(action) memory += [action, observations]
این سیستم در یک چرخه اجرا میشود و در هر مرحله یک اقدام جدید انجام میدهد (این اقدام میتواند شامل فراخوانی ابزارهای از پیش تعیینشده که در واقع توابعی در پایتون هستند باشد)، تا زمانی که مشاهدات آن مشخص کند که به وضعیت رضایتبخشی برای حل مسئله مورد نظر رسیده است. در اینجا مثالی از چگونگی حل یک سؤال ساده ریاضی توسط یک Agent چندمرحلهای آمده است:
✅ چه زمانی از Agentها استفاده کنیم / ⛔ چه زمانی از آنها اجتناب کنیم
Agentها زمانی مفید هستند که نیاز دارید یک LLM جریان کار یک برنامه را تعیین کند. اما اغلب استفاده از آنها زیادهروی است. سؤال این است: آیا واقعاً برای حل کارآمد وظیفه مورد نظر به انعطافپذیری در جریان کار نیاز دارم؟ اگر جریان کار از پیش تعیینشده خیلی اوقات کافی نیست، به این معناست که به انعطافپذیری بیشتری نیاز دارید. بیایید مثالی بزنیم: فرض کنید در حال ساخت برنامهای هستید که درخواستهای مشتری را در یک وبسایت سفرهای موجسواری مدیریت میکند.
شما میتوانید از قبل بدانید که درخواستها به یکی از این ۲ دسته تعلق خواهند داشت (بر اساس انتخاب کاربر)، و برای هر یک از این ۲ مورد یک جریان کار از پیش تعیینشده دارید:
- میخواهد اطلاعاتی درباره سفرها بداند؟ ⇐ به او دسترسی به یک نوار جستجو برای جستجو در پایگاه دانش خود بدهید
- میخواهد با فروش صحبت کند؟ ⇐ به او اجازه دهید در یک فرم تماس تایپ کند.
اگر این روش برای همه سوالات جواب میدهد، حتماً همه چیز را کدنویسی کنید! اینطوری یک سیستم ۱۰۰٪ قابل اعتماد خواهید داشت و دیگر خطایی نخواهید داشت که از دخالت LLMهای غیرقابل پیشبینی ناشی میشود. برای سادگی و اطمینان بیشتر، بهتر است تا حد امکان از رفتار Agentic خودداری کنید.
اما اگر نتوانیم جریان کار را از قبل به طور کامل و دقیق مشخص کنیم، چه؟
به عنوان مثال، کاربری میخواهد بپرسد: “من میتوانم دوشنبه بیایم، اما پاسپورتم را فراموش کردهام، پس ممکن است تا چهارشنبه تأخیر داشته باشم. آیا امکان دارد من و وسایلم را سهشنبه صبح برای موجسواری ببرید، با بیمه لغو؟” این سؤال به عوامل زیادی بستگی دارد و احتمالاً هیچکدام از معیارهای از پیش تعیینشده بالا برای این درخواست کافی نخواهد بود.
اگر جریان کار از پیش تعیینشده خیلی اوقات کافی نیست، به این معناست که به انعطافپذیری بیشتری نیاز دارید.
اینجاست که یک راهاندازی Agentic کمک میکند.
در مثال بالا، میتوانید یک Agent چندمرحلهای بسازید که به API آب و هوا برای پیشبینی آب و هوا، API گوگل مپس برای محاسبه فاصله سفر، داشبورد دسترسی کارمندان و یک سیستم RAG روی پایگاه دانش خود دسترسی داشته باشد.
تا همین اواخر، برنامههای کامپیوتری فقط میتوانستند از جریانهای کاری از پیش تعیینشده استفاده کنند و تلاش میکردند با استفاده از شرطهای if/else پیچیدگیها را مدیریت کنند. این برنامهها روی کارهای خیلی محدودی تمرکز داشتند، مثل “جمع این اعداد را حساب کن” یا “کوتاهترین مسیر را در این گراف پیدا کن”. اما در واقع، اکثر کارهای دنیای واقعی، مثل مثال سفر که بالاتر گفتیم، در قالب جریانهای کاری از پیش تعریفشده نمیگنجند. سیستمهای عاملیتی، دنیای وسیعی از وظایف واقعی را برای برنامهها باز میکنند!
کاربر میخواهد من بخش “Code agents” را ترجمه کنم. این بخش درباره روشهای مختلف برای نوشتن اقدامات توسط LLM در Agentهای چندمرحلهای است و مقایسهای بین استفاده از JSON و کد برای بیان این اقدامات ارائه میدهد.
Code Agentها
در یک Agent چندمرحلهای، در هر مرحله، LLM میتواند یک عمل را با فراخوانی ابزارهای خارجی انجام دهد. یک روش رایج (که توسط شرکتهایی مثل Anthropic و OpenAI و خیلیهای دیگر استفاده میشود) برای نوشتن این اعمال، معمولاً به این شکل است: “اعمال را به صورت JSON بنویسید که شامل نام ابزارها و آرگومانهایی است که باید استفاده شوند، سپس شما آن را parse میکنید تا بفهمید کدام ابزار را با چه آرگومانهایی اجرا کنید.”
بسیاری از مقالات پژوهشی نظیر:
- Executable Code Actions Elicit Better LLM Agents
- DynaSaur: Large Language Agents Beyond Predefined Actions
- If LLM Is the Wizard, Then Code Is the Wand: A Survey on How Code Empowers Large Language Models to Serve as Intelligent Agents
نشان دادهاند که فراخوانی ابزارها توسط LLMها در قالب کد، بسیار مؤثرتر است.
دلیلش هم ساده است: ما زبانهای کد را طوری طراحی کردهایم که بهترین روش برای بیان کارهایی باشند که یک کامپیوتر باید انجام دهد. اگر قطعه کدهای JSON عملکرد بهتری داشتند، JSON به عنوان زبان برنامهنویسی برتر مطرح میشد و برنامهنویسی به یک کابوس تبدیل میشد.
شکل زیر، که از مقالهی Executable Code Actions Elicit Better LLM Agents آورده شده، برخی از مزایای نوشتن اقدامات در قالب کد را نشان میدهد:
نوشتن اقدامات در قالب کد، نسبت به استفاده از قطعه کدهای شبه-JSON، مزایای بیشتری دارد:
- قابلیت ترکیب (Composability): آیا میتوانید اقدامات JSON را در هم تودرتو کنید یا مجموعهای از اقدامات JSON را برای استفاده مجدد در آینده تعریف کنید، درست مثل زمانی که یک تابع پایتون تعریف میکنید؟
- مدیریت اشیاء (Object management): چطور میتوانید خروجی یک عمل مثل
generate_image
را در JSON ذخیره کنید؟ - عمومیت(Generality): کد برای بیان سادهی هر چیزی که میتوانید از یک کامپیوتر بخواهید انجام دهد، ساخته شده است.
- نمایش در دادههای آموزشی LLM: مقدار زیادی از اقدامات کد با کیفیت، قبلاً در دادههای آموزشی LLMها وجود دارد، به این معنی که آنها از قبل برای این کار آموزش دیدهاند!
معرفی smolagents: سادهسازی Agentها
فریمورک smolagents در هاگینگ فیس با این اهداف ساخته شده است:
✨ سادگی: منطق Agentها در حدود چند هزار خط کد خلاصه میشود (به این فایل مراجعه کنید). سعی شده پیچیدگیها را به حداقل رسانده و فقط از سادهترین مفاهیم استفاده شود!
پشتیبانی از Code Agents: عامل یا Agentهایی که به جای JSON، مستقیماً کد مینویسند. برای امنیت بیشتر، کدهای این Agentها را در محیطهای امن (sandbox) و با استفاده از E2B اجرا میکنند.
- علاوه بر این کلاس CodeAgent، همچنان از
ToolCallingAgent
استاندارد که اقدامات را به صورت JSON/متن مینویسد نیز پشتیبانی میگردد.
ادغام با Hub: میتوانید ابزارها را از Hub بارگیری کنید و یا در آن به اشتراک بگذارید.
با smolagents، میتوانید از هر LLM استفاده کنید: چه مدلهایی که در Hugging Face Hub هستند، چه مدلهای OpenAI و Anthropic، یا هر مدل دیگری که با LiteLLM سازگار باشد.
smolagents جایگزین transformers.agents است و در آینده، با منسوخ شدن transformers.agents، جایگزین آن خواهد شد.
ساخت یک Agent
برای ساخت یک Agent، حداقل به دو عنصر نیاز دارید:
- ابزارها: فهرستی از ابزارهایی که Agent به آنها دسترسی دارد
- مدل: یک LLM که حکم موتور Agent شما را ایفا میکند.
میتوان از هر LLMای استفاده کرد: یا مدلهای متنباز را با استفاده از کلاس HfApiModel
(که از API رایگان Hugging Face استفاده میکند، مثل مثال leopard ی که در بالا آورده شد) فراخوانی کرد، یا از LiteLLMModel
که از litellm بهره میگیرد استفاده کرد و از بین بیش از 100 LLM ابری مختلف، انتخاب کرد.
برای ابزار نیز، میتوانید به سادگی یک تابع با نشانهگذاری نوع (type hints) روی ورودیها و خروجیها ایجاد کرده، و از docstrings برای توضیح ورودیها استفاده کنید، و سپس از دکوراتور @tool
برای تبدیل آن به یک ابزار استفاده کنید.
در اینجا نحوه ساخت یک ابزار سفارشی که زمانهای سفر را از Google Maps دریافت میکند و نحوه استفاده از آن در یک Agent برنامهریزی سفر آمده است:
from typing import Optional from smolagents import CodeAgent, HfApiModel, tool @tool def get_travel_duration(start_location: str, destination_location: str, transportation_mode: Optional[str] = None) -> str: """Gets the travel time between two places. Args: start_location: the place from which you start your ride destination_location: the place of arrival transportation_mode: The transportation mode, in 'driving', 'walking', 'bicycling', or 'transit'. Defaults to 'driving'. """ import os # All imports are placed within the function, to allow for sharing to Hub. import googlemaps from datetime import datetime gmaps = googlemaps.Client(os.getenv("GMAPS_API_KEY")) if transportation_mode is None: transportation_mode = "driving" try: directions_result = gmaps.directions( start_location, destination_location, mode=transportation_mode, departure_time=datetime(2025, 6, 6, 11, 0), # At 11, date far in the future ) if len(directions_result) == 0: return "No way found between these places with the required transportation mode." return directions_result[0]["legs"][0]["duration"]["text"] except Exception as e: print(e) return e agent = CodeAgent(tools=[get_travel_duration], model=HfApiModel(), additional_authorized_imports=["datetime"]) agent.run("Can you give me a nice one-day trip around Paris with a few locations and the times? Could be in the city or outside, but should fit in one day. I'm travelling only with a rented bicycle.")
پس از چند مرحله جمعآوری زمانهای سفر و انجام محاسبات، Agent این پیشنهاد نهایی را ارائه میدهد:
One-day Paris bike trip itinerary: 1. Start at Eiffel Tower at 9:00 AM. 2. Sightseeing at Eiffel Tower until 10:30 AM. 3. Travel to Notre-Dame Cathedral at 10:46 AM. 4. Sightseeing at Notre-Dame Cathedral until 12:16 PM. 5. Travel to Montmartre at 12:41 PM. 6. Sightseeing at Montmartre until 2:11 PM. 7. Travel to Jardin du Luxembourg at 2:33 PM. 8. Sightseeing at Jardin du Luxembourg until 4:03 PM. 9. Travel to Louvre Museum at 4:12 PM. 10. Sightseeing at Louvre Museum until 5:42 PM. 11. Lunch break until 6:12 PM. 12. Planned end time: 6:12 PM.
پس از ساخت یک ابزار، به اشتراکگذاری آن در Hub نیز به همین سادگی است:
get_travel_duration.push_to_hub("{your_username}/get-travel-duration-tool")
میتوانید نتیجه را در این فضا ببینید. منطق ابزار را میتوانید در فایل tool.py در این فضا بررسی کنید.
همانطور که میبینید، این ابزار به یک کلاس تبدیل شده که از کلاس Tool ارثبری میکند. کلاسی، اساس و پایه تمام ابزارهای ماست.
مدلهای باز چقدر برای گردشکارهای عاملی(agentic workflowها) قدرتمند هستند؟
گروه هاگینگ فیس نمونههایی از CodeAgent
را با استفاده از چند مدل برتر ساختند و آنها را با این benchmark ارزیابی کردند. این بنچمارک، سوالاتی را از چند بنچمارک مختلف جمعآوری کرده تا چالشهای متنوعی را ایجاد کند.
میتوانید جزئیات بیشتر درباره تنظیمات Agentها و مقایسه Code Agentها با Tool Calling Agentها را در این benchmark ببینید (اسپویلر: Code Agentها عملکرد بهتری داشتند!).
این مقایسه نشان میدهد که مدلهای منبع باز اکنون میتوانند با بهترین مدلهای غیر اوپن سورس رقابت کنند!
دیدگاهتان را بنویسید