بناء واجهة شات (Chat UI) احترافية تشبه ChatGPT باستخدام Streamlit

دقائق القراءة: 5

بناء واجهة شات احترافية تشبه ChatGPT باستخدام Streamlit

عندما نطوّر تطبيقاً يعتمد على LLM، فإن جودة النموذج وحدها لا تكفي. المستخدم لا يرى البنية الخلفية أولاً، بل يختبر الواجهة، سرعة الاستجابة، وطريقة عرض المحادثة. لهذا السبب أصبحت واجهة الشات جزءاً هندسياً من التجربة، وليست مجرد طبقة تجميلية. باستخدام مقدمة في Streamlit: أسرع طريقة لتحويل سكربتات AI الخاصة بك إلى واجهات ويب تفاعلية يمكننا الانتقال من سكربت سطر أوامر بسيط إلى تطبيق حواري احترافي خلال وقت قصير جداً.

الفكرة الأساسية هي بناء Chat UI يعالج ثلاث طبقات متداخلة: طبقة الإدخال، طبقة إدارة الحالة، وطبقة التوليد والعرض. المستخدم يرسل الرسالة، ثم تُحفظ داخل session state، ثم تُمرر إلى النموذج عبر واجهة API أو عبر سلسلة مبنية بـ LangChain، وبعدها يُعاد الناتج ليُعرض تدريجياً أو دفعة واحدة داخل مساحة المحادثة.

المعمارية الهندسية لواجهة الشات

حتى لو كان التطبيق صغيراً، من الأفضل التفكير فيه كمنظومة بيانات وليست مجرد صفحة. معمارية الواجهة تتكون غالباً من العناصر التالية:

  • واجهة أمامية مبنية بـ Streamlit لعرض الرسائل واستقبال الإدخال.
  • طبقة حالة تحتفظ بتاريخ المحادثة باستخدام st.session_state.
  • طبقة منطق تتعامل مع بناء prompt، إعداد المعاملات، واستدعاء النموذج.
  • طبقة اختيارية للذاكرة طويلة أو قصيرة المدى، ويمكن توسيعها لاحقاً بالاعتماد على الذاكرة في الذكاء الاصطناعي أو Buffer Memory.
  • طبقة بث لحظي لعرض الكلمات تدريجياً اعتماداً على Streaming Responses.

هذا الفصل مهم جداً، لأن كثيراً من المطورين يخلطون بين منطق التوليد ومنطق العرض، فيصبح تعديل التطبيق لاحقاً أكثر صعوبة. إذا أردت لاحقاً إضافة RAG أو AI Agents، فوجود هذا الفصل سيجعل التوسعة مباشرة ومنظمة.

تدفق البيانات داخل التطبيق

يمكن تلخيص دورة العمل داخل واجهة الشات كالتالي:

  1. المستخدم يكتب رسالة داخل st.chat_input().
  2. التطبيق يضيف الرسالة إلى سجل المحادثة.
  3. يُبنى messages payload بالشكل المطلوب من مزود النموذج.
  4. يُرسل الطلب إلى النموذج مع معاملات مثل temperature وmax_tokens، ويمكن فهمها أكثر عبر فهم وإعداد معاملات Temperature و Top-K برمجياً.
  5. تُعرض الاستجابة داخل عنصر دردشة جديد، مع إمكانية البث التدريجي.
  6. يُحفظ رد المساعد داخل الحالة ليبقى مرئياً بعد كل rerun.

الهيكل الأساسي لواجهة الشات

النقطة المفصلية في Streamlit هي أن الصفحة يعاد تنفيذها من أعلى إلى أسفل عند كل تفاعل. لذلك يجب تخزين المحادثة في session state، وإعادة رسم الرسائل في كل مرة.

import streamlit as st
from openai import OpenAI

st.set_page_config(page_title="AI Chat UI", page_icon="💬", layout="wide")

client = OpenAI(api_key=st.secrets["OPENAI_API_KEY"])

if "messages" not in st.session_state:
    st.session_state.messages = [
        {"role": "assistant", "content": "مرحباً، كيف يمكنني مساعدتك اليوم؟"}
    ]

st.title("Professional Chat UI with Streamlit")

for message in st.session_state.messages:
    with st.chat_message(message["role"]):
        st.markdown(message["content"])

user_prompt = st.chat_input("اكتب رسالتك هنا...")

if user_prompt:
    st.session_state.messages.append({"role": "user", "content": user_prompt})

    with st.chat_message("user"):
        st.markdown(user_prompt)

    with st.chat_message("assistant"):
        response = client.chat.completions.create(
            model="gpt-4o-mini",
            messages=st.session_state.messages,
            temperature=0.7
        )
        assistant_reply = response.choices[0].message.content
        st.markdown(assistant_reply)

    st.session_state.messages.append(
        {"role": "assistant", "content": assistant_reply}
    )

هذا النموذج يحقق الحد الأدنى المطلوب: إدخال، عرض رسائل، حفظ حالة، واستدعاء نموذج. لكنه ليس بعد واجهة تشبه ChatGPT بشكل احترافي كامل.

كيف نجعل التجربة أكثر احترافية؟

  • إضافة رسالة ترحيبية ثابتة وواضحة.
  • استخدام الشريط الجانبي لضبط الإعدادات.
  • إتاحة زر لمسح المحادثة.
  • استخدام البث التدريجي بدلاً من الانتظار حتى اكتمال النص.
  • فصل منطق استدعاء النموذج في دالة مستقلة.

إضافة البث التدريجي للردود

أحد أهم الفروق النفسية بين تطبيق بدائي وآخر احترافي هو streaming. بدلاً من انتظار الرد كاملاً، يرى المستخدم الكلمات وهي تتكون لحظياً، وهو ما يرفع الإحساس بالسرعة والتفاعل. هذه الفكرة مشروحة بتفصيل أكبر في مقال كيف تجعل الكلمات تظهر تباعاً مثل ChatGPT.

def stream_response(messages):
    stream = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=messages,
        temperature=0.7,
        stream=True
    )

    full_text = ""
    for chunk in stream:
        delta = chunk.choices[0].delta.content
        if delta:
            full_text += delta
            yield full_text
if user_prompt:
    st.session_state.messages.append({"role": "user", "content": user_prompt})

    with st.chat_message("user"):
        st.markdown(user_prompt)

    with st.chat_message("assistant"):
        placeholder = st.empty()
        final_answer = ""

        for partial_text in stream_response(st.session_state.messages):
            final_answer = partial_text
            placeholder.markdown(final_answer + "▌")

        placeholder.markdown(final_answer)

    st.session_state.messages.append(
        {"role": "assistant", "content": final_answer}
    )

تصميم شريط جانبي للتحكم في السلوك

من أفضل الممارسات جعل المستخدم أو المطور قادراً على تعديل الإعدادات دون لمس الكود. هنا تظهر قيمة الشريط الجانبي في Streamlit.

with st.sidebar:
    st.header("Settings")
    temperature = st.slider("Temperature", 0.0, 1.5, 0.7, 0.1)
    system_prompt = st.text_area(
        "System Prompt",
        value="أنت مساعد تقني احترافي يشرح البرمجة بوضوح."
    )

    if st.button("Clear Chat"):
        st.session_state.messages = [
            {"role": "assistant", "content": "تمت إعادة تعيين المحادثة. كيف أساعدك؟"}
        ]
        st.rerun()

يمكنك هنا توسيع البناء لدمج مبادئ Prompt Engineering داخل الكود عبر حقن تعليمات النظام ديناميكياً. مثال برومبت احترافي:

أنت مساعد برمجي متخصص في Python وStreamlit. أجب باختصار أولاً، ثم قدم مثالاً عملياً، ثم اختم بنصيحة هندسية لتقليل الأخطاء في الإنتاج.

الاستعداد للتوسع: من واجهة شات إلى منصة ذكاء اصطناعي

بمجرد نجاح واجهة الشات الأساسية، يصبح بإمكانك توسيعها إلى ما هو أبعد من مجرد محادثة عامة. يمكنك ربطها بسلسلة LangChain، أو دمج Output Parsers لإجبار النموذج على الرد بصيغة منظمة، أو توصيلها مع Vector Databases وEmbeddings لإنشاء مساعد يعتمد على المعرفة الخاصة بالشركة.

وعند بناء تجربة تعتمد على مستندات داخلية، ستتحول الواجهة نفسها إلى طبقة أمامية لنظام RAG. من منظور هندسي، الواجهة لا تتغير كثيراً؛ الذي يتغير هو محرك التوليد الخلفي: بدلاً من إرسال الرسالة مباشرة إلى النموذج، تمر أولاً عبر retriever لجلب السياق المناسب ثم دمجه في الطلب.

أفضل الممارسات الإنتاجية

  • لا تضع مفاتيح API داخل الكود، بل داخل st.secrets، ويمكنك الرجوع إلى جلب مفاتيح API وكتابة أول سكربت اتصال.
  • احسب استهلاك الرموز في التطبيقات الكبيرة بالاستفادة من حساب التكلفة وإدارة الرموز.
  • افصل طبقة الواجهة عن طبقة استدعاء النموذج لتسهيل الاختبار والصيانة.
  • أضف معالجة أخطاء باستخدام try/except حتى لا تنهار الواجهة أمام المستخدم.
  • ابدأ بواجهة نظيفة وبسيطة، ثم أضف الذاكرة، الاسترجاع، والأدوات تدريجياً.

الخلاصة أن بناء واجهة شات احترافية بـ Streamlit ليس مهمة تجميلية، بل عملية هندسية تربط بين تجربة المستخدم، إدارة الحالة، تدفق البيانات، واستدعاء النماذج بكفاءة. وإذا أتقنت هذا النمط، فأنت لا تبني مجرد شاشة محادثة، بل تضع أساساً قابلاً للتطوير نحو تطبيقات LLM أكثر تقدماً، سواء كانت مساعدين معرفيين، أنظمة RAG، أو وكلاء ذكيين تفاعليين.

1 comment

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *