مهندسی حلقه (Loop Engineering) چیست؟
مهندسی حلقه به جای نوشتن پرامپت، طراحی سیستمهای تکرارپذیر برای عاملهای هوش مصنوعی است. با بررسی مخزن cobusgreyling/loop-engineering و 6 بلوک سازنده آن آشنا شوید.
مهندسی حلقه (Loop engineering) به چه معناست
مهندسی حلقه به معنای طراحی چرخهای تکرارپذیر است که یک عامل هوش مصنوعی (AI agent) در آن اجرا میشود: چه چیزی آن را بیدار میکند، به چه منابعی دسترسی دارد، خروجی آن چگونه بررسی میشود و چه عاملی آن را متوقف میکند. مهندسی پرامپت (Prompt engineering) تنها یک پیام را برای مدل شکل میدهد، اما مهندسی حلقه فرآیندی را طراحی میکند که در زمان استراحت شما، هزاران پیام را ارسال میکند. در اینجا، واحد کار از پرامپت به خودِ حلقه تغییر مییابد.
بهطور خلاصه: شما از نوشتن دستورالعملهای صرف دست برمیدارید و شروع به طراحی یک سیستم کنترلی میکنید. عامل همچنان به دستورالعملهای دقیق نیاز دارد، اما این دستورالعملها اکنون تنها بخشی از چرخهای هستند که طبق زمانبندی اجرا میشود، در یک کپی ایزوله از کد شما کار میکند، نتایج خود را با تست اثبات میکند و در صورت اتمام بودجه، متوقف میشود.
چرا این اصطلاح در سال 2026 پدیدار شد
نامگذاری این مفهوم در حال حاضر در فضای عمومی در حال تثبیت است. مخزن گیتهاب cobusgreyling/loop-engineering در کمتر از دو ماه پس از ظهور اولیه (تا ژوئیه 2026) از 9,600 ستاره عبور کرد و در تاریخ 2026-10-27 حدود 11,600 ستاره را تحت عنوان «دیگر پرامپت ننویسید. حلقه را طراحی کنید. امتیاز بگیرید.» نشان میداد. در ژوئیه 2026، فایل README این پروژه، این تغییر رویکرد را در شش بلوک سازنده دستهبندی کرده بود: زمانبندی (scheduling)، درختهای کاری (worktrees)، مهارتها، افزونهها و کانکتورها، زیر-عاملها (sub-agents) و حافظه پایدار که خارج از مکالمه نگهداری میشود. README فعلی دیگر آن لیست را ندارد (بررسیشده در 2026-10-27)، اما همچنان وضعیت حلقه را در فایلی خارج از مکالمه ذخیره میکند.
این مخزن، Boris Cherny که هدایت Claude Code در Anthropic را بر عهده دارد، به عنوان یکی از الهامبخشهای خود معرفی میکند. جمله معروف او که بهطور گسترده نقل شده، این است:
من دیگر به Claude پرامپت نمیدهم. من حلقههایی دارم که در حال اجرا هستند و به Claude پرامپت میدهند.
مخزن دوم، AI-Builder-Club/skills، در نزدیکی 1,300 ستاره قرار دارد (تا 2026-10-27) و مستقیماً به دو نقش اشاره میکند: یک «مهارکننده پایگاه کد» (codebase harness) که مخزن را برای اجرای تستها و استقرار توسط یک عامل ایمن میکند، و یک «مهندس حلقه» (loop engineer) که جریانهای کاری را میسازد؛ جریانهایی که با یک محرک (trigger) بیدار میشوند، کار را انجام میدهند و آموختههای خود را در یک فایل مشترک مینویسند تا حلقه بعدی بتواند آن را بخواند.
هیچکدام از این مخازن، این رویه را ابداع نکردهاند. هر کسی که تا به حال یک nightly build، یک linter در سیستم یکپارچهسازی مداوم (CI) یا یک cron job که تیکت باز میکند را اجرا کرده باشد، با ساختار آن آشناست. آنچه جدید است، این است که عامل (worker) درون حلقه اکنون غیرقطعی (non-deterministic) است، که این موضوع وظایف ماشینآلات پیرامونی را تغییر میدهد.
چهار بخش یک حلقه
هر حلقهٔ کاری دارای این چهار بخش است؛ حلقهای که یکی از این بخشها را نداشته باشد، همان حلقهای است که شما را ساعت 3 بامداد از خواب بیدار میکند.
- Trigger (محرک). رویدادی که یک اجرا را آغاز میکند: یک تایمر، یک webhook، یک pull request جدید یا یک هشدار.
- Boundary (مرز). فایلها، اعتبارنامهها و شبکهای که عامل (agent) ممکن است در طول آن اجرا به آنها دسترسی داشته باشد.
- Verification (تأیید). بررسی با یک exit code که تعیین میکند خروجی اجرا حفظ شود یا دور ریخته شود.
- Budget (بودجه). محدودیت توکن، زمان و هزینه که فارغ از موفقیت یا عدم موفقیت، به اجرا پایان میدهد.
این چهار مورد را به صورت پرسش بازخوانی کنید؛ با این کار، یک بازبینی طراحی برای هر عاملی که قصد دارید آن را در حال اجرا رها کنید، در اختیار خواهید داشت.
محرک: چه چیزی عامل را بیدار میکند
تایمر سادهترین محرک است و در سرور لینوکسی، systemd timer به دلیل قابلیت ثبت لاگ، امکان تنظیم سیاستهای بازنشانی (retry) و جلوگیری از اجرای همزمان دو نسخه از یک واحد (unit)، بر cron برتری دارد. ویژگی آخر، رایجترین باگ همپوشانی در حلقههای عامل را حذف میکند: دو اجرای همزمان که یک شاخه (branch) را ویرایش میکنند.
ابتدا برای عامل یک کاربر اختصاصی بسازید. این کاربر بخشی از مرز امنیتی است، بنابراین فقط مالک /srv/agent است و بس:
sudo useradd --system --home-dir /srv/agent --shell /usr/sbin/nologin agent
sudo chown -R agent:agent /srv/agentسپس واحد (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.timersystemctl list-timers باید ستون NEXT را با زمانی در آینده و ستون LEFT را در حال شمارش معکوس نشان دهد. نتیجه خالی به این معنی است که تایمر فعال نشده است، زیرا enable بدون --now آن را فقط برای بوت بعدی زمانبندی میکند. TimeoutStartSec=1800 اهمیت بیشتری از آنچه به نظر میرسد دارد. یک سرویس Type=oneshot تا زمانی که اسکریپت آن خاتمه نیابد، در حال اجرا تلقی میشود و systemd بهطور پیشفرض محدودیت زمانی شروع (start timeout) را برای واحدهای oneshot غیرفعال میکند. بنابراین بدون این خط، عاملی که در انتظار ورودی معلق مانده، واحد را برای همیشه در وضعیت activating نگه میدارد و هر تیک بعدی تایمر، آن را در حال اجرا یافته و هیچ کاری انجام نمیدهد. با وجود این خط، systemd پس از 30 دقیقه اجرا را خاتمه داده و آن را به عنوان failed علامتگذاری میکند. اگر واحد را به حالت پیشفرض Type=simple تغییر دهید، TimeoutStartSec دیگر اجرای اصلی را پوشش نمیدهد و RuntimeMaxSec= تنظیمی است که سقف زمانی را تعیین میکند. یک اجرا را با journalctl -u agent-loop.service -n 50 بخوانید.
اگر حلقه را از طریق cron اجرا میکنید، محافظ همپوشانی خود را اضافه کنید، زیرا cron بدون مشکل نسخه دوم را همزمان شروع میکند:
*/30 * * * * /usr/bin/flock -n /tmp/agent-loop.lock /srv/agent/bin/loop.shflock -n در صورتی که قفل برقرار باشد، بلافاصله با وضعیت 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 آن worktree را حذف میکند، اما فقط در صورتی که تمیز باشد. اگر tree دارای فایلهای untracked یا فایلهای tracked تغییریافته باشد، یا شامل submodule باشد، git تا زمانی که --force را اضافه نکنید از حذف آن خودداری میکند؛ به همین دلیل است که اسکریپت gate در زیر، حذف را فقط در مسیری که اجرا را دور میریزد، اجباری میکند. git worktree prune ورودیهایی را که دایرکتوریشان ناپدید شده است، پاکسازی میکند. در این نقطه، حلقههای موازی ایمن میشوند، زیرا دو عامل روی دو branch در دو دایرکتوری نمیتوانند یکدیگر را بازنویسی کنند.
مرز همچنین به اعتبارنامهها مربوط میشود. حلقهای که بدون نظارت اجرا میشود، توکنهای با عمر طولانی را نگه میدارد و هر اجرا فرصتی برای نشت یکی از آنها به لاگ، commit یا context مدل است. توکن را به همان مخزنی که حلقه با آن در تماس است محدود کنید، تا حد امکان آن را از محیطی که shell عامل میبیند دور نگه دارید و پیش از آنکه به یک حلقه دسترسی production بدهید، نحوه دور نگه داشتن اسرار از عوامل هوش مصنوعی را مطالعه کنید. برای ایجاد یک دیوار سختتر، کل حلقه را روی یک ماشین مجازی یکبارمصرف که میتوانید پس از هر اجرا نابود کنید قرار دهید. ابزاری که اجرا میکنید نیز بخشی از مرز را پیش از نوشتن هر کدی تعیین میکند، بنابراین ارزش دارد پیش از تصمیمگیری درباره میزان ایزولاسیونی که باید خودتان بسازید، مقایسه sandbox مدیریتشده Cowork با Claude Code روی دستگاه شخصی را مطالعه کنید.
تایید: دروازهای که حلقه را ایمن میکند
این بخشی است که یک حلقه را از یک cron job که فقط تایپ میکند، متمایز میسازد. خروجی عامل (agent) یک پیشنهاد است. دروازه تصمیم میگیرد.
#!/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، یک غلط تایپی در نام متغیر به یک رشته خالی تبدیل میشود و عملیات پاکسازی بهجای متوقف شدن با خطا، روی مسیر اشتباه اجرا میگردد. خط آخر عمداً فاقد --force است: اگر اجرا فایلهای ردیابینشده یا commitنشده در درخت باقی بگذارد، git از حذف آن خودداری میکند و -e اجرا را پس از push با خطا به پایان میرساند، بهجای آنکه بیسروصدا کاری را که کسی commit نکرده است، حذف کند.
بلوک if ! npm test کل ایده است. کد خروجیِ بررسیای که از قبل به آن اعتماد دارید، یعنی مجموعه تستها یا type checker شما، تعیین میکند که آیا branch باید push شود یا نابود. حلقهای بدون دروازه، کاری تولید میکند که کسی وقت بررسی آن را ندارد، که این از انجام ندادن کار بدتر است. حلقهای با دروازه، branchای تولید میکند که از همان استانداردی عبور کرده است که branch یک مشارکتکننده انسانی باید از آن بگذرد. یک دروازه سبز (تاییدشده) چیزی درباره میزان کدی که عامل برای رسیدن به آن نقطه تغییر داده است نمیگوید، بنابراین ارزش دارد که این بررسی را با یک دستورالعمل ثابت مانند قانونی که باعث میشود عامل کوچکترین تغییرِ کارآمد را انتخاب کند همراه کنید؛ این کار باعث میشود diff بهاندازهای کوچک بماند که بررسی آن کمهزینه باشد.
دروازهای را انتخاب کنید که صادقانه شکست بخورد. مجموعه تستی که روی یک diff خالی عبور میکند، به حلقه میآموزد که انجام ندادن هیچ کاری یک موفقیت است. مخازن با تستهای ضعیف، حلقههای ضعیفی دریافت میکنند، و به همین دلیل است که مخازن ترند، «آمادهسازی codebase برای عامل» را پیش از «نوشتن حلقه» قرار میدهند. اگر میخواهید بدانید که آیا مجموعه تست شما واقعاً یک regression را شناسایی میکند یا صرفاً خطوط را اجرا میکند، mutation testing بررسیای است که به این پرسش پاسخ میدهد، و عاملی که بهجای درخواست برای خواندن diff، یک گزارش مستندات قابلاجرا مجدد ارائه میدهد، آن پاسخ را به چیزی تبدیل میکند که میتوانید خودتان تایید کنید.
بودجه: چه چیزی یک اجرا را متوقف میکند
عاملی که بیوقفه تلاش مجدد میکند، عاملی با هزینه نامحدود است. برای هر حلقه یک سقف زمانی (wall-clock ceiling) در نظر بگیرید که توسط TimeoutStartSec در بالا اعمال میشود؛ همچنین یک شمارنده تلاش مجدد در داخل اسکریپت خود و یک سقف هزینه که توسط حساب کاربری ارائهدهنده اعمال میشود، قرار دهید. سپس هزینه هر اجرا را ثبت کنید تا بتوانید پیش از رسیدن صورتحساب، انحراف حلقه را مشاهده کنید. کنترل هزینه برای یک VPS عامل همیشه روشن جنبههای حسابداری را پوشش میدهد و مدیریت متنی که یک عامل بین نوبتها حمل میکند بزرگترین اهرم واحد برای هزینه هر اجرا را بررسی میکند، زیرا حلقهای که هر 30 دقیقه همان مخزن را دوباره میخواند، برای هر 30 دقیقه هزینه پرداخت میکند.
هزینه دلیلی است که چرا حلقهها معمولاً بر یک نشست طولانی برتری دارند. اجرایی که تازه شروع میشود، یک کار محدود را انجام میدهد و خارج میشود، متن (context) خود را کوچک نگه میدارد. نشستی که برای هشت ساعت باز میماند، تمام اشتباهات قبلی را در تاریخچه خود حمل میکند و در هر نوبت برای کل متن هزینه میپردازد.
الگوهایی که مخازن ترند شده کدگذاری میکنند
مخزن مهندسی حلقه، هشت الگوی تولیدی را فهرست کرده است (تا 2026-10-27) که خواندن آنها به عنوان یک منو، بهتر از خواندن به عنوان یک بیانیه است. تریاژ روزانه. یک حلقه سبک که به عنوان GitHub Action اجرا میشود و هیچ فایل وضعیتی را نگه نمیدارد. یک مراقب pull-request که نظرات بازبینی را زیر نظر میگیرد و به آنها پاسخ میدهد. یک جاروبکننده یکپارچهسازی مداوم که buildهای شکستخورده (قرمز) را شناسایی میکند. یک جاروبکننده وابستگیها. یک پیشنویسکننده changelog. پاکسازی پس از merge. تریاژ issueها.
وجه اشتراک آنها، داشتن یک وظیفه محدود با یک دروازه (gate) مشخص است. «رفع build شکستخورده» شرط قبولی دارد که ماشین میتواند آن را بخواند. «بهبود پایگاه کد» چنین شرطی ندارد، بنابراین هرگز به یک حلقه تبدیل نمیشود؛ بلکه به یک آشفتگی زمانبندیشده تبدیل خواهد شد.
آنها همچنین در داشتن یک سابقه مکتوب مشترک هستند. هر دو مخزن، وضعیت را از مکالمه خارج کرده و به فایلهایی در داخل مخزن منتقل میکنند: چه چیزی اجرا شد، چه چیزی یافت شد و چه تصمیمی گرفته شد. آن فایل، حافظه حلقه است و به همین دلیل است که یک حلقه دوم میتواند به جای کشف مجدد کارها، بر پایه کار حلقه اول بنا شود. این همچنین روشی برای حسابرسی یک عامل پس از وقوع است، زیرا به محض پایان اجرا، context مدل از بین میرود. هماهنگی زنده یک کانال مجزا است و یک نشست Claude Code میتواند کار را به نشست دیگری روی همان سیستم واگذار کند در حالی که هر دو در حال اجرا هستند، اما هیچچیز در آن تبادل، عمرش از نشستها فراتر نمیرود، بنابراین فایل همچنان بخشی است که بعداً آن را بازخوانی میکنید.
کجا حلقهها شکست میخورند
شکستها خستهکننده هستند و در تیمهای مختلف تکرار میشوند.
- نبود دروازه (Gate). خروجی انباشته میشود، کسی آن را بازبینی نمیکند، اعتماد از بین میرود و حلقه خاموش میشود.
- همپوشانی. دو اجرا روی یک شاخه (branch) یا دو عامل (agent) در یک working tree، باعث ایجاد تداخلهایی میشود که عامل سپس سعی در حل آنها دارد.
- انحراف خاموش (Silent drift). حلقه همچنان با موفقیت عبور میکند زیرا بررسی (check) بیش از حد ضعیف است و شکست نمیخورد.
- دامنه نامحدود. تریگری که با هر commit در یک مخزن پرکار فعال میشود، در عرض یک روز به یک مشکل هزینهبر تبدیل میگردد.
هر کدام از این موارد راه حل یکسانی دارند: کوچکسازی کار (job)، دقیقتر کردن بررسی و ثبت لاگ اجرا. اگر نمیتوانید شرط موفقیت را در یک جمله توصیف کنید، آن کار برای خودکارسازی آماده نیست.
شروع کار بدون نیاز به واژگان پیچیده
شما نیازی به یک فریمورک ندارید. یک سرور لینوکسی کوچک که همیشه روشن است، یک مخزن git که در صورت بروز خطا در تستها، شکست میخورد، یک systemd timer و یک اسکریپت shell که حاوی یک if باشد، یک حلقه کامل را تشکیل میدهند. این دقیقاً همان نقطهای است که اکثر افراد باید از آن شروع کنند، زیرا پرسشهای مربوط به طراحی، بهجای انتخاب یک ابزار، با اجرای عملی آن پاسخ داده میشوند. هنگامی که یک حلقه پایدار شد، اجرای حلقه دوم عمدتاً شامل یک timer دیگر و یک worktree دیگر است. برای راهاندازی پایه، به نحوه اجرای یک ایجنت هوش مصنوعی برنامهنویسی روی VPS مراجعه کنید و اگر میخواهید خودِ ایجنت روی سختافزاری که کنترل آن را در دست دارید اجرا شود، گزینههای فعلی ایجنتهای هوش مصنوعی self-hosted را ببینید.
FAQ
آیا مهندسی حلقه با مهندسی پرامپت متفاوت است؟
مهندسی پرامپت یک پیام را بهینه میکند: لحن، مثالها و فرمت خروجی. مهندسی حلقه، چرخه پیرامون پیام را بهینه میکند: محرکی که اجرا را شروع میکند، sandboxای که در آن اجرا میشود، بررسیکنندهای که خروجی را میپذیرد یا رد میکند، و بودجهای که به آن پایان میدهد. شما همچنان به یک پرامپت خوب در داخل حلقه نیاز دارید. پرامپت دیگر چیزی نیست که روزانه آن را تنظیم کنید، زیرا دروازه (gate) و محرک تأثیر بیشتری بر نتیجه دارند.
آیا برای ساخت حلقه عامل به یک فریمورک نیاز دارم؟
خیر. یک systemd timer، یک git worktree برای هر اجرا، یک اسکریپت shell که با یک دستور تست خاتمه مییابد، و یک سقف هزینه در حساب ارائهدهنده، تمام بخشهای تعریف را پوشش میدهند. فریمورکها رابطهای زمانبندی، فرمتهای حافظه مشترک و مسیریابی چند-عاملی اضافه میکنند که وقتی چندین حلقه را اجرا میکنید مفید هستند. اینها هزینه ورودی برای اولین حلقه نیستند.
مهار (harness) کد منبع چیست؟
مجموعهای از ابزارهاست که به عامل اجازه میدهد بدون حضور انسان در یک مخزن کار کند: راهاندازی با یک دستور، تستهایی که بهصورت غیرتعاملی اجرا میشوند و در صورت شکست با صدای بلند اعلام میکنند، یک linter، و روشی برای استقرار یا پیشنمایش تغییرات. این اصطلاح از همان موج مخازن سال 2026 که مهندسی حلقه از آن برخاسته، آمده است. تست عملی ساده است: اگر یک مشارکتکننده انسانی جدید نتواند با یک دستور از مرحله clone به تستهای سبز برسد، عامل هم نمیتواند.
چگونه از افزایش هزینه سنگین حلقه عامل جلوگیری کنم؟
آن را در سه نقطه محدود کنید. TimeoutStartSec را روی یک واحد systemd Type=oneshot مانند نمونه بالا تنظیم کنید تا اجرای معلق کشته شود (در سرویس Type=simple، معادل آن RuntimeMaxSec است). بازنشانیها (retries) را به جای تکرار تا زمان موفقیت، در داخل اسکریپت محدود کنید. یک سقف هزینه سخت روی حساب API تنظیم کنید، زیرا این تنها سقفی است که عامل نمیتواند از آن عبور کند. سپس هزینه هر اجرا را ثبت کنید، زیرا حلقهای که هزینهاش دوبرابر میشود، معمولاً حلقهای است که دامنه فعالیتش بیسروصدا گسترش یافته است.
کدام کارها برای تبدیل شدن به حلقه در اولویت هستند؟
کاری را انتخاب کنید که شرط قبولی قابلفهم برای ماشین داشته باشد و شعاع انفجار (تأثیر خرابی) آن کوچک باشد. رفع یک build قرمز، بهروزرسانی یک وابستگی و بازتولید changelog همگی واجد شرایط هستند، زیرا یک مجموعه تست یا یک diff میتواند نتیجه را اثبات کند. کارهای باز مانند refactoring یا طراحی هنوز واجد شرایط نیستند، زیرا چیزی برای بررسی توسط دروازه وجود ندارد و حلقه بدون دروازه، راهی پرهزینه برای ایجاد بدهی بازبینی (review debt) است.