تصور کنید برای هر پاسخی که مدل زبانی شما تولید میکند پول میپردازید، اما کاربر حتی قبل از خواندن خط اول، تب مرورگر را میبندد. اگر سیستمی ندارید که این قطع ارتباط را فوراً به مدل بفهماند، شما در حال پرداخت هزینه برای توکنهایی هستید که هیچکس آنها را نمیبیند. اکثر آموزشهای موجود، ۸۰ درصد از فرآیند استریم را به درستی پوشش میدهند، اما ۲۰ درصد نهایی — یعنی مدیریت قطع اتصال کلاینت — میتواند هزینههای هنگฟتی برای شرکتها به همراه داشته باشد.
این نشت مالی رایج در اپلیکیشنهای هوش مصنوعی، محوریت یک اصلاحیه فنی حیاتی برای استریم توکنهای Claude در مرورگر بود که در ۲۴ ژوئن ۲۰۲۶ توسط یک توسعهدهنده به اشتراک گذاشته شد. استریم پاسخهای مدل زبانی بزرگ (LLM) — شبیه به پخش زنده یک فیلم که نیازی نیست تا پایان دانلود منتظر بمانید — معمولاً یک مسئله حلشده به نظر میرسد، اما وقتی با شبکههای واقعی و ناپایدار مواجه میشود، چالشهای مالی عجیبی ایجاد میکند. همانطور که در تحلیل قبلی ما دربارهی استفاده از Notion به عنوان مرکز حافظه پویا برای پروژههای مدلهای Anthropic اشاره کردیم، اکنون تمرکز توسعهدهندگان بر زیرساختهای انتقال (Plumbing) این پاسخهای با بافت بالا (High-Context) است تا بدون «سوزاندن» بودجه، تجربه کاربری را بهبود بخشند.
حالت شکست در محیط عملیاتی
طبق گزارش منتشرشده در dev.to، نسخه سادهای از استریم که در محیط محلی (Local) شما عالی کار میکند، در مواجهه با اتصالات ضعیف یا مدلهای سریع شکست میخورد. حالت شکست اصلی که در دموها دیده نمیشود این است: کاربر در حالی که مدل هنوز در حال تولید است، از صفحه خارج میشود، تب را میبندد یا دچار افت اتصال میشود.
تصور کنید یک مدل سریع در حال تولید یک پاسخ حجیم ۶۴,۰۰۰ توکنی باشد. اگر کاربر در میانه استریم تب مرورگر خود را ببندد، یک سرور ساده همچنان توکنها را از API دریافت کرده و آنها را در یک «لوله بسته» میریزد. در واقع، شما هزینه خروجیهایی را میپردازید که هیچ کاربر نهایی آنها را دریافت نمیکند. در مقیاس تولید و با خروجیهای ۶۴ هزار توکنی، یک استریم رهاشده که به تولید ادامه میدهد، به معنای از دست رفتن پول واقعی است.
جزئیات پیادهسازی فنی
برای رسیدن به یک ساختار مقاوم، طبق راهنمای dev.to، سه حرکت معماری خاص ضروری است:
- انتشار سیگنال Abort از انتها به انتها: در یک Route Handler در Next.js، سرور یک
ReadableStreamبرمیگرداند. شما باید حتماًrequest.signalرا به فراخوانیclient.messages.streamپاس دهید. علاوه بر این، باید یک Event Listener اضافه کنید:request.signal.addEventListener("abort", () => { llm.abort(); controller.close(); });. این کار تضمین میکند که به محض قطع اتصال کلاینت، SDK تولید توکنها را فوراً متوقف کند. - غیرفعال کردن بافر پروکسی: برای جلوگیری از تأخیر، باید از هدر
X-Accel-Buffering: noاستفاده کنید. بدون این هدر، Nginx استریم را در بافر نگه میدارد و کاربر تا زمان تکمیل کل پاسخ چیزی نمیبیند که عملاً هدف استریم را نابود میکند. این چالش با پیادهسازیهای پیشرفته SSE برای حذف تأخیرهای طولانی مرتبط است که بر بهینهسازی زمان پاسخگویی مدلها تمرکز داشت. همچنین، پاسخ باید شامل هدرهایContent-Type: text/event-streamبرای شناسایی SSE،Cache-Control: no-cacheبرای جلوگیری از ذخیره پاسخ وConnection: keep-aliveباشد. - بافرینگ در سمت کلاینت: از آنجایی که تکههای داده (Chunks) در مرزهای نامشخص و 임ثی تبدیل به رشته میشوند، مرورگر باید دادهها را بافر کند. پیادهسازی باید از
TextDecoderاستفاده کرده و بافر را بر اساس جداکننده SSE یعنی `
` تقسیم کند. کد باید بخش ناقص انتهای بافر (Tail) را برای تکه بعدی نگه دارد تا یکپارچگی JSON حفظ شود و پیامها نصفه رندر نشوند.
مکانیزم سمت سرور
پیادهسازی سرور از یک ReadableStream برای انتقال رویدادهای Claude استفاده میکند. منطق برنامه روی رویدادهای llm میچرخد؛ جایی که event.type برابر با content_block_delta و event.delta.type برابر با text_delta باشد. این دادههای متنی کدگذاری شده و به صورت data: { text: ... } در صف قرار میگیرند. در نهایت، سیستم یک سیگنال done: true برای اعلام پایان یا یک پیام خطا در صورت شکست استریم ارسال میکند.
بهینهسازی تجربه کاربری
عملکرد فقط در سطح شبکه اتفاق نمیافتد. مدلهای سریع اغلب توکنها را سریعتر از توان بازترسیم (Repaint) DOM تولید میکنند. بهروزرسانی وضعیت React روی هر تک توکن، باعث فشار زیاد به UI (Thrashing) شده و تجربهای کند و لرزان برای کاربر ایجاد میکند.
برای حل این مشکل، مرورگر باید از AbortController استفاده کند. با بازگرداندن این کنترلر به کامپوننت React، رابط کاربری میتواند در تابع Cleanup خود controller.abort() را صدا بزند و سیگنال توقف را تا سرور منتقل کند.
راهکار توصیهشده برای رندرینگ، بافر کردن تعدادی از توکنها یا استفاده از requestAnimationFrame برای تخلیه بهروزرسانیها در دستههای (Batches) کوچک است. از آنجایی که انسانها سریعتر از تقریباً ۱۰ بهروزرسانی در ثانیه نمیتوانند بخوانند، این روش دستهای (Batching) نرمی رابط کاربری را بدون کاهش سرعت ادراکی حفظ میکند.
این رویکرد، فرض «شبکه کامل است» را به «شبکه ناپایدار است» تغییر میدهد. در محیط عملیاتی، تفاوت بین یک استریم رهاشده که همچنان هزینه میبرد با استریمی که فوراً متوقف میشود، بسیار حیاتی است و مستقیماً بر سودآوری اثر میگذارد.
گام بعدی شما
- تمامی مسیرهای استریم (Streaming Routes) فعلی خود را ممیزی کنید تا مطمئن شوید
request.signalواقعاً به مکانیزم Abort ارائهدهنده LLM شما متصل است. - هدرهای Nginx را برای جلوگیری از بافرینگ بررسی کنید تا تأخیر در نمایش توکنها به حداقل برسد.
- برای رندرینگ توکنها در فرانتاند، به جای بهروزرسانی لحظهای، از روش دستهای (Batching) یا
requestAnimationFrameاستفاده کنید.
اما مدیریت هزینه تنها بخشی از داستان است؛ برای بهینهسازی مصرف توکنها در پنجرههای متنی بزرگ، تحلیل ما درباره استراتژیهای Caching را بخوانید.




گفتگو