یک برنامه فلاتر با قدرت جمینی بسازید، یک برنامه فلاتر مجهز به جمینی بسازید

یک برنامه فلاتر مجهز به Gemini بسازید

درباره این codelab

subjectآخرین به‌روزرسانی: ژوئن ۳, ۲۰۲۵
account_circleنویسنده: Brett Morgan

1. یک برنامه فلاتر مجهز به Gemini بسازید

چیزی که خواهی ساخت

در این نرم افزار کد، Colorist را می سازید - یک برنامه تعاملی Flutter که قدرت Gemini API را مستقیماً به برنامه Flutter شما می آورد. تا به حال خواسته اید به کاربران اجازه دهید برنامه شما را از طریق زبان طبیعی کنترل کنند اما نمی دانستید از کجا شروع کنید؟ این کد لبه به شما نشان می دهد که چگونه.

Colorist به کاربران اجازه می دهد تا رنگ ها را به زبان طبیعی توصیف کنند (مانند "نارنجی غروب خورشید" یا "آبی عمیق اقیانوس")، و برنامه:

  • این توضیحات را با استفاده از API Gemini Google پردازش می کند
  • توضیحات را به مقادیر دقیق رنگ RGB تفسیر می کند
  • رنگ را در زمان واقعی روی صفحه نمایش می دهد
  • جزئیات رنگ فنی و زمینه جالب در مورد رنگ را ارائه می دهد
  • تاریخچه ای از رنگ های اخیرا تولید شده را حفظ می کند

اسکرین شات برنامه Colorist که نمایشگر رنگی و رابط چت را نشان می دهد

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

چرا این موضوع برای توسعه دهندگان فلاتر اهمیت دارد

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

سفر یادگیری شما

این کد لبه شما را مرحله به مرحله مراحل ساخت Colorist را هدایت می کند:

  1. راه‌اندازی پروژه - شما با یک ساختار اولیه برنامه Flutter و بسته colorist_ui شروع می‌کنید
  2. ادغام پایه Gemini - برنامه خود را به Firebase AI Logic متصل کنید و ارتباطات LLM را پیاده سازی کنید
  3. درخواست موثر - یک سیستم اعلان ایجاد کنید که LLM را برای درک توضیحات رنگ راهنمایی کند
  4. اعلان های عملکرد - ابزارهایی را که LLM می تواند برای تنظیم رنگ ها در برنامه شما استفاده کند، تعریف کنید
  5. مدیریت ابزار - فراخوانی های تابع را از LLM پردازش کنید و آنها را به وضعیت برنامه خود متصل کنید
  6. پاسخ‌های جریانی - تجربه کاربر را با پاسخ‌های جریانی LLM در زمان واقعی افزایش دهید
  7. همگام سازی زمینه LLM - با اطلاع رسانی به LLM از اقدامات کاربر، یک تجربه منسجم ایجاد کنید.

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

  • Firebase AI Logic را برای برنامه های Flutter پیکربندی کنید
  • سیستم موثر Craft دستور هدایت رفتار LLM را می دهد
  • اعلان‌های عملکردی را پیاده‌سازی کنید که ویژگی‌های زبان طبیعی و برنامه را پل می‌کنند
  • پاسخ‌های جریانی را برای تجربه کاربری پاسخگو پردازش کنید
  • همگام سازی حالت بین رویدادهای UI و LLM
  • وضعیت مکالمه LLM را با استفاده از Riverpod مدیریت کنید
  • خطاها را در برنامه های کاربردی LLM به خوبی مدیریت کنید

پیش نمایش کد: مزه آنچه را که پیاده سازی خواهید کرد

در اینجا یک نگاه اجمالی از اعلان تابعی است که ایجاد می کنید تا به LLM اجازه دهید رنگ ها را در برنامه شما تنظیم کند:

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

نمای کلی ویدیویی از این کد لبه

تماشا کنید که کریگ لابنز و اندرو بروگدون در مورد این آزمایشگاه کد در شرح شماره 59 مشهود فلاتر بحث می کنند:

پیش نیازها

برای استفاده حداکثری از این کد لبه، باید موارد زیر را داشته باشید:

  • تجربه توسعه فلاتر - آشنایی با مبانی فلاتر و نحو دارت
  • دانش برنامه‌نویسی ناهمزمان - درک آینده‌ها، همگام‌سازی/انتظار، و جریان‌ها
  • حساب Firebase - برای راه اندازی Firebase به یک حساب Google نیاز دارید

بیایید شروع به ساخت اولین برنامه Flutter با LLM کنیم!

2. راه اندازی پروژه و خدمات اکو

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

آنچه در این مرحله خواهید آموخت

  • راه اندازی پروژه فلاتر با وابستگی های مورد نیاز
  • کار با بسته colorist_ui برای اجزای UI
  • پیاده سازی سرویس پیام اکو و اتصال آن به UI

یک پروژه فلاتر جدید ایجاد کنید

با ایجاد یک پروژه Flutter جدید با دستور زیر شروع کنید:

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

پرچم -e نشان می دهد که شما یک پروژه خالی بدون برنامه counter می خواهید. این برنامه برای کار در دسکتاپ، موبایل و وب طراحی شده است. با این حال، flutterfire در حال حاضر از لینوکس پشتیبانی نمی کند.

وابستگی ها را اضافه کنید

به فهرست پروژه خود بروید و وابستگی های مورد نیاز را اضافه کنید:

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

dependencies:
  flutter:
    sdk: flutter
  colorist_ui: ^0.2.4
  flutter_riverpod: ^2.6.1
  riverpod_annotation: ^2.6.1

dev_dependencies:
  flutter_test:
    sdk: flutter
  flutter_lints: ^5.0.0
  build_runner: ^2.4.15
  riverpod_generator: ^2.6.5
  riverpod_lint: ^2.6.5
  json_serializable: ^6.9.5
  custom_lint: ^0.7.5

flutter:
  uses-material-design: true

پیکربندی گزینه های تجزیه و تحلیل

custom_lint به فایل analysis_options.yaml خود در ریشه پروژه خود اضافه کنید:

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

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

این یک برنامه Flutter را راه‌اندازی می‌کند که یک سرویس اکو را پیاده‌سازی می‌کند که با برگرداندن پیام کاربر، رفتار یک LLM را تقلید می‌کند.

شناخت معماری

بیایید یک دقیقه وقت بگذاریم تا معماری برنامه colorist را درک کنیم:

بسته colorist_ui

بسته colorist_ui اجزای رابط کاربری از پیش ساخته شده و ابزارهای مدیریت حالت را ارائه می دهد:

  1. صفحه اصلی : مؤلفه اصلی رابط کاربری که نمایش می دهد:
    • طرح بندی صفحه تقسیم بر روی دسکتاپ (منطقه تعامل و پانل گزارش)
    • یک رابط زبانه‌دار در موبایل
    • نمایش رنگی، رابط چت، و تصاویر کوچک تاریخچه
  2. مدیریت ایالت : این برنامه از چندین اعلان کننده وضعیت استفاده می کند:
    • ChatStateNotifier : پیام های چت را مدیریت می کند
    • ColorStateNotifier : رنگ و تاریخچه فعلی را مدیریت می کند
    • LogStateNotifier : ورودی های گزارش را برای اشکال زدایی مدیریت می کند
  3. مدیریت پیام : برنامه از یک مدل پیام با حالت های مختلف استفاده می کند:
    • پیام های کاربر : توسط کاربر وارد شده است
    • پیام‌های LLM : توسط LLM (یا سرویس اکو شما در حال حاضر) ایجاد شده است.
    • MessageState : پیگیری می‌کند که آیا پیام‌های LLM کامل هستند یا هنوز در جریان هستند

معماری اپلیکیشن

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

  1. لایه رابط کاربری : توسط بسته colorist_ui ارائه شده است
  2. مدیریت حالت : از Riverpod برای مدیریت حالت واکنشی استفاده می کند
  3. لایه سرویس : در حال حاضر شامل سرویس اکو ساده شما است، این با سرویس چت Gemini جایگزین خواهد شد
  4. یکپارچه سازی LLM : در مراحل بعدی اضافه خواهد شد

این جداسازی به شما امکان می‌دهد تا روی اجرای یکپارچه‌سازی LLM تمرکز کنید در حالی که اجزای UI قبلاً مراقبت شده‌اند.

برنامه را اجرا کنید

برنامه را با دستور زیر اجرا کنید:

flutter run -d DEVICE

DEVICE با دستگاه مورد نظر خود، مانند macos ، windows ، chrome یا شناسه دستگاه جایگزین کنید.

اسکرین شات برنامه Colorist که نشان‌دهی رندر سرویس اکو را نشان می‌دهد

اکنون باید برنامه Colorist را با موارد زیر مشاهده کنید:

  1. یک ناحیه نمایش رنگی با رنگ پیش‌فرض
  2. یک رابط چت که در آن می توانید پیام ها را تایپ کنید
  3. پانل گزارشی که تعاملات چت را نشان می دهد

پیامی مانند "I'd like a blue color" را تایپ کنید و Send را فشار دهید. سرویس اکو به سادگی پیام شما را تکرار می کند. در مراحل بعدی، این را با تفسیر رنگ واقعی با استفاده از Firebase AI Logic جایگزین خواهید کرد.

بعدش چی؟

در مرحله بعد، شما Firebase را پیکربندی کرده و یکپارچه سازی اولیه Gemini API را برای جایگزینی سرویس echo خود با سرویس چت Gemini پیاده سازی می کنید. این به برنامه اجازه می دهد تا توضیحات رنگ را تفسیر کند و پاسخ های هوشمندانه ای ارائه دهد.

عیب یابی

مشکلات بسته UI

اگر با بسته colorist_ui با مشکل مواجه شدید:

  • مطمئن شوید که از آخرین نسخه استفاده می کنید
  • بررسی کنید که وابستگی را به درستی اضافه کرده اید
  • نسخه های بسته متناقض را بررسی کنید

خطاهای ساخت

اگر خطاهای ساخت را مشاهده کردید:

  • مطمئن شوید که آخرین کانال پایدار Flutter SDK را نصب کرده اید
  • flutter clean را اجرا کنید و سپس flutter pub get
  • خروجی کنسول را برای پیام های خطای خاص بررسی کنید

مفاهیم کلیدی آموخته شده

  • راه اندازی پروژه فلاتر با وابستگی های لازم
  • درک معماری برنامه و مسئولیت های اجزاء
  • پیاده سازی یک سرویس ساده که رفتار یک LLM را تقلید می کند
  • اتصال سرویس به اجزای UI
  • استفاده از ریورپاد برای مدیریت دولتی

3. ادغام چت اولیه Gemini

در این مرحله، با استفاده از Firebase AI Logic، سرویس اکو را از مرحله قبل با یکپارچه سازی API Gemini جایگزین می کنید. شما Firebase را پیکربندی می‌کنید، ارائه‌دهنده‌های لازم را راه‌اندازی می‌کنید و یک سرویس چت اولیه را پیاده‌سازی می‌کنید که با Gemini API ارتباط برقرار می‌کند.

آنچه در این مرحله خواهید آموخت

  • راه اندازی Firebase در برنامه Flutter
  • پیکربندی Firebase AI Logic برای دسترسی Gemini
  • ایجاد ارائه دهندگان Riverpod برای خدمات Firebase و Gemini
  • پیاده سازی سرویس چت اولیه با Gemini API
  • مدیریت پاسخ های ناهمزمان API و حالت های خطا

Firebase را راه اندازی کنید

ابتدا باید Firebase را برای پروژه Flutter خود راه اندازی کنید. این شامل ایجاد یک پروژه Firebase، افزودن برنامه خود به آن، و پیکربندی تنظیمات لازم Firebase AI Logic است.

یک پروژه Firebase ایجاد کنید

  1. به کنسول Firebase بروید و با حساب Google خود وارد شوید.
  2. روی ایجاد پروژه Firebase کلیک کنید یا یک پروژه موجود را انتخاب کنید.
  3. برای ایجاد پروژه خود، جادوگر راه اندازی را دنبال کنید.

Firebase AI Logic را در پروژه Firebase خود تنظیم کنید

  1. در کنسول Firebase، به پروژه خود بروید.
  2. در نوار کناری سمت چپ، AI را انتخاب کنید.
  3. در منوی کشویی هوش مصنوعی، AI Logic را انتخاب کنید.
  4. در کارت منطق هوش مصنوعی Firebase، Get Started را انتخاب کنید.
  5. برای فعال کردن Gemini Developer API برای پروژه خود، دستورات را دنبال کنید.

FlutterFire CLI را نصب کنید

FlutterFire CLI راه اندازی Firebase را در برنامه های Flutter ساده می کند:

dart pub global activate flutterfire_cli

Firebase را به برنامه Flutter خود اضافه کنید

  1. هسته Firebase و بسته های Firebase AI Logic را به پروژه خود اضافه کنید:
flutter pub add firebase_core firebase_ai
  1. دستور پیکربندی FlutterFire را اجرا کنید:
flutterfire configure

این دستور خواهد شد:

  • از شما می خواهد پروژه Firebase را که ایجاد کرده اید انتخاب کنید
  • برنامه(های) Flutter خود را با Firebase ثبت کنید
  • یک فایل firebase_options.dart با پیکربندی پروژه خود ایجاد کنید

این دستور به طور خودکار پلتفرم های انتخابی شما (iOS، Android، macOS، Windows، web) را شناسایی کرده و آنها را به درستی پیکربندی می کند.

پیکربندی پلتفرم خاص

Firebase به حداقل نسخه های بالاتر از نسخه های پیش فرض برای Flutter نیاز دارد. همچنین برای صحبت با سرورهای Firebase AI Logic به دسترسی به شبکه نیاز دارد.

مجوزهای macOS را پیکربندی کنید

برای macOS، باید دسترسی به شبکه را در حقوق برنامه خود فعال کنید:

  1. macos/Runner/DebugProfile.entitlements باز کنید و اضافه کنید:

macos/Runner/DebugProfile.entitlements

<key>com.apple.security.network.client</key>
<true/>
  1. همچنین macos/Runner/Release.entitlements را باز کنید و همان ورودی را اضافه کنید.
  2. حداقل نسخه macOS را در بالای macos/Podfile به روز کنید:

macos/Podfile

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

مجوزهای iOS را پیکربندی کنید

برای iOS، حداقل نسخه را در بالای ios/Podfile به‌روزرسانی کنید:

ios/Podfile

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

تنظیمات اندروید را پیکربندی کنید

برای اندروید، android/app/build.gradle.kts به روز کنید:

android/app/build.gradle.kts

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

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

ارائه دهندگان مدل Gemini را ایجاد کنید

اکنون ارائه دهندگان Riverpod را برای Firebase و 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:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

import '../firebase_options.dart';

part 'gemini.g.dart';

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

@riverpod
Future<GenerativeModel> geminiModel(Ref ref) async {
 
await ref.watch(firebaseAppProvider.future);

 
final model = FirebaseAI.googleAI().generativeModel(
   
model: 'gemini-2.0-flash',
 
);
 
return model;
}

@Riverpod(keepAlive: true)
Future<ChatSession> chatSession(Ref ref) async {
 
final model = await ref.watch(geminiModelProvider.future);
 
return model.startChat();
}

این فایل مبنایی را برای سه ارائه دهنده کلیدی تعریف می کند. این ارائه‌دهندگان زمانی ایجاد می‌شوند که dart run build_runner توسط مولدهای کد Riverpod اجرا می‌کنید.

  1. firebaseAppProvider : Firebase را با پیکربندی پروژه شما راه اندازی می کند
  2. geminiModelProvider : یک نمونه مدل مولد Gemini ایجاد می کند
  3. chatSessionProvider : یک جلسه چت با مدل Gemini ایجاد و نگهداری می کند

keepAlive: true در جلسه چت تضمین می کند که در طول چرخه عمر برنامه باقی بماند و زمینه گفتگو حفظ شود.

سرویس چت جمینی را پیاده سازی کنید

یک فایل جدید 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';

part 'gemini_chat_service.g.dart';

class GeminiChatService {
 
GeminiChatService(this.ref);
 
final Ref ref;

 
Future<void> sendMessage(String message) async {
   
final chatSession = await ref.read(chatSessionProvider.future);
   
final chatStateNotifier = ref.read(chatStateNotifierProvider.notifier);
   
final logStateNotifier = ref.read(logStateNotifierProvider.notifier);

   
chatStateNotifier.addUserMessage(message);
   
logStateNotifier.logUserText(message);
   
final llmMessage = chatStateNotifier.createLlmMessage();
   
try {
     
final response = await chatSession.sendMessage(Content.text(message));

     
final responseText = response.text;
     
if (responseText != null) {
       
logStateNotifier.logLlmText(responseText);
       
chatStateNotifier.appendToMessage(llmMessage.id, responseText);
     
}
   
} catch (e, st) {
     
logStateNotifier.logError(e, st: st);
     
chatStateNotifier.appendToMessage(
       
llmMessage.id,
       
"\nI'm sorry, I encountered an error processing your request. "
       
"Please try again.",
     
);
   
} finally {
     
chatStateNotifier.finalizeMessage(llmMessage.id);
   
}
 
}
}

@riverpod
GeminiChatService geminiChatService(Ref ref) => GeminiChatService(ref);

این سرویس:

  1. پیام های کاربر را می پذیرد و آنها را به Gemini API ارسال می کند
  2. رابط چت را با پاسخ های مدل به روز می کند
  3. تمام ارتباطات را برای سهولت درک جریان واقعی LLM ثبت می کند
  4. خطاها را با بازخورد مناسب کاربر مدیریت می کند

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

کد Riverpod را ایجاد کنید

دستور build runner را برای تولید کد Riverpod لازم اجرا کنید:

dart run build_runner build --delete-conflicting-outputs

این فایل‌های .g.dart را ایجاد می‌کند که Riverpod برای عملکرد به آن‌ها نیاز دارد.

فایل main.dart را به روز کنید

فایل lib/main.dart خود را برای استفاده از سرویس جدید چت Gemini به روز کنید:

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. جایگزینی سرویس اکو با سرویس چت مبتنی بر API Gemini
  2. افزودن صفحات بارگذاری و خطا با استفاده از الگوی AsyncValue ریورپاد با روش when
  3. اتصال رابط کاربری به سرویس چت جدید خود از طریق پاسخ تماس sendMessage

برنامه را اجرا کنید

برنامه را با دستور زیر اجرا کنید:

flutter run -d DEVICE

DEVICE با دستگاه مورد نظر خود، مانند macos ، windows ، chrome یا شناسه دستگاه جایگزین کنید.

عکس صفحه برنامه Colorist که Gemini LLM را نشان می دهد که به درخواست رنگ زرد آفتابی پاسخ می دهد

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

درک ارتباطات LLM

بیایید لحظه ای وقت بگذاریم تا بفهمیم هنگام برقراری ارتباط با Gemini API چه اتفاقی می افتد:

جریان ارتباطی

  1. ورودی کاربر : کاربر متنی را در رابط چت وارد می کند
  2. درخواست قالب‌بندی : برنامه متن را به عنوان یک شی Content برای Gemini API قالب‌بندی می‌کند
  3. ارتباط API : متن از طریق Firebase AI Logic به API Gemini ارسال می شود
  4. پردازش LLM : مدل Gemini متن را پردازش کرده و پاسخی را ایجاد می کند
  5. مدیریت پاسخ : برنامه پاسخ را دریافت می کند و رابط کاربری را به روز می کند
  6. ورود به سیستم : تمام ارتباطات برای شفافیت ثبت شده است

جلسات چت و زمینه گفتگو

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

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

بعدش چی؟

در این مرحله، می‌توانید هر چیزی را از Gemini API بپرسید، زیرا هیچ محدودیتی برای پاسخ دادن به آن وجود ندارد. برای مثال، می‌توانید خلاصه‌ای از جنگ‌های رز را از آن بخواهید، که به هدف برنامه رنگی شما مرتبط نیست.

در مرحله بعد، یک سیستم اعلان ایجاد می کنید تا Gemini را در تفسیر موثرتر توضیحات رنگ راهنمایی کند. این نشان می دهد که چگونه می توان رفتار LLM را برای نیازهای خاص برنامه سفارشی کرد و قابلیت های آن را روی دامنه برنامه خود متمرکز کرد.

عیب یابی

مشکلات پیکربندی Firebase

اگر با خطاهای اولیه Firebase مواجه شدید:

  • مطمئن شوید که فایل firebase_options.dart شما به درستی تولید شده است
  • بررسی کنید که به طرح Blaze برای دسترسی منطقی هوش مصنوعی Firebase ارتقا داده اید

خطاهای دسترسی به API

اگر هنگام دسترسی به Gemini API خطاهایی دریافت کردید:

  • تأیید کنید که صورت‌حساب به درستی در پروژه Firebase شما تنظیم شده است
  • بررسی کنید که Firebase AI Logic و Cloud AI API در پروژه Firebase شما فعال باشند
  • اتصال شبکه و تنظیمات فایروال خود را بررسی کنید
  • بررسی کنید که نام مدل ( gemini-2.0-flash ) صحیح و در دسترس باشد

مسائل زمینه گفتگو

اگر متوجه شدید که Gemini زمینه قبلی چت را به خاطر نمی آورد:

  • تأیید کنید که تابع chatSession با @Riverpod(keepAlive: true)
  • بررسی کنید که در حال استفاده مجدد از همان جلسه چت برای همه مبادلات پیام هستید
  • قبل از ارسال پیام، بررسی کنید که جلسه چت به درستی مقداردهی اولیه شده باشد

مسائل مربوط به پلتفرم

برای مسائل خاص پلت فرم:

  • iOS/macOS: اطمینان حاصل کنید که حقوق مناسب تنظیم شده اند و حداقل نسخه ها پیکربندی شده اند
  • Android: بررسی کنید که حداقل نسخه SDK به درستی تنظیم شده باشد
  • پیام های خطای خاص پلتفرم را در کنسول بررسی کنید

مفاهیم کلیدی آموخته شده

  • راه اندازی Firebase در برنامه Flutter
  • پیکربندی Firebase AI Logic برای دسترسی به Gemini
  • ایجاد ارائه دهندگان Riverpod برای خدمات ناهمزمان
  • پیاده سازی یک سرویس چت که با یک LLM ارتباط برقرار می کند
  • مدیریت وضعیت های API ناهمزمان (بارگیری، خطا، داده)
  • درک جریان ارتباطات LLM و جلسات چت

4. تحریک موثر برای توصیف رنگ

در این مرحله، یک فرمان سیستمی ایجاد و پیاده سازی می کنید که Gemini را در تفسیر توضیحات رنگ راهنمایی می کند. اعلان های سیستم یک راه قدرتمند برای سفارشی کردن رفتار LLM برای کارهای خاص بدون تغییر کد شما هستند.

آنچه در این مرحله خواهید آموخت

  • درک دستورات سیستم و اهمیت آنها در برنامه های کاربردی LLM
  • ایجاد دستورات موثر برای وظایف خاص دامنه
  • بارگیری و استفاده از فرمان های سیستم در یک برنامه Flutter
  • راهنمایی یک LLM برای ارائه پاسخ‌هایی با قالب‌بندی ثابت
  • آزمایش نحوه تأثیر اعلان های سیستم بر رفتار LLM

درک دستورات سیستم

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

اعلان های سیستم چیست؟

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

  • نقش و شخصیت LLM را مشخص کنید
  • دانش یا قابلیت های تخصصی را تعریف کنید
  • دستورالعمل های قالب بندی را ارائه دهید
  • محدودیت هایی را برای پاسخ ها تنظیم کنید
  • نحوه برخورد با سناریوهای مختلف را شرح دهید

به یک اعلان سیستم فکر کنید که به LLM "شرح شغل" خود را می دهد - به مدل می گوید که چگونه در طول مکالمه رفتار کند.

چرا سیستم به شما می گوید مهم است

اعلان‌های سیستم برای ایجاد تعاملات مفید و منسجم LLM حیاتی هستند زیرا:

  1. اطمینان از سازگاری : مدل را راهنمایی کنید تا پاسخ ها را در قالبی ثابت ارائه کند
  2. بهبود ارتباط : مدل را روی دامنه خاص خود متمرکز کنید (در مورد شما، رنگ ها)
  3. تعیین مرزها : تعریف کنید که مدل چه کاری باید انجام دهد و چه کاری را نباید انجام دهد
  4. بهبود تجربه کاربر : یک الگوی تعاملی مفیدتر و طبیعی تر ایجاد کنید
  5. کاهش پس‌پردازش : پاسخ‌ها را در قالب‌هایی دریافت کنید که تجزیه یا نمایش آسان‌تر باشد

برای برنامه Colorist خود، به LLM نیاز دارید تا به طور مداوم توضیحات رنگ را تفسیر کند و مقادیر RGB را در قالب خاصی ارائه دهد.

یک دارایی سریع سیستم ایجاد کنید

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

یک فایل جدید 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. تعریف نقش : LLM را به عنوان "دستیار متخصص رنگ" ایجاد می کند.
  2. توضیح وظیفه : وظیفه اصلی را به عنوان تفسیر رنگ ها به مقادیر RGB تعریف می کند
  3. قالب پاسخ : دقیقاً مشخص می کند که مقادیر RGB چگونه باید برای سازگاری فرمت شوند
  4. مبادله مثال : یک مثال عینی از الگوی تعامل مورد انتظار ارائه می دهد
  5. Edge case handling : نحوه رسیدگی به توضیحات نامشخص را آموزش می دهد
  6. محدودیت ها و دستورالعمل ها : مرزهایی مانند حفظ مقادیر RGB بین 0.0 و 1.0 تعیین می کند.

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

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:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'system_prompt.g.dart';

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

این ارائه دهنده از سیستم بارگذاری دارایی 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:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

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

part 'gemini.g.dart';

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

@riverpod
Future<GenerativeModel> geminiModel(Ref ref) async {
 
await ref.watch(firebaseAppProvider.future);
 
final systemPrompt = await ref.watch(systemPromptProvider.future);  // Add this line

 
final model = FirebaseAI.googleAI().generativeModel(
   
model: 'gemini-2.0-flash',
   
systemInstruction: Content.system(systemPrompt),                  // And this line
 
);
 
return model;
}

@Riverpod(keepAlive: true)
Future<ChatSession> chatSession(Ref ref) async {
 
final model = await ref.watch(geminiModelProvider.future);
 
return model.startChat();
}

تغییر کلیدی اضافه کردن systemInstruction: Content.system(systemPrompt) هنگام ایجاد مدل تولیدی است. این به Gemini می‌گوید که از دستورالعمل‌های شما به‌عنوان پیام سیستم برای همه تعاملات در این جلسه چت استفاده کند.

کد Riverpod را ایجاد کنید

دستور build runner را برای تولید کد Riverpod مورد نیاز اجرا کنید:

dart run build_runner build --delete-conflicting-outputs

برنامه را اجرا و تست کنید

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

flutter run -d DEVICE

اسکرین شات برنامه Colorist که Gemini LLM را نشان می‌دهد که با یک نویسه به یک برنامه انتخاب رنگ پاسخ می‌دهد

سعی کنید آن را با توضیحات رنگ های مختلف آزمایش کنید:

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

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

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

اهمیت مهندسی سریع برای کارهای تخصصی

اعلان های سیستم هم هنر هستند و هم علم. آنها بخش مهمی از ادغام LLM هستند که می توانند به طور چشمگیری بر میزان مفید بودن مدل برای برنامه خاص شما تأثیر بگذارند. کاری که شما در اینجا انجام داده اید شکلی از مهندسی سریع است - دستورالعمل های خیاطی برای اینکه مدل به روشی مطابق با نیازهای برنامه شما رفتار کند.

مهندسی سریع موثر شامل:

  1. تعریف روشن نقش : تعیین هدف LLM
  2. دستورالعمل های صریح : جزئیات دقیقاً چگونه LLM باید پاسخ دهد
  3. مثال‌های مشخص : نشان دادن به‌جای بیان اینکه پاسخ‌های خوب چگونه هستند
  4. رسیدگی به پرونده لبه : آموزش LLM در مورد نحوه برخورد با سناریوهای مبهم
  5. مشخصات قالب‌بندی : اطمینان از ساختاربندی پاسخ‌ها به روشی سازگار و قابل استفاده

درخواست سیستمی که ایجاد کرده‌اید، قابلیت‌های عمومی Gemini را به یک دستیار تخصصی تفسیر رنگ تبدیل می‌کند که پاسخ‌هایی را که به‌طور خاص برای نیازهای برنامه شما قالب‌بندی شده‌اند، ارائه می‌دهد. این یک الگوی قدرتمند است که می توانید آن را برای دامنه ها و وظایف مختلف اعمال کنید.

بعدش چی؟

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

عیب یابی

مشکلات بارگیری دارایی

اگر هنگام بارگیری سیستم با خطا مواجه شدید:

  • بررسی کنید که pubspec.yaml شما به درستی فهرست دارایی ها را فهرست کرده است
  • بررسی کنید که مسیر موجود در rootBundle.loadString() با مکان فایل شما مطابقت داشته باشد
  • برای تازه کردن بسته دارایی، flutter clean کنید و سپس flutter pub get را اجرا کنید

پاسخ های متناقض

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

  • سعی کنید الزامات قالب را در اعلان سیستم واضح تر کنید
  • برای نشان دادن الگوی مورد انتظار مثال های بیشتری اضافه کنید
  • اطمینان حاصل کنید که قالب درخواستی شما برای مدل معقول است

محدود کردن نرخ API

اگر با خطاهای مربوط به محدود کردن نرخ مواجه شدید:

  • توجه داشته باشید که سرویس Firebase AI Logic دارای محدودیت های استفاده است
  • اجرای منطق تلاش مجدد را با عقب نشینی نمایی در نظر بگیرید
  • کنسول Firebase خود را برای هرگونه مشکل سهمیه بررسی کنید

مفاهیم کلیدی آموخته شده

  • درک نقش و اهمیت دستورات سیستم در برنامه های کاربردی LLM
  • ایجاد اعلان‌های مؤثر با دستورالعمل‌ها، مثال‌ها و محدودیت‌های واضح
  • بارگیری و استفاده از اعلان های سیستم در یک برنامه فلاتر
  • هدایت رفتار LLM برای وظایف خاص دامنه
  • استفاده از مهندسی سریع برای شکل دادن به پاسخ های LLM

این مرحله نشان می دهد که چگونه می توانید به سفارشی سازی قابل توجهی از رفتار LLM بدون تغییر کد خود دست یابید - به سادگی با ارائه دستورالعمل های واضح در اعلان سیستم.

5. اعلان عملکرد برای ابزارهای LLM

در این مرحله، کار فعال کردن Gemini را برای انجام اقداماتی در برنامه خود با اجرای اعلان‌های عملکرد آغاز می‌کنید. این ویژگی قدرتمند به LLM اجازه می دهد تا نه تنها مقادیر RGB را پیشنهاد کند، بلکه آنها را در UI برنامه شما از طریق فراخوانی ابزار تخصصی تنظیم کند. با این حال، برای دیدن درخواست‌های LLM اجرا شده در برنامه Flutter به مرحله بعدی نیاز دارید.

آنچه در این مرحله خواهید آموخت

  • آشنایی با فراخوانی تابع LLM و مزایای آن برای برنامه های Flutter
  • تعریف اعلان های تابع مبتنی بر طرحواره برای Gemini
  • ادغام اعلان های تابع با مدل Gemini شما
  • به روز رسانی اعلان سیستم برای استفاده از قابلیت های ابزار

درک فراخوانی تابع

قبل از اجرای اعلان‌های تابع، بیایید بفهمیم که آنها چیست و چرا ارزشمند هستند:

فراخوانی تابع چیست؟

فراخوانی تابع (گاهی اوقات "استفاده از ابزار" نامیده می شود) قابلیتی است که به LLM اجازه می دهد:

  1. تشخیص اینکه چه زمانی درخواست کاربر از فراخوانی یک تابع خاص سود می برد
  2. یک شی JSON ساختار یافته با پارامترهای مورد نیاز برای آن تابع تولید کنید
  3. اجازه دهید برنامه شما تابع را با آن پارامترها اجرا کند
  4. نتیجه تابع را دریافت کنید و آن را در پاسخ آن بگنجانید

فراخوانی تابع به جای اینکه LLM فقط توضیح دهد که چه کاری باید انجام شود، LLM را قادر می سازد تا اقدامات مشخصی را در برنامه شما ایجاد کند.

چرا فراخوانی تابع برای برنامه های Flutter مهم است

فراخوانی تابع یک پل قدرتمند بین زبان طبیعی و ویژگی های برنامه ایجاد می کند:

  1. اقدام مستقیم : کاربران می‌توانند آنچه را که می‌خواهند به زبان طبیعی توصیف کنند و برنامه با اقدامات مشخص پاسخ می‌دهد
  2. خروجی ساختاریافته : LLM به جای متنی که نیاز به تجزیه دارد، داده های تمیز و ساختار یافته تولید می کند
  3. عملیات پیچیده : LLM را قادر می سازد به داده های خارجی دسترسی داشته باشد، محاسبات را انجام دهد یا وضعیت برنامه را تغییر دهد.
  4. تجربه کاربری بهتر : ادغام یکپارچه بین مکالمه و عملکرد ایجاد می کند

در برنامه Colorist شما، فراخوانی تابع به کاربران این امکان را می‌دهد که بگویند «من یک جنگل سبز می‌خواهم» و بدون نیاز به تجزیه مقادیر RGB از متن، فوراً رابط کاربری را با آن رنگ به‌روزرسانی کنند.

اعلان های تابع را تعریف کنید

یک فایل جدید lib/services/gemini_tools.dart ایجاد کنید تا اعلان های تابع خود را تعریف کنید:

lib/services/gemini_tools.dart

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

part 'gemini_tools.g.dart';

class GeminiTools {
 
GeminiTools(this.ref);

 
final Ref ref;

 
FunctionDeclaration get setColorFuncDecl => FunctionDeclaration(
   
'set_color',
   
'Set the color of the display square based on red, green, and blue values.',
   
parameters: {
     
'red': Schema.number(description: 'Red component value (0.0 - 1.0)'),
     
'green': Schema.number(description: 'Green component value (0.0 - 1.0)'),
     
'blue': Schema.number(description: 'Blue component value (0.0 - 1.0)'),
   
},
 
);

 
List<Tool> get tools => [
   
Tool.functionDeclarations([setColorFuncDecl]),
 
];
}

@riverpod
GeminiTools geminiTools(Ref ref) => GeminiTools(ref);

درک اعلان های تابع

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

  1. نام گذاری تابع : شما تابع خود را set_color نام می گذارید تا به وضوح هدف آن را مشخص کنید
  2. شرح عملکرد : شما یک توضیح واضح ارائه می‌دهید که به LLM کمک می‌کند بفهمد چه زمانی باید از آن استفاده کند
  3. تعاریف پارامتر : شما پارامترهای ساختاریافته را با توضیحات خود تعریف می کنید:
    • red : جزء قرمز رنگ RGB که به عنوان عددی بین 0.0 و 1.0 مشخص شده است
    • green : جزء سبز رنگ RGB که به صورت عددی بین 0.0 و 1.0 مشخص شده است
    • blue : جزء آبی RGB که به عنوان عددی بین 0.0 و 1.0 مشخص شده است
  4. انواع طرحواره : شما از Schema.number() برای نشان دادن این مقادیر عددی استفاده می کنید
  5. مجموعه ابزار : شما فهرستی از ابزارهای حاوی اعلان تابع خود را ایجاد می کنید

این رویکرد ساختاریافته به Gemini LLM کمک می کند تا درک کند:

  • زمانی که باید این تابع را فراخوانی کند
  • چه پارامترهایی را باید ارائه دهد
  • چه محدودیت هایی برای آن پارامترها اعمال می شود (مانند محدوده مقدار)

ارائه دهنده مدل Gemini را به روز کنید

اکنون، فایل lib/providers/gemini.dart خود را تغییر دهید تا در هنگام مقداردهی اولیه مدل Gemini، اعلان‌های تابع را نیز شامل شود:

lib/providers/gemini.dart

import 'dart:async';

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

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

part 'gemini.g.dart';

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

@riverpod
Future<GenerativeModel> geminiModel(Ref ref) async {
 
await ref.watch(firebaseAppProvider.future);
 
final systemPrompt = await ref.watch(systemPromptProvider.future);
 
final geminiTools = ref.watch(geminiToolsProvider);                // Add this line

 
final model = FirebaseAI.googleAI().generativeModel(
   
model: 'gemini-2.0-flash',
   
systemInstruction: Content.system(systemPrompt),
   
tools: geminiTools.tools,                                        // And this line
 
);
 
return model;
}

@Riverpod(keepAlive: true)
Future<ChatSession> chatSession(Ref ref) async {
 
final model = await ref.watch(geminiModelProvider.future);
 
return model.startChat();
}

تغییر کلیدی اضافه کردن tools: geminiTools.tools هنگام ایجاد مدل مولد. این باعث می شود Gemini از عملکردهایی که برای تماس در دسترس است آگاه شود.

اعلان سیستم را به روز کنید

اکنون باید اعلان سیستم خود را تغییر دهید تا به LLM در مورد استفاده از ابزار جدید 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. معرفی ابزار : به جای درخواست مقادیر RGB فرمت شده، اکنون در مورد ابزار set_color به LLM بگویید.
  2. فرآیند اصلاح شده : شما مرحله 3 را از "مقادیر قالب در پاسخ" به "استفاده از ابزار برای تنظیم مقادیر" تغییر می دهید.
  3. مثال به‌روزرسانی شده : نشان می‌دهید که چگونه پاسخ باید شامل یک تماس ابزار به جای متن قالب‌بندی شده باشد
  4. نیاز به قالب‌بندی حذف شده : از آنجایی که از فراخوانی عملکرد ساختاریافته استفاده می‌کنید، دیگر به قالب متن خاصی نیاز ندارید

این درخواست به روز شده، LLM را به استفاده از فراخوانی تابع به جای ارائه مقادیر RGB به صورت متن هدایت می کند.

کد Riverpod را ایجاد کنید

دستور build runner را برای تولید کد Riverpod مورد نیاز اجرا کنید:

dart run build_runner build --delete-conflicting-outputs

برنامه را اجرا کنید

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

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

flutter run -d DEVICE

عکس صفحه برنامه Colorist که Gemini LLM را نشان می دهد که با یک پاسخ جزئی پاسخ می دهد

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

فرآیند فراخوانی تابع

بیایید بفهمیم وقتی Gemini از فراخوانی تابع استفاده می کند چه اتفاقی می افتد:

  1. انتخاب تابع : LLM تصمیم می گیرد که آیا فراخوانی تابع بر اساس درخواست کاربر مفید باشد یا خیر
  2. تولید پارامتر : LLM مقادیر پارامتر متناسب با طرح تابع را تولید می کند
  3. فرمت فراخوانی تابع : LLM یک شیء فراخوانی تابع ساختار یافته را در پاسخ خود ارسال می کند
  4. مدیریت برنامه : برنامه شما این تماس را دریافت می کند و عملکرد مربوطه را اجرا می کند (در مرحله بعدی پیاده سازی می شود)
  5. یکپارچه سازی پاسخ : در مکالمات چند نوبتی، LLM انتظار دارد که نتیجه تابع برگردانده شود.

در وضعیت فعلی برنامه شما، سه مرحله اول در حال انجام است، اما شما هنوز مرحله 4 یا 5 (بررسی فراخوانی های تابع) را اجرا نکرده اید، که در مرحله بعد انجام خواهید داد.

جزئیات فنی: Gemini چگونه تصمیم می گیرد که چه زمانی از توابع استفاده کند

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

  1. هدف کاربر : اینکه آیا درخواست کاربر به بهترین شکل توسط یک تابع ارائه می شود یا خیر
  2. ارتباط عملکرد : عملکردهای موجود چقدر با کار مطابقت دارند
  3. در دسترس بودن پارامتر : آیا می تواند مقادیر پارامتر را با اطمینان تعیین کند
  4. دستورالعمل های سیستم : راهنمایی از سیستم شما در مورد استفاده از عملکرد

با ارائه اعلان‌های عملکرد واضح و دستورالعمل‌های سیستم، Gemini را تنظیم کرده‌اید تا درخواست‌های توصیف رنگ را به عنوان فرصتی برای فراخوانی تابع set_color تشخیص دهد.

بعدش چی؟

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

عیب یابی

مسائل مربوط به اعلام عملکرد

اگر در اعلان تابع با خطا مواجه شدید:

  • بررسی کنید که نام و انواع پارامترها با آنچه مورد انتظار است مطابقت داشته باشد
  • بررسی کنید که نام تابع واضح و توصیفی باشد
  • اطمینان حاصل کنید که توضیحات عملکرد دقیقاً هدف آن را توضیح می دهد

مشکلات سریع سیستم

اگر LLM سعی در استفاده از تابع ندارد:

  • بررسی کنید که درخواست سیستم شما به وضوح به LLM دستور می دهد که از ابزار set_color استفاده کند
  • بررسی کنید که مثال در اعلان سیستم، استفاده از عملکرد را نشان می دهد
  • سعی کنید دستورالعمل استفاده از ابزار را واضح تر کنید

مسائل کلی

اگر با مشکلات دیگری مواجه شدید:

  • کنسول را برای هر گونه خطای مربوط به اعلان عملکرد بررسی کنید
  • بررسی کنید که ابزارها به درستی به مدل منتقل شده باشند
  • اطمینان حاصل کنید که تمام کدهای تولید شده توسط Riverpod به روز هستند

مفاهیم کلیدی آموخته شده

  • تعریف اعلان‌های عملکرد برای گسترش قابلیت‌های LLM در برنامه‌های Flutter
  • ایجاد طرحواره های پارامتر برای جمع آوری داده های ساخت یافته
  • ادغام اعلان های تابع با مدل Gemini
  • به روز رسانی سیستم برای تشویق استفاده از عملکرد درخواست می کند
  • درک نحوه انتخاب و فراخوانی توابع توسط LLM

این مرحله نشان می‌دهد که چگونه LLMها می‌توانند شکاف بین ورودی زبان طبیعی و فراخوانی‌های تابع ساختاریافته را پر کنند و زمینه را برای یکپارچگی یکپارچه بین ویژگی‌های مکالمه و برنامه فراهم کنند.

6. پیاده سازی جابجایی ابزار

در این مرحله، کنترل کننده‌هایی را برای فراخوانی‌های تابعی که از Gemini ارسال می‌شوند، پیاده‌سازی می‌کنید. این حلقه ارتباط بین ورودی‌های زبان طبیعی و ویژگی‌های کاربردی خاص را تکمیل می‌کند و به LLM اجازه می‌دهد تا مستقیماً UI شما را بر اساس توضیحات کاربر دستکاری کند.

آنچه در این مرحله خواهید آموخت

  • درک کامل خط لوله فراخوانی تابع در برنامه های LLM
  • پردازش تماس‌های تابع از Gemini در برنامه Flutter
  • پیاده سازی کنترل کننده های تابع که وضعیت برنامه را تغییر می دهند
  • مدیریت پاسخ های تابع و برگرداندن نتایج به LLM
  • ایجاد یک جریان ارتباطی کامل بین LLM و UI
  • تماس و پاسخ های عملکردی برای شفافیت

درک خط لوله عملکردی

قبل از غواصی به اجرای ، بیایید خط لوله فراخوانی عملکرد کامل را درک کنیم:

جریان پایان به پایان

  1. ورودی کاربر : کاربر رنگی را در زبان طبیعی توصیف می کند (به عنوان مثال ، "جنگل سبز")
  2. پردازش LLM : جمینی توضیحات را تجزیه و تحلیل می کند و تصمیم می گیرد با عملکرد set_color تماس بگیرید
  3. تولید تماس عملکرد : جمینی JSON ساختاری را با پارامترها (مقادیر قرمز ، سبز ، آبی) ایجاد می کند
  4. پذیرش تماس عملکرد : برنامه شما این داده های ساختاری را از Gemini دریافت می کند
  5. اجرای عملکرد : برنامه شما عملکرد را با پارامترهای ارائه شده اجرا می کند
  6. بروزرسانی حالت : عملکرد وضعیت برنامه شما را به روز می کند (تغییر رنگ نمایش داده شده)
  7. تولید پاسخ : عملکرد شما نتایج به LLM برمی گردد
  8. ترکیب پاسخ : LLM این نتایج را در پاسخ نهایی خود گنجانیده است
  9. UI Update : UI شما به تغییر حالت واکنش نشان می دهد و رنگ جدید را نشان می دهد

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

پیاده سازی های عملکردی

بیایید فایل 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:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'gemini_tools.g.dart';

class GeminiTools {
 
GeminiTools(this.ref);

 
final Ref ref;

 
FunctionDeclaration get setColorFuncDecl => FunctionDeclaration(
   
'set_color',
   
'Set the color of the display square based on red, green, and blue values.',
   
parameters: {
     
'red': Schema.number(description: 'Red component value (0.0 - 1.0)'),
     
'green': Schema.number(description: 'Green component value (0.0 - 1.0)'),
     
'blue': Schema.number(description: 'Blue component value (0.0 - 1.0)'),
   
},
 
);

 
List<Tool> get tools => [
   
Tool.functionDeclarations([setColorFuncDecl]),
 
];

 
Map<String, Object?> handleFunctionCall(                           // Add from here
   
String functionName,
   
Map<String, Object?> arguments,
 
) {
   
final logStateNotifier = ref.read(logStateNotifierProvider.notifier);
   
logStateNotifier.logFunctionCall(functionName, arguments);
   
return switch (functionName) {
     
'set_color' => handleSetColor(arguments),
     
_ => handleUnknownFunction(functionName),
   
};
 
}

 
Map<String, Object?> handleSetColor(Map<String, Object?> arguments) {
   
final colorStateNotifier = ref.read(colorStateNotifierProvider.notifier);
   
final red = (arguments['red'] as num).toDouble();
   
final green = (arguments['green'] as num).toDouble();
   
final blue = (arguments['blue'] as num).toDouble();
   
final functionResults = {
     
'success': true,
     
'current_color': colorStateNotifier
         
.updateColor(red: red, green: green, blue: blue)
         
.toLLMContextMap(),
   
};

   
final logStateNotifier = ref.read(logStateNotifierProvider.notifier);
   
logStateNotifier.logFunctionResults(functionResults);
   
return functionResults;
 
}

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

@riverpod
GeminiTools geminiTools(Ref ref) => GeminiTools(ref);

درک دستگیره های عملکرد

بیایید آنچه را که این دستگیرندگان عملکرد انجام می دهند را تجزیه کنیم:

  1. handleFunctionCall : یک توزیع کننده مرکزی که:
    • ورود به سیستم برای شفافیت در پانل ورود به سیستم را ثبت می کند
    • مسیرهای مربوط به کنترل کننده مناسب بر اساس نام عملکرد
    • پاسخ ساختاری را برمی گرداند که به LLM ارسال می شود
  2. handleSetColor : کنترل کننده خاص برای عملکرد set_color شما که:
    • مقادیر RGB را از نقشه آرگومان استخراج می کند
    • آنها را به انواع مورد انتظار تبدیل می کند (دو برابر)
    • حالت رنگ برنامه را با استفاده از colorStateNotifier به روز می کند
    • با وضعیت موفقیت و اطلاعات رنگی فعلی یک پاسخ ساختاری ایجاد می کند
    • نتایج عملکرد را برای اشکال زدایی ثبت می کند
  3. handleUnknownFunction : یک کنترل کننده Fallback برای عملکردهای ناشناخته که:
    • هشدار در مورد عملکرد پشتیبانی نشده را ثبت می کند
    • پاسخ خطا به LLM را برمی گرداند

عملکرد handleSetColor از اهمیت ویژه ای برخوردار است زیرا شکاف بین درک زبان طبیعی LLM و تغییر UI بتن را ایجاد می کند.

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

حال ، بیایید پرونده lib/services/gemini_chat_service.dart را به روز کنیم تا تماس های عملکردی را از پاسخ های LLM پردازش کنیم و نتایج را به LLM ارسال کنیم:

lib/services/gemini_chat_service.dart

import 'dart:async';

import 'package:colorist_ui/colorist_ui.dart';
import 'package:firebase_ai/firebase_ai.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

import '../providers/gemini.dart';
import 'gemini_tools.dart';                                          // Add this import

part 'gemini_chat_service.g.dart';

class GeminiChatService {
 
GeminiChatService(this.ref);
 
final Ref ref;

 
Future<void> sendMessage(String message) async {
   
final chatSession = await ref.read(chatSessionProvider.future);
   
final chatStateNotifier = ref.read(chatStateNotifierProvider.notifier);
   
final logStateNotifier = ref.read(logStateNotifierProvider.notifier);

   
chatStateNotifier.addUserMessage(message);
   
logStateNotifier.logUserText(message);
   
final llmMessage = chatStateNotifier.createLlmMessage();
   
try {
     
final response = await chatSession.sendMessage(Content.text(message));

     
final responseText = response.text;
     
if (responseText != null) {
       
logStateNotifier.logLlmText(responseText);
       
chatStateNotifier.appendToMessage(llmMessage.id, responseText);
     
}

     
if (response.functionCalls.isNotEmpty) {                       // Add from here
       
final geminiTools = ref.read(geminiToolsProvider);
       
final functionResultResponse = await chatSession.sendMessage(
         
Content.functionResponses([
           
for (final functionCall in response.functionCalls)
             
FunctionResponse(
               
functionCall.name,
               
geminiTools.handleFunctionCall(
                 
functionCall.name,
                 
functionCall.args,
               
),
             
),
         
]),
       
);
       
final responseText = functionResultResponse.text;
       
if (responseText != null) {
         
logStateNotifier.logLlmText(responseText);
         
chatStateNotifier.appendToMessage(llmMessage.id, responseText);
       
}
     
}                                                              // To here.
   
} catch (e, st) {
     
logStateNotifier.logError(e, st: st);
     
chatStateNotifier.appendToMessage(
       
llmMessage.id,
       
"\nI'm sorry, I encountered an error processing your request. "
       
"Please try again.",
     
);
   
} finally {
     
chatStateNotifier.finalizeMessage(llmMessage.id);
   
}
 
}
}

@riverpod
GeminiChatService geminiChatService(Ref ref) => GeminiChatService(ref);

درک جریان ارتباطات

علاوه بر این ، استفاده کامل از تماس ها و پاسخ های عملکردی است:

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. بررسی می کند که آیا پاسخ LLM شامل هرگونه تماس عملکردی است
  2. برای هر تماس عملکردی ، با نام عملکرد و آرگومان ها از روش handleFunctionCall خود فراخوانی می کند
  3. نتایج هر تماس عملکرد را جمع می کند
  4. این نتایج را با استفاده از Content.functionResponses به LLM ارسال می کند
  5. پاسخ LLM به نتایج عملکرد را پردازش می کند
  6. UI را با متن پاسخ نهایی به روز می کند

این یک جریان سفر دور ایجاد می کند:

  • کاربر → LLM: درخواست رنگ می کند
  • LLM → برنامه: تماس با عملکرد با پارامترها
  • برنامه → کاربر: رنگ جدید نمایش داده می شود
  • برنامه → LLM: نتایج عملکرد
  • LLM → کاربر: پاسخ نهایی شامل نتایج عملکرد

کد Riverpod را تولید کنید

دستور ساخت Runner را برای تولید کد مورد نیاز RiverPod اجرا کنید:

dart run build_runner build --delete-conflicting-outputs

جریان کامل را اجرا و آزمایش کنید

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

flutter run -d DEVICE

تصویر برنامه Colorist نشان می دهد Gemini LLM که با یک تماس عملکرد پاسخ می دهد

سعی کنید توضیحات مختلف رنگ را وارد کنید:

  • "من یک قمار قرمز عمیق می خواهم"
  • "به من یک آبی آرام آسمان به من نشان دهید"
  • "رنگ برگهای نعناع تازه را به من بدهید"
  • "من می خواهم یک پرتقال غروب خورشید را ببینم"
  • "آن را به یک بنفش سلطنتی غنی تبدیل کنید"

حالا باید ببینید:

  1. پیام شما در رابط چت ظاهر می شود
  2. پاسخ جمینی که در گپ ظاهر می شود
  3. تماس های عملکردی که در پانل ورود به سیستم وارد می شوند
  4. نتایج عملکرد بلافاصله پس از ثبت نام
  5. به روزرسانی مستطیل رنگ برای نمایش رنگ توصیف شده
  6. مقادیر RGB به روزرسانی برای نشان دادن اجزای رنگ جدید
  7. پاسخ نهایی جمینی ظاهر می شود ، اغلب در مورد رنگی که تنظیم شده است اظهار نظر می کند

پانل ورود به سیستم بینشی در مورد آنچه در پشت صحنه اتفاق می افتد ارائه می دهد. خواهید دید:

  • عملکرد دقیق با جمینی تماس می گیرد
  • پارامترهایی که برای هر مقدار RGB انتخاب می کند
  • نتیجه عملکرد شما در حال بازگشت است
  • پاسخ های پیگیری از جمینی

اعلان کننده حالت رنگ

colorStateNotifier که برای به روزرسانی رنگ ها استفاده می کنید بخشی از بسته colorist_ui است. مدیریت می کند:

  • رنگ فعلی نمایش داده شده در UI
  • تاریخچه رنگ (10 رنگ آخر)
  • اطلاع رسانی در مورد تغییرات دولت در مؤلفه های UI

هنگامی که با updateColor با مقادیر جدید RGB تماس می گیرید ، آن:

  1. با مقادیر ارائه شده یک شیء جدید ColorData ایجاد می کند
  2. رنگ فعلی را در حالت برنامه به روز می کند
  3. رنگ را به تاریخ اضافه می کند
  4. به روزرسانی های UI از طریق مدیریت دولت Riverpod را تحریک می کند

اجزای UI در بسته colorist_ui این حالت را تماشا می کنند و هنگام تغییر به طور خودکار به روز می شوند و یک تجربه واکنشی ایجاد می کنند.

درک رسیدگی به خطای

اجرای شما شامل رسیدگی به خطای قوی است:

  1. بلوک Try-Catch : تمام تعامل های LLM را برای گرفتن هرگونه استثنا می کند
  2. ورود به خطا : خطاهای سوابق موجود در صفحه ورود به سیستم با آثار پشته
  3. بازخورد کاربر : یک پیام خطای دوستانه در گپ ارائه می دهد
  4. پاکسازی حالت : حتی اگر خطایی رخ دهد ، وضعیت پیام را نهایی می کند

این تضمین می کند که برنامه پایدار باقی مانده و بازخورد مناسبی را ارائه می دهد حتی در صورت بروز مشکلات با سرویس LLM یا اجرای عملکرد.

قدرت عملکرد خواستار تجربه کاربر

آنچه در اینجا انجام داده اید نشان می دهد که چگونه LLM ها می توانند رابط های طبیعی قدرتمند ایجاد کنند:

  1. رابط زبان طبیعی : کاربران در زبان روزمره قصد دارند
  2. تفسیر هوشمند : LLM توضیحات مبهم را به ارزشهای دقیق ترجمه می کند
  3. دستکاری مستقیم : UI در پاسخ به زبان طبیعی به روز می شود
  4. پاسخ های متنی : LLM زمینه مکالمه ای را در مورد تغییرات ارائه می دهد
  5. بار شناختی کم : کاربران نیازی به درک مقادیر RGB یا نظریه رنگ ندارند

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

بعدش چی؟

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

عیب یابی

مسائل مربوط به تماس عملکرد

اگر جمینی با توابع یا پارامترهای شما تماس نگیرد نادرست است:

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

مسائل پاسخ عملکرد

اگر نتایج عملکرد به درستی به LLM ارسال نمی شود:

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

مشکلات نمایش رنگ

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

  • اطمینان حاصل کنید که مقادیر RGB به درستی به دونفره تبدیل شده است (LLM ممکن است آنها را به عنوان عدد صحیح ارسال کند)
  • تأیید کنید که مقادیر در محدوده مورد انتظار هستند (0.0 تا 1.0)
  • بررسی کنید که اطلاعات حالت رنگی به درستی خوانده می شود
  • برای مقادیر دقیق منتقل شده به عملکرد ، ورود به سیستم را بررسی کنید

مشکلات عمومی

برای مسائل عمومی:

  • سیاهههای مربوط به خطاها یا هشدارها را بررسی کنید
  • اتصال منطق AI Firebase را تأیید کنید
  • هر نوع عدم تطابق را در پارامترهای عملکرد بررسی کنید
  • اطمینان حاصل کنید که تمام کد تولید شده Riverpod به روز است

مفاهیم کلیدی آموخته شده

  • اجرای یک خط لوله فراخوانی عملکرد کامل در Flutter
  • ایجاد ارتباط کامل بین LLM و برنامه شما
  • پردازش داده های ساختاری از پاسخ های LLM
  • ارسال نتایج عملکرد به LLM برای ترکیب در پاسخ ها
  • با استفاده از پانل ورود به سیستم برای تعامل LLM-Application
  • اتصال ورودی های زبان طبیعی به تغییرات UI بتن

با تکمیل این مرحله ، برنامه شما اکنون یکی از قدرتمندترین الگوهای برای ادغام LLM را نشان می دهد: ترجمه ورودی های زبان طبیعی به اقدامات UI بتن ، ضمن حفظ مکالمه منسجم که این اقدامات را تصدیق می کند. این یک رابط بصری و مکالمه ای ایجاد می کند که برای کاربران جادویی احساس می کند.

7. پاسخ های جریان برای بهتر UX

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

آنچه را در این مرحله پوشش خواهید داد

  • اهمیت جریان برای برنامه های کاربردی LLM
  • اجرای پاسخ های جریان LLM در یک برنامه Flutter
  • پردازش تکه های متن جزئی هنگام ورود از API
  • مدیریت وضعیت مکالمه برای جلوگیری از درگیری پیام
  • تماس با عملکرد تماس در پاسخ های جریان
  • ایجاد شاخص های بصری برای پاسخ های در حال پیشرفت

چرا جریان برای برنامه های LLM مهم است

قبل از اجرای ، بیایید درک کنیم که چرا پاسخ های جریان برای ایجاد تجربیات عالی کاربر با LLM ها بسیار مهم هستند:

تجربه کاربری بهبود یافته

پاسخ های جریان چندین مزایای مهم تجربه کاربر را ارائه می دهد:

  1. کاهش تأخیر درک شده : کاربران می بینند که متن بلافاصله شروع می شود (به طور معمول در 100-300ms) ، به جای اینکه منتظر چندین ثانیه برای پاسخ کامل باشید. این برداشت از فوری به طرز چشمگیری رضایت کاربر را بهبود می بخشد.
  2. ریتم مکالمه طبیعی : ظاهر تدریجی متن از نحوه ارتباط انسان تقلید می کند و یک تجربه گفتگوی طبیعی تر ایجاد می کند.
  3. پردازش اطلاعات مترقی : کاربران می توانند پردازش اطلاعات را به محض ورود به جای اینکه در یک بلوک بزرگ متن به یکباره غرق شوند ، شروع به پردازش اطلاعات کنند.
  4. فرصتی برای وقفه زودهنگام : در یک برنامه کامل ، کاربران به طور بالقوه می توانند LLM را قطع یا هدایت کنند در صورتی که ببینند در یک جهت غیرقانونی قرار دارد.
  5. تأیید بصری فعالیت : متن جریان بازخورد فوری را ارائه می دهد که سیستم در حال کار است و باعث عدم اطمینان می شود.

مزایای فنی

فراتر از پیشرفت های UX ، جریان مزایای فنی را ارائه می دهد:

  1. اجرای عملکرد اولیه : تماس های عملکردی به محض ظاهر شدن در جریان ، بدون انتظار برای پاسخ کامل ، قابل شناسایی و اجرا هستند.
  2. به روزرسانی های افزایشی UI : با ورود اطلاعات جدید می توانید UI خود را به تدریج به روز کنید و یک تجربه پویاتر را ایجاد کنید.
  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';

final conversationStateProvider = StateProvider(                     // Add from here...
 
(ref) => ConversationState.idle,
);                                                                   // To here.

class GeminiChatService {
 
GeminiChatService(this.ref);
 
final Ref ref;

 
Future<void> sendMessage(String message) async {
   
final chatSession = await ref.read(chatSessionProvider.future);
   
final conversationState = ref.read(conversationStateProvider);   // Add this line
   
final chatStateNotifier = ref.read(chatStateNotifierProvider.notifier);
   
final logStateNotifier = ref.read(logStateNotifierProvider.notifier);

   
if (conversationState == ConversationState.busy) {               // Add from here...
     
logStateNotifier.logWarning(
       
"Can't send a message while a conversation is in progress",
     
);
     
throw Exception(
       
"Can't send a message while a conversation is in progress",
     
);
   
}
   
final conversationStateNotifier = ref.read(
     
conversationStateProvider.notifier,
   
);
   
conversationStateNotifier.state = ConversationState.busy;        // To here.
   
chatStateNotifier.addUserMessage(message);
   
logStateNotifier.logUserText(message);
   
final llmMessage = chatStateNotifier.createLlmMessage();
   
try {                                                            // Modify from here...
     
final responseStream = chatSession.sendMessageStream(
       
Content.text(message),
     
);
     
await for (final block in responseStream) {
       
await _processBlock(block, llmMessage.id);
     
}                                                              // To here.
   
} catch (e, st) {
     
logStateNotifier.logError(e, st: st);
     
chatStateNotifier.appendToMessage(
       
llmMessage.id,
       
"\nI'm sorry, I encountered an error processing your request. "
       
"Please try again.",
     
);
   
} finally {
     
chatStateNotifier.finalizeMessage(llmMessage.id);
     
conversationStateNotifier.state = ConversationState.idle;      // Add this line.
   
}
 
}

 
Future<void> _processBlock(                                        // Add from here...
   
GenerateContentResponse block,
   
String llmMessageId,
 
) async {
   
final chatSession = await ref.read(chatSessionProvider.future);
   
final chatStateNotifier = ref.read(chatStateNotifierProvider.notifier);
   
final logStateNotifier = ref.read(logStateNotifierProvider.notifier);
   
final blockText = block.text;

   
if (blockText != null) {
     
logStateNotifier.logLlmText(blockText);
     
chatStateNotifier.appendToMessage(llmMessageId, blockText);
   
}

   
if (block.functionCalls.isNotEmpty) {
     
final geminiTools = ref.read(geminiToolsProvider);
     
final responseStream = chatSession.sendMessageStream(
       
Content.functionResponses([
         
for (final functionCall in block.functionCalls)
           
FunctionResponse(
             
functionCall.name,
             
geminiTools.handleFunctionCall(
               
functionCall.name,
               
functionCall.args,
             
),
           
),
       
]),
     
);
     
await for (final response in responseStream) {
       
final responseText = response.text;
       
if (responseText != null) {
         
logStateNotifier.logLlmText(responseText);
         
chatStateNotifier.appendToMessage(llmMessageId, responseText);
       
}
     
}
   
}
 
}                                                                  // To here.
}

@riverpod
GeminiChatService geminiChatService(Ref ref) => GeminiChatService(ref);

درک اجرای جریان

بیایید آنچه را که این کد انجام می دهد تجزیه کنیم:

  1. ردیابی حالت مکالمه :
    • یک conversationStateProvider پیگیری می کند که آیا این برنامه در حال حاضر پاسخ پاسخ می دهد
    • دولت از idlebusy هنگام پردازش است ، سپس به idle باز می گردد
    • این مانع از چندین درخواست همزمان می شود که می توانند درگیری داشته باشند
  2. اولیه سازی جریان :
    • sendMessageStream() به جای Future با پاسخ کامل ، یک جریان از بخش های پاسخ را برمی گرداند
    • هر تکه ممکن است حاوی متن ، تماس عملکرد یا هر دو باشد
  3. پردازش مترقی :
    • await for فرآیندها هر قطعه در زمان واقعی شدن است
    • متن بلافاصله به UI اضافه می شود و اثر جریان را ایجاد می کند
    • تماس های عملکردی به محض شناسایی اجرا می شوند
  4. عملکرد تماس با عملکرد :
    • هنگامی که یک تماس عملکردی در یک تکه تشخیص داده می شود ، بلافاصله اجرا می شود
    • نتایج از طریق یک تماس جریان دیگر به LLM ارسال می شود
    • پاسخ LLM به این نتایج نیز به صورت جریان پردازش می شود
  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 ) از این حالت برای غیرفعال کردن ورودی متن در حالی که پاسخ در حال پردازش است ، استفاده می کند.

این یک تجربه کاربر منسجم را ایجاد می کند که در آن UI وضعیت فعلی مکالمه را منعکس می کند.

کد Riverpod را تولید کنید

دستور ساخت Runner را برای تولید کد مورد نیاز RiverPod اجرا کنید:

dart run build_runner build --delete-conflicting-outputs

پاسخ های جریان را اجرا و آزمایش کنید

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

flutter run -d DEVICE

تصویر برنامه Colorist نشان می دهد Gemini LLM که به صورت جریان پاسخ می دهد

اکنون سعی کنید رفتار جریان را با توضیحات مختلف رنگ آزمایش کنید. توضیحات مانند:

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

جریان فنی جریان با جزئیات

بیایید دقیقاً بررسی کنیم که هنگام پخش پاسخ چه اتفاقی می افتد:

ایجاد اتصال

هنگامی که با sendMessageStream() تماس می گیرید ، موارد زیر اتفاق می افتد:

  1. این برنامه ارتباطی به سرویس منطق AI Firebase ایجاد می کند
  2. درخواست کاربر به سرویس ارسال می شود
  3. سرور شروع به پردازش درخواست می کند
  4. اتصال جریان باز است ، آماده انتقال تکه ها

گیربکس

همانطور که جمینی محتوا تولید می کند ، تکه ها از طریق جریان ارسال می شوند:

  1. سرور تکه های متن را به عنوان تولید می کند (به طور معمول چند کلمه یا جملات)
  2. هنگامی که جمینی تصمیم به برقراری تماس عملکردی گرفت ، اطلاعات تماس عملکرد را ارسال می کند
  3. تکه های متن اضافی ممکن است تماس های عملکردی را دنبال کنند
  4. جریان تا زمان کامل شدن نسل ادامه می یابد

پردازش پیشرونده

برنامه شما هر قطعه را به صورت تدریجی پردازش می کند:

  1. هر قطعه متن به پاسخ موجود اضافه می شود
  2. تماس های عملکردی به محض شناسایی اجرا می شوند
  3. UI در زمان واقعی با نتایج متن و عملکرد به روز می شود
  4. حالت ردیابی می شود تا نشان دهد که پاسخ هنوز جریان دارد

اتمام جریان

وقتی نسل کامل شد:

  1. جریان توسط سرور بسته شده است
  2. await for خروج حلقه به طور طبیعی است
  3. پیام به صورت کامل مشخص شده است
  4. حالت مکالمه به حالت بیکار باز می گردد
  5. به روزرسانی های UI برای بازتاب وضعیت تکمیل شده

جریان در مقابل مقایسه غیر جریان

برای درک بهتر فواید جریان ، بیایید با رویکردهای غیر پخش کننده جریان را مقایسه کنیم:

جنبه

غیر زنده

پخش جریانی

تأخیر درک شده

کاربر تا زمان آماده شدن پاسخ کامل چیزی نمی بیند

کاربر اولین کلمات را در میلی ثانیه می بیند

تجربه کاربری

انتظار طولانی به دنبال ظاهر ناگهانی متن

ظاهر متن طبیعی و مترقی

مدیریت دولتی

ساده تر (پیام ها در انتظار یا کامل هستند)

پیچیده تر (پیام ها می توانند در حالت جریان باشند)

اجرای عملکرد

فقط پس از پاسخ کامل رخ می دهد

در طول تولید پاسخ رخ می دهد

پیچیدگی اجرا

ساده تر برای اجرای

به مدیریت اضافی دولت نیاز دارد

بازیابی خطا

پاسخ همه یا هیچ چیز

پاسخ های جزئی هنوز هم ممکن است مفید باشد

پیچیدگی کد

پیچیدگی کمتر

پیچیده تر به دلیل استفاده از جریان

برای برنامه ای مانند Colorist ، مزایای UX از جریان از پیچیدگی اجرای ، به ویژه برای تفسیرهای رنگی که ممکن است چندین ثانیه طول بکشد ، بیشتر است.

بهترین روشها برای پخش UX

هنگام اجرای جریان در برنامه های LLM خود ، این بهترین روش ها را در نظر بگیرید:

  1. شاخص های بصری پاک : همیشه نشانه های بصری شفاف ارائه دهید که جریان را در مقابل پیام های کامل متمایز می کند
  2. مسدود کردن ورودی : ورودی کاربر را در حین پخش غیرفعال کنید تا از چندین درخواست همپوشانی جلوگیری کنید
  3. بازیابی خطا : در صورت قطع جریان ، UI خود را برای کنترل بازیابی برازنده طراحی کنید
  4. انتقال حالت : از انتقال صاف بین حالت های بیکار ، جریان و کامل اطمینان حاصل کنید
  5. تجسم پیشرفت : انیمیشن ها یا شاخص های ظریف را نشان می دهد که پردازش فعال را نشان می دهد
  6. گزینه های لغو : در یک برنامه کامل ، راه هایی را برای کاربران فراهم کنید تا نسل های در حال پیشرفت را لغو کنند
  7. یکپارچه سازی نتیجه عملکرد : UI خود را برای کنترل نتایج عملکردی که در اواسط جریان ظاهر می شود طراحی کنید
  8. بهینه سازی عملکرد : بازسازی های UI را در حین به روزرسانی سریع جریان به حداقل برسانید

بسته colorist_ui بسیاری از این بهترین شیوه ها را برای شما پیاده سازی می کند ، اما آنها برای هرگونه اجرای LLM ، ملاحظات مهمی هستند.

بعدش چی؟

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

عیب یابی

مسائل مربوط به پردازش جریان

اگر با پردازش جریان با مشکل روبرو شدید:

  • علائم : پاسخ های جزئی ، متن از دست رفته یا خاتمه جریان ناگهانی
  • راه حل : اتصال به شبکه را بررسی کنید و از الگوهای مناسب Async/Atait در کد خود اطمینان حاصل کنید
  • تشخیص : پانل ورود به سیستم را برای پیام های خطا یا هشدارهای مربوط به پردازش جریان بررسی کنید
  • رفع : اطمینان حاصل کنید که تمام پردازش جریان از استفاده از خطای مناسب با بلوک های try / catch استفاده می کند

تماس های عملکردی از دست رفته

اگر تماس های عملکردی در جریان شناسایی نمی شوند:

  • علائم : متن ظاهر می شود اما رنگ ها به روز نمی شوند ، یا ورود به سیستم هیچ تماسی را نشان نمی دهد
  • راه حل : دستورالعمل های سریع سیستم در مورد استفاده از تماس های عملکرد را تأیید کنید
  • تشخیص : پانل ورود را بررسی کنید تا ببینید تماس های عملکردی دریافت می شود
  • رفع : سریع سیستم خود را تنظیم کنید تا صریح تر به LLM دستور دهد تا از ابزار set_color استفاده کند

رسیدگی به خطای عمومی

برای هر موضوع دیگر:

  • مرحله 1 : پانل ورود به سیستم را برای پیام های خطا بررسی کنید
  • مرحله 2 : اتصال منطق AI Firebase را تأیید کنید
  • مرحله 3 : اطمینان حاصل کنید که تمام کد تولید شده Riverpod به روز است
  • مرحله 4 : اجرای جریان را برای هرگونه اظهارات در انتظار مفقودالاتی مرور کنید

مفاهیم کلیدی آموخته شده

  • اجرای پاسخ های جریان با API Gemini برای پاسخگوتر UX
  • مدیریت وضعیت مکالمه برای رسیدگی به درستی جریان
  • پردازش متن و عملکرد در زمان واقعی هنگام ورود
  • ایجاد UI های پاسخگو که به طور تدریجی در حین پخش به روز می شوند
  • دست زدن به جریانهای همزمان با الگوهای مناسب async
  • ارائه بازخورد بصری مناسب در هنگام پاسخ های جریان

با اجرای جریان ، شما تجربه کاربر برنامه Colorist خود را به طور قابل توجهی ارتقا داده اید و یک رابط کاربری با پاسخگوتر و جذاب تر ایجاد می کنید که واقعاً مکالمه است.

8. هماهنگ سازی زمینه LLM

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

آنچه را در این مرحله پوشش خواهید داد

  • ایجاد هماهنگ سازی زمینه LLM بین UI و LLM
  • سریال سازی رویدادهای UI در متن LLM می تواند درک کند
  • به روزرسانی زمینه مکالمه بر اساس اقدامات کاربر
  • ایجاد یک تجربه منسجم در روش های مختلف تعامل
  • افزایش آگاهی زمینه LLM فراتر از پیام های چت صریح

درک هماهنگ سازی زمینه LLM

Chatbots سنتی فقط به پیام های صریح کاربر پاسخ می دهد و هنگام تعامل کاربران با برنامه دیگر با برنامه ارتباط برقرار می کند. هماهنگ سازی زمینه LLM به این محدودیت می پردازد:

چرا همگام سازی زمینه LLM اهمیت دارد

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

  1. زمینه را حفظ می کند : LLM را در مورد کلیه اقدامات کاربر مربوطه آگاه نگه می دارد
  2. انسجام ایجاد می کند : یک تجربه منسجم ایجاد می کند که LLM تعامل UI را تصدیق می کند
  3. افزایش هوش : به LLM اجازه می دهد تا به همه اقدامات کاربر پاسخ مناسب دهد
  4. تجربه کاربر را بهبود می بخشد : باعث می شود کل برنامه یکپارچه تر و پاسخگوتر شود
  5. تلاش کاربر را کاهش می دهد : نیاز کاربران را برای توضیح دستی اقدامات UI خود از بین می برد

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

سرویس چت جمینی را برای اعلان های انتخاب رنگ به روز کنید

ابتدا روشی را به GeminiChatService اضافه می کنید تا وقتی کاربر رنگی را از تاریخ انتخاب می کند ، به LLM اطلاع دهید. پرونده 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';

final conversationStateProvider = StateProvider(
 
(ref) => ConversationState.idle,
);

class GeminiChatService {
 
GeminiChatService(this.ref);
 
final Ref ref;

 
Future<void> notifyColorSelection(ColorData color) => sendMessage(  // Add from here...
   
'User selected color from history: ${json.encode(color.toLLMContextMap())}',
 
);                                                                  // To here.

 
Future<void> sendMessage(String message) async {
   
final chatSession = await ref.read(chatSessionProvider.future);
   
final conversationState = ref.read(conversationStateProvider);
   
final chatStateNotifier = ref.read(chatStateNotifierProvider.notifier);
   
final logStateNotifier = ref.read(logStateNotifierProvider.notifier);

   
if (conversationState == ConversationState.busy) {
     
logStateNotifier.logWarning(
       
"Can't send a message while a conversation is in progress",
     
);
     
throw Exception(
       
"Can't send a message while a conversation is in progress",
     
);
   
}
   
final conversationStateNotifier = ref.read(
     
conversationStateProvider.notifier,
   
);
   
conversationStateNotifier.state = ConversationState.busy;
   
chatStateNotifier.addUserMessage(message);
   
logStateNotifier.logUserText(message);
   
final llmMessage = chatStateNotifier.createLlmMessage();
   
try {
     
final responseStream = chatSession.sendMessageStream(
       
Content.text(message),
     
);
     
await for (final block in responseStream) {
       
await _processBlock(block, llmMessage.id);
     
}
   
} catch (e, st) {
     
logStateNotifier.logError(e, st: st);
     
chatStateNotifier.appendToMessage(
       
llmMessage.id,
       
"\nI'm sorry, I encountered an error processing your request. "
       
"Please try again.",
     
);
   
} finally {
     
chatStateNotifier.finalizeMessage(llmMessage.id);
     
conversationStateNotifier.state = ConversationState.idle;
   
}
 
}

 
Future<void> _processBlock(
   
GenerateContentResponse block,
   
String llmMessageId,
 
) async {
   
final chatSession = await ref.read(chatSessionProvider.future);
   
final chatStateNotifier = ref.read(chatStateNotifierProvider.notifier);
   
final logStateNotifier = ref.read(logStateNotifierProvider.notifier);
   
final blockText = block.text;

   
if (blockText != null) {
     
logStateNotifier.logLlmText(blockText);
     
chatStateNotifier.appendToMessage(llmMessageId, blockText);
   
}

   
if (block.functionCalls.isNotEmpty) {
     
final geminiTools = ref.read(geminiToolsProvider);
     
final responseStream = chatSession.sendMessageStream(
       
Content.functionResponses([
         
for (final functionCall in block.functionCalls)
           
FunctionResponse(
             
functionCall.name,
             
geminiTools.handleFunctionCall(
               
functionCall.name,
               
functionCall.args,
             
),
           
),
       
]),
     
);
     
await for (final response in responseStream) {
       
final responseText = response.text;
       
if (responseText != null) {
         
logStateNotifier.logLlmText(responseText);
         
chatStateNotifier.appendToMessage(llmMessageId, responseText);
       
}
     
}
   
}
 
}
}

@riverpod
GeminiChatService geminiChatService(Ref ref) => GeminiChatService(ref);

علاوه بر این ، روش notifyColorSelection است که:

  1. یک شیء ColorData را نشان می دهد که رنگ انتخاب شده را نشان می دهد
  2. آن را به فرمت JSON رمزگذاری می کند که می تواند در یک پیام گنجانده شود
  3. یک پیام مخصوص فرمت شده را به LLM ارسال می کند که نشان دهنده انتخاب کاربر است
  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 است که رویداد UI (انتخاب رنگ از تاریخ) را به سیستم اعلان LLM متصل می کند.

سریع سیستم را به روز کنید

حال ، شما باید سریع سیستم خود را به روز کنید تا LLM را در مورد نحوه پاسخگویی به اعلان های انتخاب رنگ آموزش دهید. assets/system_prompt.md خود را اصلاح کنید:

دارایی/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. مفهوم اعلان های انتخاب تاریخ به LLM را توضیح می دهد
  2. نمونه ای از آنچه این اعلان ها به نظر می رسد ارائه می دهد
  3. نمونه ای از پاسخ مناسب را نشان می دهد
  4. انتظارات را برای تصدیق انتخاب و اظهار نظر در مورد رنگ تعیین می کند

این به LLM کمک می کند تا نحوه پاسخ مناسب به این پیام های ویژه را درک کند.

کد Riverpod را تولید کنید

دستور ساخت Runner را برای تولید کد مورد نیاز RiverPod اجرا کنید:

dart run build_runner build --delete-conflicting-outputs

همگام سازی زمینه LLM را اجرا و آزمایش کنید

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

flutter run -d DEVICE

تصویر برنامه Colorist نشان می دهد Gemini LLM در پاسخ به انتخابی از تاریخ رنگ

آزمایش همگام سازی زمینه LLM شامل موارد زیر است:

  1. ابتدا با توصیف آنها در گپ چند رنگ ایجاد کنید
    • "یک بنفش پر جنب و جوش به من نشان دهید"
    • "من یک جنگل سبز می خواهم"
    • "به من یک قرمز روشن بده"
  2. سپس ، روی یکی از تصاویر کوچک رنگ در نوار تاریخ کلیک کنید

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

  1. رنگ انتخاب شده در صفحه اصلی ظاهر می شود
  2. یک پیام کاربر در چت ظاهر می شود که نشان دهنده انتخاب رنگ است
  3. LLM با تأیید انتخاب و اظهار نظر در مورد رنگ پاسخ می دهد
  4. کل تعامل طبیعی و منسجم است

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

چگونه همگام سازی زمینه LLM کار می کند

بیایید جزئیات فنی نحوه عملکرد این هماهنگ سازی را بررسی کنیم:

جریان داده

  1. اقدام کاربر : کاربر روی یک رنگ در نوار تاریخ کلیک می کند
  2. رویداد UI : ویجت MainScreen این انتخاب را تشخیص می دهد
  3. اعدام پاسخ به تماس : پاسخ به تماس notifyColorSelection شروع می شود
  4. ایجاد پیام : یک پیام مخصوص فرمت شده با داده های رنگ ایجاد می شود
  5. پردازش LLM : پیام به Gemini ارسال می شود ، که قالب را تشخیص می دهد
  6. پاسخ متنی : جمینی بر اساس سریع سیستم پاسخ مناسب می دهد
  7. UI Update : پاسخ در گپ ظاهر می شود و یک تجربه منسجم ایجاد می کند

داده ها

جنبه اصلی این رویکرد این است که چگونه داده های رنگ را سریال می کنید:

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

روش toLLMContextMap() (ارائه شده توسط بسته colorist_ui ) یک شیء ColorData را با خصوصیات کلیدی که LLM می تواند درک کند ، به یک نقشه تبدیل می کند. این به طور معمول شامل:

  • مقادیر RGB (قرمز ، سبز ، آبی)
  • نمایندگی کد هگز
  • هر نام یا توضیحی در ارتباط با رنگ

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

برنامه های گسترده تر از همگام سازی زمینه LLM

این الگوی اطلاع رسانی LLM در مورد رویدادهای UI دارای برنامه های بی شماری فراتر از انتخاب رنگ است:

موارد استفاده دیگر

  1. Filter Changes : هنگامی که کاربران فیلترها را به داده ها اعمال می کنند ، به LLM اطلاع دهید
  2. رویدادهای ناوبری : هنگامی که کاربران به بخش های مختلف حرکت می کنند ، LLM را به LLM اطلاع دهید
  3. تغییرات انتخاب : هنگامی که کاربران مواردی را از لیست یا شبکه انتخاب می کنند ، LLM را به روز کنید
  4. به روزرسانی های اولویت : وقتی کاربران تنظیمات یا تنظیمات برگزیده را تغییر می دهند به LLM بگویید
  5. دستکاری داده ها : هنگام اضافه کردن ، ویرایش یا حذف داده ها ، LLM را به LLM اطلاع دهید

در هر حالت ، الگوی یکسان است:

  1. رویداد UI را تشخیص دهید
  2. داده های مربوطه را سریال کنید
  3. اعلان مخصوص فرمت شده را به LLM ارسال کنید
  4. LLM را راهنمایی کنید تا از طریق سریع سیستم پاسخ مناسب دهید

بهترین شیوه ها برای هماهنگ سازی زمینه LLM

بر اساس اجرای شما ، در اینجا بهترین روشها برای همگام سازی زمینه LLM مؤثر آورده شده است:

1. قالب بندی مداوم

برای اعلان ها از یک قالب ثابت استفاده کنید تا LLM بتواند آنها را به راحتی شناسایی کند:

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

2. زمینه غنی

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

3. دستورالعمل ها را پاک کنید

دستورالعمل های صریح را در سیستم سریع در مورد نحوه رسیدگی به اعلان ها ، ایده آل با مثال ارائه دهید.

4. ادغام طبیعی

اعلان های طراحی برای جریان طبیعی در مکالمه ، نه به عنوان وقفه های فنی.

5. اعلان انتخابی

فقط LLM را در مورد اقداماتی که مربوط به مکالمه است ، به LLM اطلاع دهید. هر رویداد UI نیازی به ابلاغ نیست.

عیب یابی

مسائل مربوط به اطلاع رسانی

اگر LLM به درستی به انتخاب رنگ پاسخ نمی دهد:

  • بررسی کنید که فرمت پیام اعلان با آنچه در سیستم توضیح داده شده است مطابقت دارد
  • تأیید کنید که داده های رنگ به درستی سریال می شوند
  • اطمینان حاصل کنید که سریع سیستم دستورالعمل های روشنی برای انتخاب انتخاب دارد
  • هنگام ارسال اعلان ها به دنبال هرگونه خطایی در سرویس چت باشید

مدیریت زمینه

اگر به نظر می رسد LLM زمینه را از دست می دهد:

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

مشکلات عمومی

برای مسائل عمومی:

  • سیاهههای مربوط به خطاها یا هشدارها را بررسی کنید
  • اتصال منطق AI Firebase را تأیید کنید
  • هر نوع عدم تطابق را در پارامترهای عملکرد بررسی کنید
  • اطمینان حاصل کنید که تمام کد تولید شده Riverpod به روز است

مفاهیم کلیدی آموخته شده

  • ایجاد هماهنگ سازی زمینه LLM بین UI و LLM
  • سریال سازی رویدادهای UI در زمینه دوستانه LLM
  • هدایت رفتار LLM برای الگوهای مختلف تعامل
  • ایجاد یک تجربه منسجم در تعامل پیام و غیر پیام
  • افزایش آگاهی LLM از وضعیت برنامه گسترده تر

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

9. تبریک می گویم!

شما با موفقیت CodeLab Colorist را تکمیل کرده اید! 🎉

آنچه شما ساخته اید

شما یک برنامه کاملاً کاربردی Flutter ایجاد کرده اید که API Gemini Google را برای تفسیر توضیحات رنگ زبان طبیعی ادغام می کند. برنامه شما اکنون می تواند:

  • توضیحات زبان طبیعی مانند "غروب نارنجی" یا "آبی عمیق اقیانوس" را پردازش کنید
  • برای ترجمه هوشمندانه این توضیحات به مقادیر RGB از جمینی استفاده کنید
  • رنگهای تفسیر شده را در زمان واقعی با پاسخ های جریان نشان دهید
  • تعامل کاربر را از طریق عناصر چت و UI انجام دهید
  • آگاهی متنی را در روشهای مختلف تعامل حفظ کنید

از اینجا به کجا برویم

اکنون که به اصول اولیه ادغام جمینی با فلاتر تسلط داده اید ، در اینجا چند راه برای ادامه سفر خود وجود دارد:

برنامه Colorist خود را تقویت کنید

  • پالت های رنگی : برای تولید طرح های رنگی مکمل یا تطبیق عملکردی اضافه کنید
  • ورودی صدا : تشخیص گفتار را برای توضیحات رنگ کلامی ادغام کنید
  • مدیریت تاریخ : گزینه های نام ، سازماندهی و صادرات را اضافه کنید
  • درخواست سفارشی : ایجاد رابط برای کاربران برای سفارشی کردن درخواست های سیستم
  • تجزیه و تحلیل پیشرفته : پیگیری کدام توضیحات به بهترین وجه کار می کنند یا باعث ایجاد مشکل می شوند

ویژگی های بیشتر جمینی را کاوش کنید

  • ورودی های چند حالته : برای استخراج رنگ ها از عکس ها ، ورودی های تصویر را اضافه کنید
  • تولید محتوا : از جمینی برای تولید محتوای مرتبط با رنگ مانند توضیحات یا داستانها استفاده کنید
  • پیشرفت های فراخوانی عملکرد : ادغام ابزار پیچیده تری با چندین توابع ایجاد کنید
  • تنظیمات ایمنی : تنظیمات مختلف ایمنی و تأثیر آنها بر پاسخ ها را کشف کنید

این الگوهای را در حوزه های دیگر اعمال کنید

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

منابع

در اینجا برخی از منابع ارزشمند برای ادامه یادگیری شما آورده شده است:

اسناد رسمی

دوره و راهنما

جامعه

سری عامل های قابل مشاهده Flutter

در Expisode #59 ، کریگ لبانز و اندرو بروگدن این CodeLab را کشف کردند و قسمتهای جالب ساخت برنامه را برجسته کردند.

در قسمت شماره 60 ، دوباره به کریگ و اندرو بپیوندید زیرا آنها برنامه CodeLab را با قابلیت های جدید گسترش می دهند و با انجام LLM ها همانطور که گفته می شود ، می جنگند.

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

بازخورد

ما دوست داریم در مورد تجربه شما با این CodeLab بشنویم! لطفا ارائه بازخورد از طریق:

با تشکر از شما برای تکمیل این CodeLab ، و امیدواریم که به بررسی امکانات هیجان انگیز در تقاطع Flutter و AI بپردازید!