برچسب هشدار یک کنترل نیست
بیشتر تیمهایی که یک عامل کدنویسی را مستقر میکنند با همان مصنوع آغاز میکنند: یک system prompt که به مدل دستور میدهد فایلها را حذف نکند، اسرار را بیرون نبرد و بستههای غیرقابلاعتماد نصب نکند. این یک برچسب هشدار است. درخواستی است که به زبان طبیعی بیان شده و خطاب به یک سیستم تصادفی است که ممکن است تحت تأثیر هر محتوایی که میخواند قرار بگیرد — یک README، اسکریپت postinstall یک وابستگی، یک stack trace، یک رشتهٔ issue واکشیشده. این دستور با هر توکن دیگری در پنجرهٔ context رقابت میکند.
سندباکس اجرا رقابت نمیکند. مرزی است که توسط کرنل، runtime کانتینر و لایهٔ شبکه اعمال میشود. اگر عامل نتواند بیرون از /workspace بنویسد، هیچ prompt injection نمیتواند باعث شود بیرون از /workspace بنویسد. اگر egress بهطور پیشفرض مسدود باشد، یک فراخوانی curl تولیدشده در سطح socket شکست میخورد، نه به صلاحدید مدل.
به همین دلیل قرارداد سندباکس باید پیش از مشخصکردن عامل نوشته شود. قابلیتهای عامل، سطح ابزارهایش، سطح خودمختاریاش — همهٔ اینها پاییندست چیزی هستند که محیط اجازه میدهد. مشخصکردن عامل در ابتدا طرحی تولید میکند که سندباکس بعداً باید با آن سازگار شود، و این جهت اشتباه اعمال محدودیت است.
عاملهای کدنویسی در واقع کجا آسیب میزنند
آسیب عاملهای کدنویسی بهندرت چشمگیر است. معمولاً یکی از سه دستهٔ پیشپاافتاده است که هر یک ویژگی محیط است نه ویژگی مدل.
دسترسی فایلسیستم فراتر از درخت کاری
عاملی که برای بازآرایی یک ماژول فراخوانده شده ممکن است تصمیم بگیرد پیکربندی مجاور را «تمیز کند»، یک lockfile را بازنویسی کند، یا برای حل یک import به دایرکتوری والد نفوذ کند. اگر کانتینر دایرکتوری home توسعهدهنده یا درخت سورس میزبان را mount کند، مجموعهٔ قابلنوشتن عملاً نامحدود است. شکست این نیست که مدل انتخاب بدی کرد؛ این است که محیط آن انتخاب را عرضه کرد.
دسترسی به اعتبارنامههای موروثی از محیط توسعهدهنده
عاملهایی که از یک shell توسعهدهنده اجرا میشوند متغیرهای محیطی، فایلهای اعتبارنامهٔ ابری، socketهای SSH agent و توکنهای cacheشده را به ارث میبرند. یک کلید ابری دیرپا در محیط، اعطای دائمی هر چیزی است که آن کلید میتواند انجام دهد. عامل برای سوءاستفاده از آن لازم نیست مخرب باشد؛ فقط باید دستوری اجرا کند که محیط را میخواند و آن را جایی میفرستد، که کاری عادی برای ابزارهای build است.
این سه دسته شکست مدل نیستند. شکست پیکربندی هستند و پیش از تولید حتی یک توکن قابل رفعاند.
مشخصکردن قرارداد

قرارداد سندباکس سه بند دارد: چه چیزی میتواند نوشته شود، چه چیزی میتواند خوانده شود، و چه چیزی میتواند خارج شود.
مجموعهٔ قابلنوشتن، مجموعهٔ قابلخواندن را تعریف کنید و همهچیز دیگر را مسدود کنید
از مسدودسازی شروع کنید و اعطاها را برشمارید. یک پیشفرض کارآمد برای یک وظیفهٔ کدنویسی چنین است:
writable: /workspace/repo/**
readable: /workspace/repo/**, /usr/lib/**, /usr/share/**
denied: everything else
مخزن واحد کار است. هر چیزی بیرون از آن یا یک وابستگی سیستمی است که باید فقطخواندنی باشد، یا یک منبع میزبان که نباید اصلاً دیده شود. این یک سیاست mount است، نه یک دستور prompt، و توسط runtime کانتینر اعمال میشود.
معاوضه واقعی است: یک مجموعهٔ قابلنوشتن باریک، جریانهای کاریای را میشکند که بهطور مشروع نیاز به دستزدن به یک دایرکتوری والد، یک مخزن خواهر یا یک cache سراسری دارند. تیمهایی که به این جریانها نیاز دارند باید قرارداد را آگاهانه گسترده کنند و دلیلش را مستند کنند، نه اینکه بهطور تصادفی قراردادی گسترده را به ارث ببرند. گستردهکردن یک رویداد قابلبازبینی است؛ یک mount بهطور تصادفی گسترده نیست.
egress را بهطور پیشفرض مسدود کنید و رجیستریها و endpointها را در allowlist قرار دهید
سیاست شبکه باید سیاست فایلسیستم را آینه کند. پیشفرض هیچ اتصال خروجی نیست. allowlist صریح و کوچک است: رجیستری بستهای که پروژه واقعاً استفاده میکند، endpoint ارائهدهندهٔ مدل اگر عامل یکی را فراخوانی میکند، و هر سرویس داخلی که وظیفه نیاز دارد.
egress: deny
allow: registry.npmjs.org
allow: api.internal.example
این مسیر نصب و مسیر کد تولیدشده را همزمان محدود میکند. همچنین رفتار شبکهای عامل را قابلمشاهده میکند: هر تلاش اتصال بیرون از allowlist یک سیگنال است، نه نویز.
معاوضهٔ صادقانه اصطکاک عملیاتی است. buildهای مشروع گاهی به endpointی نیاز دارند که پیشبینی نشده بود، و حالت شکست یک درخواست مسدودشده است که شبیه یک خطای گذرا به نظر میرسد. allowlist باید بهعنوان پیکربندی تحت کنترل نسخه تلقی شود، مانند کد بازبینی شود، و وقتی وظیفهای بهطور مشروع به آن نیاز دارد بهروزرسانی شود — نه اینکه بهخاطر شکست یک build به allow: * شل شود.
با اعتبارنامههای موقت محدود به وظیفه اجرا کنید
اعتبارنامهها باید برای وظیفه صادر شوند و با آن منقضی شوند. یک توکن محدود و کوتاهعمر که در شروع سندباکس صادر و در پایان آن باطل میشود، ارزش هر exfiltration را محدود میکند. اعتبارنامههای دیرپای خود توسعهدهنده هرگز نباید وارد سندباکس شوند؛ عامل نباید بتواند فراتر از مرز وظیفه بهجای توسعهدهنده عمل کند.
این بند اغلب سختترین برای پیادهسازی است، چون به یک سیستم هویت نیاز دارد که بتواند اعطاهای باریک و کوتاهعمر صادر کند. جایی که آن سیستم وجود ندارد، جایگزین اجرای عامل بدون هیچ اعتبارنامهای و هدایت هر عملیات ممتاز از طریق یک مرحلهٔ جداگانهٔ تأییدشده توسط انسان است.

تشخیص و پاسخ
یک قرارداد مرز را تعریف میکند. تشخیص به شما میگوید چه زمانی چیزی به آن فشار میآورد، و پاسخ تعیین میکند بعد چه میشود.
الگوهای غیرعادی syscall را بهعنوان سیگنال اصلی در نظر بگیرید
بازرسی prompt غیرقابلاعتماد است. همان دستور میتواند در یک زمینه بیضرر و در زمینهای دیگر بخشی از یک injection باشد، و روایت خود مدل از نیتش شاهدی بر رفتارش نیست. سیگنال قابلاعتماد آن است که فرایند واقعاً چه میکند: فراخوانیهای غیرمنتظرهٔ open بیرون از مجموعهٔ قابلخواندن، تلاشهای connect به آدرسهای بیرون از allowlist، execve برای shellها یا مفسرهایی که در پروفایل وظیفه نیستند، تغییرات غیرعادی file descriptor.
اینها در لایهٔ runtime بدون بازرسی استدلال مدل قابلمشاهدهاند. یک مانیتور در سطح syscall میتواند در صورت انحراف از پروفایل پایهٔ یک وظیفه هشدار دهد، و به راستگویی عامل دربارهٔ کاری که میکند وابسته نیست.
diff و تاریخچهٔ دستورات را با هم ثبت کنید
بازبینی پس از واقعه به هر دو مصنوع در یک جا نیاز دارد. یک diff بدون دستوراتی که آن را تولید کردهاند سخت قابلانتساب است؛ یک تاریخچهٔ دستورات بدون diff حاصل سخت قابلارزیابی است. ذخیرهٔ آنها با هم — دستور، برچسب زمانی، وضعیت خروج، diff حاصل — بازبینی پسنگر را ممکن میکند و امکان بازسازی آنچه عامل انجام داده را وقتی یک تست روزها بعد شکست میخورد فراهم میکند.
این یک دغدغهٔ logging است، نه دغدغهٔ مدل، و باید در قرارداد در کنار قواعد mount و egress مشخص شود.
برای عملیات برگشتناپذیر بیرون از سندباکس تأیید انسانی لازم کنید
برخی عملیات با بازگرداندن درخت کاری قابلخنثیسازی نیستند: push به یک remote، انتشار یک بسته، حذف یک منبع ابری، ارسال ایمیل، merge یک pull request. اینها باید دروازهبندی شوند. دروازه یک دستور prompt برای «قبل از push بپرس» نیست؛ سیاستی است که اعتبارنامه یا مسیر شبکهٔ لازم برای عملیات را حذف میکند تا زمانی که یک انسان آن را برای یک اقدام مشخص اعطا کند.
ترتیب مشخصسازی
پیامد عملی، تغییری در ترتیب کار است. پیش از مشخصکردن ابزارهای عامل، حافظهاش، حلقهٔ برنامهریزیاش یا سطح خودمختاریاش، محیطی را که در آن اجرا میشود مشخص کنید: مجموعهٔ قابلنوشتن، مجموعهٔ قابلخواندن، allowlist خروجی، دامنهٔ اعتبارنامه، مانیتور syscall، لاگ ترکیبی diff و دستورات، و دروازهٔ تأیید برای عملیات برگشتناپذیر بیرون از سندباکس.
سپس عامل در برابر یک مرز شناختهشده طراحی میشود. قابلیتهایش اشتراک آنچه مدل میتواند انجام دهد و آنچه سندباکس اجازه میدهد است، که سطحی کوچکتر و صادقانهتر از یک برچسب هشدار در یک system prompt است.
معاوضه این است که یک قرارداد سخت گاهی بهطور تصادفی یک وظیفهٔ مشروع را مسدود میکند، و تیم باید آگاهانه آن را گسترده کند. این همان نکته است. مرزی که هرگز آزموده نشود مرز نیست؛ مرزی که از طریق بازبینی گسترده شود مرزی است که فهمیده شده است.
