1. مقدمة
يركّز هذا الدرس التطبيقي حول الترميز على نموذج Gemini اللغوي الكبير (LLM) المستضاف على Vertex AI على Google Cloud. Vertex AI هي منصة تضم جميع منتجات وخدمات ونماذج تعلُّم الآلة على Google Cloud.
ستستخدم Java للتفاعل مع Gemini API باستخدام إطار العمل LangChain4j. ستتمكّن من الاطّلاع على أمثلة ملموسة للاستفادة من النموذج اللغوي الكبير في الإجابة عن الأسئلة، وإنشاء الأفكار، واستخراج الكيانات والمحتوى المنظَّم، والإنشاء المعزَّز لاسترجاع البيانات، واستدعاء الدوال.
ما هو الذكاء الاصطناعي التوليدي؟
يشير الذكاء الاصطناعي التوليدي إلى استخدام الذكاء الاصطناعي لإنشاء محتوى جديد، مثل النصوص والصور والموسيقى والصوت والفيديوهات.
يستند الذكاء الاصطناعي التوليدي إلى النماذج اللغوية الكبيرة (LLM) التي يمكنها تنفيذ مهام متعدّدة وتنفيذ مهام غير تقليدية، مثل التلخيص والأسئلة والأجوبة والتصنيف وغير ذلك. وباستخدام الحد الأدنى من التدريب، يمكن تكييف النماذج الأساسية لحالات الاستخدام المستهدفة مع القليل جدًا من الأمثلة على البيانات.
ما هي آلية عمل الذكاء الاصطناعي التوليدي؟
يستخدم الذكاء الاصطناعي التوليدي نموذج تعلُّم الآلة للتعرّف على الأنماط والعلاقات في مجموعة بيانات تتضمّن محتوًى من إنشاء البشر. وتعتمد بعدها على الأنماط التي تعلّمتها لإنشاء محتوى جديد.
يشكّل استخدام التعلّم الخاضع للإشراف الطريقة الأكثر شيوعًا لتدريب نموذج الذكاء الاصطناعي التوليدي. يتم منح النموذج مجموعة من المحتوى الذي أنشأه المستخدمون والتصنيفات المقابلة. ويتعلّم النظام بعد ذلك كيفية إنشاء محتوى مشابه للمحتوى الذي أنشأه المستخدمون.
ما هي التطبيقات الشائعة للذكاء الاصطناعي التوليدي؟
يمكن استخدام الذكاء الاصطناعي التوليدي للأغراض التالية:
- يمكنك تحسين تفاعلات العملاء من خلال تجارب بحث ومحادثات محسَّنة.
- استكشِف كميات هائلة من البيانات غير المنظَّمة من خلال واجهات وتلخيصات المحادثة.
- المساعدة في المهام المتكررة، مثل الرد على طلبات تقديم العروض وأقلمة المحتوى التسويقي بلغات مختلفة والتحقق من عقود العملاء للتأكد من الامتثال والمزيد
ما هي حلول الذكاء الاصطناعي التوليدي المتوفّرة في Google Cloud؟
باستخدام Vertex AI، يمكنك التفاعل مع النماذج الأساسية وتخصيصها وتضمينها في تطبيقاتك بدون خبرة خبرة في تعلُّم الآلة أو منها. يمكنك الوصول إلى النماذج الأساسية على تطبيق Model Garden، أو ضبط النماذج من خلال واجهة مستخدم بسيطة على Vertex AI Studio، أو استخدام النماذج في دفتر ملاحظات لعلم البيانات.
توفّر منصة Vertex AI Search and Conversation أسرع طريقة للمطوّرين لإنشاء محركات بحث وبرامج تتبُّع دردشة مستنِدة إلى الذكاء الاصطناعي التوليدي.
Gemini في Google Cloud مزوّد بـ Gemini، وهو عبارة عن مساعد مستند إلى الذكاء الاصطناعي متوفر في Google Cloud وبيئات IDE لمساعدتك في إنجاز المزيد من المهام بشكل أسرع. يساعد Gemini Code Assist في إكمال الرموز البرمجية وإنشاء الرموز البرمجية وشرحها، ويتيح لك الدردشة معه لطرح الأسئلة الفنية.
ما هو Gemini؟
Gemini هي مجموعة من نماذج الذكاء الاصطناعي التوليدي التي طوّرتها Google DeepMind والمصمّمة لحالات الاستخدام المتعدّدة الوسائط. يعني ذلك أنّه بإمكانها معالجة وإنشاء أنواع مختلفة من المحتوى، مثل النصوص والرموز والصور والمقاطع الصوتية.
يتوفّر Gemini بأشكال وأحجام مختلفة:
- Gemini Ultra: الإصدار الأكبر والأكثر فعالية لأداء المهام المعقّدة
- Gemini Flash: هو الأسرع والأكثر فعالية من حيث التكلفة، وهو محسَّن للمهام الكبيرة.
- Gemini Pro: نماذج متوسطة الحجم ومحسّنة لتوسيع نطاق المهام في مختلف المهام
- Gemini Nano: التطبيق الأكثر كفاءة مصمَّمًا لإنجاز المهام على الجهاز فقط
الميزات الأساسية:
- الوسائط المتعددة: لقد اكتسب Gemini قدرة أكبر على فهم تنسيقات المعلومات المتعددة ومعالجتها خطوةً مهمة تتجاوز النماذج اللغوية التقليدية التي تعتمد على النص فقط.
- الأداء: تفوّق Gemini Ultra على أحدث معايير الأداء الحالية من حيث عدّة مقاييس أداء، وكان أول نموذج يتخطى مستوى الخبراء في ما يتعلّق بمقياس "فهم اللغات المتعددة المهام" (MMLU).
- المرونة: بفضل أحجام Gemini المختلفة، يمكن تكييفها مع حالات استخدام مختلفة، بدءًا من الأبحاث على نطاق واسع وصولاً إلى نشرها على الأجهزة الجوّالة.
كيف يمكنك التفاعل مع Gemini على Vertex AI من Java؟
أمامك خياران:
- مكتبة Vertex AI Java API for Gemini الرسمية
- إطار عمل LangChain4j.
في هذا الدرس التطبيقي حول الترميز، ستستخدم إطار العمل LangChain4j.
ما هو إطار عمل LangChain4j؟
إنّ إطار العمل LangChain4j هو مكتبة مفتوحة المصدر لدمج النماذج اللغوية الكبيرة في تطبيقات Java، من خلال تنسيق مكونات مختلفة، مثل النموذج اللغوي الكبير نفسه، وكذلك أدوات أخرى مثل قواعد بيانات المتجهات (لعمليات البحث الدلالية)، وأدوات تحميل المستندات وأدوات تقسيمها (لتحليل المستندات والتعلّم منها)، وأدوات تحليل الإخراج، وغير ذلك.
استوحى هذا المشروع من مشروع LangChain في Python، ولكنّ الهدف هو خدمة مطوّري Java.
المعلومات التي ستطّلع عليها
- كيفية إعداد مشروع Java لاستخدام Gemini وLangChain4j
- كيفية إرسال طلبك الأول إلى Gemini آليًا
- كيفية عرض الردود من Gemini مباشرةً
- كيفية إنشاء محادثة بين مستخدم وGemini
- كيفية استخدام Gemini في سياق متعدد الوسائط من خلال إرسال نصوص وصور
- كيفية استخراج المعلومات المنظَّمة المفيدة من المحتوى غير المنظَّم
- كيفية معالجة نماذج الطلبات
- طريقة تصنيف النصوص مثل تحليل الآراء
- كيفية الدردشة مع مستنداتك الخاصة (استرجاع البيانات المعززة)
- كيفية توسيع نطاق روبوتات الدردشة من خلال الاتصال الوظيفي
- كيفية استخدام Gemma محليًا مع Ollama وTestContainers
المتطلبات
- معرفة لغة البرمجة Java
- مشروع على Google Cloud
- متصفح، مثل Chrome أو Firefox
2. الإعداد والمتطلبات
إعداد بيئة ذاتية
- سجِّل الدخول إلى Google Cloud Console وأنشئ مشروعًا جديدًا أو أعِد استخدام مشروع حالي. إذا لم يكن لديك حساب على Gmail أو Google Workspace، عليك إنشاء حساب.
- اسم المشروع هو الاسم المعروض للمشاركين في هذا المشروع. وهي سلسلة أحرف لا تستخدمها Google APIs. ويمكنك تعديلها في أي وقت.
- يكون رقم تعريف المشروع فريدًا في جميع مشاريع Google Cloud وغير قابل للتغيير (لا يمكن تغييره بعد تحديده). تنشئ Cloud Console سلسلة فريدة تلقائيًا. فعادةً لا تهتم بما هو. في معظم الدروس التطبيقية حول الترميز، يجب الإشارة إلى رقم تعريف المشروع (الذي يتم تحديده عادةً على أنّه
PROJECT_ID
). وإذا لم يعجبك رقم التعريف الذي تم إنشاؤه، يمكنك إنشاء رقم تعريف عشوائي آخر. ويمكنك بدلاً من ذلك تجربة طلبك الخاص ومعرفة ما إذا كان متاحًا. ولا يمكن تغييره بعد هذه الخطوة ويبقى طوال مدة المشروع. - لمعلوماتك، هناك قيمة ثالثة، وهي رقم المشروع، الذي تستخدمه بعض واجهات برمجة التطبيقات. اطّلِع على مزيد من المعلومات حول هذه القيم الثلاث في المستندات.
- بعد ذلك، عليك تفعيل الفوترة في Cloud Console لاستخدام الموارد/واجهات برمجة التطبيقات في Cloud. لن يؤدي إكمال هذا الدرس التطبيقي حول الترميز إلى فرض أي تكاليف، إن وُجدت. لإيقاف تشغيل الموارد لتجنب تحمُّل الفواتير إلى ما هو أبعد من هذا البرنامج التعليمي، يمكنك حذف الموارد التي أنشأتها أو حذف المشروع. يكون مستخدمو Google Cloud الجدد مؤهَّلون للانضمام إلى برنامج فترة تجريبية مجانية بقيمة 300 دولار أمريكي.
بدء Cloud Shell
يمكنك إدارة Google Cloud عن بُعد من الكمبيوتر المحمول، إلا أنّك ستستخدم في هذا الدرس التطبيقي Cloud Shell، وهي بيئة سطر أوامر يتم تشغيلها في السحابة الإلكترونية.
تفعيل Cloud Shell
- من Cloud Console، انقر على تفعيل Cloud Shell .
إذا كانت هذه هي المرة الأولى التي تبدأ فيها Cloud Shell، ستظهر لك شاشة وسيطة تصف ماهيتها. إذا ظهرت لك شاشة وسيطة، انقر على متابعة.
من المفترَض أن تستغرق عملية إدارة الحسابات والاتصال بخدمة Cloud Shell بضع دقائق فقط.
يتم تحميل هذا الجهاز الافتراضي مع جميع أدوات التطوير اللازمة. وتوفّر هذه الشبكة دليلاً رئيسيًا دائمًا بسعة 5 غيغابايت ويتم تشغيله في Google Cloud، ما يحسّن بشكل كبير من أداء الشبكة والمصادقة. يمكنك تنفيذ معظم عملك، إن لم يكن كلّه، في هذا الدرس التطبيقي حول الترميز باستخدام متصفّح.
بعد الربط بخدمة Cloud Shell، من المفترض أن تتأكّد من أنّه تمّت مصادقتك وأنّ المشروع مضبوط على رقم تعريف مشروعك.
- شغِّل الأمر التالي في Cloud Shell لتأكيد مصادقتك:
gcloud auth list
مخرجات الأمر
Credentialed Accounts ACTIVE ACCOUNT * <my_account>@<my_domain.com> To set the active account, run: $ gcloud config set account `ACCOUNT`
- شغّل الأمر التالي في Cloud Shell للتأكد من معرفة الأمر gcloud بمشروعك:
gcloud config list project
مخرجات الأمر
[core] project = <PROJECT_ID>
إذا لم يكن كذلك، يمكنك تعيينه من خلال هذا الأمر:
gcloud config set project <PROJECT_ID>
مخرجات الأمر
Updated property [core/project].
3- جارٍ إعداد بيئة التطوير
في هذا الدرس التطبيقي حول الترميز، ستستخدم الوحدة الطرفية في Cloud Shell ومحرِّر Cloud Shell لتطوير برامج Java.
تفعيل واجهات Vertex AI API
في وحدة تحكُّم Google Cloud، تأكَّد من عرض اسم مشروعك في أعلى وحدة تحكُّم Google Cloud. وإذا لم يكن الأمر كذلك، انقر على اختيار مشروع لفتح أداة اختيار المشروع واختَر المشروع المطلوب.
يمكنك تفعيل واجهات Vertex AI API إما من قسم Vertex AI في Google Cloud Console أو من الوحدة الطرفية في Cloud Shell.
لتفعيل الميزة من Google Cloud Console، انتقِل أولاً إلى قسم Vertex AI في قائمة Google Cloud Console:
انقر على تفعيل جميع واجهات برمجة التطبيقات المقترَحة في لوحة بيانات Vertex AI.
سيؤدي ذلك إلى تفعيل العديد من واجهات برمجة التطبيقات، ولكن الأهم في الدرس التطبيقي حول الترميز هو aiplatform.googleapis.com
.
بدلاً من ذلك، يمكنك أيضًا تفعيل واجهة برمجة التطبيقات هذه من الوحدة الطرفية في Cloud Shell باستخدام الأمر التالي:
gcloud services enable aiplatform.googleapis.com
استنساخ مستودع جيت هب
في الوحدة الطرفية Cloud Shell، استنسِخ المستودع الخاص بالدرس التطبيقي حول الترميز هذا:
git clone https://github.com/glaforge/gemini-workshop-for-java-developers.git
للتأكد من أن المشروع جاهز للتشغيل، يمكنك محاولة تشغيل "Hello World" البرنامج.
تأكَّد من أنّك في مجلد المستوى الأعلى:
cd gemini-workshop-for-java-developers/
إنشاء برنامج تضمين Gradle:
gradle wrapper
التنفيذ باستخدام "gradlew
":
./gradlew run
من المفترض أن يظهر لك الناتج التالي:
.. > Task :app:run Hello World!
فتح Cloud Editor وإعداده
افتح الرمز باستخدام Cloud Code Editor من Cloud Shell:
في Cloud Code Editor (أداة تعديل الرموز البرمجية في السحابة الإلكترونية)، افتح مجلد المصدر للدرس التطبيقي حول الترميز عن طريق اختيار File
->. Open Folder
وأشِر إلى المجلد المصدر للدرس التطبيقي حول الترميز (على سبيل المثال، /home/username/gemini-workshop-for-java-developers/
).
تثبيت Gradle لـ Java
لجعل محرر رموز السحابة الإلكترونية يعمل بشكل صحيح مع Gradle، ثبّت إضافة Gradle لـ Java.
أولاً، انتقل إلى قسم مشروعات Java واضغط على علامة الجمع:
اختيار "Gradle for Java
":
اختَر إصدار Install Pre-Release
:
بعد التثبيت، من المفترض أن يظهر لك الزرّان Disable
وUninstall
:
أخيرًا، إفراغ مساحة العمل لتطبيق الإعدادات الجديدة:
سيُطلب منك إعادة تحميل ورشة العمل وحذفها. امضِ قدمًا واختر Reload and delete
:
إذا فتحت أحد الملفات، على سبيل المثال App.java، فسترى الآن المحرر يعمل بشكل صحيح مع تحديد بناء الجملة:
يمكنك الآن إجراء بعض النماذج على Gemini.
إعداد متغيرات البيئة
افتح وحدة طرفية جديدة في أداة تعديل الرموز في السحابة الإلكترونية من خلال اختيار Terminal
->. New Terminal
قم بإعداد متغيري بيئة مطلوبين لتشغيل أمثلة التعليمات البرمجية:
- PROJECT_ID — رقم تعريف مشروعك على Google Cloud
- الموقع الجغرافي: المنطقة التي يتم نشر نموذج Gemini فيها
تصدير المتغيّرات على النحو التالي:
export PROJECT_ID=$(gcloud config get-value project) export LOCATION=us-central1
4. الطلب الأول لنموذج Gemini
بعد أن تم إعداد المشروع بشكل صحيح، حان الوقت لاستدعاء Gemini API.
ألقِ نظرة على QA.java
في دليل app/src/main/java/gemini/workshop
:
package gemini.workshop;
import dev.langchain4j.model.vertexai.VertexAiGeminiChatModel;
import dev.langchain4j.model.chat.ChatLanguageModel;
public class QA {
public static void main(String[] args) {
ChatLanguageModel model = VertexAiGeminiChatModel.builder()
.project(System.getenv("PROJECT_ID"))
.location(System.getenv("LOCATION"))
.modelName("gemini-1.5-flash-001")
.build();
System.out.println(model.generate("Why is the sky blue?"));
}
}
في هذا المثال الأول، عليك استيراد الفئة VertexAiGeminiChatModel
التي تنفّذ واجهة ChatModel
.
في طريقة main
، يمكنك ضبط نموذج لغة المحادثة باستخدام أداة إنشاء VertexAiGeminiChatModel
وتحديد ما يلي:
- Project
- الموقع الجغرافي
- اسم النموذج (
gemini-1.5-flash-001
)
الآن بعد أن أصبح النموذج اللغوي جاهزًا، يمكنك استدعاء طريقة generate()
وتمرير طلبك أو سؤالك أو التعليمات لإرسالها إلى النموذج اللغوي الكبير. هنا، تطرح سؤالاً بسيطًا حول ما يجعل السماء زرقاء.
يمكنك تغيير هذا الطلب لتجربة أسئلة أو مهام مختلفة.
شغِّل النموذج في المجلد الجذر لرمز المصدر:
./gradlew run -q -DjavaMainClass=gemini.workshop.QA
من المفترض أن تظهر لك نتيجة مشابهة لما يلي:
The sky appears blue because of a phenomenon called Rayleigh scattering. When sunlight enters the atmosphere, it is made up of a mixture of different wavelengths of light, each with a different color. The different wavelengths of light interact with the molecules and particles in the atmosphere in different ways. The shorter wavelengths of light, such as those corresponding to blue and violet light, are more likely to be scattered in all directions by these particles than the longer wavelengths of light, such as those corresponding to red and orange light. This is because the shorter wavelengths of light have a smaller wavelength and are able to bend around the particles more easily. As a result of Rayleigh scattering, the blue light from the sun is scattered in all directions, and it is this scattered blue light that we see when we look up at the sky. The blue light from the sun is not actually scattered in a single direction, so the color of the sky can vary depending on the position of the sun in the sky and the amount of dust and water droplets in the atmosphere.
تهانينا، لقد أجريت مكالمتك الأولى مع Gemini.
استجابة البث
هل لاحظت أنه تم تقديم الرد دفعة واحدة، وبعد بضع ثوانٍ؟ من الممكن أيضًا تلقّي الردّ بشكل تدريجي بفضل صيغة الرد للبث. عرض الرد تدريجيًا، يعرض النموذج الرد قطعة تلو الأخرى عندما يصبح متاحًا.
في هذا الدرس التطبيقي حول الترميز، سنلتزم بالاستجابة التي لا تظهر في البث، ولكن لنلقي نظرة على الرد على عملية البث لنفهم كيفية تنفيذه.
في StreamQA.java
في دليل app/src/main/java/gemini/workshop
، يمكنك الاطّلاع على استجابة البث المباشر:
package gemini.workshop;
import dev.langchain4j.model.chat.StreamingChatLanguageModel;
import dev.langchain4j.model.vertexai.VertexAiGeminiStreamingChatModel;
import dev.langchain4j.model.StreamingResponseHandler;
public class StreamQA {
public static void main(String[] args) {
StreamingChatLanguageModel model = VertexAiGeminiStreamingChatModel.builder()
.project(System.getenv("PROJECT_ID"))
.location(System.getenv("LOCATION"))
.modelName("gemini-1.5-flash-001")
.build();
model.generate("Why is the sky blue?", new StreamingResponseHandler<>() {
@Override
public void onNext(String text) {
System.out.println(text);
}
@Override
public void onError(Throwable error) {
error.printStackTrace();
}
});
}
}
في الوقت الحالي، نستورد صيغ فئة البث VertexAiGeminiStreamingChatModel
التي تنفّذ واجهة StreamingChatLanguageModel
. ستحتاج أيضًا إلى StreamingResponseHandler
.
هذه المرة، يختلف توقيع الطريقة generate()
قليلاً. ويكون نوع الإرجاع ملغيًا بدلاً من عرض سلسلة. بالإضافة إلى الطلب، يجب ضبط معالِج استجابة البث. في هذا المثال، يمكنك تنفيذ الواجهة من خلال إنشاء فئة داخلية مجهولة الهوية، باستخدام الطريقتين onNext(String text)
وonError(Throwable error)
. يتم استدعاء الأول في كل مرة يتوفر فيها جزء جديد من الاستجابة، بينما يتم استدعاء الخيار الثاني فقط في حالة حدوث خطأ.
التشغيل:
./gradlew run -q -DjavaMainClass=gemini.workshop.StreamQA
ستحصل على إجابة مماثلة للصف السابق، ولكن هذه المرة ستلاحظ أن الإجابة تظهر بشكل تدريجي في الواجهات الخاصة بك، بدلاً من انتظار عرض الإجابة الكاملة.
إعداد إضافي
بالنسبة إلى الإعدادات، نحدِّد فقط المشروع والموقع الجغرافي واسم النموذج، ولكن هناك مَعلمات أخرى يمكنك تحديدها للنموذج:
temperature(Float temp)
: لتحديد مدى إبداع الردّ (0 مستوى إبداعي منخفض وغالبًا ما يكون أكثر واقعية، بينما 1 للمخرجات الإبداعية الإضافية)topP(Float topP)
: لاختيار الكلمات المحتمَلة التي يصل إجمالي احتمالية إجرائها إلى رقم النقطة العائمة (بين 0 و1)topK(Integer topK)
: لاختيار كلمة بشكل عشوائي من بين أقصى عدد ممكن من الكلمات المحتمَلة لإكمال النص (من 1 إلى 40)maxOutputTokens(Integer max)
— لتحديد الحد الأقصى لطول الإجابة وفقًا للنموذج (تمثل 4 رموز مميزة بشكل عام 3 كلمات تقريبًا)maxRetries(Integer retries)
: في حال تجاوزت الطلب في كل مرة، أو في حال كانت المنصّة تواجه بعض المشاكل الفنية، يمكنك أن تطلب من النموذج إعادة محاولة الاتصال 3 مرات.
لقد طرحت حتى الآن سؤالاً واحدًا على Gemini، ولكن يمكنك أيضًا إجراء محادثة متعددة الأدوار. هذا ما ستستكشفه في القسم التالي.
5- التحدّث إلى Gemini
في الخطوة السابقة، طرحت سؤالاً واحدًا. وحان الوقت الآن لإجراء محادثة حقيقية بين أحد المستخدمين والنموذج اللغوي الكبير. يمكن أن يعتمد كل سؤال وأجوبة على الأسئلة السابقة لتشكيل مناقشة حقيقية.
ألقِ نظرة على Conversation.java
في مجلد app/src/main/java/gemini/workshop
:
package gemini.workshop;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.vertexai.VertexAiGeminiChatModel;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.service.AiServices;
import java.util.List;
public class Conversation {
public static void main(String[] args) {
ChatLanguageModel model = VertexAiGeminiChatModel.builder()
.project(System.getenv("PROJECT_ID"))
.location(System.getenv("LOCATION"))
.modelName("gemini-1.5-flash-001")
.build();
MessageWindowChatMemory chatMemory = MessageWindowChatMemory.builder()
.maxMessages(20)
.build();
interface ConversationService {
String chat(String message);
}
ConversationService conversation =
AiServices.builder(ConversationService.class)
.chatLanguageModel(model)
.chatMemory(chatMemory)
.build();
List.of(
"Hello!",
"What is the country where the Eiffel tower is situated?",
"How many inhabitants are there in that country?"
).forEach( message -> {
System.out.println("\nUser: " + message);
System.out.println("Gemini: " + conversation.chat(message));
});
}
}
عمليتا استيراد جديدتان مثيرتان للاهتمام في هذا الصف:
MessageWindowChatMemory
— صف يساعد في التعامل مع الجوانب المتعددة للمحادثة والحفاظ على الأسئلة والإجابات السابقة في الذاكرة المحليةAiServices
— صف يربط نموذج المحادثة وذاكرة المحادثة معًا
في الطريقة الرئيسية، سيتم إعداد النموذج وذاكرة المحادثة وخدمة الذكاء الاصطناعي (AI). يتم ضبط النموذج كالمعتاد مع معلومات المشروع والموقع الجغرافي واسم النموذج.
في ذاكرة المحادثة، نستخدم أداة إنشاء MessageWindowChatMemory
لإنشاء ذكرى يتم فيها تبادل آخر 20 رسالة. إنها نافذة تمرير فوق المحادثة التي يتم الاحتفاظ بسياقها محليًا في برنامج فئة Java.
يمكنك بعد ذلك إنشاء AI service
الذي يربط نموذج المحادثة بذاكرة المحادثة.
لاحِظ كيف تستفيد خدمة الذكاء الاصطناعي (AI) من واجهة ConversationService
مخصَّصة حدَّدناها، وينفِّذها LangChain4j، والتي تتطلّب طلب بحث String
وتعرض الاستجابة String
.
حان الوقت الآن لإجراء محادثة مع Gemini. أولاً، يتم إرسال تحية بسيطة، ثم السؤال الأول حول برج إيفل لمعرفة البلد الذي يقع فيه برج إيفل. لاحظ أن الجملة الأخيرة تتعلق بإجابة السؤال الأول، حيث إنك تتساءل عن عدد سكان البلد الذي يقع فيه برج إيفل، دون ذكر البلد الذي تم تقديمه في الإجابة السابقة بشكل صريح. ويظهر أنّه يتم إرسال الأسئلة والإجابات السابقة مع كل طلب.
تنفيذ النموذج:
./gradlew run -q -DjavaMainClass=gemini.workshop.Conversation
من المفترض أن ترى ثلاث إجابات مشابهة لهذه الإجابات:
User: Hello! Gemini: Hi there! How can I assist you today? User: What is the country where the Eiffel tower is situated? Gemini: France User: How many inhabitants are there in that country? Gemini: As of 2023, the population of France is estimated to be around 67.8 million.
يمكنك طرح أسئلة باتجاه واحد أو إجراء محادثات مع Gemini بعدة جولات، ولكن حتى الآن، كان الردّ على شكل نص فقط. ماذا عن الصور؟ لنستكشف الصور في الخطوة التالية.
6- ميزات متعددة مع Gemini
Gemini هو نموذج متعدد الوسائط. ولا يقبل فقط النص كإدخال، بل يقبل أيضًا الصور أو حتى الفيديوهات كإدخال. في هذا القسم، سترى حالة استخدام لمزج النصوص والصور.
هل تعتقد أنّ Gemini سيتعرّف على هذا القط؟
صورة قطة في الثلج مأخوذة من ويكيبيدياhttps://upload.wikimedia.org/wikipedia/commons/b/b6/Felis_catus-cat_on_snow.jpg
ألقِ نظرة على Multimodal.java
في دليل app/src/main/java/gemini/workshop
:
package gemini.workshop;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.vertexai.VertexAiGeminiChatModel;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.data.message.AiMessage;
import dev.langchain4j.model.output.Response;
import dev.langchain4j.data.message.ImageContent;
import dev.langchain4j.data.message.TextContent;
public class Multimodal {
static final String CAT_IMAGE_URL =
"https://upload.wikimedia.org/wikipedia/" +
"commons/b/b6/Felis_catus-cat_on_snow.jpg";
public static void main(String[] args) {
ChatLanguageModel model = VertexAiGeminiChatModel.builder()
.project(System.getenv("PROJECT_ID"))
.location(System.getenv("LOCATION"))
.modelName("gemini-1.5-flash-001")
.build();
UserMessage userMessage = UserMessage.from(
ImageContent.from(CAT_IMAGE_URL),
TextContent.from("Describe the picture")
);
Response<AiMessage> response = model.generate(userMessage);
System.out.println(response.content().text());
}
}
في عمليات الاستيراد، نلاحظ التمييز بين أنواع الرسائل والمحتوى المختلفة. ويمكن أن تحتوي السمة UserMessage
على كائن TextContent
وكائن ImageContent
. هذه هي اللعبة المتعددة الوسائط في Play، منها المزج بين النصوص والصور. يُرسِل النموذج مرة أخرى Response
التي تحتوي على AiMessage
.
يمكنك بعد ذلك استرداد AiMessage
من الرد عبر content()
، ثم نص الرسالة بفضل text()
.
تنفيذ النموذج:
./gradlew run -q -DjavaMainClass=gemini.workshop.Multimodal
لقد قدّم لك اسم الصورة تلميحًا بشأن مضمون الصورة، إلا أنّ ناتج Gemini يشبه ما يلي:
A cat with brown fur is walking in the snow. The cat has a white patch of fur on its chest and white paws. The cat is looking at the camera.
يؤدي مزج الصور والطلبات النصية إلى فتح حالات استخدام مثيرة للاهتمام. يمكنك إنشاء تطبيقات يمكنها:
- التعرّف على النص في الصور.
- تأكَّد مما إذا كانت الصورة آمنة للعرض.
- أنشئ شرحًا للصور.
- البحث في قاعدة بيانات الصور باستخدام أوصاف النص العادي.
بالإضافة إلى استخراج المعلومات من الصور، يمكنك أيضًا استخراج المعلومات من النص غير المنظَّم. هذا ما سوف تتعلمه في القسم التالي.
7. استخراج المعلومات المنظَّمة من النص غير المنظَّم
هناك العديد من المواقف التي يتم فيها تقديم معلومات مهمة في مستندات التقارير أو في الرسائل الإلكترونية أو غيرها من النصوص الطويلة بطريقة غير منظَّمة. من الناحية المثالية، أنت تريد أن تكون قادرًا على استخراج التفاصيل الأساسية الموجودة في النص غير المهيكل، في شكل كائنات منظمة. لنرَ كيف يمكنك القيام بذلك.
لنفترض أنّك تريد استخراج اسم شخص وعمره مع توفّر سيرة ذاتية أو وصف له. يمكنك توجيه النموذج اللغوي الكبير لاستخراج ملف JSON من النص غير المنظَّم باستخدام طلب تم تعديله بشكل ذكي (يُسمى هذا عادةً "هندسة الطلبات").
ألقِ نظرة على ExtractData.java
في app/src/main/java/gemini/workshop
:
package gemini.workshop;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.vertexai.VertexAiGeminiChatModel;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.service.UserMessage;
public class ExtractData {
static record Person(String name, int age) {}
interface PersonExtractor {
@UserMessage("""
Extract the name and age of the person described below.
Return a JSON document with a "name" and an "age" property, \
following this structure: {"name": "John Doe", "age": 34}
Return only JSON, without any markdown markup surrounding it.
Here is the document describing the person:
---
{{it}}
---
JSON:
""")
Person extractPerson(String text);
}
public static void main(String[] args) {
ChatLanguageModel model = VertexAiGeminiChatModel.builder()
.project(System.getenv("PROJECT_ID"))
.location(System.getenv("LOCATION"))
.modelName("gemini-1.5-flash-001")
.temperature(0f)
.topK(1)
.build();
PersonExtractor extractor = AiServices.create(PersonExtractor.class, model);
Person person = extractor.extractPerson("""
Anna is a 23 year old artist based in Brooklyn, New York. She was born and
raised in the suburbs of Chicago, where she developed a love for art at a
young age. She attended the School of the Art Institute of Chicago, where
she studied painting and drawing. After graduating, she moved to New York
City to pursue her art career. Anna's work is inspired by her personal
experiences and observations of the world around her. She often uses bright
colors and bold lines to create vibrant and energetic paintings. Her work
has been exhibited in galleries and museums in New York City and Chicago.
"""
);
System.out.println(person.name()); // Anna
System.out.println(person.age()); // 23
}
}
لنلقِ نظرة على الخطوات المختلفة في هذا الملف:
- يتم تعريف سجلّ
Person
لتمثيل التفاصيل التي تصف الشخص ( الاسم والعمر). - يتم تعريف الواجهة
PersonExtractor
من خلال طريقة تؤدي إلى عرض مثيلPerson
، عندما تكون سلسلة نصية غير منظَّمة. - تتم إضافة تعليقات توضيحية إلى
extractPerson()
من خلال تعليق توضيحي@UserMessage
يربط الطلب بها. هذا هو الطلب الذي سيستخدمه النموذج لاستخراج المعلومات وعرض التفاصيل في شكل مستند JSON الذي سيتم تحليله نيابةً عنك ودمجه في مثيلPerson
.
لنلقِ الآن نظرة على محتوى طريقة main()
:
- يتم إنشاء مثيل لنموذج المحادثة. يُرجى العلم أنّنا نستخدم قيمة
temperature
منخفضة جدًا، وtopK
من واحد فقط، لضمان الحصول على إجابة حاسمة للغاية. ويساعد ذلك أيضًا النموذج في اتّباع التعليمات بشكل أفضل. على وجه الخصوص، لا نريد أن يضمّ Gemini ردّ JSON بترميز Markdown إضافي. - يتم إنشاء عنصر
PersonExtractor
بفضل فئةAiServices
في LangChain4j. - بعد ذلك، يمكنك ببساطة استدعاء
Person person = extractor.extractPerson(...)
لاستخراج تفاصيل الشخص من النص غير المنظَّم، واسترجاع مثيلPerson
بالاسم والعمر.
تنفيذ النموذج:
./gradlew run -q -DjavaMainClass=gemini.workshop.ExtractData
من المفترض أن يظهر لك الناتج التالي:
Anna 23
نعم، اسمي مها وهم يبلغون من العمر 23 عامًا.
من خلال أسلوب AiServices
هذا، يمكنك العمل باستخدام العناصر المكتوبة بخط دقيق. أنت لا تتفاعل مباشرةً مع النموذج اللغوي الكبير. بدلاً من ذلك، ستعمل على فئات محدّدة، مثل سجلّ Person
لتمثيل المعلومات الشخصية المستخرجة، ولديك عنصر PersonExtractor
بطريقة extractPerson()
تعرض مثيل Person
. ما مِن مفهوم حول النموذج اللغوي الكبير (LLM)، وبصفتك مطوِّرًا بلغة Java، تتلاعب بالفئات والكائنات العادية.
8. تنظيم الطلبات باستخدام نماذج الطلبات
عند التفاعل مع نموذج لغوي كبير باستخدام مجموعة شائعة من التعليمات أو الأسئلة، يظهر جزء من هذا الطلب لا يتغير أبدًا، بينما تحتوي أجزاء أخرى على البيانات. على سبيل المثال، إذا كنت تريد إنشاء وصفات، يمكنك استخدام مطالبة مثل "أنت طاهٍ موهوب، يُرجى إنشاء وصفة باستخدام المكونات التالية: ..."، وبعد ذلك تُلحق المكونات بنهاية ذلك النص. هذا ما تُستخدم لقوالب المطالبة - تشبه السلاسل المتداخلة في لغات البرمجة. يحتوي نموذج الطلب على عناصر نائبة يمكنك استبدالها بالبيانات الصحيحة لاستدعاء معيّن إلى النموذج اللغوي الكبير.
بعبارة أخرى، لنتعرّف على TemplatePrompt.java
في دليل app/src/main/java/gemini/workshop
:
package gemini.workshop;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.vertexai.VertexAiGeminiChatModel;
import dev.langchain4j.data.message.AiMessage;
import dev.langchain4j.model.input.Prompt;
import dev.langchain4j.model.input.PromptTemplate;
import dev.langchain4j.model.output.Response;
import java.util.HashMap;
import java.util.Map;
public class TemplatePrompt {
public static void main(String[] args) {
ChatLanguageModel model = VertexAiGeminiChatModel.builder()
.project(System.getenv("PROJECT_ID"))
.location(System.getenv("LOCATION"))
.modelName("gemini-1.5-flash-001")
.maxOutputTokens(500)
.temperature(0.8f)
.topK(40)
.topP(0.95f)
.maxRetries(3)
.build();
PromptTemplate promptTemplate = PromptTemplate.from("""
You're a friendly chef with a lot of cooking experience.
Create a recipe for a {{dish}} with the following ingredients: \
{{ingredients}}, and give it a name.
"""
);
Map<String, Object> variables = new HashMap<>();
variables.put("dish", "dessert");
variables.put("ingredients", "strawberries, chocolate, and whipped cream");
Prompt prompt = promptTemplate.apply(variables);
Response<AiMessage> response = model.generate(prompt.toUserMessage());
System.out.println(response.content().text());
}
}
كالعادة، يمكنك ضبط نموذج "VertexAiGeminiChatModel
" الذي يحقّق مستوى عاليًا من الإبداع ودرجة حرارة عالية بالإضافة إلى قيم عالية المستوى وأعلى جودة. بعد ذلك، يمكنك إنشاء PromptTemplate
باستخدام الطريقة الثابتة from()
الخاصة بها، من خلال تمرير سلسلة الطلب، واستخدام متغيّرَي العنصر النائب المزدوجَين المعقوفَين: {{dish}}
و{{ingredients}}
.
يمكنك إنشاء الطلب النهائي من خلال استدعاء apply()
الذي يأخذ خريطة لأزواج المفتاح/القيمة التي تمثل اسم العنصر النائب وقيمة السلسلة لاستبدالها.
أخيرًا، يمكنك استدعاء طريقة generate()
لنموذج Gemini من خلال إنشاء رسالة مستخدم من هذا الطلب وفقًا لتعليمات prompt.toUserMessage()
.
تنفيذ النموذج:
./gradlew run -q -DjavaMainClass=gemini.workshop.TemplatePrompt
من المفترَض أن تظهر لك نتيجة تم إنشاؤها والتي تبدو مشابهة لما يلي:
**Strawberry Shortcake** Ingredients: * 1 pint strawberries, hulled and sliced * 1/2 cup sugar * 1/4 cup cornstarch * 1/4 cup water * 1 tablespoon lemon juice * 1/2 cup heavy cream, whipped * 1/4 cup confectioners' sugar * 1/4 teaspoon vanilla extract * 6 graham cracker squares, crushed Instructions: 1. In a medium saucepan, combine the strawberries, sugar, cornstarch, water, and lemon juice. Bring to a boil over medium heat, stirring constantly. Reduce heat and simmer for 5 minutes, or until the sauce has thickened. 2. Remove from heat and let cool slightly. 3. In a large bowl, combine the whipped cream, confectioners' sugar, and vanilla extract. Beat until soft peaks form. 4. To assemble the shortcakes, place a graham cracker square on each of 6 dessert plates. Top with a scoop of whipped cream, then a spoonful of strawberry sauce. Repeat layers, ending with a graham cracker square. 5. Serve immediately. **Tips:** * For a more elegant presentation, you can use fresh strawberries instead of sliced strawberries. * If you don't have time to make your own whipped cream, you can use store-bought whipped cream.
يمكنك تغيير قيمتَي dish
وingredients
في الخريطة وتعديل درجة الحرارة في topK
وtokP
وإعادة تشغيل الرمز. سيتيح لك ذلك ملاحظة تأثير تغيير هذه المَعلمات في النموذج اللغوي الكبير.
تُعدّ نماذج الطلبات طريقة جيدة للحصول على تعليمات قابلة لإعادة الاستخدام ويمكن تحديد مَعلمات بها لطلبات "النموذج اللغوي الكبير" (LLM). يمكنك تمرير البيانات وتخصيص الطلبات للقيم المختلفة التي يقدّمها المستخدمون.
9. تصنيف النص من خلال مطالبة بلقطات قليلة
وتستطيع النماذج اللغوية الكبيرة تصنيف النصوص إلى فئات مختلفة بشكل جيد. يمكنك مساعدة نموذج لغوي كبير (LLM) في إنجاز هذه المهمة من خلال تقديم بعض الأمثلة على النصوص والفئات المرتبطة بها. ويُطلق على هذه الطريقة عادةً تقديم بضع لقطات سريعة.
ألقِ نظرة على TextClassification.java
في دليل app/src/main/java/gemini/workshop
، لتصنيف النص حسب نوع محدّد، وهو: تحليل الآراء.
package gemini.workshop;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.vertexai.VertexAiGeminiChatModel;
import dev.langchain4j.data.message.AiMessage;
import dev.langchain4j.model.input.Prompt;
package gemini.workshop;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.vertexai.VertexAiGeminiChatModel;
import dev.langchain4j.data.message.AiMessage;
import dev.langchain4j.model.input.Prompt;
import dev.langchain4j.model.input.PromptTemplate;
import dev.langchain4j.model.output.Response;
import java.util.Map;
public class TextClassification {
public static void main(String[] args) {
ChatLanguageModel model = VertexAiGeminiChatModel.builder()
.project(System.getenv("PROJECT_ID"))
.location(System.getenv("LOCATION"))
.modelName("gemini-1.5-flash-001")
.maxOutputTokens(10)
.maxRetries(3)
.build();
PromptTemplate promptTemplate = PromptTemplate.from("""
Analyze the sentiment of the text below. Respond only with one word to describe the sentiment.
INPUT: This is fantastic news!
OUTPUT: POSITIVE
INPUT: Pi is roughly equal to 3.14
OUTPUT: NEUTRAL
INPUT: I really disliked the pizza. Who would use pineapples as a pizza topping?
OUTPUT: NEGATIVE
INPUT: {{text}}
OUTPUT:
""");
Prompt prompt = promptTemplate.apply(
Map.of("text", "I love strawberries!"));
Response<AiMessage> response = model.generate(prompt.toUserMessage());
System.out.println(response.content().text());
}
}
في طريقة main()
، يمكنك إنشاء نموذج محادثة Gemini كالمعتاد، ولكن باستخدام حدّ أقصى صغير للرمز المميّز للمخرجات، لأنّك تريد ردًا قصيرًا فقط: النص هو POSITIVE
أو NEGATIVE
أو NEUTRAL
.
بعد ذلك، تقوم بإنشاء قالب مطالبة قابل لإعادة الاستخدام باستخدام أسلوب المطالبة بلقطات قليلة، من خلال توجيه النموذج حول بعض الأمثلة على المدخلات والمخرجات. ويساعد هذا أيضًا النموذج في متابعة المخرجات الفعلية. لن يردّ Gemini بجملة كاملة، بل سيُطلَب منه الردّ بكلمة واحدة فقط.
يمكنك تطبيق المتغيّرات باستخدام الطريقة apply()
لاستبدال العنصر النائب {{text}}
بالمَعلمة الحقيقية ("I love strawberries"
)، وتحويل ذلك النموذج إلى رسالة مستخدم باستخدام toUserMessage()
.
تنفيذ النموذج:
./gradlew run -q -DjavaMainClass=gemini.workshop.TextClassification
من المفترض أن ترى كلمة واحدة:
POSITIVE
يبدو أنّ حب الفراولة هو شعور إيجابي.
10. الجيل المعزَّز والاسترجاعي
يتم تدريب النماذج اللغوية الكبيرة على كمية كبيرة من النصوص. ومع ذلك، لا تغطي معرفتهم سوى المعلومات التي اطلعوا عليها أثناء التدريب. إذا تم إصدار معلومات جديدة بعد الموعد النهائي لتدريب النماذج، لن تتوفّر تلك التفاصيل للنموذج. وبالتالي، لن يتمكن النموذج من الإجابة عن الأسئلة المتعلقة بالمعلومات التي لم يراها.
وهذا هو السبب في أنّ أساليب مثل Retrieval Augmented Generation (RAG) تساعد في توفير المعلومات الإضافية التي قد يحتاج "النموذج اللغوي الكبير" (LLM) إلى معرفتها لتلبية طلبات المستخدمين، والردّ بمعلومات قد تكون أحدث أو معلومات خاصة لا يمكن الوصول إليها في وقت التدريب.
لنعد إلى المحادثات. هذه المرة، ستتمكن من طرح أسئلة حول مستنداتك. ستنشئ روبوت دردشة قادرًا على استرداد المعلومات ذات الصلة من قاعدة بيانات تحتوي على مستنداتك مقسّمة إلى أجزاء أصغر ("مجموعات") وسيستخدم النموذج هذه المعلومات لإنشاء إجاباته، بدلاً من الاعتماد فقط على المعرفة الموجودة في تدريبه.
في RAG، هناك مرحلتان:
- مرحلة النقل: يتم تحميل المستندات في الذاكرة، وتقسيمها إلى أجزاء أصغر، ويتم حساب تضمينات المتجهات (تمثيل متجه متعدد الأبعاد مرتفع للمقاطع) وتخزينها في قاعدة بيانات متجهات قادرة على إجراء عمليات بحث دلالية. عادة ما تتم مرحلة النقل هذه مرة واحدة، عندما يلزم إضافة مستندات جديدة إلى مجموعة المستندات.
- مرحلة طلب البحث - يمكن للمستخدمين الآن طرح أسئلة حول المستندات. سيتم تحويل السؤال إلى متجه أيضًا ومقارنته بجميع المتجهات الأخرى في قاعدة البيانات. عادة ما تكون المتجهات الأكثر تماثلاً مرتبطة دلاليًا ويتم إرجاعها بواسطة قاعدة بيانات المتجه. بعد ذلك، يتم منح النموذج اللغوي الكبير سياق المحادثة، وهو أجزاء النص التي تتوافق مع المتجهات التي تعرضها قاعدة البيانات، ويُطلب منه تحديد إجابته من خلال النظر إلى تلك المقاطع.
إعداد مستنداتك
في هذا العرض التوضيحي الجديد، سوف تطرح أسئلة حول "الاهتمام هو كل ما تحتاجه". ورقة بحثية. وهو يصف بنية الشبكة العصبونية المحوّلة التي ابتكرتها Google، وهي الطريقة التي يتم بها في الوقت الحالي تنفيذ جميع النماذج اللغوية الكبيرة والحديثة.
سبق أن تم تنزيل الورقة إلى attention-is-all-you-need.pdf في المستودع.
تنفيذ برنامج الدردشة المبرمَجة
لنستكشف كيفية إنشاء نهج على مرحلتَين: أولاً من خلال عرض المستند، ثم وقت طلب البحث عندما يطرح المستخدمون أسئلة حول المستند.
في هذا المثال، يتم تنفيذ كلتا المرحلتين في نفس الفئة. وعادةً ما يكون لديك تطبيق واحد يتولّى عملية النقل، وتطبيق آخر يوفّر واجهة برنامج الدردشة المبرمَجة للمستخدمين.
كذلك، سوف نستخدم في هذا المثال قاعدة بيانات متجه في الذاكرة. في سيناريو إنتاج حقيقي، سيتم فصل مرحلتي العرض وطلب البحث في تطبيقين مختلفين، ويتم الاحتفاظ بالمتجهات في قاعدة بيانات مستقلة.
نقل المستند
الخطوة الأولى في مرحلة نقل المستند هي تحديد موقع ملف PDF الذي سبق أن نزّلناه، وإعداد PdfParser
لقراءته:
URL url = new URI("https://github.com/glaforge/gemini-workshop-for-java-developers/raw/main/attention-is-all-you-need.pdf").toURL();
ApachePdfBoxDocumentParser pdfParser = new ApachePdfBoxDocumentParser();
Document document = pdfParser.parse(url.openStream());
وبدلاً من إنشاء نموذج لغة المحادثة المعتاد، يمكنك إنشاء نسخة من نموذج تضمين. هذا نموذج معين يتمثل دوره في إنشاء تمثيلات متجهة لقطع النص (الكلمات أو الجمل أو حتى الفقرات). تقوم بإرجاع متجهات أرقام النقاط العائمة، بدلاً من عرض الردود النصية.
VertexAiEmbeddingModel embeddingModel = VertexAiEmbeddingModel.builder()
.endpoint(System.getenv("LOCATION") + "-aiplatform.googleapis.com:443")
.project(System.getenv("PROJECT_ID"))
.location(System.getenv("LOCATION"))
.publisher("google")
.modelName("textembedding-gecko@003")
.maxRetries(3)
.build();
بعد ذلك، ستحتاج إلى بعض الصفوف للتعاون معًا من أجل:
- لتحميل مستند PDF وتقسيمه إلى مجموعات
- قم بإنشاء تضمينات متجه لكل هذه المقاطع.
InMemoryEmbeddingStore<TextSegment> embeddingStore =
new InMemoryEmbeddingStore<>();
EmbeddingStoreIngestor storeIngestor = EmbeddingStoreIngestor.builder()
.documentSplitter(DocumentSplitters.recursive(500, 100))
.embeddingModel(embeddingModel)
.embeddingStore(embeddingStore)
.build();
storeIngestor.ingest(document);
يتم إنشاء مثيل لـ InMemoryEmbeddingStore
، وهو قاعدة بيانات متجه في الذاكرة، لتخزين تضمينات المتجه.
تم تقسيم المستند إلى مجموعات بفضل الفئة DocumentSplitters
. سيتم تقسيم نص ملف PDF إلى مقتطفات من 500 حرف، مع تداخل مكون من 100 حرف (مع المقطع التالي، لتجنب قص الكلمات أو الجمل، بأجزاء وأجزاء).
تربط أداة نقل المخزن أداة تقسيم المستند ونموذج التضمين لحساب المتجهات وقاعدة بيانات المتجه داخل الذاكرة. بعد ذلك، ستتولى طريقة ingest()
إجراء عملية النقل.
الآن، انتهت المرحلة الأولى، وتم تحويل المستند إلى أجزاء نصية مع تضمينات المتجهات المرتبطة بها، وتخزينها في قاعدة بيانات المتجه.
طرح الأسئلة
حان الوقت للاستعداد لطرح الأسئلة! أنشئ نموذج محادثة لبدء المحادثة:
ChatLanguageModel model = VertexAiGeminiChatModel.builder()
.project(System.getenv("PROJECT_ID"))
.location(System.getenv("LOCATION"))
.modelName("gemini-1.5-flash-001")
.maxOutputTokens(1000)
.build();
ستحتاج أيضًا إلى فئة أداة استرداد لربط قاعدة بيانات المتجه (في المتغير embeddingStore
) بنموذج التضمين. وتتمثل مهمتها في الاستعلام عن قاعدة بيانات المتجه عن طريق حساب تضمين متجه لاستعلام المستخدم، للعثور على متجهات مماثلة في قاعدة البيانات:
EmbeddingStoreContentRetriever retriever =
new EmbeddingStoreContentRetriever(embeddingStore, embeddingModel);
بخلاف الطريقة الرئيسية، أنشئ واجهة تمثّل مساعدًا خبيرًا في النموذج اللغوي الكبير، وهي واجهة ستنفّذها الفئة AiServices
للتفاعل مع النموذج:
interface LlmExpert {
String ask(String question);
}
في هذه المرحلة، يمكنك إعداد خدمة جديدة للذكاء الاصطناعي (AI):
LlmExpert expert = AiServices.builder(LlmExpert.class)
.chatLanguageModel(model)
.chatMemory(MessageWindowChatMemory.withMaxMessages(10))
.contentRetriever(retriever)
.build();
ترتبط هذه الخدمة معًا:
- نموذج لغة المحادثة الذي ضبطته سابقًا.
- ذاكرة محادثة لتتبُّع المحادثة
- تقارن أداة الاسترجاع طلب بحث تضمين المتجه بالمتجهات في قاعدة البيانات.
- يشير نموذج الطلب بشكل صريح إلى أنّه يجب أن يرد نموذج المحادثة من خلال الاعتماد على المعلومات المقدَّمة (أي المقتطفات ذات الصلة من المستندات التي يكون تضمين المتّجه فيها مشابهًا للموجّه الخاص بسؤال المستخدم).
.retrievalAugmentor(DefaultRetrievalAugmentor.builder()
.contentInjector(DefaultContentInjector.builder()
.promptTemplate(PromptTemplate.from("""
You are an expert in large language models,\s
you excel at explaining simply and clearly questions about LLMs.
Here is the question: {{userMessage}}
Answer using the following information:
{{contents}}
"""))
.build())
.contentRetriever(retriever)
.build())
أنت على استعداد لطرح أسئلتك!
List.of(
"What neural network architecture can be used for language models?",
"What are the different components of a transformer neural network?",
"What is attention in large language models?",
"What is the name of the process that transforms text into vectors?"
).forEach(query ->
System.out.printf("%n=== %s === %n%n %s %n%n", query, expert.ask(query)));
);
يمكنك العثور على رمز المصدر الكامل في RAG.java
في دليل app/src/main/java/gemini/workshop
:
تنفيذ النموذج:
./gradlew -q run -DjavaMainClass=gemini.workshop.RAG
في الإخراج، يُفترض أن ترى إجابات عن أسئلتك:
=== What neural network architecture can be used for language models? === Transformer architecture === What are the different components of a transformer neural network? === The different components of a transformer neural network are: 1. Encoder: The encoder takes the input sequence and converts it into a sequence of hidden states. Each hidden state represents the context of the corresponding input token. 2. Decoder: The decoder takes the hidden states from the encoder and uses them to generate the output sequence. Each output token is generated by attending to the hidden states and then using a feed-forward network to predict the token's probability distribution. 3. Attention mechanism: The attention mechanism allows the decoder to attend to the hidden states from the encoder when generating each output token. This allows the decoder to take into account the context of the input sequence when generating the output sequence. 4. Positional encoding: Positional encoding is a technique used to inject positional information into the input sequence. This is important because the transformer neural network does not have any inherent sense of the order of the tokens in the input sequence. 5. Feed-forward network: The feed-forward network is a type of neural network that is used to predict the probability distribution of each output token. The feed-forward network takes the hidden state from the decoder as input and outputs a vector of probabilities. === What is attention in large language models? === Attention in large language models is a mechanism that allows the model to focus on specific parts of the input sequence when generating the output sequence. This is important because it allows the model to take into account the context of the input sequence when generating each output token. Attention is implemented using a function that takes two sequences as input: a query sequence and a key-value sequence. The query sequence is typically the hidden state from the previous decoder layer, and the key-value sequence is typically the sequence of hidden states from the encoder. The attention function computes a weighted sum of the values in the key-value sequence, where the weights are determined by the similarity between the query and the keys. The output of the attention function is a vector of context vectors, which are then used as input to the feed-forward network in the decoder. The feed-forward network then predicts the probability distribution of the next output token. Attention is a powerful mechanism that allows large language models to generate text that is both coherent and informative. It is one of the key factors that has contributed to the recent success of large language models in a wide range of natural language processing tasks. === What is the name of the process that transforms text into vectors? === The process of transforming text into vectors is called **word embedding**. Word embedding is a technique used in natural language processing (NLP) to represent words as vectors of real numbers. Each word is assigned a unique vector, which captures its meaning and semantic relationships with other words. Word embeddings are used in a variety of NLP tasks, such as machine translation, text classification, and question answering. There are a number of different word embedding techniques, but one of the most common is the **skip-gram** model. The skip-gram model is a neural network that is trained to predict the surrounding words of a given word. By learning to predict the surrounding words, the skip-gram model learns to capture the meaning and semantic relationships of words. Once a word embedding model has been trained, it can be used to transform text into vectors. To do this, each word in the text is converted to its corresponding vector. The vectors for all of the words in the text are then concatenated to form a single vector, which represents the entire text. Text vectors can be used in a variety of NLP tasks. For example, text vectors can be used to train machine translation models, text classification models, and question answering models. Text vectors can also be used to perform tasks such as text summarization and text clustering.
11. استدعاء الدالة
هناك أيضًا حالات قد تريد فيها منح النماذج اللغوية الكبيرة إمكانية الوصول إلى أنظمة خارجية، مثل واجهة برمجة تطبيقات الويب عن بُعد التي تسترد معلومات أو تتضمّن إجراءً، أو الخدمات التي تنفِّذ نوعًا من العمليات الحسابية. على سبيل المثال:
واجهات برمجة تطبيقات الويب عن بُعد:
- تتبُّع طلبات العملاء وتعديلها
- ابحث عن طلب دعم أو أنشِئه في أداة تتبُّع المشاكل.
- جلب البيانات في الوقت الفعلي مثل أسعار الأسهم أو قياسات أداة استشعار إنترنت الأشياء (IoT).
- إرسال رسالة إلكترونية
أدوات الحوسبة:
- آلة حاسبة للمسائل الرياضية الأكثر تقدمًا.
- تفسير الرموز البرمجية لتشغيل الرموز عندما تحتاج النماذج اللغوية الكبيرة إلى منطق الاستنتاج
- تحويل طلبات اللغة الطبيعية إلى استعلامات SQL لكي يتمكّن "النموذج اللغوي الكبير" (LLM) من الاستعلام عن قاعدة بيانات.
يشير استدعاء الدوال إلى قدرة النموذج على طلب استدعاء دالة واحدة أو أكثر نيابةً عنه، حتى يتمكّن من الردّ بشكل صحيح على طلب المستخدم بإضافة بيانات أحدث.
استنادًا إلى طلب معيّن من أحد المستخدمين ومعرفة الدوال الحالية التي يمكن أن تكون ذات صلة بهذا السياق، يمكن للنموذج اللغوي الكبير الرد باستخدام طلب استدعاء الدالة. يمكن للتطبيق الذي يدمج النموذج اللغوي الكبير استدعاء الدالة، ثم الرد على النموذج اللغوي الكبير بردّ، ثم يفسّره من خلال الرد بإجابة نصية.
أربع خطوات لاستدعاء الدوال
لنلقِ نظرة على مثال لاستدعاء الدوال: الحصول على معلومات حول توقعات الطقس.
إذا سألت Gemini أو أي نموذج لغوي كبير آخر عن حالة الطقس في باريس، سيجيب بأنّه لا يقدّم أي معلومات عن توقعات الطقس. إذا أردت أن يصل النموذج اللغوي الكبير إلى بيانات الطقس في الوقت الفعلي، عليك تحديد بعض الوظائف التي يمكنه استخدامها.
ألق نظرة على الرسم البياني التالي:
1️⃣ أولاً، يسأل المستخدم عن حالة الطقس في باريس. يعرف تطبيق الدردشة المبرمَجة أنّ هناك وظيفة واحدة أو أكثر متاحة لمساعدة النموذج اللغوي الكبير في تلبية الطلب. يرسل كل من روبوت الدردشة الطلب الأولي بالإضافة إلى قائمة الوظائف التي يمكن استدعاؤها. هنا، دالة تسمى getWeather()
تأخذ معلمة سلسلة للموقع.
لا يتعرّف النموذج اللغوي الكبير على توقّعات الطقس، فبدلاً من الرد عبر رسالة نصية، يرسل النموذج طلب تنفيذ الدالة مرة أخرى. يجب أن يستدعي برنامج الدردشة المبرمَجة الدالة getWeather()
مع استخدام "Paris"
كمعلمة الموقع الجغرافي.
2️⃣ يستدعي برنامج الدردشة المبرمَجة الذي يعمل نيابةً عن النموذج اللغوي الكبير لاسترداد استجابة الدالة. نفترض هنا أنّ الردّ هو {"forecast": "sunny"}
.
3️⃣ يُرسِل تطبيق المحادثة المبرمَجة استجابة JSON إلى النموذج اللغوي الكبير.
4️⃣ تنظر النموذج اللغوي الكبير إلى استجابة JSON، ويفسر هذه المعلومات، وفي النهاية نرد عليها بالنص الذي يفيد بأنّ الطقس مشمس في باريس.
كل خطوة كرمز برمجي
أولاً، ستضبط نموذج Gemini كالمعتاد:
ChatLanguageModel model = VertexAiGeminiChatModel.builder()
.project(System.getenv("PROJECT_ID"))
.location(System.getenv("LOCATION"))
.modelName("gemini-1.5-flash-001")
.maxOutputTokens(100)
.build();
يمكنك تحديد مواصفات أداة تصف الدالة التي يمكن استدعاؤها:
ToolSpecification weatherToolSpec = ToolSpecification.builder()
.name("getWeatherForecast")
.description("Get the weather forecast for a location")
.addParameter("location", JsonSchemaProperty.STRING,
JsonSchemaProperty.description("the location to get the weather forecast for"))
.build();
يتم تعريف اسم الدالة، بالإضافة إلى اسم المعلمة ونوعها، ولكن لاحظ أنه يتم تقديم أوصاف لكل من الدالة والمعلمات. الأوصاف مهمة جدًا وتساعد النموذج اللغوي الكبير على فهم ما يمكن أن تقوم به دالة، وبالتالي تحديد ما إذا كان يجب استدعاء هذه الدالة في سياق المحادثة.
لنبدأ الخطوة رقم 1، بإرسال السؤال الأولي حول الطقس في باريس:
List<ChatMessage> allMessages = new ArrayList<>();
// 1) Ask the question about the weather
UserMessage weatherQuestion = UserMessage.from("What is the weather in Paris?");
allMessages.add(weatherQuestion);
في الخطوة رقم 2، نمرر الأداة التي نريد أن يستخدمها النموذج، ويرد النموذج بطلب تنفيذ جدًا:
// 2) The model replies with a function call request
Response<AiMessage> messageResponse = model.generate(allMessages, weatherToolSpec);
ToolExecutionRequest toolExecutionRequest = messageResponse.content().toolExecutionRequests().getFirst();
System.out.println("Tool execution request: " + toolExecutionRequest);
allMessages.add(messageResponse.content());
الخطوة رقم 3: في هذه المرحلة، نعرف الوظيفة التي تريد النموذج اللغوي الكبير أن نستدعيها. في الرمز البرمجي، لا نجري اتصالاً حقيقيًا بواجهة برمجة تطبيقات خارجية، بل نرجع فقط توقّعات افتراضية للطقس مباشرةً:
// 3) We send back the result of the function call
ToolExecutionResultMessage toolExecResMsg = ToolExecutionResultMessage.from(toolExecutionRequest,
"{\"location\":\"Paris\",\"forecast\":\"sunny\", \"temperature\": 20}");
allMessages.add(toolExecResMsg);
وفي الخطوة رقم 4، يتعرّف النموذج اللغوي الكبير على نتيجة تنفيذ الدالة، ويمكنه بعد ذلك إنشاء ردّ نصي:
// 4) The model answers with a sentence describing the weather
Response<AiMessage> weatherResponse = model.generate(allMessages);
System.out.println("Answer: " + weatherResponse.content().text());
المخرج هو:
Tool execution request: ToolExecutionRequest { id = null, name = "getWeatherForecast", arguments = "{"location":"Paris"}" }
Answer: The weather in Paris is sunny with a temperature of 20 degrees Celsius.
يمكنك مشاهدة في الإخراج أعلى طلب تنفيذ الأداة، وكذلك الإجابة.
يمكنك العثور على رمز المصدر الكامل في FunctionCalling.java
في دليل app/src/main/java/gemini/workshop
:
تنفيذ النموذج:
./gradlew run -q -DjavaMainClass=gemini.workshop.FunctionCalling
من المفترض أن تظهر لك نتيجة مشابهة لما يلي:
Tool execution request: ToolExecutionRequest { id = null, name = "getWeatherForecast", arguments = "{"location":"Paris"}" }
Answer: The weather in Paris is sunny with a temperature of 20 degrees Celsius.
12. يتعامل LangChain4j مع استدعاء الدوال
في الخطوة السابقة، رأيت كيف تكون تفاعلات السؤال/الإجابة النصية العادية والدالة/الطلب/الاستجابة متداخلة، وفي المنتصف، قدمت استجابة الدالة المطلوبة مباشرةً، بدون استدعاء دالة حقيقية.
مع ذلك، يوفر LangChain4j أيضًا تجريدًا على مستوى أعلى يمكنه التعامل مع استدعاءات الدالة بشفافية لك، أثناء التعامل مع المحادثة كالمعتاد.
استدعاء دالة واحدة
لنلقِ نظرة على FunctionCallingAssistant.java
، على مراحل.
أولاً، تقوم بإنشاء سجل يمثل هيكل بيانات استجابة الدالة:
record WeatherForecast(String location, String forecast, int temperature) {}
يتضمّن الردّ معلومات عن الموقع الجغرافي والتوقّعات ودرجة الحرارة.
بعد ذلك، تقوم بإنشاء فئة تحتوي على الدالة الفعلية التي تريد إتاحتها للنموذج:
static class WeatherForecastService {
@Tool("Get the weather forecast for a location")
WeatherForecast getForecast(@P("Location to get the forecast for") String location) {
if (location.equals("Paris")) {
return new WeatherForecast("Paris", "Sunny", 20);
} else if (location.equals("London")) {
return new WeatherForecast("London", "Rainy", 15);
} else {
return new WeatherForecast("Unknown", "Unknown", 0);
}
}
}
يُرجى العِلم أنّ هذه الفئة تحتوي على دالة واحدة، ولكن تحتوي على تعليق توضيحي باستخدام التعليق التوضيحي @Tool
الذي يتوافق مع وصف الدالة التي يمكن أن يطلب النموذج طلبها.
تتضمّن أيضًا مَعلمات الدالة (مَعلمة واحدة هنا) تعليقات توضيحية، ولكن مع تعليق @P
التوضيحي القصير هذا الذي يقدّم أيضًا وصفًا للمَعلمة. يمكنك إضافة أي عدد تريده من الدوال لإتاحتها في النموذج في سيناريوهات أكثر تعقيدًا.
في هذا الصف، يمكنك إرجاع بعض الردود الجاهزة، ولكن إذا أردت الاتصال بخدمة خارجية حقيقية لتوقع الطقس، فهذا في نص هذه الطريقة التي ستجري بها الاتصال بتلك الخدمة.
كما رأينا عند إنشاء ToolSpecification
في الطريقة السابقة، من المهم توثيق وظيفة الدالة ووصف ما تتوافق معه المعلَمات. وهذا يساعد النموذج في فهم كيف ومتى يمكن استخدام هذه الدالة.
بعد ذلك، يتيح لك LangChain4j توفير واجهة تتوافق مع العقد الذي تريد استخدامه للتفاعل مع النموذج. هنا، هي واجهة بسيطة تتعامل مع سلسلة تمثل رسالة المستخدم، وتعرض سلسلة تتوافق مع استجابة النموذج:
interface WeatherAssistant {
String chat(String userMessage);
}
من الممكن أيضًا استخدام توقيعات أكثر تعقيدًا تشمل UserMessage
في LangChain4j (لرسالة مستخدم) أو AiMessage
(لرد نموذج)، أو حتى TokenStream
، إذا كنت تريد التعامل مع حالات أكثر تقدمًا، لأنّ هذه العناصر الأكثر تعقيدًا تحتوي أيضًا على معلومات إضافية مثل عدد الرموز المميزة المستخدمة وما إلى ذلك. ولكن للتبسيط، سنأخذ سلسلة في الإدخال وسلسلة في الإخراج.
لننهي باستخدام طريقة main()
التي تربط جميع القطع معًا:
public static void main(String[] args) {
ChatLanguageModel model = VertexAiGeminiChatModel.builder()
.project(System.getenv("PROJECT_ID"))
.location(System.getenv("LOCATION"))
.modelName("gemini-1.5-flash-001")
.maxOutputTokens(100)
.build();
WeatherForecastService weatherForecastService = new WeatherForecastService();
WeatherAssistant assistant = AiServices.builder(WeatherAssistant.class)
.chatLanguageModel(model)
.chatMemory(MessageWindowChatMemory.withMaxMessages(10))
.tools(weatherForecastService)
.build();
System.out.println(assistant.chat("What is the weather in Paris?"));
}
كالعادة، يتم ضبط نموذج المحادثة في Gemini. ثم تنشئ مثيلاً لخدمة توقع الطقس التي تحتوي على "الوظيفة" الذي سيطلب منا النموذج الاتصال به.
يمكنك الآن استخدام فئة AiServices
مرة أخرى لربط نموذج المحادثة وذاكرة المحادثة والأداة (أي خدمة توقّعات الطقس بوظيفتها). تعرض AiServices
كائنًا ينفذ واجهة WeatherAssistant
التي حددتها. الشيء الوحيد المتبقي هو استدعاء طريقة chat()
لذلك المساعد. عند استدعائها، لن تظهر لك سوى الردود النصية، ولكن لن تظهر طلبات استدعاء الدالة واستجابات استدعائها للدالة بواسطة المطوّر، وسيتم التعامل مع هذه الطلبات تلقائيًا وبشفافية. إذا رأى Gemini أنّه يجب استدعاء إحدى الدوال، سيردّ باستخدام طلب استدعاء الدالة، وسيتولى LangChain4j استدعاء الدالة المحلية نيابةً عنك.
تنفيذ النموذج:
./gradlew run -q -DjavaMainClass=gemini.workshop.FunctionCallingAssistant
من المفترض أن تظهر لك نتيجة مشابهة لما يلي:
OK. The weather in Paris is sunny with a temperature of 20 degrees.
كان هذا مثالاً لدالة واحدة.
طلبات الوظائف المتعدّدة
يمكنك أيضًا إجراء عدة دوال والسماح لـ LangChain4j بمعالجة طلبات متعددة الدوال نيابةً عنك. ألقِ نظرة على MultiFunctionCallingAssistant.java
للاطّلاع على مثال على الدوال المتعددة.
لها دالة لتحويل العملات:
@Tool("Convert amounts between two currencies")
double convertCurrency(
@P("Currency to convert from") String fromCurrency,
@P("Currency to convert to") String toCurrency,
@P("Amount to convert") double amount) {
double result = amount;
if (fromCurrency.equals("USD") && toCurrency.equals("EUR")) {
result = amount * 0.93;
} else if (fromCurrency.equals("USD") && toCurrency.equals("GBP")) {
result = amount * 0.79;
}
System.out.println(
"convertCurrency(fromCurrency = " + fromCurrency +
", toCurrency = " + toCurrency +
", amount = " + amount + ") == " + result);
return result;
}
دالة أخرى للحصول على قيمة المخزون:
@Tool("Get the current value of a stock in US dollars")
double getStockPrice(@P("Stock symbol") String symbol) {
double result = 170.0 + 10 * new Random().nextDouble();
System.out.println("getStockPrice(symbol = " + symbol + ") == " + result);
return result;
}
دالة أخرى لتطبيق نسبة مئوية على مبلغ معين:
@Tool("Apply a percentage to a given amount")
double applyPercentage(@P("Initial amount") double amount, @P("Percentage between 0-100 to apply") double percentage) {
double result = amount * (percentage / 100);
System.out.println("applyPercentage(amount = " + amount + ", percentage = " + percentage + ") == " + result);
return result;
}
يمكنك بعد ذلك الجمع بين كل هذه الدوال وفئة الأدوات المتعددة وطرح أسئلة مثل "ما نسبة 10% من سعر سهم AAPL الذي تم تحويله من الدولار الأمريكي إلى يورو؟"
public static void main(String[] args) {
ChatLanguageModel model = VertexAiGeminiChatModel.builder()
.project(System.getenv("PROJECT_ID"))
.location(System.getenv("LOCATION"))
.modelName("gemini-1.5-flash-001")
.maxOutputTokens(100)
.build();
MultiTools multiTools = new MultiTools();
MultiToolsAssistant assistant = AiServices.builder(MultiToolsAssistant.class)
.chatLanguageModel(model)
.chatMemory(withMaxMessages(10))
.tools(multiTools)
.build();
System.out.println(assistant.chat(
"What is 10% of the AAPL stock price converted from USD to EUR?"));
}
قم بتشغيله على النحو التالي:
./gradlew run -q -DjavaMainClass=gemini.workshop.MultiFunctionCallingAssistant
ومن المفترض أن ترى الدوال المتعددة التي تسمى:
getStockPrice(symbol = AAPL) == 172.8022224055534 convertCurrency(fromCurrency = USD, toCurrency = EUR, amount = 172.8022224055534) == 160.70606683716468 applyPercentage(amount = 160.70606683716468, percentage = 10.0) == 16.07060668371647 10% of the AAPL stock price converted from USD to EUR is 16.07060668371647 EUR.
نحو الوكلاء
إنّ طلب الوظائف هو آلية رائعة لإضافة النماذج اللغوية الكبيرة، مثل Gemini. وهو يمكّننا من إنشاء أنظمة أكثر تعقيدًا تُعرف باسم "الوكلاء" أو "أدوات المساعدة المستندة إلى الذكاء الاصطناعي" ويمكن لهؤلاء الوكلاء التفاعل مع العالم الخارجي عبر واجهات برمجة التطبيقات الخارجية ومع الخدمات التي قد يكون لها آثار جانبية على البيئة الخارجية (مثل إرسال الرسائل الإلكترونية وإنشاء طلبات الدعم وما إلى ذلك).
عند إنشاء مثل هؤلاء الوكلاء الأقوياء، عليك إجراء ذلك بمسؤولية. يجب أن تفكر في وجود إنسان في الحلقة قبل اتخاذ إجراءات تلقائية. من المهم مراعاة السلامة عند تصميم برامج وكيلة تستند إلى النماذج اللغوية الكبيرة (LLM) وتتفاعل مع العالم الخارجي.
13. تشغيل Gemma مع Ollama وTestContainers
كنا نستخدم Gemini حتى الآن، ولكن هناك أيضًا نموذج Gemma الشقيق الصغير.
Gemma هي مجموعة من النماذج الحديثة والخفيفة المتاحة للجميع التي تم إنشاؤها من الأبحاث والتكنولوجيا نفسها المستخدمة في إنشاء نماذج Gemini. تتوفر Gemma في شكلين مختلفين Gemma1 وGemma2 لكل منهما بأحجام مختلفة. تتوفّر Gemma1 بحجمَين: 2B و7B. يتوفّر Gemma2 بحجمَين: 9B و27B. أوزانها متاحة مجانًا، وتعني أحجامها الصغيرة إمكانية تشغيلها بمفردك، حتى على الكمبيوتر المحمول أو في Cloud Shell.
كيف تشغّل Gemma؟
هناك العديد من الطرق لتشغيل Gemma: في السحابة الإلكترونية، عبر Vertex AI بنقرة زر، أو GKE مع بعض وحدات معالجة الرسومات، ويمكنك أيضًا تشغيله محليًا.
يُعدّ استخدام Ollama خيارًا جيدًا لتشغيل Gemma محليًا، وهي أداة تتيح لك تشغيل نماذج صغيرة، مثل Llama 2 وMistral وغيرها الكثير على جهازك المحلي. وهو مشابه لـ Docker، ولكنّه مخصّص للنماذج اللغوية الكبيرة.
ثبِّت Ollama باتّباع الإرشادات المتعلقة بنظام التشغيل.
إذا كنت تستخدم بيئة Linux، ستحتاج إلى تفعيل Ollama أولاً بعد تثبيتها.
ollama serve > /dev/null 2>&1 &
بعد التثبيت على الجهاز، يمكنك تشغيل الأوامر لسحب نموذج:
ollama pull gemma:2b
انتظر حتى يتم سحب النموذج. يمكن أن تستغرق هذه العملية بعض الوقت.
تنفيذ النموذج:
ollama run gemma:2b
والآن، يمكنك التفاعل مع النموذج:
>>> Hello! Hello! It's nice to hear from you. What can I do for you today?
للخروج من المطالبة، اضغط على Ctrl+D
تشغيل Gemma في Ollama على TestContainers
بدلاً من تثبيت تطبيق Ollama وتشغيله على الجهاز، يمكنك استخدام Ollama داخل حاوية يتم التعامل معها من خلال TestContainers.
لا تقتصر أهمية دالة TestContainers على الاختبار، ولكن يمكنك استخدامها أيضًا لتنفيذ الحاويات. هناك أيضًا OllamaContainer
محدّد يمكنك الاستفادة منه.
ها هي الصورة الكاملة:
التنفيذ
لنلقِ نظرة على GemmaWithOllamaContainer.java
، على مراحل.
أولاً، تحتاج إلى إنشاء حاوية Ollama مشتقة تسحب نموذج Gemma. هذه الصورة متوفّرة من قبل أو سيتم إنشاؤها. إذا كانت الصورة متاحة، ما عليك سوى إعلام TestContainers بأنّك تريد استبدال صورة Ollama التلقائية بالصيغة المستندة إلى Gemma:
private static final String TC_OLLAMA_GEMMA_2_B = "tc-ollama-gemma-2b";
// Creating an Ollama container with Gemma 2B if it doesn't exist.
private static OllamaContainer createGemmaOllamaContainer() throws IOException, InterruptedException {
// Check if the custom Gemma Ollama image exists already
List<Image> listImagesCmd = DockerClientFactory.lazyClient()
.listImagesCmd()
.withImageNameFilter(TC_OLLAMA_GEMMA_2_B)
.exec();
if (listImagesCmd.isEmpty()) {
System.out.println("Creating a new Ollama container with Gemma 2B image...");
OllamaContainer ollama = new OllamaContainer("ollama/ollama:0.1.26");
ollama.start();
ollama.execInContainer("ollama", "pull", "gemma:2b");
ollama.commitToImage(TC_OLLAMA_GEMMA_2_B);
return ollama;
} else {
System.out.println("Using existing Ollama container with Gemma 2B image...");
// Substitute the default Ollama image with our Gemma variant
return new OllamaContainer(
DockerImageName.parse(TC_OLLAMA_GEMMA_2_B)
.asCompatibleSubstituteFor("ollama/ollama"));
}
}
بعد ذلك، يمكنك إنشاء حاوية اختبار Ollama وتشغيلها، ثم إنشاء نموذج محادثة Ollama، من خلال الإشارة إلى عنوان ومنفذ الحاوية باستخدام النموذج الذي تريد استخدامه. أخيرًا، استدعيت model.generate(yourPrompt)
كالمعتاد:
public static void main(String[] args) throws IOException, InterruptedException {
OllamaContainer ollama = createGemmaOllamaContainer();
ollama.start();
ChatLanguageModel model = OllamaChatModel.builder()
.baseUrl(String.format("http://%s:%d", ollama.getHost(), ollama.getFirstMappedPort()))
.modelName("gemma:2b")
.build();
String response = model.generate("Why is the sky blue?");
System.out.println(response);
}
قم بتشغيله على النحو التالي:
./gradlew run -q -DjavaMainClass=gemini.workshop.GemmaWithOllamaContainer
سيستغرق التشغيل الأول بعض الوقت لإنشاء الحاوية وتشغيلها، ولكن بعد الانتهاء، يُفترض أن ترى استجابة Gemma:
INFO: Container ollama/ollama:0.1.26 started in PT2.827064047S
The sky appears blue due to Rayleigh scattering. Rayleigh scattering is a phenomenon that occurs when sunlight interacts with molecules in the Earth's atmosphere.
* **Scattering particles:** The main scattering particles in the atmosphere are molecules of nitrogen (N2) and oxygen (O2).
* **Wavelength of light:** Blue light has a shorter wavelength than other colors of light, such as red and yellow.
* **Scattering process:** When blue light interacts with these molecules, it is scattered in all directions.
* **Human eyes:** Our eyes are more sensitive to blue light than other colors, so we perceive the sky as blue.
This scattering process results in a blue appearance for the sky, even though the sun is actually emitting light of all colors.
In addition to Rayleigh scattering, other atmospheric factors can also influence the color of the sky, such as dust particles, aerosols, and clouds.
لديك Gemma قيد التشغيل في Cloud Shell!
14. تهانينا
تهانينا، لقد أنشأت بنجاح أول تطبيق دردشة مستند إلى الذكاء الاصطناعي التوليدي في Java باستخدام LangChain4j وGemini API. لقد اكتشفت على طول الطريق أنّ النماذج اللغوية الكبيرة المتعددة الوسائط فعّالة جدًا وقادرة على التعامل مع مهام مختلفة، مثل الأسئلة أو الإجابة، حتى في المستندات الخاصة بك واستخراج البيانات والتفاعل مع واجهات برمجة التطبيقات الخارجية وغير ذلك.
الخطوات التالية
حان دورك لتحسين تطبيقاتك من خلال عمليات الدمج الفعّالة للنماذج اللغوية الكبيرة.
قراءة إضافية
- حالات الاستخدام الشائعة للذكاء الاصطناعي التوليدي
- مراجع تدريبية حول الذكاء الاصطناعي التوليدي
- التفاعل مع Gemini من خلال Generative AI Studio
- الذكاء الاصطناعي المسؤول