Gemini destekli Flutter uygulaması geliştirme

1. Gemini destekli Flutter uygulaması geliştirme

Ne oluşturacaksınız?

Bu kod laboratuvarında, Gemini API'nin gücünü doğrudan Flutter uygulamanıza getiren etkileşimli bir Flutter uygulaması olan Colorist'i oluşturacaksınız. Kullanıcıların uygulamanızı doğal dil üzerinden kontrol etmesine izin vermek istediniz ancak nereden başlayacağınızı bilmiyor muydunuz? Bu kod laboratuvarında bunu nasıl yapacağınız gösterilmektedir.

Colorist, kullanıcıların renkleri doğal dille tanımlamasına olanak tanır (ör. "gün batımının turuncusu" veya "koyu okyanus mavisi"). Uygulama:

  • Bu açıklamaları Google'ın Gemini API'sini kullanarak işler.
  • Açıklamaları kesin RGB renk değerlerine dönüştürür
  • Rengi ekranda gerçek zamanlı olarak gösterir
  • Renklerle ilgili teknik ayrıntılar ve ilgi çekici bağlam bilgileri sağlar
  • Son oluşturulan renklerin geçmişini korur

Renk ekranını ve sohbet arayüzünü gösteren Colorist uygulaması ekran görüntüsü

Uygulamada, bir tarafta renkli ekran alanı ve etkileşimli sohbet sistemi, diğer tarafta ise ham LLM etkileşimlerini gösteren ayrıntılı bir günlük paneli bulunan bölünmüş ekran arayüzü bulunur. Bu günlük, LLM entegrasyonunun aslında nasıl çalıştığını daha iyi anlamanızı sağlar.

Bu durum Flutter geliştiricileri için neden önemli?

LLM'ler, kullanıcıların uygulamalarla etkileşim kurma şeklinde devrim yaratıyor ancak bunları mobil ve masaüstü uygulamalarına etkili bir şekilde entegre etmek benzersiz zorluklar sunuyor. Bu codelab'de, ham API çağrılarının ötesine geçen pratik kalıplar öğretilir.

Öğrenme yolculuğunuz

Bu codelab'de, Colorist'i adım adım oluşturma süreci açıklanmaktadır:

  1. Proje kurulumu: Temel bir Flutter uygulama yapısı ve colorist_ui paketiyle başlarsınız.
  2. Temel Gemini entegrasyonu: Uygulamanızı Firebase AI Logic'e bağlayın ve LLM iletişimini uygulayın
  3. Etkili istem: LLM'nin renk açıklamalarını anlamasına rehberlik edecek bir sistem istemi oluşturun.
  4. İşlev bildirimleri: LLM'nin uygulamanızda renkleri ayarlamak için kullanabileceği araçları tanımlayın.
  5. Araç işleme: LLM'den gelen işlev çağrılarını işleyip uygulamanızın durumuna bağlayın.
  6. Akış yanıtları: Gerçek zamanlı akış LLM yanıtlarıyla kullanıcı deneyimini iyileştirin
  7. LLM İçerik Senkronizasyonu: LLM'yi kullanıcı işlemlerinden haberdar ederek tutarlı bir deneyim oluşturun

Neler öğreneceksiniz?

  • Flutter uygulamaları için Firebase Yapay Zeka Mantığını yapılandırma
  • LLM davranışına rehberlik etmek için etkili sistem istemleri oluşturun.
  • Doğal dil ile uygulama özellikleri arasında köprü oluşturan işlev tanımları uygulayın.
  • Duyarlı bir kullanıcı deneyimi için akış yanıtlarını işleme
  • Kullanıcı arayüzü etkinlikleri ile LLM arasında durumu senkronize etme
  • Riverpod'u kullanarak LLM sohbet durumunu yönetme
  • LLM destekli uygulamalarda hataları düzgün şekilde ele alma

Kod önizlemesi: Uygulamaya koyacağınız kodlardan bir örnek

LLM'nin uygulamanızda renk ayarlamaları yapmasına izin vermek için oluşturacağınız işlev beyanını aşağıda görebilirsiniz:

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)'),
  },
);

Bu codelab'e genel bakış videosu

Craig Labenz ve Andrew Brogdon'un bu kod laboratuvarını Observable Flutter 59. bölümünde tartışmasını izleyin:

Ön koşullar

Bu codelab'den en iyi şekilde yararlanmak için şunlara sahip olmanız gerekir:

  • Flutter geliştirme deneyimi: Flutter'ın temelleri ve Dart söz dizimi hakkında bilgi sahibi olma
  • Eşzamansız programlama bilgisi: Futures, async/await ve akışları anlama
  • Firebase hesabı: Firebase'i ayarlamak için bir Google Hesabı'na ihtiyacınız vardır.

LLM destekli ilk Flutter uygulamanızı oluşturmaya başlayalım.

2. Proje oluşturma ve yankı hizmeti

Bu ilk adımda, proje yapısını ayarlayacak ve daha sonra Gemini API entegrasyonuyla değiştirilecek bir yankı hizmeti uygulayacaksınız. Bu, uygulama mimarisini oluşturur ve LLM çağrılarının karmaşıklığını eklemeden önce kullanıcı arayüzünüzün düzgün çalıştığından emin olmanızı sağlar.

Bu adımda neler öğreneceksiniz?

  • Gerekli bağımlılıklara sahip bir Flutter projesi oluşturma
  • Kullanıcı arayüzü bileşenleri için colorist_ui paketiyle çalışma
  • Yansıma mesajı hizmeti uygulama ve kullanıcı arayüzüne bağlama

Yeni bir Flutter projesi oluşturma

Aşağıdaki komutla yeni bir Flutter projesi oluşturarak başlayın:

flutter create -e colorist --platforms=android,ios,macos,web,windows

-e işareti, varsayılan counter uygulaması olmayan boş bir proje istediğinizi gösterir. Uygulama, masaüstü, mobil ve web'de çalışacak şekilde tasarlanmıştır. Ancak flutterfire şu anda Linux'u desteklememektedir.

Bağımlılıklar ekleme

Proje dizininize gidin ve gerekli bağımlılıkları ekleyin:

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

Bu işlem, aşağıdaki anahtar paketlerini ekler:

  • colorist_ui: Colorist uygulaması için kullanıcı arayüzü bileşenlerini sağlayan özel bir paket
  • flutter_riverpod ve riverpod_annotation: Durum yönetimi için
  • logging: Yapılandırılmış günlük kaydı için
  • Kod oluşturma ve linting için geliştirme bağımlılıkları

pubspec.yaml, aşağıdaki gibi görünür:

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

Analiz seçeneklerini yapılandırma

Projenizin kökündeki analysis_options.yaml dosyanıza custom_lint ekleyin:

include: package:flutter_lints/flutter.yaml

analyzer:
  plugins:
    - custom_lint

Bu yapılandırma, kod kalitesini korumaya yardımcı olmak için Riverpod'a özgü lint'leri etkinleştirir.

main.dart dosyasını uygulama

lib/main.dart içeriğini aşağıdakiyle değiştirin:

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

Bu, kullanıcının mesajını döndürerek LLM'nin davranışını taklit eden bir yankı hizmeti uygulayan bir Flutter uygulaması oluşturur.

Mimarisi anlama

colorist uygulamasının mimarisini anlamak için bir dakikanızı ayıralım:

colorist_ui paketi

colorist_ui paketi, önceden oluşturulmuş kullanıcı arayüzü bileşenleri ve durum yönetimi araçları sağlar:

  1. MainScreen: Aşağıdakileri gösteren ana kullanıcı arayüzü bileşeni:
    • Masaüstünde bölünmüş ekran düzeni (etkileşim alanı ve günlük paneli)
    • Mobil cihazlarda sekmeli arayüz
    • Renkli ekran, sohbet arayüzü ve geçmiş küçük resimleri
  2. Durum Yönetimi: Uygulama birkaç durum bildiricisi kullanır:
    • ChatStateNotifier: Sohbet mesajlarını yönetir.
    • ColorStateNotifier: Geçerli rengi ve geçmişi yönetir
    • LogStateNotifier: Hata ayıklama için günlük girişlerini yönetir
  3. Mesaj İşleme: Uygulama, farklı durumlara sahip bir mesaj modeli kullanır:
    • Kullanıcı mesajları: Kullanıcı tarafından girilir.
    • LLM mesajları: LLM (veya şu anda yankı hizmetiniz) tarafından oluşturulur.
    • MessageState: LLM mesajlarının tamamlanıp tamamlanmadığını veya hâlâ aktarılıp aktarılmadığını izler.

Uygulama mimarisi

Uygulama aşağıdaki mimariyi izler:

  1. Kullanıcı Arayüzü Katmanı: colorist_ui paketi tarafından sağlanır.
  2. Durum Yönetimi: Tepkisel durum yönetimi için Riverpod'u kullanır.
  3. Servis Katmanı: Şu anda basit yankı hizmetinizi içerir. Bu hizmet, Gemini Chat Hizmeti ile değiştirilecektir.
  4. LLM Entegrasyonu: Sonraki adımlarda eklenecektir

Bu ayrım, kullanıcı arayüzü bileşenleri zaten halledilmişken LLM entegrasyonunu uygulamaya odaklanmanıza olanak tanır.

Uygulamayı çalıştırma

Uygulamayı aşağıdaki komutla çalıştırın:

flutter run -d DEVICE

DEVICE değerini hedef cihazınızla (ör. macos, windows, chrome veya cihaz kimliği) değiştirin.

Echo hizmetinin markdown'ı oluşturmasını gösteren Colorist uygulaması ekran görüntüsü

Artık Colorist uygulamasını aşağıdakilerle birlikte görürsünüz:

  1. Varsayılan bir renge sahip renkli ekran alanı
  2. Mesaj yazabileceğiniz bir sohbet arayüzü
  3. Sohbet etkileşimlerini gösteren bir günlük paneli

"Derin mavi renk istiyorum" gibi bir mesaj yazıp Gönder'e basmayı deneyin. Yansıtma hizmeti, mesajınızı tekrarlar. Daha sonraki adımlarda, Firebase AI Logic'i kullanarak bunu gerçek renk yorumuyla değiştireceksiniz.

Sırada ne var?

Sonraki adımda, Firebase'ı yapılandıracak ve yankı hizmetinizi Gemini sohbet hizmetiyle değiştirmek için temel Gemini API entegrasyonunu uygulayacaksınız. Bu sayede uygulama, renk açıklamalarını yorumlayabilir ve akıllı yanıtlar verebilir.

Sorun giderme

Kullanıcı arayüzü paketi sorunları

colorist_ui paketiyle ilgili sorun yaşarsanız:

  • En son sürümü kullandığınızdan emin olun
  • Bağımlılığı doğru şekilde eklediğinizi doğrulayın
  • Çakışan paket sürümleri olup olmadığını kontrol edin

Derleme hataları

Derleme hataları görürseniz:

  • Flutter SDK'sının en son kararlı kanal sürümünün yüklü olduğundan emin olun
  • flutter clean'ü, ardından flutter pub get'u çalıştırın
  • Belirli hata mesajları için konsol çıkışını kontrol etme

Öğrenilen temel kavramlar

  • Gerekli bağımlılıklara sahip bir Flutter projesi oluşturma
  • Uygulamanın mimarisini ve bileşen sorumluluklarını anlama
  • LLM'nin davranışını taklit eden basit bir hizmet uygulama
  • Hizmeti kullanıcı arayüzü bileşenlerine bağlama
  • Durum yönetimi için Riverpod'u kullanma

3. Temel Gemini sohbet entegrasyonu

Bu adımda, önceki adımdaki yankı hizmetini Firebase AI Logic'i kullanarak Gemini API entegrasyonuyla değiştireceksiniz. Firebase'i yapılandıracak, gerekli sağlayıcıları ayarlayacak ve Gemini API ile iletişim kuran temel bir sohbet hizmeti uygulayacaksınız.

Bu adımda neler öğreneceksiniz?

  • Firebase'i Flutter uygulamasında ayarlama
  • Gemini erişimi için Firebase AI Logic'i yapılandırma
  • Firebase ve Gemini hizmetleri için Riverpod sağlayıcıları oluşturma
  • Gemini API ile temel bir sohbet hizmeti uygulama
  • Eşzamansız API yanıtlarını ve hata durumlarını işleme

Firebase'i ayarlama

Öncelikle Flutter projeniz için Firebase'i ayarlamanız gerekir. Bu işlem, bir Firebase projesi oluşturmayı, uygulamanızı buna eklemeyi ve gerekli Firebase yapay zeka mantığı ayarlarını yapılandırmayı içerir.

Firebase projesi oluşturma

  1. Firebase Console'a gidin ve Google Hesabınızla oturum açın.
  2. Firebase projesi oluştur'u tıklayın veya mevcut bir proje seçin.
  3. Projenizi oluşturmak için kurulum sihirbazını izleyin.

Firebase AI Logic'i Firebase projenizde ayarlama

  1. Firebase konsolunda projenize gidin.
  2. Sol kenar çubuğunda AI'yı seçin.
  3. Yapay zeka açılır menüsünde Yapay Zeka Mantığı'nı seçin.
  4. Firebase AI Logic kartında Başlayın'ı seçin.
  5. Projeniz için Gemini Developer API'yi etkinleştirmek üzere talimatları uygulayın.

FlutterFire CLI'yi yükleme

FlutterFire CLI, Flutter uygulamalarında Firebase kurulumunu basitleştirir:

dart pub global activate flutterfire_cli

Firebase'i Flutter uygulamanıza ekleme

  1. Firebase çekirdek ve Firebase AI Logic paketlerini projenize ekleyin:
flutter pub add firebase_core firebase_ai
  1. FlutterFire yapılandırma komutunu çalıştırın:
flutterfire configure

Bu komutla:

  • Yeni oluşturduğunuz Firebase projesini seçmenizi ister.
  • Flutter uygulamalarınızı Firebase'e kaydetme
  • Proje yapılandırmanızı içeren bir firebase_options.dart dosyası oluşturun

Komut, seçtiğiniz platformları (iOS, Android, macOS, Windows, web) otomatik olarak algılar ve uygun şekilde yapılandırır.

Platforma özgü yapılandırma

Firebase, Flutter için varsayılan sürümlerden daha yüksek minimum sürümler gerektirir. Ayrıca Firebase AI Logic sunucularıyla iletişim kurabilmek için ağ erişimi gerekir.

macOS izinlerini yapılandırma

macOS için uygulamanızın izinlerinde ağ erişimini etkinleştirmeniz gerekir:

  1. macos/Runner/DebugProfile.entitlements'ü açıp şunları ekleyin:

macos/Runner/DebugProfile.entitlements

<key>com.apple.security.network.client</key>
<true/>
  1. Ayrıca macos/Runner/Release.entitlements dosyasını açıp aynı girişi ekleyin.
  2. macos/Podfile'ün üst kısmındaki minimum macOS sürümünü güncelleyin:

macos/Podfile

# Firebase requires at least macOS 10.15
platform :osx, '10.15'

iOS izinlerini yapılandırma

iOS için ios/Podfile'ün üst kısmındaki minimum sürümü güncelleyin:

ios/Podfile

# Firebase requires at least iOS 13.0
platform :ios, '13.0'

Android ayarlarını yapılandırma

Android için android/app/build.gradle.kts'ü güncelleyin:

android/app/build.gradle.kts

android {
    // ...
    ndkVersion = "27.0.12077973"

    defaultConfig {
        // ...
        minSdk = 23
        // ...
    }
}

Gemini model sağlayıcıları oluşturma

Artık Firebase ve Gemini için Riverpod sağlayıcılarını oluşturacaksınız. Yeni bir dosya lib/providers/gemini.dart oluşturun:

lib/providers/gemini.dart

import 'dart:async';

import 'package:firebase_ai/firebase_ai.dart';
import 'package:firebase_core/firebase_core.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 = FirebaseAI.googleAI().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();
}

Bu dosya, üç temel sağlayıcının temelini tanımlar. Bu sağlayıcılar, dart run build_runner çalıştırdığınızda Riverpod kod oluşturucular tarafından oluşturulur.

  1. firebaseAppProvider: Firebase'i proje yapılandırmanızla başlatır
  2. geminiModelProvider: Gemini üretken model örneği oluşturur
  3. chatSessionProvider: Gemini modeliyle sohbet oturumu oluşturur ve oturumu sürdürür

Sohbet oturumundaki keepAlive: true ek açıklama, sohbet oturumunun uygulamanın yaşam döngüsü boyunca devam etmesini ve görüşme bağlamının korunmasını sağlar.

Gemini sohbet hizmetini uygulama

Sohbet hizmetini uygulamak için yeni bir dosya lib/services/gemini_chat_service.dart oluşturun:

lib/services/gemini_chat_service.dart

import 'dart:async';

import 'package:colorist_ui/colorist_ui.dart';
import 'package:firebase_ai/firebase_ai.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);

Bu hizmet:

  1. Kullanıcı mesajlarını kabul edip Gemini API'ye gönderir
  2. Sohbet arayüzünü modelden gelen yanıtlarla günceller
  3. Gerçek LLM akışını kolayca anlamak için tüm iletişimleri günlüğe kaydeder
  4. Hataları uygun kullanıcı geri bildirimiyle ele alır

Not: Günlük penceresi bu noktada sohbet penceresine neredeyse aynı görünür. İşlev çağrılarını ve ardından akış yanıtlarını eklediğinizde günlük daha ilginç hale gelir.

Riverpod kodu oluşturma

Gerekli Riverpod kodunu oluşturmak için derleme çalıştırıcı komutunu çalıştırın:

dart run build_runner build --delete-conflicting-outputs

Bu işlem, Riverpod'un çalışması için gereken .g.dart dosyalarını oluşturur.

main.dart dosyasını güncelleme

Yeni Gemini sohbet hizmetini kullanmak için lib/main.dart dosyanızı güncelleyin:

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),
      ),
    );
  }
}

Bu güncellemedeki önemli değişiklikler şunlardır:

  1. Echo hizmetini Gemini API tabanlı sohbet hizmetiyle değiştirme
  2. Riverpod'un when yöntemiyle AsyncValue desenini kullanarak yükleme ve hata ekranları ekleme
  3. sendMessage geri çağırma işlevi aracılığıyla kullanıcı arayüzünü yeni sohbet hizmetinize bağlama

Uygulamayı çalıştırma

Uygulamayı aşağıdaki komutla çalıştırın:

flutter run -d DEVICE

DEVICE değerini hedef cihazınızla (ör. macos, windows, chrome veya cihaz kimliği) değiştirin.

Gemini LLM&#39;nin güneşli sarı renk isteğiyle yanıt verdiğini gösteren Colorist uygulaması ekran görüntüsü

Artık yazdığınız mesajlar Gemini API'ye gönderilir ve LLM'den yankı yerine yanıt alırsınız. Günlük panelinde API ile etkileşimler gösterilir.

LLM iletişimini anlama

Gemini API ile iletişim kurduğunuzda neler olduğunu anlamak için biraz zaman ayıralım:

İletişim akışı

  1. Kullanıcı girişi: Kullanıcı, sohbet arayüzüne metin girer.
  2. İstek Biçimlendirme: Uygulama, metni Gemini API için Content nesnesi olarak biçimlendirir.
  3. API İletişimi: Metin, Firebase AI Logic aracılığıyla Gemini API'ye gönderilir.
  4. LLM İşleme: Gemini modeli metni işler ve yanıt oluşturur
  5. Yanıtın işlenmesi: Uygulama, yanıtı alır ve kullanıcı arayüzünü günceller.
  6. Günlük kaydı: Şeffaflık için tüm iletişimler günlüğe kaydedilir.

Chat oturumları ve ileti dizisi bağlamı

Gemini sohbet oturumu, mesajlar arasındaki bağlamı koruyarak sohbet etkileşimlerine olanak tanır. Bu, LLM'nin mevcut oturumdaki önceki alışverişleri "hatırladığı" ve daha tutarlı sohbetler sağladığı anlamına gelir.

Sohbet oturumu sağlayıcınızdaki keepAlive: true ek açıklaması, bu bağlamın uygulamanın yaşam döngüsü boyunca devam etmesini sağlar. Bu kalıcı bağlam, LLM ile doğal bir konuşma akışı sağlamak için çok önemlidir.

Sırada ne var?

Bu noktada, yanıt vereceği konularla ilgili herhangi bir kısıtlama olmadığından Gemini API'ye istediğiniz soruyu sorabilirsiniz. Örneğin, Güller Savaşı'nın özetini isteyebilirsiniz. Bu, renk uygulamanızın amacıyla ilgili değildir.

Sonraki adımda, renk açıklamalarını daha etkili bir şekilde yorumlaması için Gemini'ye rehberlik edecek bir sistem istemi oluşturacaksınız. Bu örnekte, bir LLM'nin davranışının uygulamaya özgü ihtiyaçlar için nasıl özelleştirileceği ve yeteneklerinin uygulamanızın alanına nasıl odaklanacağı gösterilmektedir.

Sorun giderme

Firebase yapılandırma sorunları

Firebase başlatma işlemiyle ilgili hatalarla karşılaşırsanız:

  • firebase_options.dart dosyanızın doğru şekilde oluşturulduğundan emin olun
  • Firebase AI Logic erişimi için Blaze planına geçtiğinizi doğrulayın

API erişim hataları

Gemini API'ye erişirken hata alırsanız:

  • Firebase projenizde faturalandırmanın doğru şekilde ayarlandığını onaylama
  • Firebase projenizde Firebase AI Logic ve Cloud AI API'nin etkinleştirildiğinden emin olun
  • Ağ bağlantınızı ve güvenlik duvarı ayarlarınızı kontrol edin
  • Model adının (gemini-2.0-flash) doğru ve kullanılabilir durumda olduğunu doğrulayın

Sohbet bağlamı sorunları

Gemini'nin sohbetteki önceki bağlamı hatırlamadığını fark ederseniz:

  • chatSession işlevinin @Riverpod(keepAlive: true) ile eklendiğinden emin olun
  • Tüm mesaj alışverişleri için aynı sohbet oturumunu yeniden kullandığınızdan emin olun
  • Mesaj göndermeden önce sohbet oturumunun düzgün bir şekilde başlatıldığını doğrulayın

Platforma özgü sorunlar

Platforma özgü sorunlar için:

  • iOS/macOS: Uygun izinlerin ayarlandığından ve minimum sürümlerin yapılandırıldığından emin olun
  • Android: Minimum SDK sürümünün doğru ayarlandığını doğrulama
  • Konsol'da platforma özgü hata mesajlarını kontrol etme

Öğrenilen temel kavramlar

  • Firebase'i Flutter uygulamasında ayarlama
  • Gemini'ye erişim için Firebase AI Logic'i yapılandırma
  • Eşzamansız hizmetler için Riverpod sağlayıcıları oluşturma
  • LLM ile iletişim kuran bir sohbet hizmeti uygulama
  • Eşzamansız API durumlarını (yükleme, hata, veri) işleme
  • LLM iletişim akışını ve sohbet oturumlarını anlama

4. Renk açıklamaları için etkili istemler

Bu adımda, renk açıklamalarını yorumlarken Gemini'ye rehberlik edecek bir sistem istemi oluşturup uygulayacaksınız. Sistem istemleri, kodunuzu değiştirmeden LLM davranışını belirli görevler için özelleştirmenin güçlü bir yoludur.

Bu adımda neler öğreneceksiniz?

  • Sistem istemlerini ve LLM uygulamalarındaki önemini anlama
  • Alana özgü görevler için etkili istemler oluşturma
  • Flutter uygulamasında sistem istemlerini yükleme ve kullanma
  • LLM'nin tutarlı şekilde biçimlendirilmiş yanıtlar vermesi için yönlendirme
  • Sistem istemlerinin LLM davranışını nasıl etkilediğini test etme

Sistem istemlerini anlama

Uygulamaya geçmeden önce sistem istemlerinin ne olduğunu ve neden önemli olduğunu anlayalım:

Sistem istemleri nedir?

Sistem istemi, LLM'ye verilen ve yanıtlarının bağlamını, davranış yönergelerini ve beklentilerini belirleyen özel bir talimat türüdür. Kullanıcı mesajlarının aksine sistem istemleri:

  • LLM'nin rolünü ve kişiliğini belirleyin
  • Özel bilgi veya özellikleri tanımlama
  • Biçimlendirme talimatları sağlama
  • Yanıtlarla ilgili kısıtlamalar belirleme
  • Çeşitli senaryolarla nasıl başa çıkılacağını açıklama

Sistem istemlerini, LLM'ye "iş tanımını" veren öğeler olarak düşünebilirsiniz. Bu öğeler, modele sohbet boyunca nasıl davranacağını söyler.

Sistem istemlerinin önemi

Sistem istemleri, tutarlı ve yararlı LLM etkileşimleri oluşturmak için kritik öneme sahiptir. Bunun nedeni, sistem istemlerinin:

  1. Tutarlılığı sağlayın: Modeli, tutarlı bir biçimde yanıtlar sağlayacak şekilde yönlendirin
  2. Alaka düzeyini artırma: Modeli belirli alanınıza (bu durumda, renklerinize) odaklayın.
  3. Sınırları belirleme: Modelin ne yapması ve ne yapmaması gerektiğini tanımlayın
  4. Kullanıcı deneyimini iyileştirin: Daha doğal ve faydalı bir etkileşim kalıbı oluşturun
  5. Sonraki işlemleri azaltın: Yanıtları ayrıştırması veya göstermesi daha kolay biçimlerde alın.

Renk uzmanı uygulamanız için renk açıklamalarını tutarlı bir şekilde yorumlamak ve RGB değerlerini belirli bir biçimde sağlamak üzere LLM'ye ihtiyacınız vardır.

Sistem istemi öğesi oluşturma

Öncelikle, çalışma zamanında yüklenecek bir sistem istemi dosyası oluşturursunuz. Bu yaklaşım, uygulamanızı yeniden derlemeden istemi değiştirmenize olanak tanır.

Aşağıdaki içeriği içeren yeni bir assets/system_prompt.md dosyası oluşturun:

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

Sistem istemi yapısını anlama

Bu istemin ne işe yaradığını inceleyelim:

  1. Rol tanımı: LLM'yi "renk uzmanı asistanı" olarak tanımlar.
  2. Görev açıklaması: Birincil görevi, renk açıklamalarını RGB değerlerine dönüştürme olarak tanımlar.
  3. Yanıt biçimi: RGB değerlerinin tutarlılık için tam olarak nasıl biçimlendirileceğini belirtir.
  4. Örnek exchange: Beklenen etkileşim kalıbına dair somut bir örnek sağlar.
  5. Uç durumların ele alınması: Net olmayan açıklamaların nasıl ele alınacağını belirtir.
  6. Kısıtlamalar ve yönergeler: RGB değerlerini 0,0 ile 1,0 arasında tutma gibi sınırlar belirler.

Bu yapılandırılmış yaklaşım, LLM'nin yanıtlarının tutarlı, bilgilendirici olmasını ve RGB değerlerini programatik olarak ayıklamak isterseniz ayrıştırılması kolay olacak şekilde biçimlendirilmesini sağlar.

pubspec.yaml dosyasını güncelleme

Ardından, pubspec.yaml dosyanızı assets dizini içerecek şekilde güncelleyin:

pubspec.yaml

flutter:
  uses-material-design: true

  assets:
    - assets/

Öğe paketini yenilemek için flutter pub get komutunu çalıştırın.

Sistem istemi sağlayıcı oluşturma

Sistem istemini yüklemek için yeni bir dosya lib/providers/system_prompt.dart oluşturun:

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

Bu sağlayıcı, istem dosyasını çalışma zamanında okumak için Flutter'ın öğe yükleme sistemini kullanır.

Gemini model sağlayıcısını güncelleme

Ardından lib/providers/gemini.dart dosyanızı sistem istemini içerecek şekilde değiştirin:

lib/providers/gemini.dart

import 'dart:async';

import 'package:firebase_ai/firebase_ai.dart';
import 'package:firebase_core/firebase_core.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 = FirebaseAI.googleAI().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();
}

Anahtar değişiklik, üretken model oluşturulurken systemInstruction: Content.system(systemPrompt) eklenmesidir. Bu işlem, Gemini'ye bu sohbet oturumundaki tüm etkileşimler için sistem istemi olarak talimatlarınızı kullanmasını söyler.

Riverpod kodu oluşturma

Gerekli Riverpod kodunu oluşturmak için derleme çalıştırıcı komutunu çalıştırın:

dart run build_runner build --delete-conflicting-outputs

Uygulamayı çalıştırma ve test etme

Şimdi uygulamanızı çalıştırın:

flutter run -d DEVICE

Gemini LLM&#39;nin renk seçim uygulaması için karaktere uygun bir yanıt verdiğini gösteren renk uzmanı uygulaması ekran görüntüsü

Çeşitli renk açıklamalarıyla test etmeyi deneyin:

  • "Gök mavisi istiyorum"
  • "Bana bir orman yeşili söyle"
  • "Canlı bir gün batımı turuncusu oluşturma"
  • "Taze lavanta rengini istiyorum"
  • "Bana koyu okyanus mavisi gibi bir şey göster"

Gemini'nin artık tutarlı şekilde biçimlendirilmiş RGB değerlerinin yanı sıra renklerle ilgili konuşma dilinde açıklamalarla yanıt verdiğini fark edeceksiniz. Sistem istemi, LLM'ye ihtiyacınız olan yanıt türlerini sunması için etkili bir şekilde rehberlik etti.

Ayrıca, renk bağlamının dışındaki içerikler için de sormayı deneyin. Gül Savaşları'nın başlıca nedenlerini söyleyin. Önceki adımdan farklı bir durumla karşılaşırsınız.

Uzmanlaşmış görevler için istem mühendisliğinin önemi

Sistem istemleri hem sanat hem de bilimdir. Bunlar, LLM entegrasyonunun kritik bir parçasıdır ve modelin belirli uygulamanız için ne kadar yararlı olacağını önemli ölçüde etkileyebilir. Burada, istem mühendisliği yaptınız. Bu, talimatları modelin uygulamanızın ihtiyaçlarına uygun şekilde davranmasını sağlayacak şekilde uyarlama işlemidir.

Etkili istem mühendisliği şunları içerir:

  1. Net rol tanımı: LLM'nin amacının ne olduğunu belirleme
  2. Açık talimatlar: LLM'nin tam olarak nasıl yanıt vermesi gerektiğini ayrıntılı olarak belirtir.
  3. Somut örnekler: İyi yanıtların nasıl göründüğünü anlatmak yerine gösterme
  4. Sıra dışı durumların ele alınması: LLM'ye belirsiz senaryolarla nasıl başa çıkacağı konusunda talimat verme
  5. Biçimlendirme özellikleri: Yanıtların tutarlı ve kullanılabilir bir şekilde yapılandırılmasını sağlama

Oluşturduğunuz sistem istemi, Gemini'nin genel özelliklerini, uygulamanızın ihtiyaçlarına özel olarak biçimlendirilmiş yanıtlar sağlayan özel bir renk yorumlama asistanı haline getirir. Bu, birçok farklı alana ve göreve uygulayabileceğiniz güçlü bir kalıptır.

Sırada ne var?

Bir sonraki adımda, LLM'nin yalnızca RGB değerleri önermesine değil, rengi doğrudan ayarlamak için uygulamanızdaki işlevleri çağırmasına olanak tanıyan işlev bildirimleri ekleyerek bu temeli geliştireceksiniz. Bu, LLM'lerin doğal dil ile somut uygulama özellikleri arasındaki boşluğu nasıl doldurabileceğini gösterir.

Sorun giderme

Öğe yükleme sorunları

Sistem istemi yüklenirken hatalarla karşılaşırsanız:

  • pubspec.yaml dosyasının öğe dizinini doğru şekilde listelediğini doğrulayın
  • rootBundle.loadString() içindeki yolun dosya konumunuzla eşleştiğinden emin olun
  • Öğe paketini yenilemek için flutter clean ve ardından flutter pub get komutunu çalıştırın.

Tutarsız yanıtlar

LLM, biçimlendirme talimatlarınıza tutarlı bir şekilde uymuyorsa:

  • Sistem isteminde biçim şartlarını daha açık hale getirmeyi deneyin
  • Beklenen kalıbı göstermek için daha fazla örnek ekleyin
  • İstediğiniz biçimin model için uygun olduğundan emin olun

API hız sınırlaması

Hız sınırlamasıyla ilgili hatalarla karşılaşırsanız:

  • Firebase AI Logic hizmetinin kullanım sınırlamaları olduğunu unutmayın
  • Eksponansiyel geri yüklemeyle yeniden deneme mantığını uygulamayı düşünün
  • Kota sorunları olup olmadığını görmek için Firebase konsolunuzu kontrol edin

Öğrenilen temel kavramlar

  • LLM uygulamalarında sistem istemlerinin rolünü ve önemini anlama
  • Net talimatlar, örnekler ve kısıtlamalar içeren etkili istemler oluşturma
  • Flutter uygulamasında sistem istemlerini yükleme ve kullanma
  • Alana özgü görevler için LLM davranışını yönlendirme
  • LLM yanıtlarını şekillendirmek için istem mühendisliğini kullanma

Bu adımda, kodunuzu değiştirmeden yalnızca sistem isteminde net talimatlar sağlayarak LLM davranışında nasıl önemli ölçüde özelleştirme yapabileceğiniz gösterilmektedir.

5. LLM araçları için işlev bildirimleri

Bu adımda, işlev beyanları uygulayarak Gemini'nin uygulamanızda işlem yapmasını sağlamaya başlayacaksınız. Bu güçlü özellik, LLM'nin yalnızca RGB değerlerini önermesini değil, özel araç çağrıları aracılığıyla bunları uygulamanızın kullanıcı arayüzünde ayarlamasını sağlar. Ancak Flutter uygulamasında LLM isteklerinin yürütüldüğünü görmek için bir sonraki adımı uygulamanız gerekir.

Bu adımda neler öğreneceksiniz?

  • LLM işlev çağrısını ve Flutter uygulamalarına olan avantajlarını anlama
  • Gemini için şemaya dayalı işlev tanımları tanımlama
  • İşlev tanımlarını Gemini modelinizle entegre etme
  • Sistem istemini, araç özelliklerini kullanacak şekilde güncelleme

İşlev çağrısını anlama

İşlev tanımlarını uygulamadan önce bunların ne olduğunu ve neden değerli olduklarını anlayalım:

İşlev çağrısı nedir?

İşlev çağrısı (bazen "araç kullanımı" olarak da adlandırılır), LLM'nin aşağıdakileri yapmasına olanak tanıyan bir özelliktir:

  1. Bir kullanıcı isteğinin belirli bir işlevin çağrılmasından ne zaman fayda sağlayacağını anlama
  2. Söz konusu işlev için gereken parametreleri içeren yapılandırılmış bir JSON nesnesi oluşturun
  3. Uygulamanızın işlevi bu parametrelerle yürütmesine izin verin
  4. İşlevin sonucunu alıp yanıtına dahil edin

LLM'nin ne yapılması gerektiğini açıklamak yerine işlev çağrısı, LLM'nin uygulamanızda somut işlemleri tetiklemesini sağlar.

Flutter uygulamaları için işlev çağırma neden önemlidir?

İşlev çağrısı, doğal dil ile uygulama özellikleri arasında güçlü bir köprü oluşturur:

  1. Doğrudan işlem: Kullanıcılar istediklerini doğal dilde tanımlayabilir ve uygulama somut işlemlerle yanıt verir.
  2. Yapılandırılmış çıkış: LLM, ayrıştırılması gereken metin yerine temiz ve yapılandırılmış veriler oluşturur.
  3. Karmaşık işlemler: LLM'nin harici verilere erişmesine, hesaplama yapmasına veya uygulama durumunu değiştirmesine olanak tanır
  4. Daha iyi kullanıcı deneyimi: Sohbet ile işlevsellik arasında sorunsuz bir entegrasyon sağlar.

Renk uzmanı uygulamanızda işlev çağrısı, kullanıcıların "orman yeşili istiyorum" demesi ve metindeki RGB değerlerini ayrıştırmak zorunda kalmadan kullanıcı arayüzünün anında bu renkle güncellenmesini sağlar.

İşlev tanımlarını tanımlama

İşlev beyanlarınızı tanımlamak için yeni bir lib/services/gemini_tools.dart dosyası oluşturun:

lib/services/gemini_tools.dart

import 'package:firebase_ai/firebase_ai.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);

İşlev bildirimlerini anlama

Bu kodun ne işe yaradığını inceleyelim:

  1. İşlev adlandırma: İşlevinizin amacını açıkça belirtmek için işlevinizi set_color olarak adlandırırsınız.
  2. İşlev açıklaması: LLM'nin ne zaman kullanılacağını anlamasına yardımcı olacak net bir açıklama sağlarsınız.
  3. Parametre tanımları: Yapılandırılmış parametreleri kendi açıklamalarıyla tanımlarsınız:
    • red: RGB'nin kırmızı bileşeni.0,0 ile 1,0 arasında bir sayı olarak belirtilir.
    • green: RGB'nin yeşil bileşeni.0,0 ile 1,0 arasında bir sayı olarak belirtilir.
    • blue: RGB'nin mavi bileşeni.0,0 ile 1,0 arasında bir sayı olarak belirtilir.
  4. Şema türleri: Bunların sayısal değerler olduğunu belirtmek için Schema.number() kullanırsınız.
  5. Araç koleksiyonu: İşlev beyanınızı içeren bir araç listesi oluşturursunuz.

Bu yapılandırılmış yaklaşım, Gemini LLM'nin aşağıdakileri anlamasına yardımcı olur:

  • Bu işlevin ne zaman çağrılacağı
  • Sağlaması gereken parametreler
  • Bu parametreler için geçerli olan kısıtlamalar (değer aralığı gibi)

Gemini model sağlayıcısını güncelleme

Şimdi, lib/providers/gemini.dart dosyanızı Gemini modelini başlatırken işlev tanımlarını içerecek şekilde değiştirin:

lib/providers/gemini.dart

import 'dart:async';

import 'package:firebase_ai/firebase_ai.dart';
import 'package:firebase_core/firebase_core.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 = FirebaseAI.googleAI().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();
}

Anahtar değişiklik, üretken model oluşturulurken tools: geminiTools.tools parametresinin eklenmesidir. Bu sayede Gemini, çağırabileceği işlevlerden haberdar olur.

Sistem istemini güncelleme

Şimdi, LLM'ye yeni set_color aracını kullanma talimatı vermek için sistem isteminizi değiştirmeniz gerekir. assets/system_prompt.md güncellemesi:

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

Sistem isteminde yapılan önemli değişiklikler şunlardır:

  1. Araca giriş: Artık biçimlendirilmiş RGB değerlerini istemek yerine LLM'ye set_color aracı hakkında bilgi veriyorsunuz.
  2. Değiştirilmiş işlem: 3. adımı "yanıtta değerleri biçimlendirin" yerine "değer ayarlamak için aracı kullanın" olarak değiştirirseniz
  3. Güncellenen örnek: Yanıtın, biçimlendirilmiş metin yerine nasıl bir araç çağrısı içermesi gerektiğini gösterirsiniz
  4. Biçimlendirme koşulu kaldırıldı: Yapılandırılmış işlev çağrıları kullandığınızdan artık belirli bir metin biçimine ihtiyacınız yoktur.

Bu güncellenmiş istem, LLM'yi yalnızca RGB değerlerini metin biçiminde sağlamak yerine işlev çağrısı yapmaya yönlendirir.

Riverpod kodu oluşturma

Gerekli Riverpod kodunu oluşturmak için derleme çalıştırıcı komutunu çalıştırın:

dart run build_runner build --delete-conflicting-outputs

Uygulamayı çalıştırın

Bu noktada Gemini, işlev çağrısını kullanmaya çalışan içerikler oluşturur ancak işlev çağrıları için henüz işleyiciler uygulamamışsınızdır. Uygulamayı çalıştırıp bir rengi tanımladığınızda Gemini'nin bir aracı çağırmış gibi yanıt verdiğini görürsünüz ancak bir sonraki adıma kadar kullanıcı arayüzünde renk değişikliği görmezsiniz.

Uygulamanızı çalıştırın:

flutter run -d DEVICE

Gemini LLM&#39;nin kısmi yanıt verdiğini gösteren Colorist uygulaması ekran görüntüsü

"Derin okyanus mavisi" veya "orman yeşili" gibi bir rengi tanımlamayı deneyin ve yanıtları gözlemleyin. LLM, yukarıda tanımlanan işlevleri çağırmaya çalışıyor ancak kodunuz henüz işlev çağrılarını algılamıyor.

İşlev çağırma işlemi

Gemini, işlev çağrısı yaptığında ne olur?

  1. İşlev seçimi: LLM, kullanıcının isteğine göre bir işlev çağrısının yararlı olup olmayacağına karar verir.
  2. Parametre oluşturma: LLM, işlevin şemasına uygun parametre değerleri oluşturur.
  3. İşlev çağrısı biçimi: LLM, yanıtında yapılandırılmış bir işlev çağrısı nesnesi gönderir.
  4. Uygulama işleme: Uygulamanız bu çağrıyı alır ve ilgili işlevi yürütür (sonraki adımda uygulanır).
  5. Yanıt entegrasyonu: Çok turlu görüşmelerde LLM, işlevin sonucunun döndürülmesini bekler.

Uygulamanızın mevcut durumunda ilk üç adım gerçekleşiyor ancak henüz 4. veya 5. adımı (işlev çağrılarını işleme) uygulamadınız. Bunu sonraki adımda yapacaksınız.

Teknik ayrıntılar: Gemini, işlevleri ne zaman kullanacağına nasıl karar verir?

Gemini, işlevlerin ne zaman kullanılacağı konusunda aşağıdakilere göre akıllıca kararlar alır:

  1. Kullanıcı amacı: Kullanıcının isteğinin en iyi şekilde bir işlev tarafından sunulup sunulmayacağı
  2. İşlev alaka düzeyi: Mevcut işlevlerin görevle ne kadar eşleştiği
  3. Parametre kullanılabilirliği: Parametre değerlerini güvenle belirleyip belirleyemediği
  4. Sistem talimatları: İşlev kullanımıyla ilgili sistem isteminizdeki rehberlik

Net işlev tanımları ve sistem talimatları sağlayarak Gemini'yi, renk açıklaması isteklerini set_color işlevini çağırma fırsatları olarak tanıyacak şekilde ayarladınız.

Sırada ne var?

Sonraki adımda, Gemini'den gelen işlev çağrıları için işleyiciler uygulayacaksınız. Bu işlem, döngüyü tamamlayarak kullanıcı açıklamalarının LLM'nin işlev çağrıları aracılığıyla kullanıcı arayüzünde gerçek renk değişikliklerini tetiklemesine olanak tanır.

Sorun giderme

İşlev beyanı sorunları

İşlev bildirimleriyle ilgili hatalarla karşılaşırsanız:

  • Parametre adlarının ve türlerinin beklenenlerle eşleştiğinden emin olun
  • İşlev adının net ve açıklayıcı olup olmadığını doğrulayın
  • İşlev açıklamasının, işlevin amacını doğru bir şekilde açıkladığından emin olun

Sistem istemi sorunları

LLM işlevi kullanmaya çalışmıyorsa:

  • Sistem isteminizin, LLM'ye set_color aracını kullanması gerektiğini net bir şekilde belirttiğinden emin olun.
  • Sistem istemindeki örneğin işlev kullanımını gösterdiğinden emin olun
  • Aracı kullanma talimatlarını daha açık hale getirmeyi deneyin

Genel sorunlar

Başka sorunlarla karşılaşırsanız:

  • İşlev bildirimleriyle ilgili hataları kontrol etmek için konsolu kontrol edin.
  • Araçların modele doğru şekilde iletildiğini doğrulama
  • Riverpod tarafından oluşturulan tüm kodun güncel olduğundan emin olun

Öğrenilen temel kavramlar

  • Flutter uygulamalarında LLM özelliklerini genişletmek için işlev beyanlarını tanımlama
  • Yapılandırılmış veri toplama için parametre şemaları oluşturma
  • Fonksiyon tanımlarını Gemini modeline entegre etme
  • İşlev kullanımını teşvik etmek için sistem istemlerini güncelleme
  • LLM'lerin işlevleri nasıl seçip çağırdığını anlama

Bu adımda, LLM'lerin doğal dil girişi ile yapılandırılmış işlev çağrıları arasındaki boşluğu nasıl doldurabileceği gösterilmektedir. Böylece, konuşma ve uygulama özellikleri arasında sorunsuz entegrasyon için zemin hazırlanır.

6. Araç kullanımı uygulama

Bu adımda, Gemini'den gelen işlev çağrıları için işleyiciler uygulayacaksınız. Bu işlem, doğal dil girişleri ile somut uygulama özellikleri arasındaki iletişim döngüsünü tamamlar ve LLM'nin kullanıcı açıklamalarına göre kullanıcı arayüzünüzü doğrudan değiştirmesine olanak tanır.

Bu adımda neler öğreneceksiniz?

  • LLM uygulamalarında işlev çağrısı ardışık düzeninin tamamını anlama
  • Flutter uygulamasında Gemini'den gelen işlev çağrılarını işleme
  • Uygulama durumunu değiştiren işlev işleyicileri uygulama
  • İşlev yanıtlarını işleme ve sonuçları LLM'ye döndürme
  • LLM ile kullanıcı arayüzü arasında eksiksiz bir iletişim akışı oluşturma
  • Şeffaflık için işlev çağrılarını ve yanıtlarını günlük kaydetme

İşlev çağrısı ardışık düzenini anlama

Uygulamaya geçmeden önce işlev çağrısı ardışık düzeninin tamamını anlayalım:

Uçtan uca akış

  1. Kullanıcı girişi: Kullanıcı bir rengi doğal dilde tanımlar (ör. "orman yeşili")
  2. LLM işleme: Gemini, açıklamayı analiz eder ve set_color işlevini çağırmaya karar verir
  3. İşlev çağrısı oluşturma: Gemini, parametreler (kırmızı, yeşil, mavi değerler) içeren yapılandırılmış bir JSON oluşturur.
  4. İşlev çağrısı alma: Uygulamanız bu yapılandırılmış verileri Gemini'den alır.
  5. İşlev yürütme: Uygulamanız, işlevi sağlanan parametrelerle yürütür.
  6. Durum güncellemesi: İşlev, uygulamanızın durumunu günceller (gösterilen rengi değiştirir).
  7. Yanıt oluşturma: İşleviniz sonuçları LLM'ye döndürür.
  8. Yanıtın dahil edilmesi: LLM bu sonuçları nihai yanıtına dahil eder.
  9. Kullanıcı arayüzü güncellemesi: Kullanıcı arayüzünüz durum değişikliğine tepki vererek yeni rengi gösterir.

Doğru LLM entegrasyonu için iletişim döngüsünün tamamı gereklidir. Bir LLM işlev çağrısı yaptığında, isteği gönderip devam etmez. Bunun yerine, uygulamanızın işlevi yürütmesini ve sonuçları döndürmesini bekler. LLM, nihai yanıtını oluşturmak için bu sonuçları kullanır ve gerçekleştirilen işlemleri kabul eden doğal bir sohbet akışı oluşturur.

İşlev işleyicileri uygulama

İşlev çağrıları için işleyiciler eklemek üzere lib/services/gemini_tools.dart dosyanızı güncelleyelim:

lib/services/gemini_tools.dart

import 'package:colorist_ui/colorist_ui.dart';
import 'package:firebase_ai/firebase_ai.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);

İşlev işleyicilerini anlama

Bu işlev işleyicilerin ne işe yaradığını inceleyelim:

  1. handleFunctionCall: Aşağıdakileri yapan merkezi bir dağıtıcı:
    • Şeffaflık için işlev çağrısını günlük paneline kaydeder
    • İşlev adına göre uygun işleyiciye yönlendirir.
    • LLM'ye geri gönderilecek yapısal bir yanıt döndürür
  2. handleSetColor: set_color işleviniz için özel işleyici. Bu işleyici:
    • Bağımsız değişkenler haritasından RGB değerlerini çıkarır
    • Bunları beklenen türlere (çift) dönüştürür.
    • colorStateNotifier
    • Başarı durumunu ve mevcut renk bilgilerini içeren yapılandırılmış bir yanıt oluşturur
    • Hata ayıklama için işlev sonuçlarını günlüğe kaydeder
  3. handleUnknownFunction: Bilinmeyen işlevler için yedek işleyici. Bu işlevler:
    • Desteklenmeyen işlevle ilgili bir uyarı günlüğe kaydedilir.
    • LLM'ye hata yanıtı döndürür

handleSetColor işlevi, özellikle LLM'nin doğal dil anlama özelliği ile somut kullanıcı arayüzü değişiklikleri arasındaki boşluğu doldurduğu için önemlidir.

İşlev çağrılarını ve yanıtlarını işlemesi için Gemini sohbet hizmetini güncelleme

Şimdi, lib/services/gemini_chat_service.dart dosyasını LLM yanıtlarından gelen işlev çağrılarını işlemek ve sonuçları LLM'ye geri göndermek için güncelleyelim:

lib/services/gemini_chat_service.dart

import 'dart:async';

import 'package:colorist_ui/colorist_ui.dart';
import 'package:firebase_ai/firebase_ai.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);

İletişim akışını anlama

Buradaki önemli ekleme, işlev çağrılarının ve yanıtlarının tamamen işlenmesidir:

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

Bu kod:

  1. LLM yanıtının işlev çağrısı içerip içermediğini kontrol eder
  2. Her işlev çağrısında, handleFunctionCall yönteminizi işlev adı ve bağımsız değişkenlerle çağırır.
  3. Her işlev çağrısının sonuçlarını toplar
  4. Bu sonuçları Content.functionResponses kullanarak LLM'ye geri gönderir.
  5. LLM'nin işlev sonuçlarına verdiği yanıtı işler
  6. Kullanıcı arayüzünü nihai yanıt metniyle günceller

Bu işlem, gidiş dönüş akışı oluşturur:

  • Kullanıcı → LLM: Renk ister
  • LLM → Uygulama: Parametre içeren işlev çağrıları
  • Uygulama → Kullanıcı: Yeni renk gösteriliyor
  • Uygulama → LLM: İşlev sonuçları
  • LLM → Kullanıcı: İşlev sonuçlarını içeren nihai yanıt

Riverpod kodu oluşturma

Gerekli Riverpod kodunu oluşturmak için derleme çalıştırıcı komutunu çalıştırın:

dart run build_runner build --delete-conflicting-outputs

Akıştaki tüm adımları çalıştırıp test etme

Şimdi uygulamanızı çalıştırın:

flutter run -d DEVICE

Gemini LLM&#39;nin işlev çağrısıyla yanıt verdiğini gösteren Colorist uygulaması ekran görüntüsü

Farklı renk açıklamaları girmeyi deneyin:

  • "Koyu bordo istiyorum"
  • "Rahatlatıcı bir gök mavisi göster"
  • "Taze nane yapraklarının rengini söyle"
  • "Sıcak bir gün batımı turuncusu görmek istiyorum"
  • "Gerçekten koyu bir mor olsun"

Aşağıdakileri göreceksiniz:

  1. Mesajınız sohbet arayüzünde görünür.
  2. Gemini'nin yanıtı sohbette görünür
  3. Günlük paneline kaydedilen işlev çağrıları
  4. İşlev sonuçları hemen günlüğe kaydedilir.
  5. Açıklanan rengi göstermek için güncellenen renk dikdörtgeni
  6. RGB değerleri, yeni rengin bileşenlerini gösterecek şekilde güncellenir.
  7. Gemini'nin son yanıtı gösterilir. Bu yanıtta genellikle ayarlanan renk hakkında yorum yapılır.

Günlük paneli, arka planda neler olup bittiğine dair bilgi sağlar. Şunları görürsünüz:

  • Gemini'nin yaptığı tam işlev çağrıları
  • Her RGB değeri için seçtiği parametreler
  • İşlevinizin döndürdüğü sonuçlar
  • Gemini'nin takip yanıtları

Renk durumu bildiricisi

Renkleri güncellemek için kullandığınız colorStateNotifier, colorist_ui paketinin bir parçasıdır. Aşağıdakileri yönetir:

  • Kullanıcı arayüzünde gösterilen mevcut renk
  • Renk geçmişi (son 10 renk)
  • Kullanıcı arayüzü bileşenlerindeki durum değişikliklerinin bildirilmesi

updateColor işlevini yeni RGB değerleriyle çağırdığınızda işlev:

  1. Sağlanan değerlerle yeni bir ColorData nesnesi oluşturur
  2. Uygulama durumundaki mevcut rengi günceller.
  3. Rengi geçmişe ekler.
  4. Riverpod'un durum yönetimi aracılığıyla kullanıcı arayüzü güncellemelerini tetikler.

colorist_ui paketindeki kullanıcı arayüzü bileşenleri bu durumu izler ve değiştiğinde otomatik olarak güncellenerek duyarlı bir deneyim oluşturur.

Hata işleme özelliğini anlama

Uygulamanız güçlü bir hata işleme mekanizması içeriyor:

  1. Try-catch bloğu: Tüm LLM etkileşimlerini sarmalayarak istisnaları yakalar
  2. Hata günlüğü: Günlük panelinde hatalara yığın izlemeleriyle birlikte kayıt yapar.
  3. Kullanıcı geri bildirimi: Sohbette kullanıcı dostu bir hata mesajı sağlar.
  4. Durum temizleme: Hata meydana gelse bile mesaj durumunu kesinleştirir.

Bu sayede, LLM hizmetinde veya işlev yürütmede sorun oluştuğunda bile uygulamanın kararlı kalması ve uygun geri bildirim sağlaması sağlanır.

Kullanıcı deneyimi için işlev çağrısının gücü

Burada başardığınız şey, LLM'lerin nasıl güçlü doğal arayüzler oluşturabileceğini gösterir:

  1. Doğal dil arayüzü: Kullanıcılar amacı günlük dilde ifade eder.
  2. Akıllı yorumlama: LLM, belirsiz açıklamaları kesin değerlere dönüştürür.
  3. Doğrudan manipülasyon: Kullanıcı arayüzü, doğal dile yanıt olarak güncellenir.
  4. Bağlama dayalı yanıtlar: LLM, değişikliklerle ilgili sohbet bağlamı sağlar.
  5. Düşük bilişsel yük: Kullanıcıların RGB değerlerini veya renk teorisini anlaması gerekmez.

Doğal dil ile kullanıcı arayüzü işlemlerini birbirine bağlamak için LLM işlev çağrısı kullanmanın bu kalıbı, renk seçiminin ötesinde sayısız başka alana genişletilebilir.

Sırada ne var?

Sonraki adımda, akış yanıtları uygulayarak kullanıcı deneyimini iyileştirirsiniz. Yanıtın tamamını beklemek yerine, metin parçalarını ve işlev çağrılarını alındıkça işlersiniz. Böylece daha duyarlı ve ilgi çekici bir uygulama oluşturursunuz.

Sorun giderme

İşlev çağrısı sorunları

Gemini işlevlerinizi çağırmıyorsa veya parametreler yanlışsa:

  • İşlev beyanınızın sistem isteminde açıklananlarla eşleştiğini doğrulama
  • Parametre adlarının ve türlerinin tutarlı olup olmadığını kontrol edin
  • Sistem isteminizi, LLM'ye aracı kullanması için açıkça talimat vereceğinden emin olun.
  • İşleyicinizdeki işlev adının, beyandaki adla tam olarak eşleştiğini doğrulayın
  • İşlev çağrılarıyla ilgili ayrıntılı bilgi için günlük panelini inceleyin

İşlev yanıtıyla ilgili sorunlar

İşlev sonuçları LLM'ye düzgün şekilde geri gönderilmiyorsa:

  • İşlevinizin doğru biçimlendirilmiş bir Harita döndürdüğünden emin olun
  • Content.functionResponses öğesinin doğru şekilde oluşturulduğunu doğrulama
  • Günlükte işlev yanıtlarıyla ilgili hataları arayın
  • Yanıt için aynı sohbet oturumunu kullandığınızdan emin olun

Renkli ekran sorunları

Renkler doğru şekilde gösterilmiyorsa:

  • RGB değerlerinin çiftlere doğru şekilde dönüştürüldüğünden emin olun (LLM bunları tam sayı olarak gönderebilir)
  • Değerlerin beklenen aralıkta (0,0 ile 1,0 arasında) olduğunu doğrulayın
  • Renk durumu bildiricisinin doğru şekilde çağrılıp çağrılmadığını kontrol edin
  • İşleve iletilen tam değerleri görmek için günlüğü inceleyin

Genel sorunlar

Genel sorunlar için:

  • Günlüklerde hata veya uyarı olup olmadığını inceleyin
  • Firebase AI Logic bağlantısını doğrulama
  • İşlev parametrelerinde tür uyuşmazlığı olup olmadığını kontrol etme
  • Riverpod tarafından oluşturulan tüm kodun güncel olduğundan emin olun

Öğrenilen temel kavramlar

  • Flutter'da işlev çağırma ardışık düzenini uygulama
  • LLM ile uygulamanız arasında tam iletişim oluşturma
  • LLM yanıtlarından elde edilen yapılandırılmış verileri işleme
  • İşlev sonuçlarını yanıtlara dahil edilmeleri için LLM'ye geri gönderme
  • LLM-uygulama etkileşimlerini görmek için günlük panelini kullanma
  • Doğal dil girişlerini somut kullanıcı arayüzü değişikliklerine bağlama

Bu adım tamamlandığında uygulamanız, LLM entegrasyonu için en güçlü kalıplardan birini gösterir: Doğal dil girişlerini somut kullanıcı arayüzü işlemlerine çevirirken bu işlemleri onaylayan tutarlı bir sohbet sürdürür. Bu sayede, kullanıcılar için büyülü bir deneyim sunan sezgisel ve etkileşimli bir arayüz oluşturulur.

7. Daha iyi kullanıcı deneyimi için yanıtları akış şeklinde gönderme

Bu adımda, Gemini'den gelen akış yanıtlarını uygulayarak kullanıcı deneyimini iyileştirirsiniz. Yanıtın tamamını oluşturulmasını beklemek yerine, metin parçalarını ve işlev çağrılarını alındıkça işlersiniz. Böylece daha duyarlı ve ilgi çekici bir uygulama oluşturursunuz.

Bu adımda ele alınacak konular

  • LLM destekli uygulamalar için akışın önemi
  • Bir Flutter uygulamasında akış şeklinde LLM yanıtları uygulama
  • API'den gelen kısmi metin parçalarını işleme
  • Mesaj çakışmalarını önlemek için görüşme durumunu yönetme
  • Akış yanıtlarında işlev çağrılarını işleme
  • Devam eden yanıtlar için görsel göstergeler oluşturma

Canlı yayınların LLM uygulamaları için önemi

Bu özelliği uygulamadan önce, LLM'lerle mükemmel kullanıcı deneyimleri oluşturmak için akış yanıtlarının neden önemli olduğunu anlayalım:

İyileştirilmiş kullanıcı deneyimi

Yanıtları akış şeklinde sunmak, kullanıcı deneyimi açısından önemli avantajlar sağlar:

  1. Algılanan gecikmenin azaltılması: Kullanıcılar, yanıtın tamamını görmek için birkaç saniye beklemek yerine metnin hemen (genellikle 100-300 milisaniye içinde) görünmeye başladığını görür. Bu anlıklık algısı, kullanıcı memnuniyetini önemli ölçüde artırır.
  2. Doğal konuşma ritmi: Metnin kademeli olarak görünmesi, insanların iletişim şeklini taklit ederek daha doğal bir diyalog deneyimi oluşturur.
  3. Aşamalı bilgi işleme: Kullanıcılar, büyük bir metin bloğunu aynı anda görmek yerine bilgileri geldikçe işlemeye başlayabilir.
  4. Erken kesinti fırsatı: Tam uygulamada kullanıcılar, LLM'nin faydalı olmayan bir yönde ilerlediğini görürse LLM'yi kesintiye uğratabilir veya yönlendirebilir.
  5. Etkinliğin görsel olarak onaylanması: Akış metni, sistemin çalıştığına dair anında geri bildirim sağlayarak belirsizliği azaltır.

Teknik avantajlar

Canlı yayın, kullanıcı deneyimi iyileştirmelerinin yanı sıra teknik avantajlar da sunar:

  1. Erken işlev yürütme: İşlev çağrıları, yanıtın tamamını beklemeden akışta göründüğü anda algılanıp yürütülebilir.
  2. Artımlı kullanıcı arayüzü güncellemeleri: Yeni bilgiler geldikçe kullanıcı arayüzünüzü kademeli olarak güncelleyerek daha dinamik bir deneyim oluşturabilirsiniz.
  3. Sohbet durumu yönetimi: Akış, yanıtların ne zaman tamamlandığı ve ne zaman devam ettiği hakkında net sinyaller sağlayarak daha iyi durum yönetimi sağlar.
  4. Azaltılmış zaman aşımı riskleri: Akış olmayan yanıtlarda uzun süren oluşturma işlemleri bağlantı zaman aşımı riski oluşturur. Akış, bağlantıyı erkenden kurar ve korur.

Renk Uzmanı uygulamanızda akış özelliğini uygulamak, kullanıcıların hem metin yanıtlarını hem de renk değişikliklerini daha hızlı görmesi anlamına gelir. Bu da önemli ölçüde daha duyarlı bir deneyim oluşturur.

Görüşme durumu yönetimi ekleme

Öncelikle, uygulamanın şu anda bir yayın yanıtını işleyip işlemediğini izlemek için bir durum sağlayıcı ekleyelim. lib/services/gemini_chat_service.dart dosyanızı güncelleyin:

lib/services/gemini_chat_service.dart

import 'dart:async';

import 'package:colorist_ui/colorist_ui.dart';
import 'package:firebase_ai/firebase_ai.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);

Akış uygulamasını anlama

Bu kodun ne işe yaradığını inceleyelim:

  1. İleti dizisi durumu izleme:
    • conversationStateProvider, uygulamanın şu anda bir yanıt işleyip işlemediğini izler.
    • İşleme sırasında durum idlebusy olarak değişip tekrar idle olur.
    • Bu sayede, çakışabilecek birden fazla eşzamanlı istek gönderilmez.
  2. Akış başlatma:
    • sendMessageStream(), yanıtın tamamını içeren bir Future yerine yanıt parçalarının akışını döndürür
    • Her bir parça metin, işlev çağrıları veya her ikisi de içerebilir.
  3. Progresif işleme:
    • await for, her bir parçayı geldiği anda gerçek zamanlı olarak işler
    • Metin, kullanıcı arayüzüne hemen eklenir ve akış efekti oluşturulur.
    • İşlev çağrıları, algılandıkları anda yürütülür
  4. İşlev çağrısı işleme:
    • Bir parçada işlev çağrısı algılandığında bu çağrı hemen yürütülür.
    • Sonuçlar başka bir akış çağrısı üzerinden LLM'ye geri gönderilir.
    • LLM'nin bu sonuçlara verdiği yanıt da akış şeklinde işlenir.
  5. Hata işleme ve temizleme:
    • try/catch güçlü hata işleme sağlar
    • finally bloğu, ileti dizisi durumunun düzgün şekilde sıfırlanmasını sağlar.
    • Hata oluşsa bile mesaj her zaman sonlandırılır

Bu uygulama, uygun sohbet durumunu korurken duyarlı ve güvenilir bir akış deneyimi oluşturur.

Ana ekranı, görüşme durumunu bağlayacak şekilde güncelleme

Görüşme durumunu ana ekrana aktarmak için lib/main.dart dosyanızı değiştirin:

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),
      ),
    );
  }
}

Buradaki önemli değişiklik, conversationState öğesinin MainScreen widget'ına aktarılmasıdır. MainScreen (colorist_ui paketi tarafından sağlanır), bir yanıt işlenirken metin girişini devre dışı bırakmak için bu durumu kullanır.

Bu sayede kullanıcı arayüzünün, görüşmenin mevcut durumunu yansıttığı tutarlı bir kullanıcı deneyimi oluşturulur.

Riverpod kodu oluşturma

Gerekli Riverpod kodunu oluşturmak için derleme çalıştırıcı komutunu çalıştırın:

dart run build_runner build --delete-conflicting-outputs

Akış yanıtlarını çalıştırma ve test etme

Uygulamanızı çalıştırın:

flutter run -d DEVICE

Gemini LLM&#39;nin akış şeklinde yanıt verdiğini gösteren renk uzmanı uygulaması ekran görüntüsü

Akış davranışını çeşitli renk açıklamalarıyla test etmeyi deneyin. Aşağıdaki gibi açıklamalar deneyebilirsiniz:

  • "Alacakaranlıkta okyanusun koyu turkuaz rengini göster"
  • "Tropikal çiçekleri hatırlatan canlı bir mercan görmek istiyorum"
  • "Eski ordu kıyafetleri gibi mat bir zeytin yeşili oluşturun"

Akışla ilgili teknik akış ayrıntılı olarak

Yanıtlar canlı olarak yayınlanırken tam olarak ne olduğunu inceleyelim:

Bağlantı kurma

sendMessageStream() numarayı aradığınızda aşağıdakiler gerçekleşir:

  1. Uygulama, Firebase AI Logic hizmetine bağlantı kurar
  2. Kullanıcı isteği hizmete gönderilir
  3. Sunucu, isteği işlemeye başlar
  4. Akış bağlantısı açık kalır ve parçaları aktarmaya hazırdır.

Parça aktarımı

Gemini içerik oluştururken parçalar akış üzerinden gönderilir:

  1. Sunucu, oluşturuldukları sırada metin parçalarını (genellikle birkaç kelime veya cümle) gönderir.
  2. Gemini bir işlev çağrısı yapmaya karar verdiğinde işlev çağrısı bilgilerini gönderir
  3. İşlev çağrılarını ek metin parçaları takip edebilir
  4. Oluşturma işlemi tamamlanana kadar aktarma devam eder.

İlerleme durumu açıklaması

Uygulamanız her bir parçayı kademeli olarak işler:

  1. Her metin parçası mevcut yanıta eklenir.
  2. İşlev çağrıları, algılandıkları anda yürütülür
  3. Kullanıcı arayüzü, hem metin hem de işlev sonuçlarıyla gerçek zamanlı olarak güncellenir.
  4. Yanıtın hâlâ yayınlandığını göstermek için durum izlenir.

Akış tamamlandığında

Oluşturma işlemi tamamlandığında:

  1. Akış sunucu tarafından kapatılır.
  2. await for döngüsünüz doğal olarak sona erer
  3. İleti tamamlandı olarak işaretlenir.
  4. İleti dizisi durumu tekrar etkin değil olarak ayarlanır.
  5. Kullanıcı arayüzü, tamamlanmış durumu yansıtacak şekilde güncellenir.

Akış ve akış dışı karşılaştırması

Akışla ilgili avantajları daha iyi anlamak için akışlı ve akışsız yaklaşımları karşılaştıralım:

En Boy Oranı

Canlı Yayın Dışı

Canlı Yayın

Algılanan gecikme

Tam yanıt hazır olana kadar kullanıcı hiçbir şey görmez.

Kullanıcı ilk kelimeleri milisaniyeler içinde görür

Kullanıcı deneyimi

Uzun bir bekleme süresinin ardından aniden metin görünmesi

Doğal, aşamalı metin görünümü

Durum yönetimi

Daha basit (mesajlar beklemede veya tamamlanmış durumdadır)

Daha karmaşık (mesajlar akış durumunda olabilir)

İşlev yürütme

Yalnızca tam yanıttan sonra gerçekleşir

Yanıt oluşturulurken ortaya çıkar.

Uygulama karmaşıklığı

Uygulaması daha kolaydır

Ek durum yönetimi gerektirir

Hatadan kurtarma

"Ya hep ya hiç" yanıtı

Kısmi yanıtlar da yararlı olabilir

Kod karmaşıklığı

Daha az karmaşık

Akış işleme nedeniyle daha karmaşık

Colorist gibi bir uygulamada, aktarmanın kullanıcı deneyimi açısından sağladığı avantajlar, özellikle de oluşturulması birkaç saniye sürebilecek renk yorumları için uygulama karmaşıklığından daha ağır basar.

Akış kullanıcı deneyimi için en iyi uygulamalar

Kendi LLM uygulamalarınızda akış özelliğini uygularken aşağıdaki en iyi uygulamaları göz önünde bulundurun:

  1. Net görsel göstergeler: Her zaman, akış mesajlarını eksik mesajlardan ayıran net görsel ipuçları sağlayın.
  2. Giriş engelleme: Birden fazla çakışık isteği önlemek için yayın sırasında kullanıcı girişini devre dışı bırakın.
  3. Hata kurtarma: Akış kesintiye uğradığında sorunsuz bir şekilde kurtarma işlemi yapabilecek şekilde kullanıcı arayüzünüzü tasarlayın.
  4. Durum geçişleri: Boş, aktarma ve tamamlandı durumları arasında sorunsuz geçişler yapın.
  5. İlerleme görselleştirme: Etkin işleme sürecini gösteren ince animasyonlar veya göstergeler kullanabilirsiniz.
  6. İptal seçenekleri: Tam bir uygulamada, kullanıcıların devam eden oluşturma işlemlerini iptal etmeleri için yöntemler sağlayın.
  7. İşlev sonucu entegrasyonu: Kullanıcı arayüzünüzü, yayın sırasında görünen işlev sonuçlarını işleyecek şekilde tasarlayın.
  8. Performans optimizasyonu: Hızlı yayın güncellemeleri sırasında kullanıcı arayüzünün yeniden oluşturulmasını en aza indirin

colorist_ui paketi bu en iyi uygulamaların çoğunu sizin için uygular ancak bu uygulamalar, akışlı LLM uygulaması için önemli hususlardır.

Sırada ne var?

Bir sonraki adımda, kullanıcılar geçmişten renk seçtiğinde Gemini'yi bilgilendirerek LLM senkronizasyonunu uygulayacaksınız. Bu sayede, LLM'nin uygulama durumunda kullanıcı tarafından başlatılan değişikliklerden haberdar olduğu daha uyumlu bir deneyim oluşturulur.

Sorun giderme

Akış işleme sorunları

Akış işlemeyle ilgili sorun yaşarsanız:

  • Belirtiler: Kısmi yanıtlar, eksik metin veya ani yayın sonlandırma
  • Çözüm: Ağ bağlantısını kontrol edin ve kodunuzda uygun async/await kalıplarının kullanıldığından emin olun.
  • Teşhis: Yayın işlemeyle ilgili hata mesajları veya uyarılar olup olmadığını görmek için günlük panelini inceleyin.
  • Çözüm: Tüm yayın işleme işlemlerinde try/catch bloklarıyla doğru hata işleme yönteminin kullanılmasını sağlayın

Eksik işlev çağrıları

Akışta işlev çağrıları algılanmazsa:

  • Belirtiler: Metin görünür ancak renkler güncellenmez veya günlükte işlev çağrısı gösterilmez
  • Çözüm: Sistem isteminde işlev çağrılarını kullanmayla ilgili talimatları doğrulayın
  • Teşhis: İşlev çağrılarının alınıp alınmadığını görmek için günlük panelini kontrol edin
  • Çözüm: Sistem isteminizi, LLM'ye set_color aracını kullanması için daha net talimat verecek şekilde ayarlayın.

Genel hata işleme

Diğer sorunlar için:

  • 1. adım: Günlük panelinde hata mesajı olup olmadığını kontrol edin
  • 2. adım: Firebase AI Logic bağlantısını doğrulayın
  • 3. Adım: Riverpod tarafından oluşturulan tüm kodun güncel olduğundan emin olun
  • 4. Adım: Akış uygulamasını, eksik await ifadeleri olup olmadığını kontrol edin.

Öğrenilen temel kavramlar

  • Daha duyarlı bir kullanıcı deneyimi için Gemini API ile akış yanıtları uygulama
  • Akış etkileşimlerini düzgün şekilde işlemek için sohbet durumunu yönetme
  • Gerçek zamanlı mesaj ve işlev çağrılarını geldikçe işleme
  • Yayın sırasında kademeli olarak güncellenen duyarlı kullanıcı arayüzleri oluşturma
  • Doğru eşzamansız kalıplarla eşzamanlı akışları işleme
  • Yanıtlar akış halinde sunulurken uygun görsel geri bildirim sağlama

Akış özelliğini uygulayarak Colorist uygulamanızın kullanıcı deneyimini önemli ölçüde iyileştirdiniz ve gerçekten sohbet ediyormuş hissi veren daha duyarlı ve ilgi çekici bir arayüz oluşturdunuz.

8. LLM Bağlam Senkronizasyonu

Bu bonus adımda, kullanıcılar geçmişten renk seçtiğinde Gemini'yi bilgilendirerek LLM bağlam senkronizasyonunu uygulayacaksınız. Bu sayede LLM, yalnızca açık mesajlar değil, kullanıcının arayüzdeki işlemlerinden de haberdar olur.

Bu adımda ele alınacak konular

  • Kullanıcı arayüzünüz ile LLM arasında LLM bağlamı senkronizasyonu oluşturma
  • Kullanıcı arayüzü etkinliklerini LLM'nin anlayabileceği bir bağlamda serileştirme
  • Sohbet bağlamını kullanıcı işlemlerine göre güncelleme
  • Farklı etkileşim yöntemlerinde tutarlı bir deneyim oluşturma
  • LLM bağlam farkındalığını uygunsuz sohbet mesajlarının ötesine taşıma

LLM Bağlam Senkronizasyonunu Anlama

Geleneksel chatbot'lar yalnızca açık kullanıcı mesajlarına yanıt verir. Bu da kullanıcılar uygulamayla başka yollarla etkileşime geçtiğinde bağlantının kesilmesine neden olur. LLM bağlam senkronizasyonu bu sınırlamayı giderir:

LLM bağlam senkronizasyonunun önemi

Kullanıcılar uygulamanızla kullanıcı arayüzü öğeleri aracılığıyla etkileşime geçtiğinde (ör. geçmişten bir renk seçerek), açıkça belirtmediğiniz sürece LLM ne olduğunu bilemez. LLM Bağlam Senkronizasyonu:

  1. Bağlamı korur: LLM'yi ilgili tüm kullanıcı işlemleri hakkında bilgilendirir.
  2. Tutarlılık sağlar: LLM'nin kullanıcı arayüzü etkileşimlerini tanıdığı tutarlı bir deneyim oluşturur.
  3. Zeka düzeyini artırır: LLM'nin tüm kullanıcı işlemlerine uygun şekilde yanıt vermesine olanak tanır
  4. Kullanıcı deneyimini iyileştirir: Uygulamanın tamamının daha entegre ve duyarlı olmasını sağlar
  5. Kullanıcı çabasını azaltır: Kullanıcıların kullanıcı arayüzü işlemlerini manuel olarak açıklama ihtiyacını ortadan kaldırır

Colorist uygulamanızda, bir kullanıcı geçmişten bir renk seçtiğinde Gemini'nin bu işlemi onaylaması ve seçilen renk hakkında akıllıca yorum yapması, böylece sorunsuz ve bilinçli bir asistan izlenimi vermesi gerekir.

Renk seçimi bildirimleri için Gemini sohbet hizmetini güncelleme

Öncelikle, kullanıcı geçmişten bir renk seçtiğinde LLM'yi bilgilendirmek için GeminiChatService öğesine bir yöntem eklersiniz. lib/services/gemini_chat_service.dart dosyanızı güncelleyin:

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_ai/firebase_ai.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);

En önemli ekleme, notifyColorSelection yöntemidir. Bu yöntem:

  1. Seçilen rengi temsil eden bir ColorData nesnesi alır
  2. Bu bilgileri bir iletiye dahil edilebilecek JSON biçiminde kodlar
  3. LLM'ye kullanıcı seçimini belirten özel biçimlendirilmiş bir mesaj gönderir
  4. Bildirimi işlemek için mevcut sendMessage yöntemini yeniden kullanır

Bu yaklaşım, mevcut ileti işleme altyapınızı kullanarak yinelemeyi önler.

Renk seçimi bildirimlerini bağlamak için ana uygulamayı güncelleme

Ardından, lib/main.dart dosyanızı renk seçimi bildirim işlevini ana ekrana iletecek şekilde değiştirin:

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),
      ),
    );
  }
}

Temel değişiklik, kullanıcı arayüzü etkinliğini (geçmişten bir renk seçme) LLM bildirim sistemine bağlayan notifyColorSelection geri çağırma işlevinin eklenmesidir.

Sistem istemini güncelleme

Ardından, LLM'ye renk seçimi bildirimlerine nasıl yanıt vereceğini bildirmek için sistem isteminizi güncellemeniz gerekir. assets/system_prompt.md dosyanızı değiştirin:

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

En önemli ekleme, "Kullanıcılar Geçmiş Renkleri Seçtiğinde" bölümüdür. Bu bölümde:

  1. Geçmiş seçimi bildirimlerinin kavramını LLM'ye açıklar
  2. Bu bildirimlerin nasıl göründüğüne dair bir örnek sağlar
  3. Uygun bir yanıt örneği gösterir.
  4. Seçimi onaylama ve renk hakkında yorum yapmayla ilgili beklentileri belirler

Bu, LLM'nin bu özel mesajlara nasıl uygun şekilde yanıt vereceğini anlamasına yardımcı olur.

Riverpod kodu oluşturma

Gerekli Riverpod kodunu oluşturmak için derleme çalıştırıcı komutunu çalıştırın:

dart run build_runner build --delete-conflicting-outputs

LLM bağlam senkronizasyonunu çalıştırma ve test etme

Uygulamanızı çalıştırın:

flutter run -d DEVICE

Renk geçmişindeki bir seçime yanıt veren Gemini LLM&#39;yi gösteren Renk Uzmanı uygulaması ekran görüntüsü

LLM bağlam senkronizasyonunu test etmek için:

  1. Öncelikle sohbette açıklayarak birkaç renk oluşturun
    • "Canlı bir mor göster"
    • "Orman yeşili istiyorum"
    • "Bana parlak kırmızı ver"
  2. Ardından, geçmiş şeridindeki renk küçük resimlerinden birini tıklayın.

Şunları gözlemlemeniz gerekir:

  1. Seçilen renk ana ekranda görünür
  2. Sohbette, renk seçimini belirten bir kullanıcı mesajı görünür.
  3. LLM, seçimi onaylayarak ve renk hakkında yorum yaparak yanıt verir
  4. Etkileşimin tamamı doğal ve tutarlı olmalıdır.

Bu sayede LLM hem doğrudan mesajları hem de kullanıcı arayüzü etkileşimlerini algılayıp uygun şekilde yanıt vererek sorunsuz bir deneyim sunar.

LLM bağlam senkronizasyonunun işleyiş şekli

Bu senkronizasyonun işleyiş şekliyle ilgili teknik ayrıntılara göz atalım:

Veri Akışları

  1. Kullanıcı işlemi: Kullanıcı, geçmiş şeridinde bir rengi tıklar
  2. Kullanıcı arayüzü etkinliği: MainScreen widget'ı bu seçimi algılar
  3. Geri çağırma yürütme: notifyColorSelection geri çağırma işlevi tetiklenir.
  4. Mesaj oluşturma: Renk verileri kullanılarak özel biçimlendirilmiş bir mesaj oluşturulur.
  5. LLM işleme: Mesaj, biçimi tanıyan Gemini'ye gönderilir.
  6. Bağlamla ilgili yanıt: Gemini, sistem istemine göre uygun şekilde yanıt verir.
  7. Kullanıcı arayüzü güncellemesi: Yanıt sohbette gösterilerek tutarlı bir deneyim oluşturulur.

Veri serileştirme

Bu yaklaşımın önemli bir yönü, renk verilerini nasıl seri hale getirdiğinizdir:

'User selected color from history: ${json.encode(color.toLLMContextMap())}'

toLLMContextMap() yöntemi (colorist_ui paketi tarafından sağlanır), bir ColorData nesnesini LLM'nin anlayabileceği temel özelliklere sahip bir haritaya dönüştürür. Bu ekip genellikle şu kişilerden oluşur:

  • RGB değerleri (kırmızı, yeşil, mavi)
  • Onaltılık kod gösterimi
  • Renkle ilişkili herhangi bir ad veya açıklama

Bu verileri tutarlı bir şekilde biçimlendirip iletiye ekleyerek LLM'nin uygun şekilde yanıt vermek için ihtiyaç duyduğu tüm bilgilere sahip olmasını sağlarsınız.

LLM bağlam senkronizasyonunun daha geniş uygulamaları

LLM'yi kullanıcı arayüzü etkinlikleri hakkında bilgilendirme yönteminin renk seçiminin dışında birçok uygulaması vardır:

Diğer kullanım alanları

  1. Filtre değişiklikleri: Kullanıcılar verilere filtre uyguladığında LLM'yi bilgilendirme
  2. Gezinme etkinlikleri: Kullanıcılar farklı bölümlere gittiğinde LLM'yi bilgilendirin
  3. Seçim değişiklikleri: Kullanıcılar listelerden veya ızgaralardan öğe seçtiğinde LLM'yi güncelleyin
  4. Tercih güncellemeleri: Kullanıcılar ayarları veya tercihleri değiştirdiğinde LLM'ye bildirin
  5. Veri değiştirme: Kullanıcılar veri eklediğinde, düzenlediğinde veya sildiğinde LLM'yi bilgilendirme

Her durumda, kalıp aynıdır:

  1. Kullanıcı arayüzü etkinliğini algılama
  2. Alakalı verileri serileştirme
  3. LLM'ye özel biçimlendirilmiş bir bildirim gönderme
  4. Sistem istemi aracılığıyla LLM'yi uygun şekilde yanıt vermeye yönlendirin

LLM bağlam senkronizasyonu için en iyi uygulamalar

Uygulamanıza bağlı olarak, etkili LLM bağlam senkronizasyonu için bazı en iyi uygulamaları aşağıda bulabilirsiniz:

1. Tutarlı format

LLM'nin bildirimleri kolayca tanımlayabilmesi için bildirimler için tutarlı bir biçim kullanın:

"User [action] [object]: [structured data]"

2. Zengin bağlam

LLM'nin akıllıca yanıt vermesi için bildirimlere yeterli ayrıntı ekleyin. Renkler için bu, RGB değerleri, onaltılık kodlar ve diğer alakalı özellikler anlamına gelir.

3. Net talimatlar

Sistem isteminde, bildirimlerle nasıl başa çıkılacağı konusunda net talimatlar verin (tercihen örneklerle).

4. Doğal entegrasyon

Bildirimleri, teknik kesintiler olarak değil, sohbet akışında doğal bir şekilde akacak şekilde tasarlayın.

5. Seçmeli bildirim

LLM'yi yalnızca sohbetle alakalı işlemler hakkında bilgilendirin. Her kullanıcı arayüzü etkinliğinin paylaşılması gerekmez.

Sorun giderme

Bildirim sorunları

LLM, renk seçimlerine düzgün yanıt vermiyorsa:

  • Bildirim mesajı biçiminin, sistem isteminde açıklananla eşleştiğinden emin olun.
  • Renk verilerinin düzgün şekilde serileştirildiğini doğrulama
  • Sistem isteminde, seçimlerle ilgili net talimatlar yer almalıdır.
  • Bildirim gönderirken sohbet hizmetinde hata olup olmadığını kontrol etme

Bağlam yönetimi

LLM bağlamı kaybediyor gibi görünüyorsa:

  • Sohbet oturumunun düzgün şekilde sürdürüldüğünü kontrol edin
  • İleti dizisi durumlarının doğru şekilde geçiş yaptığını doğrulama
  • Bildirimlerin aynı sohbet oturumu üzerinden gönderildiğinden emin olun

Genel sorunlar

Genel sorunlar için:

  • Günlüklerde hata veya uyarı olup olmadığını inceleyin
  • Firebase AI Logic bağlantısını doğrulama
  • İşlev parametrelerinde tür uyuşmazlığı olup olmadığını kontrol etme
  • Riverpod tarafından oluşturulan tüm kodun güncel olduğundan emin olun

Öğrenilen temel kavramlar

  • Kullanıcı arayüzü ile LLM arasında LLM bağlamı senkronizasyonu oluşturma
  • Kullanıcı arayüzü etkinliklerini LLM'ye uygun bağlamda serileştirme
  • Farklı etkileşim kalıpları için LLM davranışına rehberlik etme
  • Mesaj ve mesaj dışı etkileşimler arasında tutarlı bir deneyim oluşturma
  • LLM'nin daha geniş uygulama durumu hakkındaki farkındalığını artırma

LLM bağlam senkronizasyonunu uygulayarak LLM'nin yalnızca metin üreten bir araç yerine bilinçli ve duyarlı bir asistan gibi hissettirdiği, gerçekten entegre bir deneyim oluşturdunuz. Bu kalıp, yapay zeka destekli daha doğal ve sezgisel arayüzler oluşturmak için sayısız başka uygulamaya uygulanabilir.

9. Tebrikler!

Renk uzmanı kod laboratuvarını başarıyla tamamladınız. 🎉

Oluşturduğunuz içerikler

Doğal dildeki renk açıklamalarını yorumlamak için Google'ın Gemini API'sini entegre eden, tamamen işlevsel bir Flutter uygulaması oluşturdunuz. Uygulamanız artık şunları yapabilir:

  • "Gün batımı turuncusu" veya "koyu okyanus mavisi" gibi doğal dil açıklamalarını işleme
  • Bu açıklamaları RGB değerlerine akıllıca dönüştürmek için Gemini'yi kullanın
  • Aktarılan yanıtlarla yorumlanan renkleri anlık olarak görüntüleme
  • Hem sohbet hem de kullanıcı arayüzü öğeleri aracılığıyla kullanıcı etkileşimlerini yönetme
  • Farklı etkileşim yöntemlerinde bağlamsal farkındalığı koruma

Şimdi beni ne bekliyor?

Gemini'yi Flutter ile entegre etmenin temellerini öğrendiğinize göre yolculuğunuza devam etmenin bazı yollarını burada bulabilirsiniz:

Renk uzmanı uygulamanızı geliştirme

  • Renk paletleri: Tamamlayıcı veya eşleşen renk şemaları oluşturmak için işlev ekleyin
  • Ses girişi: Sözlü renk açıklamaları için konuşma tanımayı entegre edin
  • Geçmiş yönetimi: Renk gruplarını adlandırma, düzenleme ve dışa aktarma seçenekleri ekleyin
  • Özel istemler: Kullanıcıların sistem istemlerini özelleştirebileceği bir arayüz oluşturun
  • Gelişmiş analizler: En iyi sonuçları sağlayan veya zorluklara neden olan açıklamaları takip edin

Diğer Gemini özelliklerini keşfedin

  • Çok modlu girişler: Fotoğraflardan renkleri ayıklamak için resim girişleri ekleyin
  • İçerik üretme: Açıklamalar veya hikayeler gibi renklerle ilgili içerikler oluşturmak için Gemini'yi kullanın.
  • İşlev çağırma geliştirmeleri: Birden fazla işlevle daha karmaşık araç entegrasyonları oluşturun
  • Güvenlik ayarları: Farklı güvenlik ayarlarını ve bunların yanıtlar üzerindeki etkisini keşfedin

Bu kalıpları diğer alanlara uygulama

  • Belge analizi: Belgeleri anlayabilen ve analiz edebilen uygulamalar oluşturun
  • Yaratıcı yazma yardımı: LLM destekli önerilerle yazma araçları oluşturun
  • Görev otomasyonu: Doğal dili otomatik görevlere dönüştüren uygulamalar tasarlayın
  • Bilgi tabanlı uygulamalar: Belirli alanlarda uzman sistemleri oluşturun

Kaynaklar

Öğrenmeye devam edebileceğiniz bazı değerli kaynaklar:

Resmi dokümanlar

İstem yazma kursu ve kılavuzu

Topluluk

Observable Flutter Agentic serisi

59. bölümde Craig Labenz ve Andrew Brogden bu codelab'i inceleyerek uygulama derlemenin ilgi çekici kısımlarını öne çıkarıyor.

60. bölümde, Craig ve Andrew'a codelab uygulamasını yeni özelliklerle genişletirken ve LLM'leri söyleneni yapmaya çalışırken tekrar katılın.

61. bölümde Craig'e Chris Sells eşlik ediyor. İkili, haber başlıklarını analiz edip bunlarla ilgili görseller oluşturuyor.

Geri bildirim

Bu codelab ile ilgili deneyiminizi bizimle paylaşırsanız seviniriz. Lütfen aşağıdaki yöntemlerden birini kullanarak geri bildirimde bulunun:

Bu codelab'i tamamladığınız için teşekkür ederiz. Flutter ve yapay zekanın kesişimindeki heyecan verici olasılıkları keşfetmeye devam edeceğinizi umuyoruz.