البث المباشر للردود (Streaming Responses): كيف تجعل الكلمات تظهر تباعاً مثل ChatGPT؟
ما هو Streaming Responses ولماذا يبدو أكثر “ذكاءً” للمستخدم؟
عند بناء تطبيقات تعتمد على نماذج اللغة الكبيرة LLMs، فإن عرض الرد دفعة واحدة بعد اكتماله ليس دائماً أفضل تجربة. أسلوب Streaming Responses يعني إرسال أجزاء الرد تدريجياً فور توليدها، بحيث تبدأ الكلمات أو الرموز بالظهور مباشرة للمستخدم، تماماً كما يحدث في ChatGPT. هذا لا يجعل النموذج أسرع حسابياً بالضرورة، لكنه يحسن Perceived Latency؛ أي الزمن الذي يشعر به المستخدم.
برمجياً، النموذج لا ينتج النص كاملاً دفعة واحدة، بل يولّد Tokens متتابعة بعد مرحلة Tokenization. لذلك يمكن للتطبيق التقاط كل جزء فور صدوره من واجهة المزود البرمجية ثم دفعه إلى الواجهة الأمامية. هذا مهم جداً في تطبيقات المساعدات الذكية، الدردشة، أنظمة البحث التوليدي، وحتى سيناريوهات إخراج JSON مهيكل عندما تحتاج لفهم كيف يتكون الرد لحظة بلحظة.
الهيكلية الهندسية لتدفق البث من النموذج إلى الواجهة
لفهم البث المباشر جيداً، فكّر في المعمارية على شكل خط أنابيب بيانات Pipeline:
- المستخدم يرسل الرسالة من
Frontend. - الخادم
BackendيبنيPromptمناسباً، وقد يضيف سياقاً من هندسة الأوامر داخل الكود أو من أنظمة الاسترجاع المعززRAG. - يتم إرسال الطلب إلى مزود النموذج مع تفعيل
stream=Trueأو ما يعادله. - المزود يعيد أحداثاً متتالية تحتوي على أجزاء نصية صغيرة.
- الخادم يمرر هذه الأجزاء فوراً إلى المتصفح عبر
Server-Sent EventsأوWebSocket. - الواجهة تجمع القطع وتحدّث النص الظاهر للمستخدم بشكل حي.
هذا التصميم يفصل بين ثلاث طبقات مهمة: طبقة التوليد، طبقة النقل، وطبقة العرض. وكل طبقة لها أعطالها الخاصة: انقطاع في الشبكة، انتهاء مبكر للبث، تكرار Tokens، أو وصول بيانات وصفية بدلاً من النص.
ما الذي يصل فعلياً أثناء البث؟
في أغلب مزودات API، لا تستقبل “جملة كاملة” بل كائنات متتابعة تحتوي على أجزاء داخل حقول مثل delta أو content. لذلك يجب ألا تبني منطقك على افتراض أن كل حدث يحتوي كلمة مفهومة؛ أحياناً يكون الحدث حرفاً، مسافة، أو حتى إشارة انتهاء.
تنفيذ عملي باستخدام OpenAI API في Python
إذا كنت قد أعددت المفاتيح سابقاً كما في درس جلب مفاتيح API وكتابة أول اتصال، فتنفيذ البث المباشر يبدأ من تفعيل نمط stream. المثال التالي يطبع الرد تدريجياً في الطرفية:
from openai import OpenAI
client = OpenAI()
stream = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": "You are a helpful technical assistant."},
{"role": "user", "content": "Explain streaming responses in simple engineering terms."}
],
temperature=0.3,
stream=True
)
full_text = ""
for chunk in stream:
delta = chunk.choices[0].delta
if delta.content:
print(delta.content, end="", flush=True)
full_text += delta.content
print("\n\nFinal collected text:")
print(full_text)
لاحظ هنا أننا لا ننتظر الحقل النهائي، بل نقرأ كل chunk، ثم نستخرج delta.content. كما أننا نجمع الناتج في متغير ثانٍ لأن النص المبثوث قد تحتاج لتخزينه في قاعدة بيانات أو نظام مراقبة لاحقاً.
ربط البث مع FastAPI وواجهة الويب
أفضل خيار بسيط لعرض البث في المتصفح هو Server-Sent Events. هذا النمط ممتاز عندما يكون اتجاه البيانات من الخادم إلى العميل فقط، وهو شائع جداً في الدردشة.
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
from openai import OpenAI
app = FastAPI()
client = OpenAI()
def generate_stream():
stream = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": "You are a precise AI tutor."},
{"role": "user", "content": "Explain why token streaming improves UX."}
],
stream=True
)
for chunk in stream:
delta = chunk.choices[0].delta
if delta.content:
yield f"data: {delta.content}\n\n"
@app.get("/stream")
def stream_endpoint():
return StreamingResponse(generate_stream(), media_type="text/event-stream")
في هذه البنية، كل yield يرسل دفعة صغيرة للمتصفح. وإذا كنت تبني نظاماً تعليمياً أو مساعداً معرفياً عبر بيئة Python منظمة، فهذه المقاربة تمنحك مرونة أعلى في المراقبة والتوسع.
البث المباشر داخل LangChain
عند استخدام LangChain، فكرة البث لا تختلف جذرياً، لكنك تتعامل غالباً مع Callbacks أو واجهات بث جاهزة. هذا مفيد خصوصاً حين تجمع بين الاستدعاء، الذاكرة، وربما الاسترجاع المعتمد على Embeddings وVector DBs.
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model="gpt-4o-mini",
temperature=0.2,
streaming=True
)
for chunk in llm.stream("Explain token streaming in Arabic for developers."):
print(chunk.content, end="", flush=True)
إذا كنت تضبط الإبداع، فتذكر أن إعدادات مثل Temperature قد تؤثر على إيقاع الرد وتفرعه، لكنها لا تلغي منطق البث نفسه.
أفضل الممارسات الهندسية عند بناء Streaming UX
1) افصل بين النص المبثوث والنص النهائي
اعرض الأجزاء مباشرة للمستخدم، لكن احتفظ بنسخة نهائية مجمعة بعد اكتمال التدفق. هذا يفيد في الفهرسة، التخزين، التحليلات، وإعادة المحاولة.
2) عالج حالات الانتهاء والانقطاع
قد ينقطع البث بسبب حد زمني، شبكة، أو خطأ من المزود. لذلك أضف منطقاً يميز بين partial response وcompleted response.
3) لا تبالغ في تحديث الواجهة
إذا حدث تحديث للـ DOM عند كل حرف، قد تتراجع السلاسة. الأفضل تجميع عدة أجزاء صغيرة ثم تحديث الشاشة كل بضعة ميلي ثانية.
4) انتبه إلى البث مع المخرجات المهيكلة
إذا كنت تتوقع JSON، فالبث قد يرسل هيكلاً غير مكتمل مؤقتاً. لهذا من الأفضل أحياناً عرض النص للمستخدم تدريجياً، لكن تأخير Parsing إلى ما بعد اكتمال الرد.
تصميم البرومبت ليتوافق مع البث التدريجي
في بعض الحالات، صياغة الطلب نفسها تؤثر على جودة التجربة. فلو طلبت من النموذج مقدمة طويلة جداً قبل الوصول إلى الفكرة، سيشعر المستخدم بالبطء حتى مع البث. الأفضل أن تطلب إجابة تبدأ مباشرة بالخلاصة ثم تكمل التوسع.
ابدأ الإجابة بجملة تعريفية قصيرة جداً، ثم اعرض الشرح على شكل خطوات متتابعة. لا تضع مقدمة طويلة قبل الوصول إلى الفكرة الأساسية.
هذا النوع من الصياغة ينسجم مع مبادئ هندسة الأوامر Prompt Engineering لأنك لا تحسن جودة النص فقط، بل أيضاً ترتيب ظهوره زمنياً.
متى تستخدم Streaming ومتى قد لا يكون مناسباً؟
استخدمه عندما تكون تجربة المحادثة، الشرح التفاعلي، أو المساعدة اللحظية أولوية. أما إذا كان المطلوب نتيجة نهائية صارمة جداً مثل كائن JSON صالح 100% أو مخرجات تمر بخط تحقق دقيق، فقد يكون العرض الكامل بعد التحقق أكثر أماناً. في التطبيقات الإنتاجية الناضجة، كثيراً ما يتم الجمع بين النمطين: بث نصي للمستخدم، وتجميع نهائي للخلفية، ثم تحقق وتحويل وتخزين.
الخلاصة الهندسية هي أن Streaming Responses ليس مجرد تأثير بصري، بل قرار معماري يمس واجهة الاستخدام، بروتوكولات النقل، إدارة الحالة، ومراقبة الجودة. وعندما يُنفذ بشكل صحيح، فإنه يمنح تطبيقات LLM إحساساً فورياً بالاستجابة والاحترافية يشبه أفضل المنتجات التوليدية الحديثة.
11 comments