تصور کنید یک تست خودکار در Cypress بهتنها بهدلیل اینکه مدل عبارت «سلام، من» را در یک تکه فرستاده بهجای دو تکه، با خطا مواجه شود. در این لحظه شما در حال تست کردن جزئیات انتقال داده هستید، نه تجربه کاربر.
هر تستی که انتظار توالی صلب توکنها را داشته باشد — مثلاً «سلا» سپس «سلام» و بعد «سلام، من» — محکوم به شکست است؛ زیرا رفتار ارائهدهنده، زمانبندی شبکه یا بافرینگ تغییر میکند و تستها را بهشدت شکننده میکند.
بسیاری از توسعهدهندگان با جریان بایتهای ورودی مانند مجموعهای از کالبکهای مستندنشده برخورد میکنند. این رویکرد محیطی متزلزل میسازد که در آن یک بهروزرسانی ساده در کتابخانه کلاینت یا تغییر در بافرینگ ارائهدهنده، کل مجموعه تست را میشکند، حتی اگر نتیجه نهایی برای کاربر درست باشد. طبق گزارشی که در ۱۸ سپتامبر ۲۰۲۶ در dev.to منتشر شد، راهکار این مشکل تغییر رویکرد از «تست توکنها» به «تست قراردادها» است. این چالشها نشان میدهد که چرا صرفاً تکیه بر فراخوانیهای سادهی API برای ایجاد جریانهای کاری پایدار کافی نیست و نیاز به معماریهای پیچیدهتری برای مدیریت گردش کار در محیط عملیاتی وجود دارد.

برای پایدار کردن این تستها، توسعهدهندگان باید استریم را به مجموعهای از رویدادهای تعریفشده تبدیل کنند. بهجای متن خام، سیستم باید یک نوع داده به نام AgentEvent را ردیابی کند. این نوع داده باید رویدادهایی مانند موارد زیر را بهطور صریح تعریف کند:
started: شامل یک رشتهrunId.text_delta: شاملrunIdو رشتهtext.tool_started: شاملrunIdو نام ابزار.tool_completed: شاملrunIdو نام ابزار.completed: شاملrunId.failed: شاملrunIdو کد خطا.
رویکرد ماشین حالت
با مدلسازی استریم بهعنوان یک ماشین حالت (State Machine) — شبیه به یک نقشه راه که دقیقاً میگوید سیستم در هر لحظه در چه وضعیتی است و چه تغییری میتواند رخ دهد — رندرکننده مجموعهای کوچک از حالتهای قابل مشاهده برای کاربر را استخراج میکند:
بیکار (Idle) $\rightarrow$ در حال اتصال $\rightarrow$ در حال استریم $\rightarrow$ در حال استفاده از ابزار $\rightarrow$ در حال استریم $\rightarrow$ تکمیل شده
همچنین هر حالتی میتواند مستقیماً به حالت «خطا» (Failed) برود.
همانطور که در تحلیلهای قبلی ما دربارهی معماری عاملهای هوش مصنوعی اشاره کردیم، جداسازی منطق وضعیت از دادههای خام کلید پایداری است. بر اساس آموزشهای dev.to، هدف این است که تأیید شود رابط کاربری پیشرفت را نشان میدهد و تفاوت بین فراخوانی ابزار و متن معمولی را تشخیص میدهد، فارغ از اینکه ارائهدهنده جمله را در چند تکه ارسال کرده است. این رویکرد بهویژه برای کاهش تأخیر در توکنهای نخستین و بهبود ادراک کاربر از سرعت پاسخدهی حیاتی است.
پیادهسازی استریمهای مصنوعی
برای تستهای سرتاسری (End-to-End)، لایه انتقال باید پشت یک رابط (Interface) قرار گیرد. در نسخههای تست، توسعهدهندگان میتوانند یک منبع رویداد مصنوعی را از طریق شیء window، مثلاً window.agentTestStream در دسترس قرار دهند. این کار به Cypress اجازه میدهد بدون فراخوانی مدل زنده، علیت رویدادها را کنترل کند.
بهعنوان مثال، یک تست میتواند رویداد tool_started را برای ابزاری به نام "search_policy" صادر کند و بلافاصله تأیید کند که رابط کاربری عبارت «در حال جستوجوی سیاستها» را نمایش میدهد. پس از صدور رویداد tool_completed و ارسال آخرین text_delta (مثلاً «مرجوعیها تا ۳۰ روز پذیرفته میشوند»)، تست بهجای بررسی تکتک کاراکترها، نقاط عطف معنایی نهایی را تأیید میکند.
تست «مسیرهای ناموفق»
بیشترین ارزش تستها در سناریوهایی است که استریم باعث میشود بهراحتی نادیده گرفته شوند:
- اجراهای منقضیشده (Stale Runs): اجرای اول را شروع کنید، سپس اجرای دوم را. یک تکه داده دیررس از اجرای اول بفرستید و تأیید کنید که نادیده گرفته شده است. هر رویداد باید شناسه اجرا داشته باشد و رندرکننده فقط فعالترین شناسه را بپذیرد.
- لغو عملیات: پس از کلیک کاربر روی «توقف»، یک تکه داده دیگر بفرستید. رابط کاربری باید در حالت لغو باقی بماند و متنی اضافه نکند. همچنین باید تأیید شود که تابع
abortلایه انتقال فراخوانی شده است. - خطاهای جزئی: رویداد
tool_startedو سپسfailedرا صادر کنید. متنهای ناقص نباید رابط کاربری را بهگونهای نشان دهند که انگار پاسخ کامل شده است؛ خطا باید قابل مشاهده باشد. - رویدادهای تکراری: در هنگام اتصال مجدد، ممکن است رویدادها تکرار شوند. با دادن شناسههای پایدار به رویدادها، تأیید کنید که یک تکه داده تکراری، دوبار رندر نمیشود.
- قطع ناگهانی: لایه انتقال را پس از چند تکه داده اما قبل از رویداد
completedببندید. رابط کاربری باید به حالت «قطع شده» برود، نه اینکه متن ناقص را بهعنوان پاسخ نهایی ارائه دهد.
لایههای پروتکل و رهگیری شبکه
اگرچه cy.intercept() برای وضعیتهای HTTP و خطاهای احراز هویت مفید است، اما دقت لازم برای مجموعهای طولانی از رویدادهای سطح اپلیکیشن را ندارد. استفاده از یک آداپتور (Adapter) — شبیه به یک مبدل برق که ولتاژ را برای دستگاه شما مناسب میکند — باعث میشود رفتار مرورگر واقعی بماند اما زمانبندی رویدادها قطعی (Deterministic) شود.
این تفکیک معماری به تیمها اجازه میدهد شکستها را بهراحتی طبقهبندی کنند: یا مشکل در پروتکل است (بستهبندی/تجزیه) یا در رندرینگ (وضعیت UI). در لایه پروتکل، باید حالتهایی را تست کرد که یک رویداد منطقی در چندین تکه شبکه تقسیم شده یا چندین رویداد در یک تکه میرسند. تجزیهکننده (Parser) باید این فریمها را بازسازی کند تا کامپوننت UI از جزئیات TCP یا SSE بیخبر بماند.
تأیید مسئولیتها
تستهای استوار باید تغییرات وضعیت قابل مشاهده، ترتیب فعالیتهای ابزار و اعلانهای دسترسیپذیری (Accessibility) را بررسی کنند. برای دسترسیپذیری، تأیید کنید که تغییرات وضعیت فقط یکبار اعلام میشوند و تکههای دادهای که با سرعت میرسند، باعث بمباران مناطق زنده (Live Regions) نمیشوند. در حالی که پاسخ بصری بهطور مداوم بهروز میشود، فناوریهای کمکی باید فقط رویدادهای کلیدی مانند «در حال جستوجو»، «نیاز به تأیید» و «تکمیل شده» را دریافت کنند.
با تأیید مسئولیتها — مانند نبود محتوای تکراری و نتیجه ساختاریافته نهایی — بهجای تأیید متن، تستها نسبت به باگهای واقعی سختگیرتر و نسبت به زمانبندی توکنها حساستر میشوند.
گام بعدی شما
- لایه انتقال دادههای AI خود را پشت یک Interface قرار دهید تا امکان تزریق استریمهای مصنوعی فراهم شود.
- برای هر رویداد استریم یک
runIdتعریف کنید تا از تداخل پاسخهای قدیمی و جدید جلوگیری کنید. - تستهای لبه (Edge Cases) مانند قطع ناگهانی شبکه را به جای تکیه بر متن، بر اساس تغییر وضعیت ماشین حالت بنویسید.
اما مدیریت حافظه در این استریمهای طولانی چالش بزرگتری است — به تحلیل ما دربارهی بهینهسازی KV Cache مراجعه کنید.




گفتگو