اگر امروز یک دستیار هوش مصنوعی میسازید، کاربران شما احتمالاً پس از ۱۰ ثانیه خیره شدن به یک صفحه خالی و یک نشانگر بارگذاری (loading spinner)، اپلیکیشن را میبندند. این دقیقاً بحرانی بود که یک توسعهدهنده هنگام ساخت یک ربات مستندات با آن روبرو شد؛ جایی که ادغامهای ساده و ابتدایی با API منجر به ریزش شدید کاربران میشد. هدف پروژه ساده بود: کاربران یک سوال را تایپ میکنند و یک مدل هوش مصنوعی پاسخی را که از دل مستندات استخراج شده، بازمیگرداند. با این حال، هر پرسوجو ۱۰ تا ۱۵ ثانیه زمان میبرد و کاربران را با یک صفحه خالی و یک مشتری ناراضی رها میکرد.
بسیاری از برنامهنویسان با APIهای هوش مصنوعی مانند نقاط انتهایی REST سنتی رفتار میکنند و منتظر میمانند تا کل بسته JSON دریافت شود و سپس آن را رندر کنند. در یک محیط عملیاتی و واقعی، این یک اشتباه استراتژیک و بحرانی است. تصور کنید پیتزا سفارش میدهید اما تا زمانی که تمام تکهها در جعبه قرار نگیرند و کل پیتزا تحویل داده نشود، حتی یک تکه را نمیخورید؛ این ناکارآمدترین راه برای مصرف محصولی است که اساساً تکهتکه تولید میشود.
رویکرد سنتی (که شکست خورد)
پیادهسازی اولیه این توسعهدهنده بر پایه یک فراخوانی ساده requests.post() بود. در این بسته ارسالی (payload)، متن زمینه (context) و سوال کاربر قرار داشت و محدودیت max_tokens روی ۵۰۰ توکن تنظیم شده بود. از آنجایی که متد requests.post() تا زمانی که پاسخ کامل بازگردانده نشود، اجرای برنامه را مسدود (block) میکند، کاربر مجبور است برای دریافت کل توالی توکنها منتظر بماند.
طبق جزئیات فنی، گلوگاه اصلی سرعت مدل نبود؛ مدل به طور متوسط ۵۰ توکن در ثانیه تولید میکرد. اما مشکل اصلی ماهیت مسدودکننده درخواست بود. انتظار برای دریافت ۵۰۰ توکن با این سرعت، به معنای حداقل ۱۰ ثانیه انتظار اجباری برای کاربر است.
آنچه امتحان شد (و چرا شکست خورد)
پیش از رسیدن به راهکار نهایی، توسعهدهنده سه روش رایج را برای رفع مشکل امتحان کرد که هیچکدام هسته مشکل را حل نکردند:
- HTTP غیرهمزمان (aiohttp): اگرچه async به مدیریت همزمانی کمک میکند، اما رفتار سرور را تغییر نمیدهد. درخواست همچنان تنها زمانی بازمیگردد که پاسخ کامل آماده شده باشد.
- پیشمحاسبه پاسخها (Pre-computing): سیستم کشینگ برای سوالات متداول (FAQs) ساده جواب داد، اما پیشمحاسبه پاسخ برای هر سوال احتمالی در حجم گسترده مستندات غیرممکن بود.
- کاهش max_tokens: محدود کردن پاسخ به ۱۰۰ توکن، تأخیر را به حدود ۲ ثانیه کاهش داد، اما پاسخها اغلب در میانه راه قطع میشدند و بیفایده بودند؛ بنابراین این سبک هزینه، پذیرفتنی نبود.
برای حل این بحران، توسعهدهنده دو مکانیزم فنی خاص را پیاده کرد:
۱. استریمینگ آنی (Real-Time Streaming)
با استفاده از رویدادهای ارسالی سرور (Server-Sent Events یا SSE) و کتابخانه httpx در پایتون، سیستم شروع به تحویل متن در قالب تکههای کوچک (chunks) کرد. در این پیادهسازی، از متد httpx.stream("POST", url, json=data) استفاده شد تا خطوط پاسخ به محض رسیدن، پیمایش و ارسال شوند. در سمت فرانت-اند، یک EventSource جاوااسکریپتی به کار گرفته شد تا توکنها را در لحظه به یک element از نوع div اضافه کند.
- نتیجه: اکنون اولین توکن در مدت ۲۰۰ تا ۳۰۰ میلیثانیه میرسد (که به آن زمان رسیدن به اولین توکن یا TTFT میگویند).
- تأثیر بر تجربه کاربری (UX): کاربران میبینند که دستیار در حال «فکر کردن» و تایپ کردن در لحظه است. این موضوع نرخ تعامل را بهشدت افزایش میدهد، حتی اگر تولید کامل ۵۰۰ توکن همچنان ۱۰ ثانیه زمان ببرد.
۲. کش معنایی (Semantic Caching)
برای مدیریت سوالات تکراری یا تقریباً یکسان، توسعهدهنده از تطبیق ساده رشتههای متنی (string matching) فراتر رفت. با استفاده از sentence-transformers (بهطور مشخص مدل all-MiniLM-L6-v2) و پایگاه داده برداری در-حافظه FAISS، سیستم اکنون میتواند سوالاتی با معانی مشابه را شناسایی کند.
- سازوکار: سیستم یک بردار معنایی (Embedding) ۳۸۴ بُعدی از پرسوجوی کاربر محاسبه میکند و آن را با استفاده از دستور
index.search(emb, 1)در یک ایندکس برداری مقایسه میکند. - آستانه شباهت: یک آستانه شباهت ۰.۸۵ تعیین شد تا مشخص شود آیا پاسخ کششده به اندازه کافی نزدیک هست که بتوان آن را به کاربر ارائه داد یا خیر.
- نتیجه: اکنون تقریباً ۳۰ درصد از پرسوجوها در کمتر از ۱۰ میلیثانیه پاسخ میگیرند و بهطور کامل از فراخوانی API هوش مصنوعی عبور میکنند.
این تغییر در معماری ثابت میکند که تأخیر دریافتشده (perceived latency) اغلب یک مشکل مربوط به فرانت-اند و نحوه یکپارچهسازی است، نه یک محدودیت در مدل. برای خواننده، این بدان معناست که «سریع بودن» یک ویژگی هوش مصنوعی، کمتر به تعداد پارامترهای LLM و بیشتر به کارایی خط لوله دادهها (data pipeline) بستگی دارد.
درسها و سبکاسازیها (Trade-offs)
این دستاوردها با هزینههای مهندسی خاصی همراه هستند. استریمینگ بهبود عظیمی در UX ایجاد میکند اما مدیریت آن در بک-اند سختتر است، زیرا نیازمند مدیریت اتصالات باز و قطع شدنهای احتمالی است. علاوه بر این، همه APIهای هوش مصنوعی از استریمینگ پشتیبانی نمیکنند.
کش معنایی هزینهها و تعداد فراخوانیهای API را بهشدت کاهش میدهد، اما یک سربار ۱۰۰ میلیثانیهای برای جستجوی Embeddingها اضافه میکند. همچنین تنظیم آستانه شباهت دشوار است: آستانهای که خیلی پایین باشد منجر به پاسخهای نامرتبط میشود و آستانهای که خیلی بالا باشد، باعث میشود کش بهندرت مورد استفاده قرار گیرد.
راهنمای پیادهسازی
توسعهدهندگان باید اندازهگیری TTFT (زمان رسیدن اولین توکن) را به عنوان معیار اصلی UX خود در اولویت قرار دهند، زیرا اکثر ابزارهای مانیتورینگ به طور پیشفرض آن را ردیابی نمیکنند. با این حال، این سیستمهای پیچیده همیشه لازم نیستند. اگر پرامپتهای شما بسیار پویا هستند (هر پرسوجو منحصربهفرد است)، کشینگ کمکی نخواهد کرد. اگر تأخیر شما در حال حاضر زیر دو ثانیه است، یا اگر کاربران نیاز به یک پاسخ کامل و تأیید شده (مانند یک حقیقت-سنج یا fact-checker) پیش از مطالعه دارند، استریمینگ ممکن است غیرضروری یا حتی گیجکننده باشد.
برای بهینهسازی بیشتر، توصیه میشود از ابتدا یک سرور غیرهمزمان FastAPI به همراه SSE مستقر کنید تا زمان خود را روی رویکردهای مسدودکننده تلف نکنید. علاوه بر این، افزودن یک دکمه «تولید مجدد» (Regenerate) به کاربران اجازه میدهد تا در صورتی که پاسخ کششده غیردقیق به نظر میرسد، یک فراخوانی تازه از API را اجبار کنند.




گفتگو