Gemini در جاوا با Vertex AI و LangChain4j

1. مقدمه

این آزمایشگاه کد روی مدل زبان بزرگ Gemini (LLM) متمرکز است که بر روی Vertex AI در Google Cloud میزبانی شده است. Vertex AI پلتفرمی است که تمامی محصولات، خدمات و مدل‌های یادگیری ماشین را در Google Cloud در بر می‌گیرد.

شما از جاوا برای تعامل با Gemini API با استفاده از چارچوب LangChain4j استفاده خواهید کرد. برای استفاده از LLM برای پاسخگویی به سؤال، تولید ایده، استخراج محتوای ساختاریافته، بازیابی تولید افزوده شده و فراخوانی تابع، نمونه‌های عینی را مرور خواهید کرد.

هوش مصنوعی مولد چیست؟

هوش مصنوعی مولد به استفاده از هوش مصنوعی برای ایجاد محتوای جدید مانند متن، تصاویر، موسیقی، صدا و ویدئو اشاره دارد.

هوش مصنوعی مولد از مدل‌های زبان بزرگ (LLM) پشتیبانی می‌کند که می‌توانند چند کار را انجام دهند و کارهای خارج از چارچوب مانند خلاصه‌سازی، پرسش و پاسخ، طبقه‌بندی و موارد دیگر را انجام دهند. با حداقل آموزش، مدل‌های پایه را می‌توان برای موارد استفاده هدفمند با داده‌های نمونه بسیار کمی تطبیق داد.

هوش مصنوعی Generative چگونه کار می کند؟

هوش مصنوعی مولد با استفاده از مدل یادگیری ماشینی (ML) برای یادگیری الگوها و روابط موجود در مجموعه داده ای از محتوای ساخته شده توسط انسان کار می کند. سپس از الگوهای آموخته شده برای تولید محتوای جدید استفاده می کند.

رایج ترین راه برای آموزش یک مدل هوش مصنوعی مولد استفاده از یادگیری تحت نظارت است. به مدل مجموعه ای از محتوای ایجاد شده توسط انسان و برچسب های مربوطه داده می شود. سپس یاد می گیرد که محتوایی شبیه به محتوای ساخته شده توسط انسان تولید کند.

برنامه های رایج هوش مصنوعی Generative چیست؟

از هوش مصنوعی مولد می توان برای موارد زیر استفاده کرد:

  • تعاملات مشتری را از طریق چت و تجربه جستجوی پیشرفته بهبود بخشید.
  • حجم وسیعی از داده های بدون ساختار را از طریق رابط های مکالمه و خلاصه سازی کاوش کنید.
  • در انجام کارهای تکراری مانند پاسخ به درخواست‌های پیشنهادی، بومی‌سازی محتوای بازاریابی به زبان‌های مختلف و بررسی قراردادهای مشتری برای انطباق و موارد دیگر کمک کنید.

Google Cloud چه پیشنهادات هوش مصنوعی مولد دارد؟

با Vertex AI ، می‌توانید بدون تخصص ML با مدل‌های پایه تعامل، سفارشی‌سازی و در برنامه‌های خود جاسازی کنید. می‌توانید به مدل‌های پایه در Model Garden دسترسی داشته باشید، مدل‌ها را از طریق یک رابط کاربری ساده در Vertex AI Studio تنظیم کنید، یا از مدل‌ها در یک نوت‌بوک علم داده استفاده کنید.

Vertex AI Search and Conversation به توسعه دهندگان سریع ترین راه را برای ساخت موتورهای جستجو و چت بات های مبتنی بر هوش مصنوعی ارائه می دهد.

پشتیبانی شده توسط Gemini، Gemini for Google Cloud یک همکار مبتنی بر هوش مصنوعی است که در سراسر Google Cloud و IDE ها در دسترس است تا به شما کمک کند کارهای بیشتری را سریعتر انجام دهید. Gemini Code Assist تکمیل کد، تولید کد، توضیحات کد را ارائه می دهد و به شما امکان می دهد با آن چت کنید تا سوالات فنی بپرسید.

جمینی چیست؟

جمینی یک خانواده از مدل‌های هوش مصنوعی تولید شده توسط Google DeepMind است که برای موارد استفاده چندوجهی طراحی شده است. Multimodal به این معنی است که می تواند انواع مختلف محتوا مانند متن، کد، تصاویر و صدا را پردازش و تولید کند.

b9913d011999e7c7.png

جمینی در انواع و اندازه های مختلف وجود دارد:

  • Gemini Ultra : بزرگترین و تواناترین نسخه برای کارهای پیچیده.
  • Gemini Flash : سریعترین و مقرون به صرفه ترین، بهینه سازی شده برای کارهای با حجم بالا.
  • Gemini Pro : اندازه متوسط، بهینه‌سازی شده برای مقیاس‌بندی در کارهای مختلف.
  • Gemini Nano : کارآمدترین، طراحی شده برای کارهای روی دستگاه.

ویژگی های کلیدی:

  • چندوجهی : توانایی Gemini برای درک و مدیریت فرمت‌های اطلاعاتی متعدد، گامی مهم فراتر از مدل‌های زبانی سنتی است.
  • عملکرد : Gemini Ultra در بسیاری از معیارها از پیشرفته ترین حالت فعلی بهتر عمل می کند و اولین مدلی بود که در معیار چالش برانگیز MMLU (درک زبان چند وظیفه ای عظیم) از متخصصان انسانی پیشی گرفت.
  • انعطاف پذیری : اندازه های مختلف Gemini آن را برای موارد استفاده مختلف، از تحقیقات در مقیاس بزرگ تا استقرار در دستگاه های تلفن همراه، سازگار می کند.

چگونه می توانید با Gemini در Vertex AI از جاوا تعامل داشته باشید؟

شما دو گزینه دارید:

  1. API رسمی Vertex AI Java برای کتابخانه Gemini .
  2. چارچوب LangChain4j .

در این کد لبه از چارچوب LangChain4j استفاده خواهید کرد.

چارچوب LangChain4j چیست؟

چارچوب LangChain4j یک کتابخانه منبع باز برای ادغام LLM ها در برنامه های جاوا شما، با هماهنگ کردن اجزای مختلف، مانند خود LLM، و همچنین ابزارهای دیگر مانند پایگاه های داده برداری (برای جستجوهای معنایی)، بارکننده اسناد و تقسیم کننده ها (برای تجزیه و تحلیل اسناد و یادگیری) است. از آنها)، تجزیه کننده های خروجی، و موارد دیگر.

این پروژه از پروژه LangChain Python الهام گرفته شده است اما با هدف خدمت به توسعه دهندگان جاوا.

bb908ea1e6c96ac2.png

چیزی که یاد خواهید گرفت

  • نحوه راه اندازی یک پروژه جاوا برای استفاده از Gemini و LangChain4j
  • چگونه اولین درخواست خود را به صورت برنامه ریزی شده به Gemini ارسال کنید
  • نحوه پخش جریانی پاسخ ها از Gemini
  • نحوه ایجاد مکالمه بین کاربر و جمینی
  • نحوه استفاده از Gemini در زمینه چند وجهی با ارسال متن و تصویر
  • نحوه استخراج اطلاعات ساختاریافته مفید از محتوای بدون ساختار
  • نحوه دستکاری الگوهای سریع
  • نحوه انجام طبقه بندی متن مانند تجزیه و تحلیل احساسات
  • چگونه با اسناد خود چت کنیم (نسل تقویت شده بازیابی)
  • چگونه ربات های چت خود را با فراخوانی تابع گسترش دهید
  • نحوه استفاده از Gemma به صورت محلی با Olama و TestContainers

آنچه شما نیاز دارید

  • آشنایی با زبان برنامه نویسی جاوا
  • یک پروژه Google Cloud
  • مرورگری مانند کروم یا فایرفاکس

2. راه اندازی و الزامات

تنظیم محیط خود به خود

  1. به Google Cloud Console وارد شوید و یک پروژه جدید ایجاد کنید یا از یک موجود استفاده مجدد کنید. اگر قبلاً یک حساب Gmail یا Google Workspace ندارید، باید یک حساب ایجاد کنید .

fbef9caa1602edd0.png

a99b7ace416376c4.png

5e3ff691252acf41.png

  • نام پروژه نام نمایشی برای شرکت کنندگان این پروژه است. این یک رشته کاراکتری است که توسط API های Google استفاده نمی شود. همیشه می توانید آن را به روز کنید.
  • شناسه پروژه در تمام پروژه‌های Google Cloud منحصربه‌فرد است و تغییرناپذیر است (پس از تنظیم نمی‌توان آن را تغییر داد). Cloud Console به طور خودکار یک رشته منحصر به فرد تولید می کند. معمولاً برای شما مهم نیست که چیست. در اکثر کدها، باید شناسه پروژه خود را ارجاع دهید (معمولاً با نام PROJECT_ID شناخته می شود). اگر شناسه تولید شده را دوست ندارید، ممکن است یک شناسه تصادفی دیگر ایجاد کنید. از طرف دیگر، می‌توانید خودتان را امتحان کنید، و ببینید آیا در دسترس است یا خیر. پس از این مرحله نمی توان آن را تغییر داد و در طول مدت پروژه باقی می ماند.
  • برای اطلاع شما، یک مقدار سوم وجود دارد، یک شماره پروژه ، که برخی از API ها از آن استفاده می کنند. در مورد هر سه این مقادیر در مستندات بیشتر بیاموزید.
  1. در مرحله بعد، برای استفاده از منابع Cloud/APIها باید صورتحساب را در کنسول Cloud فعال کنید . اجرا کردن از طریق این کد لبه هزینه زیادی نخواهد داشت. برای خاموش کردن منابع برای جلوگیری از تحمیل صورت‌حساب فراتر از این آموزش، می‌توانید منابعی را که ایجاد کرده‌اید حذف کنید یا پروژه را حذف کنید. کاربران جدید Google Cloud واجد شرایط برنامه آزمایشی رایگان 300 دلاری هستند.

Cloud Shell را راه اندازی کنید

در حالی که Google Cloud را می توان از راه دور از لپ تاپ شما کار کرد، در این کد لبه از Cloud Shell استفاده خواهید کرد، یک محیط خط فرمان که در Cloud اجرا می شود.

Cloud Shell را فعال کنید

  1. از Cloud Console، روی Activate Cloud Shell کلیک کنید 853e55310c205094.png .

3c1dabeca90e44e5.png

اگر این اولین باری است که Cloud Shell را راه اندازی می کنید، با یک صفحه میانی روبرو می شوید که آن را توصیف می کند. اگر با یک صفحه میانی مواجه شدید، روی Continue کلیک کنید.

9c92662c6a846a5c.png

تهیه و اتصال به Cloud Shell فقط باید چند لحظه طول بکشد.

9f0e51b578fecce5.png

این ماشین مجازی با تمام ابزارهای توسعه مورد نیاز بارگذاری شده است. این یک فهرست اصلی 5 گیگابایتی دائمی ارائه می‌کند و در Google Cloud اجرا می‌شود، که عملکرد و احراز هویت شبکه را بسیار افزایش می‌دهد. بسیاری از کارهای شما، اگر نه همه، در این کد لبه با مرورگر قابل انجام است.

پس از اتصال به Cloud Shell، باید ببینید که احراز هویت شده اید و پروژه به ID پروژه شما تنظیم شده است.

  1. برای تایید احراز هویت، دستور زیر را در 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`
  1. دستور زیر را در 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 برای توسعه برنامه های جاوا خود استفاده می کنید.

API های Vertex AI را فعال کنید

در کنسول Google Cloud، مطمئن شوید که نام پروژه شما در بالای کنسول Google Cloud شما نمایش داده شده است. اگر اینطور نیست، روی Select a project کلیک کنید تا Project Selector باز شود و پروژه مورد نظر خود را انتخاب کنید.

می‌توانید APIهای Vertex AI را از بخش Vertex AI کنسول Google Cloud یا از ترمینال Cloud Shell فعال کنید.

برای فعال کردن از کنسول Google Cloud، ابتدا به بخش Vertex AI منوی کنسول Google Cloud بروید:

451976f1c8652341.png

روی Enable All Recommended APIs در داشبورد Vertex AI کلیک کنید.

این چندین API را فعال می‌کند، اما مهم‌ترین آنها برای Codelab aiplatform.googleapis.com است.

همچنین می‌توانید این API را از ترمینال Cloud Shell با دستور زیر فعال کنید:

gcloud services enable aiplatform.googleapis.com

مخزن Github را شبیه سازی کنید

در ترمینال Cloud Shell، مخزن این Codelab را شبیه سازی کنید:

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 از Cloud Shell باز کنید:

42908e11b28f4383.png

در Cloud Code Editor، با انتخاب File -> Open Folder ، پوشه منبع codelab را باز کنید و به پوشه منبع codelab اشاره کنید (به عنوان مثال /home/username/gemini-workshop-for-java-developers/ ).

Gradle را برای جاوا نصب کنید

برای اینکه ویرایشگر کد ابری به درستی با Gradle کار کند، پسوند Gradle for Java را نصب کنید.

ابتدا به بخش Java Projects رفته و علامت مثبت را فشار دهید:

84d15639ac61c197.png

Gradle for Java انتخاب کنید:

34d6c4136a3cc9ff.png

نسخه Install Pre-Release انتخاب کنید:

3b044fb450cccb7.png

پس از نصب، باید دکمه های Disable و Uninstall را مشاهده کنید:

46410fe86d777f9c.png

در نهایت، فضای کاری را تمیز کنید تا تنظیمات جدید اعمال شوند:

31e27e9bb61d975d.png

این از شما می خواهد که کارگاه را مجدداً بارگیری و حذف کنید. ادامه دهید و Reload and delete انتخاب کنید:

d6303bc49e391dc.png

اگر یکی از فایل ها را باز کنید، به عنوان مثال App.java، اکنون باید ویرایشگر را ببینید که به درستی با برجسته کردن نحو کار می کند:

fed1b1b5de0dff58.png

اکنون آماده اجرای چند نمونه در برابر Gemini هستید!

تنظیم متغیرهای محیطی

با انتخاب Terminal -> New Terminal ، ترمینال جدیدی را در ویرایشگر کد ابری باز کنید. دو متغیر محیطی مورد نیاز برای اجرای نمونه کد را تنظیم کنید:

  • PROJECT_ID — شناسه پروژه Google Cloud شما
  • مکان - منطقه ای که مدل Gemini در آن مستقر شده است

متغیرها را به صورت زیر صادر کنید:

export PROJECT_ID=$(gcloud config get-value project)
export LOCATION=us-central1

4. اولین تماس با مدل جمینی

اکنون که پروژه به درستی راه اندازی شده است، زمان فراخوانی 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 پیکربندی کرده و مشخص می‌کنید:

  • پروژه
  • مکان
  • نام مدل ( gemini-1.5-flash-001 ).

اکنون که مدل زبان آماده است، می‌توانید متد generate() فراخوانی کنید و درخواست، سؤال یا دستورالعمل‌های خود را برای ارسال به LLM ارسال کنید. در اینجا، شما یک سوال ساده در مورد اینکه چه چیزی آسمان را آبی می‌کند، می‌پرسید.

برای امتحان سوالات یا کارهای مختلف، می توانید این درخواست را تغییر دهید.

نمونه را در پوشه ریشه کد منبع اجرا کنید:

./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.

تبریک می گویم، شما اولین تماس خود را با جمینی برقرار کردید!

پاسخ جریانی

آیا متوجه شدید که پس از چند ثانیه پاسخ یکباره داده شد؟ همچنین به لطف نوع پاسخ جریانی، امکان دریافت پاسخ تدریجی وجود دارد. پاسخ جریان، مدل پاسخ را تکه تکه برمی گرداند، همانطور که در دسترس می شود.

در این لبه کد، ما به پاسخ غیر جریانی می مانیم، اما بیایید نگاهی به پاسخ جریان بیاندازیم تا ببینیم چگونه می توان آن را انجام داد.

در 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 بار تماس را دوباره امتحان کند.

تا به حال یک سوال از جمینی پرسیده اید، اما می توانید یک مکالمه چند نوبتی نیز داشته باشید. این چیزی است که در بخش بعدی بررسی خواهید کرد.

5. با جمینی چت کنید

در مرحله قبل یک سوال پرسیدید. اکنون زمان آن رسیده است که یک مکالمه واقعی بین کاربر و LLM داشته باشیم. هر پرسش و پاسخی می تواند بر اساس پرسش های قبلی باشد تا یک بحث واقعی را شکل دهد.

نگاهی به 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 - کلاسی که مدل چت و حافظه چت را به هم متصل می کند

در روش اصلی، شما قصد دارید مدل، حافظه چت و سرویس هوش مصنوعی را تنظیم کنید. مدل طبق معمول با اطلاعات پروژه، مکان و نام مدل پیکربندی می شود.

برای حافظه چت، از سازنده MessageWindowChatMemory برای ایجاد حافظه ای استفاده می کنیم که 20 پیام آخر رد و بدل شده را نگه می دارد. این یک پنجره کشویی روی مکالمه است که زمینه آن به صورت محلی در کلاینت کلاس جاوا ما نگهداری می شود.

سپس AI service را ایجاد می کنید که مدل چت را با حافظه چت پیوند می دهد.

توجه کنید که چگونه سرویس هوش مصنوعی از یک رابط ConversationService سفارشی که ما تعریف کرده‌ایم استفاده می‌کند، که LangChain4j پیاده‌سازی می‌کند، و یک عبارت String را می‌گیرد و یک پاسخ String را برمی‌گرداند.

اکنون زمان گفتگو با جمینی فرا رسیده است. ابتدا یک تبریک ساده ارسال می شود، سپس یک سوال در مورد برج ایفل برای اینکه بدانید در کدام کشور می توان آن را پیدا کرد. توجه داشته باشید که جمله آخر مربوط به پاسخ سوال اول است، زیرا شما تعجب می کنید که در کشوری که برج ایفل در آن قرار دارد چند نفر هستند، بدون اینکه صریحاً به کشوری که در پاسخ قبلی گفته شد اشاره کنید. این نشان می دهد که سوالات و پاسخ های گذشته با هر اعلان ارسال می شود.

نمونه را اجرا کنید:

./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.

می توانید سؤالات تک نوبتی بپرسید یا با جمینی مکالمات چند نوبتی داشته باشید، اما تاکنون، ورودی فقط متن بوده است. در مورد تصاویر چطور؟ بیایید در مرحله بعدی تصاویر را بررسی کنیم.

6. چند وجهی با جمینی

جمینی یک مدل چندوجهی است. نه تنها متن را به عنوان ورودی می پذیرد، بلکه تصاویر یا حتی فیلم ها را نیز به عنوان ورودی می پذیرد. در این بخش، یک مورد استفاده برای ترکیب متن و تصاویر را مشاهده خواهید کرد.

فکر می کنید جمینی این گربه را می شناسد؟

af00516493ec9ade.png

تصویر گربه در برف برگرفته از ویکی پدیا 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 باشد. این چند وجهی در بازی است: ترکیب متن و تصاویر. مدل یک 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. استخراج اطلاعات ساختاریافته از متن بدون ساختار

موقعیت‌های زیادی وجود دارد که اطلاعات مهم در اسناد گزارش، ایمیل‌ها یا سایر متون طولانی به روشی بدون ساختار داده می‌شود. در حالت ایده آل، شما دوست دارید بتوانید جزئیات کلیدی موجود در متن بدون ساختار را به شکل اشیاء ساختاریافته استخراج کنید. بیایید ببینیم چگونه می توانید این کار را انجام دهید.

فرض کنید می خواهید نام و سن یک فرد را با توجه به بیوگرافی یا شرحی از آن شخص استخراج کنید. می‌توانید به LLM دستور دهید تا 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 شما با اشیاء تایپ شده قوی کار می کنید. شما مستقیماً با LLM در تعامل نیستید. در عوض، شما با کلاس‌های مشخصی کار می‌کنید، مانند رکورد Person برای نمایش اطلاعات شخصی استخراج‌شده، و یک شی PersonExtractor با متد extractPerson() دارید که یک نمونه Person را برمی‌گرداند. مفهوم LLM انتزاع شده است، و به عنوان یک توسعه دهنده جاوا، شما فقط کلاس ها و اشیاء معمولی را دستکاری می کنید.

8. دستورات ساختاری با الگوهای سریع

وقتی با یک LLM با استفاده از مجموعه‌ای از دستورالعمل‌ها یا سوالات متداول تعامل می‌کنید، بخشی از آن فرمان وجود دارد که هرگز تغییر نمی‌کند، در حالی که بخش‌های دیگر حاوی داده‌ها هستند. به عنوان مثال، اگر می‌خواهید دستور پخت تهیه کنید، ممکن است از دستوری مانند «شما یک سرآشپز با استعداد هستید، لطفاً یک دستور غذا با مواد زیر ایجاد کنید: ...» استفاده کنید و سپس مواد را به انتهای آن اضافه کنید. آن متن این همان چیزی است که قالب‌های سریع برای آن هستند - شبیه رشته‌های درون‌یابی در زبان‌های برنامه‌نویسی. یک الگوی سریع حاوی متغیرهایی است که می توانید آنها را با داده های مناسب برای یک تماس خاص با LLM جایگزین کنید.

به طور دقیق تر، اجازه دهید 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 را با سطح خلاقیت بالا با دمای بالا و همچنین مقادیر بالای topP و topK پیکربندی می‌کنید. سپس یک PromptTemplate با متد from() استاتیک آن، با عبور دادن رشته اعلان ما ایجاد می‌کنید و از متغیرهای مکان‌نمای دوتایی curly-braces استفاده می‌کنید: و .

شما اعلان نهایی را با فراخوانی apply() ایجاد می‌کنید که نقشه‌ای از جفت‌های کلید/مقدار را می‌گیرد که نشان‌دهنده نام مکان‌نما و مقدار رشته برای جایگزینی آن است.

در نهایت، شما متد generate() مدل Gemini را با ایجاد یک پیام کاربر از آن prompt با دستور 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 مشاهده کنید.

الگوهای اعلان راه خوبی برای داشتن دستورالعمل‌های قابل استفاده مجدد و پارامتری برای تماس‌های LLM هستند. می‌توانید داده‌ها را ارسال کنید و درخواست‌ها را برای مقادیر مختلف ارائه شده توسط کاربران خود سفارشی کنید.

9. طبقه بندی متن با چند شات تحریک

LLM ها در طبقه بندی متن به دسته های مختلف بسیار خوب هستند. شما می توانید با ارائه چند نمونه از متون و دسته بندی های مرتبط با آنها به یک LLM در این کار کمک کنید. این رویکرد اغلب به نام چند shot prompting نامیده می شود.

نگاهی به 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 است.

سپس، با آموزش مدل در مورد چند نمونه ورودی و خروجی، یک الگوی اعلان قابل استفاده مجدد با تکنیک اعلان چند شات ایجاد می کنید. این همچنین به مدل کمک می کند تا خروجی واقعی را دنبال کند. جمینی با یک جمله کامل پاسخ نمی دهد، در عوض، به او دستور داده شده که فقط با یک کلمه پاسخ دهد.

متغیرها را با متد apply() اعمال می‌کنید تا متغیر را با پارامتر واقعی ( "I love strawberries" ) جایگزین کنید و آن الگو را با استفاده از toUserMessage() به یک پیام کاربر تبدیل کنید.

نمونه را اجرا کنید:

./gradlew run -q -DjavaMainClass=gemini.workshop.TextClassification

شما باید یک کلمه را ببینید:

POSITIVE

به نظر می رسد دوست داشتن توت فرنگی یک احساس مثبت است!

10. بازیابی نسل افزوده

LLM ها بر روی مقدار زیادی متن آموزش می بینند. با این حال، دانش آنها فقط اطلاعاتی را پوشش می دهد که در طول آموزش دیده است. اگر اطلاعات جدیدی پس از پایان تاریخ آموزش مدل منتشر شود، این جزئیات برای مدل در دسترس نخواهد بود. بنابراین، مدل قادر نخواهد بود به سؤالات مربوط به اطلاعاتی که ندیده پاسخ دهد.

به همین دلیل است که رویکردهایی مانند Retrieval Augmented Generation (RAG) به ارائه اطلاعات اضافی کمک می کند که یک LLM ممکن است برای برآورده کردن درخواست های کاربرانش نیاز داشته باشد، برای پاسخ دادن به اطلاعاتی که ممکن است جدیدتر باشد یا در مورد اطلاعات خصوصی که در زمان آموزش در دسترس نیست. .

بیایید به گفتگوها برگردیم. این بار می توانید در مورد مدارک خود سوال بپرسید. شما یک ربات چت خواهید ساخت که قادر است اطلاعات مربوطه را از یک پایگاه داده حاوی اسناد شما که به قطعات کوچکتر تقسیم شده است ("تکه") بازیابی کند و این اطلاعات به جای تکیه بر دانش موجود در آموزش آن

در RAG دو مرحله وجود دارد:

  1. مرحله جذب - اسناد در حافظه بارگذاری می‌شوند، به قطعات کوچک‌تر تقسیم می‌شوند و جاسازی‌های برداری (نمایش برداری چندبعدی بالا از تکه‌ها) محاسبه و در یک پایگاه داده برداری ذخیره می‌شوند که قادر به انجام جستجوهای معنایی است. این مرحله جذب معمولاً یک بار انجام می شود، زمانی که اسناد جدید باید به مجموعه سند اضافه شود.

cd07d33d20ffa1c8.png

  1. مرحله پرس و جو - اکنون کاربران می توانند در مورد اسناد سوال بپرسند. سوال نیز به یک بردار تبدیل می شود و با تمام بردارهای دیگر در پایگاه داده مقایسه می شود. مشابه ترین بردارها معمولاً از نظر معنایی مرتبط هستند و توسط پایگاه داده برداری برگردانده می شوند. سپس، به LLM زمینه گفتگو داده می شود، تکه هایی از متن که مطابق با بردارهای بازگردانده شده توسط پایگاه داده است، و از آن خواسته می شود که با مشاهده آن تکه ها، پاسخ خود را پایه گذاری کند.

a1d2e2deb83c6d27.png

مدارک خود را آماده کنید

برای این نسخه ی نمایشی جدید، سؤالاتی در مورد مقاله تحقیقاتی «توجه تنها چیزی است که نیاز دارید» خواهید پرسید. این معماری شبکه عصبی ترانسفورماتور را توصیف می‌کند که توسط گوگل پیشگام شده است، که اینگونه است که امروزه همه مدل‌های زبان بزرگ مدرن پیاده‌سازی می‌شوند.

مقاله قبلاً در آدرس-at-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() مراقب انجام ingestion خواهد بود.

اکنون، مرحله اول به پایان رسیده است، سند به تکه های متنی با جاسازی های برداری مرتبط تبدیل شده و در پایگاه داده برداری ذخیره می شود.

سوال پرسیدن

وقت آن است که برای پرسیدن سؤال آماده شوید! یک مدل چت برای شروع مکالمه ایجاد کنید:

ChatLanguageModel model = VertexAiGeminiChatModel.builder()
        .project(System.getenv("PROJECT_ID"))
        .location(System.getenv("LOCATION"))
        .modelName("gemini-1.5-flash-001")
        .maxOutputTokens(1000)
        .build();

همچنین برای پیوند دادن پایگاه داده برداری (در متغیر embeddingStore ) با مدل embedding به یک کلاس retriever نیاز دارید. وظیفه آن این است که از پایگاه داده برداری با محاسبه یک جاسازی برداری برای پرس و جو کاربر، پرس و جو کند تا بردارهای مشابه را در پایگاه داده پیدا کند:

EmbeddingStoreContentRetriever retriever =
    new EmbeddingStoreContentRetriever(embeddingStore, embeddingModel);

خارج از روش اصلی ، رابط کاربری ایجاد کنید که یک دستیار متخصص LLM را نشان دهد ، این رابط کاربری است که کلاس AiServices برای شما برای تعامل با مدل پیاده سازی می کند:

interface LlmExpert {
    String ask(String question);
}

در این مرحله ، می توانید یک سرویس AI جدید را پیکربندی کنید:

LlmExpert expert = AiServices.builder(LlmExpert.class)
    .chatLanguageModel(model)
    .chatMemory(MessageWindowChatMemory.withMaxMessages(10))
    .contentRetriever(retriever)
    .build();

این سرویس به هم متصل می شود:

  • مدل زبان چت که قبلاً پیکربندی کرده اید.
  • یک حافظه چت برای پیگیری مکالمه.
  • Retriever یک وکتور تعبیه کننده را با بردارهای موجود در پایگاه داده مقایسه می کند.
  • یک الگوی سریع به صراحت می گوید که مدل گپ باید با استفاده از پاسخ خود بر روی اطلاعات ارائه شده پاسخ دهد (یعنی گزیده های مربوط به اسناد و مدارک که تعبیه وکتور آن شبیه به بردار سوال کاربر است).
.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 DIRECTORY:

نمونه را اجرا کنید:

./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. تماس عملکرد

همچنین موقعیت هایی وجود دارد که دوست دارید یک LLM به سیستم های خارجی دسترسی داشته باشد ، مانند یک API وب از راه دور که اطلاعات را بازیابی می کند یا یک عمل یا خدماتی را انجام می دهد که نوعی محاسبات را انجام می دهد. به عنوان مثال:

API های وب از راه دور:

  • سفارشات مشتری را پیگیری و به روز کنید.
  • بلیط را در یک ردیاب شماره پیدا یا ایجاد کنید.
  • داده های زمان واقعی مانند نقل قول های سهام یا اندازه گیری سنسور IoT را بدست آورید.
  • ارسال یک ایمیل.

ابزارهای محاسباتی:

  • یک ماشین حساب برای مشکلات ریاضی پیشرفته تر.
  • تفسیر کد برای اجرای کد هنگامی که LLM ها به منطق استدلال نیاز دارند.
  • درخواست های زبان طبیعی را به نمایش داده های SQL تبدیل کنید تا یک LLM بتواند از یک پایگاه داده پرس و جو کند.

فراخوانی عملکرد این امکان است که مدل بتواند یک یا چند تماس عملکردی را از طرف آن درخواست کند ، بنابراین می تواند با داده های تازه تر به سرعت کاربر پاسخ دهد.

با توجه به یک درخواست خاص از یک کاربر ، و دانش عملکردهای موجود که می تواند مربوط به آن زمینه باشد ، یک LLM می تواند با یک درخواست تماس عملکرد پاسخ دهد. برنامه ادغام LLM می تواند سپس عملکرد را فراخوانی کند و سپس با پاسخ به LLM پاسخ دهد و LLM سپس با پاسخ با یک پاسخ متنی دوباره تفسیر می کند.

چهار مرحله از تماس با عملکرد

بیایید نگاهی به نمونه ای از تماس با عملکرد داشته باشیم: دریافت اطلاعات در مورد پیش بینی آب و هوا.

اگر از Gemini یا هر LLM دیگری در مورد آب و هوا در پاریس سؤال کنید ، آنها با گفتن اینکه هیچ اطلاعاتی در مورد پیش بینی آب و هوا ندارد ، پاسخ می دهند. اگر می خواهید LLM به داده های آب و هوا در زمان واقعی باشد ، باید برخی از کارکردهایی را که می تواند استفاده کند تعریف کنید.

به نمودار زیر نگاهی بیندازید:

31E0C2ABA5E6F21C.PNG

1⃣ اول ، کاربر در مورد آب و هوا در پاریس سؤال می کند. برنامه Chatbot می داند که یک یا چند کارکرد وجود دارد که در اختیار آن است تا به LLM کمک کند تا پرس و جو را انجام دهد. chatbot هر دو سریع سریع و همچنین لیست توابعی را که می توان نامیده می شود ارسال می کند. در اینجا ، تابعی به نام getWeather() که یک پارامتر رشته برای مکان را می گیرد.

8863be53a73c4a70.png

از آنجا که LLM در مورد پیش بینی آب و هوا نمی داند ، به جای پاسخ دادن از طریق متن ، درخواست اجرای عملکرد را ارسال می کند. Chatbot باید عملکرد getWeather() را با "Paris" به عنوان پارامتر موقعیت مکانی فراخوانی کند.

D1367CC69C07B14D.png

2⃣ chatbot از طرف LLM این عملکرد را فراخوانی می کند ، پاسخ عملکرد را بازیابی می کند. در اینجا ، ما تصور می کنیم که پاسخ {"forecast": "sunny"} .

73A5F2ED19F47D8.PNG

3⃣ برنامه chatbot پاسخ JSON را به LLM ارسال می کند.

20832cb1ee6fbfeb.png

4⃣ LLM به پاسخ JSON نگاه می کند ، آن اطلاعات را تفسیر می کند و در نهایت با متن پاسخ می دهد که هوا در پاریس آفتابی است.

هر مرحله به عنوان کد

ابتدا ، شما مدل جمینی را طبق معمول پیکربندی می کنید:

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();

نام عملکرد تعریف شده است ، و همچنین نام و نوع پارامتر ، اما توجه داشته باشید که به عملکرد و پارامترها توضیحات داده می شود. توضیحات بسیار مهم هستند و به LLM کمک می کنند تا واقعاً بفهمد چه عملکردی می تواند انجام دهد ، و بنابراین قضاوت کنید که آیا این عملکرد باید در متن مکالمه فراخوانی شود.

بیایید با ارسال سؤال اولیه در مورد آب و هوا در پاریس ، مرحله شماره 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. در این مرحله ، ما می دانیم که LLM دوست دارد با چه عملکردی تماس بگیریم. در کد ، ما در حال تماس واقعی با یک API خارجی نیستیم ، ما فقط یک پیش بینی آب و هوا فرضی را مستقیماً برمی گردانیم:

// 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 ، LLM در مورد نتیجه اجرای عملکرد می آموزد ، و سپس می تواند یک پاسخ متنی را سنتز کند:

// 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?"));
}

طبق معمول ، شما مدل چت جمینی را پیکربندی می کنید. سپس سرویس پیش بینی آب و هوا خود را که شامل "عملکردی" است که مدل از ما درخواست می کند تماس بگیرید.

اکنون ، شما دوباره از کلاس AiServices استفاده می کنید تا مدل گپ ، حافظه گپ و ابزار را به هم وصل کنید (یعنی سرویس پیش بینی آب و هوا با عملکرد آن). AiServices شیئی را که رابط کاربری WeatherAssistant شما را تعریف کرده است ، برمی گرداند. تنها چیزی که باقی مانده است تماس با روش chat() آن دستیار است. هنگام استناد به آن ، فقط پاسخ های متن را مشاهده خواهید کرد ، اما درخواست تماس عملکرد و پاسخ های تماس عملکرد از توسعه دهنده قابل مشاهده نخواهد بود و این درخواست ها به صورت خودکار و شفاف انجام می شوند. اگر جمینی فکر کند که یک تابع باید فراخوانی شود ، با درخواست تماس عملکرد پاسخ می دهد ، و 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;
}

سپس می توانید تمام این کارکردها و یک کلاس های Multitools را با هم ترکیب کنید و سؤالاتی مانند "10 ٪ قیمت سهام AAPL از USD به یورو تبدیل شده است؟"

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.

به سمت نمایندگان

فراخوانی عملکرد یک مکانیسم فرمت عالی برای مدلهای بزرگ زبان مانند جمینی است. این ما را قادر می سازد تا سیستمهای پیچیده تری بسازیم که اغلب به آنها "مأمور" یا "دستیاران هوش مصنوعی" گفته می شود. این عوامل می توانند از طریق API های خارجی و با خدماتی که می توانند در محیط خارجی عوارض جانبی داشته باشند با دنیای خارجی ارتباط برقرار کنند (مانند ارسال ایمیل ، ایجاد بلیط و غیره)

هنگام ایجاد چنین عوامل قدرتمندی ، باید این کار را با مسئولیت پذیری انجام دهید. شما باید قبل از انجام اقدامات خودکار ، یک حلقه انسان را در نظر بگیرید. مهم است که هنگام طراحی عوامل دارای LLM که با دنیای خارجی تعامل دارند ، ایمنی را در نظر داشته باشید.

13. اجرای Gemma با Ollama و TestContainers

تاکنون ، ما از Gemini استفاده کرده ایم اما Gemma ، مدل خواهر کوچک آن نیز وجود دارد.

Gemma خانواده ای از مدل های سبک و مدرن و مدرن است که از همان تحقیق و فناوری ساخته شده برای ایجاد مدلهای جمینی ساخته شده است. Gemma در دو نوع Gemma1 و Gemma2 با اندازه های مختلف در دسترس است. Gemma1 در دو اندازه موجود است: 2B و 7b. Gemma2 در دو اندازه موجود است: 9b و 27b. وزن آنها آزادانه در دسترس است ، و اندازه های کوچک آنها به این معنی است که می توانید آن را به تنهایی ، حتی در لپ تاپ یا پوسته ابر خود اجرا کنید.

چگونه Gemma را اجرا می کنید؟

روش های زیادی برای اجرای Gemma وجود دارد: در ابر ، از طریق Vertex AI با کلیک یک دکمه یا GKE با برخی از GPU ها ، اما می توانید آن را به صورت محلی اجرا کنید.

یکی از گزینه های خوب برای اجرای Gemma به صورت محلی با Ollama است ، ابزاری که به شما امکان می دهد مدل های کوچکی مانند Llama 2 ، Mistral و بسیاری دیگر را در دستگاه محلی خود اجرا کنید. این شبیه به Docker است اما برای LLMS.

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 خاص وجود دارد که می توانید از آن استفاده کنید!

در اینجا کل تصویر وجود دارد:

2382C05A48708DFD.PNG

پیاده سازی

بیایید نگاهی به GemmaWithOllamaContainer.java ، قطعه به قطعه داشته باشیم.

ابتدا باید یک ظرف اولیاما مشتق شده را ایجاد کنید که در مدل 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 را در پوسته ابر اجرا می کنید!

14. تبریک می گویم

تبریک می گویم ، شما اولین برنامه چت AI تولیدی خود را در جاوا با استفاده از Langchain4j و Gemini API ساخته اید! شما در این راه متوجه شده اید که مدل های بزرگ زبان چند حالته بسیار قدرتمند و قادر به انجام وظایف مختلف مانند سؤال/پاسخ دادن هستند ، حتی در مستندات خود ، استخراج داده ها ، تعامل با API های خارجی و موارد دیگر.

بعدش چی؟

نوبت شماست که برنامه های خود را با ادغام های قدرتمند LLM تقویت کنید!

در ادامه مطلب

اسناد مرجع