تصور کنید برنامهنویسی هستید که میخواهد یک دستیار هوشمند بسازد، اما نمیداند چرا گاهی مدل او در یک حلقهٔ بیپایان از درخواست ابزار گیر میکند و هزینههای API را به شدت بالا میبرد. حقیقت این است که یک عامل هوش مصنوعی هیچ جادویی نیست؛ بلکه فقط یک حلقهٔ تکرار (While Loop) است که تا رسیدن به یک شرط خاص فعال میماند. این حقیقت مکانیکی هسته مرکزی Vercel AI SDK 6 است.
این تحلیل بر اساس یک بررسی فنی با استفاده از مدلهای شبیهساز (Mock-model) در محیط ویندوز ۱۱، نود جیاس نسخه v24.15.0، بسته [email protected] و [email protected] انجام شده است. از آنجا که در این تستها هیچ مدل زبانی واقعی (LLM) فراخوانی نمیشود، نتایج در هر بار اجرا دقیقاً یکسان و تکرارپذیر هستند.
برای توسعهدهندگان، اصطلاح «عامل» (Agent) اغلب شبیه به تبلیغات بازاریابی به نظر میرسد. اما در واقعیت، یک عامل صرفاً یک مسیر رفتوبرگشت است: مدل ابزاری را فراخوانی میکند، کد آن ابزار را اجرا کرده و نتیجه را برمیگرداند و سپس مدل تصمیم میگیرد حرکت بعدی چه باشد. این چرخه تا زمان اتمام تسک یا رسیدن به یک سقف مجاز تکرار میشود.
در مسیر تکامل ارکستراسیون مدلهای زبانی، Vercel AI SDK مانند یک آداپتور جهانی عمل میکند. این ابزار به توسعهدهندگان اجازه میدهد مدلهای OpenAI، Anthropic یا گوگل را بدون بازنویسی منطق برنامه جابهجا کنند و در واقع به عنوان «fetch» دوران هوش مصنوعی عمل میکند. این SDK APIهای اختصاصی هر فروشنده را پشت یک تابع واحد مانند generateText میبرد تا تعویض مدلها مانند تغییر یک دوشاخه در پریز برق ساده باشد. تا سال ۲۰۲۶، AI SDK 6 به انتشار عمومی (GA) رسیده است و بسته [email protected] تا اواخر ژوئن همچنان بهروزرسانیهای خود را دریافت کرده است.
زمینه: چهار ستون اصلی SDK 6
برای درک کامل گذار از نسخه ۵ به ۶، لازم است به چهار تغییر کلیدی که وضعیت فعلی این کتابخانه را تعریف میکنند، نگاه کنیم:
- ToolLoopAgent: رابط رسمی (API) که مفهوم «عامل» را در قالب یک کلاس واحد دستهبندی و بستهبندی میکند.
- تأیید ابزار توسط انسان (Human-in-the-loop): یک مکانیسم ایمنی که پیش از اجرای یک فراخوانی ابزار پرریسک، یک مرحله بررسی انسانی را وارد چرخه میکند. در این زمینه، چالشهای متعددی وجود دارد که میتواند باعث شود نظارت انسانی نتواند بهطور کامل جلوی خطاهای عاملها را بگیرد و نرخ موفقیت مداخلات انسانی را کاهش دهد.
- خروجی ساختاریافتهٔ پایدار (Stabilized structured output): روشی برای اجبار مدل به بازگرداندن پاسخ در قالب یک شیء با شکل ثابت و تعریف شده، بهجای متن آزاد.
- ابزارهای توسعه (DevTools): مجموعهای از ابزارهای دیباگ که برای بازرسی دقیق گامهایی که یک عامل در حین اجرا برداشته است، طراحی شدهاند.
کالبدشکافی مکانیسم حلقه
پیشرفت در درک SDK 6 در گزینه stopWhen نهفته است. به طور پیشفرض، تابع generateText یک عملیات تکمرحلهای (One-shot) است. برای اثبات این موضوع، یک مدل شبیهساز را در نظر بگیرید که برای یک توالی خاص برنامهریزی شده است: در گام اول، یک فراخوانی ابزار با متن «ابزار آبوهوا را برای {city:'Seoul'} فراخوانی کن» برمیگرداند و در گام دوم، متن نهایی «دمای سئول ۲۱ درجه است» را ارسال میکند.
هنگام فراخوانی generateText({ model, tools, prompt }) بدون هیچ گزینهای، نتایج به این شکل است:
- مقدار مشاهده شده (Observation Value): متن نهایی یک رشتهٔ خالی «""» است.
- تعداد گامها (Step count): ۱.
- فراخوانیهای مدل (doGenerate): ۱.
- اجرای ابزار: بله ({city:'Seoul', tempC:21}).
در اینجا ابزار به وضوح اجرا شده است، اما پاسخ نهایی خالی است. این اتفاق میافتد چون مدل یک بار ابزار را فراخوانی کرد و متوقف شد؛ نتیجه هرگز به مدل بازنگشت و مدل نوبت دومی برای پاسخ دادن دریافت نکرد. به طور پیشفرض، generateText به محض اینکه یک ابزار یک بار فراخوانی شود، به پایان میرسد.
فعال کردن stopWhen این تابع را به یک عامل تبدیل میکند. با افزودن stopWhen: stepCountIs(5)، نتایج به شدت تغییر میکنند:
- مقدار مشاهده شده: متن نهایی «دمای سئول ۲۱ درجه است».
- تعداد گامها: ۲.
- فراخوانیهای مدل: ۲.
در این سناریو، SDK ابزار را اجرا کرد، نتیجه را به تاریخچه گفتگو اضافه کرد و دوباره مدل را فراخوانی نمود. این تایید میکند که یک «عامل» در AI SDK 6 صرفاً یک حلقه تکرار با یک شرط پایان است. شرایط دیگر شامل hasToolCall('weather') است که به محض فراخوانی یک ابزار خاص، چرخه را متوقف میکند، یا توابع سفارشی برای منطقهای پیچیدهتر. اگر hasToolCall('weather') تنظیم شود، فرآیند دقیقاً در گام اول و بلافاصل بعد از فراخوانی ابزار متوقف میشود.
کلاس ToolLoopAgent و تلهٔ هزینهها
ورسل کلاس ToolLoopAgent را معرفی کرد تا مدلها، ابزارها و شرایط توقف را در یک شیء واحد جمع کند. از نظر فنی، ToolLoopAgent به عنوان یک پوشش نازک (Thin wrapper) عمل میکند. در نصبهای فعلی، عبارت ToolLoopAgent === Experimental_Agent مقدار true دارد، زیرا نام بتای قدیمی همچنان به عنوان یک نام مستعار (Alias) باقی مانده است.
برخلاف رویکرد تابعی ساده در generateText، کلاس ToolLoopAgent با یک سقف ایمنی پیشفرض stepCountIs(20) عرضه میشود. وقتی یک مدل شبیهساز که بهطور بیپایان درخواست ابزار میکند را به هر دو رویکرد میدهیم، تفاوت چشمگیر است:
- generateText (بدون stopWhen): ۱ گام (بدون حلقه).
- ToolLoopAgent: ۲۰ گام (توقف در سقف پیشفرض).
این پیشفرض یک ریسک مالی قابل توجه ایجاد میکند. اگر مدلی هرگز از فراخوانی ابزارها دست نکشد، ToolLoopAgent بیصدا ۲۰ بار مدل را فراخوانی میکند و این یعنی کاربر ۲۰ بار هزینه پرداخت میکند. این مکانیسم از یک حلقه بیپایان جلوگیری میکند، اما همین سقف، در واقع سقف هزینههای شماست. پارامتر stopWhen همزمان هم به عنوان ترمز ایمنی و هم به عنوان پیچ تنظیم بودجه عمل میکند؛ عدد ۲۰ در این بافت، عملاً تمام هویت «عامل» است.
خودترمیمی در شکست ابزارها
یکی از رفتارهای غافلگیرکننده در SDK 6، رویکرد آن به خطاهاست. وقتی یک ابزار شکست میخورد — مثلاً یک تابع execute() که بلافاصله خطای throw new Error('TOOL_BOOM') را پرتاب میکند — SDK برنامه را کرش نمیدهد. بهجای پرتاب Exception، حلقه ادامه مییابد.
سیستم محتوای آن گام را به عنوان دو بخش ثبت میکند: ["tool-call", "tool-error"]. این tool-error بهجای نتیجه، به مدل بازگردانده میشود. در گام بعدی، مدل خطا را میبیند و میتواند برای بازیابی (Recovery) تلاش کند (مثلاً با تولید متن «بازیابی شد») یا ابزار دیگری را انتخاب کند. این ساختار خودترمیمی حتی زمانی که مدل ابزاری را فراخوانی کند که اصلاً وجود ندارد نیز اعمال میشود؛ این اتفاق صرفاً به عنوان یک tool-error ظاهر شده و حلقه ادامه مییابد.
با این حال، این بازخورد خودکار بدون نقص نیست:
- شکستهای خاموش: چون خطاها جذب گفتگو میشوند، توسعهدهنده ممکن است تنها پس از بررسی صورتحساب متوجه شکست شود: «ابزار شکست خورد، پس چرا من هزینه بیشتری پرداخت کردم؟»
- لغو تعاملی (Cooperative Cancellation): این رفتار مشابه لغو تعاملی در سیستمهاست که در آن سطح کنترل بستگی به این دارد که آیا فرآیند با متوقف شدن موافق است یا خیر. کنترل هرگز بهطور یکجانبه در دست نیست.
اعتبارسنجی زمان اجرا و خروجیهای ساختاریافته
SDK 6 بر مرز بین تایپهای زمان کامپایل (Compile-time) و اعتبارسنجی زمان اجرا (Runtime) تاکید دارد. برای دستیابی به شیئی با شکل ثابت مانند { city: string, tempC: number }، توسعهدهندگان میتوانند از generateObject یا مسیر جدید توصیهشده استفاده کنند: generateText({ output: Output.object({ schema }) }). اگرچه نتیجه دومی هنوز تحت نام experimental_output است و کاملاً تثبیت نشده، اما مسیر پیشنهادی فعلی است. شایان ذکر است که generateObject در نسخه ۶ علامت @deprecated (منسوخ) خورده است.
با استفاده از یک اسکیما در zod (z.object({ city: z.string(), tempC: z.number() }))، سه حالت پیش میآید:
۱. JSON معتبر: مقدار {"city":"Seoul","tempC":21} منجر به یک شیء تایپشده میشود. عملیات زمان اجرا مانند object.tempC * 2 = 42 بهدرستی کار میکند.
۲. نقض تایپ: مقدار {"city":"Seoul","tempC":"hot"} منجر به پرتاب خطای AI_NoObjectGeneratedError توسط SDK میشود.
۳. JSON نامعتبر: پاسخهایی که «به هیچ وجه JSON نیستند» نیز خطای AI_NoObjectGeneratedError را پرتاب میکنند.
این یک باور غلط رایج است که ارائه یک تایپ TypeScript تضمین میکند مدل از آن پیروی میکند. تایپ TypeScript یک وعده در زمان کامپایل است. در یک بررسی سختگیرانه (tsc --strict)، خروجی .text در generateText یک رشته بدون ساختار است، در حالی که .object.tempC در generateObject عددی است که از اسکیما استخراج شده است. اختصاص یک رشته به جایی که عدد باید باشد، خطای کامپایل ایجاد میکند. اما اعتبارسنجی واقعی در زمان اجرا توسط zod انجام میشود. اگر مدل وعده تایپی را بشکند، SDK بهجای اینکه بهطور خاموش آن را تطبیق دهد، یک Exception پرتاب میکند.
تلههای مهاجرت و استریمینگ
استریمینگ در SDK 6 با استفاده از streamText جریانی از بخشهای تایپشده است. هنگام استفاده از یک مدل شبیهساز برای ردیابی fullStream، ترتیب اتفاقات دقیقاً به این صورت است:start $\rightarrow$ start-step $\rightarrow$ text-start $\rightarrow$ text-delta $\rightarrow$ text-end $\rightarrow$ tool-input-start $\rightarrow$ tool-input-delta $\rightarrow$ tool-input-end $\rightarrow$ tool-call $\rightarrow$ tool-result $\rightarrow$ finish-step $\rightarrow$ finish
این ساختار به رابط کاربری (UI) اجازه میدهد دقیقاً نمایش دهد که مدل در حال حاضر در حال نوشتن متن است یا در حال فراخوانی ابزار. نکته حیاتی این است که tool-result در میانه استریم و بلافاصله بعد از tool-call ظاهر میشود. این یعنی SDK منتظر پایان استریم نمیماند تا ابزار را اجرا کند؛ بلکه ابزار را در میانه جریان اجرا کرده و نتیجه را دوباره به استریم میچسباند.
توسعهدهندگانی که از نسخه ۵ مهاجرت میکنند با یک تله خاموش در مورد maxSteps روبرو هستند. در نسخه ۵، maxSteps حلقه چندمرحلهای را فعال میکرد. در نسخه ۶، این گزینه حذف و به stopWhen تغییر نام داد. پارامترهای سیستم نیز به طور مشابه به instructions تغییر نام یافتند. اگر توسعهدهندهای maxSteps: 5 را در محیط نسخه ۶ (که بررسی تایپ را نادیده میگیرد، مانند JS خالص یا بیلدهای شل) ارسال کند، این گزینه بهطور خاموش نادیده گرفته میشود. در این حالت، عامل به یک تابع تکمرحلهای تنزل مییابد و پاسخ نهایی یک رشته خالی میشود، بدون اینکه هیچ خطایی در زمان اجرا رخ دهد.
نقش مدلهای شبیهساز و مقایسهها
برای اعتبارسنجی این رفتارها بدون سوزاندن اعتبار API، ورسل MockLanguageModelV3 را در بسته ai/test ارائه داده است. این ابزار به توسعهدهندگان اجازه میدهد دقیقاً برنامهریزی کنند که مدل چه چیزی برگرداند — مثلاً «ابتدا این فراخوانی ابزار و سپس این متن را برگردان» — و به این ترتیب جریان کنترل عامل، شرایط توقف، مدیریت خطا و ترتیب استریمینگ را قطعی (Deterministic) میکنند. این موضوع حیاتی است زیرا مدلهای زبانی واقعی دمدمیمزاج هستند و ممکن است آرگومانهای بیمعنی برای ابزارها تولید کنند یا اشتهای سیریناپذیری برای فراخوانی ابزارها داشته باشند. یک مدل شبیهساز «قرارداد» رفتاری SDK را نشان میدهد، در حالی که ارائهدهندگان واقعی نشان میدهند مدلها چگونه این قرارداد را میشکنند.
در مقایسه با سایر گزینهها، AI SDK 6 در جایگاه خاصی قرار دارد:
| گزینه | سطح انتزاع | ایمنی تایپی | تستپذیری | ریسک وابستگی (Lock-in) |
|---|---|---|---|---|
| AI SDK 6 | متوسط (Provider + Loop) | بالا (TS + zod) | بالا (Mock Model) | پایین (Multi-provider) |
| LangChain.js | بالا (Chains/Graphs) | متوسط | متوسط | متوسط (وابستگی شدید به اکوسیستم) |
| Mastra | بالا (Workflow-centric) | بالا (TS-first) | متوسط | متوسط |
| OpenAI SDK | متوسط (Agent-spec) | متوسط | متوسط | بالا (OpenAI-flavored) |
| Raw fetch | هیچ | دستی | دستی | هیچ |
جمعبندی نهایی
پیادهسازی یک قابلیت هوش مصنوعی امروز مستلزم این است که دقیقاً بدانید عامل شما روی کدام حلقه در حال اجراست. نکات کلیدی عبارتند از:
- یک عامل، یک حلقه تکرار است که توسط
stopWhenفعال میشود. - تابع
generateTextبهطور پیشفرض خاموش است؛ اماToolLoopAgentبا پیشفرض ۲۰ گام روشن است. - هر گام از حلقه یک هزینه است؛
stopWhenپیچ تنظیم بودجه شماست. - شکست ابزارها بهطور پیشفرض باعث توقف برنامه نمیشود؛ آنها به مدل بازگردانده میشوند. این خودترمیمی راحت است اما میتواند به یک هزینه خاموش تبدیل شود.
- تایپهای TypeScript $\neq$ اعتبارسنجی زمان اجرا؛ zod دومی را مدیریت میکند و
generateObjectبه مدلی که وعده تایپی را میشکند، با یک Exception پاسخ میدهد. - گزینه
maxStepsحذف شده است؛ برای جلوگیری از شکست خاموش در زمان مهاجرت، ازstopWhenاستفاده کنید.
بدون این دیدهبندی، شما یک عامل را مدیریت نمیکنید، بلکه در حال مدیریت یک نشت بودجه هستید. در عصری که «عاملها کد مینویسند»، استفاده از یک عامل بدون دانستن اینکه در داخل آن چه حلقهای در جریان است، مخاطرهبار است.
گام بعدی شما
- اگر از نسخه ۵ استفاده میکنید، فوراً تمام
maxStepsها را بهstopWhenتغییر دهید تا از شکست خاموش سیستم جلوگیری کنید. - برای کنترل هزینهها، سقف پیشفرض ۲۰ گامه در
ToolLoopAgentرا به عدد پایینتری (مثلاً ۵ یا ۱۰) تغییر دهید. - از
MockLanguageModelV3برای تست لبههای شکست (Edge Cases) در توقف حلقهها استفاده کنید.
اما داستان سختافزاری این تحول حتی شگفتانگیزتر است — به تحلیل ما دربارهی تراشههای Blackwell مراجعه کنید.




گفتگو