کلاد کد یک قابلیت تازه دارد: dynamic workflows. یک اسکریپت جاوااسکریپت، هماهنگی دهها تا صدها ایجنت را از داخل گفتگو بیرون میبرد و در پسزمینه اجرا میشود. در این پست یک linter میسازیم که همان اسکریپت را پیش از اجرا میسنجد، و یک باگ واقعی را میگیریم که در غیر این صورت توکن میسوزاند.
چرا یک ایجنت معمولی کم میآورد
در روش قدیمی، خود کلاد تصمیم میگیرد در هر نوبت چه را بسپارد و نتیجهی هر کار در پنجرهی کانتکست خودش مینشیند. dynamic workflow این ترتیب را برمیگرداند: نقشه، حلقه، انشعاب و نتیجههای میانی در متغیرهای اسکریپت میمانند و فقط پاسخ نهایی به کانتکست برمیگردد. صفحهی اجرای موازی ایجنتها همین تفاوت را در یک ردیف مقایسه میکند.
نکتهی عملی این است که workflow یک قابلیت عمومی نیست. مستندات آن را برای همهی پلنهای پولی، دسترسی API و Bedrock، Google Cloud و Microsoft Foundry در دسترس میداند و روی پلن Pro باید از ردیف Dynamic workflows در /config روشنش کنید. لحظهی خواندن: ۶ اکتبر ۲۰۲۶. تازهترین نسخهی منتشرشده روی npm نسخهی 2.1.291 است که همان روز ساعت 03:32 به وقت UTC منتشر شده. نسخهای که روی این سرور نصب است 2.1.283 است و در 2026-09-25 بیرون آمد.
| راه موازی | نقشه را چه نگه میدارد | نتیجهی میانی کجا مینشیند | پس از توقف |
|---|---|---|---|
| زیرایجنت | کلاد، نوبتبهنوبت | پنجرهی کانتکست | نوبت از نو شروع میشود |
| دستور مهارت | کلاد، بر پایهی پرامپت | پنجرهی کانتکست | نوبت از نو شروع میشود |
| تیم ایجنت | ایجنت راهبر، نوبتبهنوبت | فهرست کار مشترک | همتیمیها ادامه میدهند |
| workflow پویا | خود اسکریپت | متغیرهای اسکریپت | همان نشست قابل ادامه است |
قانونی که جلوی خرج بزرگ را میگیرد
پرچمی که مستندات روی آن بیشترین تأکید را دارند، انتخاب اندازه است. این مقدار به کلاد گفته میشود چند ایجنت بساز، و مستندات تصریح میکنند که این یک نصیحت است، نه سقف: پرامپتی که مقیاس دیگری بخواهد، بر آن غلبه میکند. جدول زیر از همان صفحه نقل شده و ستون آخر حداقل نسخهی هر مقدار را نشان میدهد.
| مقدار | تعداد ایجنتی که کلاد هدف میگیرد | حداقل نسخه |
|---|---|---|
unrestricted | بدون راهنما | پیش از ۲٫۱٫۲۰۲ |
small | کمتر از ۵ | ۲٫۱٫۲۰۲ |
medium | کمتر از ۱۰ | ۲٫۱٫۲۰۲ |
large | کمتر از ۵۰ | ۲٫۱٫۲۰۲ |
medium بهصورت پیشفرض | کمتر از ۱۰ | ۲٫۱٫۲۱۹ |
تفاوت پیشفرض و پرچم، یک جزئیت امنیتی است که در همهجا تکرار نشده. از نسخهی 2.1.219 به بعد، تا وقتی خودتان مقداری انتخاب نکرده باشید، ردیف مربوطه در پنل /config مقدار پیشفرض را نشان میدهد و خط پیشرفت اجرا نام اندازهی فعال را مینویسد. اگر مقداری را در فایل تنظیمات بگذارید، آن مقدار بر /config مقدم است و کلاد کد آن ردیف را در پنل پنهان میکند تا فکر نکنید از آنجا هم عوض میشود.
$ npm install -g @anthropic-ai/claude-code
$ claude --version
2.1.283 (Claude Code)
# اندازه را در فایل پروژه ثابت کنید تا به همهی نشستهای تیم برسد
$ mkdir -p .claude && cat > .claude/settings.json <<'JSON'
{
"workflowSizeGuideline": "small"
}
JSON
# یا فقط برای همین نشست، بدون دستزدن به فایل
$ claude --effort ultracode
اگر نسخهی شما پایینتر از 2.1.219 باشد، کلید workflowSizeGuideline در فایل تنظیمات بیاثر است و پیش از آن نسخه مقدار پیشفرض همان unrestricted میماند. برای دیدن نسخه همان یک خط claude --version کافی است. پرچم --effort ultracode را از نسخهی 2.1.203 به بعد میپذیرد و همزمان سطح تلاش را روی xhigh میگذارد.
سقفهای سخت: جایی که راهنما به پایان میرسد
مهمترین چیزی که باید از صفحهی مستندات برداشت این است که انتخاب اندازه همهی داستان نیست. runtime چند سقف دارد که مستند صریح است و با هیچ تنظیمی از بین نمیرود.
| سقف | مقدار | توضیح |
|---|---|---|
| همزمانی پیشفرض | ۱۶ | اگر CPU کمتری در دسترس باشد کمتر هم میشود |
| بیشینهی همزمانی | ۲۵۶ | از 2.1.269، بازهی ۱ تا ۲۵۶ |
آیتم در یک parallel() | ۴۰۹۶ | فهرست بلندتر با خطا رد میشود |
| ایجنت در کل یک اجرا | ۱۰۰۰ | جلوگیری از حلقهی بیپایان |
آستانهی هشدار Large workflow | ۲۵ | یا ۱٫۵ میلیون توکن پیشبینیشده |
سقف ۴۰۹۶ عمدی رد میشود و نه بیصدا بریده. مستندات توضیح میدهند که runtime فهرست بلندتر را با خطا رد میکند، چون یک سقف بیصدا بخشی از کار را بدون اطلاع دادن حذف میکرد. همین منطق دربارهی کش پرامپت هم هست: runtime همهی ایجنتهای هماهنگ را تا شروع پاسخ اول نگه میدارد و بعد یکجا رها میکند، با سقف زمانی 5000 میلیثانیه که با CLAUDE_CODE_WORKFLOW_PREFIX_STAGGER_MS عوض میشود.
مرجع متغیرهای محیطی میگوید همهی متغیرهای عددی علاوه بر رقم ساده، نمایش علمی و جداکننده هم میپذیرند، یعنی 2e3 را ۲۰۰۰ میخواند. اما پیش از 2.1.211 همین نگارشها میتوانستند مقداری بسیار کوچکتر بگذارند، مثل 1e6 که یک زمانسنج را روی ۱ میگذاشت. اگر متغیری را دستی تنظیم میکنید، همان نسخهی کلاد کد را هم چک کنید.
قبل از اجرا، اسکریپت را بسنجید
بزرگترین ریسک عملی این است که اسکریپت را ذخیره کنید، اجرا را شروع کنید و بعد بفهمید runtime آن را نمیپذیرد. مستندات چند قاعدهی بارگذاری را صریح نوشته است. کد زیر همان قاعدهها را مکانیکی میکند تا پیش از خرج توکن، خطا را ببینید.
$ cat > wf-lint.mjs <<'JS'
import { readFileSync } from 'node:fs'
const RULES = [
{ id: 'meta-literal', test: (s) =>
// «export const meta را اولین دستور نگه دار و یک شیء ساده باشد؛
// اگر چیزی جز مقدار ساده داشته باشد، / از / حذف میشود.»
!/export\s+const\s+meta\s*=/.test(s) ? 'missing'
: s.trimStart().startsWith('export const meta') ? null
: 'meta is not the first statement' },
{ id: 'meta-fields', test: (s) => {
const m = s.match(/export\s+const\s+meta\s*=\s*\{([\s\S]*?)\n\}/)
if (!m) return 'could not parse meta'
if (!/\bname\s*:/.test(m[1])) return 'meta has no name'
if (!/\bdescription\s*:/.test(m[1])) return 'meta has no description'
return null } },
{ id: 'no-import', test: (s) =>
// «اسکریپتی که import() دارد پیش از شروع اجرا شکست میخورد.»
/\bimport\s*\(/.test(s) || /^\s*import\s.+from\s/m.test(s)
? 'import() fails before the run starts' : null },
{ id: 'no-nondeterminism', test: (s) => {
// «کلاد کد این سه را داخل اسکریپت پرتاب میکند تا اجرای دوباره
// همان فراخوانیهای agent() را تکرار کند.»
const bad = []
if (/Date\.now\s*\(/.test(s)) bad.push('Date.now()')
if (/Math\.random\s*\(/.test(s)) bad.push('Math.random()')
if (/new\s+Date\s*\(\s*\)/.test(s)) bad.push('new Date()')
return bad.length ? `throws inside a run: ${bad.join(', ')}` : null } },
{ id: 'phase-in-meta', test: (s) => {
const m = s.match(/export\s+const\s+meta\s*=\s*\{([\s\S]*?)\n\}/)
if (!m || !/phases\s*:/.test(m[1])) return null
const declared = [...m[1].matchAll(/'([^']+)'|"([^"]+)"/g)]
.map((x) => x[1] ?? x[2])
const used = [...s.matchAll(/phase\s*\(\s*['"]([^'"]+)['"]/g)]
.map((x) => x[1])
const missing = used.filter((t) => !declared.includes(t))
return missing.length
? `phase() title with no meta entry: ${missing.join(', ')}` : null } },
]
let bad = 0
for (const path of process.argv.slice(2)) {
const problems = RULES.map((r) => [r.id, r.test(readFileSync(path, 'utf8'))])
.filter(([, msg]) => msg)
if (!problems.length) console.log(`ok ${path.split('/').pop()}`)
else { bad++; console.log(`FAIL ${path.split('/').pop()}`)
for (const [id, msg] of problems) console.log(` - ${id}: ${msg}`) }
}
process.exit(bad ? 1 : 0)
JS
$ node --version
v26.7.0
حالا دو اسکریپت را میسنجیم. اولی همان نمونهی خود مستندات است و دومی عمداً چهار قانون را میشکند. خروجی زیر از اجرای همین دو فایل به دست آمده است.
$ node wf-lint.mjs wf-demo-good.js wf-demo-bad.js
ok wf-demo-good.js
FAIL wf-demo-bad.js
- meta-literal: meta is not the first statement
- no-import: import() fails before the run starts
- no-nondeterminism: throws inside a run: Math.random(), new Date()
- phase-in-meta: phase() title with no meta entry: adversarial
wf-lint: 1 file(s) failed
$ echo $?
1
هر چهار خطا همان چیزی است که runtime موقع بارگذاری میگفت. اما نکتهی آموزشی در خط اول است: در فایل خراب، meta اولین دستور نبود چون دو import بالای آن آمده بودند. یعنی یک خطای ظاهراً بیربط، اعتبار قانون بعدی را هم بیاعتبار میکرد. همین قاعده که meta باید اولین دستور باشد، بیشترین خطای تازهکارها را میگیرد.
توقف و ادامه: چیزی که بقیه ندارند
مهمترین برتری workflow نسبت به زیرایجنت، رفتار آن هنگام توقف است. مستندات روند پخش دوباره را دقیق توضیح دادهاند و نتیجهی عملیاش این است که توقف در میانهی کار، کارِ تمامشده را از بین میبرد. پخش دوباره به ترتیب شروع ایجنتها انجام میشود و هر ایجنت یا نتیجهی ذخیرهشدهاش را برمیگرداند یا دوباره اجرا میشود.
اگر اسکریپت چهار کار را بهترتیب شروع کند و کار دوم شکست بخورد، اجرای دوباره کار اول را از کش برمیگرداند و سهتای بعدی را از نو اجرا میکند. یعنی خرابی یک ایجنت، کار دو ایجنت دیگر را هم دوباره پرداخت میکند. توقف دستی یک ایجنت هم به همین شکل حساب میشود، چون از دید runtime یک شکست است. تنها استثنا حالتی است که خود کل workflow را متوقف کنید، که در آن صورت هیچ ایجنتی شکستخورده شمرده نمیشود.
برای کار عملی، اسکریپت را طوری بنویسید که مرحلههای گران از هم جدا باشند. اگر هزار فایل در یک pipeline() بریزند، یک خطای کوچک هزار بار پرداخت میشود. مستندات راهحل را چند workflow جدا با نقطهی ادامهی مستقل پیشنهاد میکنند، چون در میانهی اجرا کاربر نمیتواند ورودی بدهد.
جایی که این روش به کار شما نمیخورد
سه محدودیت را باید پیش از انتخاب این روش بدانید. اول، اجرای workflow به پلن پولی نیاز دارد و هر اجرا به سهمیهی مصرف شما اضافه میشود. دوم، اسکریپت به فایلسیستم و شل دسترسی ندارد؛ خود ایجنتها این کار را میکنند و اسکریپت فقط آنها را هماهنگ میکند. سوم، کلیدواژهی ultracode فقط وقتی از پرامپتی که خودتان تایپ میکنید کار میکند و در claude -p یا از طریق webhook اجرا نمیشود.
اگر کارتان یک ویرایش کوچک است یا یک پرسش کوتاه، workflow ابزار اشتباهی است و فقط هزینه دارد. نشانهی درست انتخاب، تعداد فایل یا منبعی است که باید همزمان دیده شود: اگر جواب به یک دهتایی نیاز دارد، یک نشست معمولی کافی است. اگر صد تایی است، تازه اینجا ارزش دارد. مقایسهی کامل چهار راه موازی را در صفحهی مستندات workflow و راهنمای cookbook برای SDK پایتون میخوانید. اگر میخواهید قلابهای فرزند را هم ببندید، نوشتهی اتصال کلاد کد به ترمینال همان قلابها را با اجرای واقعی نشان میدهد.
منابع
- dynamic workflows در مستندات کلاد کد: اندازهی انتخابی، سقفهای runtime، توقف و ادامه
- یادداشت انتشار کلاد کد، شامل افزودهشدن کلید workflowSizeGuideline
- محل زندگی فایلهای تنظیمات و ترتیب تقدم آنها
- مقایسهی زیرایجنت، مهارت، تیم ایجنت و workflow
- مرجع متغیرهای محیطی و قاعدهی نمایش علمی در نسخههای پیش از 2.1.211
- معرفی dynamic workflows در کلاد کد و وضعیت دسترسپذیری عمومی
- یادداشت انتشار گیتهاب، شامل dynamic workflows در کپیلات کلیپ
- نسخهی دیگری از مستندات workflow روی دامنهی docs.anthropic.com
- راهنمای cookbook برای اجرای workflow از Agent SDK پایتون
- زیرایجنتها و ترتیب انتخاب مدل برای آنها
- صفحهی بستهی npm برای نسخههای منتشرشدهی کلاد کد
- مرجع TypeScript برای Agent SDK
دیدگاهها
۰ موردهنوز دیدگاهی ثبت نشده. اولین نفر باشید.