تصور کنید به جای زیرساختهای پیچیده که معمولاً برای هماهنگی میان یک ناوگان از رابطهای خط فرمان (CLI) عاملهای هوش مصنوعی مورد نیاز است، تنها از یک دایرکتوری مشترک از فایلهای اتمیک استفاده کنید. این الگو که ابتدا در hexisteme notes به عنوان بخشی از یک سری مقالات درباره ساخت و اجرای ناوگان عاملهای هوش مصنوعی منتشر شد، به توسعهدهندگان اجازه میدهد تا عاملهای مستقل را بدون تحمیل سربار یک واسط پیام (Message Broker) یا یک چارچوب سختگیرانه درون-پروسهای، سازماندهی کنند.
در حال حاضر، اکثر توسعهدهندگان بین دو گزینه افراطی گیر کردهاند: یا از چارچوبهای درون-پروسهای مانند LangGraph یا حلقههایی به سبک AutoGPT استفاده میکنند که کارگران (Workers) را به یک پروسه و یک زبان برنامهنویسی واحد محدود میکند، یا به سراغ واسطهای پیام سنگین مانند Redis، Kafka یا RabbitMQ میروند. برای یک ناوگان که توسط یک اپراتور واحد اداره میشود، این ابزارها اغلب بیشتر از آنکه مشکل را حل کنند، اصطکاک عملیاتی ایجاد میکنند. یک واسط پیام، در واقع زیرساخت اضافهای است که فرد باید آن را اجرا کند، ایمن سازد و مانیتور نماید. در عوض، سیستمی را تصور کنید که در آن وضعیت هماهنگی به سادگی یک پوشه روی دیسک شماست؛ سیستمی که فارغ از زبان برنامهنویسی است، در برابر ریاستارتها مقاوم است و تنها با یک دستور ساده ls قابل عیبیابی است.
سازوکار عملیاتی
این معماری بر پایه یک پروسه «رهبر» (Conductor) است که یک دایرکتوری مشترک را مدیریت میکند و این دایرکتوری در واقع به عنوان «باس» (Bus) عمل میکند. برای اجرای یک هدف، رهبر ابتدا آن هدف را به یک گراف جهتدار بدون دور (DAG) از زیر-وظایف تقسیم میکند؛ برای مثال، توالی عملیاتی ممکن است از «جمعآوری» $ \rightarrow $ «روایت» $ \rightarrow $ «ساخت» حرکت کند. این رویکرد در مدیریت جریانهای کاری پیچیده، یادآور استراتژیهای بهینهسازی در سیستمهای چندعاملی است، مشابه آنچه در پروژه Clioloop برای ترکیب مدلهای ارزانقیمت جهت رقابت با مدلهای پیشرو مشاهده میکنیم.
برای هر زیر-وظیفه آماده، رهبر یک فایل Task را در باس مینویسد که با برچسب قابلیت (Capability) مورد نیاز آن وظیفه، علامتگذاری شده است. سپس رهبر با استفاده از یک بازه انتظار کوتاه (Backoff)، برای یافتن فایل Result متناظر، پوشه را نظارت (Poll) میکند. زمانی که نتیجه جذب و اعتبارسنجی شد، رهبر زیر-وظایههای بعدی در گراف را آزاد میکند.
برای تضمین یکپارچگی دادهها، سیستم «نوشتارهای اتمیک» را اجباری میکند. از آنجا که دستور rename در سیستمهای فایل POSIX اتمیک است، یک خواننده یا کل فایل را میبیند یا هیچچیز را، و هرگز با یک رکورد نیمهکاره مواجه نمیشود. پیادهسازی فنی این منطق به صورت زیر است:
# atomic publish — a reader never sees a partial record
def publish(path, record):
tmp = path.with_suffix(".tmp")
tmp.write_text(record.model_dump_json())
tmp.rename(path) # atomic on POSIX
حلقه تکرار رهبر از یک ترتیب توپولوژیک پیروی میکند: وظیفه را در مسیر bus / {task.id}.task.json منتشر میکند، برای نتیجه در مسیر bus / {task.id}.result.json نظارت میکند و در نهایت خروجی را جذب میکند.
جزئیات طراحی
- وضعیت در برابر رویداد: یک باس رویداد (Event Bus) مبتنی بر مدل «Push» است؛ تولیدکنندگان رویدادهای گذرایی را منتشر میکنند و هر کسی که در آن لحظه گوش نمیدهد، آنها را از دست میدهد. اما یک باسِ کاری مبتنی بر فایل، «وضعیت» (State) است؛ به این معنا که رکوردهای Task و Result فایلهای بادوامی هستند که تا زمان مصرف باقی میمانند. همانطور که در یادداشتهای hexisteme آمده است: «حقیقت در وضعیت است، رویدادها تنها شایعهاند». عاملی که دیرتر شروع به کار کند یا در میانهی اجرا ریاستارت شود، باز هم وظیفه خود را منتظر در پوشه مییابد.
- مسیریابی مبتنی بر قابلیت: رهبر به صورت سختافزاری دستور نمیدهد که «گام ۲ به عامل X برود». در عوض، هر عامل قابلیتهای خود را اعلام (Advertise) میکند و هر زیر-وظیفه، قابلیت مورد نیازش را اظهار میکند. رهبر در زمان اعزام (Dispatch)، با یافتن یک عامل سالم، آنها را تطبیق میدهد. این امر اجازه میدهد ناوگان ناهمگون باشد؛ افزودن یا حذف یک عامل نیازی به ویرایش نمودار سیمکشی ندارد.
- اعتبارسنجی سختگیرانه: خروجی عاملها به عنوان ورودی ناموثق تلقی میشود که از یک مرز عبور کرده است. رکوردهای Task و Result توسط یک طرحواره کوچک pydantic تایپ شدهاند. هر نتیجه در لبهی ورود تجزیه و نرمالسازی میشود تا مشکلاتی مانند تفاوت در حروف بزرگ و کوچک بین فرمتهای انتقال و Enums داخلی برطرف شود.
- منشأ و شواهد: ادعاهای کلیدی که باری از مسئولیت دارند، باید شامل یک برچسب منشأ (Provenance) باشند. اگر ادعایی با برچسب
FACTبرسد اما فاقد شناسههای شواهد (Evidence IDs) باشد، در همان مرحلهی تجزیه (Parse) رد میشود، نه اینکه کورکورانه مورد اعتماد قرار گیرد.
مدیریت شکست و تخریب تدریجی
یکی از حیاتیترین رفتارهای یک ناوگان در حال توسعه این است که نبود یک عامل نباید باعث شکست کل اجرا شود. اگر زیر-وظیفهای نیازمند قابلیتی باشد که هیچ عامل سالمی در حال حاضر آن را ارائه نمیدهد، رهبر کرش نمیکند. در عوض، آن گره را به عنوان «پریده» (Skipped) علامت میزند (و خطای worker_absent را ثبت میکند) و بقیهی گراف را ادامه میدهد تا خروجی را از هر آنچه کامل شده است، ترکیب کند.
در روز اول، زمانی که ممکن است اکثر عاملها هنوز وجود خارجی نداشته باشند، رهبر همچنان از ابتدا تا انتها اجرا شده و یک خروجی ناقص تولید میکند. این مکانیسم، گزارش تخطيها (Skip Log) را به یک لیست دقیق از کارهایی تبدیل میکند که توسعهدهنده باید در مراحل بعدی بسازد. در اینجا به جای کرش کردن، یک «شکاف» (Gap) گزارش میشود.
محدودیتها و مرزها
مسیریابی مبتنی بر قابلیت یک لبهی تیز دارد: نبودِ یک شرط توقف در مسیریابی مجدد. اگر گرهی به «هر عاملی که قابلیت C را دارد» ارجاع شود و نتایج بهطور مکرر در اعتبارسنجی شکست بخورند، یک رهبر ساده ممکن است وارد یک حلقهی بیانتها شود. برای حل این مشکل، سیستم نیازمند یک «بودجه تلاش» (Attempt Budget) صریح برای هر گره و یک نتیجهی «نامه-مرده» (Dead-letter) است. دوام (Durability) و جداسازی (Decoupling) پیروزیهای اصلی این مدل هستند، اما یک سیاست بازبینی محدود (Bounded Retry)، هزینه لازم برای دستیابی ایمن به این مزایا است.
این مدل برای ناوگانهای کوچک و ناهمگونی که وظایفشان ثانیهها تا دقایق زمان میبرد و هماهنگی توسط یک اپراتور انجام میشود، ایدهآل است. این روش جایگزینی برای سیستمهای با توان عملیاتی بالا و تأخیر کم که نیاز به توزیع گسترده (Massive Fan-out) دارند، نیست. در آن موارد، تأخیرِ نظارت (Polling) روی سیستم فایل بسیار کند خواهد بود و یک باس پیام واقعی ابزار درست است. برای یک ناوگان تکنفره، درد اصلی سربار عملیاتی است؛ و دایرکتوریای از فایلهای اتمیک این بار را برمیدارد.
گام بعدی شما
- اگر از Redis برای هماهنگی عاملهای محلی استفاده میکنید، سعی کنید یک پروتوتایپ ساده با پوشه و فایلهای JSON بسازید تا پیچیدگی زیرساختی را کاهش دهید.
- برای تضمین پایداری، از کتابخانه pydantic برای تعریف سختگیرانه ورودی و خروجی هر عامل استفاده کنید.
- یک بودجهی تلاش (Retry Limit) برای هر گره در گراف وظایف تعریف کنید تا از حلقههای تکرار بینهایت جلوگیری شود.
اما داستان سختافزاری این تحول حتی شگفتانگیزتر است — به تحلیل ما دربارهی تراشههای Blackwell مراجعه کنید.




گفتگو