اذهب إلى المحتوى

تطوير تطبيق 'علمني' لاستكشاف المواضيع باستخدام جافا سكريبت و ChatGPT


Hassan Hedr

عند التعرف على موضوع جديد والقراءة عنه نبدأ عادة بالبحث عن تعريف يشرح ويوضح الصورة العامة له، ثم نكتشف مجموعة من المواضيع الأخرى الفرعية المتعلقة بالموضوع الأساسي، وعادة ما نبدأ البحث عبر الإنترنت عبر ويكيبيديا مثلًا، ونقرأ ونستكشف المواضيع بين صفحة وأخرى.

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

هل يملك ChatGPT المعرفة

تٌحاكي نماذج اللغة الكبيرة (أو Large Language Models) ومنها ChatGPT البيانات التي دُّربت عليها، فإذا دُربت على نصوص متنوعة ستتمكن من محاكاة وتوليد نصوص مشابهة لها قد لا يُلاحظ الفرق بينها وبين الأصلية، ولكن لا يمكن الاعتماد على دقتها فتلك النماذج خصوصًا المُدربة على طيف واسع من البيانات تُحاكي فقط ما دُربت عليه ولا تملك فهم حقيقي كما نحن البشر عن تلك النصوص.

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

أفكار لمشاريع باستخدام ChatGPT

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

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

تحضير صفحات التطبيق

نبدأ بإنشاء مجلد جديد لملفات المشروع ونُنشئ ملف HTML بالاسم index.html ونستورد ضمنه ملف مكتبة jQuery والتي سنستخدمها لتعديل محتويات الصفحة، وملف جافا سكريبت باسم script.js والذي سيحوي شيفرة التطبيق، وملف CSS يتضمن التنسيقات style.css كالتالي:

<html lang="ar">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <script src="https://code.jquery.com/jquery-3.7.0.min.js"></script>
    <script src="script.js" defer></script>
    <link rel="stylesheet" href="style.css" />
    <title>علمني</title>
  </head>
  <body>
    <div>
      <h1></h1>
      <p><span>?</span> اختر أحد المواضيع لمعرفة المزيد</p>
    </div>
    <div id="container">
    </div>
  </body>
</html>

كما تلاحظ في الكود أعلاه تحتوي الصفحة على عنصر <div> بالمٌعرّف container وضمنه سنضيف محتوى المواضيع الأساسية والمواضيع الفرعية.

بعد ذلك سوف ننشئ ملف التنسيقات style.css ضمن مجلد المشروع وسنكتب ضمنه التنسيقات التالية:

body {
    direction: rtl;
    display: flex;
    flex-direction: column;
    max-width: 60ch;
    justify-items: center;
    align-items: center;
    font-size: x-large;
    margin: 0 auto;
    padding: 20vh 10vw;
    background-color: #f9f9f9;
    color: #121212;
    font-family: system-ui;
    align-items: stretch;
    gap: 1rem;
}

p {
    line-height: 2;
    color: darkslategray;
}

a {
    padding: 1rem;
    font-size: inherit;
    margin: 0 .5rem;
    color: darkblue;
}

footer {
    margin-top: 2rem;
}

form {
    display: flex;
    flex-direction: column;
    gap: 2rem;
    text-align: center;
}

button {
    background-color: #121212;
    color: #f9f9f9;
    border: none;
    padding: 1rem 2rem;
    font-size: inherit;
    cursor: pointer;
    font-family: inherit;
}

input {
    padding: 1rem 2rem;
    font-size: inherit;
    font-family: inherit;
    text-align: center;
    border: .2rem solid;
}

.subject {
    padding: 2rem 2rem;
    cursor: pointer;
    border: .15rem solid lightgray;
    border-radius: .5rem;
}

#container {
    gap: 2rem;
    display: flex;
    flex-direction: column;
}

لمعاينة شكل الموقع نضيف بعض المواضيع الوهمية يدويًا ضمن العنصر بالمعرّف container بالشكل التالي:

<div class="subject">
  <h2>الموضوع</h2>
  <p>شرح عن الموضوع</p>
</div>

نفتح الصفحة index.html ضمن المتصفح ونلاحظ شكل الموقع بعد التنسيق:

index.png

توليد وعرض مواضيع عشوائية للمستخدم

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

ولاستخدام ذلك المفتاح يجب حفظه في مكان آمن، وبما أننا لن نستخدم أي شيفرات جافاسكربت خارجية فسنخزن المفتاح ضمن التخزين المحلي لمتصفح الويب الخاص بالمستخدم باستخدام الكائن localStorage، أما في حال استخدامك لأي شيفرات من مكتبات خارجية ضمن المشروع فيمكن لتلك الشيفرات قراءة المفتاح وسرقته من المستخدم، عندها يجب البحث عن حلول أخرى كتخزين المفتاح بأمان ضمن خادم المشروع والتعريف عن المستخدم بطريقة ما مثل استخدام ملفات تعريف الارتباط Cookies وإرسال الطلبات من قبل الخادم بدلًا من قبل متصفح المستخدم نفسه، والآن نعرف التابع openAIKey والذي يتحقق وجود مفتاح مُخزن سابقًا ويعيده، وإلا يطلب من المستخدم إدخال مفتاحه باستخدام الدالة prompt وحفظ ذلك المفتاح للاستخدام لاحقًا:

function openAIKey() {
    const localStorageKey = 'OPENAI_API_KEY';

    // استخراج المفتاح المُخزن سابقًا
    let apiKey = localStorage.getItem(localStorageKey)

    if (!apiKey) {
        // طلب إدخال المفتاح من المستخدم
        apiKey = prompt('أدخل مفتاح OpenAI الخاص بك')
        // تخزين المفتاح
        localStorage.setItem(localStorageKey, apiKey)
    }

    return apiKey
}

وللتعامل مع نموذج ChatGPT نُعرف التابع chatGPT والذي يقبل التعليمة التي سترسل له كمعامل أول، ويُرسل طلب من نوع POST إلى المسار https://api.openai.com/v1/chat/completions بحسب توثيق تلك الواجهة البرمجية وكما شرحناها في مقال سابق، ثم نستخرج رد النموذج منه كالتالي:

function chatGPT(prompt) {
// إرسال الطلب
    return fetch('https://api.openai.com/v1/chat/completions', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            // ترويسة الاستيثاق
            'Authorization': 'Bearer ' + openAIKey(),
        },
        body: JSON.stringify({
            // اسم النموذج
            "model": "gpt-3.5-turbo",
            // تعليمة المستخدم
            "messages": [{ "role": "user", "content": prompt }]
        }),
    })
        // تفسير الطلب
        .then(response => response.json())
        // استخراج رد النموذج
        .then(json => json.choices[0].message.content)
}

باستخدام التابع chatGPT يمكننا الآن توليد محتوى الأفكار العشوائية الأولية التي ستظهر للمستخدم بإرسال التعليمة التالية إلى ChatGPT:

اقترح بعض المواضيع لأتعلم عنها، المواضيع كل موضوع بسطر:

لاحظ أننا نحدد في نهاية التعليمة شكل الخرج الذي نريده وهو في هذه الحالة أن يكتب لنا كل موضوع بسطر، لنتمكن من استخراج تلك المواضيع بتقسيم الرد إلى الأسطر المكونة له، ثم تحويل كل سطر إلى كائن جافاسكربت يحتوي على الحقل title وهو عنوان الموضوع، نٌعرّف تلك العملية ضمن التابع getInitialSubjects كالتالي:

function getInitialSubjects() {
    return chatGPT('اقترح بعض المواضيع لأتعلم عنها، المواضيع كل موضوع بسطر:')
        .then(subjects => subjects.split('\n').map(title => ({ title })))
}

ولعرض تلك المواضيع نستخدم مكتبة jQuery لتحديد العنصر الذي سيحتوي المحتوى باستخدام التابع $ وهو صاحب المٌعرّف container، ونفرغ محتواه باستخدام empty ونمر على كل كائن منها ونولد له عناصر تحوي على عنوان الموضوع من الحقل title، ونستخدم التابع append لإضافة تلك العناصر داخل بعضها، ونعرّف عملية عرض كائنات المواضيع ضمن التابع renderSubjects ليكون كالتالي:

function renderSubjects(subjects) {
    // العنصر الحاوي
    const $container = $('#container')

    // مسح المحتوى السابق
    $container.empty()

    for (const subject of subjects) {
        // إنشاء عنصر الموضوع 
        const $subject = $(`<div class="subject"></div>`)

        // إضافة العنوان
        $subject.append(`<h2>${subject.title}</h2>`)

        // إضافة الموضوع إلى الحاوية
        $container.append($subject)
    }
}

وبذلك أصبحت كل التوابع جاهزة من توليد الأفكار العشوائية إلى عملية عرضها، لذا في نهاية الملف نستدعي التابع getInitialSubjects ونمرر تابع عرض المواضيع renderSubjects إلى التابع then من ذلك التابع لعرض تلك المواضيع بعد جلبها، ونفتح الملف index.html ضمن المتصفح سنلاحظ طلب التطبيق منا لأول مرة إدخال مفتاح الواجهة البرمجية:

[prompt.png]

وبعد إدخال المفتاح الخاص بنا سيُرسل طلب توليد المواضيع إلى ChatGPT وتعرض النتائج كالتالي:

main.png

نلاحظ تنوع المواضيع التي ولدها وكيف أمكننا استخراجها من الرد وعرض كل منها على حدى، وفي الفقرة التالية سنضيف ميزة الدخول إلى مواضيع فرعية من أي موضوع مقترح نريد معرفة المزيد عنه.

عرض مواضيع فرعية عن موضوع

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

اقتباس

اقترح بعض الأفكار الفرعية عن <اسم الموضوع> لأتعلم عنها مع شرح قصير عن كل منها بالصيغة التالية:

الفكرة شرح قصير

الفكرة شرح قصير

الأفكار الفرعية بالصيغة السابقة:

حيث نستبدل <اسم الموضوع> بعنوان الموضوع الذي اختاره المستخدم، ونلاحظ كيف أرفقنا أمثلة عن شكل الخرج المطلوب وفي نهاية التعليمة نرشد النموذج أن يبدأ مباشرة بتوليد تلك الأفكار بالصيغة المُحددة، ولإرسال تلك التعليمة وتحويل الرد المولّد عنها إلى كائنات بعنوان ضمن الحقل title وشرح عنها ضمن الحقل desc نُعرّف التابع getSubSubjects والذي يقبل كمعامل أول عنوان الموضوع ويستدعي داخله التابع chatGPT ويمرر التعليمة مدرجًا ضمنها الموضوع المٌمرر، ثم يحول الرد إلى كائنات مواضيع بالصيغة المطلوبة كالتالي:

function getSubSubjects(subject) {
    // توليد التعليمة
    const prompt = `اقترح بعض الأفكار الفرعية عن "${subject}" لأتعلم عنها مع شرح قصير عن كل منها بالصيغة التالية:

الفكرة
شرح قصير

الفكرة
شرح قصير

الأفكار الفرعية بالصيغة السابقة:`

    return chatGPT(prompt)
        // تحويل الرد إلى كائنات
        .then(subjects => subjects.split('\n\n').map(subject => {
            const [title, desc] = subject.split('\n')
            return { title, desc }
        }))
}

نريد توليد مواضيع فرعية عند ضغط المستخدم على موضوع ما، لذا نُعدل التابع renderSubjects لإضافة تابع معالجة لحدث الضغط click والذي سيستدعي تابع توليد المواضيع الفرعية getSubSubjects مع تمرير عنوان الموضوع الذي اختاره المستخدم، ثم يمرر النتيجة للتابع نفسه renderSubjects كي تُعرض المواضيع الفرعية بدل الحالية، ونضيف لكل موضوع الشرح من الحقل desc في حال وجوده كالتالي:

function renderSubjects(subjects) {
    ...
    for (const subject of subjects) {
        const $subject = $(`<div class="subject"></div>`)

        // توليد المواضيع الفرعية عند الضغط
        $subject.on('click', () => getSubSubjects(subject.title).then(renderSubjects))

        $subject.append(`<h2>${subject.title}</h2>`)

        // استخراج الشرح وعرضه
        if (subject.desc){
            $subject.append(`<p>${subject.desc}</p>`)
        }

        $container.append($subject)
    }
}

تجربة التطبيق

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

science.png

ولد مواضيع فرعية مع شرح بسيط عن كل منها عن ذلك الموضوع لندخل مثلًا إلى موضوع الذكاء الاصطناعي:

ai.png

أيضًا أصبحت المواضيع أكثر تخصصًا لنختر تعلم الآلة:

ml.png

الختام

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

اقرأ أيضًا


تفاعل الأعضاء

أفضل التعليقات

لا توجد أية تعليقات بعد



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...