1. Mem-build aplikasi Flutter yang didukung Gemini
Yang akan Anda build
Dalam codelab ini, Anda akan mem-build Colorist - aplikasi Flutter interaktif yang menghadirkan kecanggihan Gemini API langsung ke aplikasi Flutter Anda. Pernah ingin mengizinkan pengguna mengontrol aplikasi Anda melalui bahasa alami, tetapi tidak tahu harus memulai dari mana? Codelab ini menunjukkan caranya.
Colorist memungkinkan pengguna mendeskripsikan warna dalam bahasa alami (seperti "oranye matahari terbenam" atau "biru laut dalam"), dan aplikasi:
- Memproses deskripsi ini menggunakan Gemini API Google
- Menafsirkan deskripsi menjadi nilai warna RGB yang akurat
- Menampilkan warna di layar secara real time
- Memberikan detail warna teknis dan konteks menarik tentang warna
- Mempertahankan histori warna yang baru dibuat
Aplikasi ini memiliki antarmuka layar terpisah dengan area tampilan warna dan sistem chat interaktif di satu sisi, serta panel log mendetail yang menampilkan interaksi LLM mentah di sisi lain. Log ini memungkinkan Anda lebih memahami cara kerja integrasi LLM di balik layar.
Mengapa hal ini penting bagi developer Flutter
LLM merevolusi cara pengguna berinteraksi dengan aplikasi, tetapi mengintegrasikannya secara efektif ke dalam aplikasi seluler dan desktop menghadirkan tantangan unik. Codelab ini mengajarkan pola praktis yang lebih dari sekadar panggilan API mentah.
Perjalanan Anda dalam belajar
Codelab ini akan memandu Anda melalui proses pembuatan Colorist langkah demi langkah:
- Penyiapan project - Anda akan memulai dengan struktur aplikasi Flutter dasar dan paket
colorist_ui
- Integrasi Gemini dasar - Hubungkan aplikasi Anda ke Vertex AI di Firebase dan terapkan komunikasi LLM sederhana
- Perintah yang efektif - Buat perintah sistem yang memandu LLM untuk memahami deskripsi warna
- Deklarasi fungsi - Menentukan alat yang dapat digunakan LLM untuk menetapkan warna di aplikasi Anda
- Penanganan alat - Memproses panggilan fungsi dari LLM dan menghubungkannya ke status aplikasi Anda
- Respons streaming - Meningkatkan pengalaman pengguna dengan respons LLM streaming real-time
- Sinkronisasi Konteks LLM - Membuat pengalaman yang kohesif dengan memberi tahu LLM tentang tindakan pengguna
Yang akan Anda pelajari
- Mengonfigurasi Vertex AI in Firebase untuk aplikasi Flutter
- Membuat perintah sistem yang efektif untuk memandu perilaku LLM
- Mengimplementasikan deklarasi fungsi yang menjembatani bahasa alami dan fitur aplikasi
- Memproses respons streaming untuk pengalaman pengguna yang responsif
- Menyinkronkan status antara peristiwa UI dan LLM
- Mengelola status percakapan LLM menggunakan Riverpod
- Menangani error dengan baik di aplikasi yang didukung LLM
Pratinjau kode: Contoh yang akan Anda terapkan
Berikut adalah sekilas deklarasi fungsi yang akan Anda buat untuk memungkinkan LLM menetapkan warna di aplikasi Anda:
FunctionDeclaration get setColorFuncDecl => FunctionDeclaration(
'set_color',
'Set the color of the display square based on red, green, and blue values.',
parameters: {
'red': Schema.number(description: 'Red component value (0.0 - 1.0)'),
'green': Schema.number(description: 'Green component value (0.0 - 1.0)'),
'blue': Schema.number(description: 'Blue component value (0.0 - 1.0)'),
},
);
Ringkasan video codelab ini
Tonton Craig Labenz dan Andrew Brogdon membahas codelab ini di episode Observable Flutter #59:
Prasyarat
Untuk mendapatkan hasil maksimal dari codelab ini, Anda harus memiliki:
- Pengalaman pengembangan Flutter - Pemahaman tentang dasar-dasar Flutter dan sintaksis Dart
- Pengetahuan pemrograman asinkron - Pemahaman tentang Future, async/await, dan streaming
- Akun Firebase - Anda memerlukan Akun Google untuk menyiapkan Firebase
- Project Firebase dengan penagihan diaktifkan - Vertex AI in Firebase memerlukan akun penagihan
Mari kita mulai mem-build aplikasi Flutter pertama Anda yang didukung LLM.
2. Penyiapan project & layanan echo
Pada langkah pertama ini, Anda akan menyiapkan struktur project dan menerapkan layanan echo sederhana yang nantinya akan diganti dengan integrasi Gemini API. Tindakan ini akan menetapkan arsitektur aplikasi dan memastikan UI Anda berfungsi dengan benar sebelum menambahkan kompleksitas panggilan LLM.
Yang akan Anda pelajari di langkah ini
- Menyiapkan project Flutter dengan dependensi yang diperlukan
- Menggunakan paket
colorist_ui
untuk komponen UI - Menerapkan layanan pesan echo dan menghubungkannya ke UI
Catatan penting tentang harga
Membuat project Flutter baru
Mulailah dengan membuat project Flutter baru dengan perintah berikut:
flutter create -e colorist --platforms=android,ios,macos,web,windows
Flag -e
menunjukkan bahwa Anda menginginkan project kosong tanpa aplikasi counter
default. Aplikasi ini dirancang agar berfungsi di desktop, perangkat seluler, dan web. Namun, flutterfire
saat ini tidak mendukung Linux.
Menambahkan dependensi
Buka direktori project Anda dan tambahkan dependensi yang diperlukan:
cd colorist
flutter pub add colorist_ui flutter_riverpod riverpod_annotation
flutter pub add --dev build_runner riverpod_generator riverpod_lint json_serializable custom_lint
Tindakan ini akan menambahkan paket kunci berikut:
colorist_ui
: Paket kustom yang menyediakan komponen UI untuk aplikasi Coloristflutter_riverpod
danriverpod_annotation
: Untuk pengelolaan statuslogging
: Untuk logging terstruktur- Dependensi pengembangan untuk pembuatan kode dan linting
pubspec.yaml
Anda akan terlihat seperti ini:
pubspec.yaml
name: colorist
description: "A new Flutter project."
publish_to: 'none'
version: 0.1.0
environment:
sdk: ^3.8.0
dependencies:
flutter:
sdk: flutter
colorist_ui: ^0.2.3
flutter_riverpod: ^2.6.1
riverpod_annotation: ^2.6.1
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^5.0.0
build_runner: ^2.4.15
riverpod_generator: ^2.6.5
riverpod_lint: ^2.6.5
json_serializable: ^6.9.5
custom_lint: ^0.7.5
flutter:
uses-material-design: true
Mengonfigurasi opsi analisis
Tambahkan custom_lint
ke file analysis_options.yaml
di root project Anda:
include: package:flutter_lints/flutter.yaml
analyzer:
plugins:
- custom_lint
Konfigurasi ini memungkinkan lint khusus Riverpod untuk membantu mempertahankan kualitas kode.
Menerapkan file main.dart
Ganti konten lib/main.dart
dengan konten berikut:
lib/main.dart
import 'package:colorist_ui/colorist_ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() async {
runApp(ProviderScope(child: MainApp()));
}
class MainApp extends ConsumerWidget {
const MainApp({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
return MaterialApp(
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
),
home: MainScreen(
sendMessage: (message) {
sendMessage(message, ref);
},
),
);
}
// A fake LLM that just echoes back what it receives.
void sendMessage(String message, WidgetRef ref) {
final chatStateNotifier = ref.read(chatStateNotifierProvider.notifier);
final logStateNotifier = ref.read(logStateNotifierProvider.notifier);
chatStateNotifier.addUserMessage(message);
logStateNotifier.logUserText(message);
chatStateNotifier.addLlmMessage(message, MessageState.complete);
logStateNotifier.logLlmText(message);
}
}
Tindakan ini akan menyiapkan aplikasi Flutter yang mengimplementasikan layanan echo sederhana yang meniru perilaku LLM dengan hanya menampilkan pesan pengguna.
Memahami arsitektur
Mari luangkan waktu untuk memahami arsitektur aplikasi colorist
:
Paket colorist_ui
Paket colorist_ui
menyediakan komponen UI bawaan dan alat pengelolaan status:
- MainScreen: Komponen UI utama yang menampilkan:
- Tata letak layar terpisah di desktop (area interaksi dan panel log)
- Antarmuka dengan tab di perangkat seluler
- Tampilan warna, antarmuka chat, dan thumbnail histori
- Pengelolaan Status: Aplikasi menggunakan beberapa notifikasi status:
- ChatStateNotifier: Mengelola pesan chat
- ColorStateNotifier: Mengelola warna dan histori saat ini
- LogStateNotifier: Mengelola entri log untuk proses debug
- Penanganan Pesan: Aplikasi menggunakan model pesan dengan status yang berbeda:
- Pesan pengguna: Dimasukkan oleh pengguna
- Pesan LLM: Dibuat oleh LLM (atau layanan echo Anda untuk saat ini)
- MessageState: Melacak apakah pesan LLM sudah selesai atau masih di-streaming
Arsitektur aplikasi
Aplikasi mengikuti arsitektur berikut:
- Lapisan UI: Disediakan oleh paket
colorist_ui
- Pengelolaan Status: Menggunakan Riverpod untuk pengelolaan status reaktif
- Lapisan Layanan: Saat ini berisi layanan echo sederhana Anda, yang akan diganti dengan Layanan Gemini Chat
- Integrasi LLM: Akan ditambahkan di langkah berikutnya
Pemisahan ini memungkinkan Anda berfokus pada penerapan integrasi LLM saat komponen UI sudah ditangani.
Menjalankan aplikasi
Jalankan aplikasi dengan perintah berikut:
flutter run -d DEVICE
Ganti DEVICE
dengan perangkat target Anda, seperti macos
, windows
, chrome
, atau ID perangkat.
Sekarang Anda akan melihat aplikasi Colorist dengan:
- Area tampilan warna dengan warna default
- Antarmuka chat tempat Anda dapat mengetik pesan
- Panel log yang menampilkan interaksi chat
Coba ketik pesan seperti "Saya ingin warna biru tua", lalu tekan Kirim. Layanan echo hanya akan mengulangi pesan Anda. Pada langkah berikutnya, Anda akan menggantinya dengan interpretasi warna yang sebenarnya menggunakan Gemini API melalui Vertex AI di Firebase.
Apa langkah selanjutnya?
Pada langkah berikutnya, Anda akan mengonfigurasi Firebase dan menerapkan integrasi Gemini API dasar untuk mengganti layanan echo dengan layanan chat Gemini. Hal ini akan memungkinkan aplikasi menafsirkan deskripsi warna dan memberikan respons yang cerdas.
Pemecahan masalah
Masalah paket UI
Jika Anda mengalami masalah dengan paket colorist_ui
:
- Pastikan Anda menggunakan versi terbaru
- Pastikan Anda telah menambahkan dependensi dengan benar
- Memeriksa apakah ada versi paket yang bentrok
Error build
Jika Anda melihat error build:
- Pastikan Anda telah menginstal Flutter SDK saluran stabil terbaru
- Jalankan
flutter clean
diikuti denganflutter pub get
- Periksa output konsol untuk melihat pesan error tertentu
Konsep utama yang dipelajari
- Menyiapkan project Flutter dengan dependensi yang diperlukan
- Memahami arsitektur dan tanggung jawab komponen aplikasi
- Menerapkan layanan sederhana yang meniru perilaku LLM
- Menghubungkan layanan ke komponen UI
- Menggunakan Riverpod untuk pengelolaan status
3. Integrasi chat Gemini dasar
Pada langkah ini, Anda akan mengganti layanan echo dari langkah sebelumnya dengan integrasi Gemini API menggunakan Vertex AI di Firebase. Anda akan mengonfigurasi Firebase, menyiapkan penyedia yang diperlukan, dan menerapkan layanan chat dasar yang berkomunikasi dengan Gemini API.
Yang akan Anda pelajari di langkah ini
- Menyiapkan Firebase di aplikasi Flutter
- Mengonfigurasi Vertex AI in Firebase untuk akses Gemini
- Membuat penyedia Riverpod untuk layanan Firebase dan Gemini
- Mengimplementasikan layanan chat dasar dengan Gemini API
- Menangani respons API asinkron dan status error
Menyiapkan Firebase
Pertama, Anda harus menyiapkan Firebase untuk project Flutter. Hal ini mencakup pembuatan project Firebase, menambahkan aplikasi Anda ke project tersebut, dan mengonfigurasi setelan Vertex AI yang diperlukan.
Membuat project Firebase
- Buka Firebase Console dan login dengan Akun Google Anda.
- Klik Create a Firebase project atau pilih project yang ada.
- Ikuti wizard penyiapan untuk membuat project Anda.
- Setelah project dibuat, Anda harus mengupgrade ke paket Blaze (bayar sesuai penggunaan) untuk mengakses layanan Vertex AI. Klik tombol Upgrade di kiri bawah Firebase console.
Menyiapkan Vertex AI di project Firebase
- Di Firebase console, buka project Anda.
- Di sidebar kiri, pilih AI.
- Di kartu Vertex AI in Firebase, pilih Get Started.
- Ikuti petunjuk untuk mengaktifkan Vertex AI in Firebase API untuk project Anda.
Menginstal FlutterFire CLI
FlutterFire CLI menyederhanakan penyiapan Firebase di aplikasi Flutter:
dart pub global activate flutterfire_cli
Menambahkan Firebase ke aplikasi Flutter
- Tambahkan paket Firebase core dan Vertex AI ke project Anda:
flutter pub add firebase_core firebase_vertexai
- Jalankan perintah konfigurasi FlutterFire:
flutterfire configure
Perintah ini akan:
- Meminta Anda untuk memilih project Firebase yang baru saja dibuat
- Mendaftarkan aplikasi Flutter Anda ke Firebase
- Membuat file
firebase_options.dart
dengan konfigurasi project Anda
Perintah ini akan otomatis mendeteksi platform yang Anda pilih (iOS, Android, macOS, Windows, web) dan mengonfigurasinya dengan tepat.
Konfigurasi khusus platform
Firebase memerlukan versi minimum yang lebih tinggi dari versi default untuk Flutter. Server ini juga memerlukan akses jaringan untuk berkomunikasi dengan Vertex AI di server Firebase.
Mengonfigurasi izin macOS
Untuk macOS, Anda perlu mengaktifkan akses jaringan di hak aplikasi:
- Buka
macos/Runner/DebugProfile.entitlements
dan tambahkan:
macos/Runner/DebugProfile.entitlements
<key>com.apple.security.network.client</key>
<true/>
- Buka juga
macos/Runner/Release.entitlements
dan tambahkan entri yang sama. - Perbarui versi macOS minimum di bagian atas
macos/Podfile
:
macos/Podfile
# Firebase requires at least macOS 10.15
platform :osx, '10.15'
Mengonfigurasi izin iOS
Untuk iOS, perbarui versi minimum di bagian atas ios/Podfile
:
ios/Podfile
# Firebase requires at least iOS 13.0
platform :ios, '13.0'
Mengonfigurasi setelan Android
Untuk Android, update android/app/build.gradle.kts
:
android/app/build.gradle.kts
android {
// ...
ndkVersion = "27.0.12077973"
defaultConfig {
// ...
minSdk = 23
// ...
}
}
Membuat penyedia model Gemini
Sekarang Anda akan membuat penyedia Riverpod untuk Firebase dan Gemini. Buat file baru lib/providers/gemini.dart
:
lib/providers/gemini.dart
import 'dart:async';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_vertexai/firebase_vertexai.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import '../firebase_options.dart';
part 'gemini.g.dart';
@riverpod
Future<FirebaseApp> firebaseApp(Ref ref) =>
Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
@riverpod
Future<GenerativeModel> geminiModel(Ref ref) async {
await ref.watch(firebaseAppProvider.future);
final model = FirebaseVertexAI.instance.generativeModel(
model: 'gemini-2.0-flash',
);
return model;
}
@Riverpod(keepAlive: true)
Future<ChatSession> chatSession(Ref ref) async {
final model = await ref.watch(geminiModelProvider.future);
return model.startChat();
}
File ini menentukan dasar untuk tiga penyedia utama. Penyedia ini dibuat saat Anda menjalankan dart run build_runner
oleh generator kode Riverpod.
firebaseAppProvider
: Melakukan inisialisasi Firebase dengan konfigurasi project AndageminiModelProvider
: Membuat instance model generatif GeminichatSessionProvider
: Membuat dan mempertahankan sesi chat dengan model Gemini
Anotasi keepAlive: true
pada sesi chat memastikannya tetap ada di seluruh siklus proses aplikasi, sehingga mempertahankan konteks percakapan.
Mengimplementasikan layanan chat Gemini
Buat file baru lib/services/gemini_chat_service.dart
untuk menerapkan layanan chat:
lib/services/gemini_chat_service.dart
import 'dart:async';
import 'package:colorist_ui/colorist_ui.dart';
import 'package:firebase_vertexai/firebase_vertexai.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import '../providers/gemini.dart';
part 'gemini_chat_service.g.dart';
class GeminiChatService {
GeminiChatService(this.ref);
final Ref ref;
Future<void> sendMessage(String message) async {
final chatSession = await ref.read(chatSessionProvider.future);
final chatStateNotifier = ref.read(chatStateNotifierProvider.notifier);
final logStateNotifier = ref.read(logStateNotifierProvider.notifier);
chatStateNotifier.addUserMessage(message);
logStateNotifier.logUserText(message);
final llmMessage = chatStateNotifier.createLlmMessage();
try {
final response = await chatSession.sendMessage(Content.text(message));
final responseText = response.text;
if (responseText != null) {
logStateNotifier.logLlmText(responseText);
chatStateNotifier.appendToMessage(llmMessage.id, responseText);
}
} catch (e, st) {
logStateNotifier.logError(e, st: st);
chatStateNotifier.appendToMessage(
llmMessage.id,
"\nI'm sorry, I encountered an error processing your request. "
"Please try again.",
);
} finally {
chatStateNotifier.finalizeMessage(llmMessage.id);
}
}
}
@riverpod
GeminiChatService geminiChatService(Ref ref) => GeminiChatService(ref);
Layanan ini:
- Menerima pesan pengguna dan mengirimkannya ke Gemini API
- Memperbarui antarmuka chat dengan respons dari model
- Mencatat semua komunikasi untuk memudahkan pemahaman alur LLM yang sebenarnya
- Menangani error dengan masukan pengguna yang sesuai
Catatan: Pada tahap ini, jendela Log akan terlihat hampir sama dengan jendela chat. Log akan menjadi lebih menarik setelah Anda memperkenalkan panggilan fungsi, lalu melakukan streaming respons.
Membuat kode Riverpod
Jalankan perintah runner build untuk membuat kode Riverpod yang diperlukan:
dart run build_runner build --delete-conflicting-outputs
Tindakan ini akan membuat file .g.dart
yang diperlukan Riverpod agar dapat berfungsi.
Memperbarui file main.dart
Perbarui file lib/main.dart
untuk menggunakan layanan chat Gemini yang baru:
lib/main.dart
import 'package:colorist_ui/colorist_ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'providers/gemini.dart';
import 'services/gemini_chat_service.dart';
void main() async {
runApp(ProviderScope(child: MainApp()));
}
class MainApp extends ConsumerWidget {
const MainApp({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final model = ref.watch(geminiModelProvider);
return MaterialApp(
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
),
home: model.when(
data: (data) => MainScreen(
sendMessage: (text) {
ref.read(geminiChatServiceProvider).sendMessage(text);
},
),
loading: () => LoadingScreen(message: 'Initializing Gemini Model'),
error: (err, st) => ErrorScreen(error: err),
),
);
}
}
Perubahan utama dalam update ini adalah:
- Mengganti layanan echo dengan layanan chat berbasis Gemini API
- Menambahkan layar pemuatan dan error menggunakan pola
AsyncValue
Riverpod dengan metodewhen
- Menghubungkan UI ke layanan chat baru Anda melalui callback
sendMessage
Menjalankan aplikasi
Jalankan aplikasi dengan perintah berikut:
flutter run -d DEVICE
Ganti DEVICE
dengan perangkat target Anda, seperti macos
, windows
, chrome
, atau ID perangkat.
Sekarang, saat Anda mengetik pesan, pesan tersebut akan dikirim ke Gemini API, dan Anda akan menerima respons dari LLM, bukan echo. Panel log akan menampilkan interaksi dengan API.
Memahami komunikasi LLM
Mari luangkan waktu sejenak untuk memahami apa yang terjadi saat Anda berkomunikasi dengan Gemini API:
Alur komunikasi
- Input pengguna: Pengguna memasukkan teks di antarmuka chat
- Pemformatan Permintaan: Aplikasi memformat teks sebagai objek
Content
untuk Gemini API - Komunikasi API: Teks dikirim ke Gemini API melalui Vertex AI in Firebase
- Pemrosesan LLM: Model Gemini memproses teks dan menghasilkan respons
- Penanganan Respons: Aplikasi menerima respons dan memperbarui UI
- Logging: Semua komunikasi dicatat untuk transparansi
Sesi chat dan konteks percakapan
Sesi chat Gemini mempertahankan konteks di antara pesan, sehingga memungkinkan interaksi percakapan. Artinya, LLM "mengingat" pertukaran sebelumnya dalam sesi saat ini, sehingga memungkinkan percakapan yang lebih koheren.
Anotasi keepAlive: true
di penyedia sesi chat Anda memastikan konteks ini tetap ada selama siklus proses aplikasi. Konteks persisten ini sangat penting untuk mempertahankan alur percakapan yang alami dengan LLM.
Apa langkah selanjutnya?
Pada tahap ini, Anda dapat menanyakan apa pun kepada Gemini API, karena tidak ada batasan terkait hal yang akan diresponsnya. Misalnya, Anda dapat meminta ringkasan Perang Mawar, yang tidak terkait dengan tujuan aplikasi warna Anda.
Pada langkah berikutnya, Anda akan membuat perintah sistem untuk memandu Gemini dalam menafsirkan deskripsi warna secara lebih efektif. Ini akan menunjukkan cara menyesuaikan perilaku LLM untuk kebutuhan khusus aplikasi dan memfokuskan kemampuannya pada domain aplikasi Anda.
Pemecahan masalah
Masalah konfigurasi Firebase
Jika Anda mengalami error dengan inisialisasi Firebase:
- Pastikan file
firebase_options.dart
Anda dibuat dengan benar - Pastikan Anda telah mengupgrade ke paket Blaze untuk akses Vertex AI
Error akses API
Jika Anda menerima error saat mengakses Gemini API:
- Pastikan penagihan disiapkan dengan benar di project Firebase Anda
- Pastikan Vertex AI dan Cloud AI API diaktifkan di project Firebase Anda
- Periksa konektivitas jaringan dan setelan firewall Anda
- Pastikan nama model (
gemini-2.0-flash
) sudah benar dan tersedia
Masalah konteks percakapan
Jika Anda melihat bahwa Gemini tidak mengingat konteks sebelumnya dari chat:
- Pastikan fungsi
chatSession
dianotasi dengan@Riverpod(keepAlive: true)
- Pastikan Anda menggunakan kembali sesi chat yang sama untuk semua pertukaran pesan
- Memastikan sesi chat diinisialisasi dengan benar sebelum mengirim pesan
Masalah khusus platform
Untuk masalah khusus platform:
- iOS/macOS: Memastikan hak yang tepat ditetapkan dan versi minimum dikonfigurasi
- Android: Memverifikasi bahwa versi SDK minimum ditetapkan dengan benar
- Memeriksa pesan error khusus platform di konsol
Konsep utama yang dipelajari
- Menyiapkan Firebase di aplikasi Flutter
- Mengonfigurasi Vertex AI in Firebase untuk akses ke Gemini
- Membuat penyedia Riverpod untuk layanan asinkron
- Mengimplementasikan layanan chat yang berkomunikasi dengan LLM
- Menangani status API asinkron (pemuatan, error, data)
- Memahami alur komunikasi LLM dan sesi chat
4. Perintah yang efektif untuk deskripsi warna
Pada langkah ini, Anda akan membuat dan menerapkan perintah sistem yang memandu Gemini dalam menafsirkan deskripsi warna. Perintah sistem adalah cara yang efektif untuk menyesuaikan perilaku LLM untuk tugas tertentu tanpa mengubah kode Anda.
Yang akan Anda pelajari di langkah ini
- Memahami perintah sistem dan pentingnya dalam aplikasi LLM
- Membuat perintah yang efektif untuk tugas khusus domain
- Memuat dan menggunakan perintah sistem di aplikasi Flutter
- Memandu LLM untuk memberikan respons yang diformat secara konsisten
- Menguji pengaruh perintah sistem terhadap perilaku LLM
Memahami perintah sistem
Sebelum membahas implementasi, mari kita pahami apa yang dimaksud dengan perintah sistem dan mengapa perintah sistem itu penting:
Apa yang dimaksud dengan perintah sistem?
Perintah sistem adalah jenis petunjuk khusus yang diberikan ke LLM yang menetapkan konteks, panduan perilaku, dan ekspektasi untuk responsnya. Tidak seperti pesan pengguna, perintah sistem:
- Menetapkan peran dan persona LLM
- Menentukan pengetahuan atau kemampuan khusus
- Memberikan petunjuk pemformatan
- Menetapkan batasan pada respons
- Menjelaskan cara menangani berbagai skenario
Anggap perintah sistem sebagai memberikan "deskripsi pekerjaan" kepada LLM - perintah ini memberi tahu model bagaimana harus berperilaku selama percakapan.
Pentingnya perintah sistem
Perintah sistem sangat penting untuk menciptakan interaksi LLM yang konsisten dan berguna karena:
- Memastikan konsistensi: Membimbing model untuk memberikan respons dalam format yang konsisten
- Meningkatkan relevansi: Memfokuskan model pada domain tertentu (dalam kasus Anda, warna)
- Menetapkan batasan: Menentukan hal yang boleh dan tidak boleh dilakukan model
- Meningkatkan pengalaman pengguna: Membuat pola interaksi yang lebih alami dan bermanfaat
- Mengurangi pascapemrosesan: Mendapatkan respons dalam format yang lebih mudah diuraikan atau ditampilkan
Untuk aplikasi Colorist, Anda memerlukan LLM untuk menafsirkan deskripsi warna secara konsisten dan memberikan nilai RGB dalam format tertentu.
Membuat aset perintah sistem
Pertama, Anda akan membuat file perintah sistem yang akan dimuat saat runtime. Pendekatan ini memungkinkan Anda mengubah perintah tanpa mengompilasi ulang aplikasi.
Buat file baru assets/system_prompt.md
dengan konten berikut:
assets/system_prompt.md
# Colorist System Prompt
You are a color expert assistant integrated into a desktop app called Colorist. Your job is to interpret natural language color descriptions and provide the appropriate RGB values that best represent that description.
## Your Capabilities
You are knowledgeable about colors, color theory, and how to translate natural language descriptions into specific RGB values. When users describe a color, you should:
1. Analyze their description to understand the color they are trying to convey
2. Determine the appropriate RGB values (values should be between 0.0 and 1.0)
3. Respond with a conversational explanation and explicitly state the RGB values
## How to Respond to User Inputs
When users describe a color:
1. First, acknowledge their color description with a brief, friendly response
2. Interpret what RGB values would best represent that color description
3. Always include the RGB values clearly in your response, formatted as: `RGB: (red=X.X, green=X.X, blue=X.X)`
4. Provide a brief explanation of your interpretation
Example:
User: "I want a sunset orange"
You: "Sunset orange is a warm, vibrant color that captures the golden-red hues of the setting sun. It combines a strong red component with moderate orange tones.
RGB: (red=1.0, green=0.5, blue=0.25)
I've selected values with high red, moderate green, and low blue to capture that beautiful sunset glow. This creates a warm orange with a slightly reddish tint, reminiscent of the sun low on the horizon."
## When Descriptions are Unclear
If a color description is ambiguous or unclear, please ask the user clarifying questions, one at a time.
## Important Guidelines
- Always keep RGB values between 0.0 and 1.0
- Always format RGB values as: `RGB: (red=X.X, green=X.X, blue=X.X)` for easy parsing
- Provide thoughtful, knowledgeable responses about colors
- When possible, include color psychology, associations, or interesting facts about colors
- Be conversational and engaging in your responses
- Focus on being helpful and accurate with your color interpretations
Memahami struktur perintah sistem
Mari kita uraikan fungsi perintah ini:
- Definisi peran: Menetapkan LLM sebagai "asisten pakar warna"
- Penjelasan tugas: Menentukan tugas utama sebagai menafsirkan deskripsi warna menjadi nilai RGB
- Format respons: Menentukan dengan tepat cara nilai RGB harus diformat untuk konsistensi
- Contoh pertukaran: Memberikan contoh konkret tentang pola interaksi yang diharapkan
- Penanganan kasus ekstrem: Memberi petunjuk cara menangani deskripsi yang tidak jelas
- Batasan dan panduan: Menetapkan batas seperti mempertahankan nilai RGB antara 0,0 dan 1,0
Pendekatan terstruktur ini memastikan respons LLM akan konsisten, informatif, dan diformat sedemikian rupa sehingga mudah diuraikan jika Anda ingin mengekstrak nilai RGB secara terprogram.
Memperbarui pubspec.yaml
Sekarang, perbarui bagian bawah pubspec.yaml
untuk menyertakan direktori aset:
pubspec.yaml
flutter:
uses-material-design: true
assets:
- assets/
Jalankan flutter pub get
untuk memuat ulang paket aset.
Membuat penyedia perintah sistem
Buat file baru lib/providers/system_prompt.dart
untuk memuat perintah sistem:
lib/providers/system_prompt.dart
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'system_prompt.g.dart';
@riverpod
Future<String> systemPrompt(Ref ref) =>
rootBundle.loadString('assets/system_prompt.md');
Penyedia ini menggunakan sistem pemuatan aset Flutter untuk membaca file perintah saat runtime.
Mengupdate penyedia model Gemini
Sekarang ubah file lib/providers/gemini.dart
Anda untuk menyertakan perintah sistem:
lib/providers/gemini.dart
import 'dart:async';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_vertexai/firebase_vertexai.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import '../firebase_options.dart';
import 'system_prompt.dart'; // Add this import
part 'gemini.g.dart';
@riverpod
Future<FirebaseApp> firebaseApp(Ref ref) =>
Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
@riverpod
Future<GenerativeModel> geminiModel(Ref ref) async {
await ref.watch(firebaseAppProvider.future);
final systemPrompt = await ref.watch(systemPromptProvider.future); // Add this line
final model = FirebaseVertexAI.instance.generativeModel(
model: 'gemini-2.0-flash',
systemInstruction: Content.system(systemPrompt), // And this line
);
return model;
}
@Riverpod(keepAlive: true)
Future<ChatSession> chatSession(Ref ref) async {
final model = await ref.watch(geminiModelProvider.future);
return model.startChat();
}
Perubahan utama adalah menambahkan systemInstruction: Content.system(systemPrompt)
saat membuat model generatif. Tindakan ini akan memberi tahu Gemini untuk menggunakan petunjuk Anda sebagai perintah sistem untuk semua interaksi dalam sesi chat ini.
Membuat kode Riverpod
Jalankan perintah runner build untuk membuat kode Riverpod yang diperlukan:
dart run build_runner build --delete-conflicting-outputs
Menjalankan dan menguji aplikasi
Sekarang jalankan aplikasi Anda:
flutter run -d DEVICE
Coba uji dengan berbagai deskripsi warna:
- "Saya ingin warna biru langit"
- "Berikan warna hijau hutan"
- "Buat oranye cerah seperti matahari terbenam"
- "Saya ingin warna lavender segar"
- "Tampilkan sesuatu yang seperti biru laut yang dalam"
Anda akan melihat bahwa Gemini kini merespons dengan penjelasan percakapan tentang warna beserta nilai RGB yang diformat secara konsisten. Perintah sistem telah memandu LLM secara efektif untuk memberikan jenis respons yang Anda butuhkan.
Coba juga minta konten di luar konteks warna. Misalnya, penyebab utama Perang Mawar. Anda akan melihat perbedaan dari langkah sebelumnya.
Pentingnya rekayasa perintah untuk tugas khusus
Perintah sistem adalah seni dan ilmu pengetahuan. Ini adalah bagian penting dari integrasi LLM yang dapat secara drastis memengaruhi seberapa berguna model tersebut untuk aplikasi spesifik Anda. Yang telah Anda lakukan di sini adalah bentuk rekayasa perintah - menyesuaikan petunjuk agar model berperilaku dengan cara yang sesuai dengan kebutuhan aplikasi Anda.
Rekayasa perintah yang efektif melibatkan:
- Definisi peran yang jelas: Menetapkan tujuan LLM
- Petunjuk eksplisit: Menjelaskan secara mendetail bagaimana LLM harus merespons
- Contoh konkret: Menunjukkan, bukan hanya memberi tahu, seperti apa respons yang baik
- Penanganan kasus ekstrem: Memberi petunjuk kepada LLM tentang cara menangani skenario yang ambigu
- Spesifikasi pemformatan: Memastikan respons disusun dengan cara yang konsisten dan dapat digunakan
Perintah sistem yang telah Anda buat mengubah kemampuan umum Gemini menjadi asisten penafsiran warna khusus yang memberikan respons yang diformat khusus untuk kebutuhan aplikasi Anda. Ini adalah pola yang efektif yang dapat Anda terapkan ke berbagai domain dan tugas.
Apa langkah selanjutnya?
Pada langkah berikutnya, Anda akan mem-build fondasi ini dengan menambahkan deklarasi fungsi, yang memungkinkan LLM tidak hanya menyarankan nilai RGB, tetapi benar-benar memanggil fungsi di aplikasi Anda untuk menetapkan warna secara langsung. Hal ini menunjukkan bagaimana LLM dapat menjembatani kesenjangan antara bahasa alami dan fitur aplikasi konkret.
Pemecahan masalah
Masalah pemuatan aset
Jika Anda mengalami error saat memuat perintah sistem:
- Pastikan
pubspec.yaml
mencantumkan direktori aset dengan benar - Pastikan jalur di
rootBundle.loadString()
cocok dengan lokasi file Anda - Jalankan
flutter clean
diikuti denganflutter pub get
untuk memuat ulang paket aset
Respons tidak konsisten
Jika LLM tidak konsisten mengikuti petunjuk format Anda:
- Coba buat persyaratan format lebih eksplisit di perintah sistem
- Tambahkan contoh lainnya untuk menunjukkan pola yang diharapkan
- Pastikan format yang Anda minta wajar untuk model
Pembatasan kapasitas API
Jika Anda mengalami error terkait pembatasan kapasitas:
- Perhatikan bahwa layanan Vertex AI memiliki batas penggunaan
- Pertimbangkan untuk menerapkan logika percobaan ulang dengan backoff eksponensial
- Periksa Firebase console Anda untuk mengetahui masalah kuota
Konsep utama yang dipelajari
- Memahami peran dan pentingnya perintah sistem dalam aplikasi LLM
- Membuat perintah yang efektif dengan petunjuk, contoh, dan batasan yang jelas
- Memuat dan menggunakan perintah sistem di aplikasi Flutter
- Membimbing perilaku LLM untuk tugas khusus domain
- Menggunakan rekayasa perintah untuk membentuk respons LLM
Langkah ini menunjukkan cara Anda dapat mencapai penyesuaian perilaku LLM yang signifikan tanpa mengubah kode - cukup dengan memberikan petunjuk yang jelas di perintah sistem.
5. Deklarasi fungsi untuk alat LLM
Pada langkah ini, Anda akan memulai pekerjaan untuk mengaktifkan Gemini agar dapat mengambil tindakan di aplikasi Anda dengan menerapkan deklarasi fungsi. Fitur canggih ini memungkinkan LLM tidak hanya menyarankan nilai RGB, tetapi benar-benar menetapkannya di UI aplikasi Anda melalui panggilan alat khusus. Namun, Anda memerlukan langkah berikutnya untuk melihat permintaan LLM yang dijalankan di aplikasi Flutter.
Yang akan Anda pelajari di langkah ini
- Memahami panggilan fungsi LLM dan manfaatnya untuk aplikasi Flutter
- Menentukan deklarasi fungsi berbasis skema untuk Gemini
- Mengintegrasikan deklarasi fungsi dengan model Gemini
- Memperbarui perintah sistem untuk menggunakan kemampuan alat
Memahami panggilan fungsi
Sebelum menerapkan deklarasi fungsi, mari kita pahami definisinya dan mengapa deklarasi fungsi itu penting:
Apa yang dimaksud dengan pemanggilan fungsi?
Panggilan fungsi (terkadang disebut "penggunaan alat") adalah kemampuan yang memungkinkan LLM untuk:
- Mengenali kapan permintaan pengguna akan mendapatkan manfaat dari pemanggilan fungsi tertentu
- Buat objek JSON terstruktur dengan parameter yang diperlukan untuk fungsi tersebut
- Izinkan aplikasi Anda menjalankan fungsi dengan parameter tersebut
- Menerima hasil fungsi dan menggabungkannya ke dalam responsnya
Panggilan fungsi memungkinkan LLM memicu tindakan konkret dalam aplikasi Anda, bukan hanya menjelaskan apa yang harus dilakukan.
Alasan pentingnya panggilan fungsi untuk aplikasi Flutter
Panggilan fungsi menciptakan jembatan yang kuat antara bahasa alami dan fitur aplikasi:
- Tindakan langsung: Pengguna dapat mendeskripsikan apa yang mereka inginkan dalam bahasa alami, dan aplikasi merespons dengan tindakan konkret
- Output terstruktur: LLM menghasilkan data terstruktur yang bersih, bukan teks yang perlu diuraikan
- Operasi kompleks: Memungkinkan LLM mengakses data eksternal, melakukan penghitungan, atau mengubah status aplikasi
- Pengalaman pengguna yang lebih baik: Menciptakan integrasi yang lancar antara percakapan dan fungsi
Di aplikasi Colorist, panggilan fungsi memungkinkan pengguna mengucapkan "Saya ingin warna hijau hutan" dan membuat UI segera diperbarui dengan warna tersebut, tanpa harus mengurai nilai RGB dari teks.
Menentukan deklarasi fungsi
Buat file baru lib/services/gemini_tools.dart
untuk menentukan deklarasi fungsi Anda:
lib/services/gemini_tools.dart
import 'package:firebase_vertexai/firebase_vertexai.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'gemini_tools.g.dart';
class GeminiTools {
GeminiTools(this.ref);
final Ref ref;
FunctionDeclaration get setColorFuncDecl => FunctionDeclaration(
'set_color',
'Set the color of the display square based on red, green, and blue values.',
parameters: {
'red': Schema.number(description: 'Red component value (0.0 - 1.0)'),
'green': Schema.number(description: 'Green component value (0.0 - 1.0)'),
'blue': Schema.number(description: 'Blue component value (0.0 - 1.0)'),
},
);
List<Tool> get tools => [
Tool.functionDeclarations([setColorFuncDecl]),
];
}
@riverpod
GeminiTools geminiTools(Ref ref) => GeminiTools(ref);
Memahami deklarasi fungsi
Mari kita uraikan fungsi kode ini:
- Penamaan fungsi: Anda memberi nama fungsi
set_color
untuk menunjukkan tujuannya dengan jelas - Deskripsi fungsi: Anda memberikan deskripsi yang jelas yang membantu LLM memahami kapan harus menggunakannya
- Definisi parameter: Anda menentukan parameter terstruktur dengan deskripsinya sendiri:
red
: Komponen merah RGB, yang ditentukan sebagai angka antara 0,0 dan 1,0green
: Komponen hijau RGB, yang ditentukan sebagai angka antara 0,0 dan 1,0blue
: Komponen biru RGB, yang ditentukan sebagai angka antara 0,0 dan 1,0
- Jenis skema: Anda menggunakan
Schema.number()
untuk menunjukkan bahwa ini adalah nilai numerik - Kumpulan alat: Anda membuat daftar alat yang berisi deklarasi fungsi
Pendekatan terstruktur ini membantu LLM Gemini memahami:
- Kapan harus memanggil fungsi ini
- Parameter yang perlu disediakan
- Batasan yang berlaku untuk parameter tersebut (seperti rentang nilai)
Mengupdate penyedia model Gemini
Sekarang, ubah file lib/providers/gemini.dart
untuk menyertakan deklarasi fungsi saat melakukan inisialisasi model Gemini:
lib/providers/gemini.dart
import 'dart:async';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_vertexai/firebase_vertexai.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import '../firebase_options.dart';
import '../services/gemini_tools.dart'; // Add this import
import 'system_prompt.dart';
part 'gemini.g.dart';
@riverpod
Future<FirebaseApp> firebaseApp(Ref ref) =>
Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
@riverpod
Future<GenerativeModel> geminiModel(Ref ref) async {
await ref.watch(firebaseAppProvider.future);
final systemPrompt = await ref.watch(systemPromptProvider.future);
final geminiTools = ref.watch(geminiToolsProvider); // Add this line
final model = FirebaseVertexAI.instance.generativeModel(
model: 'gemini-2.0-flash',
systemInstruction: Content.system(systemPrompt),
tools: geminiTools.tools, // And this line
);
return model;
}
@Riverpod(keepAlive: true)
Future<ChatSession> chatSession(Ref ref) async {
final model = await ref.watch(geminiModelProvider.future);
return model.startChat();
}
Perubahan utamanya adalah menambahkan parameter tools: geminiTools.tools
saat membuat model generatif. Hal ini membuat Gemini mengetahui fungsi yang tersedia untuk dipanggil.
Memperbarui perintah sistem
Sekarang Anda perlu mengubah perintah sistem untuk memberi tahu LLM tentang penggunaan alat set_color
baru. Update assets/system_prompt.md
:
assets/system_prompt.md
# Colorist System Prompt
You are a color expert assistant integrated into a desktop app called Colorist. Your job is to interpret natural language color descriptions and set the appropriate color values using a specialized tool.
## Your Capabilities
You are knowledgeable about colors, color theory, and how to translate natural language descriptions into specific RGB values. You have access to the following tool:
`set_color` - Sets the RGB values for the color display based on a description
## How to Respond to User Inputs
When users describe a color:
1. First, acknowledge their color description with a brief, friendly response
2. Interpret what RGB values would best represent that color description
3. Use the `set_color` tool to set those values (all values should be between 0.0 and 1.0)
4. After setting the color, provide a brief explanation of your interpretation
Example:
User: "I want a sunset orange"
You: "Sunset orange is a warm, vibrant color that captures the golden-red hues of the setting sun. It combines a strong red component with moderate orange tones."
[Then you would call the set_color tool with approximately: red=1.0, green=0.5, blue=0.25]
After the tool call: "I've set a warm orange with strong red, moderate green, and minimal blue components that is reminiscent of the sun low on the horizon."
## When Descriptions are Unclear
If a color description is ambiguous or unclear, please ask the user clarifying questions, one at a time.
## Important Guidelines
- Always keep RGB values between 0.0 and 1.0
- Provide thoughtful, knowledgeable responses about colors
- When possible, include color psychology, associations, or interesting facts about colors
- Be conversational and engaging in your responses
- Focus on being helpful and accurate with your color interpretations
Perubahan utama pada perintah sistem adalah:
- Pengantar alat: Daripada meminta nilai RGB yang diformat, Anda kini memberi tahu LLM tentang alat
set_color
- Proses yang diubah: Anda mengubah langkah 3 dari "memformat nilai dalam respons" menjadi "menggunakan alat untuk menetapkan nilai"
- Contoh yang diperbarui: Anda menunjukkan bagaimana respons harus menyertakan panggilan alat, bukan teks berformat
- Menghapus persyaratan pemformatan: Karena menggunakan panggilan fungsi terstruktur, Anda tidak perlu lagi format teks tertentu
Perintah yang diperbarui ini mengarahkan LLM untuk menggunakan panggilan fungsi, bukan hanya memberikan nilai RGB dalam bentuk teks.
Membuat kode Riverpod
Jalankan perintah runner build untuk membuat kode Riverpod yang diperlukan:
dart run build_runner build --delete-conflicting-outputs
Menjalankan aplikasi
Pada tahap ini, Gemini akan membuat konten yang mencoba menggunakan panggilan fungsi, tetapi Anda belum menerapkan pengendali untuk panggilan fungsi. Saat menjalankan aplikasi dan mendeskripsikan warna, Anda akan melihat Gemini merespons seolah-olah telah memanggil alat, tetapi Anda tidak akan melihat perubahan warna apa pun di UI hingga langkah berikutnya.
Jalankan aplikasi Anda:
flutter run -d DEVICE
Coba deskripsikan warna seperti "deep ocean blue" atau "forest green" dan amati responsnya. LLM mencoba memanggil fungsi yang ditentukan di atas, tetapi kode Anda belum mendeteksi panggilan fungsi.
Proses pemanggilan fungsi
Mari kita pahami apa yang terjadi saat Gemini menggunakan panggilan fungsi:
- Pemilihan fungsi: LLM memutuskan apakah panggilan fungsi akan berguna berdasarkan permintaan pengguna
- Pembuatan parameter: LLM menghasilkan parameter value yang sesuai dengan skema fungsi
- Format panggilan fungsi: LLM mengirimkan objek panggilan fungsi terstruktur dalam responsnya
- Penanganan aplikasi: Aplikasi Anda akan menerima panggilan ini dan menjalankan fungsi yang relevan (diterapkan pada langkah berikutnya)
- Integrasi respons: Dalam percakapan multi-giliran, LLM mengharapkan hasil fungsi ditampilkan
Dalam status aplikasi saat ini, tiga langkah pertama terjadi, tetapi Anda belum menerapkan langkah 4 atau 5 (menangani panggilan fungsi), yang akan Anda lakukan di langkah berikutnya.
Detail teknis: Cara Gemini memutuskan kapan harus menggunakan fungsi
Gemini membuat keputusan cerdas tentang kapan harus menggunakan fungsi berdasarkan:
- Intent pengguna: Apakah permintaan pengguna akan ditayangkan dengan sebaik mungkin oleh fungsi
- Relevansi fungsi: Seberapa baik fungsi yang tersedia cocok dengan tugas
- Ketersediaan parameter: Apakah parameter dapat menentukan nilai parameter dengan yakin
- Petunjuk sistem: Panduan dari perintah sistem tentang penggunaan fungsi
Dengan memberikan deklarasi fungsi dan petunjuk sistem yang jelas, Anda telah menyiapkan Gemini untuk mengenali permintaan deskripsi warna sebagai peluang untuk memanggil fungsi set_color
.
Apa langkah selanjutnya?
Pada langkah berikutnya, Anda akan menerapkan pengendali untuk panggilan fungsi yang berasal dari Gemini. Tindakan ini akan melengkapi lingkaran, sehingga deskripsi pengguna dapat memicu perubahan warna yang sebenarnya di UI melalui panggilan fungsi LLM.
Pemecahan masalah
Masalah deklarasi fungsi
Jika Anda mengalami error dengan deklarasi fungsi:
- Pastikan nama dan jenis parameter cocok dengan yang diharapkan
- Pastikan nama fungsi jelas dan deskriptif
- Pastikan deskripsi fungsi menjelaskan tujuannya secara akurat
Masalah perintah sistem
Jika LLM tidak mencoba menggunakan fungsi:
- Pastikan perintah sistem Anda dengan jelas menginstruksikan LLM untuk menggunakan alat
set_color
- Pastikan contoh di perintah sistem menunjukkan penggunaan fungsi
- Coba buat petunjuk untuk menggunakan alat ini lebih eksplisit
Masalah umum
Jika Anda mengalami masalah lain:
- Periksa konsol untuk menemukan error yang terkait dengan deklarasi fungsi
- Memverifikasi bahwa alat diteruskan dengan benar ke model
- Memastikan semua kode yang dihasilkan Riverpod sudah yang terbaru
Konsep utama yang dipelajari
- Menentukan deklarasi fungsi untuk memperluas kemampuan LLM di aplikasi Flutter
- Membuat skema parameter untuk pengumpulan data terstruktur
- Mengintegrasikan deklarasi fungsi dengan model Gemini
- Memperbarui perintah sistem untuk mendorong penggunaan fungsi
- Memahami cara LLM memilih dan memanggil fungsi
Langkah ini menunjukkan bagaimana LLM dapat menjembatani kesenjangan antara input bahasa alami dan panggilan fungsi terstruktur, yang meletakkan dasar untuk integrasi yang lancar antara fitur percakapan dan aplikasi.
6. Menerapkan penanganan alat
Pada langkah ini, Anda akan menerapkan pengendali untuk panggilan fungsi yang berasal dari Gemini. Hal ini melengkapi lingkaran komunikasi antara input bahasa alami dan fitur aplikasi konkret, sehingga LLM dapat langsung memanipulasi UI Anda berdasarkan deskripsi pengguna.
Yang akan Anda pelajari di langkah ini
- Memahami pipeline panggilan fungsi lengkap di aplikasi LLM
- Memproses panggilan fungsi dari Gemini di aplikasi Flutter
- Mengimplementasikan pengendali fungsi yang mengubah status aplikasi
- Menangani respons fungsi dan menampilkan hasil ke LLM
- Membuat alur komunikasi lengkap antara LLM dan UI
- Mencatat panggilan dan respons fungsi untuk transparansi
Memahami pipeline panggilan fungsi
Sebelum mempelajari implementasi, mari kita pahami pipeline panggilan fungsi lengkap:
Alur menyeluruh
- Input pengguna: Pengguna mendeskripsikan warna dalam bahasa alami (misalnya, "forest green")
- Pemrosesan LLM: Gemini menganalisis deskripsi dan memutuskan untuk memanggil fungsi
set_color
- Pembuatan panggilan fungsi: Gemini membuat JSON terstruktur dengan parameter (nilai merah, hijau, biru)
- Penerimaan panggilan fungsi: Aplikasi Anda menerima data terstruktur ini dari Gemini
- Eksekusi fungsi: Aplikasi Anda mengeksekusi fungsi dengan parameter yang disediakan
- Pembaruan status: Fungsi ini memperbarui status aplikasi Anda (mengubah warna yang ditampilkan)
- Pembuatan respons: Fungsi Anda menampilkan hasil kembali ke LLM
- Penggabungan respons: LLM menggabungkan hasil ini ke dalam respons akhirnya
- Pembaruan UI: UI Anda bereaksi terhadap perubahan status, menampilkan warna baru
Siklus komunikasi yang lengkap sangat penting untuk integrasi LLM yang tepat. Saat membuat panggilan fungsi, LLM tidak hanya mengirimkan permintaan dan melanjutkan. Sebagai gantinya, fungsi ini menunggu aplikasi Anda mengeksekusi fungsi dan menampilkan hasil. LLM kemudian menggunakan hasil ini untuk merumuskan respons akhirnya, sehingga menciptakan alur percakapan yang alami yang mengonfirmasi tindakan yang diambil.
Menerapkan pengendali fungsi
Mari kita perbarui file lib/services/gemini_tools.dart
untuk menambahkan pengendali panggilan fungsi:
lib/services/gemini_tools.dart
import 'package:colorist_ui/colorist_ui.dart';
import 'package:firebase_vertexai/firebase_vertexai.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'gemini_tools.g.dart';
class GeminiTools {
GeminiTools(this.ref);
final Ref ref;
FunctionDeclaration get setColorFuncDecl => FunctionDeclaration(
'set_color',
'Set the color of the display square based on red, green, and blue values.',
parameters: {
'red': Schema.number(description: 'Red component value (0.0 - 1.0)'),
'green': Schema.number(description: 'Green component value (0.0 - 1.0)'),
'blue': Schema.number(description: 'Blue component value (0.0 - 1.0)'),
},
);
List<Tool> get tools => [
Tool.functionDeclarations([setColorFuncDecl]),
];
Map<String, Object?> handleFunctionCall( // Add from here
String functionName,
Map<String, Object?> arguments,
) {
final logStateNotifier = ref.read(logStateNotifierProvider.notifier);
logStateNotifier.logFunctionCall(functionName, arguments);
return switch (functionName) {
'set_color' => handleSetColor(arguments),
_ => handleUnknownFunction(functionName),
};
}
Map<String, Object?> handleSetColor(Map<String, Object?> arguments) {
final colorStateNotifier = ref.read(colorStateNotifierProvider.notifier);
final red = (arguments['red'] as num).toDouble();
final green = (arguments['green'] as num).toDouble();
final blue = (arguments['blue'] as num).toDouble();
final functionResults = {
'success': true,
'current_color': colorStateNotifier
.updateColor(red: red, green: green, blue: blue)
.toLLMContextMap(),
};
final logStateNotifier = ref.read(logStateNotifierProvider.notifier);
logStateNotifier.logFunctionResults(functionResults);
return functionResults;
}
Map<String, Object?> handleUnknownFunction(String functionName) {
final logStateNotifier = ref.read(logStateNotifierProvider.notifier);
logStateNotifier.logWarning('Unsupported function call $functionName');
return {
'success': false,
'reason': 'Unsupported function call $functionName',
};
} // To here.
}
@riverpod
GeminiTools geminiTools(Ref ref) => GeminiTools(ref);
Memahami pengendali fungsi
Mari kita uraikan apa yang dilakukan pengendali fungsi ini:
handleFunctionCall
: Pengirim pusat yang:- Mencatat panggilan fungsi untuk transparansi di panel log
- Merutekan ke pengendali yang sesuai berdasarkan nama fungsi
- Menampilkan respons terstruktur yang akan dikirim kembali ke LLM
handleSetColor
: Pengendali spesifik untuk fungsiset_color
Anda yang:- Mengekstrak nilai RGB dari peta argumen
- Mengonversinya ke jenis yang diharapkan (ganda)
- Memperbarui status warna aplikasi menggunakan
colorStateNotifier
- Membuat respons terstruktur dengan status berhasil dan informasi warna saat ini
- Mencatat hasil fungsi ke dalam log untuk proses debug
handleUnknownFunction
: Pengendali penggantian untuk fungsi yang tidak diketahui yang:- Mencatat peringatan tentang fungsi yang tidak didukung
- Menampilkan respons error ke LLM
Fungsi handleSetColor
sangat penting karena menjembatani kesenjangan antara pemahaman bahasa alami LLM dan perubahan UI konkret.
Memperbarui layanan chat Gemini untuk memproses panggilan dan respons fungsi
Sekarang, mari kita perbarui file lib/services/gemini_chat_service.dart
untuk memproses panggilan fungsi dari respons LLM dan mengirim hasilnya kembali ke LLM:
lib/services/gemini_chat_service.dart
import 'dart:async';
import 'package:colorist_ui/colorist_ui.dart';
import 'package:firebase_vertexai/firebase_vertexai.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import '../providers/gemini.dart';
import 'gemini_tools.dart'; // Add this import
part 'gemini_chat_service.g.dart';
class GeminiChatService {
GeminiChatService(this.ref);
final Ref ref;
Future<void> sendMessage(String message) async {
final chatSession = await ref.read(chatSessionProvider.future);
final chatStateNotifier = ref.read(chatStateNotifierProvider.notifier);
final logStateNotifier = ref.read(logStateNotifierProvider.notifier);
chatStateNotifier.addUserMessage(message);
logStateNotifier.logUserText(message);
final llmMessage = chatStateNotifier.createLlmMessage();
try {
final response = await chatSession.sendMessage(Content.text(message));
final responseText = response.text;
if (responseText != null) {
logStateNotifier.logLlmText(responseText);
chatStateNotifier.appendToMessage(llmMessage.id, responseText);
}
if (response.functionCalls.isNotEmpty) { // Add from here
final geminiTools = ref.read(geminiToolsProvider);
final functionResultResponse = await chatSession.sendMessage(
Content.functionResponses([
for (final functionCall in response.functionCalls)
FunctionResponse(
functionCall.name,
geminiTools.handleFunctionCall(
functionCall.name,
functionCall.args,
),
),
]),
);
final responseText = functionResultResponse.text;
if (responseText != null) {
logStateNotifier.logLlmText(responseText);
chatStateNotifier.appendToMessage(llmMessage.id, responseText);
}
} // To here.
} catch (e, st) {
logStateNotifier.logError(e, st: st);
chatStateNotifier.appendToMessage(
llmMessage.id,
"\nI'm sorry, I encountered an error processing your request. "
"Please try again.",
);
} finally {
chatStateNotifier.finalizeMessage(llmMessage.id);
}
}
}
@riverpod
GeminiChatService geminiChatService(Ref ref) => GeminiChatService(ref);
Memahami alur komunikasi
Penambahan utama di sini adalah penanganan lengkap panggilan dan respons fungsi:
if (response.functionCalls.isNotEmpty) {
final geminiTools = ref.read(geminiToolsProvider);
final functionResultResponse = await chatSession.sendMessage(
Content.functionResponses([
for (final functionCall in response.functionCalls)
FunctionResponse(
functionCall.name,
geminiTools.handleFunctionCall(
functionCall.name,
functionCall.args,
),
),
]),
);
final responseText = functionResultResponse.text;
if (responseText != null) {
logStateNotifier.logLlmText(responseText);
chatStateNotifier.appendToMessage(llmMessage.id, responseText);
}
}
Kode ini:
- Memeriksa apakah respons LLM berisi panggilan fungsi
- Untuk setiap panggilan fungsi, panggil metode
handleFunctionCall
Anda dengan nama dan argumen fungsi - Mengumpulkan hasil setiap panggilan fungsi
- Mengirimkan hasil ini kembali ke LLM menggunakan
Content.functionResponses
- Memproses respons LLM terhadap hasil fungsi
- Memperbarui UI dengan teks respons akhir
Tindakan ini akan membuat alur bolak-balik:
- Pengguna → LLM: Meminta warna
- LLM → Aplikasi: Panggilan fungsi dengan parameter
- Aplikasi → Pengguna: Warna baru ditampilkan
- Aplikasi → LLM: Hasil fungsi
- LLM → Pengguna: Respons akhir yang menggabungkan hasil fungsi
Membuat kode Riverpod
Jalankan perintah runner build untuk membuat kode Riverpod yang diperlukan:
dart run build_runner build --delete-conflicting-outputs
Menjalankan dan menguji alur lengkap
Sekarang jalankan aplikasi Anda:
flutter run -d DEVICE
Coba masukkan berbagai deskripsi warna:
- "Saya ingin warna merah tua"
- "Tampilkan warna biru langit yang menenangkan"
- "Beri tahu saya warna daun mint segar"
- "Saya ingin melihat warna oranye hangat saat matahari terbenam"
- "Buat warnanya menjadi ungu kerajaan yang kaya"
Sekarang Anda akan melihat:
- Pesan Anda yang muncul di antarmuka chat
- Respons Gemini yang muncul di chat
- Panggilan fungsi yang dicatat di panel log
- Hasil fungsi dicatat dalam log segera setelah
- Pembaruan persegi panjang warna untuk menampilkan warna yang dijelaskan
- Nilai RGB diperbarui untuk menampilkan komponen warna baru
- Respons akhir Gemini muncul, sering kali mengomentari warna yang ditetapkan
Panel log memberikan insight tentang apa yang terjadi di balik layar. Anda akan melihat:
- Panggilan fungsi yang tepat yang dilakukan Gemini
- Parameter yang dipilih untuk setiap nilai RGB
- Hasil yang ditampilkan fungsi Anda
- Respons lanjutan dari Gemini
Notifikasi status warna
colorStateNotifier
yang Anda gunakan untuk memperbarui warna adalah bagian dari paket colorist_ui
. API ini mengelola:
- Warna saat ini yang ditampilkan di UI
- Histori warna (10 warna terakhir)
- Notifikasi perubahan status ke komponen UI
Saat Anda memanggil updateColor
dengan nilai RGB baru, tindakan ini akan:
- Membuat objek
ColorData
baru dengan nilai yang diberikan - Memperbarui warna saat ini dalam status aplikasi
- Menambahkan warna ke histori
- Memicu update UI melalui pengelolaan status Riverpod
Komponen UI dalam paket colorist_ui
memantau status ini dan otomatis diperbarui saat berubah, sehingga menciptakan pengalaman reaktif.
Memahami penanganan error
Penerapan Anda mencakup penanganan error yang andal:
- Blok try-catch: Menggabungkan semua interaksi LLM untuk menangkap pengecualian
- Pembuatan log error: Mencatat error di panel log dengan pelacakan tumpukan
- Masukan pengguna: Memberikan pesan error yang mudah dipahami di chat
- Pembersihan status: Menyelesaikan status pesan meskipun terjadi error
Hal ini memastikan aplikasi tetap stabil dan memberikan masukan yang sesuai meskipun terjadi masalah dengan layanan atau eksekusi fungsi LLM.
Manfaat panggilan fungsi untuk pengalaman pengguna
Yang telah Anda capai di sini menunjukkan bagaimana LLM dapat membuat antarmuka alami yang efektif:
- Antarmuka natural language: Pengguna mengekspresikan intent dalam bahasa sehari-hari
- Penafsiran cerdas: LLM menerjemahkan deskripsi yang tidak jelas menjadi nilai yang tepat
- Manipulasi langsung: UI diperbarui sebagai respons terhadap bahasa alami
- Respons kontekstual: LLM memberikan konteks percakapan tentang perubahan
- Beban kognitif rendah: Pengguna tidak perlu memahami nilai RGB atau teori warna
Pola penggunaan panggilan fungsi LLM ini untuk menjembatani natural language dan tindakan UI dapat diperluas ke banyak domain lain di luar pemilihan warna.
Apa langkah selanjutnya?
Pada langkah berikutnya, Anda akan meningkatkan pengalaman pengguna dengan menerapkan respons streaming. Daripada menunggu respons lengkap, Anda akan memproses potongan teks dan panggilan fungsi saat diterima, sehingga membuat aplikasi yang lebih responsif dan menarik.
Pemecahan masalah
Masalah panggilan fungsi
Jika Gemini tidak memanggil fungsi atau parameter Anda salah:
- Memastikan deklarasi fungsi Anda cocok dengan yang dijelaskan di perintah sistem
- Pastikan nama dan jenis parameter konsisten
- Pastikan perintah sistem Anda secara eksplisit menginstruksikan LLM untuk menggunakan alat tersebut
- Pastikan nama fungsi di pengendali Anda sama persis dengan yang ada dalam deklarasi
- Periksa panel log untuk mengetahui informasi mendetail tentang panggilan fungsi
Masalah respons fungsi
Jika hasil fungsi tidak dikirim kembali dengan benar ke LLM:
- Pastikan fungsi Anda menampilkan Peta yang diformat dengan benar
- Pastikan Content.functionResponses dibuat dengan benar
- Cari error apa pun dalam log yang terkait dengan respons fungsi
- Pastikan Anda menggunakan sesi chat yang sama untuk respons
Masalah tampilan warna
Jika warna tidak ditampilkan dengan benar:
- Memastikan nilai RGB dikonversi dengan benar ke bilangan ganda (LLM mungkin mengirimkannya sebagai bilangan bulat)
- Pastikan nilai berada dalam rentang yang diharapkan (0,0 hingga 1,0)
- Pastikan notifikasi status warna dipanggil dengan benar
- Periksa log untuk mengetahui nilai persis yang diteruskan ke fungsi
Masalah umum
Untuk masalah umum:
- Periksa log untuk menemukan error atau peringatan
- Memverifikasi konektivitas Vertex AI in Firebase
- Memeriksa ketidakcocokan jenis dalam parameter fungsi
- Memastikan semua kode yang dihasilkan Riverpod sudah yang terbaru
Konsep utama yang dipelajari
- Menerapkan pipeline panggilan fungsi lengkap di Flutter
- Membuat komunikasi penuh antara LLM dan aplikasi Anda
- Memproses data terstruktur dari respons LLM
- Mengirim hasil fungsi kembali ke LLM untuk digabungkan ke dalam respons
- Menggunakan panel log untuk mendapatkan visibilitas ke interaksi aplikasi LLM
- Menghubungkan input bahasa alami ke perubahan UI yang konkret
Setelah langkah ini selesai, aplikasi Anda kini menunjukkan salah satu pola paling canggih untuk integrasi LLM: menerjemahkan input bahasa alami menjadi tindakan UI yang konkret, sekaligus mempertahankan percakapan yang koheren yang mengonfirmasi tindakan ini. Hal ini menciptakan antarmuka percakapan yang intuitif dan terasa ajaib bagi pengguna.
7. Respons streaming untuk UX yang lebih baik
Pada langkah ini, Anda akan meningkatkan pengalaman pengguna dengan menerapkan respons streaming dari Gemini. Daripada menunggu seluruh respons dibuat, Anda akan memproses potongan teks dan panggilan fungsi saat diterima, sehingga membuat aplikasi yang lebih responsif dan menarik.
Yang akan Anda bahas dalam langkah ini
- Pentingnya streaming untuk aplikasi yang didukung LLM
- Mengimplementasikan respons LLM streaming di aplikasi Flutter
- Memproses potongan teks sebagian saat tiba dari API
- Mengelola status percakapan untuk mencegah konflik pesan
- Menangani panggilan fungsi dalam respons streaming
- Membuat indikator visual untuk respons yang sedang berlangsung
Alasan streaming penting untuk aplikasi LLM
Sebelum menerapkan, mari kita pahami mengapa respons streaming sangat penting untuk menciptakan pengalaman pengguna yang luar biasa dengan LLM:
Pengalaman pengguna yang ditingkatkan
Respons streaming memberikan beberapa manfaat pengalaman pengguna yang signifikan:
- Mengurangi latensi yang dirasakan: Pengguna melihat teks mulai langsung muncul (biasanya dalam waktu 100-300 md), bukan menunggu beberapa detik untuk mendapatkan respons lengkap. Persepsi tentang kedekatan ini secara dramatis meningkatkan kepuasan pengguna.
- Ritme percakapan yang alami: Tampilan teks yang bertahap meniru cara manusia berkomunikasi, sehingga menciptakan pengalaman dialog yang lebih alami.
- Pemrosesan informasi progresif: Pengguna dapat mulai memproses informasi saat informasi tersebut diterima, bukan dikejutkan dengan blok teks yang besar sekaligus.
- Peluang untuk gangguan awal: Dalam aplikasi lengkap, pengguna berpotensi mengganggu atau mengalihkan LLM jika mereka melihatnya mengarah ke arah yang tidak membantu.
- Konfirmasi visual aktivitas: Teks streaming memberikan masukan langsung bahwa sistem berfungsi, sehingga mengurangi ketidakpastian.
Keunggulan teknis
Selain peningkatan UX, streaming menawarkan manfaat teknis:
- Eksekusi fungsi awal: Panggilan fungsi dapat dideteksi dan dieksekusi segera setelah muncul di aliran data, tanpa menunggu respons lengkap.
- Pembaruan UI inkremental: Anda dapat memperbarui UI secara bertahap saat informasi baru tiba, sehingga menciptakan pengalaman yang lebih dinamis.
- Pengelolaan status percakapan: Streaming memberikan sinyal yang jelas tentang kapan respons selesai vs. masih dalam proses, sehingga memungkinkan pengelolaan status yang lebih baik.
- Risiko waktu tunggu yang berkurang: Dengan respons non-streaming, pembuatan yang berjalan lama berisiko mengalami waktu tunggu koneksi. Streaming membuat koneksi lebih awal dan mempertahankannya.
Untuk aplikasi Colorist, menerapkan streaming berarti pengguna akan melihat respons teks dan perubahan warna muncul lebih cepat, sehingga menciptakan pengalaman yang jauh lebih responsif.
Menambahkan pengelolaan status percakapan
Pertama, mari kita tambahkan penyedia status untuk melacak apakah aplikasi saat ini menangani respons streaming. Update file lib/services/gemini_chat_service.dart
Anda:
lib/services/gemini_chat_service.dart
import 'dart:async';
import 'package:colorist_ui/colorist_ui.dart';
import 'package:firebase_vertexai/firebase_vertexai.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import '../providers/gemini.dart';
import 'gemini_tools.dart';
part 'gemini_chat_service.g.dart';
final conversationStateProvider = StateProvider( // Add from here...
(ref) => ConversationState.idle,
); // To here.
class GeminiChatService {
GeminiChatService(this.ref);
final Ref ref;
Future<void> sendMessage(String message) async {
final chatSession = await ref.read(chatSessionProvider.future);
final conversationState = ref.read(conversationStateProvider); // Add this line
final chatStateNotifier = ref.read(chatStateNotifierProvider.notifier);
final logStateNotifier = ref.read(logStateNotifierProvider.notifier);
if (conversationState == ConversationState.busy) { // Add from here...
logStateNotifier.logWarning(
"Can't send a message while a conversation is in progress",
);
throw Exception(
"Can't send a message while a conversation is in progress",
);
}
final conversationStateNotifier = ref.read(
conversationStateProvider.notifier,
);
conversationStateNotifier.state = ConversationState.busy; // To here.
chatStateNotifier.addUserMessage(message);
logStateNotifier.logUserText(message);
final llmMessage = chatStateNotifier.createLlmMessage();
try { // Modify from here...
final responseStream = chatSession.sendMessageStream(
Content.text(message),
);
await for (final block in responseStream) {
await _processBlock(block, llmMessage.id);
} // To here.
} catch (e, st) {
logStateNotifier.logError(e, st: st);
chatStateNotifier.appendToMessage(
llmMessage.id,
"\nI'm sorry, I encountered an error processing your request. "
"Please try again.",
);
} finally {
chatStateNotifier.finalizeMessage(llmMessage.id);
conversationStateNotifier.state = ConversationState.idle; // Add this line.
}
}
Future<void> _processBlock( // Add from here...
GenerateContentResponse block,
String llmMessageId,
) async {
final chatSession = await ref.read(chatSessionProvider.future);
final chatStateNotifier = ref.read(chatStateNotifierProvider.notifier);
final logStateNotifier = ref.read(logStateNotifierProvider.notifier);
final blockText = block.text;
if (blockText != null) {
logStateNotifier.logLlmText(blockText);
chatStateNotifier.appendToMessage(llmMessageId, blockText);
}
if (block.functionCalls.isNotEmpty) {
final geminiTools = ref.read(geminiToolsProvider);
final responseStream = chatSession.sendMessageStream(
Content.functionResponses([
for (final functionCall in block.functionCalls)
FunctionResponse(
functionCall.name,
geminiTools.handleFunctionCall(
functionCall.name,
functionCall.args,
),
),
]),
);
await for (final response in responseStream) {
final responseText = response.text;
if (responseText != null) {
logStateNotifier.logLlmText(responseText);
chatStateNotifier.appendToMessage(llmMessageId, responseText);
}
}
}
} // To here.
}
@riverpod
GeminiChatService geminiChatService(Ref ref) => GeminiChatService(ref);
Memahami penerapan streaming
Mari kita uraikan fungsi kode ini:
- Pelacakan status percakapan:
conversationStateProvider
melacak apakah aplikasi saat ini sedang memproses respons- Status bertransisi dari
idle
→busy
saat memproses, lalu kembali keidle
- Hal ini mencegah beberapa permintaan serentak yang dapat menimbulkan konflik
- Inisialisasi streaming:
sendMessageStream()
menampilkan Stream potongan respons, bukanFuture
dengan respons lengkap- Setiap bagian dapat berisi teks, panggilan fungsi, atau keduanya
- Pemrosesan progresif:
await for
memproses setiap bagian saat tiba secara real time- Teks langsung ditambahkan ke UI, sehingga menciptakan efek streaming
- Panggilan fungsi dieksekusi segera setelah terdeteksi
- Penanganan panggilan fungsi:
- Saat panggilan fungsi terdeteksi dalam bagian, panggilan tersebut akan langsung dieksekusi
- Hasil dikirim kembali ke LLM melalui panggilan streaming lain
- Respons LLM terhadap hasil ini juga diproses dengan cara streaming
- Penanganan dan pembersihan error:
try
/catch
memberikan penanganan error yang andal- Blok
finally
memastikan status percakapan direset dengan benar - Pesan selalu diselesaikan, meskipun terjadi error
Implementasi ini menciptakan pengalaman streaming yang responsif dan andal sekaligus mempertahankan status percakapan yang tepat.
Memperbarui layar utama untuk menghubungkan status percakapan
Ubah file lib/main.dart
untuk meneruskan status percakapan ke layar utama:
lib/main.dart
import 'package:colorist_ui/colorist_ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'providers/gemini.dart';
import 'services/gemini_chat_service.dart';
void main() async {
runApp(ProviderScope(child: MainApp()));
}
class MainApp extends ConsumerWidget {
const MainApp({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final model = ref.watch(geminiModelProvider);
final conversationState = ref.watch(conversationStateProvider); // Add this line
return MaterialApp(
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
),
home: model.when(
data: (data) => MainScreen(
conversationState: conversationState, // And this line
sendMessage: (text) {
ref.read(geminiChatServiceProvider).sendMessage(text);
},
),
loading: () => LoadingScreen(message: 'Initializing Gemini Model'),
error: (err, st) => ErrorScreen(error: err),
),
);
}
}
Perubahan utama di sini adalah meneruskan conversationState
ke widget MainScreen
. MainScreen
(disediakan oleh paket colorist_ui
) akan menggunakan status ini untuk menonaktifkan input teks saat respons sedang diproses.
Hal ini menciptakan pengalaman pengguna yang kohesif dengan UI yang mencerminkan status percakapan saat ini.
Membuat kode Riverpod
Jalankan perintah runner build untuk membuat kode Riverpod yang diperlukan:
dart run build_runner build --delete-conflicting-outputs
Menjalankan dan menguji respons streaming
Jalankan aplikasi Anda:
flutter run -d DEVICE
Sekarang, coba uji perilaku streaming dengan berbagai deskripsi warna. Coba deskripsi seperti:
- "Tampilkan warna biru kehijauan tua lautan saat senja"
- "Saya ingin melihat karang yang cerah dan mengingatkan saya pada bunga tropis"
- "Buat warna hijau zaitun yang redup seperti seragam tentara lama"
Alur teknis streaming secara mendetail
Mari kita pelajari apa yang sebenarnya terjadi saat melakukan streaming respons:
Pembentukan koneksi
Saat Anda memanggil sendMessageStream()
, hal berikut akan terjadi:
- Aplikasi membuat koneksi ke layanan Vertex AI
- Permintaan pengguna dikirim ke layanan
- Server mulai memproses permintaan
- Koneksi streaming tetap terbuka, siap untuk mengirimkan potongan
Transmisi bagian
Saat Gemini membuat konten, potongan dikirim melalui streaming:
- Server mengirimkan potongan teks saat dibuat (biasanya beberapa kata atau kalimat)
- Saat memutuskan untuk melakukan panggilan fungsi, Gemini akan mengirimkan informasi panggilan fungsi
- Potongan teks tambahan dapat mengikuti panggilan fungsi
- Streaming berlanjut hingga pembuatan selesai
Pemrosesan progresif
Aplikasi Anda memproses setiap bagian secara bertahap:
- Setiap bagian teks ditambahkan ke respons yang ada
- Panggilan fungsi dieksekusi segera setelah terdeteksi
- UI diperbarui secara real-time dengan hasil teks dan fungsi
- Status dilacak untuk menunjukkan bahwa respons masih di-streaming
Penyelesaian streaming
Setelah pembuatan selesai:
- Streaming ditutup oleh server
- Loop
await for
Anda keluar secara alami - Pesan ditandai sebagai selesai
- Status percakapan ditetapkan kembali ke tidak ada aktivitas
- UI diperbarui untuk mencerminkan status selesai
Perbandingan streaming vs. non-streaming
Untuk lebih memahami manfaat streaming, mari kita bandingkan pendekatan streaming vs. non-streaming:
Aspek | Non-Streaming | Streaming |
Latensi yang dirasakan | Pengguna tidak melihat apa pun hingga respons lengkap siap | Pengguna melihat kata pertama dalam milidetik |
Pengalaman pengguna | Waktu tunggu yang lama diikuti dengan kemunculan teks secara tiba-tiba | Tampilan teks progresif yang alami |
Pengelolaan status | Lebih sederhana (pesan tertunda atau selesai) | Lebih kompleks (pesan dapat berada dalam status streaming) |
Eksekusi fungsi | Hanya terjadi setelah respons lengkap | Terjadi selama pembuatan respons |
Kompleksitas penerapan | Lebih mudah diterapkan | Memerlukan pengelolaan status tambahan |
Pemulihan dari error | Respons semua atau tidak sama sekali | Respons sebagian mungkin masih berguna |
Kompleksitas kode | Lebih sederhana | Lebih kompleks karena penanganan streaming |
Untuk aplikasi seperti Colorist, manfaat UX dari streaming lebih besar daripada kompleksitas penerapan, terutama untuk interpretasi warna yang mungkin memerlukan waktu beberapa detik untuk dibuat.
Praktik terbaik untuk UX streaming
Saat menerapkan streaming di aplikasi LLM Anda sendiri, pertimbangkan praktik terbaik berikut:
- Indikator visual yang jelas: Selalu berikan isyarat visual yang jelas yang membedakan pesan streaming vs. pesan lengkap
- Pemblokiran input: Menonaktifkan input pengguna selama streaming untuk mencegah beberapa permintaan yang tumpang-tindih
- Pemulihan error: Desain UI Anda untuk menangani pemulihan yang halus jika streaming terganggu
- Transisi status: Memastikan transisi yang lancar antara status tidak ada aktivitas, streaming, dan selesai
- Visualisasi progres: Pertimbangkan animasi atau indikator halus yang menunjukkan pemrosesan aktif
- Opsi pembatalan: Dalam aplikasi lengkap, berikan cara bagi pengguna untuk membatalkan pembuatan yang sedang berlangsung
- Integrasi hasil fungsi: Mendesain UI untuk menangani hasil fungsi yang muncul di tengah streaming
- Pengoptimalan performa: Meminimalkan pembuatan ulang UI selama pembaruan streaming yang cepat
Paket colorist_ui
menerapkan banyak praktik terbaik ini untuk Anda, tetapi praktik ini merupakan pertimbangan penting untuk setiap penerapan LLM streaming.
Apa langkah selanjutnya?
Pada langkah berikutnya, Anda akan menerapkan sinkronisasi LLM dengan memberi tahu Gemini saat pengguna memilih warna dari histori. Hal ini akan menciptakan pengalaman yang lebih kohesif dengan LLM yang mengetahui perubahan yang dimulai pengguna pada status aplikasi.
Pemecahan masalah
Masalah stream processing
Jika Anda mengalami masalah dengan pemrosesan streaming:
- Gejala: Respons sebagian, teks tidak ada, atau penghentian streaming secara tiba-tiba
- Solusi: Periksa konektivitas jaringan dan pastikan pola asinkron/menunggu yang tepat dalam kode Anda
- Diagnosis: Periksa panel log untuk menemukan pesan error atau peringatan yang terkait dengan pemrosesan streaming
- Perbaikan: Memastikan semua pemrosesan streaming menggunakan penanganan error yang tepat dengan blok
try
/catch
Panggilan fungsi tidak ada
Jika panggilan fungsi tidak terdeteksi di aliran data:
- Gejala: Teks muncul, tetapi warna tidak diperbarui, atau log tidak menampilkan panggilan fungsi
- Solusi: Verifikasi petunjuk perintah sistem tentang penggunaan panggilan fungsi
- Diagnosis: Periksa panel log untuk melihat apakah panggilan fungsi diterima
- Perbaikan: Sesuaikan perintah sistem Anda untuk secara lebih eksplisit menginstruksikan LLM agar menggunakan alat
set_color
Penanganan error umum
Untuk masalah lainnya:
- Langkah 1: Periksa panel log untuk menemukan pesan error
- Langkah 2: Verifikasi Vertex AI dalam konektivitas Firebase
- Langkah 3: Pastikan semua kode yang dihasilkan Riverpod sudah yang terbaru
- Langkah 4: Tinjau penerapan streaming untuk menemukan pernyataan await yang hilang
Konsep utama yang dipelajari
- Mengimplementasikan respons streaming dengan Gemini API untuk UX yang lebih responsif
- Mengelola status percakapan untuk menangani interaksi streaming dengan benar
- Memproses panggilan fungsi dan teks real-time saat diterima
- Membuat UI responsif yang diperbarui secara bertahap selama streaming
- Menangani streaming serentak dengan pola asinkron yang tepat
- Memberikan respons visual yang sesuai selama respons streaming
Dengan menerapkan streaming, Anda telah meningkatkan pengalaman pengguna aplikasi Colorist secara signifikan, sehingga menciptakan antarmuka yang lebih responsif dan menarik yang benar-benar terasa seperti percakapan.
8. Sinkronisasi Konteks LLM
Pada langkah bonus ini, Anda akan menerapkan Sinkronisasi Konteks LLM dengan memberi tahu Gemini saat pengguna memilih warna dari histori. Hal ini menciptakan pengalaman yang lebih kohesif, yaitu LLM mengetahui tindakan pengguna di antarmuka, bukan hanya pesan eksplisit mereka.
Yang akan Anda bahas dalam langkah ini
- Membuat Sinkronisasi Konteks LLM antara UI dan LLM
- Membuat serialisasi peristiwa UI ke dalam konteks yang dapat dipahami LLM
- Memperbarui konteks percakapan berdasarkan tindakan pengguna
- Membuat pengalaman yang koheren di berbagai metode interaksi
- Meningkatkan kesadaran konteks LLM di luar pesan chat vulgar
Memahami Sinkronisasi Konteks LLM
Chatbot tradisional hanya merespons pesan pengguna yang eksplisit, sehingga menyebabkan pemutusan saat pengguna berinteraksi dengan aplikasi melalui cara lain. Sinkronisasi Konteks LLM mengatasi batasan ini:
Pentingnya Sinkronisasi Konteks LLM
Saat pengguna berinteraksi dengan aplikasi Anda melalui elemen UI (seperti memilih warna dari histori), LLM tidak dapat mengetahui apa yang terjadi kecuali jika Anda memberi tahu secara eksplisit. Sinkronisasi Konteks LLM:
- Mempertahankan konteks: Memastikan LLM selalu mendapatkan informasi tentang semua tindakan pengguna yang relevan
- Menciptakan koherensi: Menghasilkan pengalaman yang kohesif saat LLM mengonfirmasi interaksi UI
- Meningkatkan kecerdasan: Memungkinkan LLM merespons semua tindakan pengguna dengan tepat
- Meningkatkan pengalaman pengguna: Membuat seluruh aplikasi terasa lebih terintegrasi dan responsif
- Mengurangi upaya pengguna: Menghilangkan kebutuhan pengguna untuk menjelaskan tindakan UI mereka secara manual
Di aplikasi Colorist, saat pengguna memilih warna dari histori, Anda ingin Gemini mengonfirmasi tindakan ini dan memberikan komentar yang cerdas tentang warna yang dipilih, sehingga mempertahankan ilusi asisten yang lancar dan cerdas.
Memperbarui layanan chat Gemini untuk notifikasi pemilihan warna
Pertama, Anda akan menambahkan metode ke GeminiChatService
untuk memberi tahu LLM saat pengguna memilih warna dari histori. Update file lib/services/gemini_chat_service.dart
Anda:
lib/services/gemini_chat_service.dart
import 'dart:async';
import 'dart:convert'; // Add this import
import 'package:colorist_ui/colorist_ui.dart';
import 'package:firebase_vertexai/firebase_vertexai.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import '../providers/gemini.dart';
import 'gemini_tools.dart';
part 'gemini_chat_service.g.dart';
final conversationStateProvider = StateProvider(
(ref) => ConversationState.idle,
);
class GeminiChatService {
GeminiChatService(this.ref);
final Ref ref;
Future<void> notifyColorSelection(ColorData color) => sendMessage( // Add from here...
'User selected color from history: ${json.encode(color.toLLMContextMap())}',
); // To here.
Future<void> sendMessage(String message) async {
final chatSession = await ref.read(chatSessionProvider.future);
final conversationState = ref.read(conversationStateProvider);
final chatStateNotifier = ref.read(chatStateNotifierProvider.notifier);
final logStateNotifier = ref.read(logStateNotifierProvider.notifier);
if (conversationState == ConversationState.busy) {
logStateNotifier.logWarning(
"Can't send a message while a conversation is in progress",
);
throw Exception(
"Can't send a message while a conversation is in progress",
);
}
final conversationStateNotifier = ref.read(
conversationStateProvider.notifier,
);
conversationStateNotifier.state = ConversationState.busy;
chatStateNotifier.addUserMessage(message);
logStateNotifier.logUserText(message);
final llmMessage = chatStateNotifier.createLlmMessage();
try {
final responseStream = chatSession.sendMessageStream(
Content.text(message),
);
await for (final block in responseStream) {
await _processBlock(block, llmMessage.id);
}
} catch (e, st) {
logStateNotifier.logError(e, st: st);
chatStateNotifier.appendToMessage(
llmMessage.id,
"\nI'm sorry, I encountered an error processing your request. "
"Please try again.",
);
} finally {
chatStateNotifier.finalizeMessage(llmMessage.id);
conversationStateNotifier.state = ConversationState.idle;
}
}
Future<void> _processBlock(
GenerateContentResponse block,
String llmMessageId,
) async {
final chatSession = await ref.read(chatSessionProvider.future);
final chatStateNotifier = ref.read(chatStateNotifierProvider.notifier);
final logStateNotifier = ref.read(logStateNotifierProvider.notifier);
final blockText = block.text;
if (blockText != null) {
logStateNotifier.logLlmText(blockText);
chatStateNotifier.appendToMessage(llmMessageId, blockText);
}
if (block.functionCalls.isNotEmpty) {
final geminiTools = ref.read(geminiToolsProvider);
final responseStream = chatSession.sendMessageStream(
Content.functionResponses([
for (final functionCall in block.functionCalls)
FunctionResponse(
functionCall.name,
geminiTools.handleFunctionCall(
functionCall.name,
functionCall.args,
),
),
]),
);
await for (final response in responseStream) {
final responseText = response.text;
if (responseText != null) {
logStateNotifier.logLlmText(responseText);
chatStateNotifier.appendToMessage(llmMessageId, responseText);
}
}
}
}
}
@riverpod
GeminiChatService geminiChatService(Ref ref) => GeminiChatService(ref);
Penambahan utama adalah metode notifyColorSelection
, yang:
- Mengambil objek
ColorData
yang mewakili warna yang dipilih - Mengenkodenya ke format JSON yang dapat disertakan dalam pesan
- Mengirim pesan berformat khusus ke LLM yang menunjukkan pemilihan pengguna
- Menggunakan kembali metode
sendMessage
yang ada untuk menangani notifikasi
Pendekatan ini menghindari duplikasi dengan memanfaatkan infrastruktur penanganan pesan yang ada.
Memperbarui aplikasi utama untuk menghubungkan notifikasi pemilihan warna
Sekarang, ubah file lib/main.dart
untuk meneruskan fungsi notifikasi pemilihan warna ke layar utama:
lib/main.dart
import 'package:colorist_ui/colorist_ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'providers/gemini.dart';
import 'services/gemini_chat_service.dart';
void main() async {
runApp(ProviderScope(child: MainApp()));
}
class MainApp extends ConsumerWidget {
const MainApp({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final model = ref.watch(geminiModelProvider);
final conversationState = ref.watch(conversationStateProvider);
return MaterialApp(
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
),
home: model.when(
data: (data) => MainScreen(
conversationState: conversationState,
notifyColorSelection: (color) { // Add from here...
ref.read(geminiChatServiceProvider).notifyColorSelection(color);
}, // To here.
sendMessage: (text) {
ref.read(geminiChatServiceProvider).sendMessage(text);
},
),
loading: () => LoadingScreen(message: 'Initializing Gemini Model'),
error: (err, st) => ErrorScreen(error: err),
),
);
}
}
Perubahan utamanya adalah menambahkan callback notifyColorSelection
, yang menghubungkan peristiwa UI (memilih warna dari histori) ke sistem notifikasi LLM.
Memperbarui perintah sistem
Sekarang, Anda perlu memperbarui perintah sistem untuk memberi tahu LLM cara merespons notifikasi pemilihan warna. Ubah file assets/system_prompt.md
Anda:
assets/system_prompt.md
# Colorist System Prompt
You are a color expert assistant integrated into a desktop app called Colorist. Your job is to interpret natural language color descriptions and set the appropriate color values using a specialized tool.
## Your Capabilities
You are knowledgeable about colors, color theory, and how to translate natural language descriptions into specific RGB values. You have access to the following tool:
`set_color` - Sets the RGB values for the color display based on a description
## How to Respond to User Inputs
When users describe a color:
1. First, acknowledge their color description with a brief, friendly response
2. Interpret what RGB values would best represent that color description
3. Use the `set_color` tool to set those values (all values should be between 0.0 and 1.0)
4. After setting the color, provide a brief explanation of your interpretation
Example:
User: "I want a sunset orange"
You: "Sunset orange is a warm, vibrant color that captures the golden-red hues of the setting sun. It combines a strong red component with moderate orange tones."
[Then you would call the set_color tool with approximately: red=1.0, green=0.5, blue=0.25]
After the tool call: "I've set a warm orange with strong red, moderate green, and minimal blue components that is reminiscent of the sun low on the horizon."
## When Descriptions are Unclear
If a color description is ambiguous or unclear, please ask the user clarifying questions, one at a time.
## When Users Select Historical Colors
Sometimes, the user will manually select a color from the history panel. When this happens, you'll receive a notification about this selection that includes details about the color. Acknowledge this selection with a brief response that recognizes what they've done and comments on the selected color.
Example notification:
User: "User selected color from history: {red: 0.2, green: 0.5, blue: 0.8, hexCode: #3380CC}"
You: "I see you've selected an ocean blue from your history. This tranquil blue with a moderate intensity has a calming, professional quality to it. Would you like to explore similar shades or create a contrasting color?"
## Important Guidelines
- Always keep RGB values between 0.0 and 1.0
- Provide thoughtful, knowledgeable responses about colors
- When possible, include color psychology, associations, or interesting facts about colors
- Be conversational and engaging in your responses
- Focus on being helpful and accurate with your color interpretations
Penambahan utamanya adalah bagian "Saat Pengguna Memilih Warna Historis", yang:
- Menjelaskan konsep notifikasi pemilihan histori ke LLM
- Memberikan contoh tampilan notifikasi ini
- Menampilkan contoh respons yang sesuai
- Menetapkan ekspektasi untuk mengonfirmasi pilihan dan mengomentari warna
Hal ini membantu LLM memahami cara merespons pesan khusus ini dengan tepat.
Membuat Kode Riverpod
Jalankan perintah runner build untuk membuat kode Riverpod yang diperlukan:
dart run build_runner build --delete-conflicting-outputs
Menjalankan dan menguji Sinkronisasi Konteks LLM
Jalankan aplikasi Anda:
flutter run -d DEVICE
Pengujian Sinkronisasi Konteks LLM melibatkan:
- Pertama, buat beberapa warna dengan mendeskripsikannya di chat
- "Tampilkan warna ungu cerah"
- "Saya ingin warna hijau hutan"
- "Berikan warna merah terang"
- Kemudian, klik salah satu thumbnail warna di strip histori
Anda harus mengamati:
- Warna yang dipilih akan muncul di layar utama
- Pesan pengguna muncul di chat yang menunjukkan pemilihan warna
- LLM merespons dengan mengonfirmasi pilihan dan mengomentari warna
- Seluruh interaksi terasa alami dan kohesif
Hal ini menciptakan pengalaman yang lancar, dengan LLM mengetahui dan merespons pesan langsung dan interaksi UI dengan tepat.
Cara kerja Sinkronisasi Konteks LLM
Mari kita pelajari detail teknis cara kerja sinkronisasi ini:
Aliran Data
- Tindakan pengguna: Pengguna mengklik warna di strip histori
- Peristiwa UI: Widget
MainScreen
mendeteksi pilihan ini - Eksekusi callback: Callback
notifyColorSelection
dipicu - Pembuatan pesan: Pesan berformat khusus dibuat dengan data warna
- Pemrosesan LLM: Pesan dikirim ke Gemini, yang mengenali format
- Respons kontekstual: Gemini merespons dengan tepat berdasarkan perintah sistem
- Pembaruan UI: Respons muncul di chat, sehingga menciptakan pengalaman yang kohesif
Serialisasi data
Aspek utama dari pendekatan ini adalah cara Anda melakukan serialisasi data warna:
'User selected color from history: ${json.encode(color.toLLMContextMap())}'
Metode toLLMContextMap()
(disediakan oleh paket colorist_ui
) mengonversi objek ColorData
menjadi peta dengan properti utama yang dapat dipahami LLM. Hal ini biasanya mencakup:
- Nilai RGB (merah, hijau, biru)
- Representasi kode heksadesimal
- Nama atau deskripsi apa pun yang terkait dengan warna
Dengan memformat data ini secara konsisten dan menyertakannya dalam pesan, Anda memastikan LLM memiliki semua informasi yang diperlukan untuk merespons dengan tepat.
Aplikasi yang lebih luas dari Sinkronisasi Konteks LLM
Pola pemberitahuan LLM tentang peristiwa UI ini memiliki banyak aplikasi di luar pemilihan warna:
Kasus penggunaan lainnya
- Perubahan filter: Memberi tahu LLM saat pengguna menerapkan filter ke data
- Peristiwa navigasi: Memberi tahu LLM saat pengguna membuka berbagai bagian
- Perubahan pilihan: Memperbarui LLM saat pengguna memilih item dari daftar atau petak
- Pembaruan preferensi: Memberi tahu LLM saat pengguna mengubah setelan atau preferensi
- Manipulasi data: Memberi tahu LLM saat pengguna menambahkan, mengedit, atau menghapus data
Dalam setiap kasus, polanya tetap sama:
- Mendeteksi peristiwa UI
- Membuat serialisasi data yang relevan
- Mengirim notifikasi yang diformat secara khusus ke LLM
- Memandu LLM untuk merespons secara tepat melalui perintah sistem
Praktik terbaik untuk Sinkronisasi Konteks LLM
Berdasarkan penerapan Anda, berikut beberapa praktik terbaik untuk Sinkronisasi Konteks LLM yang efektif:
1. Format yang konsisten
Gunakan format yang konsisten untuk notifikasi sehingga LLM dapat dengan mudah mengidentifikasinya:
"User [action] [object]: [structured data]"
2. Konteks yang kaya
Sertakan detail yang cukup dalam notifikasi agar LLM dapat merespons dengan cerdas. Untuk warna, ini berarti nilai RGB, kode hex, dan properti relevan lainnya.
3. Petunjuk yang jelas
Berikan petunjuk yang jelas di perintah sistem tentang cara menangani notifikasi, idealnya dengan contoh.
4. Integrasi alami
Desain notifikasi agar mengalir secara alami dalam percakapan, bukan sebagai gangguan teknis.
5. Notifikasi selektif
Hanya beri tahu LLM tentang tindakan yang relevan dengan percakapan. Tidak semua peristiwa UI perlu dikomunikasikan.
Pemecahan masalah
Masalah notifikasi
Jika LLM tidak merespons dengan benar pemilihan warna:
- Pastikan format pesan notifikasi cocok dengan yang dijelaskan dalam perintah sistem
- Memverifikasi bahwa data warna diserialisasi dengan benar
- Pastikan perintah sistem memiliki petunjuk yang jelas untuk menangani pilihan
- Mencari error di layanan chat saat mengirim notifikasi
Pengelolaan konteks
Jika LLM tampaknya kehilangan konteks:
- Pastikan sesi chat dikelola dengan benar
- Memverifikasi bahwa status percakapan bertransisi dengan benar
- Memastikan notifikasi dikirim melalui sesi chat yang sama
Masalah umum
Untuk masalah umum:
- Periksa log untuk menemukan error atau peringatan
- Memverifikasi konektivitas Vertex AI in Firebase
- Memeriksa ketidakcocokan jenis dalam parameter fungsi
- Memastikan semua kode yang dihasilkan Riverpod sudah yang terbaru
Konsep utama yang dipelajari
- Membuat Sinkronisasi Konteks LLM antara UI dan LLM
- Membuat serial peristiwa UI ke dalam konteks yang kompatibel dengan LLM
- Memandu perilaku LLM untuk berbagai pola interaksi
- Membuat pengalaman yang kohesif di seluruh interaksi pesan dan non-pesan
- Meningkatkan kesadaran LLM tentang status aplikasi yang lebih luas
Dengan menerapkan Sinkronisasi Konteks LLM, Anda telah menciptakan pengalaman yang benar-benar terintegrasi, dengan LLM yang terasa seperti asisten yang tanggap dan sadar, bukan sekadar generator teks. Pola ini dapat diterapkan ke banyak aplikasi lain untuk membuat antarmuka yang lebih alami dan intuitif yang didukung AI.
9. Selamat!
Anda berhasil menyelesaikan codelab Colorist. 🎉
Yang telah Anda build
Anda telah membuat aplikasi Flutter yang berfungsi penuh dan mengintegrasikan Gemini API Google untuk menafsirkan deskripsi warna dalam bahasa sehari-hari. Aplikasi Anda kini dapat:
- Memproses deskripsi bahasa alami seperti "oranye matahari terbenam" atau "biru laut dalam"
- Gunakan Gemini untuk menerjemahkan deskripsi ini menjadi nilai RGB secara cerdas
- Menampilkan warna yang ditafsirkan secara real time dengan respons streaming
- Menangani interaksi pengguna melalui elemen chat dan UI
- Mempertahankan kesadaran kontekstual di berbagai metode interaksi
Tujuan berikutnya
Setelah Anda menguasai dasar-dasar integrasi Gemini dengan Flutter, berikut beberapa cara untuk melanjutkan perjalanan Anda:
Meningkatkan kualitas aplikasi Colorist
- Palet warna: Menambahkan fungsi untuk menghasilkan skema warna pelengkap atau yang cocok
- Input suara: Mengintegrasikan pengenalan ucapan untuk deskripsi warna verbal
- Pengelolaan histori: Menambahkan opsi untuk memberi nama, mengatur, dan mengekspor kumpulan warna
- Perintah kustom: Membuat antarmuka bagi pengguna untuk menyesuaikan perintah sistem
- Analisis lanjutan: Melacak deskripsi yang paling efektif atau menyebabkan masalah
Jelajahi fitur Gemini lainnya
- Input multimodal: Menambahkan input gambar untuk mengekstrak warna dari foto
- Pembuatan konten: Menggunakan Gemini untuk membuat konten terkait warna seperti deskripsi atau cerita
- Peningkatan panggilan fungsi: Membuat integrasi alat yang lebih kompleks dengan beberapa fungsi
- Setelan keamanan: Jelajahi berbagai setelan keamanan dan dampaknya terhadap respons
Menerapkan pola ini ke domain lain
- Analisis dokumen: Membuat aplikasi yang dapat memahami dan menganalisis dokumen
- Bantuan penulisan kreatif: Membuat alat penulisan dengan saran yang didukung LLM
- Otomatisasi tugas: Mendesain aplikasi yang menerjemahkan bahasa alami ke dalam tugas otomatis
- Aplikasi berbasis pengetahuan: Membuat sistem pakar di domain tertentu
Resource
Berikut adalah beberapa referensi berharga untuk melanjutkan pembelajaran Anda:
Dokumentasi resmi
Kursus dan panduan perintah
Komunitas
Seri Agentic Flutter yang dapat diamati
Dalam episode #59, Craig Labenz dan Andrew Brogden menjelajahi codelab ini, yang menyoroti bagian-bagian menarik dari build aplikasi.
Di episode #60, bergabunglah kembali dengan Craig dan Andrew saat mereka memperluas aplikasi codelab dengan kemampuan baru dan berjuang untuk membuat LLM melakukan apa yang diperintahkan.
Di episode #61, Craig bergabung dengan Chris Sells untuk mendapatkan perspektif baru dalam menganalisis judul berita dan membuat gambar yang sesuai.
Masukan
Kami ingin mendengar pengalaman Anda dengan codelab ini. Harap pertimbangkan untuk memberikan masukan melalui:
Terima kasih telah menyelesaikan codelab ini, dan kami harap Anda terus menjelajahi kemungkinan menarik di persimpangan Flutter dan AI.