مهندسی حلقه (Loop Engineering) چیست؟ تعریف دقیق
مهندسی حلقه به جای نوشتن یک پرامپت، طراحی سیستمهای تکرارپذیر برای AI agent است. با یادگیری طراحی محرک، مرز، تایید و بودجه، فرآیند خودکارسازی را از سال 2026 متحول کنید.
مهندسی حلقه (Loop engineering) به چه معناست
مهندسی حلقه به معنای طراحی چرخهای تکرارپذیر است که یک عامل هوش مصنوعی (AI agent) در آن اجرا میشود: چه چیزی آن را بیدار میکند، به چه منابعی دسترسی دارد، خروجی آن چگونه بررسی میشود و چه عاملی آن را متوقف میکند. مهندسی پرامپت (Prompt engineering) تنها یک پیام را برای مدل شکل میدهد، اما مهندسی حلقه فرآیندی را طراحی میکند که در زمان استراحت شما، هزاران پیام را ارسال میکند. در اینجا، واحد کار از پرامپت به خودِ حلقه تغییر مییابد.
بهطور خلاصه: شما از نوشتن دستورالعملهای صرف دست برمیدارید و شروع به طراحی یک سیستم کنترلی میکنید. عامل همچنان به دستورالعملهای دقیق نیاز دارد، اما این دستورالعملها اکنون تنها بخشی از چرخهای هستند که طبق زمانبندی اجرا میشود، در یک کپی ایزوله از کد شما کار میکند، نتایج خود را با تست اثبات میکند و در صورت اتمام بودجه، متوقف میشود.
چرا این اصطلاح در سال 2026 پدیدار شد
نامگذاری این مفهوم هماکنون در فضای عمومی در حال تثبیت است. مخزن گیتهاب cobusgreyling/loop-engineering در کمتر از دو ماه پس از انتشار اولیه (تا ژوئیه 2026) از مرز 9,600 ستاره گذشت؛ این مخزن تحت شعار «پرامپتنویسی را متوقف کنید. حلقه را طراحی کنید. امتیاز بگیرید» فعالیت میکند. این رویکرد، تغییر پارادایم را در شش بلوک سازنده خلاصه میکند: زمانبندی (scheduling)، درختهای کاری (worktrees)، مهارتها، افزونهها و کانکتورها، زیر-عاملها (sub-agents) و حافظه پایدار که خارج از محیط گفتگو نگهداری میشود.
این مخزن به بوریس چرنی، مدیر پروژه Claude Code در شرکت Anthropic استناد میکند:
من دیگر به Claude پرامپت نمیدهم. من حلقههایی دارم که در حال اجرا هستند و به Claude پرامپت میدهند.
مخزن دوم، AI-Builder-Club/skills، با نزدیک به 1,100 ستاره (تا ژوئیه 2026) دو نقش را بهطور مستقیم نامگذاری میکند: یک «مهارکننده کدبیس» (codebase harness) که مخزن را برای اجرای تستها و استقرار توسط یک عامل ایمن میکند، و یک «مهندس حلقه» (loop engineer) که گردشکارهایی را میسازد که با یک محرک (trigger) فعال میشوند، کار را انجام میدهند و آموختههای خود را در یک فایل مشترک مینویسند تا حلقه بعدی بتواند آن را بخواند.
هیچکدام از این مخازن، این رویه را ابداع نکردهاند. هر کسی که تا به حال یک بیلد شبانه (nightly build)، یک ابزار linter در محیط یکپارچهسازی مداوم (CI) یا یک cron job که تیکت باز میکند را اجرا کرده باشد، با ساختار آن آشناست. آنچه جدید است، این است که عاملِ درونِ حلقه اکنون غیرقطعی (non-deterministic) است، و این موضوع ماهیت وظایفِ زیرساختهای پیرامونی را تغییر میدهد.
چهار بخش یک حلقه
هر حلقهٔ کاری دارای این چهار بخش است؛ حلقهای که یکی از این بخشها را نداشته باشد، همان حلقهای است که شما را ساعت 3 بامداد از خواب بیدار میکند.
- Trigger (محرک). رویدادی که یک اجرا را آغاز میکند: یک تایمر، یک webhook، یک pull request جدید یا یک هشدار.
- Boundary (مرز). فایلها، اعتبارنامهها و شبکهای که عامل (agent) ممکن است در طول آن اجرا به آنها دسترسی داشته باشد.
- Verification (تأیید). بررسی با یک exit code که تعیین میکند خروجی اجرا حفظ شود یا دور ریخته شود.
- Budget (بودجه). محدودیت توکن، زمان و هزینه که فارغ از موفقیت یا عدم موفقیت، به اجرا پایان میدهد.
این چهار مورد را به صورت پرسش بازخوانی کنید؛ با این کار، یک بازبینی طراحی برای هر عاملی که قصد دارید آن را در حال اجرا رها کنید، در اختیار خواهید داشت.
محرک: چه چیزی عامل را بیدار میکند
تایمر سادهترین محرک است و در سرور لینوکسی، systemd timer به دلیل قابلیت ثبت لاگ، امکان تنظیم سیاستهای بازتلاش (retry) و جلوگیری از اجرای همزمان دو نسخه از یک unit، بر cron برتری دارد. ویژگی آخر، رایجترین باگ همپوشانی در حلقههای عامل (agent loops) را حذف میکند: یعنی دو اجرای همزمان که یک شاخه (branch) را ویرایش میکنند.
فایل unit را در /etc/systemd/system/agent-loop.service بنویسید:
[Unit]
Description=Agent loop: triage open issues
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
User=agent
WorkingDirectory=/srv/agent/repo
ExecStart=/srv/agent/bin/loop.sh
TimeoutStartSec=1800و تایمر را در /etc/systemd/system/agent-loop.timer قرار دهید:
[Unit]
Description=Run the triage loop every 30 minutes
[Timer]
OnBootSec=5min
OnUnitActiveSec=30min
Unit=agent-loop.service
[Install]
WantedBy=timers.targetsudo systemctl daemon-reload
sudo systemctl enable --now agent-loop.timer
systemctl list-timers agent-loop.timerدستور systemctl list-timers باید ستون NEXT را با زمانی در آینده و ستون LEFT را در حال شمارش معکوس نشان دهد. نتیجه خالی به این معناست که تایمر فعال نشده است، زیرا enable بدون --now، آن را فقط برای بوت بعدی زمانبندی میکند. پارامتر TimeoutStartSec=1800 بیش از آنچه به نظر میرسد اهمیت دارد: در غیر این صورت، عاملی که در انتظار ورودی معلق بماند، unit را برای همیشه فعال نگه میدارد و تایمر دیگر هرگز اجرا نخواهد شد. یک اجرا را با journalctl -u agent-loop.service -n 50 بخوانید.
اگر حلقه را از طریق cron اجرا میکنید، حتماً محافظ همپوشانی خود را اضافه کنید، زیرا cron بدون مشکل نسخه دوم را هم شروع میکند:
*/30 * * * * /usr/bin/flock -n /tmp/agent-loop.lock /srv/agent/bin/loop.shدستور flock -n در صورتی که قفل (lock) در اختیار باشد، بلافاصله با وضعیت 1 خارج میشود؛ بنابراین اجرای دوم بهجای رقابت با اجرای اول، بیسروصدا ناپدید میشود. همان تنظیمات systemd service و timer برای هر کار طولانیمدت روی سیستم، چه عامل باشد و چه نباشد، صدق میکند.
مرز: برای هر اجرا یک کپی مجزا در نظر بگیرید
عاملی که در حال ویرایش working tree شماست، میتواند تغییرات ذخیرهنشدهتان را از بین ببرد. Git worktrees این مشکل را بهسادگی حل میکنند: هر اجرا دایرکتوری و branch مخصوص به خود را دارد، در حالی که همگی از یک object store مشترک استفاده میکنند.
cd /srv/agent/repo
git worktree add -b loop/triage-01 /srv/agent/work/triage-01 origin/main
git worktree listgit worktree list برای هر tree یک خط شامل مسیر، commit و branch آن چاپ میکند. پس از پایان اجرا، git worktree remove /srv/agent/work/triage-01 دایرکتوری را حذف میکند و git worktree prune ورودیهایی که دایرکتوری آنها از بین رفته است را پاکسازی میکند. در این حالت، حلقههای موازی ایمن میشوند، زیرا دو عامل روی دو branch در دو دایرکتوری مجزا نمیتوانند تغییرات یکدیگر را بازنویسی کنند.
این مرز شامل اعتبارنامهها نیز میشود. حلقهای که بدون نظارت اجرا میشود، توکنهای با عمر طولانی در اختیار دارد و هر اجرا فرصتی برای نشت یکی از آنها به لاگ، commit یا context مدل است. دامنهٔ دسترسی توکن را فقط به مخزنی که حلقه با آن در ارتباط است محدود کنید، تا حد امکان آن را از محیط shell عامل دور نگه دارید و پیش از دادن دسترسی production به یک حلقه، نحوه دور نگه داشتن اسرار از عاملهای هوش مصنوعی را مطالعه کنید. برای ایجاد یک دیوار مستحکمتر، کل حلقه را روی یک ماشین مجازی یکبارمصرف که پس از هر اجرا قابل نابودی است قرار دهید. ابزاری که انتخاب میکنید نیز بخشی از این مرز را پیش از نوشتن هر کدی تعیین میکند؛ بنابراین پیش از تصمیمگیری دربارهٔ میزان ایزولاسیونی که باید خودتان پیادهسازی کنید، ارزش دارد مقایسه sandbox مدیریتشده Cowork با Claude Code روی ماشین شخصی را بخوانید.
تأیید: دروازهای که حلقه را ایمن میکند
این بخشی است که یک حلقه را از یک cron job که فقط تایپ میکند، متمایز میسازد. خروجی عامل یک پیشنهاد است. دروازه تصمیمگیرنده است.
#!/usr/bin/env bash
set -euo pipefail
repo=/srv/agent/repo
branch="loop/$(date -u +%Y%m%dT%H%M%SZ)"
tree="/srv/agent/work/$(basename "$branch")"
cd "$repo"
git fetch --quiet origin
git worktree add -b "$branch" "$tree" origin/main
cd "$tree"
# the agent's own command runs here, in non-interactive mode
if ! npm test; then
echo "gate failed: discarding $branch" >&2
cd "$repo"
git worktree remove --force "$tree"
exit 1
fi
git push origin "$branch"
cd "$repo"
git worktree remove "$tree"set -euo pipefail در آن اسکریپت کار واقعی انجام میدهد. بدون -e، یک git fetch ناموفق نادیده گرفته میشود و اجرا با یک origin/main قدیمی ادامه مییابد. بدون -u، یک غلط تایپی در نام متغیر به یک رشته خالی تبدیل میشود و عملیات پاکسازی بهجای شکست خوردن با صدای بلند، روی مسیر اشتباه اجرا میگردد.
بلوک if ! npm test تمام ایده اصلی است. کد خروجیِ بررسیای که به آن اعتماد دارید، یعنی مجموعه تستها یا بررسیکننده نوع (type checker) شما، تعیین میکند که آیا شاخه (branch) push شود یا حذف گردد. حلقهای که دروازه ندارد، کاری تولید میکند که هیچکس فرصت بازبینی آن را ندارد؛ این وضعیت از انجام ندادن کار بدتر است. حلقهای که دروازه دارد، شاخهای تولید میکند که از همان استانداردی عبور کرده که شاخه یک مشارکتکننده انسانی باید از آن بگذرد. یک دروازه سبز (تأیید شده) چیزی درباره میزان کدی که عامل برای رسیدن به آن نقطه تغییر داده نمیگوید؛ بنابراین بهتر است این بررسی را با دستورالعملی ثابت مانند قانونی که باعث میشود عامل کوچکترین تغییرِ کارآمد را اعمال کند همراه کنید تا تفاوت (diff) به اندازه کافی کوچک بماند و بازبینی آن کمهزینه باشد.
دروازهای را انتخاب کنید که صادقانه شکست بخورد. مجموعه تستی که روی یک diff خالی عبور میکند، به حلقه میآموزد که هیچ کاری نکردن، موفقیت است. مخازنی که تستهای ضعیفی دارند، حلقههای ضعیفی هم خواهند داشت؛ به همین دلیل است که مخازن ترند، «آمادهسازی کدبیس برای عامل» را پیش از «نوشتن حلقه» قرار میدهند. اگر میخواهید بدانید که آیا مجموعه تست شما واقعاً یک رگرسیون را شناسایی میکند یا صرفاً خطوط کد را اجرا میکند، تست جهش (mutation testing) همان بررسیای است که پاسخ را میدهد، و عاملی که بهجای درخواست برای خواندن diff، یک گزارش مستندِ قابلاجرا مجدد ارائه میدهد، آن پاسخ را به چیزی تبدیل میکند که میتوانید شخصاً تأیید کنید.
بودجه: چه چیزی یک اجرا را متوقف میکند
عاملی که بیوقفه تلاش مجدد میکند، عاملی با هزینه نامحدود است. برای هر حلقه یک سقف زمانی (wall-clock ceiling) در نظر بگیرید که توسط TimeoutStartSec در بالا اعمال میشود؛ همچنین یک شمارنده تلاش مجدد در داخل اسکریپت خود و یک سقف هزینه که توسط حساب کاربری ارائهدهنده اعمال میشود، قرار دهید. سپس هزینه هر اجرا را ثبت کنید تا بتوانید پیش از رسیدن صورتحساب، انحراف حلقه را مشاهده کنید. کنترل هزینه برای یک VPS عامل همیشه روشن جنبههای حسابداری را پوشش میدهد و مدیریت متنی که یک عامل بین نوبتها حمل میکند بزرگترین اهرم واحد برای هزینه هر اجرا را بررسی میکند، زیرا حلقهای که هر 30 دقیقه همان مخزن را دوباره میخواند، برای هر 30 دقیقه هزینه پرداخت میکند.
هزینه دلیلی است که چرا حلقهها معمولاً بر یک نشست طولانی برتری دارند. اجرایی که تازه شروع میشود، یک کار محدود را انجام میدهد و خارج میشود، متن (context) خود را کوچک نگه میدارد. نشستی که برای هشت ساعت باز میماند، تمام اشتباهات قبلی را در تاریخچه خود حمل میکند و در هر نوبت برای کل متن هزینه میپردازد.
الگوهایی که مخازن ترند شده کدگذاری میکنند
مخزن loop-engineering هفت الگوی عملیاتی را فهرست میکند که ارزش خواندن به عنوان یک منو را دارند، نه یک بیانیه. تریاژ روزانه. یک مراقب pull-request که نظرات بازبینی را زیر نظر میگیرد و به آنها پاسخ میدهد. یک جاروبگر یکپارچهسازی مداوم (CI) که بیلدهای ناموفق را شناسایی میکند. یک جاروبگر وابستگیها. یک پیشنویسکننده changelog. پاکسازی پس از merge. تریاژ issueها.
وجه اشتراک آنها، وظیفهای محدود با یک دروازه مشخص است. «اصلاح بیلد ناموفق» شرط قبولی دارد که ماشین میتواند آن را بخواند. «بهبود codebase» چنین شرطی ندارد، بنابراین هرگز به یک حلقه (loop) تبدیل نمیشود. بلکه به آشفتگی زمانبندیشده تبدیل میشود.
آنها همچنین در داشتن یک سابقه مکتوب مشترک هستند. هر دو مخزن وضعیت را از گفتگو خارج کرده و به فایلهای درون مخزن منتقل میکنند: چه چیزی اجرا شد، چه چیزی یافت شد و چه تصمیمی گرفته شد. آن فایل، حافظه حلقه است و دلیلی است که یک حلقه دوم میتواند به جای کشف مجدد، بر کار حلقه اول بنا شود. این همچنین روشی است که شما یک عامل (agent) را پس از وقوع بررسی میکنید، زیرا به محض خروج از اجرا، context مدل از بین میرود. هماهنگی زنده یک کانال جداگانه است و یک نشست Claude Code میتواند کار را به نشست دیگری روی همان سیستم واگذار کند در حالی که هر دو در حال اجرا هستند، اما هیچچیز در آن تبادل، طول عمر بیشتری از نشستها ندارد، بنابراین فایل همان بخشی است که بعداً بازخوانی میکنید.
کجا حلقهها شکست میخورند
شکستها خستهکننده هستند و در تیمهای مختلف تکرار میشوند.
- نبود دروازه (Gate). خروجی انباشته میشود، کسی آن را بازبینی نمیکند، اعتماد از بین میرود و حلقه خاموش میشود.
- همپوشانی. دو اجرا روی یک شاخه (branch) یا دو عامل (agent) در یک working tree، باعث ایجاد تداخلهایی میشود که عامل سپس سعی در حل آنها دارد.
- انحراف خاموش (Silent drift). حلقه همچنان با موفقیت عبور میکند زیرا بررسی (check) بیش از حد ضعیف است و شکست نمیخورد.
- دامنه نامحدود. تریگری که با هر commit در یک مخزن پرکار فعال میشود، در عرض یک روز به یک مشکل هزینهبر تبدیل میگردد.
هر کدام از این موارد راه حل یکسانی دارند: کوچکسازی کار (job)، دقیقتر کردن بررسی و ثبت لاگ اجرا. اگر نمیتوانید شرط موفقیت را در یک جمله توصیف کنید، آن کار برای خودکارسازی آماده نیست.
شروع کار بدون نیاز به واژگان پیچیده
شما نیازی به یک فریمورک ندارید. یک سرور لینوکسی کوچک که همیشه روشن است، یک مخزن git که در صورت بروز خطا در تستها، شکست میخورد، یک systemd timer و یک اسکریپت shell که حاوی یک if باشد، یک حلقه کامل را تشکیل میدهند. این دقیقاً همان نقطهای است که اکثر افراد باید از آن شروع کنند، زیرا پرسشهای مربوط به طراحی، بهجای انتخاب یک ابزار، با اجرای عملی آن پاسخ داده میشوند. هنگامی که یک حلقه پایدار شد، اجرای حلقه دوم عمدتاً شامل یک timer دیگر و یک worktree دیگر است. برای راهاندازی پایه، به نحوه اجرای یک ایجنت هوش مصنوعی برنامهنویسی روی VPS مراجعه کنید و اگر میخواهید خودِ ایجنت روی سختافزاری که کنترل آن را در دست دارید اجرا شود، گزینههای فعلی ایجنتهای هوش مصنوعی self-hosted را ببینید.
FAQ
آیا مهندسی حلقه (loop engineering) با مهندسی پرامپت متفاوت است؟
مهندسی پرامپت یک پیام واحد را بهینهسازی میکند: جملهبندی، مثالها و فرمت خروجی. مهندسی حلقه، چرخهای را که پیرامون آن پیام قرار دارد بهینهسازی میکند: محرکی که اجرا را آغاز میکند، محیط sandbox که در آن اجرا میشود، بررسیکنندهای که خروجی را میپذیرد یا رد میکند، و بودجهای که به آن پایان میدهد. شما همچنان به یک پرامپت خوب در داخل حلقه نیاز دارید. پرامپت دیگر آن چیزی نیست که روزانه تنظیم میکنید، زیرا دروازه (gate) و محرک (trigger) تأثیر بیشتری بر نتیجه دارند.
آیا برای ساخت یک حلقه عامل (agent loop) به یک فریمورک نیاز دارم؟
خیر. یک systemd timer، یک git worktree برای هر اجرا، یک اسکریپت shell که با یک دستور تست پایان مییابد، و یک سقف هزینه در حساب ارائهدهنده، تمام بخشهای این تعریف را پوشش میدهند. فریمورکها رابطهای زمانبندی، فرمتهای حافظه اشتراکی و مسیریابی چند-عاملی اضافه میکنند که پس از اجرای چندین حلقه مفید هستند. اینها هزینه ورودی برای اولین حلقه نیستند.
مهار کد (codebase harness) چیست؟
مجموعهای از ابزارهاست که به یک عامل اجازه میدهد بدون حضور انسان در یک مخزن کار کند: راهاندازی با یک دستور، تستهایی که بهصورت غیرتعاملی اجرا میشوند و در صورت شکست با صدای بلند اعلام وضعیت میکنند، یک linter، و روشی برای استقرار یا پیشنمایش تغییرات. این اصطلاح از همان موج مخازن سال 2026 که مهندسی حلقه از آن برخاسته، نشأت میگیرد. تست عملی ساده است: اگر یک مشارکتکننده انسانی جدید نتواند با یک دستور از مرحله clone به تستهای سبز برسد، یک عامل نیز نمیتواند.
چگونه از افزایش هزینههای سنگین توسط یک حلقه عامل جلوگیری کنم؟
آن را در سه نقطه محدود کنید. روی unit مربوط به systemd یک TimeoutStartSec تنظیم کنید تا اجرای معلق متوقف شود. تعداد تلاشهای مجدد (retries) را در داخل اسکریپت محدود کنید، بهجای اینکه تا زمان موفقیت حلقه بزنید. یک سقف هزینه سخت در حساب API تنظیم کنید، زیرا این تنها سقفی است که عامل نمیتواند با مذاکره از آن عبور کند. سپس هزینه هر اجرا را لاگ کنید، زیرا حلقهای که هزینهاش دوبرابر میشود، معمولاً حلقهای است که دامنه فعالیتش بیسروصدا گسترش یافته است.
کدام کارها برای تبدیل شدن به یک حلقه در اولویت هستند؟
کاری را انتخاب کنید که دارای شرط قبولی قابلفهم برای ماشین و شعاع انفجار (تأثیر) کوچک باشد. رفع یک build قرمز، بهروزرسانی یک dependency و بازتولید changelog همگی واجد شرایط هستند، زیرا یک مجموعه تست یا یک diff میتواند نتیجه را اثبات کند. کارهای باز مانند refactoring یا طراحی هنوز واجد شرایط نیستند، زیرا چیزی برای بررسی توسط دروازه وجود ندارد و حلقهای بدون دروازه، راهی پرهزینه برای تولید بدهی بازبینی (review debt) است.