با ادغام فزاینده مدلهای زبانی بزرگ (LLMs) در برنامههای عملیاتی، انتظار برای پاسخدهی فوری هرگز به این اندازه بالا نبوده است. الگوهای درخواست-پاسخ سنتی، که در آنها مشتری منتظر تکمیل کامل تولید میماند تا خروجی را نمایش دهد، اغلب منجر به تأخیرهای بسیار بالا و غیرقابل قبول میشوند. مشکل «زمان تا اولین توکن» برای تولید محتوای طولانیمدت بهویژه حاد است. راه حل چیست؟ جریانیسازی توکن (Token Streaming).
جریانیسازی توکن تکنیکی است که توکنهای تولید شده (کلمات یا بخشهایی از کلمات) را به محض تولید به مشتری ارسال میکند، به جای اینکه منتظر پایان کامل پاسخ بماند. با پیادهسازی این الگو، توسعهدهندگان میتوانند تأخیر ادراکشده را بهطور قابل توجهی کاهش دهند و به کاربران اجازه دهند تا شروع پاسخ را در حالی که هوش مصنوعی هنوز در حال فکر کردن به انتهای آن است، بخوانند. این پست وبلاگ معماری، جزئیات پیادهسازی و بهترین شیوهها را برای ساخت زیرساخت جریانیسازی توکن مقاوم بررسی میکند.
معماری جریانیسازی
در سطح بالا، جریانیسازی توکن نیازمند یک اتصال پایدار بین مشتری و سرور است. رایجترین پروتکل برای این کار در زمینههای وب، رویدادهای ارسالی سرور (SSE) است. برخلاف وبسوکتها که دوطرفه هستند، SSE یک کانال یکطرفه است که برای فشار دادن دادهها از سرور به مشتری ایدهآل میباشد. این پروتکل بر روی HTTP ساخته شده است، بنابراین پروکسی کردن آن از طریق فایروالها و تعادلکنندههای بار استاندارد بدون نیاز به اضافهبار پیکربندی اضافی آسان است.
هنگامی که یک LLM پاسخی را تولید میکند، کل متن را یکجا تولید نمیکند. در عوض، توکنها را به صورت خودتکرارشونده (auto-regressive) و یکییکی تولید میکند. نقش سرور، دریافت این توکنها از جریان خروجی مدل و ارسال فوری آنها به مشتری متصل است. این فرآیند، یک درخواست HTTP مسدودکننده (blocking) را به یک جریان داده پیوسته تبدیل میکند.
پیادهسازی رویدادهای ارسالی سرور در پایتون
پیادهسازی SSE با استفاده از چارچوبهای ناهمگام مدرن مانند FastAPI ساده است. در زیر یک مثال عملی از نحوه ایجاد نقطه پایانی (endpoint) که پاسخها را از یک مشتری LLM فرضی جریانیسازی میکند، آورده شده است.
import asyncio
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
app = FastAPI()
async def generate_stream(prompt: str):
"""
شبیهسازی تولید توکنها یکییکی توسط یک LLM.
در محیط عملیاتی، این باید یک تکرارگر ناهمگام بر روی خروجی مدل باشد.
"""
# توکنهای شبیهسازی شده
tokens = [
"The ",
"future ",
"of ",
"AI ",
"is ",
"here.",
"\n\n",
"Streaming ",
"reduces ",
"latency."
]
for token in tokens:
# ارسال توکن با فرمت SSE
yield f"data: {token}\n\n"
# شبیهسازی تأخیر شبکه یا پردازش مدل
await asyncio.sleep(0.1)
@app.post("/chat/stream")
async def stream_response(prompt: str):
return StreamingResponse(
generate_stream(prompt),
media_type="text/event-stream"
)
مدیریت پایداری اتصال و بازیابی خطا
در حالی که جریانیسازی تجربه کاربری را بهبود میبخشد، پیچیدگیهایی را در مورد پایداری اتصال ایجاد میکند. قطعیهای شبکه میتوانند جریان را قطع کنند و کاربر را با محتوای ناقص رها کنند. برای کاهش این مشکل، همیشه یک فیلد id را در پیامهای SSE خود گنجانید. این امکان را به مشتری میدهد تا تنها توکنهای جدید را از یک نقطه خاص درخواست کند و اتصال مجدد را بدون از دست دادن داده تسهیل میکند.
علاوه بر این، در نظر بگیرید که استراتژیهای بازگشت با تأخیر (backoff) را در سمت مشتری پیادهسازی کنید. اگر اتصال قطع شود، مشتری نباید بلافاصله تلاش مجدد کند؛ در عوض، باید از بازگشت با تأخیر نمایی (exponential backoff) برای جلوگیری از تحمیل بار بیش از حد به سرور در دورههای ناپایداری استفاده کند. برای تولیدات طولانیمدت، ممکن است بخواهید یک مکانیسم زمانبندی (timeout) نیز پیادهسازی کنید که اگر مشتری زودتر از موعد قطع شد، منابع استفاده نشده را در سرور پاکسازی کند.
نتیجهگیری
جریانیسازی توکن دیگر یک لوکس نیست، بلکه ضروری برای برنامههای هوش مصنوعی مدرن است. با بهرهگیری از رویدادهای ارسالی سرور و برنامهنویسی ناهمگام، توسعهدهندگان میتوانند تجربههایی پاسخده و مشابه ChatGPT را برای کاربران خود ارائه دهند. با بالغتر شدن زیرساختها، انتظار میرود کتابخانهها و بهترین شیوههای استاندارد بیشتری ظهور یابند که یکپارچهسازی جریانیسازی را حتی در دسترستر میکند. از امروز جریانیسازی را پیادهسازی کنید تا تعامل بلادرنگی را که کاربران انتظار دارند، به آنها بدهید.