حلقه Ralph یک ایجنت را روی یک PRD میچرخاند. قدم بعدی این است که چند ایجنت، چند Workflow و یک Control Plane واحد با هم کار کنند: یکی PRD بنویسد، یکی پیاده کند، یکی باگ را شکار کند. این درس از Symphony اوپنایآی شروع میشود، به ارکستراتور خودسازنده سری میرسد، و با دو نمونه واقعی از Production تمام میشود — نه دموی اسباببازی.
Symphony: یک Spec، یک Workflow
اوپنایآی ارکستراتور کوچکی به نام Symphony منتشر کرد. روش توزیعش جسورانه است: بهجای نصب یک محصول، یک Spec File میدهی به ایجنت و میگویی «این را پیاده کن». خود ارکستراتور از روی همان فایل ساخته میشود.
گردش کار را در یک `workflow.md` توصیف میکنی. مثلاً:
# workflow.md — Symphony
If an issue has the label "Agent Ralph":
work on its child issues.
Only when every child is complete,
mark the parent issue as complete.
The CLI polls Linear at a fixed cadence.
When a matching issue appears,
the orchestrator spawns an agent.این کار میکند. ولی دو محدودیت جدی دارد:
| محدودیت | اثر |
|---|---|
| فقط یک `workflow.md` | باید «همهچیز» را در یک فایل جا بدهی |
| ساخته اوپنایآی | پشتیبانی از Claude ندارد |
Better Symphony: چند Workflow + Claude + لایه Routing
سازنده سری با Claude، ارکستراتور خودش را روی ایدههای Symphony ساخت — اسمش را گذاشت Better Symphony. دو فرق کلیدی:
- پشتیبانی از Claude
- چند فایل Workflow، چون یک لایه Routing کوچک دارد
در پروژهٔ نمونه دو Workflow کنار هم زندگی میکنند: `ralph.md` و `prd.md`. Ralph و YoloBox اینجا First-class هستند.
نمونه واقعی `ralph.md`:
# ralph.md
agent: claude
mode: ralph-loop # حلقه را ارکستراتور میچرخاند، نه مدل
yolobox: true
## hooks
after-create:
- git clone {{repo}}
- setup workspace (copy-on-write)
## routing
active_when: [todo, in_progress]
terminal_when: [done, cancelled]
required_label: Agent Ralphبدنهٔ پرامپت با Liquid.js نوشته میشود؛ متغیرها در Runtime جایگزین میشوند:
Ralph Loop: sub-task {{ current.index }} of {{ children.size }}
Working on {{ issue.identifier }} — {{ issue.title }}
Parent: {{ parent.identifier }} — {{ parent.title }}
Progress:
{% for child in children %}
- [{% if child.completed %}x{% else %} {% endif %}] {{ child.title }}
{% endfor %}
Instructions:
- Move this sub-task to In Progress
- Swap labels when the work starts / finishes
- Commit, push, and comment on the Linear issueWorkflow دوم، `prd.md`، Label دیگری میخواهد و موازیسازی صریح دارد:
# prd.md
required_label: Agent PRD
max_concurrent: 3 # تا سه ایجنت همزمان PRD بسازند
## instructions
Load the PRD skill from .claude/skills/prd/SKILL.md
Ask clarifying questions, research the repo, write the PRD
Create a parent Linear issue + one child per implementation taskاجرا با یک Web UI:
bunx better-symphony --webرابط دو Workflow لودشده را نشان میدهد و چهار Slot: سه تا برای PRD، یکی برای Ralph.
نمونه زنده: بکاند Laravel بهجای menu.js استاتیک
روی Linear یک Issue کوچک:
Add a Laravel backend to the example app
instead of the static menu.js file.Label `Agent PRD` + وضعیت `To Do`. در Web UI میبینی Issue برداشته شد، Claude شروع کرد، در Linear به `In Progress` رفت و Label `Agent PRD progress` خورد. زیبایی کار این است که Workflow فقط یک فایل Markdown است — هر تیمی مال خودش را مینویسد.
وقتی PRD تمام شد، Sub-issueها اینها بودند — موتور واقعی حلقه:
- [ ] Scaffold the Laravel project
- [ ] Migrations for menu items, customizations, coupons
- [ ] Eloquent models
- [ ] Controllers
- [ ] Seeders
- [ ] Update the Vue app to fetch from the API
instead of static importsLabel را به `Agent Ralph` عوض میکنی، دوباره `To Do` میگذاری، یک Poll اجباری میزنی — Ralph برش میدارد و پیاده میکند.
دو داستان واقعی از Production
**۱) MultiSafePay — تایماوت ۳۰ روزه.** درخواست اصلی سازنده عملاً یک جمله بود، بهعلاوهٔ ارجاع به مستندات: «تایماوت تراکنش روی ۳۰ روز است؛ باید ۶۰ دقیقه باشد.» Label `Agent PRD` خورد، کل PRD ساخته شد، سه Sub-issue درآمد، Label `Agent Ralph` زد و حدود ۲۰ دقیقه بعد کار تمام بود. Workflow قدرتش را اینجا نشان میدهد: انسان مقصد را میگوید، PRD جزئیات را درمیآورد، Ralph پیاده میکند.
**۲) Workflow شکار باگ — فیش VAT چاپ نشد.** مشتری گفت فیش مالیات چاپ نمیشود. سازنده حدس زد مشکل در منطق Sync است. ایجنت Triage رفت، در تحلیل اول باگ را پیدا نکرد. بعد مشتری زنگ زد: «سفارش اصلاً ثبت نشده و هنوز Open است.» همین Comment را گذاشت و Issue را دوباره Dispatch کرد. ایجنت کل ماجرا را بازبینی کرد و باگ واقعی را پیدا کرد: اگر یک Flag خاموش باشد، سفارشهای میز هرگز ثبت نمیشوند. شکار این باگ برای انسان ساعتها طول میکشید؛ Workflow در چند دقیقه تمامش کرد.
مرور کل مسیر
از یک ترمینال ساکت در Print Mode شروع کردیم. Streaming اضافه شد. PRD آمد. tmux حلقه را در پسزمینه زنده نگه داشت. Sandbox، Permission، Hook، Auto Mode، و در نهایت Bypass امن با YoloBox. اسکریپت تبدیل به CLI شد. Worktree و Copy-on-Write ایزوله کردند. PRD به Linear رفت، انسان آگاهانه به حلقه برگشت، حلقه روی ماشین ریموت چرخید، حلقه ابدی آزمایش شد — و حالا چند Workflow روی یک ارکستراتور، کار را از یک جمله تا Pull Request میبرند.
به زبان ساده
ارکستراتور چند Workflow را روی Linear راه میبرد: یکی PRD مینویسد، یکی با Ralph پیاده میکند، یکی باگ را شکار میکند. حلقه را خود ارکستراتور میچرخاند، نه داخل حافظه مدل.
مثال واقعی
یک جمله: «تایماوت MultiSafePay ۳۰ روز است، باید ۶۰ دقیقه باشد» بهعلاوه لینک مستندات. Label Agent PRD خورد، PRD و سه Sub-issue ساخته شد، Label شد Agent Ralph، حدود ۲۰ دقیقه بعد تمام بود. در باگ فیش VAT، Comment مشتری («سفارش ثبت نشده») ایجنت Triage را به Flag خاموش رساند — کاری که برای انسان ساعتها طول میکشید.