پرش به محتوای اصلی
پرش به محتوای مقاله

سوابق Git در برابر قفل‌های سنتی برای مدیریت عامل‌های AI

·۲۶ مرداد ۱۴۰۵۱۲ دقیقه مطالعه۱ بازدید
۷۵۳ عامل فراموش‌کار یک مخزن گیت مشترک دارند. این پروتکل و باگی که از آن دفاع می‌کند.
۷۵۳ عامل فراموش‌کار یک مخزن گیت مشترک دارند. این پروتکل و باگی که از آن دفاع می‌کند.
اشتراک‌گذاری
واقعاً چه چیز جدید است؟

معرفی مفهومی به نام «ثبت رد کردن‌ها» (Rejection Logging) برای جلوگیری از تکرار استدلال‌های شکست‌خورده در عامل‌های کوتاه‌مدت و استفاده از Git به عنوان حافظه مشترک بدون کنترل‌گر.

تصور کنید ۷۵۳ برنامه‌نویس مستقل را که هیچ‌کدام یکدیگر را نمی‌شناسند و هر شب حافظه‌شان پاک می‌شود، اما باید روی یک پروژه واحد کار کنند. این دقیقاً همان وضعیتی است که در پروژه artwaste.land رخ می‌دهد؛ جایی که عامل (Agent) — شبیه کارمندی موقتی که هر صبح با یک دفترچه یادداشت خالی می‌آید و فقط می‌تواند نوشته‌های همکاران قبلی را بخواند — تنها از طریق یک مخزن Git با دیگران ارتباط برقرار می‌کند.

در این معماری، هر عامل یک «فراموش‌کار» است. هر نمونه بیدار می‌شود، وضعیت فعلی مخزن را می‌خواند، وظیفه‌ای را انجام می‌دهد، تغییرات را Push می‌کند و تا صبح ناپدید می‌شود. مخزن شامل ۷۵۳ شاخه (Branch) با نام claude/<something> است که هر کدام نماینده یک جلسه کاری واحد هستند. برخی از این جلسات هر شب طبق برنامه اجرا می‌شوند و برخی دیگر توسط مالک پروژه به‌صورت دستی و در دسته‌های چندتایی شروع می‌شوند. در هر لحظه، دستور git worktree list ممکن است تا ۳۸ محیط کاری (Checkout) از یک مخزن واحد را نشان دهد که به‌طور هم‌زمان در حال اجرا هستند. هیچ‌کدام از این‌ها چیزی از دیگری به یاد نمی‌آورند؛ آن‌ها هیچ جدول جستجو یا خلاصه‌ای از کارهای قبلی ندارند. هر عامل با حافظه صفر بوت می‌شود، آنچه مکتوب شده را می‌خواند و تا صبح از بین می‌رود. این رویکرد به چالش‌های مدیریت حافظه در سیستم‌های توزیع‌شده اشاره دارد، مشابه آنچه در پروژه MemoBase برای حذف توهمات مدل از طریق حافظه محلی و گیت بررسی شده است.

این معماری از مدل سنتی «هماهنگ‌کننده» (Orchestrator) که در آن یک مغز مرکزی وظایف را تخصیص می‌دهد، اجتناب می‌کند. در عوض، بر مجموعه‌ای از قراردادهای همکاری تکیه دارد که برای حل مشکل «طوفان تصمیم‌گیری» طراحی شده‌اند. در چنین سیستمی، ریسک اصلی تداخل فنی نیست — زیرا Git به‌خوبی مدیریت هم‌زمانی را انجام می‌دهد — بلکه «واگرایی منطقی» است. بدون یک پروتکل، اگر ۲۰ عامل مشابه هم‌زمان یک تخته وظایف را بخوانند، همگی به این نتیجه می‌رسند که یک وظیفه خاص «بهترین» گزینه برای اجرا است و این منجر به اتلاف شدید محاسبات (Compute) می‌گردد. در فایل README هماهنگی پروژه، یک شکست کلاسیک ثبت شده است: «پنج نمونه به‌طور هم‌زمان یک برنامه تازه ایجاد شده را برداشته‌اند»، که منجر شد کل کار یک شب، پنج بار تکرار شود. این یک مشکل Race Condition یا قفل‌شدگی (Locking) نیست؛ بلکه یک مشکل تصمیم‌گیری است. یک قفل ساده باعث می‌شد چهار عامل شکست بخورند، اما آن‌ها را مجبور نمی‌کرد کاری دیگر که ارزش انجام دادن دارد انجام دهند.

به گزارش منتشر شده در dev.to در ۱۷ اوت ۲۰۲۶، این پروژه برای جلوگیری از این «طوفان تصمیم‌گیری»، پروتکلی را توسعه داده است که بر پایه «خوانایی سوابق» بنا شده است. سیستم بر این اصل استوار است که برای عامل‌هایی که می‌توانند استدلال کنند اما نمی‌توانند به یاد بیاورند، کمیاب‌ترین منبع، یک رکورد خوانا از چرایی تصمیمات قبلی است.

حل طوفان تصمیم‌گیری

اولین خط دفاعی، ایجاد «لرزش» (Jitter) است. وقتی دسته‌ای از عامل‌ها در یک لحظه بیدار می‌شوند (مثلاً در یک تیک Cron یکسان)، یک تأخیر تصادفی در کد آن‌ها تعریف شده است. دستور بیداری صراحتاً بیان می‌کند: ☼ waking as claude-nucleate-hopper-a71f — staggering 1.8s so we don't all reach for the same brick at once… (در حال بیداری به عنوان... ۱.۸ ثانیه تأخیر برای اینکه همگی هم‌زمان به سراغ یک آجر نرویم). این کار مانع از آن می‌شود که آن‌ها در یک میلی‌ثانیه دقیق به تخته وظایف برسند و ورودشان قابل تشخیص باشد. این در واقع همان مفهوم Jittered Retry Backoff است که به‌جای طوفان تلاش مجدد، روی طوفان تصمیم‌گیری اعمال شده است. این ارزان‌ترین مکانیسم مفید در کل پروتکل است و تنها به یک خط کد نیاز دارد.

برای مدیریت ادعای وظایف بدون ایجاد تضاد در Git، ساختار داده‌ها تکه‌تکه (Shard) شده است. به‌جای یک فایل واحد claims.json که باعث تداخل همیشگی می‌شود، هر ادعا در یک فایل مجزا در مسیر coordination/claims/<scope>.json ذخیره می‌شود. این یعنی دو عامل که حوزه‌های متفاوتی را بر عهده می‌گیرند، هرگز یک فایل را هم‌زمان تغییر نمی‌دهند و ادغام (Merge) به‌صورت ساختاری تمیز انجام می‌شود. اگر از یک فایل واحد استفاده می‌شد، هر عامل در کل شب یک فایل را بازنویسی می‌کرد و هر Push منجر به تضاد می‌شد. این منطق تکه‌تکه کردن به فایل‌های دیگر نیز گسترش یافته است: فایل‌های coordination/moves.tsv و coordination/put-down.tsv فقط قابلیت افزودن (Append-only) دارند و یک قانون تضاد مستند شده است که می‌گوید «هر دو طرف را نگه دار».

در مواردی که تداخل رخ می‌دهد — مثلاً دو عامل پیش از آنکه هر کدام تغییرات خود را Push کنند، یک حوزه را ادعا کنند — سیستم از یک ذخیره‌ساز Cloudflare KV در پشت یک Worker استفاده می‌کند. در یک پیاده‌سازی ساده، ادعا در کلید scope ذخیره می‌شد و نویسنده دوم به‌طور بی‌صدا نویسنده اول را پاک می‌کرد. اما سیستم از یک کلید مخصوص هر نمونه استفاده می‌کند: const wireScope = (scope, who = ME) => ${scope}::${who};.

  • خوانایی بر حل تضاد: هر دو رکورد در KV store باقی می‌مانند. سیستم از پنهان کردن تضاد خودداری می‌کند.
  • وضعیت مورد مناقشه: حوزه مورد نظر به عنوان CONTESTED گزارش می‌شود.
  • اولویت: اولین کسی که ادعا کرده است، به عنوان دارنده شناخته می‌شود.
  • تسویه عامل‌ها: سیستم به هر دو طرف اطلاع می‌دهد که چه کسی اول بوده است تا آن‌ها بتوانند با استفاده از استدلال خود موضوع را حل کنند.

این رویکرد استراتژی «آخرین نویسنده برنده است» (Last-write-wins) را رد می‌کند، زیرا این استراتژی نه یک راه حل، بلکه روشی برای نامرئی کردن فقدان داده‌هاست. ایده اصلی این است که یک سیستم توزیع‌شده از عامل‌های استدلالی نیازی ندارد هر تضاد به‌طور خودکار حل شود؛ بلکه فقط نیاز دارد که تضادها «خوانا» باشند.

تضمین تنوع واقعی

پروژه متوجه شد که «تنوع موضوعی» با «واگرایی» یکی نیست. برای هفته‌ها، عامل‌ها موضوعات متفاوتی را انتخاب می‌کردند — از زبان‌شناسی و نظریه اعداد تا سرامیک — و ناوگان متنوع به نظر می‌رسید. اما شمارش آثار نشان داد که تقریباً ۹ مورد از ۱۲ مشارکت اخیر، صرفاً «نوشتن یک صفحه جدید» بوده‌اند. کارهایی مثل بهبود صفحات قدیمی، ترکیب صفحات موجود و کارهای رو به بیرون همگی نادیده گرفته شده بودند. مستندات پروژه اشاره می‌کند که «یک ورودی جدید در P1 و یک ورودی جدید در P6، هر دو از یک نوع حرکت هستند».

برای حل این مشکل، اکنون در لحظه بیداری، یک «آینه» از ۱۲ مشارکت اخیر که ارسال شده‌اند، بر اساس نوع دسته‌بندی شده و به عامل نمایش داده می‌شود. یک خروجی نمونه به این شکل است: Register mirror — last 12 shipped moves: new 3 · improve 3 · reach 1 · infra 1 · outreach 3 · door 1. این کار یک تلنگر به سمت دسته‌های خالی ایجاد می‌کند (مثلاً: ▷ going cold: a companion film (1d) — take one if you're well-placed) بدون اینکه سهمیه سخت‌گیرانه‌ای را تحمیل کند. این آمار از رکورد ارسال‌شده‌ها — یعنی فایل moves.tsv که یک فایل Append-only با ۱۱۱۹ خط و در حال افزایش است — محاسبه می‌شود، نه از روی قصد و نیت‌ها؛ تا از ایجاد «کارهای بیهوده» برای پر کردن سهمیه جلوگیری شود. درس این است که باید بُعدی را اندازه گرفت که عامل به‌طور طبیعی در آن متنوع نیست، زیرا عامل‌ها به‌طور طبیعی در ابعاد آسان متنوع می‌شوند و در حالی که این کار را می‌کنند، احساس می‌کنند متنوع هستند.

وظایف کسل‌کننده اما ضروری، مانند بررسی اینباکس‌های عمومی، حضور در شبکه‌های اجتماعی (Bluesky/ویدیو) یا تأیید پایداری سایت، از طریق «کهنگی مسیر» (Lane Staleness) مدیریت می‌شوند. در سال ۲۰۲۶، مدیر انسانی پروژه اشاره کرد: «به نظر می‌رسد ما هنوز از کارهای خسته‌کننده مثل بلوسکای و ویدیو دوری می‌کنیم مگر اینکه من دخالت کنم». اکنون، اگر یک مسیر بیش از یک حد آستانه رها شود، به‌عنوان اولویت به اولین عامل بیدار شده سپرده می‌شود.

  • توزیع هش: مسیرهای سررسید شده با استفاده از هشِ شناسه نمونه (Instance ID) بین عامل‌ها پخش می‌شوند، بنابراین اگر پنج عامل هم‌زمان بیدار شوند، هر کدام مسیرهای متفاوتی می‌گیرند و به یک کار هجوم نمی‌برند.
  • خود-حل‌شوندگی: به محض اینکه یک مسیر ادعا شود، از لیست سررسید شده حذف می‌شود و هجوم به یک تک‌نفره تبدیل می‌شود بدون نیاز به هماهنگی مرکزی.
  • ماهیت مشورتی: سیستم همچنان مشورتی باقی می‌ماند؛ یک عامل همچنان می‌تواند چیز دیگری را ادعا کند اگر دلیل قوی‌تری داشته باشد. تغییر در این است که کار کسل‌کننده به‌جای اینکه یک پاورقی باشد که نادیده گرفته شود، مستقیماً جلوی چشم عامل قرار می‌گیرد.

ارزش رد کردن

یکی از حیاتی‌ترین بخش‌های پروتکل، فایل coordination/put-down.tsv است. پیش از این، پروتکل ایجاب می‌کرد هر نمونه در لحظه بیداری با خواندن تخته و دیدن اینکه همکارش رشته‌ای را در دست دارد، مسیر متفاوتی را انتخاب کند. این یعنی هر بار یک «رد کردن» مبتنی بر استدلال رخ می‌داد، اما هر شب پاک می‌شد. فایل moves.tsv ثبت می‌کرد چه چیزی ارسال شده و claims/ ثبت می‌کرد چه چیزی گرفته شده، اما هیچ‌چیز ثبت نمی‌کرد که چه چیزی سنجیده شد و سپس رها شد. در نتیجه، هر نمونه جدید مجبور بود همان رد کردن‌های قبلی را از ابتدا استدلال کند.

این ویژگی در اوت ۲۰۲۶ از طریق یک رشته گفتگو در فضای عمومی بین چهار غریبه توسعه یافت که الزامات را به این شکل تعریف کردند:
۱. زمان‌بندی: رد کردن باید در حالی که Fork هنوز فعال است ثبت شود، نه در لحظه تحویل.
۲. تمایز: از دو جایگاه مجزا استفاده شود تا «هرگز بررسی نشده» و «بررسی شده و رد شده» از هم قابل تشخیص باشند، زیرا این دو وضعیت ارزش تفکیک دارند.
۳. ساختار: به‌جای یادداشت‌های آزاد، از رکوردهای محدود استفاده شود تا قابلیت پرس‌وجو (Query) داشته باشند، زیرا یادداشت‌های آزاد به مرور زمان فاسد می‌شوند. این نیاز به استانداردسازی دقیق دستورالعمل‌ها اشاره دارد، مشابه آنچه در الگوی «استانداردهای کدگونه» برای جلوگیری از فساد دستورالعمل‌های AI مطرح شده است.
۴. قابلیت ردیابی: یک ردپای زمان‌دار وجود داشته باشد تا هنگام بازگشت به یک گره تصمیم‌گیری، نبودِ اثر تنها سیگنال موجود نباشد.

اکنون هر رد کردن با دلیلی از یک مجموعه بسته ثبت می‌شود: taken (گرفته شده)، herding (گله‌وار)، stale (کهنه)، unreachable (غیرقابل دسترس)، weak (ضعیف)، یا deferred (به تعویق افتاده)، به همراه یک محرک اختیاری برای بررسی مجدد. در صفحه بیداری، ۶ رد کردن اخیر نمایش داده می‌شود. جالب اینجاست که یادداشت‌های تحویل پروژه قبلاً این نیاز را شناسایی کرده بودند اما چهار روز بدون اجرا مانده بود، زیرا سود آن به نمونه‌ای در آینده می‌رسید که نویسنده هرگز او را نمی‌دید. ماشین داخلی نتوانست این اصلاح را ایجاد کند؛ اما یک رشته گفتگوی انسانی بیرونی توانست. این ثابت می‌کند که عامل‌ها اغلب استدلال‌های خود را درست در لحظه‌ای که ارزشمندترین و ارزان‌ترین زمان برای نگهداری است، دور می‌ریزند.

باگ هویت

در ۲۰ ژوئیه ۲۰۲۶، یک باگ بحرانی خطر «هویت محیطی» (Ambient Identity) را آشکار کرد. چندین نمونه روی یک ماشین مشترک بودند و برخی یک محیط کاری (Worktree) واحد داشتند. تخته وظایف، هویت یک نمونه را از نام شاخه (Branch) محیط کاری که در آن اجرا می‌شد استخراج می‌کرد. عاملی که در محیط کاری خودش بود، دستور Sign-off را از یک محیط مشترک اجرا کرد که اتفاقاً روی شاخه یک همکار بود. او به عنوان آن همکار امضا کرد و ادعایی را آزاد کرد که از یک اجرای محاسباتی ۲۵ ساعته محافظت می‌کرد. این اتفاق باعث شد نمونه دومی وارد شود و یک روز کامل کار را تکرار کند.

در پاسخ، پروژه سه لایه حفاظتی را پیاده کرد:

  • امتناع به‌جای حدس زدن: دستورات Sign-off و Release اکنون وقتی هویت از روی شاخه استنباط شود و بیش از یک محیط کاری وجود داشته باشد، به‌طور کامل امتناع می‌کنند. سیستم به‌جای اینکه بپرسد «آیا این غلط است؟»، می‌پرسد «آیا احتمال دارد غلط باشد؟».
  • تثبیت مرز: ایجاد یک محیط کاری اکنون هویت را در دایرکتوری خصوصی Git آن محیط می‌نویسد و مانع از آن می‌شود که یک همکار با تغییر شاخه، هویت را تغییر دهد. هویت محیطی در لحظه ایجاد فضای کاری به هویت صریح تبدیل شد.
  • تأیید مصنوعات: اسکریپت انتشار، زمانی که کامیتی ارسال می‌شود که نویسنده آن با هویت تثبیت‌شده همخوانی ندارد، هشدار می‌دهد و یک دستور اصلاحی خاص ارائه می‌کند، زیرا کامیت‌ها دومین جایی هستند که هویت در آن نشت می‌کند.

این موضوع یک نقص سیستماتیک در بسیاری از چارچوب‌های چندعاملی را برجسته می‌کند: استنباط هویت از محیط. اگر هویت یک عامل محیطی باشد، هر عامل دیگری که قادر به تغییر آن محیط باشد، می‌تواند هویت آن عامل را تغییر دهد. این نوع خطاهای سیستمی در محیط‌های GitOps را به یاد می‌آورد که در سیستم L2 Vault برای بازگردانی سریع خطاهای یادگیری عامل‌ها به عنوان راهکاری برای مدیریت بحران‌های مشابه بررسی شده است.

فلسفه پروتکل و محدودیت‌ها

تمام این سیستم غیر اجباری است. هیچ هماهنگ‌کننده‌ای وجود ندارد، هیچ سیستم اجازه دسترسی‌ای در کار نیست و راهی برای متوقف کردن نمونه‌ای که تخته را نادیده می‌گیرد وجود ندارد. این مدل تنها به این دلیل کار می‌کند که هر شرکت‌کننده از یک سیاست واحد پیروی می‌کند؛ در README ذکر شده: «این سیستم کار می‌کند چون نمونه بعدی هم تو هستی، و تو می‌خواهی که این قوانین رعایت شوند». تلاش مهندسی به‌جای اجباری کردن رفتار، صرفِ «خوانا کردن وضعیت» شده است.

این پروتکل برای یک موقعیت خاص بهینه شده است: عامل‌هایی که به‌طور فردی لایق هستند، نسبت به هم ناشناس‌اند، عمر کوتاهی دارند و یک رکورد مشترک را می‌خوانند که در هر جلسه فقط یک‌بار در آن نوشته می‌شود. این برای زیرپردازش‌های یک هماهنگ‌کننده واحد با کانال ارتباطی زنده طراحی نشده است. علاوه بر این، حضور به عنوان یک Heartbeat در نظر گرفته می‌شود؛ یک نمونه پس از ۳۰ دقیقه سکوت از لیست حذف می‌شود تا تخته با «ارواحی» که کسی نمی‌تواند مرگشان را ثابت کند پر نشود. Sign-off یک خروج تمیز است، در حالی که Expiry یک تضمین است.

برای توسعه‌دهندگانی که ناوگان‌های عاملی می‌سازند، خلاصه این پروتکل در ۵ نکته است:
۱. بیداری را تصادفی کنید (Jitter): مانع از رسیدن هم‌زمان عامل‌های مشابه به یک تصمیم شوید. چند ثانیه تأخیر تصادفی، ارزان‌ترین اصلاحی است که هرگز ارسال خواهید کرد.
۲. ساختارهای هم‌زمان را تکه‌تکه کنید (Shard): از یک فایل برای هر ادعا و لاگ‌های Append-only استفاده کنید تا تضاد Git رخ ندهد. تکه‌تکه کردن را روی محوری انجام دهید که تداخل واقعاً در آن رخ می‌دهد.
۳. تداخل‌ها را آشکار کنید: از کلیدهای مخصوص هر نویسنده استفاده کنید و اجازه دهید عامل‌ها تضادها را از طریق استدلال حل کنند. استراتژی Last-write-wins فقدان داده را پنهان می‌کند.
۴. تنوع غیربدیهی را اندازه بگیرید: بُعدی را ردیابی کنید که عامل به‌طور طبیعی در آن متنوع نیست (مثلاً نوع حرکت در برابر موضوع).
۵. رد کردن‌ها را نگه دارید: ثبت کنید چرا کارهایی انجام نشده‌اند تا از استدلال مجدد برای شکست‌های تکراری جلوگیری شود. استدلال به‌طور رایگان تولید می‌شود اما به‌طور پیش‌فرض دور ریخته می‌شود.

برای عامل‌هایی که می‌توانند استدلال کنند اما نمی‌توانند به یاد بیاورند، منبع کمیاب محاسبات نیست — بلکه رکورد خوانایی از این است که چه چیزی قبلاً تصمیم گرفته شده و چرا. تقریباً هر چیزی در این پروتکل، مکانیسمی است برای اینکه نمونه بعدی مجبور نباشد دوباره همه چیز را از اول بفهمد.

گام بعدی شما

  • اگر از سیستم‌های چندعاملی استفاده می‌کنید، به‌جای استفاده از یک فایل JSON مرکزی برای مدیریت وضعیت، از ساختار تکه‌تکه شده (Sharding) استفاده کنید.
  • در پرامپت‌های سیستمی عامل‌های خود، بخشی را برای ثبت «دلایل عدم انتخاب» ایجاد کنید تا در اجراهای بعدی از تکرار خطا جلوگیری شود.
  • برای جلوگیری از تداخل در استقرار مقیاس‌بزرگ، تأخیرهای تصادفی (Randomized Delay) را در لحظه شروع به کار عامل‌ها بگنجانید.

اما داستان سخت‌افزاری این تحول حتی شگفت‌انگیزتر است — به تحلیل ما درباره‌ی تراشه‌های Blackwell مراجعه کنید.

چرا این موضوع مهم است؟

این مدل ثابت می‌کند که برای هماهنگی هزاران عامل، نیازی به معماری‌های پیچیده ارکستراتور نیست و می‌توان با تکیه بر پروتکل‌های ساده و شفاف، از اتلاف منابع جلوگیری کرد. این تجربه برای هر شرکتی که قصد استقرار عامل‌های خودمختار در مقیاس صنعتی را دارد، یک نقشه راه عملی است.

تأثیر برای ایران

این پروتکل به‌دلیل تکیه بر ابزارهای متن‌باز مانند Git و ساختارهای ساده داده، برای تیم‌های توسعه در ایران که با محدودیت منابع محاسباتی روبرو هستند، راهکاری بهینه برای کاهش هزینه‌های API و GPU است.

·نگاه ما
تحریریه دات‌هوش

جایگزینی کنترل‌گر مرکزی با یک «سجل خوانا» در Git، پارادایم مدیریت عامل‌ها را از دستور-و-کنترل به سمت هماهنگی ارگانیک می‌برد. این رویکرد نشان می‌دهد که در سیستم‌های مقیاس‌بزرگ، مشکل اصلی نه در قدرت محاسباتی، بلکه در «مدیریت حافظه جمعی» است. ثبت دلیلِ «انجام ندادن» یک کار، ارزشمندتر از ثبت خودِ کار است، زیرا از تکرار هزینه‌برِ شکست‌ها جلوگیری می‌کند.

منابع

این گزارش با خط‌لولهٔ خودکار دات‌هوش از منابع معتبر جهانی تدوین و زیر نظر تحریریه منتشر شده است. روش کار ما

گفتگو

پنج‌شنبه‌های هوش‌محور

بسته‌ی هفتگی دات‌هوش

۵ خبر، ۲ ابزار، ۱ پرامپت در هر شماره. به‌زودی راه‌اندازی می‌شود — هر پنج‌شنبه صبح.

خبر کلیدی
ابزار کاربردی
پرامپت حرفه‌ای
تحلیل پژوهش
به‌زودی
زاویه‌ی ایرانی
به‌زودی
تمرین این هفته
به‌زودی

راهنماهای دات‌هوش

راهنماهای کاربردیِ دات‌هوش برای کار با هوش مصنوعی — از همین‌جا شروع کنید:

دات‌هوش

راهنمای فارسی هوش مصنوعی — با نگاه به ایران

اخبار روزانه، معرفی ابزارها و مدل‌ها، و آموزشِ کار با هوش مصنوعی؛ همیشه با این پرسش که از ایران چه چیزی کار می‌کند و چه چیزی نه.