1. Pengantar
Codelab ini berfokus pada Model Bahasa Besar (LLM) Gemini yang dihosting di Vertex AI di Google Cloud. Vertex AI adalah platform yang mencakup semua produk, layanan, dan model machine learning di Google Cloud.
Anda akan menggunakan Java untuk berinteraksi dengan Gemini API menggunakan framework LangChain4j. Anda akan mempelajari contoh konkret cara memanfaatkan LLM untuk menjawab pertanyaan, pembuatan ide, ekstraksi entitas dan konten terstruktur, pembuatan versi yang ditingkatkan saat mengambil, dan panggilan fungsi.
Apa itu AI Generatif?
AI generatif mengacu pada penggunaan kecerdasan buatan untuk membuat konten baru, seperti teks, gambar, musik, audio, dan video.
AI generatif didukung oleh model bahasa besar (LLM) yang dapat melakukan beberapa tugas sekaligus dan melakukan tugas siap pakai seperti perangkuman, tanya jawab, klasifikasi, dan sebagainya. Dengan pelatihan minimal, model dasar dapat diadaptasikan untuk kasus penggunaan tertarget dengan data contoh yang sangat sedikit.
Bagaimana cara kerja AI Generatif?
AI generatif bekerja menggunakan model Machine Learning (ML) untuk mempelajari pola dan hubungan dalam set data konten buatan manusia. Sistem ini kemudian menggunakan pola-pola yang telah dipelajarinya untuk membuat konten baru.
Cara yang paling umum digunakan untuk melatih model AI generatif adalah menggunakan supervised learning. Model ini diberi sekumpulan konten buatan manusia dan label yang sesuai. AI generatif kemudian belajar membuat konten yang serupa dengan konten buatan manusia.
Apa saja aplikasi AI Generatif yang umum?
AI generatif dapat digunakan untuk:
- Tingkatkan interaksi pelanggan melalui chat dan pengalaman penelusuran yang ditingkatkan kualitasnya.
- Eksplorasi sejumlah besar data tidak terstruktur melalui antarmuka percakapan dan perangkuman.
- Bantu tugas berulang seperti membalas permintaan proposal, melokalkan konten pemasaran dalam berbagai bahasa dan memeriksa kontrak pelanggan terkait kepatuhan, dan lainnya.
Penawaran AI Generatif apa yang dimiliki Google Cloud?
Dengan Vertex AI, Anda dapat berinteraksi dengan model dasar, menyesuaikan model tersebut, dan menyematkannya ke aplikasi Anda dengan sedikit atau tanpa keahlian ML. Anda dapat mengakses model dasar di Model Garden, menyesuaikan berbagai model melalui UI yang sederhana di Vertex AI Studio, atau menggunakan model dalam notebook data science.
Vertex AI Search and Conversation menawarkan cara tercepat bagi para developer untuk membangun mesin telusur dan chatbot yang didukung teknologi AI generatif.
Dengan dukungan Gemini, Gemini untuk Google Cloud adalah kolaborator dengan teknologi AI yang tersedia di Google Cloud dan IDE untuk membantu Anda menyelesaikan lebih banyak hal dengan lebih cepat. Gemini Code Assist menyediakan penyelesaian kode, pembuatan kode, penjelasan kode, dan memungkinkan Anda melakukan chat dengannya untuk mengajukan pertanyaan teknis.
Apa itu Gemini?
Gemini adalah rangkaian model AI generatif yang dikembangkan oleh Google DeepMind dan dirancang untuk kasus penggunaan multimodal. Multimodal artinya dapat memproses dan menghasilkan berbagai jenis konten seperti teks, kode, gambar, dan audio.
Gemini tersedia dalam berbagai variasi dan ukuran:
- Gemini Ultra: Versi terbesar dan paling canggih untuk tugas-tugas kompleks.
- Gemini Flash: Tercepat dan paling hemat biaya, dioptimalkan untuk tugas bervolume tinggi.
- Gemini Pro: Berukuran sedang, dioptimalkan untuk penskalaan di berbagai tugas.
- Gemini Nano: Yang paling efisien dan dirancang untuk tugas di perangkat.
Fitur Utama:
- Multimodalitas: Kemampuan Gemini untuk memahami dan menangani berbagai format informasi merupakan langkah yang signifikan melampaui model bahasa tradisional yang hanya menampilkan teks.
- Performa: Gemini Ultra mengungguli performa tercanggih saat ini dalam banyak tolok ukur dan merupakan model pertama yang mengungguli pakar manusia dalam tolok ukur MMLU (Massive Multitask Language Understanding).
- Fleksibilitas: Ukuran Gemini yang berbeda membuatnya dapat disesuaikan untuk berbagai kasus penggunaan, mulai dari riset skala besar hingga deployment di perangkat seluler.
Bagaimana cara berinteraksi dengan Gemini di Vertex AI dari Java?
Ada dua opsi:
- Library Vertex AI Java API untuk Gemini resmi.
- LangChain4j.
Dalam codelab ini, Anda akan menggunakan framework LangChain4j.
Apa itu framework LangChain4j?
Framework LangChain4j adalah library open source untuk mengintegrasikan LLM di aplikasi Java, dengan mengorkestrasi berbagai komponen, seperti LLM itu sendiri, serta alat lain seperti database vektor (untuk penelusuran semantik), loader dokumen, dan pemisah (untuk menganalisis dokumen dan belajar darinya), parser output, dan banyak lagi.
Project ini terinspirasi oleh project Python LangChain, tetapi bertujuan melayani developer Java.
Yang akan Anda pelajari
- Cara menyiapkan project Java untuk menggunakan Gemini dan LangChain4j
- Cara mengirim perintah pertama Anda ke Gemini secara terprogram
- Cara mengalirkan respons dari Gemini
- Cara membuat percakapan antara pengguna dan Gemini
- Cara menggunakan Gemini dalam konteks multimodal dengan mengirim teks dan gambar
- Cara mengekstrak informasi terstruktur yang berguna dari konten yang tidak terstruktur
- Cara memanipulasi template perintah
- Cara melakukan klasifikasi teks seperti analisis sentimen
- Cara melakukan chat dengan dokumen Anda sendiri (Pengambilan Augmented Generation)
- Cara memperluas chatbot Anda dengan panggilan fungsi
- Cara menggunakan Gemma secara lokal dengan Ollama dan TestContainers
Yang Anda butuhkan
- Pengetahuan tentang bahasa pemrograman Java
- Project Google Cloud
- Browser, seperti Chrome atau Firefox
2. Penyiapan dan persyaratan
Penyiapan lingkungan mandiri
- Login ke Google Cloud Console dan buat project baru atau gunakan kembali project yang sudah ada. Jika belum memiliki akun Gmail atau Google Workspace, Anda harus membuatnya.
- Project name adalah nama tampilan untuk peserta project ini. String ini adalah string karakter yang tidak digunakan oleh Google API. Anda dapat memperbaruinya kapan saja.
- Project ID bersifat unik di semua project Google Cloud dan tidak dapat diubah (tidak dapat diubah setelah ditetapkan). Cloud Console otomatis membuat string unik; biasanya Anda tidak mementingkan kata-katanya. Di sebagian besar codelab, Anda harus merujuk Project ID-nya (umumnya diidentifikasi sebagai
PROJECT_ID
). Jika tidak suka dengan ID yang dibuat, Anda dapat membuat ID acak lainnya. Atau, Anda dapat mencobanya sendiri, dan lihat apakah ID tersebut tersedia. ID tidak dapat diubah setelah langkah ini dan tersedia selama durasi project. - Sebagai informasi, ada nilai ketiga, Project Number, yang digunakan oleh beberapa API. Pelajari lebih lanjut ketiga nilai ini di dokumentasi.
- Selanjutnya, Anda harus mengaktifkan penagihan di Konsol Cloud untuk menggunakan resource/API Cloud. Menjalankan operasi dalam codelab ini tidak akan memakan banyak biaya, bahkan mungkin tidak sama sekali. Guna mematikan resource agar tidak menimbulkan penagihan di luar tutorial ini, Anda dapat menghapus resource yang dibuat atau menghapus project-nya. Pengguna baru Google Cloud memenuhi syarat untuk mengikuti program Uji Coba Gratis senilai $300 USD.
Mulai Cloud Shell
Meskipun Google Cloud dapat dioperasikan secara jarak jauh dari laptop Anda, dalam codelab ini Anda akan menggunakan Cloud Shell, yakni lingkungan command line yang berjalan di Cloud.
Mengaktifkan Cloud Shell
- Dari Cloud Console, klik Aktifkan Cloud Shell .
Jika ini pertama kalinya Anda memulai Cloud Shell, Anda akan melihat layar perantara yang menjelaskan apa itu Cloud Shell. Jika Anda melihat layar perantara, klik Lanjutkan.
Perlu waktu beberapa saat untuk penyediaan dan terhubung ke Cloud Shell.
Mesin virtual ini dimuat dengan semua alat pengembangan yang diperlukan. Layanan ini menawarkan direktori beranda tetap sebesar 5 GB dan beroperasi di Google Cloud, sehingga sangat meningkatkan performa dan autentikasi jaringan. Sebagian besar pekerjaan Anda dalam codelab ini dapat dilakukan dengan browser.
Setelah terhubung ke Cloud Shell, Anda akan melihat bahwa Anda telah diautentikasi dan project sudah ditetapkan ke project ID Anda.
- Jalankan perintah berikut di Cloud Shell untuk mengonfirmasi bahwa Anda telah diautentikasi:
gcloud auth list
Output perintah
Credentialed Accounts ACTIVE ACCOUNT * <my_account>@<my_domain.com> To set the active account, run: $ gcloud config set account `ACCOUNT`
- Jalankan perintah berikut di Cloud Shell untuk mengonfirmasi bahwa perintah gcloud mengetahui project Anda:
gcloud config list project
Output perintah
[core] project = <PROJECT_ID>
Jika tidak, Anda dapat menyetelnya dengan perintah ini:
gcloud config set project <PROJECT_ID>
Output perintah
Updated property [core/project].
3. Menyiapkan lingkungan pengembangan Anda
Dalam codelab ini, Anda akan menggunakan terminal Cloud Shell dan editor Cloud Shell untuk mengembangkan program Java.
Mengaktifkan Vertex AI API
Di konsol Google Cloud, pastikan nama project Anda ditampilkan di bagian atas Konsol Google Cloud. Jika tidak, klik Select a project untuk membuka Project Selector, lalu pilih project yang Anda inginkan.
Anda dapat mengaktifkan Vertex AI API dari bagian Vertex AI di Konsol Google Cloud atau dari terminal Cloud Shell.
Untuk mengaktifkan dari konsol Google Cloud, pertama, buka bagian Vertex AI di menu Konsol Google Cloud:
Klik Enable All Recommended APIs di dasbor Vertex AI.
Tindakan ini akan mengaktifkan beberapa API, tetapi yang paling penting untuk codelab adalah aiplatform.googleapis.com
.
Atau, Anda juga dapat mengaktifkan API ini dari terminal Cloud Shell dengan perintah berikut:
gcloud services enable aiplatform.googleapis.com
Meng-clone repositori GitHub
Di terminal Cloud Shell, clone repositori untuk codelab ini:
git clone https://github.com/glaforge/gemini-workshop-for-java-developers.git
Untuk memeriksa apakah project siap dijalankan, Anda dapat mencoba menjalankan aplikasi "Hello World" (Halo Dunia) program ini.
Pastikan Anda berada di folder tingkat atas:
cd gemini-workshop-for-java-developers/
Buat wrapper Gradle:
gradle wrapper
Jalankan dengan gradlew
:
./gradlew run
Anda akan melihat output berikut:
.. > Task :app:run Hello World!
Membuka dan menyiapkan Cloud Editor
Buka kode dengan Cloud Code Editor dari Cloud Shell:
Di Cloud Code Editor, buka folder sumber codelab dengan memilih File
-> Open Folder
dan arahkan kursor ke folder sumber codelab (misalnya, /home/username/gemini-workshop-for-java-developers/
).
Menginstal Gradle untuk Java
Agar editor kode cloud berfungsi dengan baik dengan Gradle, instal ekstensi Gradle untuk Java.
Pertama, buka bagian Java Projects dan tekan tanda plus:
Pilih Gradle for Java
:
Pilih versi Install Pre-Release
:
Setelah diinstal, Anda akan melihat tombol Disable
dan Uninstall
:
Terakhir, bersihkan ruang kerja untuk menerapkan setelan baru:
Tindakan ini akan meminta Anda memuat ulang dan menghapus workshop. Lanjutkan dan pilih Reload and delete
:
Jika membuka salah satu file, misalnya App.java, Anda kini akan melihat editor berfungsi dengan benar dengan penyorotan sintaksis:
Anda sekarang siap untuk menjalankan beberapa contoh terhadap Gemini.
Menyiapkan variabel lingkungan
Buka terminal baru di Cloud Code Editor dengan memilih Terminal
-> New Terminal
. Siapkan dua variabel lingkungan yang diperlukan untuk menjalankan contoh kode:
- PROJECT_ID — ID project Google Cloud Anda
- LOCATION — Region tempat model Gemini di-deploy
Ekspor variabel sebagai berikut:
export PROJECT_ID=$(gcloud config get-value project) export LOCATION=us-central1
4. Panggilan pertama ke model Gemini
Setelah project disiapkan dengan benar, saatnya untuk memanggil Gemini API.
Lihat QA.java
di direktori 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?"));
}
}
Pada contoh pertama ini, Anda harus mengimpor class VertexAiGeminiChatModel
, yang menerapkan antarmuka ChatModel
.
Dalam metode main
, Anda mengonfigurasi model bahasa chat menggunakan builder untuk VertexAiGeminiChatModel
dan menentukan:
- Project
- Lokasi
- Nama model (
gemini-1.5-flash-001
).
Setelah model bahasa siap, Anda dapat memanggil metode generate()
dan meneruskan perintah, pertanyaan, atau petunjuk Anda untuk dikirim ke LLM. Di sini, Anda mengajukan pertanyaan sederhana tentang apa yang membuat langit berwarna biru.
Jangan ragu untuk mengubah {i>prompt<i} ini untuk mencoba pertanyaan atau tugas yang berbeda.
Jalankan contoh di folder root kode sumber:
./gradlew run -q -DjavaMainClass=gemini.workshop.QA
Anda akan melihat output yang mirip dengan output ini:
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.
Selamat, Anda telah melakukan panggilan pertama ke Gemini.
Respons streaming
Apakah Anda memperhatikan bahwa respons diberikan dalam satu sesi, setelah beberapa detik? Berkat varian respons streaming, Anda juga bisa mendapatkan respons secara bertahap. Respons streaming, model akan menampilkan respons sepotong demi sepotong, saat tersedia.
Dalam codelab ini, kita akan tetap menggunakan respons non-streaming, tetapi mari kita lihat respons streaming untuk melihat cara melakukannya.
Di StreamQA.java
di direktori app/src/main/java/gemini/workshop
, Anda dapat melihat cara kerja respons streaming:
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();
}
});
}
}
Kali ini, kita akan mengimpor varian class streaming VertexAiGeminiStreamingChatModel
yang menerapkan antarmuka StreamingChatLanguageModel
. Anda juga memerlukan StreamingResponseHandler
.
Kali ini, tanda tangan metode generate()
sedikit berbeda. Sebagai ganti menampilkan string, jenis nilai yang ditampilkan menjadi batal. Selain prompt, Anda harus meneruskan pengendali respons streaming. Di sini, Anda akan menerapkan antarmuka dengan membuat class dalam anonim, dengan dua metode onNext(String text)
dan onError(Throwable error)
. Yang pertama dipanggil setiap kali bagian baru dari respons tersedia, sementara yang kedua hanya dipanggil jika terjadi error.
Jalankan:
./gradlew run -q -DjavaMainClass=gemini.workshop.StreamQA
Anda akan mendapatkan jawaban yang serupa dengan class sebelumnya, tetapi kali ini, Anda akan melihat bahwa jawabannya muncul secara bertahap di shell Anda, bukan menunggu tampilan jawaban lengkap.
Konfigurasi tambahan
Untuk konfigurasi, kita hanya menentukan project, lokasi, dan nama model, tetapi ada parameter lain yang dapat Anda tetapkan untuk model tersebut:
temperature(Float temp)
— untuk menentukan seberapa kreatifkah respons yang Anda inginkan (0 jika materi iklan rendah dan sering kali lebih faktual, sedangkan 1 untuk output materi iklan yang lebih banyak)topP(Float topP)
— untuk memilih kemungkinan kata yang total probabilitasnya menjumlahkan bilangan floating point tersebut (antara 0 dan 1)topK(Integer topK)
— untuk memilih kata secara acak dari jumlah maksimum kata yang mungkin untuk pelengkapan teks (dari 1 sampai 40)maxOutputTokens(Integer max)
— untuk menentukan panjang maksimum jawaban yang diberikan oleh model (umumnya, 4 token mewakili sekitar 3 kata)maxRetries(Integer retries)
— jika Anda melampaui kuota permintaan per waktu, atau platform mengalami beberapa masalah teknis, Anda dapat meminta model untuk mencoba kembali panggilan tersebut 3 kali
Sejauh ini, Anda mengajukan satu pertanyaan kepada Gemini, tetapi Anda juga dapat melakukan percakapan bolak-balik. Itulah yang akan Anda pelajari di bagian berikutnya.
5. Mulai percakapan dengan Gemini
Di langkah sebelumnya, Anda mengajukan satu pertanyaan. Sekarang saatnya melakukan percakapan nyata antara pengguna dan LLM. Setiap pertanyaan dan jawaban dapat dibangun berdasarkan pertanyaan dan jawaban sebelumnya untuk membentuk diskusi yang nyata.
Lihat Conversation.java
di folder 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));
});
}
}
Beberapa impor baru yang menarik di class ini:
MessageWindowChatMemory
— class yang akan membantu menangani aspek percakapan multi-giliran, serta menyimpan pertanyaan dan jawaban sebelumnya dalam memori lokalAiServices
— kelas yang akan menggabungkan model chat dan memori chat
Di metode utama, Anda akan menyiapkan model, memori chat, dan layanan AI. Model dikonfigurasi seperti biasa dengan informasi project, lokasi, dan nama model.
Untuk memori chat, kita menggunakan builder MessageWindowChatMemory
untuk membuat memori yang menyimpan 20 pesan terakhir yang dipertukarkan. Ini adalah jendela geser di atas percakapan yang konteksnya disimpan secara lokal di klien class Java kita.
Anda kemudian membuat AI service
yang mengikat model chat dengan memori chat.
Perhatikan cara layanan AI menggunakan antarmuka ConversationService
kustom yang telah kita tentukan, yang diimplementasikan oleh LangChain4j, dan menggunakan kueri String
serta menampilkan respons String
.
Sekarang, saatnya berdiskusi dengan Gemini. Pertama, sapaan sederhana dikirim, lalu pertanyaan pertama tentang menara Eiffel untuk mengetahui di negara mana sapaan itu dapat ditemukan. Perhatikan bahwa kalimat terakhir berkaitan dengan jawaban pertanyaan pertama, saat Anda bertanya-tanya berapa jumlah penduduk di negara tempat menara Eiffel berada, tanpa menyebutkan secara eksplisit negara yang diberikan pada jawaban sebelumnya. Bagian ini menunjukkan bahwa pertanyaan dan jawaban sebelumnya dikirim dengan setiap perintah.
Jalankan contoh:
./gradlew run -q -DjavaMainClass=gemini.workshop.Conversation
Anda akan melihat tiga jawaban yang serupa dengan berikut ini:
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.
Anda dapat mengajukan pertanyaan satu giliran atau melakukan percakapan bolak-balik dengan Gemini, tetapi sejauh ini, inputnya hanya berupa teks. Bagaimana dengan gambar? Mari kita pelajari gambar pada langkah berikutnya.
6. Multimodalitas dengan Gemini
Gemini adalah model multimodal. Sistem ini tidak hanya menerima teks sebagai input, tetapi juga menerima gambar, atau bahkan video sebagai input. Di bagian ini, Anda akan melihat kasus penggunaan untuk mencampur teks dan gambar.
Apa menurutmu Gemini akan mengenali kucing ini?
Gambar kucing di salju yang diambil dari Wikipediahttps://upload.wikimedia.org/wikipedia/commons/b/b6/Felis_catus-cat_on_snow.jpg
Lihat Multimodal.java
di direktori 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());
}
}
Dalam impor, perhatikan bahwa kita membedakan antara berbagai jenis pesan dan konten. UserMessage
dapat berisi objek TextContent
dan ImageContent
. Inilah multimodalitas yang berfungsi: mencampur teks dan gambar. Model mengirimkan kembali Response
yang berisi AiMessage
.
Anda kemudian mengambil AiMessage
dari respons melalui content()
, kemudian teks pesan berkat text()
.
Jalankan contoh:
./gradlew run -q -DjavaMainClass=gemini.workshop.Multimodal
Nama gambar itu tentu memberi Anda petunjuk tentang isi gambar tersebut, tetapi output Gemini-nya mirip dengan yang berikut ini:
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.
Mencampur perintah gambar dan teks akan membuka kasus penggunaan yang menarik. Anda dapat membuat aplikasi yang dapat:
- Mengenali teks dalam gambar.
- Memeriksa apakah gambar aman untuk ditampilkan.
- Membuat teks gambar.
- Menelusuri database gambar dengan deskripsi teks biasa.
Selain mengekstrak informasi dari gambar, Anda juga dapat mengekstrak informasi dari teks yang tidak terstruktur. Itulah yang akan Anda pelajari di bagian berikutnya.
7. Mengekstrak informasi terstruktur dari teks yang tidak terstruktur
Ada banyak situasi di mana informasi penting diberikan dalam dokumen laporan, di email, atau teks panjang lainnya dengan cara yang tidak terstruktur. Idealnya, Anda ingin dapat mengekstrak detail kunci yang terkandung dalam teks yang tidak terstruktur, dalam bentuk objek terstruktur. Mari kita lihat cara melakukannya.
Misalkan Anda ingin mengekstrak nama dan usia seseorang, berdasarkan biografi atau deskripsi orang tersebut. Anda dapat menginstruksikan LLM untuk mengekstrak JSON dari teks tidak terstruktur dengan prompt yang diubah secara cerdas (ini biasa disebut "prompt engineering").
Lihat ExtractData.java
di 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
}
}
Mari kita lihat berbagai langkah dalam file ini:
- Data
Person
ditentukan untuk mewakili detail yang mendeskripsikan seseorang ( nama dan usia). - Antarmuka
PersonExtractor
ditentukan dengan metode yang diberi string teks tidak terstruktur, dan menampilkan instancePerson
. extractPerson()
dianotasi dengan anotasi@UserMessage
yang mengaitkan perintah dengannya. Itulah perintah yang akan digunakan model untuk mengekstrak informasi, dan menampilkan detail dalam bentuk dokumen JSON, yang akan diurai untuk Anda, dan dibatalkan susunannya menjadi instancePerson
.
Sekarang mari kita lihat konten metode main()
:
- Model chat dibuat instance-nya. Perhatikan bahwa kita menggunakan
temperature
yang sangat rendah dari nol, dantopK
dari hanya satu, untuk memastikan jawaban yang sangat determenistik. Hal ini juga membantu model mengikuti petunjuk dengan lebih baik. Secara khusus, kita tidak ingin Gemini menggabungkan respons JSON dengan markup Markdown tambahan. - Objek
PersonExtractor
dibuat berkat classAiServices
LangChain4j. - Kemudian, Anda cukup memanggil
Person person = extractor.extractPerson(...)
untuk mengekstrak detail orang dari teks yang tidak terstruktur, dan mendapatkan kembali instancePerson
dengan nama dan usia.
Jalankan contoh:
./gradlew run -q -DjavaMainClass=gemini.workshop.ExtractData
Anda akan melihat output berikut:
Anna 23
Ya, ini Anna dan mereka berusia 23 tahun!
Dengan pendekatan AiServices
ini, Anda beroperasi dengan objek dengan sistem yang kuat. Anda tidak berinteraksi langsung dengan LLM. Sebagai gantinya, Anda akan menggunakan class konkret, seperti data Person
untuk menampilkan informasi pribadi yang diekstrak, dan Anda memiliki objek PersonExtractor
dengan metode extractPerson()
yang menampilkan instance Person
. Konsep LLM diabstraksi, dan sebagai developer Java, Anda hanya memanipulasi class dan objek normal.
8. Membuat struktur prompt dengan template perintah
Saat Anda berinteraksi dengan LLM menggunakan serangkaian petunjuk atau pertanyaan umum, ada bagian dari perintah tersebut yang tidak pernah berubah, sementara bagian lainnya berisi data. Misalnya, jika ingin membuat resep, Anda dapat menggunakan perintah seperti "Anda seorang koki berbakat, tolong buat resep dengan bahan-bahan berikut: ...", lalu tambahkan bahan-bahan di akhir teks tersebut. Itulah fungsi template prompt, mirip dengan string interpolasi dalam bahasa pemrograman. Template perintah berisi placeholder yang dapat Anda ganti dengan data yang tepat untuk panggilan tertentu ke LLM.
Lebih jelasnya, mari kita pelajari TemplatePrompt.java
di direktori 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());
}
}
Seperti biasa, Anda mengonfigurasi model VertexAiGeminiChatModel
, dengan tingkat kreativitas yang tinggi dengan suhu yang tinggi serta nilai topP dan topK yang tinggi. Kemudian, Anda membuat PromptTemplate
dengan metode statis from()
, dengan meneruskan string perintah, dan menggunakan variabel placeholder tanda kurung kurawal ganda: {{dish}}
dan {{ingredients}}
.
Anda membuat perintah akhir dengan memanggil apply()
yang menggunakan peta key-value pair yang mewakili nama placeholder dan nilai string yang akan menggantikannya.
Terakhir, Anda memanggil metode generate()
model Gemini dengan membuat pesan pengguna dari perintah tersebut, menggunakan petunjuk prompt.toUserMessage()
.
Jalankan contoh:
./gradlew run -q -DjavaMainClass=gemini.workshop.TemplatePrompt
Anda akan melihat output yang dihasilkan dan terlihat mirip dengan output ini:
**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.
Jangan ragu untuk mengubah nilai dish
dan ingredients
di peta dan menyesuaikan suhu, topK
dan tokP
, lalu menjalankan ulang kodenya. Hal ini akan memungkinkan Anda untuk mengamati efek perubahan parameter ini pada LLM.
Template perintah adalah cara yang baik untuk memiliki petunjuk yang dapat digunakan kembali dan yang dapat diparameterisasi untuk panggilan LLM. Anda dapat meneruskan data dan menyesuaikan dialog untuk berbagai nilai yang diberikan oleh pengguna.
9. Klasifikasi teks dengan few-shot prompting
LLM cukup bagus dalam mengklasifikasikan teks ke dalam berbagai kategori. Anda dapat membantu LLM dalam melakukan tugas tersebut dengan memberikan beberapa contoh teks dan kategori terkaitnya. Pendekatan ini sering disebut few shot prompting.
Lihat TextClassification.java
di direktori app/src/main/java/gemini/workshop
, untuk melakukan jenis klasifikasi teks tertentu: analisis sentimen.
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());
}
}
Dalam metode main()
, Anda membuat model chat Gemini seperti biasa, tetapi dengan jumlah token output maksimum yang kecil, karena Anda hanya menginginkan respons singkat: teksnya adalah POSITIVE
, NEGATIVE
, atau NEUTRAL
.
Kemudian, Anda akan membuat template prompt yang dapat digunakan kembali dengan teknik few-shot prompting, dengan memberi petunjuk kepada model tentang beberapa contoh input dan output. Hal ini juga membantu model mengikuti output aktual. Gemini tidak akan membalas dengan kalimat lengkap, tetapi diminta untuk membalas hanya dengan satu kata.
Anda akan menerapkan variabel dengan metode apply()
, untuk mengganti placeholder {{text}}
dengan parameter asli ("I love strawberries"
), dan mengubah template tersebut menjadi pesan pengguna dengan toUserMessage()
.
Jalankan contoh:
./gradlew run -q -DjavaMainClass=gemini.workshop.TextClassification
Anda akan melihat satu kata:
POSITIVE
Sepertinya mencintai stroberi adalah sentimen positif!
10. Pembuatan Augmented Reality
LLM dilatih dengan teks dalam jumlah besar. Namun, pengetahuan mereka hanya mencakup informasi yang dilihat selama pelatihan. Jika ada informasi baru yang dirilis setelah batas waktu pelatihan model, detail tersebut tidak akan tersedia untuk model. Dengan demikian, model tidak akan mampu menjawab pertanyaan tentang informasi yang belum dilihat.
Itulah sebabnya pendekatan seperti Retrieval Augmented Generation (RAG) membantu memberikan informasi tambahan yang mungkin perlu diketahui LLM untuk memenuhi permintaan penggunanya, untuk membalas dengan informasi yang mungkin lebih baru atau berdasarkan informasi pribadi yang tidak dapat diakses pada waktu pelatihan.
Mari kita kembali ke percakapan. Kali ini, Anda akan dapat mengajukan pertanyaan tentang dokumen Anda. Anda akan membangun chatbot yang mampu mengambil informasi yang relevan dari database yang berisi bagian dokumen yang lebih kecil ("bagian") dan informasi tersebut akan digunakan oleh model untuk mendasarkan jawabannya, alih-alih hanya mengandalkan pengetahuan yang terdapat dalam pelatihannya.
Dalam RAG, terdapat dua fase:
- Fase penyerapan — Dokumen dimuat dalam memori, dibagi menjadi potongan-potongan yang lebih kecil, dan embedding vektor (representasi vektor multidimensi tinggi dari potongan tersebut) dihitung dan disimpan dalam database vektor yang mampu melakukan penelusuran semantik. Fase penyerapan ini biasanya dilakukan satu kali, saat dokumen baru perlu ditambahkan ke korpus dokumen.
- Fase kueri — Pengguna kini dapat mengajukan pertanyaan tentang dokumen. Pertanyaan itu juga akan diubah menjadi vektor dan dibandingkan dengan semua vektor lain dalam {i>database<i}. Vektor yang paling mirip biasanya terkait secara semantik dan ditampilkan oleh database vektor. Kemudian, LLM diberi konteks percakapan, potongan-potongan teks yang sesuai dengan vektor yang ditampilkan oleh {i>database<i}, dan diminta untuk mendasarkan jawabannya dengan melihat potongan-potongan tersebut.
Menyiapkan dokumen
Untuk demo baru ini, Anda akan mengajukan pertanyaan tentang panduan "Attention is all you need" makalah penelitian. Studi tersebut menjelaskan arsitektur jaringan neural transformator, yang dipelopori oleh Google, dan merupakan cara penerapan semua model bahasa besar modern saat ini.
Makalah ini sudah didownload ke attention-is-all-you-need.pdf di repositori.
Mengimplementasikan chatbot
Mari pelajari cara membangun pendekatan 2 fase: pertama dengan penyerapan dokumen, lalu waktu kueri saat pengguna mengajukan pertanyaan tentang dokumen.
Dalam contoh ini, kedua fase diimplementasikan di class yang sama. Biasanya, Anda memiliki satu aplikasi yang menangani penyerapan, dan aplikasi lain yang menawarkan antarmuka chatbot kepada pengguna.
Selain itu, dalam contoh ini kita akan menggunakan database vektor dalam memori. Dalam skenario produksi yang sebenarnya, fase penyerapan dan kueri akan dipisahkan dalam dua aplikasi yang berbeda, dan vektor tersebut disimpan dalam database mandiri.
Penyerapan dokumen
Langkah pertama dari fase penyerapan dokumen adalah menemukan file PDF yang sudah didownload, dan menyiapkan PdfParser
untuk membacanya:
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());
Alih-alih membuat model bahasa chat biasa, Anda harus membuat instance model penyematan. Ini adalah model tertentu yang perannya adalah membuat representasi vektor dari potongan teks (kata, kalimat, atau bahkan paragraf). Metode ini mengembalikan vektor bilangan floating point, bukan menampilkan respons teks.
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();
Selanjutnya, Anda memerlukan beberapa kelas untuk dikerjakan bersama guna:
- Muat dan bagi dokumen PDF dalam beberapa bagian.
- Membuat embedding vektor untuk semua potongan ini.
InMemoryEmbeddingStore<TextSegment> embeddingStore =
new InMemoryEmbeddingStore<>();
EmbeddingStoreIngestor storeIngestor = EmbeddingStoreIngestor.builder()
.documentSplitter(DocumentSplitters.recursive(500, 100))
.embeddingModel(embeddingModel)
.embeddingStore(embeddingStore)
.build();
storeIngestor.ingest(document);
Instance InMemoryEmbeddingStore
, yang merupakan database vektor dalam memori, dibuat untuk menyimpan embedding vektor.
Dokumen ini dibagi menjadi beberapa bagian berkat class DocumentSplitters
. Ini akan membagi teks file PDF menjadi cuplikan 500 karakter, dengan tumpang tindih 100 karakter (dengan potongan berikut, untuk menghindari pemotongan kata atau kalimat, menjadi per bagian).
Penyerapan penyimpanan menghubungkan pemisah dokumen, model embedding untuk menghitung vektor, dan database vektor dalam memori. Kemudian, metode ingest()
akan menangani penyerapan.
Sekarang, fase pertama selesai, dokumen telah diubah menjadi potongan teks dengan embedding vektor terkait, dan disimpan dalam database vektor.
Mengajukan pertanyaan
Saatnya bersiap mengajukan pertanyaan! Buat model chat untuk memulai percakapan:
ChatLanguageModel model = VertexAiGeminiChatModel.builder()
.project(System.getenv("PROJECT_ID"))
.location(System.getenv("LOCATION"))
.modelName("gemini-1.5-flash-001")
.maxOutputTokens(1000)
.build();
Anda juga memerlukan class retriever untuk menautkan database vektor (dalam variabel embeddingStore
) dengan model embedding. Tugasnya adalah mengkueri database vektor dengan menghitung embedding vektor untuk kueri pengguna, untuk menemukan vektor serupa dalam database:
EmbeddingStoreContentRetriever retriever =
new EmbeddingStoreContentRetriever(embeddingStore, embeddingModel);
Di luar metode utama, buat antarmuka yang merepresentasikan asisten pakar LLM, yaitu antarmuka yang akan diimplementasikan oleh class AiServices
agar Anda dapat berinteraksi dengan model:
interface LlmExpert {
String ask(String question);
}
Pada tahap ini, Anda dapat mengonfigurasi layanan AI baru:
LlmExpert expert = AiServices.builder(LlmExpert.class)
.chatLanguageModel(model)
.chatMemory(MessageWindowChatMemory.withMaxMessages(10))
.contentRetriever(retriever)
.build();
Layanan ini mengikat bersama:
- Model bahasa chat yang Anda konfigurasi sebelumnya.
- Memori chat untuk melacak percakapan.
- Pengambil membandingkan kueri penyematan vektor dengan vektor dalam database.
- Template perintah secara eksplisit menyatakan bahwa model chat harus membalas dengan mendasarkan responsnya pada informasi yang diberikan (yaitu cuplikan relevan dari dokumentasi yang penyematan vektornya mirip dengan vektor pertanyaan pengguna).
.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())
Akhirnya Anda siap mengajukan pertanyaan!
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)));
);
Kode sumber lengkap ada di RAG.java
di direktori app/src/main/java/gemini/workshop
:
Jalankan contoh:
./gradlew -q run -DjavaMainClass=gemini.workshop.RAG
Di output, Anda akan melihat jawaban atas pertanyaan Anda:
=== 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. Panggilan fungsi
Ada juga situasi saat Anda ingin LLM memiliki akses ke sistem eksternal, seperti API web jarak jauh yang mengambil informasi atau memiliki tindakan, maupun layanan yang melakukan semacam komputasi. Contoh:
API web jarak jauh:
- Melacak dan memperbarui pesanan pelanggan.
- Temukan atau buat tiket di issue tracker.
- Ambil data real-time seperti harga saham atau pengukuran sensor IoT.
- Kirim email.
Alat komputasi:
- Kalkulator untuk soal matematika tingkat lanjut.
- Interpretasi kode untuk menjalankan kode saat LLM memerlukan logika penalaran.
- Mengonversi permintaan bahasa alami menjadi kueri SQL sehingga LLM dapat melakukan kueri database.
Panggilan fungsi adalah kemampuan model untuk meminta satu atau beberapa panggilan fungsi dilakukan atas namanya, sehingga dapat menjawab perintah pengguna dengan tepat menggunakan data yang lebih baru.
Dengan mempertimbangkan perintah tertentu dari pengguna, dan pengetahuan fungsi yang ada yang mungkin relevan dengan konteks tersebut, LLM dapat membalas dengan permintaan panggilan fungsi. Aplikasi yang mengintegrasikan LLM kemudian dapat memanggil fungsi, lalu membalas LLM dengan respons, kemudian LLM kemudian menafsirkan kembali dengan memberikan jawaban tekstual.
Empat langkah untuk melakukan panggilan fungsi
Mari kita lihat contoh panggilan fungsi: mendapatkan informasi tentang prakiraan cuaca.
Jika Anda bertanya kepada Gemini atau LLM lain tentang cuaca di Paris, mereka akan menjawab dengan mengatakan bahwa Gemini atau LLM lain tidak memiliki informasi tentang perkiraan cuaca. Jika ingin LLM memiliki akses real time ke data cuaca, Anda perlu menentukan beberapa fungsi yang dapat digunakannya.
Lihat diagram berikut:
1️⃣ Pertama, pengguna bertanya tentang cuaca di Paris. Aplikasi chatbot mengetahui bahwa ada satu atau beberapa fungsi yang dapat digunakan untuk membantu LLM memenuhi kueri. Chatbot mengirimkan perintah awal, serta daftar fungsi yang dapat dipanggil. Di sini, fungsi bernama getWeather()
yang menggunakan parameter string untuk lokasi.
Karena LLM tidak mengetahui prakiraan cuaca, LLM akan mengirim kembali permintaan eksekusi fungsi, alih-alih membalas melalui teks. Chatbot harus memanggil fungsi getWeather()
dengan "Paris"
sebagai parameter lokasi.
2️⃣ Chatbot memanggil fungsi tersebut atas nama LLM, mengambil respons fungsi. Di sini, kita bayangkan bahwa responsnya adalah {"forecast": "sunny"}
.
3️⃣ Aplikasi chatbot mengirimkan respons JSON kembali ke LLM.
4️⃣ LLM melihat respons JSON, menafsirkan informasi tersebut, dan akhirnya membalas dengan teks bahwa cuaca di Paris cerah.
Setiap langkah sebagai kode
Pertama, Anda akan mengonfigurasi model Gemini seperti biasa:
ChatLanguageModel model = VertexAiGeminiChatModel.builder()
.project(System.getenv("PROJECT_ID"))
.location(System.getenv("LOCATION"))
.modelName("gemini-1.5-flash-001")
.maxOutputTokens(100)
.build();
Anda menentukan spesifikasi alat yang menjelaskan fungsi yang dapat dipanggil:
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();
Nama fungsi telah ditentukan, nama dan jenis parameternya, tetapi perhatikan bahwa baik fungsi maupun parameternya diberi deskripsi. Deskripsi sangat penting dan membantu LLM benar-benar memahami apa yang dapat dilakukan suatu fungsi, dan dengan demikian menilai apakah fungsi ini perlu dipanggil dalam konteks percakapan.
Mari kita mulai langkah #1, dengan mengirimkan pertanyaan awal tentang cuaca di Paris:
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);
Pada langkah #2, kita meneruskan alat yang kita inginkan untuk digunakan model, dan model akan membalas dengan permintaan eksekusi yang terlalu tinggi:
// 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());
Langkah #3. Pada tahap ini, kita tahu fungsi apa yang perlu dipanggil oleh LLM. Dalam kode, kita tidak membuat panggilan nyata ke API eksternal, kita hanya menampilkan hipotesis perkiraan cuaca secara langsung:
// 3) We send back the result of the function call
ToolExecutionResultMessage toolExecResMsg = ToolExecutionResultMessage.from(toolExecutionRequest,
"{\"location\":\"Paris\",\"forecast\":\"sunny\", \"temperature\": 20}");
allMessages.add(toolExecResMsg);
Dan di langkah #4, LLM mempelajari hasil eksekusi fungsi, lalu dapat menyintesis respons tekstual:
// 4) The model answers with a sentence describing the weather
Response<AiMessage> weatherResponse = model.generate(allMessages);
System.out.println("Answer: " + weatherResponse.content().text());
Outputnya adalah:
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.
Anda dapat melihat output di atas permintaan eksekusi fitur, beserta jawabannya.
Kode sumber lengkap ada di FunctionCalling.java
di direktori app/src/main/java/gemini/workshop
:
Jalankan contoh:
./gradlew run -q -DjavaMainClass=gemini.workshop.FunctionCalling
Anda akan melihat output yang mirip dengan berikut ini:
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 menangani panggilan fungsi
Pada langkah sebelumnya, Anda telah melihat bagaimana interaksi pertanyaan/jawaban teks normal dan interaksi permintaan/respons teks disisipi, dan di antaranya, Anda memberikan respons fungsi yang diminta secara langsung, tanpa memanggil fungsi sebenarnya.
Namun, LangChain4j juga menawarkan abstraksi tingkat lebih tinggi yang dapat menangani panggilan fungsi secara transparan untuk Anda, sambil menangani percakapan seperti biasa.
Panggilan fungsi tunggal
Mari kita lihat FunctionCallingAssistant.java
, bagian demi bagian.
Pertama, Anda akan membuat data yang akan mewakili struktur data respons fungsi:
record WeatherForecast(String location, String forecast, int temperature) {}
Respons akan berisi informasi tentang lokasi, perkiraan cuaca, dan suhu.
Kemudian, Anda membuat class yang berisi fungsi sebenarnya yang ingin Anda sediakan untuk model:
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);
}
}
}
Perhatikan bahwa class ini berisi satu fungsi, tetapi dianotasi dengan anotasi @Tool
yang sesuai dengan deskripsi fungsi yang dapat diminta oleh model untuk dipanggil.
Parameter fungsi (satu parameter di sini) juga dianotasi, tetapi dengan anotasi @P
singkat ini, yang juga memberikan deskripsi parameter. Anda dapat menambahkan fungsi sebanyak yang diinginkan, membuatnya tersedia untuk model, untuk skenario yang lebih kompleks.
Di class ini, Anda akan menampilkan beberapa template pesan, tetapi jika ingin memanggil layanan prakiraan cuaca eksternal yang sebenarnya, layanan ini ada dalam isi metode yang akan Anda panggil ke layanan tersebut.
Seperti yang telah kita lihat saat membuat ToolSpecification
dalam pendekatan sebelumnya, penting untuk mendokumentasikan fungsi fungsi, dan menjelaskan hal yang berkaitan dengan parameter tersebut. Hal ini membantu model memahami bagaimana dan kapan fungsi ini dapat digunakan.
Selanjutnya, LangChain4j memungkinkan Anda menyediakan antarmuka yang sesuai dengan kontrak yang ingin Anda gunakan untuk berinteraksi dengan model. Di sini, ini adalah antarmuka sederhana yang mengambil string yang mewakili pesan pengguna, dan menampilkan string yang sesuai dengan respons model:
interface WeatherAssistant {
String chat(String userMessage);
}
Anda juga dapat menggunakan tanda tangan yang lebih kompleks yang melibatkan UserMessage
LangChain4j (untuk pesan pengguna) atau AiMessage
(untuk respons model), atau bahkan TokenStream
, jika Anda ingin menangani situasi yang lebih canggih, karena objek yang lebih rumit tersebut juga berisi informasi tambahan seperti jumlah token yang dikonsumsi, dll. Namun, agar lebih mudah, kita hanya akan mengambil string dalam input, dan string dalam output.
Mari kita selesaikan dengan metode main()
yang menggabungkan semua bagian:
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?"));
}
Seperti biasa, Anda mengonfigurasi model percakapan Gemini. Lalu, buat instance layanan prakiraan cuaca yang berisi "fungsi" yang akan diminta model untuk kita panggil.
Sekarang, Anda akan menggunakan class AiServices
lagi untuk mengikat model chat, memori chat, dan alat (yaitu layanan prakiraan cuaca dengan fungsinya). AiServices
menampilkan objek yang mengimplementasikan antarmuka WeatherAssistant
yang telah Anda tentukan. Satu-satunya hal yang tersisa adalah memanggil metode chat()
asisten itu. Saat memanggilnya, Anda hanya akan melihat respons teks, tetapi permintaan panggilan fungsi dan respons panggilan fungsi tidak akan terlihat dari developer, dan permintaan tersebut akan ditangani secara otomatis dan transparan. Jika menganggap suatu fungsi harus dipanggil, Gemini akan membalas dengan permintaan panggilan fungsi, dan LangChain4j akan menangani panggilan fungsi lokal untuk Anda.
Jalankan contoh:
./gradlew run -q -DjavaMainClass=gemini.workshop.FunctionCallingAssistant
Anda akan melihat output yang mirip dengan berikut ini:
OK. The weather in Paris is sunny with a temperature of 20 degrees.
Ini adalah contoh fungsi tunggal.
Beberapa panggilan fungsi
Anda juga dapat memiliki beberapa fungsi dan mengizinkan LangChain4j menangani beberapa panggilan fungsi atas nama Anda. Lihat MultiFunctionCallingAssistant.java
untuk melihat beberapa contoh fungsi.
Ini memiliki fungsi untuk mengonversi mata uang:
@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;
}
Fungsi lain untuk mendapatkan nilai saham:
@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;
}
Fungsi lain untuk menerapkan persentase ke jumlah tertentu:
@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;
}
Anda kemudian dapat menggabungkan semua fungsi ini dan kelas MultiTools dan mengajukan pertanyaan seperti "Berapa 10% dari harga saham AAPL dikonversi dari USD ke EUR?""
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?"));
}
Jalankan sebagai berikut:
./gradlew run -q -DjavaMainClass=gemini.workshop.MultiFunctionCallingAssistant
Dan Anda akan melihat beberapa fungsi bernama:
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.
Kepada Agen
Panggilan fungsi adalah mekanisme ekstensi yang bagus untuk model bahasa besar seperti Gemini. Layanan ini memungkinkan kami membangun sistem yang lebih kompleks yang sering disebut "agen" atau "asisten AI". Agen ini dapat berinteraksi dengan dunia eksternal melalui API eksternal dan dengan layanan yang dapat memiliki efek samping terhadap lingkungan eksternal (seperti mengirim email, membuat tiket, dll.)
Saat membuat agen yang ampuh, Anda harus melakukannya secara bertanggung jawab. Anda harus mempertimbangkan interaksi manusia sebelum melakukan tindakan otomatis. Penting untuk mengingat keamanan saat mendesain agen berteknologi LLM yang berinteraksi dengan dunia eksternal.
13. Menjalankan Gemma dengan Ollama dan TestContainers
Sejauh ini, kami telah menggunakan Gemini, tetapi ada juga Gemma, yang merupakan model adik perempuannya.
Gemma adalah rangkaian model terbuka yang ringan dan canggih, dibangun dari riset dan teknologi yang sama dengan yang digunakan untuk membuat model Gemini. Gemma tersedia dalam dua variasi Gemma1 dan Gemma2 masing-masing dengan berbagai ukuran. Gemma1 tersedia dalam dua ukuran: 2B dan 7B. Gemma2 tersedia dalam dua ukuran: 9B dan 27B. Bobotnya tersedia gratis, dan ukurannya yang kecil berarti Anda dapat menjalankannya sendiri, bahkan di laptop Anda atau di Cloud Shell.
Bagaimana cara menjalankan Gemma?
Ada banyak cara untuk menjalankan Gemma: di cloud, melalui Vertex AI dengan sekali klik, atau GKE dengan beberapa GPU, tetapi Anda juga dapat menjalankannya secara lokal.
Salah satu opsi yang bagus untuk menjalankan Gemma secara lokal adalah dengan Ollama, alat yang memungkinkan Anda menjalankan model kecil, seperti Llama 2, Mistral, dan banyak lagi di komputer lokal Anda. Mirip dengan Docker, tetapi untuk LLM.
Instal Ollama dengan mengikuti petunjuk untuk Sistem Operasi Anda.
Jika menggunakan lingkungan Linux, Anda harus mengaktifkan Ollama terlebih dahulu setelah menginstalnya.
ollama serve > /dev/null 2>&1 &
Setelah diinstal secara lokal, Anda dapat menjalankan perintah untuk mengambil model:
ollama pull gemma:2b
Tunggu hingga model ditarik. Proses ini dapat memerlukan waktu agak lama.
Jalankan model:
ollama run gemma:2b
Sekarang, Anda dapat berinteraksi dengan model:
>>> Hello! Hello! It's nice to hear from you. What can I do for you today?
Untuk keluar dari perintah, tekan Ctrl+D
Menjalankan Gemma di Ollama pada TestContainer
Daripada harus menginstal dan menjalankan Ollama secara lokal, Anda dapat menggunakan Ollama di dalam container, yang ditangani oleh TestContainers.
TestContainers tidak hanya berguna untuk pengujian, tetapi Anda juga dapat menggunakannya untuk menjalankan container. Bahkan ada OllamaContainer
spesifik yang dapat Anda manfaatkan.
Berikut gambaran lengkapnya:
Penerapan
Mari kita lihat GemmaWithOllamaContainer.java
, bagian demi bagian.
Pertama, Anda perlu membuat container Ollama turunan yang menarik model Gemma. Gambar ini sudah ada dari proses sebelumnya atau akan dibuat. Jika image tersebut sudah ada, Anda hanya akan memberi tahu TestContainers bahwa Anda ingin mengganti image Ollama default dengan varian yang didukung 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"));
}
}
Selanjutnya, Anda membuat dan memulai container pengujian Ollama, lalu membuat model chat Ollama, dengan mengarahkan ke alamat dan port container menggunakan model yang ingin Anda gunakan. Terakhir, Anda cukup memanggil model.generate(yourPrompt)
seperti biasa:
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);
}
Jalankan sebagai berikut:
./gradlew run -q -DjavaMainClass=gemini.workshop.GemmaWithOllamaContainer
Proses pertama akan memakan waktu beberapa saat untuk membuat dan menjalankan container, tetapi setelah selesai, Anda akan melihat Gemma merespons:
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.
Anda memiliki Gemma yang berjalan di Cloud Shell!
14. Selamat
Selamat, Anda telah berhasil membangun aplikasi chat AI Generatif pertama Anda di Java menggunakan LangChain4j dan Gemini API. Selama ini, Anda telah mendapati bahwa model bahasa besar multimodal cukup canggih dan mampu menangani berbagai tugas seperti tanya jawab, bahkan pada dokumentasi Anda sendiri, ekstraksi data, berinteraksi dengan API eksternal, dan banyak lagi.
Apa selanjutnya?
Sekarang giliran Anda untuk meningkatkan kualitas aplikasi dengan integrasi LLM yang canggih.
Bacaan lebih lanjut
- Kasus penggunaan umum AI generatif
- Materi pelatihan tentang AI Generatif
- Berinteraksi dengan Gemini melalui Generative AI Studio
- Responsible AI