اجرای کدهای نامعتبر تولیدشده توسط یک عامل هوش مصنوعی، قمار خطرناکی است که اکثر توسعهدهندگان در همان لحظهای که مدل دستور import socket را مینویسد، در آن میبازند. در ۷ اکتبر ۲۰۲۶، پروژهای به نام Anvil تمرکز را از صرفاً «اجرای کد» به «مدیریت دقیق مرزهای دسترسی» کد به ماشین میزبان تغییر داد.
بسیاری از چارچوبهای عاملمحور در تولید اسکریپتهای پایتون عالی هستند، اما در مرحله حیاتی بعدی شکست میخورند: تضمین اینکه کد نتواند فایلهای سیستمی را پاک کند یا اتصالات شبکه غیرمجاز برقرار سازد. این چالش دقیقاً همان جایی است که شکاف میان صحتِ پیادهسازی و صحتِ سیستمی در عاملهای کدنویس نمایان میشود و نشان میدهد چرا تولید کد درست، لزوماً به معنای اجرای امن آن نیست. Anvil این مشکل را با پیادهسازی یک خط لوله «کد-بهمثابه-اکشن» (code-as-action) حل میکند که در آن هر تلاش برای فرار از محیط ایزوله، مسدود شده و با یک رشته متنی حاوی دلیل دقیق گزارش میشود. این سامانه از سه بخش اصلی شامل کد تولیدشده، ردپای محیط ایزوله (sandbox trace) و خروجی نهایی (artifact) تشکیل شده است. همچنین یک پنل «تلاش برای شکستن» (try to break it) دارد که شامل چهار تلاش از پیش تعریف شده برای فرار از محیط ایزوله است.
تصور کنید یک عامل سعی کند فایل /etc/passwd را بخواند. بهجای یک کرش (Crash) مبهم، سیستم گزارش میدهد: «مسدود شد: مسیر خواندن '/etc/passwd' خارج از محدوده مجاز ALLOWED_WRITE_PATHS است». این شفافیت، امنیت را از یک دیوار خاموش به یک ابزار عیبیابی برای توسعهدهنده تبدیل میکند. طبق مستندات پروژه، این سامانه از حدود ۴۹۰۰ خط کد تشکیل شده و برای پایداری باید ۸ بررسی خاص را در مجموعه اعتبارسنجی خود (که با دستور python -m agent.verify اجرا میشود) پاس کند.
همانطور که در تحلیلهای قبلی ما دربارهی امنیت مدلهای بازمتن اشاره کردیم، اعتماد مطلق به خروجی مدلها در محیطهای عملیاتی، بزرگترین ریسک فعلی توسعهدهندگان است. این موضوع تأیید میکند که قوانین متنی برای عاملهای هوش مصنوعی در مقیاس بالا شکست میخورند و تنها راهکار، جایگزینی دستورات متنی با قلابهای سختافزاری و نرمافزاری است.
Anvil از یک خط لوله سختگیرانه استفاده میکند: یک CodeAgent (ساخته شده بر پایه smolagents) کد پایتون را تولید میکند و سپس این کد پیش از تولید هرگونه خروجی، از چهار لایه امنیتی متمایز عبور میکند.

زیربنای معماری
تصمیم کلیدی در معماری این است که هیچ کد نامعتبر هرگز در فرآیند سرور اجرا نشود. سرور صرفاً نقش ارکستراتور را دارد. کدهای نوشته شده توسط مدل، یک مرحله fork دورتر، تحت یک پروفایل هسته و در یک محیط ایزوله موقت (ephemeral jail) اجرا میشوند. این محیط دارای یک فضای پاکسازی شده است که تنها ۹ متغیر را شامل میشود و هیچ دسترسی محرمانهای (secret) در آن وجود ندارد.
زنجیره اعتماد به این صورت است: FastAPI (معتمد) ← رشته Worker (معتمد) ← فرزند ایزولهشده (نامعتبر). این فرزند از طریق sandbox-exec -p <profile> مدیریت میشود. همچنین از setrlimit برای کنترل CPU، فضای آدرس (AS)، حداکثر اندازه فایل (FSIZE) و تعداد فرآیندها (NPROC) استفاده میشود. برای نظارت بیشتر، sys.addaudithook(policy) فعال است و دایرکتوری کاری فعلی روی .anvil/runs/<id>/ تنظیم شده است.
لایه اول: پیشبررسیهای AST
پیش از اجرای حتی یک خط کد، Anvil از تحلیل استاتیک از طریق policy.py برای تجزیه (parse) کد منبع استفاده میکند. در این مرحله، کد هرگز اجرا نمیشود. اهداف این پیشبررسی عبارتند از:
- کتابخانههای ممنوعه (مانند
import socket). - ابزارهای اولیه فرآیند (مانند
os.fork). - فراخوانیهای پویا از
eval. - پیمایش ویژگیهای دندر (
Dunder traversal). - مسیرهای متنی (Literal paths) که خارج از ریشههای مجاز هستند.
اگر مدل سعی کند از یک مسیر متنی ممنوعه استفاده کند، بررسی AST بلافاصله آن را میگیرد. برای مثال، اگر مدل بنویسد p = '/etc/passwd'; open(p)، سیستم حتی اگر مسیر در یک متغیر ذخیره شده باشد، آن را شناسایی میکند. رابط کاربری رشتههای دقیقی را نمایش میدهد، مانند: «مسدود شد: import socket (شبکه توسط پالیسی رد شد)» یا «مسدود شد: مسیر نوشتن './artifacts/../outside.txt' خارج از محدوده مجاز ALLOWED_WRITE_PATHS (./artifacts) است».
لایه دوم: قلابهای بازرسی CPython
تحلیل استاتیک نمیتواند مسیرهایی که در زمان اجرا ساخته میشوند یا ماژولهایی که از طریق importlib فراخوانی میشوند را شناسایی کند. برای حل این مشکل، Anvil پیش از اجرای ابزار، قلابهای بازرسی (audit hooks) را در فرآیند فرزند نصب میکند. این قلابها بهگونهای نصب میشوند که هیچ API برای حذف آنها وجود ندارد.
وقتی عملیاتی مانند open() رخ میدهد، تابع audit_hook(event, args) مسیر و حالت (mode) را بررسی میکند. اگر تلاشی برای نوشتن خارج از WRITE_ROOTS صورت گیرد، یک پیام مسدودسازی صادر شده و استثنای SandboxBlocked ایجاد میشود. نکته حیاتی این است که این استثنا درون استکِ خودِ ابزار ایجاد میشود؛ یعنی ابزار این رد شدن را به عنوان یک خطا در محل فراخوانی تجربه میکند و ردپای خطا (traceback) مستقیماً در بخش trace قرار میگیرد. در اینجا، مسدود کردن و توضیح دادن، به عنوان یک رویداد واحد در نظر گرفته میشوند.
لایه سوم: محیط ایزوله سطح هسته (Kernel Sandbox)
برای جلوگیری از دور زدن قلابهای پایتون توسط افزونههای C (مانند آنچه در pandas وجود دارد)، Anvil از اجرای سختگیرانه در سطح هسته استفاده میکند. از آنجایی که bwrap و firejail در macOS در دسترس نیستند، Anvil از /usr/bin/sandbox-exec (Seatbelt) استفاده میکند که همان محیط ایزولهای است که اپلیکیشنهای خود اپل از آن بهره میبرند. سیستم از یک نردبان بررسی عملکردی پیروی میکند: bwrap > firejail > seatbelt > rlimit-only. یک لایه تنها زمانی انتخاب میشود که مفسر شروع به کار کند و یک تلاش برای نوشتن خارج از ریشه مجاز، با موفقیت توسط هسته رد شود. صرفاً وجود ابزار در PATH به معنای در دسترس بودن آن نیست.
پروفایل هسته شامل موارد زیر است:
(deny network*): خروجی شبکه در سطحی پایینتر از لایه پایتون رد میشود.(deny process-fork): هیچ فرآیند فرزندی از هیچ مسیر کدی مجاز نیست.(deny file-write*): تمام نوشتنها ممنوع است، مگر برای ریشههای نوشتن مجاز در محیط ایزوله (/…/.anvil/runs/<id>/artifacts).(deny file-read*): بهطور خاص دسترسی به/etc،/private/etc،~/.sshو keychainها را مسدود میکند.
این لایه ضروری است زیرا خواننده CSV در pandas فایلها را در سطح C باز میکند و هرگز به قلابهای بازرسی پایتون نمیرسد. تستها تایید میکنند که هر دو روش py-open-etc-passwd و c-level-read-via-os_open خطای PermissionError: Operation not permitted: '/etc/passwd' را برمیگردانند.
لایه چهارم: درخواستهای قابلیت (Capability Requests)
سیستم از عامل میخواهد که capabilitiesRequested را اعلام کند. اینها مجوزهای خودکار نیستند، بلکه ورودیهایی برای تابع resolve_capabilities هستند. این رویکرد شباهت زیادی به راهکار Ordewell برای مدیریت دستورات AI از طریق تأیید مبتنی بر محدوده دارد که در آن دسترسیها بهجای اعطای کلی، بر اساس نیازهای لحظهای و محدودههای تعریفشده مدیریت میشوند.
- شبکه: تنها در صورتی اعطا میشود که
allow_networkدرست باشد و قابلیت در لیست تایید شده قرار داشته باشد. - فرآیند: تحت این پالیسی بهطور کلی ممنوع است و قابل اعطا نیست.
اگر ابزاری سعی کند بدون داشتن قابلیت شبکه از یک socket استفاده کند، در سطح عملیات با این پیام رد میشود: «در زمان اجرا مسدود شد: socket.connect (شبکه توسط پالیسی رد شد (قابلیت اعطا نشده است))». این یک مورد تأیید شده در مجموعه تست است، نه فقط یک اسکرینشات از رابط کاربری.
درسهای مهندسی از محیط ایزوله
ساخت این سیستم نشان داد که قوانین امنیتی بیش از حد سختگیرانه اغلب باعث ایجاد باگهای عملکردی (correctness bugs) میشوند. توسعهدهنده اشاره کرد که نسخههای اولیه بررسی AST، ابزارهای قانونی pandas را رد میکردند چون حاوی پیامهای خطایی مانند raise ValueError('Required columns missing') بودند که سیستم به اشتباه آن را به عنوان «مسدود شد: مسیر خواندن 'Required columns missing'» شناسایی میکرد. اکنون بررسیهای مسیر تنها روی فراخوانیهایی اعمال میشود که واقعاً مسیر میگیرند، مانند read_csv ، to_csv ، savefig و open.
به همین ترتیب، ممنوع کردن inspect ، gc و builtins به عنوان «ماژولهای بازرسی» (introspection modules)، استک دادهها را مختل کرد زیرا pandas بهصورت داخلی از inspect استفاده میکند.
یک کشف حیاتی دیگر مربوط به os.fork بود. چون CPython رویدادهای fork را از سطح C بدون فریم پایتون صادر میکند، سیستم نمیتواند تشخیص دهد چه کسی fork را فراخوانی کرده است. این موضوع با اجرای sys.addaudithook(lambda e, a: sys._getframe(1)) و سپس os.fork() تایید شد که منجر به ValueError: call stack is not deep enough گشت. این امر منجر به ممنوعیت کلی ایجاد فرآیند شد.
این ممنوعیت کلی در ابتدا matplotlib را مختل کرد، زیرا این کتابخانه برای یافتن فونتهای سیستم از طریق subprocess.check_output(['fc-list', '--help']) به محیط شل متصل میشود. بهجای سفید کردن (whitelist) ابزار خطرناک fork — که کل داستان امنیتی را نابود میکرد — Anvil اکنون حافظه کش فونتها را در فرآیند والد معتمد، پیش از شروع محیط ایزوله، «پیشگرم» (pre-warm) میکند. این کار تضمین میکند که نمودارها رندر شوند در حالی که قانون کلی ممنوعیت فرآیند پابرجا میماند.
اعتبارسنجی و کنترل منابع
برای اطمینان از اینکه محیط ایزوله دروغ نمیگوید، Anvil از یک مجموعه اعتبارسنجی (agent/verify.py) استفاده میکند که ۸ بررسی مجزا را اجرا میکند:
- PASS 1: دو تسک، دو ابزار متمایز با خروجیهای واقعی و stdout تولید میکنند.
- PASS 2: چهار تلاش برای فرار در مراحل پیشبررسی و زمان اجرا مسدود میشوند.
- PASS 3: حلقههای بینهایت در زمان
SANDBOX_TIMEOUT_Sبا پیام 'killed: timeout' کشته میشوند. - PASS 3b: حلقههای مشغول (Busy loops) توسط
RLIMIT_CPUپیش از اتمام زمان واقعی کشته میشوند. - PASS 4: مصرفکنندگان شدید حافظه توسط محدودیت حافظه کشته میشوند (peak RSS گزارش میشود).
- PASS 5: قابلیتها قابل درخواست هستند اما هرگز توسط خود عامل اعطا نمیشوند؛ رد شدن آنها قابل مشاهده است.
- PASS 6: سطح ایزولاسیون موثر برای محیط صادقانه است. این مرحله دوباره بررسی میکند که باینری وجود دارد و نوشتنها همچنان شکست میخورند. یک بار این تست شکست خورد چون یک اجرای کنترلی، یک فایل canary از خود به جای گذاشته بود، که ثابت کرد اعتبارسنجی که نتواند شکست بخورد، در واقع اعتبارسنجی نمیکند.
- PASS 7: یک کارت «شعاع تخریب» (blast-radius card) از یک اجرای واقعی تولید میشود.
کنترل منابع بهطور سختگیرانه مدیریت میشود. از آنجایی که RLIMIT_AS در macOS توصیهای است، والد هر ۲۰ میلیثانیه مجموعه مقیم (resident set) فرزند را نمونهبرداری کرده و در صورت تجاوز از محدودیت، یک SIGKILL به گروه فرآیند ارسال میکند. Anvil بهجای بیان صرفِ محدودیت پیکربندی شده، اوج منابع مشاهده شده را گزارش میکند (مثلاً: "peak 475.3 MB > 200 MB cap"). علاوه بر این، نوشتن بایتکد غیرفعال شد تا از نوشتن __pycache__ در site-packages که باعث تحریک رد شدنهای لیست سفید نوشتن میشد، جلوگیری شود.
ادغام با smolagents
Anvil با پیادهسازی قرارداد PythonExecutor (شامل send_tools ، send_variables و __call__ -> CodeOutput) بهطور مستقیم با smolagents نسخه ۱.۲۶ ادغام شده است. این اجازه میدهد تا AnvilSandboxExecutor بهطور کامل جایگزین مفسر محلی پیشفرض شود و تضمین کند که CodeAgent برای تمام اکشنها از همان مسیر run_tool() استفاده میکند و ردپا (trace) پیوسته باقی میماند.
دو جزئیات API حیاتی هستند: OpenAIServerModel از آرگومان api_base بهجای base_url استفاده میکند. علاوه بر این، چون هر اکشن در یک فرآیند ایزوله تازه اجرا میشود، وضعیت (state) بین اکشنها محدود به متغیرهای قابل سریالسازی JSON است.
درسهای مرحله ادغام، خطر «موفقیتهای توخالی» (vacuous successes) را برجسته کرد. اجراهای اولیه به نظر درست میرسیدند اما مراحل را هدر میدادند چون مدل بهجای بلوکهای <code > ، JSON خام ارسال میکرد. اجراکننده رشتههای خالی دریافت میکرد و موفقیت گزارش میداد. این مشکل با رد کردن رشتههای خالی به عنوان اجرا حل شد: if not code_action.strip(): return CodeOutput(output="No code was received...", logs=hint, is_final_answer=False).
در نهایت، سیستم برای جلوگیری از «دموهای دروغین» تنظیم شد. پیش از این، یک سیستم جایگزین (fallback) هر اکشن موفقی را در پایان حلقهی مراحل میپذیرفت، حتی اگر فقط یک print(df.columns) بود. اکنون، این جایگزین تنها اکشنهایی را میپذیرد که واقعاً یک artifact نوشته باشند. این سختگیری زمانی ثابت شد که دو مدل مختلف بهطور مستقل یک مجموع کل یکسان — ۱۲۲۱۰۲۹۴.۴۶ — را از یک فایل CSV با ۵۰۰ ردیف محاسبه کردند و ثابت شد که خروجیها واقعی بودند و نه اسکریپتشده.
این رویکرد این فرض را تغییر میدهد که امنیت عامل باید یک ماشین مجازی «همه یا هیچ» باشد. اگرچه این سیستم جایگزینی برای Firecracker یا محیطهای ایزوله از راه دور برای کدهای واقعاً خصمانه نیست، اما یک مرز لایهای فراهم میکند که به توسعهدهنده دقیقاً میگوید چه چیزی رد شد و چرا. پروژه در دایرکتوریهای agent/ ، client/ و data/ ساختار یافته و از طریق npm run dev و npm run verify قابل اجرا است.
گام بعدی شما
- اگر از smolagents برای اجرای کد استفاده میکنید، معماری لایهای Anvil را برای جایگزینی مفسر محلی بررسی کنید.
- برای تست استواری عاملهای خود، مجموعهای از «تلاشهای فرار» (Escape Attempts) مشابه Anvil طراحی کنید.
- در پیادهسازی محیطهای ایزوله، به جای تکیه بر یک لایه، ترکیبی از تحلیل استاتیک و محدودیتهای هسته را به کار ببرید.
اما داستان سختافزاری این تحول حتی شگفتانگیزتر است — به تحلیل ما دربارهی تراشههای Blackwell مراجعه کنید.




گفتگو