Gemini की मदद से काम करने वाला Flutter ऐप्लिकेशन बनाना

1. Gemini की सुविधाओं वाला Flutter ऐप्लिकेशन बनाना

आपको क्या बनाना है

इस कोडलैब में, आपको Colorist बनाने का तरीका बताया जाएगा. यह एक इंटरैक्टिव Flutter ऐप्लिकेशन है, जो Gemini API की सुविधाओं को सीधे आपके Flutter ऐप्लिकेशन में उपलब्ध कराता है. क्या आपको कभी उपयोगकर्ताओं को अपनी सामान्य भाषा में ऐप्लिकेशन को कंट्रोल करने की सुविधा देनी थी, लेकिन आपको यह नहीं पता था कि इसकी शुरुआत कहां से करें? इस कोडलैब में इसका तरीका बताया गया है.

Colorist की मदद से, उपयोगकर्ता सामान्य भाषा में रंगों के बारे में बता सकते हैं. जैसे, "सूर्यास्त का नारंगी रंग" या "गहरे समुद्र का नीला रंग". इसके बाद, ऐप्लिकेशन:

  • Google के Gemini API का इस्तेमाल करके, इन ब्यौरों को प्रोसेस करता है
  • यह ब्यौरों को सटीक आरजीबी कलर वैल्यू में बदलता है
  • स्क्रीन पर रंग को रीयल-टाइम में दिखाता है
  • इसमें रंग के बारे में तकनीकी जानकारी और दिलचस्प कॉन्टेक्स्ट दिया गया है
  • यह कुकी, हाल ही में जनरेट किए गए रंगों का इतिहास सेव करती है

Colorist ऐप्लिकेशन के स्क्रीनशॉट में, रंग और चैट इंटरफ़ेस दिखाया गया है

इस ऐप्लिकेशन में स्प्लिट-स्क्रीन इंटरफ़ेस है. इसमें एक तरफ़ कलर डिसप्ले एरिया और इंटरैक्टिव चैट सिस्टम है. वहीं, दूसरी तरफ़ ज़्यादा जानकारी वाला लॉग पैनल है, जिसमें एलएलएम के साथ हुई बातचीत का रॉ डेटा दिखाया गया है. इस लॉग से, आपको यह बेहतर तरीके से समझने में मदद मिलती है कि एलएलएम इंटिग्रेशन असल में कैसे काम करता है.

Flutter डेवलपर के लिए यह क्यों ज़रूरी है

एलएलएम, ऐप्लिकेशन के साथ उपयोगकर्ताओं के इंटरैक्ट करने के तरीके में बदलाव ला रहे हैं. हालांकि, इन्हें मोबाइल और डेस्कटॉप ऐप्लिकेशन में असरदार तरीके से इंटिग्रेट करने में कई चुनौतियां आती हैं. इस कोडलैब में, आपको ऐसे पैटर्न के बारे में बताया जाएगा जो सिर्फ़ एपीआई कॉल से आगे बढ़ते हैं.

सीखने का आपका सफ़र

इस कोडलैब में, Colorist को चरण दर चरण बनाने की प्रोसेस के बारे में बताया गया है:

  1. प्रोजेक्ट सेटअप करना - आपको बेसिक Flutter ऐप्लिकेशन स्ट्रक्चर और colorist_ui पैकेज से शुरुआत करनी होगी
  2. Gemini का बुनियादी इंटिग्रेशन - अपने ऐप्लिकेशन को Firebase AI Logic से कनेक्ट करें और एलएलएम कम्यूनिकेशन लागू करें
  3. असरदार प्रॉम्प्ट - एक ऐसा सिस्टम प्रॉम्प्ट बनाएं जो एलएलएम को रंगों के ब्यौरे समझने में मदद करे
  4. फ़ंक्शन के एलान - ऐसे टूल तय करें जिनका इस्तेमाल एलएलएम, आपके ऐप्लिकेशन में रंग सेट करने के लिए कर सकता है
  5. टूल हैंडलिंग - LLM से फ़ंक्शन कॉल प्रोसेस करना और उन्हें अपने ऐप्लिकेशन की स्थिति से कनेक्ट करना
  6. जवाबों को स्ट्रीम करना - एलएलएम के जवाबों को रीयल-टाइम में स्ट्रीम करके, उपयोगकर्ता अनुभव को बेहतर बनाएं
  7. एलएलएम के कॉन्टेक्स्ट को सिंक करना - एलएलएम को उपयोगकर्ता की कार्रवाइयों के बारे में बताकर, एक बेहतर अनुभव देना

आपको क्या सीखने को मिलेगा

  • Flutter ऐप्लिकेशन के लिए, Firebase AI Logic को कॉन्फ़िगर करना
  • एलएलएम के व्यवहार को कंट्रोल करने के लिए, असरदार सिस्टम प्रॉम्प्ट तैयार करना
  • फ़ंक्शन के ऐसे एलान लागू करें जो नैचुरल लैंग्वेज और ऐप्लिकेशन की सुविधाओं के बीच पुल का काम करते हैं
  • उपयोगकर्ता को बेहतर अनुभव देने के लिए, स्ट्रीमिंग के ज़रिए मिले जवाबों को प्रोसेस करना
  • यूज़र इंटरफ़ेस (यूआई) इवेंट और एलएलएम के बीच स्टेट को सिंक करना
  • Riverpod का इस्तेमाल करके, एलएलएम से की गई बातचीत की स्थिति को मैनेज करना
  • एलएलएम की मदद से काम करने वाले ऐप्लिकेशन में, गड़बड़ियों को आसानी से ठीक करना

कोड की झलक: आपको लागू किए जाने वाले कोड की झलक दिखेगी

यहां फ़ंक्शन के एलान की झलक दी गई है. इसे आपको बनाना होगा, ताकि एलएलएम आपके ऐप्लिकेशन में रंग सेट कर सके:

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

इस कोडलैब के बारे में खास जानकारी देने वाला वीडियो

Craig Labenz और Andrew Brogdon को Observable Flutter के एपिसोड #59 में इस कोडलैब के बारे में चर्चा करते हुए देखें:

ज़रूरी शर्तें

इस कोडलैब का ज़्यादा से ज़्यादा फ़ायदा पाने के लिए, आपके पास ये चीज़ें होनी चाहिए:

  • Flutter डेवलपमेंट का अनुभव - Flutter की बुनियादी बातों और Dart सिंटैक्स के बारे में जानकारी
  • एसिंक्रोनस प्रोग्रामिंग की जानकारी - फ़्यूचर, एसिंक/अवेट, और स्ट्रीम की जानकारी
  • Firebase खाता - Firebase को सेट अप करने के लिए, आपको Google खाते की ज़रूरत होगी

आइए, एलएलएम की मदद से काम करने वाला अपना पहला Flutter ऐप्लिकेशन बनाना शुरू करें!

2. प्रोजेक्ट सेटअप करना और इको सेवा

पहले चरण में, प्रोजेक्ट का स्ट्रक्चर सेट अप किया जाएगा. साथ ही, एक ऐसी इको सेवा लागू की जाएगी जिसे बाद में Gemini API इंटिग्रेशन से बदल दिया जाएगा. इससे ऐप्लिकेशन का आर्किटेक्चर तय होता है. साथ ही, यह पक्का होता है कि एलएलएम कॉल की जटिलता को जोड़ने से पहले, आपका यूज़र इंटरफ़ेस (यूआई) सही तरीके से काम कर रहा हो.

इस चरण में आपको क्या सीखने को मिलेगा

  • ज़रूरी डिपेंडेंसी के साथ Flutter प्रोजेक्ट सेट अप करना
  • यूज़र इंटरफ़ेस कॉम्पोनेंट के लिए colorist_ui पैकेज का इस्तेमाल करना
  • इको मैसेज सेवा लागू करना और उसे यूज़र इंटरफ़ेस (यूआई) से कनेक्ट करना

नया Flutter प्रोजेक्ट बनाना

नीचे दिए गए निर्देश का इस्तेमाल करके, नया Flutter प्रोजेक्ट बनाएं:

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

-e फ़्लैग से पता चलता है कि आपको डिफ़ॉल्ट counter ऐप्लिकेशन के बिना एक खाली प्रोजेक्ट चाहिए. इस ऐप्लिकेशन को डेस्कटॉप, मोबाइल, और वेब पर काम करने के लिए डिज़ाइन किया गया है. हालांकि, फ़िलहाल flutterfire पर Linux का इस्तेमाल नहीं किया जा सकता.

डिपेंडेंसी जोड़ें

अपनी प्रोजेक्ट डायरेक्ट्री पर जाएं और ज़रूरी डिपेंडेंसी जोड़ें:

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

इससे ये मुख्य पैकेज जुड़ जाएंगे:

  • colorist_ui: यह एक कस्टम पैकेज है. यह Colorist ऐप्लिकेशन के लिए यूज़र इंटरफ़ेस (यूआई) कॉम्पोनेंट उपलब्ध कराता है
  • flutter_riverpod और riverpod_annotation: इनका इस्तेमाल स्टेट मैनेजमेंट के लिए किया जाता है
  • logging: स्ट्रक्चर्ड लॉगिंग के लिए
  • कोड जनरेट करने और लिंटिंग के लिए डेवलपमेंट डिपेंडेंसी

आपका pubspec.yaml कुछ ऐसा दिखेगा:

pubspec.yaml

name: colorist
description: "A new Flutter project."
publish_to: 'none'
version: 0.1.0

environment:
  sdk: ^3.9.2

dependencies:
  flutter:
    sdk: flutter
  colorist_ui: ^0.3.0
  flutter_riverpod: ^3.0.0
  riverpod_annotation: ^3.0.0

dev_dependencies:
  flutter_test:
    sdk: flutter
  flutter_lints: ^6.0.0
  build_runner: ^2.7.1
  riverpod_generator: ^3.0.0
  riverpod_lint: ^3.0.0
  json_serializable: ^6.11.1

flutter:
  uses-material-design: true

विश्लेषण के विकल्पों को कॉन्फ़िगर करना

अपने प्रोजेक्ट की रूट डायरेक्ट्री में मौजूद analysis_options.yaml फ़ाइल में custom_lint जोड़ें:

include: package:flutter_lints/flutter.yaml

analyzer:
  plugins:
    - custom_lint

इस कॉन्फ़िगरेशन से, Riverpod के लिए खास तौर पर तैयार की गई लिंटिंग की सुविधा मिलती है. इससे कोड की क्वालिटी बनाए रखने में मदद मिलती है.

main.dart फ़ाइल लागू करना

lib/main.dart के कॉन्टेंट को इससे बदलें:

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(chatStateProvider.notifier);
    final logStateNotifier = ref.read(logStateProvider.notifier);

    chatStateNotifier.addUserMessage(message);
    logStateNotifier.logUserText(message);
    chatStateNotifier.addLlmMessage(message, MessageState.complete);
    logStateNotifier.logLlmText(message);
  }
}

यह एक Flutter ऐप्लिकेशन सेट अप करता है. यह ऐप्लिकेशन, इको सेवा लागू करता है. यह सेवा, एलएलएम के व्यवहार की नकल करती है. इसके लिए, यह उपयोगकर्ता के मैसेज को वापस भेजती है.

आर्किटेक्चर को समझना

आइए, कुछ समय निकालकर colorist ऐप्लिकेशन के आर्किटेक्चर के बारे में जानते हैं:

colorist_ui पैकेज

colorist_ui पैकेज में, पहले से बने यूज़र इंटरफ़ेस (यूआई) कॉम्पोनेंट और स्टेट मैनेजमेंट टूल मिलते हैं:

  1. MainScreen: यह मुख्य यूज़र इंटरफ़ेस (यूआई) कॉम्पोनेंट है. यह इन चीज़ों को दिखाता है:
    • डेस्कटॉप पर स्प्लिट-स्क्रीन लेआउट (इंटरैक्शन एरिया और लॉग पैनल)
    • मोबाइल पर टैब वाला इंटरफ़ेस
    • रंग दिखाने की सुविधा, चैट इंटरफ़ेस, और इतिहास के थंबनेल
  2. स्टेट मैनेजमेंट: ऐप्लिकेशन में कई स्टेट नोटिफ़ायर का इस्तेमाल किया जाता है:
    • ChatStateNotifier: यह चैट मैसेज मैनेज करता है
    • ColorStateNotifier: यह मौजूदा रंग और इतिहास को मैनेज करता है
    • LogStateNotifier: यह डीबग करने के लिए लॉग एंट्री मैनेज करता है
  3. मैसेज हैंडल करना: यह ऐप्लिकेशन, मैसेज मॉडल का इस्तेमाल करता है. इसमें अलग-अलग स्थितियां होती हैं:
    • उपयोगकर्ता के मैसेज: उपयोगकर्ता ने डाले हैं
    • एलएलएम मैसेज: एलएलएम (या फ़िलहाल, आपकी इको सेवा) से जनरेट किए जाते हैं
    • MessageState: इससे यह ट्रैक किया जाता है कि एलएलएम के मैसेज पूरे हो गए हैं या अब भी स्ट्रीम किए जा रहे हैं

ऐप्लिकेशन आर्किटेक्चर

ऐप्लिकेशन में इस आर्किटेक्चर का इस्तेमाल किया गया है:

  1. यूज़र इंटरफ़ेस (यूआई) लेयर: यह colorist_ui पैकेज से मिलती है
  2. स्टेट मैनेजमेंट: यह रिएक्टिव स्टेट मैनेजमेंट के लिए Riverpod का इस्तेमाल करता है
  3. सर्विस लेयर: इसमें फ़िलहाल आपकी सामान्य इको सेवा शामिल है. इसे Gemini Chat की सेवा से बदल दिया जाएगा
  4. एलएलएम इंटिग्रेशन: इसे बाद के चरणों में जोड़ा जाएगा

इस तरह से अलग-अलग काम करने से, आपको एलएलएम इंटिग्रेशन को लागू करने पर फ़ोकस करने में मदद मिलती है. वहीं, यूज़र इंटरफ़ेस (यूआई) कॉम्पोनेंट का ध्यान पहले से रखा जाता है.

ऐप्लिकेशन चलाएं

इस कमांड का इस्तेमाल करके ऐप्लिकेशन चलाएं:

flutter run -d DEVICE

DEVICE को अपने टारगेट डिवाइस से बदलें. जैसे, macos, windows, chrome या डिवाइस आईडी.

Colorist ऐप्लिकेशन का स्क्रीनशॉट. इसमें Echo सेवा के लिए रेंडर किया गया मार्कडाउन दिखाया गया है

अब आपको Colorist ऐप्लिकेशन दिखेगा. इसमें ये सुविधाएं होंगी:

  1. कलर डिसप्ले एरिया, जिसमें डिफ़ॉल्ट कलर दिखता है
  2. चैट इंटरफ़ेस, जहां मैसेज टाइप किए जा सकते हैं
  3. चैट इंटरैक्शन दिखाने वाला लॉग पैनल

"मुझे गहरा नीला रंग चाहिए" जैसा कोई मैसेज टाइप करें और भेजें पर क्लिक करें. इको सेवा, आपके मैसेज को दोहराएगी. बाद के चरणों में, आपको इसे Firebase के एआई लॉजिक का इस्तेमाल करके, रंग की असल जानकारी से बदलना होगा.

आगे क्या करना है?

अगले चरण में, Firebase को कॉन्फ़िगर किया जाएगा. साथ ही, Gemini API को बुनियादी तौर पर इंटिग्रेट किया जाएगा, ताकि Gemini की चैट सेवा से आपकी इको सेवा को बदला जा सके. इससे ऐप्लिकेशन को रंगों के बारे में दी गई जानकारी को समझने और स्मार्ट जवाब देने में मदद मिलेगी.

समस्या का हल

यूज़र इंटरफ़ेस (यूआई) पैकेज से जुड़ी समस्याएं

अगर आपको colorist_ui पैकेज से जुड़ी समस्याएं आ रही हैं, तो:

  • पक्का करें कि आपके पास नया वर्शन हो
  • पुष्टि करें कि आपने डिपेंडेंसी को सही तरीके से जोड़ा है
  • देखें कि पैकेज के वर्शन में कोई टकराव तो नहीं है

बिल्ड से जुड़ी गड़बड़ियां

अगर आपको बिल्ड से जुड़ी गड़बड़ियां दिखती हैं, तो:

  • पक्का करें कि आपने Flutter SDK टूल का नया स्टेबल चैनल इंस्टॉल किया हो
  • flutter clean चलाएं, इसके बाद flutter pub get चलाएं
  • गड़बड़ी के मैसेज के लिए, कंसोल आउटपुट देखें

सीखे गए मुख्य कॉन्सेप्ट

  • ज़रूरी डिपेंडेंसी के साथ Flutter प्रोजेक्ट सेट अप करना
  • ऐप्लिकेशन के आर्किटेक्चर और कॉम्पोनेंट की ज़िम्मेदारियों को समझना
  • ऐसी सामान्य सेवा लागू करना जो एलएलएम के व्यवहार की नकल करती हो
  • सेवा को यूज़र इंटरफ़ेस (यूआई) कॉम्पोनेंट से कनेक्ट करना
  • स्टेट मैनेजमेंट के लिए Riverpod का इस्तेमाल करना

3. Gemini Chat के साथ बुनियादी इंटिग्रेशन

इस चरण में, आपको पिछले चरण में इस्तेमाल की गई इको सेवा को Firebase AI Logic का इस्तेमाल करके Gemini API इंटिग्रेशन से बदलना होगा. आपको Firebase को कॉन्फ़िगर करना होगा, ज़रूरी प्रोवाइडर सेट अप करने होंगे, और Gemini API के साथ कम्यूनिकेट करने वाली बुनियादी चैट सेवा लागू करनी होगी.

इस चरण में आपको क्या सीखने को मिलेगा

  • Flutter ऐप्लिकेशन में Firebase सेट अप करना
  • Gemini का ऐक्सेस पाने के लिए, Firebase AI Logic को कॉन्फ़िगर करना
  • Firebase और Gemini की सेवाओं के लिए Riverpod प्रोवाइडर बनाना
  • Gemini API की मदद से, चैट की बुनियादी सेवा लागू करना
  • एसिंक्रोनस एपीआई रिस्पॉन्स और गड़बड़ी की स्थितियों को हैंडल करना

Firebase सेट अप करना

सबसे पहले, आपको अपने Flutter प्रोजेक्ट के लिए Firebase सेट अप करना होगा. इसके लिए, आपको एक Firebase प्रोजेक्ट बनाना होगा. साथ ही, उसमें अपना ऐप्लिकेशन जोड़ना होगा. इसके अलावा, आपको Firebase के एआई लॉजिक की ज़रूरी सेटिंग कॉन्फ़िगर करनी होंगी.

Firebase प्रोजेक्ट बनाना

  1. Firebase कंसोल पर जाएं और अपने Google खाते से साइन इन करें.
  2. Firebase प्रोजेक्ट बनाएं पर क्लिक करें या कोई मौजूदा प्रोजेक्ट चुनें.
  3. अपना प्रोजेक्ट बनाने के लिए, सेटअप विज़र्ड का इस्तेमाल करें.

अपने Firebase प्रोजेक्ट में Firebase AI लॉजिक सेट अप करना

  1. Firebase कंसोल में, अपने प्रोजेक्ट पर जाएं.
  2. बाईं ओर मौजूद साइडबार में, एआई को चुनें.
  3. एआई ड्रॉप-डाउन मेन्यू में, एआई लॉजिक चुनें.
  4. Firebase के एआई लॉजिक कार्ड में जाकर, शुरू करें को चुनें.
  5. अपने प्रोजेक्ट के लिए Gemini Developer API चालू करने के लिए, स्क्रीन पर दिए गए निर्देशों का पालन करें.

FlutterFire सीएलआई इंस्टॉल करना

FlutterFire CLI की मदद से, Flutter ऐप्लिकेशन में Firebase को आसानी से सेट अप किया जा सकता है:

dart pub global activate flutterfire_cli

अपने Flutter ऐप्लिकेशन में Firebase जोड़ना

  1. अपने प्रोजेक्ट में Firebase कोर और Firebase AI लॉजिक पैकेज जोड़ें:
flutter pub add firebase_core firebase_ai
  1. FlutterFire कॉन्फ़िगरेशन कमांड चलाएं:
flutterfire configure

इस कमांड से:

  • आपको अभी-अभी बनाए गए Firebase प्रोजेक्ट को चुनने के लिए कहा जाएगा
  • अपने Flutter ऐप्लिकेशन को Firebase के साथ रजिस्टर करना
  • अपने प्रोजेक्ट के कॉन्फ़िगरेशन के साथ firebase_options.dart फ़ाइल जनरेट करना

यह कमांड, चुने गए प्लैटफ़ॉर्म (iOS, Android, macOS, Windows, वेब) का अपने-आप पता लगा लेगी और उन्हें सही तरीके से कॉन्फ़िगर कर देगी.

प्लैटफ़ॉर्म के हिसाब से कॉन्फ़िगरेशन

Firebase के लिए, Flutter के डिफ़ॉल्ट वर्शन से ज़्यादा वर्शन की ज़रूरत होती है. इसके लिए, नेटवर्क ऐक्सेस की भी ज़रूरत होती है, ताकि Firebase के एआई लॉजिक सर्वर से कम्यूनिकेट किया जा सके.

macOS पर अनुमतियां कॉन्फ़िगर करना

macOS के लिए, आपको अपने ऐप्लिकेशन के एनटाइटलमेंट में नेटवर्क ऐक्सेस चालू करना होगा:

  1. macos/Runner/DebugProfile.entitlements खोलें और यह जोड़ें:

macos/Runner/DebugProfile.entitlements

<key>com.apple.security.network.client</key>
<true/>
  1. इसके अलावा, macos/Runner/Release.entitlements खोलें और वही एंट्री जोड़ें.

iOS डिवाइस के लिए सेटिंग कॉन्फ़िगर करना

iOS के लिए, ios/Podfile में सबसे ऊपर मौजूद, कम से कम वर्शन को अपडेट करें:

ios/Podfile

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

Gemini मॉडल उपलब्ध कराने वाली कंपनियां बनाना

अब Firebase और Gemini के लिए Riverpod प्रोवाइडर बनाए जाएंगे. नई फ़ाइल lib/providers/gemini.dart बनाएं:

lib/providers/gemini.dart

import 'dart:async';

import 'package:firebase_ai/firebase_ai.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

import '../firebase_options.dart';

part 'gemini.g.dart';

@Riverpod(keepAlive: true)
Future<FirebaseApp> firebaseApp(Ref ref) =>
    Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);

@Riverpod(keepAlive: true)
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();
}

यह फ़ाइल, तीन मुख्य प्रोवाइडर के लिए आधार तय करती है. ये प्रोवाइडर, Riverpod के कोड जनरेटर की मदद से dart run build_runner को चलाने पर जनरेट होते हैं. इस कोड में, Riverpod 3 के एनोटेशन पर आधारित अप्रोच का इस्तेमाल किया गया है. इसमें अपडेट किए गए प्रोवाइडर पैटर्न शामिल हैं.

  1. firebaseAppProvider: इस फ़ंक्शन का इस्तेमाल करके, प्रोजेक्ट के कॉन्फ़िगरेशन के साथ Firebase को चालू किया जाता है
  2. geminiModelProvider: Gemini के जनरेटिव मॉडल का इंस्टेंस बनाता है
  3. chatSessionProvider: Gemini मॉडल के साथ चैट सेशन बनाता है और उसे बनाए रखता है

चैट सेशन पर मौजूद keepAlive: true एनोटेशन यह पक्का करता है कि यह ऐप्लिकेशन की पूरी लाइफ़साइकल में बना रहे. इससे बातचीत का कॉन्टेक्स्ट बना रहता है.

Gemini Chat की सेवा लागू करना

चैट सेवा लागू करने के लिए, नई फ़ाइल lib/services/gemini_chat_service.dart बनाएं:

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: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(chatStateProvider.notifier);
    final logStateNotifier = ref.read(logStateProvider.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(keepAlive: true)
GeminiChatService geminiChatService(Ref ref) => GeminiChatService(ref);

इस सेवा के ज़रिए:

  1. यह कुकी, उपयोगकर्ता के मैसेज स्वीकार करती है और उन्हें Gemini API को भेजती है
  2. यह कुकी, मॉडल से मिले जवाबों के साथ चैट इंटरफ़ेस को अपडेट करती है
  3. यह कुकी, सभी कम्यूनिकेशन को लॉग करती है, ताकि एलएलएम के फ़्लो को आसानी से समझा जा सके
  4. उपयोगकर्ता की सही प्रतिक्रिया के साथ गड़बड़ियों को ठीक करता है

ध्यान दें: इस समय, लॉग विंडो, चैट विंडो की तरह ही दिखेगी. फ़ंक्शन कॉल और फिर स्ट्रीमिंग रिस्पॉन्स की सुविधा चालू करने के बाद, लॉग ज़्यादा दिलचस्प हो जाएगा.

Riverpod कोड जनरेट करना

ज़रूरी Riverpod कोड जनरेट करने के लिए, build runner कमांड चलाएं:

dart run build_runner build --delete-conflicting-outputs

इससे .g.dart फ़ाइलें बनेंगी. Riverpod को काम करने के लिए इनकी ज़रूरत होती है.

main.dart फ़ाइल अपडेट करना

Gemini Chat की नई सेवा का इस्तेमाल करने के लिए, अपनी lib/main.dart फ़ाइल अपडेट करें:

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

इस अपडेट में ये मुख्य बदलाव किए गए हैं:

  1. इको सेवा की जगह, Gemini API पर आधारित चैट सेवा का इस्तेमाल करना
  2. when तरीके के साथ Riverpod के AsyncValue पैटर्न का इस्तेमाल करके, लोडिंग और गड़बड़ी वाली स्क्रीन जोड़ना
  3. sendMessage कॉलबैक के ज़रिए, यूज़र इंटरफ़ेस (यूआई) को नई चैट सेवा से कनेक्ट करना

ऐप्लिकेशन चलाएं

इस कमांड का इस्तेमाल करके ऐप्लिकेशन चलाएं:

flutter run -d DEVICE

DEVICE को अपने टारगेट डिवाइस से बदलें. जैसे, macos, windows, chrome या डिवाइस आईडी.

Colorist ऐप्लिकेशन के स्क्रीनशॉट में, Gemini LLM को धूप के रंग के बारे में जानकारी देने का अनुरोध किया गया है

अब मैसेज टाइप करने पर, उसे Gemini API को भेजा जाएगा. इसके बाद, आपको LLM से जवाब मिलेगा, न कि कोई गूंज सुनाई देगी. लॉग पैनल में, एपीआई के साथ हुए इंटरैक्शन दिखेंगे.

एलएलएम के कम्यूनिकेशन को समझना

आइए, एक पल के लिए यह समझते हैं कि Gemini API से कम्यूनिकेट करने पर क्या होता है:

कम्यूनिकेशन फ़्लो

  1. उपयोगकर्ता का इनपुट: उपयोगकर्ता, चैट इंटरफ़ेस में टेक्स्ट डालता है
  2. अनुरोध का फ़ॉर्मैट: ऐप्लिकेशन, Gemini API के लिए टेक्स्ट को Content ऑब्जेक्ट के तौर पर फ़ॉर्मैट करता है
  3. एपीआई कम्यूनिकेशन: टेक्स्ट को Firebase AI Logic के ज़रिए Gemini API पर भेजा जाता है
  4. एलएलएम प्रोसेसिंग: Gemini मॉडल, टेक्स्ट को प्रोसेस करता है और जवाब जनरेट करता है
  5. जवाब को हैंडल करना: ऐप्लिकेशन को जवाब मिलता है और वह यूज़र इंटरफ़ेस (यूआई) को अपडेट करता है
  6. लॉगिंग: पारदर्शिता के लिए, सभी बातचीत को लॉग किया जाता है

चैट सेशन और बातचीत का कॉन्टेक्स्ट

Gemini से की गई बातचीत के सेशन में, मैसेज के बीच के कॉन्टेक्स्ट को बनाए रखा जाता है. इससे बातचीत करने में आसानी होती है. इसका मतलब है कि एलएलएम को मौजूदा सेशन में हुई पिछली बातचीत "याद" रहती है. इससे, बातचीत को ज़्यादा बेहतर तरीके से जारी रखा जा सकता है.

चैट सेशन उपलब्ध कराने वाली कंपनी के लिए keepAlive: true एनोटेशन का इस्तेमाल करने से, यह कॉन्टेक्स्ट ऐप्लिकेशन के पूरे लाइफ़साइकल में बना रहता है. एलएलएम के साथ बातचीत के फ़्लो को स्वाभाविक बनाए रखने के लिए, इस कॉन्टेक्स्ट को बनाए रखना ज़रूरी है.

आगे क्या करना है?

इस समय, Gemini API से कुछ भी पूछा जा सकता है. इस पर कोई पाबंदी नहीं है कि यह किस तरह के सवालों के जवाब देगा. उदाहरण के लिए, इससे गुलाबों के युद्ध के बारे में खास जानकारी माँगी जा सकती है. यह जानकारी, आपके कलर ऐप्लिकेशन के मकसद से जुड़ी नहीं है.

अगले चरण में, आपको एक सिस्टम प्रॉम्प्ट बनाना होगा. इससे Gemini को रंगों के ब्यौरे को ज़्यादा असरदार तरीके से समझने में मदद मिलेगी. इससे यह पता चलेगा कि ऐप्लिकेशन की ज़रूरतों के हिसाब से, एलएलएम के व्यवहार को कैसे पसंद के मुताबिक बनाया जा सकता है. साथ ही, इसकी क्षमताओं को आपके ऐप्लिकेशन के डोमेन पर कैसे फ़ोकस किया जा सकता है.

समस्या का हल

Firebase कॉन्फ़िगरेशन से जुड़ी समस्याएं

अगर आपको Firebase को शुरू करने में गड़बड़ियां मिलती हैं, तो:

  • पक्का करें कि आपकी firebase_options.dart फ़ाइल सही तरीके से जनरेट हुई हो
  • पुष्टि करें कि आपने Firebase AI Logic का ऐक्सेस पाने के लिए, Blaze प्लान पर अपग्रेड किया हो

एपीआई ऐक्सेस करने से जुड़ी गड़बड़ियां

अगर आपको Gemini API को ऐक्सेस करने में गड़बड़ियां मिलती हैं, तो:

  • पुष्टि करें कि आपके Firebase प्रोजेक्ट के लिए बिलिंग की सुविधा सही तरीके से सेट अप की गई हो
  • देखें कि आपके Firebase प्रोजेक्ट में, Firebase AI Logic और Cloud AI API चालू हों
  • इंटरनेट कनेक्टिविटी और फ़ायरवॉल की सेटिंग की जांच करना
  • पुष्टि करें कि मॉडल का नाम (gemini-2.0-flash) सही हो और उपलब्ध हो

बातचीत के कॉन्टेक्स्ट से जुड़ी समस्याएं

अगर आपको लगता है कि Gemini को चैट का पिछला कॉन्टेक्स्ट याद नहीं है, तो:

  • पुष्टि करें कि chatSession फ़ंक्शन को @Riverpod(keepAlive: true) के साथ एनोटेट किया गया हो
  • देखें कि मैसेज के सभी लेन-देन के लिए, एक ही चैट सेशन का इस्तेमाल किया जा रहा हो
  • मैसेज भेजने से पहले, पुष्टि करें कि चैट सेशन सही तरीके से शुरू हो गया हो

प्लैटफ़ॉर्म से जुड़ी समस्याएं

प्लैटफ़ॉर्म से जुड़ी समस्याओं के लिए:

  • iOS/macOS: पक्का करें कि सही एनटाइटलमेंट सेट किए गए हों और कम से कम वर्शन कॉन्फ़िगर किए गए हों
  • Android: पुष्टि करें कि एसडीके का कम से कम वर्शन सही तरीके से सेट किया गया हो
  • कंसोल में, प्लैटफ़ॉर्म के हिसाब से गड़बड़ी के मैसेज देखना

सीखे गए मुख्य कॉन्सेप्ट

  • Flutter ऐप्लिकेशन में Firebase सेट अप करना
  • Gemini का ऐक्सेस पाने के लिए, Firebase AI Logic को कॉन्फ़िगर करना
  • एसिंक्रोनस सेवाओं के लिए Riverpod प्रोवाइडर बनाना
  • ऐसी चैट सेवा लागू करना जो एलएलएम के साथ कम्यूनिकेट करती हो
  • एसिंक्रोनस एपीआई की स्थितियों (लोड हो रहा है, गड़बड़ी, डेटा) को हैंडल करना
  • एलएलएम के कम्यूनिकेशन फ़्लो और चैट सेशन को समझना

4. रंगों के ब्यौरे के लिए असरदार प्रॉम्प्ट

इस चरण में, आपको एक सिस्टम प्रॉम्प्ट बनाना और लागू करना होगा. इससे Gemini को रंगों के ब्यौरे को समझने में मदद मिलेगी. सिस्टम प्रॉम्प्ट, एलएलएम के बर्ताव को अपनी पसंद के मुताबिक़ बनाने का एक असरदार तरीका है. इससे, कोड में बदलाव किए बिना ही एलएलएम को खास टास्क के लिए तैयार किया जा सकता है.

इस चरण में आपको क्या सीखने को मिलेगा

  • सिस्टम प्रॉम्प्ट और एलएलएम ऐप्लिकेशन में उनकी अहमियत के बारे में जानकारी
  • डोमेन से जुड़े टास्क के लिए असरदार प्रॉम्प्ट बनाना
  • Flutter ऐप्लिकेशन में सिस्टम प्रॉम्प्ट लोड करना और उनका इस्तेमाल करना
  • एलएलएम को एक जैसे फ़ॉर्मैट में जवाब देने के लिए गाइड करना
  • यह टेस्ट करना कि सिस्टम प्रॉम्प्ट से एलएलएम के व्यवहार पर क्या असर पड़ता है

सिस्टम प्रॉम्प्ट के बारे में जानकारी

सिस्टम प्रॉम्प्ट को लागू करने से पहले, आइए जानते हैं कि सिस्टम प्रॉम्प्ट क्या होते हैं और ये क्यों ज़रूरी हैं:

सिस्टम प्रॉम्प्ट क्या होते हैं?

सिस्टम प्रॉम्प्ट, एलएलएम को दिया जाने वाला एक खास तरह का निर्देश होता है. इससे एलएलएम को जवाब देने के लिए कॉन्टेक्स्ट, व्यवहार से जुड़े दिशा-निर्देश, और उम्मीदों के बारे में पता चलता है. उपयोगकर्ता के मैसेज के उलट, सिस्टम प्रॉम्प्ट:

  • एलएलएम की भूमिका और पर्सोना तय करना
  • खास जानकारी या क्षमताओं के बारे में बताना
  • फ़ॉर्मैटिंग के निर्देश देना
  • जवाबों पर पाबंदियां सेट करना
  • अलग-अलग स्थितियों को हैंडल करने का तरीका बताएं

सिस्टम प्रॉम्प्ट को एलएलएम के लिए "नौकरी की जानकारी" के तौर पर देखा जा सकता है. इससे मॉडल को यह पता चलता है कि बातचीत के दौरान उसे कैसा व्यवहार करना है.

सिस्टम प्रॉम्प्ट क्यों ज़रूरी होते हैं

सिस्टम प्रॉम्प्ट, एलएलएम के साथ लगातार और काम की बातचीत करने के लिए ज़रूरी होते हैं. ऐसा इसलिए, क्योंकि:

  1. एक जैसा अनुभव यानी कंसिस्टेंसी बनाए रखना: मॉडल को एक जैसे फ़ॉर्मैट में जवाब देने के लिए गाइड करना
  2. बेहतर जवाब पाना: मॉडल को अपने खास डोमेन (आपके मामले में, रंग) पर फ़ोकस करने के लिए कहें
  3. सीमाएं तय करना: तय करें कि मॉडल को क्या करना चाहिए और क्या नहीं करना चाहिए
  4. उपयोगकर्ता अनुभव को बेहतर बनाना: ज़्यादा स्वाभाविक और मददगार इंटरैक्शन पैटर्न बनाना
  5. पोस्ट-प्रोसेसिंग को कम करना: ऐसे फ़ॉर्मैट में जवाब पाएं जिन्हें पार्स करना या दिखाना आसान हो

Colorist ऐप्लिकेशन के लिए, आपको एलएलएम की ज़रूरत होगी. इससे, रंग की जानकारी को लगातार समझा जा सकेगा और आरजीबी वैल्यू को किसी खास फ़ॉर्मैट में दिया जा सकेगा.

सिस्टम प्रॉम्प्ट ऐसेट बनाना

सबसे पहले, आपको एक सिस्टम प्रॉम्प्ट फ़ाइल बनानी होगी. इसे रनटाइम के दौरान लोड किया जाएगा. इस तरीके से, ऐप्लिकेशन को फिर से कंपाइल किए बिना ही प्रॉम्प्ट में बदलाव किया जा सकता है.

यहां दिए गए कॉन्टेंट के साथ एक नई फ़ाइल assets/system_prompt.md बनाएं:

assets/system_prompt.md

# Colorist System Prompt

You are a color expert assistant integrated into a desktop app called Colorist. Your job is to interpret natural language color descriptions and 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

सिस्टम प्रॉम्प्ट के स्ट्रक्चर को समझना

आइए, जानते हैं कि यह प्रॉम्प्ट क्या करता है:

  1. भूमिका की परिभाषा: एलएलएम को "कलर एक्सपर्ट असिस्टेंट" के तौर पर सेट करता है
  2. टास्क के बारे में जानकारी: इसमें मुख्य टास्क के बारे में बताया गया है. जैसे, रंग के ब्यौरे को आरजीबी वैल्यू में बदलना
  3. जवाब का फ़ॉर्मैट: इससे यह तय होता है कि आरजीबी वैल्यू को एक जैसा रखने के लिए, उन्हें किस तरह से फ़ॉर्मैट किया जाना चाहिए
  4. एक्सचेंज का उदाहरण: इससे इंटरैक्शन के अनुमानित पैटर्न का सटीक उदाहरण मिलता है
  5. कभी-कभार आने वाले केस को हैंडल करना: इसमें यह बताया जाता है कि साफ़ तौर पर न दी गई जानकारी को कैसे हैंडल करें
  6. सीमाएं और दिशा-निर्देश: सीमाएं तय करता है, जैसे कि आरजीबी वैल्यू को 0.0 और 1.0 के बीच रखना

इस स्ट्रक्चर्ड अप्रोच से यह पक्का होता है कि एलएलएम के जवाब एक जैसे होंगे, उनमें जानकारी होगी, और उन्हें इस तरह से फ़ॉर्मैट किया जाएगा कि अगर आपको प्रोग्राम के हिसाब से आरजीबी वैल्यू निकालनी हो, तो उन्हें आसानी से पार्स किया जा सके.

pubspec.yaml फ़ाइल अपडेट करना

अब, अपनी pubspec.yaml के सबसे नीचे, ऐसेट डायरेक्ट्री को शामिल करने के लिए अपडेट करें:

pubspec.yaml

flutter:
  uses-material-design: true

  assets:
    - assets/

ऐसेट बंडल को रीफ़्रेश करने के लिए, flutter pub get चलाएं.

सिस्टम प्रॉम्प्ट देने वाला कोई प्लगिन बनाना

सिस्टम प्रॉम्प्ट लोड करने के लिए, नई फ़ाइल lib/providers/system_prompt.dart बनाएं:

lib/providers/system_prompt.dart

import 'package:flutter/services.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'system_prompt.g.dart';

@Riverpod(keepAlive: true)
Future<String> systemPrompt(Ref ref) =>
    rootBundle.loadString('assets/system_prompt.md');

यह प्रोवाइडर, रनटाइम के दौरान प्रॉम्प्ट फ़ाइल को पढ़ने के लिए, Flutter के ऐसेट लोडिंग सिस्टम का इस्तेमाल करता है.

Gemini मॉडल उपलब्ध कराने वाली कंपनी को अपडेट करना

अब सिस्टम प्रॉम्प्ट को शामिल करने के लिए, अपनी lib/providers/gemini.dart फ़ाइल में बदलाव करें:

lib/providers/gemini.dart

import 'dart:async';

import 'package:firebase_ai/firebase_ai.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

import '../firebase_options.dart';
import 'system_prompt.dart';                                          // Add this import

part 'gemini.g.dart';

@Riverpod(keepAlive: true)
Future<FirebaseApp> firebaseApp(Ref ref) =>
    Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);

@Riverpod(keepAlive: true)
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();
}

जनरेटिव मॉडल बनाते समय, systemInstruction: Content.system(systemPrompt) जोड़ने का विकल्प उपलब्ध होता है. इससे Gemini को यह पता चलता है कि इस चैट सेशन में सभी इंटरैक्शन के लिए, आपके निर्देशों को सिस्टम प्रॉम्प्ट के तौर पर इस्तेमाल करना है.

Riverpod कोड जनरेट करना

ज़रूरी Riverpod कोड जनरेट करने के लिए, build runner कमांड चलाएं:

dart run build_runner build --delete-conflicting-outputs

ऐप्लिकेशन को चलाएं और उसकी जांच करें

अब अपना ऐप्लिकेशन चलाएं:

flutter run -d DEVICE

Colorist ऐप्लिकेशन के स्क्रीनशॉट में, Gemini LLM को रंग चुनने वाले ऐप्लिकेशन के लिए जवाब देते हुए दिखाया गया है

इसे अलग-अलग रंगों के ब्यौरे के साथ आज़माएँ:

  • "मुझे स्काई ब्लू रंग चाहिए"
  • "मुझे फ़ॉरेस्ट ग्रीन रंग दिखाओ"
  • "चटक नारंगी रंग में सूर्यास्त की तस्वीर बनाओ"
  • "मुझे ताज़ा लैवेंडर के रंग का पेंट चाहिए"
  • "मुझे गहरे नीले रंग जैसा कुछ दिखाओ"

आपको दिखेगा कि Gemini अब रंगों के बारे में बातचीत के तौर पर जानकारी दे रहा है. साथ ही, आरजीबी वैल्यू को एक ही फ़ॉर्मैट में दिखा रहा है. सिस्टम प्रॉम्प्ट ने एलएलएम को सही तरीके से गाइड किया है, ताकि वह आपको आपकी ज़रूरत के मुताबिक जवाब दे सके.

इसके अलावा, रंगों के कॉन्टेक्स्ट से बाहर जाकर भी इससे कॉन्टेंट जनरेट करने के लिए कहें. जैसे, रोज़ेज़ के युद्धों की मुख्य वजहें. आपको पिछले चरण के मुकाबले अंतर दिखेगा.

खास टास्क के लिए प्रॉम्प्ट इंजीनियरिंग की अहमियत

सिस्टम प्रॉम्प्ट, कला और विज्ञान, दोनों हैं. ये एलएलएम इंटिग्रेशन का एक अहम हिस्सा हैं. इनसे इस बात पर काफ़ी असर पड़ सकता है कि मॉडल आपके ऐप्लिकेशन के लिए कितना मददगार है. आपने यहां प्रॉम्प्ट इंजीनियरिंग का इस्तेमाल किया है. इसका मतलब है कि आपने मॉडल को अपनी ज़रूरत के हिसाब से काम करने के लिए निर्देश दिए हैं.

प्रॉम्प्ट इंजीनियरिंग में ये चीज़ें शामिल होती हैं:

  1. भूमिका की साफ़ तौर पर जानकारी देना: एलएलएम का मकसद क्या है, यह बताना
  2. साफ़ तौर पर दिए गए निर्देश: इसमें साफ़ तौर पर बताया जाता है कि एलएलएम को किस तरह जवाब देना चाहिए
  3. उदाहरणों के साथ जानकारी देना: सिर्फ़ यह बताने के बजाय कि अच्छे जवाब कैसे होते हैं, यह दिखाना कि वे कैसे होते हैं
  4. कभी-कभी होने वाले मामलों को हैंडल करना: एलएलएम को यह निर्देश देना कि वह अस्पष्ट स्थितियों को कैसे हैंडल करे
  5. फ़ॉर्मैट के बारे में खास जानकारी: यह पक्का करना कि जवाबों को एक ही तरह से और इस्तेमाल करने लायक तरीके से व्यवस्थित किया गया हो

आपने जो सिस्टम प्रॉम्प्ट बनाया है वह Gemini की सामान्य क्षमताओं को, रंग की व्याख्या करने वाले एक खास असिस्टेंट में बदल देता है. यह असिस्टेंट, आपके ऐप्लिकेशन की ज़रूरतों के हिसाब से फ़ॉर्मैट किए गए जवाब देता है. यह एक ऐसा पैटर्न है जिसका इस्तेमाल कई अलग-अलग डोमेन और टास्क के लिए किया जा सकता है.

आगे क्या करना है?

अगले चरण में, इस बुनियादी ढांचे को और बेहतर बनाया जाएगा. इसके लिए, फ़ंक्शन के एलान जोड़े जाएंगे. इससे एलएलएम, सिर्फ़ आरजीबी वैल्यू का सुझाव नहीं देगा. इसके बजाय, वह आपके ऐप्लिकेशन में मौजूद फ़ंक्शन को कॉल करके, सीधे तौर पर रंग सेट कर पाएगा. इससे पता चलता है कि एलएलएम, नैचुरल लैंग्वेज और ऐप्लिकेशन की सुविधाओं के बीच के अंतर को कैसे कम कर सकते हैं.

समस्या का हल

ऐसेट लोड होने से जुड़ी समस्याएं

अगर आपको सिस्टम प्रॉम्प्ट लोड करने में गड़बड़ियां मिलती हैं, तो:

  • पुष्टि करें कि आपके pubspec.yaml में ऐसेट डायरेक्ट्री सही तरीके से लिस्ट की गई हो
  • देखें कि rootBundle.loadString() में दिया गया पाथ, आपकी फ़ाइल की जगह से मेल खाता हो
  • ऐसेट बंडल को रीफ़्रेश करने के लिए, flutter clean के बाद flutter pub get चलाएं

जवाबों में अंतर होना

अगर एलएलएम, फ़ॉर्मैट से जुड़े निर्देशों का लगातार पालन नहीं कर रहा है, तो:

  • सिस्टम प्रॉम्प्ट में फ़ॉर्मैट से जुड़ी ज़रूरी शर्तों के बारे में ज़्यादा जानकारी दें
  • अनुमानित पैटर्न दिखाने के लिए, ज़्यादा उदाहरण जोड़ें
  • पक्का करें कि आपने जिस फ़ॉर्मैट में जवाब मांगा है वह मॉडल के हिसाब से सही हो

एपीआई के लिए अनुरोधों की संख्या सीमित करना

अगर आपको दर सीमित करने से जुड़ी गड़बड़ियां मिलती हैं, तो:

  • ध्यान रखें कि Firebase AI Logic सेवा के इस्तेमाल की सीमाएं हैं
  • एक्स्पोनेंशियल बैकऑफ़ के साथ फिर से कोशिश करने का लॉजिक लागू करें
  • कोटे से जुड़ी किसी भी समस्या के लिए, Firebase कंसोल देखें

सीखे गए मुख्य कॉन्सेप्ट

  • एलएलएम ऐप्लिकेशन में सिस्टम प्रॉम्प्ट की भूमिका और अहमियत के बारे में जानकारी
  • साफ़ तौर पर निर्देश, उदाहरण, और शर्तें देकर असरदार प्रॉम्प्ट बनाना
  • Flutter ऐप्लिकेशन में सिस्टम प्रॉम्प्ट लोड करना और उनका इस्तेमाल करना
  • डोमेन से जुड़े टास्क के लिए, एलएलएम के व्यवहार को गाइड करना
  • एलएलएम से मिलने वाले जवाबों को बेहतर बनाने के लिए, प्रॉम्प्ट इंजीनियरिंग का इस्तेमाल करना

इस चरण में दिखाया गया है कि अपने कोड में बदलाव किए बिना, एलएलएम के व्यवहार को कैसे काफ़ी हद तक पसंद के मुताबिक बनाया जा सकता है. इसके लिए, आपको सिस्टम प्रॉम्प्ट में साफ़ तौर पर निर्देश देने होंगे.

5. एलएलएम टूल के लिए फ़ंक्शन के एलान

इस चरण में, फ़ंक्शन के बारे में जानकारी देकर, Gemini को अपने ऐप्लिकेशन में कार्रवाई करने की अनुमति दी जाएगी. इस सुविधा की मदद से, एलएलएम न सिर्फ़ आरजीबी वैल्यू का सुझाव दे सकता है, बल्कि खास टूल कॉल के ज़रिए उन्हें आपके ऐप्लिकेशन के यूज़र इंटरफ़ेस (यूआई) में सेट भी कर सकता है. हालांकि, Flutter ऐप्लिकेशन में LLM के अनुरोधों को पूरा होते हुए देखने के लिए, आपको अगला चरण पूरा करना होगा.

इस चरण में आपको क्या सीखने को मिलेगा

  • एलएलएम फ़ंक्शन कॉलिंग और Flutter ऐप्लिकेशन के लिए इसके फ़ायदों के बारे में जानकारी
  • Gemini के लिए, स्कीमा पर आधारित फ़ंक्शन के एलान तय करना
  • फ़ंक्शन के एलान को Gemini मॉडल के साथ इंटिग्रेट करना
  • टूल की क्षमताओं का इस्तेमाल करने के लिए, सिस्टम प्रॉम्प्ट को अपडेट करना

फ़ंक्शन कॉल करने की सुविधा के बारे में जानकारी

फ़ंक्शन के बारे में जानकारी देने वाले स्टेटमेंट को लागू करने से पहले, आइए जानते हैं कि ये क्या होते हैं और क्यों ज़रूरी होते हैं:

फ़ंक्शन कॉलिंग क्या है?

फ़ंक्शन कॉलिंग (इसे कभी-कभी "टूल का इस्तेमाल" भी कहा जाता है) एक ऐसी सुविधा है जिसकी मदद से एलएलएम ये काम कर सकता है:

  1. यह पहचानना कि किसी उपयोगकर्ता के अनुरोध के लिए, किसी खास फ़ंक्शन को चालू करने से फ़ायदा होगा
  2. उस फ़ंक्शन के लिए ज़रूरी पैरामीटर के साथ एक स्ट्रक्चर्ड JSON ऑब्जेक्ट जनरेट करें
  3. अपने ऐप्लिकेशन को उन पैरामीटर के साथ फ़ंक्शन को लागू करने की अनुमति दें
  4. फ़ंक्शन का नतीजा पाना और उसे अपने जवाब में शामिल करना

एलएलएम सिर्फ़ यह बताने के बजाय कि क्या करना है, फ़ंक्शन कॉलिंग की सुविधा एलएलएम को आपके ऐप्लिकेशन में कार्रवाइयां ट्रिगर करने की अनुमति देती है.

Flutter ऐप्लिकेशन के लिए फ़ंक्शन कॉलिंग क्यों ज़रूरी है

फ़ंक्शन कॉलिंग, आम बोलचाल की भाषा और ऐप्लिकेशन की सुविधाओं के बीच एक मज़बूत पुल बनाती है:

  1. सीधे तौर पर कार्रवाई करना: उपयोगकर्ता, सामान्य भाषा में अपनी ज़रूरत के बारे में बता सकते हैं. इसके बाद, ऐप्लिकेशन सीधे तौर पर कार्रवाई करता है
  2. स्ट्रक्चर्ड आउटपुट: एलएलएम, पार्स किए जाने वाले टेक्स्ट के बजाय साफ़ और स्ट्रक्चर्ड डेटा जनरेट करता है
  3. जटिल कार्रवाइयां: इससे एलएलएम को बाहरी डेटा ऐक्सेस करने, हिसाब लगाने या ऐप्लिकेशन की स्थिति में बदलाव करने की अनुमति मिलती है
  4. बेहतर उपयोगकर्ता अनुभव: बातचीत और फ़ंक्शन के बीच आसान इंटिग्रेशन बनाता है

Colorist ऐप्लिकेशन में, फ़ंक्शन कॉलिंग की सुविधा का इस्तेमाल करके लोग "मुझे फ़ॉरेस्ट ग्रीन रंग चाहिए" कह सकते हैं. इसके बाद, यूज़र इंटरफ़ेस (यूआई) तुरंत उस रंग में अपडेट हो जाता है. इसके लिए, टेक्स्ट से आरजीबी वैल्यू पार्स करने की ज़रूरत नहीं होती.

फ़ंक्शन के एलान तय करना

अपने फ़ंक्शन के एलान तय करने के लिए, नई फ़ाइल lib/services/gemini_tools.dart बनाएं:

lib/services/gemini_tools.dart

import 'package:firebase_ai/firebase_ai.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(keepAlive: true)
GeminiTools geminiTools(Ref ref) => GeminiTools(ref);

फ़ंक्शन के एलान को समझना

आइए, जानते हैं कि यह कोड क्या करता है:

  1. फ़ंक्शन का नाम रखना: फ़ंक्शन का नाम set_color इस तरह से रखें कि उससे उसके मकसद के बारे में साफ़ तौर पर पता चले
  2. फ़ंक्शन का ब्यौरा: आपको फ़ंक्शन का ऐसा ब्यौरा देना होगा जिससे एलएलएम को यह समझने में मदद मिले कि इसका इस्तेमाल कब करना है
  3. पैरामीटर की परिभाषाएं: स्ट्रक्चर्ड पैरामीटर को उनकी जानकारी के साथ तय किया जाता है:
    • red: आरजीबी का लाल कॉम्पोनेंट, जिसे 0.0 से 1.0 के बीच की संख्या के तौर पर तय किया जाता है
    • green: आरजीबी का हरा कॉम्पोनेंट. इसे 0.0 से 1.0 के बीच की संख्या के तौर पर तय किया जाता है
    • blue: आरजीबी का नीला कॉम्पोनेंट. इसे 0.0 और 1.0 के बीच की संख्या के तौर पर तय किया जाता है
  4. स्कीमा टाइप: Schema.number() का इस्तेमाल यह बताने के लिए किया जाता है कि ये संख्यात्मक वैल्यू हैं
  5. टूल का कलेक्शन: इसमें फ़ंक्शन के बारे में जानकारी देने वाले टूल की सूची बनाई जाती है

इस स्ट्रक्चर्ड अप्रोच से, Gemini LLM को यह समझने में मदद मिलती है:

  • इस फ़ंक्शन को कब कॉल करना चाहिए
  • इसे कौनसे पैरामीटर देने होंगे
  • उन पैरामीटर पर कौनसी शर्तें लागू होती हैं, जैसे कि वैल्यू की सीमा

Gemini मॉडल उपलब्ध कराने वाली कंपनी को अपडेट करना

अब, Gemini मॉडल को शुरू करते समय फ़ंक्शन के एलान शामिल करने के लिए, अपनी lib/providers/gemini.dart फ़ाइल में बदलाव करें:

lib/providers/gemini.dart

import 'dart:async';

import 'package:firebase_ai/firebase_ai.dart';
import 'package:firebase_core/firebase_core.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(keepAlive: true)
Future<FirebaseApp> firebaseApp(Ref ref) =>
    Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);

@Riverpod(keepAlive: true)
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();
}

जनरेटिव मॉडल बनाते समय, tools: geminiTools.tools पैरामीटर जोड़ना ज़रूरी है. इससे Gemini को उन फ़ंक्शन के बारे में पता चलता है जिन्हें वह कॉल कर सकता है.

सिस्टम प्रॉम्प्ट को अपडेट करना

अब आपको अपने सिस्टम प्रॉम्प्ट में बदलाव करना होगा, ताकि एलएलएम को set_color टूल का इस्तेमाल करने के बारे में निर्देश दिया जा सके. assets/system_prompt.md को अपडेट करें:

assets/system_prompt.md

# Colorist System Prompt

You are a color expert assistant integrated into a desktop app called Colorist. Your job is to interpret natural language color descriptions and set the appropriate color values using a specialized tool.

## Your Capabilities

You are knowledgeable about colors, color theory, and how to translate natural language descriptions into specific RGB values. You have access to the following tool:

`set_color` - Sets the RGB values for the color display based on a description

## How to Respond to User Inputs

When users describe a color:

1. First, acknowledge their color description with a brief, friendly response
2. Interpret what RGB values would best represent that color description
3. Use the `set_color` tool to set those values (all values should be between 0.0 and 1.0)
4. After setting the color, provide a brief explanation of your interpretation

Example:
User: "I want a sunset orange"
You: "Sunset orange is a warm, vibrant color that captures the golden-red hues of the setting sun. It combines a strong red component with moderate orange tones."

[Then you would call the set_color tool with approximately: red=1.0, green=0.5, blue=0.25]

After the tool call: "I've set a warm orange with strong red, moderate green, and minimal blue components that is reminiscent of the sun low on the horizon."

## When Descriptions are Unclear

If a color description is ambiguous or unclear, please ask the user clarifying questions, one at a time.

## Important Guidelines

- Always keep RGB values between 0.0 and 1.0
- Provide thoughtful, knowledgeable responses about colors
- When possible, include color psychology, associations, or interesting facts about colors
- Be conversational and engaging in your responses
- Focus on being helpful and accurate with your color interpretations

सिस्टम प्रॉम्प्ट में ये मुख्य बदलाव किए गए हैं:

  1. टूल के बारे में जानकारी: अब फ़ॉर्मैट की गई आरजीबी वैल्यू मांगने के बजाय, एलएलएम को set_color टूल के बारे में जानकारी दी जाती है
  2. बदली गई प्रोसेस: आपने तीसरे चरण में "जवाब में वैल्यू को फ़ॉर्मैट करो" के बजाय "वैल्यू सेट करने के लिए टूल का इस्तेमाल करो" को चुना है
  3. अपडेट किया गया उदाहरण: इसमें दिखाया गया है कि जवाब में फ़ॉर्मैट किए गए टेक्स्ट के बजाय, टूल कॉल को शामिल किया जाना चाहिए
  4. फ़ॉर्मैटिंग की ज़रूरी शर्त हटा दी गई है: स्ट्रक्चर्ड फ़ंक्शन कॉल का इस्तेमाल करने पर, अब आपको किसी खास टेक्स्ट फ़ॉर्मैट की ज़रूरत नहीं है

अपडेट किए गए इस प्रॉम्प्ट से, एलएलएम को सिर्फ़ टेक्स्ट फ़ॉर्म में आरजीबी वैल्यू देने के बजाय, फ़ंक्शन कॉलिंग का इस्तेमाल करने का निर्देश मिलता है.

Riverpod कोड जनरेट करना

ज़रूरी Riverpod कोड जनरेट करने के लिए, build runner कमांड चलाएं:

dart run build_runner build --delete-conflicting-outputs

ऐप्लिकेशन चलाना

इस समय, Gemini ऐसा कॉन्टेंट जनरेट करेगा जिसमें फ़ंक्शन कॉलिंग का इस्तेमाल किया गया हो. हालांकि, आपने फ़ंक्शन कॉल के लिए हैंडलर लागू नहीं किए हैं. ऐप्लिकेशन चलाने और किसी रंग के बारे में बताने पर, आपको Gemini का जवाब दिखेगा. ऐसा लगेगा कि उसने किसी टूल का इस्तेमाल किया है. हालांकि, अगले चरण तक आपको यूज़र इंटरफ़ेस (यूआई) में कोई बदलाव नहीं दिखेगा.

अपना ऐप्लिकेशन चलाएं:

flutter run -d DEVICE

Colorist ऐप्लिकेशन का स्क्रीनशॉट. इसमें Gemini LLM, क्वेरी का कुछ हिस्सा जवाब के तौर पर दिखा रहा है

"गहरा नीला" या "हरा" जैसे किसी रंग के बारे में बताएं और देखें कि आपको कैसे जवाब मिलते हैं. एलएलएम, ऊपर बताए गए फ़ंक्शन को कॉल करने की कोशिश कर रहा है. हालांकि, आपका कोड अब तक फ़ंक्शन कॉल का पता नहीं लगा पाया है.

फ़ंक्शन कॉल करने की प्रोसेस

आइए, समझते हैं कि Gemini, फ़ंक्शन कॉलिंग का इस्तेमाल कैसे करता है:

  1. फ़ंक्शन चुनना: एलएलएम यह तय करता है कि उपयोगकर्ता के अनुरोध के आधार पर, फ़ंक्शन कॉल करना मददगार होगा या नहीं
  2. पैरामीटर जनरेट करना: एलएलएम, फ़ंक्शन के स्कीमा के हिसाब से पैरामीटर वैल्यू जनरेट करता है
  3. फ़ंक्शन कॉल का फ़ॉर्मैट: एलएलएम, अपने जवाब में स्ट्रक्चर्ड फ़ंक्शन कॉल ऑब्जेक्ट भेजता है
  4. ऐप्लिकेशन हैंडलिंग: आपका ऐप्लिकेशन इस कॉल को रिसीव करेगा और इससे जुड़ा फ़ंक्शन (अगले चरण में लागू किया गया) एक्ज़ीक्यूट करेगा
  5. जवाब को इंटिग्रेट करना: एक से ज़्यादा बार की जाने वाली बातचीत में, एलएलएम को फ़ंक्शन का नतीजा वापस मिलने की उम्मीद होती है

आपके ऐप्लिकेशन की मौजूदा स्थिति में, पहले तीन चरण पूरे हो चुके हैं. हालांकि, आपने अब तक चौथा या पांचवां चरण (फ़ंक्शन कॉल हैंडल करना) लागू नहीं किया है. इसे अगले चरण में लागू किया जाएगा.

तकनीकी जानकारी: Gemini यह कैसे तय करता है कि फ़ंक्शन का इस्तेमाल कब करना है

Gemini, फ़ंक्शन का इस्तेमाल कब करना है, इसके बारे में इन आधारों पर बेहतर फ़ैसले लेता है:

  1. उपयोगकर्ता का इरादा: क्या उपयोगकर्ता के अनुरोध को किसी फ़ंक्शन के ज़रिए सबसे अच्छी तरह से पूरा किया जा सकता है
  2. फ़ंक्शन की काम की जानकारी: उपलब्ध फ़ंक्शन, टास्क से कितने मिलते-जुलते हैं
  3. पैरामीटर की उपलब्धता: इससे पता चलता है कि पैरामीटर की वैल्यू का सटीक अनुमान लगाया जा सकता है या नहीं
  4. सिस्टम के निर्देश: फ़ंक्शन के इस्तेमाल के बारे में, सिस्टम प्रॉम्प्ट से मिली जानकारी

फ़ंक्शन के बारे में साफ़ तौर पर जानकारी देने और सिस्टम को निर्देश देने से, आपने Gemini को यह समझने में मदद की है कि रंग के बारे में जानकारी देने के अनुरोधों को set_color फ़ंक्शन को कॉल करने के अवसरों के तौर पर पहचाना जाए.

आगे क्या करना है?

अगले चरण में, Gemini से आने वाले फ़ंक्शन कॉल के लिए हैंडलर लागू किए जाएंगे. इससे सर्कल पूरा हो जाएगा. साथ ही, एलएलएम के फ़ंक्शन कॉल के ज़रिए, यूज़र डिस्क्रिप्शन से यूज़र इंटरफ़ेस (यूआई) में रंग के बदलाव ट्रिगर किए जा सकेंगे.

समस्या का हल

फ़ंक्शन के एलान से जुड़ी समस्याएं

अगर आपको फ़ंक्शन के एलान से जुड़ी गड़बड़ियां मिलती हैं, तो:

  • देखें कि पैरामीटर के नाम और टाइप, उम्मीद के मुताबिक हों
  • पुष्टि करें कि फ़ंक्शन का नाम साफ़ तौर पर लिखा गया हो और उसमें जानकारी दी गई हो
  • पक्का करें कि फ़ंक्शन के ब्यौरे में, इसके मकसद के बारे में सही जानकारी दी गई हो

सिस्टम प्रॉम्प्ट से जुड़ी समस्याएं

अगर एलएलएम, फ़ंक्शन का इस्तेमाल करने की कोशिश नहीं कर रहा है, तो:

  • पुष्टि करें कि आपके सिस्टम प्रॉम्प्ट में, एलएलएम को set_color टूल का इस्तेमाल करने के बारे में साफ़ तौर पर बताया गया हो
  • देखें कि सिस्टम प्रॉम्प्ट में दिए गए उदाहरण में, फ़ंक्शन के इस्तेमाल के बारे में बताया गया हो
  • टूल का इस्तेमाल करने के लिए, निर्देश को ज़्यादा साफ़ तौर पर देने की कोशिश करें

आम तौर पर आने वाली समस्याएं

अगर आपको कोई और समस्या आ रही है, तो:

  • फ़ंक्शन के एलान से जुड़ी किसी भी गड़बड़ी के लिए कंसोल देखें
  • पुष्टि करें कि टूल, मॉडल को सही तरीके से पास किए गए हों
  • पक्का करें कि Riverpod से जनरेट किया गया सभी कोड अप-टू-डेट हो

सीखे गए मुख्य कॉन्सेप्ट

  • Flutter ऐप्लिकेशन में एलएलएम की क्षमताओं को बढ़ाने के लिए, फ़ंक्शन के बारे में जानकारी देना
  • स्ट्रक्चर्ड डेटा इकट्ठा करने के लिए पैरामीटर स्कीमा बनाना
  • Gemini मॉडल के साथ फ़ंक्शन के बारे में जानकारी को इंटिग्रेट करना
  • फ़ंक्शन के इस्तेमाल को बढ़ावा देने के लिए, सिस्टम प्रॉम्प्ट को अपडेट किया जा रहा है
  • यह समझना कि एलएलएम, फ़ंक्शन को कैसे चुनते हैं और उन्हें कॉल कैसे करते हैं

इस चरण में दिखाया गया है कि एलएलएम, नैचुरल लैंग्वेज इनपुट और स्ट्रक्चर्ड फ़ंक्शन कॉल के बीच के अंतर को कैसे कम कर सकते हैं. इससे बातचीत और ऐप्लिकेशन की सुविधाओं के बीच आसानी से इंटिग्रेशन किया जा सकता है.

6. टूल हैंडलिंग को लागू करना

इस चरण में, Gemini से आने वाले फ़ंक्शन कॉल के लिए हैंडलर लागू किए जाएँगे. इससे, नैचुरल लैंग्वेज इनपुट और ऐप्लिकेशन की सुविधाओं के बीच कम्यूनिकेशन का दायरा पूरा हो जाता है. इससे एलएलएम को, उपयोगकर्ता की जानकारी के आधार पर सीधे तौर पर आपके यूज़र इंटरफ़ेस (यूआई) में बदलाव करने की अनुमति मिलती है.

इस चरण में आपको क्या सीखने को मिलेगा

  • एलएलएम ऐप्लिकेशन में फ़ंक्शन कॉल करने की पूरी पाइपलाइन को समझना
  • Flutter ऐप्लिकेशन में, Gemini से मिले फ़ंक्शन कॉल को प्रोसेस करना
  • ऐसे फ़ंक्शन हैंडलर लागू करना जो ऐप्लिकेशन की स्थिति में बदलाव करते हैं
  • फ़ंक्शन के जवाबों को मैनेज करना और एलएलएम को नतीजे वापस भेजना
  • एलएलएम और यूज़र इंटरफ़ेस (यूआई) के बीच बातचीत का पूरा फ़्लो बनाना
  • पारदर्शिता के लिए, फ़ंक्शन कॉल और जवाबों को लॉग करना

फ़ंक्शन कॉल करने की पाइपलाइन को समझना

लागू करने की प्रोसेस शुरू करने से पहले, फ़ंक्शन कॉलिंग की पूरी पाइपलाइन को समझ लेते हैं:

एंड-टू-एंड फ़्लो

  1. उपयोगकर्ता का इनपुट: उपयोगकर्ता, सामान्य भाषा में किसी रंग के बारे में बताता है. जैसे, "फ़ॉरेस्ट ग्रीन")
  2. एलएलएम प्रोसेसिंग: Gemini, ब्यौरे का विश्लेषण करता है और set_color फ़ंक्शन को कॉल करने का फ़ैसला करता है
  3. फ़ंक्शन कॉल जनरेट करना: Gemini, पैरामीटर (लाल, हरा, नीला रंग) के साथ स्ट्रक्चर्ड JSON बनाता है
  4. फ़ंक्शन कॉल रिसेप्शन: आपका ऐप्लिकेशन, Gemini से यह स्ट्रक्चर्ड डेटा पाता है
  5. फ़ंक्शन को लागू करना: आपका ऐप्लिकेशन, दिए गए पैरामीटर के साथ फ़ंक्शन को लागू करता है
  6. स्टेट अपडेट: यह फ़ंक्शन, आपके ऐप्लिकेशन की स्थिति को अपडेट करता है. इससे डिसप्ले का रंग बदल जाता है
  7. जवाब जनरेट करना: आपका फ़ंक्शन, एलएलएम को नतीजे वापस भेजता है
  8. जवाब में जानकारी शामिल करना: एलएलएम, इन नतीजों को अपने फ़ाइनल जवाब में शामिल करता है
  9. यूज़र इंटरफ़ेस (यूआई) अपडेट: यूज़र इंटरफ़ेस (यूआई), स्थिति में हुए बदलाव के हिसाब से काम करता है और नया रंग दिखाता है

एलएलएम को सही तरीके से इंटिग्रेट करने के लिए, बातचीत का पूरा साइकल ज़रूरी है. जब कोई एलएलएम फ़ंक्शन कॉल करता है, तो वह सिर्फ़ अनुरोध नहीं भेजता और आगे नहीं बढ़ता. इसके बजाय, यह आपके ऐप्लिकेशन के फ़ंक्शन को लागू करने और नतीजे दिखाने का इंतज़ार करता है. इसके बाद, एलएलएम इन नतीजों का इस्तेमाल करके अपना फ़ाइनल जवाब तैयार करता है. इससे बातचीत का फ़्लो नैचुरल लगता है और इसमें की गई कार्रवाइयों की जानकारी भी शामिल होती है.

फ़ंक्शन हैंडलर लागू करना

फ़ंक्शन कॉल के लिए हैंडलर जोड़ने के लिए, अपनी lib/services/gemini_tools.dart फ़ाइल अपडेट करें:

lib/services/gemini_tools.dart

import 'package:colorist_ui/colorist_ui.dart';
import 'package:firebase_ai/firebase_ai.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(logStateProvider.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(colorStateProvider.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(logStateProvider.notifier);
    logStateNotifier.logFunctionResults(functionResults);
    return functionResults;
  }

  Map<String, Object?> handleUnknownFunction(String functionName) {
    final logStateNotifier = ref.read(logStateProvider.notifier);
    logStateNotifier.logWarning('Unsupported function call $functionName');
    return {
      'success': false,
      'reason': 'Unsupported function call $functionName',
    };
  }                                                                  // To here.
}

@Riverpod(keepAlive: true)
GeminiTools geminiTools(Ref ref) => GeminiTools(ref);

फ़ंक्शन हैंडलर के बारे में जानकारी

आइए, जानते हैं कि ये फ़ंक्शन हैंडलर क्या करते हैं:

  1. handleFunctionCall: यह एक सेंट्रल डिस्पैचर है, जो:
    • यह फ़ंक्शन कॉल को लॉग पैनल में पारदर्शिता के लिए लॉग करता है
    • फ़ंक्शन के नाम के आधार पर, सही हैंडलर पर रूट करता है
    • यह एक स्ट्रक्चर्ड जवाब देता है, जिसे एलएलएम को वापस भेजा जाएगा
  2. handleSetColor: यह आपके set_color फ़ंक्शन के लिए खास हैंडलर होता है. यह:
    • यह आर्ग्युमेंट मैप से आरजीबी वैल्यू निकालता है
    • इन्हें अनुमानित टाइप (डबल) में बदलता है
    • यह कुकी, colorStateNotifier का इस्तेमाल करके ऐप्लिकेशन के रंग की स्थिति को अपडेट करती है
    • यह फ़ंक्शन, स्टेटस और मौजूदा रंग की जानकारी के साथ स्ट्रक्चर्ड जवाब बनाता है
    • यह कुकी, डीबग करने के लिए फ़ंक्शन के नतीजों को लॉग करती है
  3. handleUnknownFunction: यह ऐसे फ़ंक्शन के लिए फ़ालबैक हैंडलर है जिनके बारे में जानकारी नहीं है. यह:
    • यह फ़ंक्शन, इस्तेमाल न की जा सकने वाली सुविधा के बारे में चेतावनी लॉग करता है
    • एलएलएम को गड़बड़ी का मैसेज दिखाता है

handleSetColor फ़ंक्शन खास तौर पर इसलिए अहम है, क्योंकि यह एलएलएम की नैचुरल लैंग्वेज को समझने की क्षमता और यूज़र इंटरफ़ेस (यूआई) में किए गए बदलावों के बीच के अंतर को कम करता है.

फ़ंक्शन कॉल और जवाबों को प्रोसेस करने के लिए, Gemini Chat की सेवा को अपडेट करें

अब, एलएलएम से मिले जवाबों से फ़ंक्शन कॉल को प्रोसेस करने और एलएलएम को नतीजे वापस भेजने के लिए, lib/services/gemini_chat_service.dart फ़ाइल को अपडेट करें:

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: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(chatStateProvider.notifier);
    final logStateNotifier = ref.read(logStateProvider.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(keepAlive: true)
GeminiChatService geminiChatService(Ref ref) => GeminiChatService(ref);

बातचीत के फ़्लो को समझना

यहां मुख्य रूप से, फ़ंक्शन कॉल और जवाबों को पूरी तरह से मैनेज करने की सुविधा जोड़ी गई है:

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

यह कोड:

  1. इस कुकी से यह पता चलता है कि एलएलएम के जवाब में कोई फ़ंक्शन कॉल शामिल है या नहीं
  2. हर फ़ंक्शन कॉल के लिए, फ़ंक्शन के नाम और आर्ग्युमेंट के साथ handleFunctionCall तरीके को लागू करता है
  3. यह कुकी, हर फ़ंक्शन कॉल के नतीजे इकट्ठा करती है
  4. Content.functionResponses का इस्तेमाल करके, इन नतीजों को एलएलएम को वापस भेजता है
  5. यह फ़ंक्शन के नतीजों के लिए, एलएलएम के जवाब को प्रोसेस करता है
  6. यह फ़ंक्शन, यूज़र इंटरफ़ेस (यूआई) को जवाब के फ़ाइनल टेक्स्ट के साथ अपडेट करता है

इससे राउंड ट्रिप फ़्लो बनता है:

  • उपयोगकर्ता → एलएलएम: रंग का अनुरोध करता है
  • एलएलएम → ऐप्लिकेशन: पैरामीटर के साथ फ़ंक्शन कॉल
  • ऐप्लिकेशन → उपयोगकर्ता: नया रंग दिखाया गया
  • ऐप्लिकेशन → एलएलएम: फ़ंक्शन के नतीजे
  • एलएलएम → उपयोगकर्ता: फ़ंक्शन के नतीजों को शामिल करके दिया गया फ़ाइनल जवाब

Riverpod कोड जनरेट करना

ज़रूरी Riverpod कोड जनरेट करने के लिए, build runner कमांड चलाएं:

dart run build_runner build --delete-conflicting-outputs

पूरे फ़्लो को चलाएं और उसकी जांच करें

अब अपना ऐप्लिकेशन चलाएं:

flutter run -d DEVICE

Colorist ऐप्लिकेशन के स्क्रीनशॉट में, Gemini LLM को फ़ंक्शन कॉल के साथ जवाब देते हुए दिखाया गया है

अलग-अलग रंगों के बारे में जानकारी डालकर देखें:

  • "मुझे गहरा किरमिज़ी लाल रंग चाहिए"
  • "मुझे हल्का नीला रंग दिखाओ"
  • "मुझे ताज़े पुदीने की पत्तियों का रंग बताओ"
  • "मुझे गर्म रंग का सूर्यास्त वाला नारंगी रंग देखना है"
  • "इसे गहरे बैंगनी रंग का बनाओ"

अब आपको यह दिखेगा:

  1. चैट इंटरफ़ेस में दिखने वाला आपका मैसेज
  2. चैट में Gemini का जवाब दिख रहा है
  3. फ़ंक्शन कॉल को लॉग पैनल में लॉग किया जा रहा है
  4. फ़ंक्शन के नतीजे तुरंत लॉग किए जा रहे हैं
  5. रंग के बारे में बताई गई जानकारी के हिसाब से, रंग बदलने वाला आयत
  6. नए रंग के कॉम्पोनेंट दिखाने के लिए, आरजीबी वैल्यू अपडेट हो रही हैं
  7. Gemini का फ़ाइनल जवाब दिख रहा है. इसमें अक्सर सेट किए गए रंग के बारे में टिप्पणी की जाती है

लॉग पैनल से, पर्दे के पीछे की गतिविधियों के बारे में जानकारी मिलती है. आपको ये चीज़ें दिखेंगी:

  • Gemini के ज़रिए किए जा रहे फ़ंक्शन कॉल की सटीक जानकारी
  • हर आरजीबी वैल्यू के लिए चुने गए पैरामीटर
  • आपके फ़ंक्शन से मिले नतीजे
  • Gemini से मिले फ़ॉलो-अप जवाब

रंग की स्थिति के बारे में सूचना देने वाला

रंगों को अपडेट करने के लिए इस्तेमाल किया जा रहा colorStateNotifier, colorist_ui पैकेज का हिस्सा है. इसकी मदद से, ये काम किए जा सकते हैं:

  • यूज़र इंटरफ़ेस (यूआई) में दिखाया गया मौजूदा रंग
  • रंग चुनने का इतिहास (पिछले 10 रंग)
  • यूज़र इंटरफ़ेस (यूआई) कॉम्पोनेंट की स्थिति में हुए बदलावों की सूचना

नई आरजीबी वैल्यू के साथ updateColor को कॉल करने पर:

  1. दी गई वैल्यू के साथ एक नया ColorData ऑब्जेक्ट बनाता है
  2. यह ऐप्लिकेशन की स्थिति में मौजूद रंग को अपडेट करता है
  3. इससे इतिहास में रंग जुड़ जाता है
  4. यह Riverpod के स्टेट मैनेजमेंट की मदद से, यूज़र इंटरफ़ेस (यूआई) को अपडेट करता है

colorist_ui पैकेज में मौजूद यूज़र इंटरफ़ेस (यूआई) कॉम्पोनेंट, इस स्थिति पर नज़र रखते हैं. साथ ही, स्थिति में बदलाव होने पर अपने-आप अपडेट हो जाते हैं. इससे, यूज़र इंटरफ़ेस (यूआई) में बदलाव तुरंत दिखते हैं.

गड़बड़ी ठीक करने की सुविधा के बारे में जानकारी

आपके इंटिग्रेशन में, गड़बड़ी को ठीक करने की सुविधा शामिल है:

  1. Try-catch ब्लॉक: यह एलएलएम के साथ होने वाली सभी इंटरैक्शन को रैप करता है, ताकि किसी भी अपवाद को पकड़ा जा सके
  2. गड़बड़ी लॉग करना: स्टैक ट्रेस के साथ लॉग पैनल में गड़बड़ियां रिकॉर्ड करता है
  3. उपयोगकर्ता से मिला सुझाव, शिकायत या राय: चैट में गड़बड़ी के बारे में जानकारी देने वाला मैसेज दिखाता है
  4. स्टेट क्लीनअप: गड़बड़ी होने पर भी मैसेज की स्थिति को फ़ाइनल करता है

इससे यह पक्का होता है कि ऐप्लिकेशन स्थिर बना रहे और एलएलएम सेवा या फ़ंक्शन के एक्ज़ीक्यूशन में समस्याएं आने पर भी सही फ़ीडबैक दे.

उपयोगकर्ता अनुभव के लिए फ़ंक्शन कॉलिंग की सुविधा

आपने यहां जो काम किया है उससे पता चलता है कि एलएलएम, नैचुरल इंटरफ़ेस कैसे बना सकते हैं:

  1. नैचुरल लैंग्वेज इंटरफ़ेस: उपयोगकर्ता अपनी रोज़मर्रा की भाषा में क्वेरी करते हैं
  2. इंटेलिजेंट इंटरप्रिटेशन: एलएलएम, अस्पष्ट ब्यौरों को सटीक वैल्यू में बदलता है
  3. सीधे तौर पर बदलाव करना: यूज़र इंटरफ़ेस (यूआई) में, सामान्य भाषा में दिए गए निर्देश के हिसाब से बदलाव होता है
  4. कॉन्टेक्स्ट के हिसाब से जवाब: एलएलएम, बदलावों के बारे में बातचीत के कॉन्टेक्स्ट की जानकारी देता है
  5. कम कॉग्निटिव लोड: उपयोगकर्ताओं को आरजीबी वैल्यू या कलर थ्योरी समझने की ज़रूरत नहीं होती

नैचुरल लैंग्वेज और यूज़र इंटरफ़ेस (यूआई) की कार्रवाइयों के बीच तालमेल बिठाने के लिए, एलएलएम फ़ंक्शन कॉलिंग के इस पैटर्न का इस्तेमाल, रंग चुनने के अलावा कई अन्य डोमेन में भी किया जा सकता है.

आगे क्या करना है?

अगले चरण में, स्ट्रीमिंग रिस्पॉन्स लागू करके उपयोगकर्ता अनुभव को बेहतर बनाया जाएगा. पूरे जवाब का इंतज़ार करने के बजाय, आपको टेक्स्ट के हिस्सों और फ़ंक्शन कॉल को मिलते ही प्रोसेस करना होगा. इससे, ज़्यादा रिस्पॉन्सिव और दिलचस्प ऐप्लिकेशन बनाया जा सकेगा.

समस्या का हल

फ़ंक्शन कॉल से जुड़ी समस्याएं

अगर Gemini आपके फ़ंक्शन को कॉल नहीं कर रहा है या पैरामीटर गलत हैं, तो:

  • पुष्टि करें कि फ़ंक्शन के एलान में दी गई जानकारी, सिस्टम प्रॉम्प्ट में दी गई जानकारी से मेल खाती हो
  • देखें कि पैरामीटर के नाम और टाइप एक जैसे हों
  • पक्का करें कि आपके सिस्टम प्रॉम्प्ट में, एलएलएम को टूल इस्तेमाल करने के लिए साफ़ तौर पर निर्देश दिया गया हो
  • पुष्टि करें कि आपके हैंडलर में मौजूद फ़ंक्शन का नाम, एलान में दिए गए नाम से पूरी तरह मेल खाता हो
  • फ़ंक्शन कॉल के बारे में ज़्यादा जानकारी के लिए, लॉग पैनल देखें

फ़ंक्शन के जवाब से जुड़ी समस्याएं

अगर फ़ंक्शन के नतीजे, एलएलएम को ठीक से नहीं भेजे जा रहे हैं, तो:

  • देखें कि आपका फ़ंक्शन, सही तरीके से फ़ॉर्मैट किया गया मैप दिखाता हो
  • पुष्टि करें कि Content.functionResponses को सही तरीके से बनाया जा रहा है
  • फ़ंक्शन के जवाबों से जुड़ी किसी भी गड़बड़ी के लिए लॉग देखें
  • पक्का करें कि जवाब देने के लिए, उसी चैट सेशन का इस्तेमाल किया जा रहा हो

रंग दिखने से जुड़ी समस्याएं

अगर रंग सही तरीके से नहीं दिख रहे हैं, तो:

  • पक्का करें कि आरजीबी वैल्यू को डबल में सही तरीके से बदला गया हो. एलएलएम इन्हें पूर्णांक के तौर पर भेज सकता है
  • पुष्टि करें कि वैल्यू, तय की गई रेंज (0.0 से 1.0) में हों
  • देखें कि कलर स्टेट नोटिफ़ायर को सही तरीके से कॉल किया जा रहा हो
  • फ़ंक्शन को पास की जा रही सटीक वैल्यू के लिए लॉग की जांच करें

सामान्य समस्याएं

सामान्य समस्याओं के लिए:

  • गड़बड़ियों या चेतावनियों के लिए लॉग की जांच करना
  • Firebase AI Logic की कनेक्टिविटी की पुष्टि करना
  • फ़ंक्शन पैरामीटर में टाइप के मेल न खाने की किसी भी समस्या की जांच करना
  • पक्का करें कि Riverpod से जनरेट किया गया सभी कोड अप-टू-डेट हो

सीखे गए मुख्य कॉन्सेप्ट

  • Flutter में फ़ंक्शन कॉलिंग की पूरी पाइपलाइन लागू करना
  • एलएलएम और आपके ऐप्लिकेशन के बीच पूरी तरह से कम्यूनिकेशन की सुविधा चालू करना
  • एलएलएम के जवाबों से स्ट्रक्चर्ड डेटा प्रोसेस करना
  • जवाबों में शामिल करने के लिए, फ़ंक्शन के नतीजों को एलएलएम को वापस भेजना
  • एलएलएम और ऐप्लिकेशन के बीच हुए इंटरैक्शन की जानकारी पाने के लिए, लॉग पैनल का इस्तेमाल करना
  • नैचुरल लैंग्वेज इनपुट को यूज़र इंटरफ़ेस में किए गए बदलावों से कनेक्ट करना

इस चरण के पूरा होने के बाद, आपका ऐप्लिकेशन अब एलएलएम इंटिग्रेशन के सबसे असरदार पैटर्न में से एक को दिखाता है: नैचुरल लैंग्वेज इनपुट को यूज़र इंटरफ़ेस (यूआई) की कार्रवाइयों में बदलना. साथ ही, इन कार्रवाइयों को ध्यान में रखते हुए बातचीत को जारी रखना. इससे बातचीत करने वाला एक ऐसा इंटरफ़ेस बनता है जो उपयोगकर्ताओं को काफ़ी पसंद आता है.

7. बेहतर UX के लिए, जवाबों को स्ट्रीम करने की सुविधा

इस चरण में, Gemini से स्ट्रीमिंग के ज़रिए जवाब पाने की सुविधा लागू करके, उपयोगकर्ता अनुभव को बेहतर बनाया जाएगा. पूरे जवाब के जनरेट होने का इंतज़ार करने के बजाय, आपको टेक्स्ट के हिस्सों और फ़ंक्शन कॉल को मिलते ही प्रोसेस करना होगा. इससे ज़्यादा रिस्पॉन्सिव और दिलचस्प ऐप्लिकेशन बनाया जा सकेगा.

इस चरण में आपको क्या करना है

  • एलएलएम की मदद से काम करने वाले ऐप्लिकेशन के लिए स्ट्रीमिंग की अहमियत
  • किसी Flutter ऐप्लिकेशन में एलएलएम के जवाबों को स्ट्रीम करने की सुविधा लागू करना
  • एपीआई से मिलने वाले टेक्स्ट के कुछ हिस्सों को प्रोसेस करना
  • मैसेज के टकराव को रोकने के लिए, बातचीत की स्थिति को मैनेज करना
  • स्ट्रीमिंग रिस्पॉन्स में फ़ंक्शन कॉल को हैंडल करना
  • जवाबों के प्रोसेस में होने की जानकारी देने वाले विज़ुअल इंडिकेटर बनाना

एलएलएम ऐप्लिकेशन के लिए स्ट्रीमिंग क्यों ज़रूरी है

इसे लागू करने से पहले, आइए समझते हैं कि एलएलएम के साथ बेहतरीन उपयोगकर्ता अनुभव देने के लिए, स्ट्रीमिंग रिस्पॉन्स क्यों ज़रूरी हैं:

उपयोगकर्ताओं को मिलेगा बेहतर अनुभव

जवाबों को स्ट्रीम करने से, उपयोगकर्ता अनुभव को बेहतर बनाने में कई फ़ायदे मिलते हैं:

  1. कम समय में जवाब मिलना: उपयोगकर्ताओं को पूरा जवाब मिलने में कई सेकंड का इंतज़ार नहीं करना पड़ता. इसके बजाय, उन्हें तुरंत (आम तौर पर 100 से 300 मि॰से॰ के अंदर) टेक्स्ट दिखने लगता है. इससे लोगों को तुरंत जवाब मिलने का एहसास होता है और वे ज़्यादा संतुष्ट होते हैं.
  2. बातचीत के दौरान टेक्स्ट का धीरे-धीरे दिखना: टेक्स्ट का धीरे-धीरे दिखना, इंसानों के बातचीत करने के तरीके जैसा होता है. इससे बातचीत ज़्यादा नैचुरल लगती है.
  3. जानकारी को धीरे-धीरे प्रोसेस करना: उपयोगकर्ता, जानकारी मिलते ही उसे प्रोसेस करना शुरू कर सकते हैं. उन्हें एक साथ बहुत ज़्यादा टेक्स्ट पढ़ने की ज़रूरत नहीं पड़ती.
  4. जवाब को बीच में रोकने का विकल्प: पूरे ऐप्लिकेशन में, अगर उपयोगकर्ताओं को लगता है कि एलएलएम सही दिशा में काम नहीं कर रहा है, तो वे उसे बीच में रोक सकते हैं या रीडायरेक्ट कर सकते हैं.
  5. गतिविधि की पुष्टि करने के लिए विज़ुअल: स्ट्रीमिंग टेक्स्ट से तुरंत पता चलता है कि सिस्टम काम कर रहा है. इससे अनिश्चितता कम होती है.

तकनीकी फ़ायदे

UX को बेहतर बनाने के साथ-साथ, स्ट्रीमिंग के ये तकनीकी फ़ायदे भी हैं:

  1. फ़ंक्शन को तुरंत लागू करना: फ़ंक्शन कॉल का पता लगाया जा सकता है और उन्हें स्ट्रीम में दिखने के तुरंत बाद लागू किया जा सकता है. इसके लिए, पूरे जवाब का इंतज़ार करने की ज़रूरत नहीं होती.
  2. यूज़र इंटरफ़ेस (यूआई) में धीरे-धीरे अपडेट करना: नई जानकारी मिलने पर, यूज़र इंटरफ़ेस (यूआई) को धीरे-धीरे अपडेट किया जा सकता है. इससे, ज़्यादा डाइनैमिक अनुभव मिलता है.
  3. बातचीत की स्थिति को मैनेज करना: स्ट्रीमिंग से यह साफ़ तौर पर पता चलता है कि जवाब कब पूरे हो गए हैं और कब अब भी प्रोसेस किए जा रहे हैं. इससे बातचीत की स्थिति को बेहतर तरीके से मैनेज किया जा सकता है.
  4. टाइमआउट के जोखिम कम होते हैं: स्ट्रीमिंग के बिना जवाब देने पर, लंबे समय तक जनरेट होने वाले जवाबों से कनेक्शन टाइमआउट होने का जोखिम होता है. स्ट्रीमिंग से, कनेक्शन जल्दी बन जाता है और बना रहता है.

Colorist ऐप्लिकेशन के लिए, स्ट्रीमिंग की सुविधा लागू करने का मतलब है कि उपयोगकर्ताओं को टेक्स्ट वाले जवाब और रंग में हुए बदलाव, दोनों तुरंत दिखेंगे. इससे उन्हें बेहतर अनुभव मिलेगा.

बातचीत की स्थिति को मैनेज करने की सुविधा जोड़ना

सबसे पहले, एक स्टेट प्रोवाइडर जोड़ते हैं. इससे यह ट्रैक किया जा सकेगा कि ऐप्लिकेशन फ़िलहाल स्ट्रीमिंग रिस्पॉन्स को हैंडल कर रहा है या नहीं. अपनी lib/services/gemini_chat_service.dart फ़ाइल अपडेट करें:

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

class ConversationStateNotifier extends Notifier<ConversationState> {  // Add from here...
  @override
  ConversationState build() => ConversationState.idle;

  void busy() {
    state = ConversationState.busy;
  }

  void idle() {
    state = ConversationState.idle;
  }
}

final conversationStateProvider =
    NotifierProvider<ConversationStateNotifier, ConversationState>(
      ConversationStateNotifier.new,
    );                                                                 // 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(chatStateProvider.notifier);
    final logStateNotifier = ref.read(logStateProvider.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.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.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(chatStateProvider.notifier);
    final logStateNotifier = ref.read(logStateProvider.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(keepAlive: true)
GeminiChatService geminiChatService(Ref ref) => GeminiChatService(ref);

स्ट्रीमिंग लागू करने के बारे में जानकारी

आइए, जानते हैं कि यह कोड क्या करता है:

  1. बातचीत की स्थिति को ट्रैक करना:
    • conversationStateProvider यह ट्रैक करता है कि ऐप्लिकेशन फ़िलहाल किसी जवाब को प्रोसेस कर रहा है या नहीं
    • प्रोसेसिंग के दौरान, स्थिति idlebusy में बदल जाती है. इसके बाद, यह वापस idle पर आ जाती है
    • इससे एक साथ कई ऐसे अनुरोध नहीं किए जा सकते जो एक-दूसरे से टकरा सकते हैं
  2. स्ट्रीम शुरू करना:
    • sendMessageStream(), पूरे जवाब के साथ Future के बजाय, जवाब के हिस्सों की स्ट्रीम दिखाता है
    • हर चंक में टेक्स्ट, फ़ंक्शन कॉल या दोनों शामिल हो सकते हैं
  3. प्रोग्रेसिव प्रोसेसिंग:
    • await for, हर चंक को रीयल-टाइम में प्रोसेस करता है
    • टेक्स्ट को यूज़र इंटरफ़ेस (यूआई) में तुरंत जोड़ दिया जाता है. इससे स्ट्रीमिंग इफ़ेक्ट मिलता है
    • फ़ंक्शन कॉल का पता चलते ही उन्हें लागू कर दिया जाता है
  4. फ़ंक्शन कॉल हैंडलिंग:
    • किसी चंक में फ़ंक्शन कॉल का पता चलने पर, उसे तुरंत एक्ज़ीक्यूट किया जाता है
    • नतीजों को एलएलएम पर वापस भेजने के लिए, एक और स्ट्रीमिंग कॉल किया जाता है
    • इन नतीजों के आधार पर एलएलएम के जवाब को भी स्ट्रीम किया जाता है
  5. गड़बड़ी ठीक करना और डेटा को व्यवस्थित करना:
    • try/catch में गड़बड़ी ठीक करने की बेहतर सुविधा मिलती है
    • finally ब्लॉक से यह पक्का होता है कि बातचीत की स्थिति को सही तरीके से रीसेट किया गया है
    • गड़बड़ियां होने पर भी मैसेज हमेशा फ़ाइनल किया जाता है

इस सुविधा को लागू करने से, बातचीत की स्थिति को बनाए रखते हुए, स्ट्रीमिंग का बेहतर अनुभव मिलता है.

बातचीत की स्थिति को कनेक्ट करने के लिए, मुख्य स्क्रीन को अपडेट करें

बातचीत की स्थिति को मुख्य स्क्रीन पर भेजने के लिए, अपनी lib/main.dart फ़ाइल में बदलाव करें:

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

यहां मुख्य बदलाव यह है कि conversationState को MainScreen विजेट में पास किया जा रहा है. MainScreen (colorist_ui पैकेज से मिला) इस स्थिति का इस्तेमाल, जवाब प्रोसेस होने के दौरान टेक्स्ट इनपुट को बंद करने के लिए करेगा.

इससे उपयोगकर्ता को बेहतर अनुभव मिलता है. साथ ही, यूज़र इंटरफ़ेस (यूआई) में बातचीत की मौजूदा स्थिति दिखती है.

Riverpod कोड जनरेट करना

ज़रूरी Riverpod कोड जनरेट करने के लिए, build runner कमांड चलाएं:

dart run build_runner build --delete-conflicting-outputs

स्ट्रीम किए जा रहे जवाबों को चलाना और उनकी जांच करना

अपना ऐप्लिकेशन चलाएं:

flutter run -d DEVICE

Colorist ऐप्लिकेशन के स्क्रीनशॉट में, Gemini LLM को स्ट्रीम किए जा रहे जवाब को दिखाया गया है

अब अलग-अलग रंगों के ब्यौरे के साथ, स्ट्रीमिंग की सुविधा को आज़माएँ. इस तरह के ब्यौरे आज़माएं:

  • "मुझे शाम के समय समुद्र का गहरा नीला रंग दिखाओ"
  • "मुझे चमकीले रंग का ऐसा कोरल दिखाओ जो मुझे ट्रॉपिकल फूलों की याद दिलाए"
  • "सेना की पुरानी वर्दी की तरह, हल्का जैतूनी हरा रंग बनाओ"

स्ट्रीमिंग का तकनीकी फ़्लो

आइए, देखते हैं कि जवाब को स्ट्रीम करने पर क्या होता है:

कनेक्शन स्थापित करना

sendMessageStream() को कॉल करने पर, ये होता है:

  1. ऐप्लिकेशन, Firebase AI Logic सेवा से कनेक्ट होता है
  2. उपयोगकर्ता का अनुरोध सेवा को भेजा जाता है
  3. सर्वर, अनुरोध को प्रोसेस करना शुरू करता है
  4. स्ट्रीम कनेक्शन खुला रहता है और डेटा के छोटे-छोटे हिस्से ट्रांसमिट करने के लिए तैयार रहता है

डेटा को छोटे-छोटे हिस्सों में ट्रांसफ़र करना

Gemini के कॉन्टेंट जनरेट करने के दौरान, स्ट्रीम के ज़रिए चंक भेजे जाते हैं:

  1. सर्वर, टेक्स्ट के छोटे-छोटे हिस्से जनरेट होने पर उन्हें भेजता है. आम तौर पर, ये कुछ शब्द या वाक्य होते हैं
  2. जब Gemini किसी फ़ंक्शन को कॉल करने का फ़ैसला करता है, तो वह फ़ंक्शन कॉल की जानकारी भेजता है
  3. फ़ंक्शन कॉल के बाद, टेक्स्ट के अन्य हिस्से भी शामिल किए जा सकते हैं
  4. जनरेट होने की प्रोसेस पूरी होने तक स्ट्रीम जारी रहती है

प्रोग्रेसिव प्रोसेसिंग

आपका ऐप्लिकेशन, हर चंक को धीरे-धीरे प्रोसेस करता है:

  1. हर टेक्स्ट चंक को मौजूदा जवाब में जोड़ दिया जाता है
  2. फ़ंक्शन कॉल का पता चलते ही उन्हें लागू कर दिया जाता है
  3. यूज़र इंटरफ़ेस (यूआई) पर, टेक्स्ट और फ़ंक्शन, दोनों के नतीजे रीयल टाइम में अपडेट होते हैं
  4. स्टेट को ट्रैक किया जाता है, ताकि यह दिखाया जा सके कि जवाब अब भी स्ट्रीम किया जा रहा है

स्ट्रीम पूरी होने पर

जनरेट करने की प्रोसेस पूरी होने पर:

  1. सर्वर ने स्ट्रीम बंद कर दी है
  2. await for लूप अपने-आप बंद हो जाता है
  3. मैसेज को 'पूरा हो गया' के तौर पर मार्क किया गया हो
  4. बातचीत की स्थिति को फिर से 'कुछ समय से इस्तेमाल में नहीं है' पर सेट कर दिया जाता है
  5. यूज़र इंटरफ़ेस (यूआई) अपडेट हो जाता है और उसमें टास्क पूरा होने की स्थिति दिखती है

स्ट्रीमिंग और नॉन-स्ट्रीमिंग की तुलना

स्ट्रीमिंग के फ़ायदों को बेहतर तरीके से समझने के लिए, स्ट्रीमिंग और नॉन-स्ट्रीमिंग के तरीकों की तुलना करते हैं:

पक्ष

नॉन-स्ट्रीमिंग

स्ट्रीमिंग

इंतज़ार का समय

जब तक पूरा जवाब तैयार नहीं हो जाता, तब तक उपयोगकर्ता को कुछ नहीं दिखता

उपयोगकर्ता को कुछ ही मिलीसेकंड में पहले शब्द दिखते हैं

उपयोगकर्ता अनुभव

लंबा इंतज़ार करने के बाद अचानक टेक्स्ट दिखना

टेक्स्ट का नैचुरल और प्रोग्रेसिव तरीके से दिखना

स्टेट मैनेजमेंट

आसान (मैसेज या तो स्वीकार किए जाने बाकी हैं या स्वीकार किए जा चुके हैं)

ज़्यादा जटिल (मैसेज स्ट्रीमिंग की स्थिति में हो सकते हैं)

फ़ंक्शन लागू करना

यह सिर्फ़ पूरा जवाब मिलने के बाद होता है

यह गड़बड़ी, जवाब जनरेट करने के दौरान होती है

लागू करने में आने वाली समस्याएं

लागू करने में आसान

इसके लिए, स्टेट मैनेजमेंट की अतिरिक्त सुविधा की ज़रूरत होती है

गड़बड़ी ठीक करना

सभी या कोई भी नहीं

अधूरे जवाब भी काम के हो सकते हैं

कोड की जटिलता

कम जटिल

स्ट्रीम हैंडलिंग की वजह से ज़्यादा जटिल

Colorist जैसे ऐप्लिकेशन के लिए, स्ट्रीमिंग के UX फ़ायदे, लागू करने की जटिलता से ज़्यादा हैं. खास तौर पर, रंग की व्याख्याओं के लिए, जिन्हें जनरेट होने में कई सेकंड लग सकते हैं.

स्ट्रीमिंग के UX के लिए सबसे सही तरीके

अपने एलएलएम ऐप्लिकेशन में स्ट्रीमिंग की सुविधा लागू करते समय, इन सबसे सही तरीकों को ध्यान में रखें:

  1. विज़ुअल इंडिकेटर साफ़ तौर पर दिखें: हमेशा ऐसे विज़ुअल इंडिकेटर दिखाएं जिनसे स्ट्रीमिंग और पूरे मैसेज के बीच अंतर साफ़ तौर पर पता चले
  2. इनपुट ब्लॉक करना: स्ट्रीमिंग के दौरान उपयोगकर्ता के इनपुट को बंद करें, ताकि एक साथ कई अनुरोध न किए जा सकें
  3. गड़बड़ी ठीक करना: अगर स्ट्रीमिंग में रुकावट आती है, तो यूज़र इंटरफ़ेस (यूआई) को इस तरह से डिज़ाइन करें कि वह आसानी से ठीक हो जाए
  4. स्टेट ट्रांज़िशन: यह पक्का करें कि आइडल, स्ट्रीमिंग, और पूरी हो चुकी स्थिति के बीच ट्रांज़िशन आसानी से हो
  5. प्रोग्रेस विज़ुअलाइज़ेशन: ऐसे हल्के ऐनिमेशन या इंडिकेटर का इस्तेमाल करें जो प्रोसेसिंग की मौजूदा स्थिति को दिखाते हों
  6. रद्द करने के विकल्प: पूरे ऐप्लिकेशन में, उपयोगकर्ताओं को जनरेट किए जा रहे कॉन्टेंट को रद्द करने के तरीके उपलब्ध कराएं
  7. फ़ंक्शन के नतीजे को इंटिग्रेट करना: अपने यूज़र इंटरफ़ेस (यूआई) को इस तरह से डिज़ाइन करें कि वह स्ट्रीम के बीच में दिखने वाले फ़ंक्शन के नतीजों को हैंडल कर सके
  8. परफ़ॉर्मेंस को ऑप्टिमाइज़ करना: स्ट्रीम को तेज़ी से अपडेट करने के दौरान, यूज़र इंटरफ़ेस (यूआई) को फिर से बनाने की प्रोसेस को कम करना

colorist_ui पैकेज, आपके लिए इन सबसे सही तरीकों को लागू करता है. हालांकि, स्ट्रीमिंग एलएलएम को लागू करने के लिए, इन बातों का ध्यान रखना ज़रूरी है.

आगे क्या करना है?

अगले चरण में, एलएलएम को सिंक करने की सुविधा लागू की जाएगी. इसके लिए, जब उपयोगकर्ता इतिहास से रंग चुनेंगे, तब Gemini को सूचना दी जाएगी. इससे, उपयोगकर्ताओं को बेहतर अनुभव मिलेगा. साथ ही, एलएलएम को ऐप्लिकेशन की स्थिति में उपयोगकर्ता के किए गए बदलावों के बारे में पता चलेगा.

समस्या का हल

स्ट्रीम प्रोसेसिंग से जुड़ी समस्याएं

अगर आपको स्ट्रीम प्रोसेस करने से जुड़ी समस्याएं आ रही हैं, तो:

  • समस्याएं: जवाब अधूरा होना, टेक्स्ट मौजूद न होना या स्ट्रीम अचानक बंद हो जाना
  • समाधान: नेटवर्क कनेक्टिविटी की जांच करें और पक्का करें कि आपके कोड में async/await पैटर्न सही तरीके से इस्तेमाल किए गए हों
  • गड़बड़ी की जानकारी: स्ट्रीम प्रोसेस करने से जुड़ी गड़बड़ी के मैसेज या चेतावनियों के लिए, लॉग पैनल देखें
  • ठीक करें: पक्का करें कि स्ट्रीम को प्रोसेस करने के दौरान, गड़बड़ी को ठीक करने के लिए try/catch ब्लॉक का सही तरीके से इस्तेमाल किया गया हो

फ़ंक्शन कॉल मौजूद नहीं हैं

अगर स्ट्रीम में फ़ंक्शन कॉल का पता नहीं चल रहा है, तो:

  • समस्याएं: टेक्स्ट दिखता है, लेकिन रंग अपडेट नहीं होते या लॉग में कोई फ़ंक्शन कॉल नहीं दिखता
  • समस्या का हल: सिस्टम प्रॉम्प्ट में, फ़ंक्शन कॉल इस्तेमाल करने के बारे में दिए गए निर्देशों की पुष्टि करें
  • निदान: लॉग पैनल देखें. इससे पता चलेगा कि फ़ंक्शन कॉल मिल रहे हैं या नहीं
  • ठीक करें: अपने सिस्टम प्रॉम्प्ट में बदलाव करें, ताकि एलएलएम को set_color टूल का इस्तेमाल करने के बारे में ज़्यादा साफ़ तौर पर निर्देश दिया जा सके

गड़बड़ी ठीक करने की सामान्य प्रोसेस

किसी अन्य समस्या के लिए:

  • पहला चरण: गड़बड़ी के मैसेज के लिए, लॉग पैनल देखें
  • दूसरा चरण: Firebase AI Logic की कनेक्टिविटी की पुष्टि करना
  • तीसरा चरण: पक्का करें कि Riverpod से जनरेट किया गया सभी कोड अप-टू-डेट हो
  • चौथा चरण: स्ट्रीमिंग लागू करने के तरीके की समीक्षा करें. देखें कि कहीं await स्टेटमेंट तो नहीं छूटे हैं

सीखे गए मुख्य कॉन्सेप्ट

  • Gemini API की मदद से, स्ट्रीमिंग वाले जवाबों को लागू करना, ताकि यूज़र एक्सपीरियंस को बेहतर बनाया जा सके
  • स्ट्रीमिंग इंटरैक्शन को सही तरीके से हैंडल करने के लिए, बातचीत की स्थिति को मैनेज करना
  • रीयल-टाइम में टेक्स्ट और फ़ंक्शन कॉल को प्रोसेस करना
  • ऐसे रिस्पॉन्सिव यूज़र इंटरफ़ेस (यूआई) बनाना जो स्ट्रीमिंग के दौरान धीरे-धीरे अपडेट होते हैं
  • एक साथ कई स्ट्रीम को सही तरीके से हैंडल करने के लिए, एसिंक पैटर्न का इस्तेमाल करना
  • स्ट्रीमिंग के दौरान जवाबों के लिए सही विज़ुअल फ़ीडबैक देना

स्ट्रीमिंग की सुविधा लागू करने से, आपने Colorist ऐप्लिकेशन का उपयोगकर्ता अनुभव बेहतर बनाया है. इससे ऐप्लिकेशन का इंटरफ़ेस ज़्यादा रिस्पॉन्सिव और दिलचस्प बन गया है. साथ ही, ऐसा लगता है कि ऐप्लिकेशन से बातचीत की जा रही है.

8. एलएलएम कॉन्टेक्स्ट सिंक्रनाइज़ेशन

इस बोनस चरण में, आपको एलएलएम कॉन्टेक्स्ट सिंक्रनाइज़ेशन लागू करना होगा. इसके लिए, उपयोगकर्ताओं के इतिहास से रंग चुनने पर Gemini को सूचना दें. इससे ज़्यादा बेहतर अनुभव मिलता है. इसमें एलएलएम को इंटरफ़ेस में उपयोगकर्ता की कार्रवाइयों के बारे में पता होता है, न कि सिर्फ़ उनके साफ़ तौर पर दिए गए मैसेज के बारे में.

इस चरण में आपको क्या करना है

  • आपके यूज़र इंटरफ़ेस (यूआई) और एलएलएम के बीच एलएलएम कॉन्टेक्स्ट सिंक्रनाइज़ेशन बनाना
  • यूज़र इंटरफ़ेस (यूआई) इवेंट को ऐसे कॉन्टेक्स्ट में बदलना जिसे एलएलएम समझ सके
  • उपयोगकर्ता की कार्रवाइयों के आधार पर बातचीत के कॉन्टेक्स्ट को अपडेट किया जा रहा है
  • इंटरैक्शन के अलग-अलग तरीकों के लिए, एक जैसा अनुभव देना
  • एलएलएम को चैट मैसेज के अलावा अन्य कॉन्टेंट के बारे में भी जानकारी देना

एलएलएम के कॉन्टेक्स्ट को सिंक करने के बारे में जानकारी

पारंपरिक चैटबॉट, सिर्फ़ उपयोगकर्ता के मैसेज का जवाब देते हैं. इसलिए, जब उपयोगकर्ता किसी दूसरे तरीके से ऐप्लिकेशन का इस्तेमाल करते हैं, तो उन्हें चैटबॉट से कोई जवाब नहीं मिलता. एलएलएम कॉन्टेक्स्ट सिंक्रनाइज़ेशन की मदद से, इस समस्या को हल किया जा सकता है:

एलएलएम के कॉन्टेक्स्ट को सिंक करना क्यों ज़रूरी है

जब उपयोगकर्ता, यूज़र इंटरफ़ेस (यूआई) एलिमेंट के ज़रिए आपके ऐप्लिकेशन से इंटरैक्ट करते हैं (जैसे, इतिहास से कोई रंग चुनना), तो एलएलएम को यह पता नहीं चलता कि क्या हुआ. ऐसा तब तक होता है, जब तक आप उसे साफ़ तौर पर नहीं बताते. एलएलएम के कॉन्टेक्स्ट को सिंक करना:

  1. कॉन्टेक्स्ट बनाए रखता है: एलएलएम को उपयोगकर्ता की सभी ज़रूरी कार्रवाइयों के बारे में जानकारी देता है
  2. जानकारी को व्यवस्थित करता है: इससे एक ऐसा अनुभव मिलता है जिसमें एलएलएम, यूज़र इंटरफ़ेस (यूआई) के साथ होने वाली बातचीत को समझता है
  3. बेहतर जवाब देने की क्षमता: इससे एलएलएम को उपयोगकर्ता की सभी कार्रवाइयों का सही जवाब देने में मदद मिलती है
  4. उपयोगकर्ताओं को बेहतर अनुभव मिलता है: इससे पूरा ऐप्लिकेशन ज़्यादा इंटिग्रेटेड और रिस्पॉन्सिव लगता है
  5. उपयोगकर्ताओं को कम मेहनत करनी पड़ती है: इससे उपयोगकर्ताओं को यूज़र इंटरफ़ेस (यूआई) पर की गई अपनी कार्रवाइयों के बारे में मैन्युअल तरीके से बताने की ज़रूरत नहीं पड़ती

Colorist ऐप्लिकेशन में, जब कोई उपयोगकर्ता इतिहास से कोई रंग चुनता है, तो आपको Gemini से यह कार्रवाई स्वीकार करने और चुने गए रंग के बारे में समझदारी से टिप्पणी करने के लिए कहना है. इससे, उपयोगकर्ता को ऐसा लगेगा कि Gemini एक मददगार और समझदार असिस्टेंट है.

रंग चुनने की सूचनाओं के लिए, Gemini Chat की सेवा को अपडेट करना

सबसे पहले, GeminiChatService में एक तरीका जोड़ें, ताकि जब कोई उपयोगकर्ता इतिहास से कोई रंग चुने, तो एलएलएम को इसकी सूचना दी जा सके. अपनी lib/services/gemini_chat_service.dart फ़ाइल अपडेट करें:

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

class ConversationStateNotifier extends Notifier<ConversationState> {
  @override
  ConversationState build() => ConversationState.idle;

  void busy() {
    state = ConversationState.busy;
  }

  void idle() {
    state = ConversationState.idle;
  }
}

final conversationStateProvider =
    NotifierProvider<ConversationStateNotifier, ConversationState>(
      ConversationStateNotifier.new,
    );

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(chatStateProvider.notifier);
    final logStateNotifier = ref.read(logStateProvider.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.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.idle();
    }
  }

  Future<void> _processBlock(
    GenerateContentResponse block,
    String llmMessageId,
  ) async {
    final chatSession = await ref.read(chatSessionProvider.future);
    final chatStateNotifier = ref.read(chatStateProvider.notifier);
    final logStateNotifier = ref.read(logStateProvider.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(keepAlive: true)
GeminiChatService geminiChatService(Ref ref) => GeminiChatService(ref);

इसमें notifyColorSelection तरीका जोड़ा गया है. यह तरीका:

  1. यह ColorData ऑब्जेक्ट लेता है, जो चुने गए रंग को दिखाता है
  2. इसे JSON फ़ॉर्मैट में एन्कोड करता है, ताकि इसे मैसेज में शामिल किया जा सके
  3. यह कुकी, एलएलएम को खास फ़ॉर्मैट में मैसेज भेजती है. इससे एलएलएम को पता चलता है कि उपयोगकर्ता ने कोई विकल्प चुना है
  4. सूचना को मैनेज करने के लिए, मौजूदा sendMessage तरीके का फिर से इस्तेमाल करता है

इस तरीके से, डुप्लीकेट होने से बचा जा सकता है. इसके लिए, मैसेज हैंडल करने वाले आपके मौजूदा इन्फ़्रास्ट्रक्चर का इस्तेमाल किया जाता है.

रंग चुनने की सूचनाएं पाने के लिए, मुख्य ऐप्लिकेशन को अपडेट करें

अब, lib/main.dart फ़ाइल में बदलाव करके, रंग चुनने की सूचना देने वाले फ़ंक्शन को मुख्य स्क्रीन पर भेजें:

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

मुख्य बदलाव यह है कि इसमें notifyColorSelection कॉलबैक जोड़ा गया है. यह यूज़र इंटरफ़ेस (यूआई) इवेंट (इतिहास से कोई रंग चुनना) को एलएलएम के सूचना सिस्टम से कनेक्ट करता है.

सिस्टम प्रॉम्प्ट को अपडेट करना

अब आपको अपने सिस्टम प्रॉम्प्ट को अपडेट करना होगा, ताकि एलएलएम को यह निर्देश दिया जा सके कि रंग चुनने की सूचनाओं का जवाब कैसे देना है. अपनी assets/system_prompt.md फ़ाइल में बदलाव करें:

assets/system_prompt.md

# Colorist System Prompt

You are a color expert assistant integrated into a desktop app called Colorist. Your job is to interpret natural language color descriptions and set the appropriate color values using a specialized tool.

## Your Capabilities

You are knowledgeable about colors, color theory, and how to translate natural language descriptions into specific RGB values. You have access to the following tool:

`set_color` - Sets the RGB values for the color display based on a description

## How to Respond to User Inputs

When users describe a color:

1. First, acknowledge their color description with a brief, friendly response
2. Interpret what RGB values would best represent that color description
3. Use the `set_color` tool to set those values (all values should be between 0.0 and 1.0)
4. After setting the color, provide a brief explanation of your interpretation

Example:
User: "I want a sunset orange"
You: "Sunset orange is a warm, vibrant color that captures the golden-red hues of the setting sun. It combines a strong red component with moderate orange tones."

[Then you would call the set_color tool with approximately: red=1.0, green=0.5, blue=0.25]

After the tool call: "I've set a warm orange with strong red, moderate green, and minimal blue components that is reminiscent of the sun low on the horizon."

## When Descriptions are Unclear

If a color description is ambiguous or unclear, please ask the user clarifying questions, one at a time.

## 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

इसमें मुख्य रूप से "जब उपयोगकर्ता पुराने रंग चुनते हैं" सेक्शन जोड़ा गया है. इसमें ये काम किए जा सकते हैं:

  1. यह कुकी, एलएलएम को इतिहास चुनने की सूचनाओं के कॉन्सेप्ट के बारे में बताती है
  2. इन सूचनाओं का उदाहरण देता है
  3. इसमें सही जवाब का उदाहरण दिखाया गया है
  4. यह कुकी, रंग चुनने और उस पर टिप्पणी करने के लिए ज़रूरी शर्तें सेट करती है

इससे एलएलएम को यह समझने में मदद मिलती है कि इन खास मैसेज का सही तरीके से जवाब कैसे देना है.

Riverpod कोड जनरेट करना

ज़रूरी Riverpod कोड जनरेट करने के लिए, build runner कमांड चलाएं:

dart run build_runner build --delete-conflicting-outputs

एलएलएम कॉन्टेक्स्ट सिंक्रनाइज़ेशन को चलाना और उसकी जांच करना

अपना ऐप्लिकेशन चलाएं:

flutter run -d DEVICE

Colorist ऐप्लिकेशन के स्क्रीनशॉट में, Gemini LLM को कलर हिस्ट्री से चुने गए रंग के बारे में जवाब देते हुए दिखाया गया है

एलएलएम के कॉन्टेक्स्ट सिंक होने की जांच करने के लिए, ये काम करने होते हैं:

  1. सबसे पहले, चैट में रंगों के बारे में बताकर कुछ रंग जनरेट करें
    • "मुझे गहरा बैंगनी रंग दिखाओ"
    • "मुझे फ़ॉरेस्ट ग्रीन रंग चाहिए"
    • "मुझे गहरा लाल रंग दिखाओ"
  2. इसके बाद, इतिहास की पट्टी में मौजूद किसी रंग के थंबनेल पर क्लिक करें

आपको इन बातों का ध्यान रखना चाहिए:

  1. चुना गया रंग, मुख्य डिसप्ले में दिखता है
  2. चैट में उपयोगकर्ता को एक मैसेज दिखता है, जिसमें रंग चुनने की जानकारी होती है
  3. एलएलएम, चुने गए रंग की पुष्टि करके जवाब देता है
  4. पूरा इंटरैक्शन सहज और एक जैसा लगता है

इससे एलएलएम को डायरेक्ट मैसेज और यूज़र इंटरफ़ेस (यूआई) इंटरैक्शन, दोनों के बारे में पता होता है और वह इनके हिसाब से सही जवाब देता है.

एलएलएम कॉन्टेक्स्ट सिंक्रनाइज़ेशन की सुविधा कैसे काम करती है

आइए, इस सिंक करने की सुविधा के काम करने के तरीके के बारे में तकनीकी जानकारी देखें:

डेटा फ़्लो

  1. उपयोगकर्ता की कार्रवाई: उपयोगकर्ता, इतिहास की पट्टी में मौजूद किसी रंग पर क्लिक करता है
  2. यूज़र इंटरफ़ेस (यूआई) इवेंट: MainScreen विजेट इस चुने गए विकल्प का पता लगाता है
  3. कॉलबैक एक्ज़ीक्यूट करना: notifyColorSelection कॉलबैक ट्रिगर होता है
  4. मैसेज बनाना: कलर डेटा की मदद से, खास फ़ॉर्मैट वाला मैसेज बनाया जाता है
  5. एलएलएम प्रोसेसिंग: मैसेज को Gemini को भेजा जाता है, जो फ़ॉर्मैट को पहचानता है
  6. संदर्भ के हिसाब से जवाब देना: Gemini, सिस्टम प्रॉम्प्ट के आधार पर सही जवाब देता है
  7. यूज़र इंटरफ़ेस (यूआई) अपडेट: जवाब, चैट में दिखता है. इससे आपको एक जैसा अनुभव मिलता है

डेटा को क्रम से लगाना

इस तरीके का मुख्य पहलू यह है कि कलर डेटा को कैसे क्रमबद्ध किया जाता है:

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

toLLMContextMap() तरीका (colorist_ui पैकेज से मिला) ColorData ऑब्जेक्ट को एक ऐसे मैप में बदलता है जिसमें मुख्य प्रॉपर्टी होती हैं. एलएलएम इन प्रॉपर्टी को समझ सकता है. आम तौर पर, इसमें यह जानकारी शामिल होती है:

  • आरजीबी वैल्यू (लाल, हरा, नीला)
  • हेक्स कोड का फ़ॉर्मैट
  • रंग से जुड़ा कोई भी नाम या ब्यौरा

इस डेटा को एक ही फ़ॉर्मैट में रखने और इसे मैसेज में शामिल करने से, यह पक्का किया जा सकता है कि एलएलएम के पास सही जवाब देने के लिए ज़रूरी सभी जानकारी मौजूद है.

एलएलएम कॉन्टेक्स्ट सिंक्रनाइज़ेशन के ज़्यादा ऐप्लिकेशन

यूज़र इंटरफ़ेस (यूआई) इवेंट के बारे में एलएलएम को सूचना देने के इस पैटर्न का इस्तेमाल, रंग चुनने के अलावा कई अन्य कामों के लिए भी किया जा सकता है:

इस्तेमाल के अन्य उदाहरण

  1. फ़िल्टर में बदलाव: जब उपयोगकर्ता डेटा पर फ़िल्टर लागू करें, तब एलएलएम को सूचना दें
  2. नेविगेशन इवेंट: जब उपयोगकर्ता अलग-अलग सेक्शन पर जाते हैं, तब एलएलएम को इसकी जानकारी देना
  3. चुने गए आइटम में बदलाव: जब उपयोगकर्ता सूचियों या ग्रिड से आइटम चुनते हैं, तब एलएलएम को अपडेट करना
  4. प्राथमिकताओं से जुड़े अपडेट: एलएलएम को बताएं कि उपयोगकर्ताओं ने सेटिंग या प्राथमिकताएं कब बदली हैं
  5. डेटा में बदलाव करना: जब उपयोगकर्ता डेटा जोड़ें, उसमें बदलाव करें या उसे मिटाएं, तब एलएलएम को इसकी सूचना दें

हर मामले में, पैटर्न एक जैसा रहता है:

  1. यूज़र इंटरफ़ेस (यूआई) इवेंट का पता लगाना
  2. काम के डेटा को क्रम से लगाना
  3. एलएलएम को खास तौर पर फ़ॉर्मैट की गई सूचना भेजना
  4. सिस्टम प्रॉम्प्ट के ज़रिए एलएलएम को सही जवाब देने के लिए गाइड करना

एलएलएम के कॉन्टेक्स्ट को सिंक करने के सबसे सही तरीके

आपके लागू करने के तरीके के आधार पर, एलएलएम के कॉन्टेक्स्ट को असरदार तरीके से सिंक करने के कुछ सबसे सही तरीके यहां दिए गए हैं:

1. एक ही तरह का फ़ॉर्मैट इस्तेमाल करना

सूचनाओं के लिए एक जैसा फ़ॉर्मैट इस्तेमाल करें, ताकि एलएलएम उन्हें आसानी से पहचान सके:

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

2. ज़्यादा जानकारी

सूचनाओं में इतनी जानकारी शामिल करें कि एलएलएम, स्मार्ट तरीके से जवाब दे सके. रंगों के लिए, इसका मतलब आरजीबी वैल्यू, हेक्स कोड, और अन्य काम की प्रॉपर्टी से है.

3. निर्देशों को मिटाना

सिस्टम प्रॉम्प्ट में, सूचनाओं को मैनेज करने के तरीके के बारे में साफ़ तौर पर निर्देश दें. बेहतर होगा कि आप उदाहरण भी दें.

4. नैचुरल इंटिग्रेशन

सूचनाओं को इस तरह से डिज़ाइन करें कि वे बातचीत में स्वाभाविक रूप से शामिल हों, न कि तकनीकी रुकावटों के तौर पर दिखें.

5. चुनिंदा सूचनाएं

एलएलएम को सिर्फ़ उन कार्रवाइयों के बारे में सूचना दें जो बातचीत के लिए ज़रूरी हैं. हर यूज़र इंटरफ़ेस (यूआई) इवेंट की जानकारी देना ज़रूरी नहीं है.

समस्या का हल

सूचना से जुड़ी समस्याएं

अगर एलएलएम, रंग चुनने पर सही जवाब नहीं दे रहा है, तो:

  • देखें कि सूचना वाले मैसेज का फ़ॉर्मैट, सिस्टम प्रॉम्प्ट में बताए गए फ़ॉर्मैट से मेल खाता हो
  • पुष्टि करें कि रंग का डेटा सही तरीके से क्रम से लगाया जा रहा है
  • पक्का करें कि सिस्टम प्रॉम्प्ट में, चुने गए विकल्पों को मैनेज करने के लिए साफ़ तौर पर निर्देश दिए गए हों
  • सूचनाएं भेजते समय, चैट सेवा में हुई किसी भी गड़बड़ी का पता लगाएं

पिछली बातचीत से जुड़ा डेटा मैनेज करना

अगर एलएलएम को कॉन्टेक्स्ट समझ में नहीं आ रहा है, तो:

  • देखें कि चैट सेशन को सही तरीके से मैनेज किया जा रहा हो
  • पुष्टि करें कि बातचीत की स्थितियां सही तरीके से बदल रही हैं
  • पक्का करें कि सूचनाएं उसी चैट सेशन से भेजी जा रही हों

सामान्य समस्याएं

सामान्य समस्याओं के लिए:

  • गड़बड़ियों या चेतावनियों के लिए लॉग की जांच करना
  • Firebase AI Logic की कनेक्टिविटी की पुष्टि करना
  • फ़ंक्शन पैरामीटर में टाइप के मेल न खाने की किसी भी समस्या की जांच करना
  • पक्का करें कि Riverpod से जनरेट किया गया सभी कोड अप-टू-डेट हो

सीखे गए मुख्य कॉन्सेप्ट

  • यूज़र इंटरफ़ेस (यूआई) और एलएलएम के बीच एलएलएम कॉन्टेक्स्ट सिंक्रनाइज़ेशन बनाना
  • यूज़र इंटरफ़ेस (यूआई) इवेंट को एलएलएम के हिसाब से कॉन्टेक्स्ट में बदलना
  • अलग-अलग इंटरैक्शन पैटर्न के लिए, एलएलएम के व्यवहार को गाइड करना
  • मैसेज और मैसेज के अलावा अन्य इंटरैक्शन के बीच एक जैसा अनुभव देना
  • ऐप्लिकेशन की पूरी स्थिति के बारे में एलएलएम की जानकारी को बेहतर बनाना

एलएलएम के कॉन्टेक्स्ट को सिंक करने की सुविधा लागू करके, आपने एक ऐसा अनुभव तैयार किया है जिसमें एलएलएम सिर्फ़ टेक्स्ट जनरेट करने के बजाय, एक ऐसी असिस्टेंट की तरह काम करता है जो आपकी ज़रूरतों को समझती है और उनके हिसाब से जवाब देती है. इस पैटर्न को अनगिनत अन्य ऐप्लिकेशन पर लागू किया जा सकता है, ताकि एआई की मदद से ज़्यादा सहज और आसानी से समझ में आने वाले इंटरफ़ेस बनाए जा सकें.

9. बधाई हो!

आपने कलररिस्ट कोडलैब पूरा कर लिया है! 🎉

आपने क्या बनाया है

आपने पूरी तरह से काम करने वाला एक Flutter ऐप्लिकेशन बनाया है. इसमें Google के Gemini API को इंटिग्रेट किया गया है, ताकि रंग के बारे में नेचुरल लैंग्वेज में दी गई जानकारी को समझा जा सके. अब आपका ऐप्लिकेशन ये काम कर सकता है:

  • "सनसेट ऑरेंज" या "डीप ओशन ब्लू" जैसी सामान्य भाषा में दी गई जानकारी को प्रोसेस करना
  • Gemini का इस्तेमाल करके, इन ब्यौरों को आरजीबी वैल्यू में स्मार्ट तरीके से ट्रांसलेट करें
  • स्ट्रीमिंग के ज़रिए मिलने वाले जवाबों के साथ, अनुवाद किए गए रंगों को रीयल-टाइम में दिखाना
  • चैट और यूज़र इंटरफ़ेस (यूआई) एलिमेंट, दोनों के ज़रिए उपयोगकर्ता के इंटरैक्शन को मैनेज करना
  • अलग-अलग इंटरैक्शन के तरीकों में, कॉन्टेक्स्ट के बारे में जानकारी बनाए रखना

यहां से कहां जाएं

अब आपने Gemini को Flutter के साथ इंटिग्रेट करने की बुनियादी बातें जान ली हैं. इसलिए, यहां कुछ तरीके दिए गए हैं जिनसे आप आगे बढ़ सकते हैं:

Colorist ऐप्लिकेशन को बेहतर बनाना

  • कलर पैलेट: मिलती-जुलती या मेल खाने वाली कलर स्कीम जनरेट करने की सुविधा जोड़ी गई है
  • बोलकर फ़ोन को निर्देश देना: रंग के बारे में बोलकर जानकारी देने के लिए, बोली पहचान की सुविधा को इंटिग्रेट करें
  • इतिहास को मैनेज करना: कलर सेट को नाम देने, व्यवस्थित करने, और एक्सपोर्ट करने के विकल्प जोड़ें
  • कस्टम प्रॉम्प्टिंग: उपयोगकर्ताओं के लिए एक ऐसा इंटरफ़ेस बनाएं जिससे वे सिस्टम प्रॉम्प्ट को पसंद के मुताबिक बना सकें
  • बेहतर आंकड़े: ट्रैक करें कि कौनसे ब्यौरे सबसे अच्छा परफ़ॉर्म कर रहे हैं या किनसे समस्याएं आ रही हैं

Gemini की अन्य सुविधाएँ एक्सप्लोर करना

  • टेक्स्ट, इमेज वग़ैरह को प्रोसेस करने वाले मोडल के इनपुट: फ़ोटो से रंग निकालने के लिए, इमेज इनपुट जोड़ें
  • कॉन्टेंट जनरेट करना: Gemini का इस्तेमाल करके, रंग से जुड़ा कॉन्टेंट जनरेट करें. जैसे, ब्यौरे या कहानियां
  • फ़ंक्शन कॉल करने की बेहतर सुविधाएं: एक से ज़्यादा फ़ंक्शन के साथ, टूल इंटिग्रेशन को ज़्यादा बेहतर तरीके से बनाया जा सकता है
  • सुरक्षा सेटिंग: अलग-अलग सुरक्षा सेटिंग और जवाबों पर उनके असर के बारे में जानें

इन पैटर्न को अन्य डोमेन पर लागू करें

  • दस्तावेज़ का विश्लेषण करना: ऐसे ऐप्लिकेशन बनाएँ जो दस्तावेज़ों को समझ सकें और उनका विश्लेषण कर सकें
  • क्रिएटिव तरीके से लिखने में मदद करने वाली सुविधाएं: एलएलएम की मदद से मिलने वाले सुझावों का इस्तेमाल करके, कॉन्टेंट लिखने में मदद करने वाले टूल बनाना
  • टास्क अपने-आप पूरे होने की सुविधा: ऐसे ऐप्लिकेशन डिज़ाइन करें जो नैचुरल लैंग्वेज को अपने-आप पूरे होने वाले टास्क में बदल दें
  • नॉलेज-बेस्ड ऐप्लिकेशन: खास डोमेन में एक्सपर्ट सिस्टम बनाना

संसाधन

यहां कुछ ज़रूरी संसाधन दिए गए हैं. इनकी मदद से, इस बारे में ज़्यादा जानें:

आधिकारिक दस्तावेज़

प्रॉम्प्ट से जुड़ा कोर्स और गाइड

कम्यूनिटी

ऑब्ज़र्वेबल फ़्लटर एजेंटिक सीरीज़

एपिसोड #59 में, क्रेग लैबेंज़ और एंड्रयू ब्रोगडेन ने इस कोडलैब के बारे में बताया है. साथ ही, ऐप्लिकेशन बनाने के दिलचस्प हिस्सों के बारे में हाइलाइट किया है.

एपिसोड #60 में, क्रेग और एंड्रयू के साथ फिर से जुड़ें. वे कोडलैब ऐप्लिकेशन में नई सुविधाएं जोड़ रहे हैं. साथ ही, वे एलएलएम को अपनी बात मनवाने की कोशिश कर रहे हैं.

एपिसोड #61 में, क्रेग के साथ क्रिस सेल्स शामिल हैं. इस एपिसोड में, खबरों की हेडलाइन का विश्लेषण करने और उनसे जुड़ी इमेज जनरेट करने के बारे में बताया गया है.

सुझाव/राय दें या शिकायत करें

हमें यह जानकर खुशी होगी कि इस कोडलैब को इस्तेमाल करने का आपका अनुभव कैसा रहा! कृपया इन तरीकों से सुझाव/राय दें या शिकायत करें:

इस कोडलैब को पूरा करने के लिए धन्यवाद. हमें उम्मीद है कि आप Flutter और एआई के बीच मौजूद दिलचस्प संभावनाओं को एक्सप्लोर करना जारी रखेंगे!