چند ماه است که بیشترِ وقتم صرف کاری میشود که نباید بشود: نشستن پای ترمینال و پرامپتدادن به ایجنت، یکی پس از دیگری. کار پیش میرود، اما هر قدمش به من وصل است. اگر من نباشم، هیچچیز جلو نمیرود.
اسمِ کاری که باید جایش را بگیرد گذاشتهاند حلقهسازی. ایدهاش یک جمله است:
دیگر به ایجنت پرامپت نمیدهی. سیستمی میسازی که بهجای تو به او پرامپت بدهد.
این نوشته دربارهی همان است، اما نه از سرِ تعریفکردن. از سرِ دو سؤالی که وقتی خواستم واقعاً بسازمش جلویم سبز شدند و جوابشان در هیچکدام از مقالهها نبود.
حلقه در یک پاراگراف
حلقه یعنی سه چیز. همین و بس:
- یک صف: فایلی که در آن نوشته چه کارهایی مانده.
- یک اجراکننده: چیزی که از صف یک آیتم برمیدارد و انجامش میدهد.
- یک داور: چیزی که میگوید این آیتم تمام شد، یا نشد و باید دوباره.
بقیهی حرفها جزئیاتِ همین سهتاست. اگر صف نداری، حلقه نداری. چت داری.
داور، همانجایی که همهچیز میشکند
صف و اجراکننده سادهاند. داور سخت است، و تقریباً همه از رویش رد میشوند.
دلیلش یک واقعیتِ ناخوشایند دربارهی مدلهاست: مدل کارِ خودش را تحسین میکند. اگر از همان ایجنتی که کد را نوشته بپرسی «خوب شد؟»، جواب تقریباً همیشه بله است. تلاش برای اینکه ایجنت را نسبت به خودش سختگیر کنی هم جواب نمیدهد.
چیزی که جواب میدهد جداکردنِ ساختاری است. یکی میسازد، یکی دیگر نقد میکند، و آن دومی دستور دارد که فرض کند کار خراب است.
راستیآزمایی خودِ محصول است، نه مرحلهی آخرِ کار.
اگر حلقهات سیگنالِ واقعی از محیط نگیرد — خروجی تست، کدِ خروج، لاگِ CI — حلقه نساختهای. یک ماشینِ تولیدِ متن ساختهای که خودش را تشویق میکند.
سؤال اول: کار از کجا بیاید؟
مقالهها میگویند لایهی کشف را اتومات کن: CI را بخوان، ایشوهای لیبلخورده را بردار، کامنتهای ریویو را جمع کن.
اما خیلی از ما چنین چیزی نداریم. صفِ باگِ پرشدهای در کار نیست. مشتری زنگ میزند و نیازش را میگوید، یا خودمان مینشینیم و تصمیم میگیریم چه چیزی بهتر است. کشفِ کار در جلسه اتفاق میافتد، نه در داشبورد.
جوابی که بعد از مدتی به آن رسیدم این بود:
مشکلْ کشف نیست. مشکل این است که چیزی که کشف میشود هیچوقت نوشته نمیشود.
ایجنت از حرفی که در جلسه زده شده کار برنمیدارد. از یک فایل برمیدارد. پس چیزی که باید اتومات شود پیداکردنِ کار نیست، نوشتنِ آن است.
صف را با دست بساز
یک فایل در ریشهی پروژه، با قالبِ ثابت. ثابتبودنِ قالب مهمترین بخشش است، چون ایجنت باید بتواند بخواندش.
## OPEN
### [P1] درخواست بدون دستهبندی، بیسروصدا پارک میشود
منبع: مشتری، جلسهی ۱۴ مرداد
مشکل: وقتی دستهی کالا تعریف نشده، درخواست ساکت کنار گذاشته میشود و کسی خبردار نمیشود.
تمام است وقتی: کاربر اعلان میگیرد و آیتم در صفِ مسدودها دیده میشود.
تست: tests/behavior/test_blocked_queue.py
## DONE
سه فیلد اجباری: منبع، «تمام است وقتی»، و تست.
قانونِ سادهای که خیلی چیزها را تمیز کرد: اگر نتوانستی «تمام است وقتی» را بنویسی، آن آیتم هنوز آمادهی حلقه نیست. هنوز یک ایده است، نه یک کار.
حالا کشف دو جنس میشود.
جنسِ اول انسانی است. اینکه مشتری چه میخواهد و چه چیزی مهمتر است، قضاوتِ توست و هیچوقت اتومات نمیشود. آنچه اتومات میشود فقط ثبتش است: بعد از هر جلسه، یادداشت را بده به مدل تا آیتمها را با همان قالب بسازد، و هرجا نتوانست «تمام است وقتی» را از حرفِ مشتری دربیاورد علامت بزند. آن علامتها دقیقاً فهرستِ سؤالهایی است که باید از مشتری بپرسی.
جنسِ دوم قطعی است، و اینجاست که پروژهی خودت پر از موادِ خام است. هر قانونی که یک بار نوشتهای و یک بار بهخاطر نقضشدنش ضرر کردهای، یک اسکریپت است:
- فیلدی در خروجی API که تایپِ مشخص ندارد.
- مسیری که توضیح ندارد.
- مدلی که ستونی دارد و مهاجرتِ دیتابیس آن را نساخته.
- مسیرِ تازهای که تستِ رفتاریاش نوشته نشده.
- قانونِ کسبوکاری که بهجای لایهی دامنه در روتر نشسته.
هیچکدام هوش نمیخواهد. یک اسکریپت است که خروجیاش یک فهرست است، و آن فهرست مستقیم میرود توی صف.
قضاوت مالِ توست. صف مالِ ماشین.
سؤال دوم: به تستی که خودِ مدل نوشته نمیشود اعتماد کرد
این سؤال از اولی مهمتر است.
اگر یک مدل هم کد را بنویسد هم تست را، آن تست فقط یک چیز را ثابت میکند: کد همان کاری را میکند که کد میکند.
که هیچ ارزشی ندارد. آن تست نیست، عکس است. یک اسنپشات از رفتارِ فعلی، نه مشخصاتی از رفتارِ درست. مشکل هم این است که تست از روی کد نوشته شده، نه از روی نیاز.
برای اینکه از این حلقهی بسته بیرون بیایی، اول باید ببینی سه سؤالِ متفاوت را با هم قاطی کردهایم:
- آیا کد همان کاری را میکند که کد میکند؟ تستِ نوشتهی مدل این را جواب میدهد، و بیارزش است.
- آیا کد همان کاری را میکند که سند میگوید؟ تست این را جواب میدهد، اما فقط اگر از روی سند نوشته شده باشد.
- آیا سند همان چیزی است که مشتری میخواهد؟ فقط مشتری این را جواب میدهد. هیچ اتوماسیونی جایش را نمیگیرد.
بیاعتمادیات به سطحِ سوم برمیگردد، و کاملاً بهجاست. کاری که میشود کرد این نیست که سطح سوم را اتومات کنیم. این است که آنقدر ارزانش کنیم که در دو دقیقه چک شود.
معیارها را قبل از کد بنویس، به زبانِ مشتری
قبل از اینکه یک خط کد نوشته شود، فهرستِ معیارها را بنویس. به زبانی که مشتری بخواند و بفهمد. نه کد، نه اسمِ فیلد:
- اگر مبلغ از سقفِ تأییدکننده بیشتر باشد، تأیید رد میشود.
- اگر از بودجهی پروژه بیشتر بزند، تأیید انجام میشود اما هشدار میرود.
- اگر مرکز هزینه مشخص نشده باشد، تأیید اصلاً انجام نمیشود.
- یک سفارش دو بار تأیید نمیشود.
مشتری این چهار خط را در دو دقیقه میخواند و میگوید بله، یا میگوید نه، شمارهی دو باید مسدود شود نه هشدار.
و این تنها چیزی است که تو باید مرور کنی. نه کدِ تست، نه کدِ پیادهسازی. همین چند خط.
تست را از روی سند بنویس، نه از روی کد
وقتی نوبتِ تست شد، عمداً کدِ پیادهسازی را به مدل نده. فقط فهرستِ معیارها و شکلِ API را بده و بگو فایلهای پیادهسازی را نخواند.
دلیلش ساده است: اگر مدل پیادهسازی را ببیند، ناخودآگاه تست را طوری مینویسد که پاس شود. اگر نبیند، مجبور است از روی سند بنویسد. آنوقت اگر کد با سند فرق داشته باشد، تست قرمز میشود. که دقیقاً همان چیزی است که میخواستیم.
ارزانترین چکِ اعتماد: قانون را عمداً خراب کن
این مهمترین تکهی کلِ ماجراست و تقریباً کسی انجامش نمیدهد.
خطی را که قانونِ کسبوکار را چک میکند کامنت کن، و تست را اجرا کن.
- اگر تست قرمز شد، آن تست واقعاً دارد آن قانون را نگه میدارد.
- اگر سبز ماند، آن تست فقط داشت چک میکرد که آدرس وجود دارد.
همین. یک حرکتِ چند ثانیهای که بیاعتمادیِ مبهمت را به یک عدد تبدیل میکند. و کاملاً هم قابلِ اتوماتشدن است: برای هر قانون در فهرستِ معیارها، یک بار خرابش کن و مطمئن شو تست میگیردش.
نکتهی نزدیک به همین: تستی که میگوید «۲۰۰ برگشت» تقریباً هیچچیز ثابت نمیکند. تستی که میگوید این کار نباید انجام شود همهچیز را ثابت میکند. اگر همهی تستهای یک فایل مثبتاند، آن فایل دارد خودش را گول میزند.
تقسیمِ کارِ تازه
وقتی اینها سرِ جایشان بنشینند، نقشها عوض میشوند:
- تو و مشتری معیارها را مینویسید، به زبانِ ساده.
- یک ایجنت از روی معیارها تست مینویسد، بدون دیدنِ کد.
- ایجنتِ دیگری کد را مینویسد تا تست پاس شود.
- یک اسکریپت قانونها را عمداً خراب میکند و میبیند تست میگیردشان یا نه.
و تو از «مرور کردنِ چهارصد خط تست» رسیدهای به «خواندنِ چهار خط فارسی». کلِ هدف همین بود.
از کجا شروع کنیم
سه قدم، به همین ترتیب. قدمِ دوم را قبل از اول نرو:
اول، فایلِ صف را بساز و هرچه الان در سرت و در یادداشتهایت هست بریز تویش، با همان سه فیلد. هیچ اتوماسیونی نساز. فقط بنویس. اگر این فایل بعد از یک هفته زنده نماند، بقیهاش بیفایده است.
دوم، یک آیتم بردار، برایش فهرستِ معیار بنویس، بده مشتری تأییدش کند، بعد بگذار مدل از رویش تست بنویسد. یک بار. دستی. ببین کجا میلنگد.
سوم، چکِ خرابکردنِ عمدی را اسکریپت کن. حالا عددی داری که میگوید به تستهایت چقدر میشود اعتماد کرد.
بعد از این تازه میارزد دربارهی زمانبندی و اجرای موازی و ایجنتهای تودرتو حرف بزنیم.
حرفِ آخر
حلقهسازی کار را آسانتر نمیکند. فقط نقطهی اتکا را جابهجا میکند: از کیفیتِ پرامپت به کیفیتِ سیستم.
و دو خطری که باید حواست بهشان باشد هم همانجاست. اول اینکه حلقهی بیناظر، اشتباهِ بیناظر میکند؛ راستیآزمایی هنوز مسئولیتِ توست. دوم اینکه سریعتر تحویلدادن بدون فهمیدن، بدهیای میسازد که دیرتر و گرانتر پرداخت میشود.
حلقه را برای این نمیسازیم که فکر نکنیم. برای این میسازیم که فکرکردنمان جای بهتری خرج شود.
منابع
- Building Effective AI Agents و Effective context engineering for AI agents از تیم مهندسیِ Anthropic.
- Loop engineering: getting started with loops و مستندِ رفتنِ ایجنت به سمتِ یک هدف.
- نوشتهی Addy Osmani دربارهی حلقهسازی. این یکی را با احتیاط بخوان: سندی که این روزها بهعنوان «پلیبوکِ رسمی Anthropic» دستبهدست میشود انتشارِ رسمیِ Anthropic نیست، کارِ مشترکِ چند نفر از سازندگانِ همین ابزارهاست.