1. إنشاء تطبيق Flutter يستند إلى Gemini
ما ستنشئه
في هذا الدرس العملي، ستنشئ تطبيق Colorist، وهو تطبيق تفاعلي متوافق مع Flutter يتيح لك الاستفادة من إمكانات Gemini API مباشرةً في تطبيقك المتوافق مع Flutter. هل أردت يومًا أن تتيح للمستخدمين التحكّم في تطبيقك من خلال اللغة الطبيعية ولكن لم تعرف من أين تبدأ؟ يوضّح لك هذا الدرس العملي كيفية إجراء ذلك.
يتيح تطبيق Colorist للمستخدمين وصف الألوان بلغة طبيعية (مثل "برتقالي غروب الشمس" أو "أزرق المحيط العميق")، ويقوم التطبيق بما يلي:
- تعالج هذه الأوصاف باستخدام Gemini API من Google
- تفسير الأوصاف إلى قيم ألوان دقيقة بنظام RGB
- تعرض هذه السمة اللون على الشاشة في الوقت الفعلي
- تقدّم تفاصيل فنية عن اللون وسياقًا مثيرًا للاهتمام حوله
- الاحتفاظ بسجلّ للألوان التي تم إنشاؤها مؤخرًا
يتميّز التطبيق بواجهة مقسّمة إلى شاشتين، إحداهما تعرض منطقة ملونة ونظام دردشة تفاعلي، والأخرى تعرض لوحة سجلّ مفصّلة توضّح التفاعلات الأولية مع النموذج اللغوي الكبير. يتيح لك هذا السجلّ فهمًا أفضل لكيفية عمل عملية دمج نموذج لغوي كبير (LLM) في الخلفية.
أهمية ذلك لمطوّري Flutter
تُحدث النماذج اللغوية الكبيرة ثورة في طريقة تفاعل المستخدمين مع التطبيقات، ولكن دمجها بفعالية في تطبيقات الأجهزة الجوّالة وأجهزة الكمبيوتر المكتبي يطرح تحديات فريدة. يعلّمك هذا الدرس التطبيقي حول الترميز أنماطًا عملية تتجاوز مجرد طلبات واجهة برمجة التطبيقات الأولية.
رحلة التعلّم
يرشدك هذا الدرس التطبيقي حول الترميز خلال عملية إنشاء Colorist خطوة بخطوة:
- إعداد المشروع: ستبدأ بهيكل أساسي لتطبيق Flutter وحزمة
colorist_ui
- عملية تكامل أساسية مع Gemini: يمكنك ربط تطبيقك بـ "منطق الذكاء الاصطناعي" في Firebase وتنفيذ عملية التواصل مع النموذج اللغوي الكبير.
- الطلبات الفعّالة: إنشاء طلب نظام يوجّه النموذج اللغوي الكبير لفهم أوصاف الألوان
- تعريفات الدوال: تحديد الأدوات التي يمكن للنموذج اللغوي الكبير استخدامها لضبط الألوان في تطبيقك
- التعامل مع الأدوات: معالجة طلبات استدعاء الدوال من نموذج اللغة الكبير وربطها بحالة تطبيقك
- عرض الردود تدريجيًا: تحسين تجربة المستخدم من خلال عرض ردود نماذج اللغات الكبيرة تدريجيًا في الوقت الفعلي
- مزامنة سياق النموذج اللغوي الكبير: يمكنك إنشاء تجربة متماسكة من خلال إعلام النموذج اللغوي الكبير بإجراءات المستخدم.
المُعطيات
- ضبط Firebase AI Logic لتطبيقات Flutter
- صياغة طلبات نظام فعّالة لتوجيه سلوك النماذج اللغوية الكبيرة
- تنفيذ تعريفات الدوال التي تربط بين اللغة الطبيعية وميزات التطبيق
- معالجة الردود المتدفّقة لتوفير تجربة مستخدم متجاوبة
- مزامنة الحالة بين أحداث واجهة المستخدم والنموذج اللغوي الكبير
- إدارة حالة محادثة نموذج اللغة الكبير باستخدام Riverpod
- التعامل مع الأخطاء بشكلٍ آمن في التطبيقات المستندة إلى نماذج اللغات الكبيرة
معاينة الرمز البرمجي: نظرة سريعة على ما ستنفّذه
في ما يلي لمحة عن تعريف الدالة الذي ستنشئه للسماح للنموذج اللغوي الكبير بتحديد الألوان في تطبيقك:
FunctionDeclaration get setColorFuncDecl => FunctionDeclaration(
'set_color',
'Set the color of the display square based on red, green, and blue values.',
parameters: {
'red': Schema.number(description: 'Red component value (0.0 - 1.0)'),
'green': Schema.number(description: 'Green component value (0.0 - 1.0)'),
'blue': Schema.number(description: 'Blue component value (0.0 - 1.0)'),
},
);
فيديو يقدّم نظرة عامة على هذا الدرس التطبيقي حول الترميز
يمكنك مشاهدة "كريغ لابينز" و"أندرو بروغدون" وهما يناقشان هذا الدرس العملي في الحلقة 59 من برنامج Observable Flutter:
المتطلبات الأساسية
للاستفادة إلى أقصى حدّ من هذا الدرس التطبيقي حول الترميز، يجب أن يكون لديك:
- تجربة تطوير تطبيقات Flutter: الإلمام بأساسيات Flutter وبنية Dart
- معرفة البرمجة غير المتزامنة: فهم Futures وasync/await وstreams
- حساب Firebase: ستحتاج إلى حساب Google لإعداد Firebase
لنبدأ بإنشاء أول تطبيق Flutter يستند إلى نموذج لغوي كبير.
2. إعداد المشروع وخدمة المحاكاة
في هذه الخطوة الأولى، عليك إعداد بنية المشروع وتنفيذ خدمة echo التي سيتم استبدالها لاحقًا بعملية دمج Gemini API. يؤدي ذلك إلى إنشاء بنية التطبيق والتأكّد من عمل واجهة المستخدم بشكل صحيح قبل إضافة تعقيد طلبات النموذج اللغوي الكبير.
ما ستتعرّف عليه في هذه الخطوة
- إعداد مشروع Flutter مع التبعيات المطلوبة
- العمل مع حزمة
colorist_ui
لمكوّنات واجهة المستخدم - تنفيذ خدمة رسائل الصدى وربطها بواجهة المستخدم
إنشاء مشروع Flutter جديد
ابدأ بإنشاء مشروع Flutter جديد باستخدام الأمر التالي:
flutter create -e colorist --platforms=android,ios,macos,web,windows
تشير العلامة -e
إلى أنّك تريد مشروعًا فارغًا بدون تطبيق counter
التلقائي. تم تصميم التطبيق ليعمل على أجهزة الكمبيوتر والأجهزة الجوّالة والويب. يُرجى العِلم أنّ flutterfire
لا يتوافق مع نظام التشغيل Linux في الوقت الحالي.
إضافة عناصر تابعة
انتقِل إلى دليل مشروعك وأضِف التبعيات المطلوبة:
cd colorist
flutter pub add colorist_ui flutter_riverpod riverpod_annotation
flutter pub add --dev build_runner riverpod_generator riverpod_lint json_serializable
سيؤدي ذلك إلى إضافة حِزم المفاتيح التالية:
-
colorist_ui
: حزمة مخصّصة توفّر مكوّنات واجهة المستخدم لتطبيق Colorist -
flutter_riverpod
وriverpod_annotation
: لإدارة الحالة -
logging
: لتسجيل البيانات المنظَّمة - التبعيات التطويرية لإنشاء الرموز البرمجية والتدقيق فيها
سيبدو pubspec.yaml
على النحو التالي:
pubspec.yaml
name: colorist
description: "A new Flutter project."
publish_to: 'none'
version: 0.1.0
environment:
sdk: ^3.9.2
dependencies:
flutter:
sdk: flutter
colorist_ui: ^0.3.0
flutter_riverpod: ^3.0.0
riverpod_annotation: ^3.0.0
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^6.0.0
build_runner: ^2.7.1
riverpod_generator: ^3.0.0
riverpod_lint: ^3.0.0
json_serializable: ^6.11.1
flutter:
uses-material-design: true
تنفيذ ملف main.dart
استبدِل محتوى lib/main.dart
بما يلي:
lib/main.dart
import 'package:colorist_ui/colorist_ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() async {
runApp(ProviderScope(child: MainApp()));
}
class MainApp extends ConsumerWidget {
const MainApp({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
return MaterialApp(
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
),
home: MainScreen(
sendMessage: (message) {
sendMessage(message, ref);
},
),
);
}
// A fake LLM that just echoes back what it receives.
void sendMessage(String message, WidgetRef ref) {
final chatStateNotifier = ref.read(chatStateProvider.notifier);
final logStateNotifier = ref.read(logStateProvider.notifier);
chatStateNotifier.addUserMessage(message);
logStateNotifier.logUserText(message);
chatStateNotifier.addLlmMessage(message, MessageState.complete);
logStateNotifier.logLlmText(message);
}
}
يؤدي ذلك إلى إعداد تطبيق Flutter ينفّذ خدمة صدى تحاكي سلوك نموذج لغوي كبير من خلال عرض رسالة المستخدم.
فهم البنية
لنتعرّف على بنية تطبيق "colorist
":
حزمة colorist_ui
توفر حزمة colorist_ui
مكوّنات واجهة مستخدم مُنشأة مسبقًا وأدوات لإدارة الحالة:
- MainScreen: مكوّن واجهة المستخدِم الرئيسية الذي يعرض ما يلي:
- تنسيق تقسيم الشاشة على الكمبيوتر المكتبي (مساحة التفاعل ولوحة السجلّ)
- واجهة مقسّمة إلى علامات تبويب على جهاز جوّال
- عرض الألوان وواجهة المحادثة والصور المصغّرة لسجلّ المحادثات
- إدارة الحالة: يستخدم التطبيق العديد من أدوات إرسال إشعارات الحالة:
- ChatStateNotifier: يدير رسائل المحادثة
- ColorStateNotifier: لإدارة اللون الحالي والسجلّ
- LogStateNotifier: لإدارة إدخالات السجلّات لتصحيح الأخطاء
- التعامل مع الرسائل: يستخدم التطبيق نموذج رسائل يتضمّن حالات مختلفة:
- رسائل المستخدم: يتم إدخالها من قِبل المستخدم
- رسائل النموذج اللغوي الكبير: يتم إنشاؤها بواسطة النموذج اللغوي الكبير (أو خدمة المحاكاة الصوتية حاليًا)
- MessageState: يتتبّع ما إذا كانت رسائل النموذج اللغوي الكبير مكتملة أو لا تزال قيد البث
بنية التطبيق
يتبع التطبيق البنية التالية:
- طبقة واجهة المستخدم: مقدَّمة من حزمة
colorist_ui
- إدارة الحالة: تستخدم Riverpod لإدارة الحالة التفاعلية
- طبقة الخدمة: تحتوي حاليًا على خدمة الصدى البسيطة، وسيتم استبدالها بخدمة Gemini Chat
- دمج النموذج اللغوي الكبير: سيتم إضافته في خطوات لاحقة
يتيح لك هذا الفصل التركيز على تنفيذ عملية الدمج مع النموذج اللغوي الكبير، بينما يتم الاهتمام بمكوّنات واجهة المستخدم.
تشغيل التطبيق
شغِّل التطبيق باستخدام الأمر التالي:
flutter run -d DEVICE
استبدِل DEVICE
بالجهاز المستهدَف، مثل macos
أو windows
أو chrome
أو رقم تعريف الجهاز.
من المفترض أن يظهر لك الآن تطبيق Colorist مع:
- مساحة عرض الألوان مع لون تلقائي
- واجهة دردشة يمكنك كتابة الرسائل فيها
- لوحة سجلّ تعرض تفاعلات المحادثة
جرِّب كتابة رسالة مثل "أريد لونًا أزرق داكنًا" واضغط على "إرسال". ستكرّر خدمة الصدى رسالتك ببساطة. في الخطوات اللاحقة، ستستبدل هذا الرمز بتفسير الألوان الفعلي باستخدام "منطق الذكاء الاصطناعي" في Firebase.
ما هي الخطوات التالية؟
في الخطوة التالية، عليك إعداد Firebase وتنفيذ عملية دمج أساسية لواجهة Gemini API من أجل استبدال خدمة الردّ بخدمة المحادثة من Gemini. سيسمح ذلك للتطبيق بتفسير أوصاف الألوان وتقديم ردود ذكية.
تحديد المشاكل وحلّها
مشاكل في حزمة واجهة المستخدم
في حال مواجهة مشاكل في حزمة colorist_ui
:
- تأكَّد من استخدام أحدث إصدار
- التأكّد من إضافة التبعية بشكل صحيح
- التحقّق من توفّر أي إصدارات حِزم متعارضة
أخطاء الإنشاء
إذا ظهرت لك أخطاء في الإصدار، اتّبِع الخطوات التالية:
- تأكَّد من تثبيت أحدث إصدار من حزمة تطوير البرامج (SDK) لقناة Flutter الثابتة
- تشغيل
flutter clean
ثمflutter pub get
- التحقّق من ناتج وحدة التحكّم بحثًا عن رسائل خطأ معيّنة
المفاهيم الرئيسية التي تم تعلّمها
- إعداد مشروع Flutter مع التبعيات اللازمة
- فهم بنية التطبيق ومسؤوليات المكوّنات
- تنفيذ خدمة بسيطة تحاكي سلوك نموذج لغوي كبير
- ربط الخدمة بمكوّنات واجهة المستخدم
- استخدام Riverpod لإدارة الحالة
3- عملية الدمج الأساسية لـ Gemini Chat
في هذه الخطوة، ستستبدل خدمة الردّ من الخطوة السابقة بعملية دمج Gemini API باستخدام "منطق الذكاء الاصطناعي" في Firebase. ستضبط إعدادات Firebase، وتعدّ إعدادات موفّري الخدمات اللازمين، وتنفّذ خدمة محادثة أساسية تتواصل مع Gemini API.
ما ستتعرّف عليه في هذه الخطوة
- إعداد Firebase في تطبيق Flutter
- ضبط Firebase AI Logic للوصول إلى Gemini
- إنشاء موفّري Riverpod لخدمات Firebase وGemini
- تنفيذ خدمة محادثة أساسية باستخدام Gemini API
- التعامل مع الردود غير المتزامنة من واجهة برمجة التطبيقات وحالات الخطأ
إعداد Firebase
أولاً، عليك إعداد Firebase لمشروع Flutter. يتضمّن ذلك إنشاء مشروع على Firebase وإضافة تطبيقك إليه وإعدادات "منطق الذكاء الاصطناعي" اللازمة في Firebase.
إنشاء مشروع Firebase
- انتقِل إلى وحدة تحكّم Firebase وسجِّل الدخول باستخدام حسابك على Google.
- انقر على إنشاء مشروع Firebase أو اختَر مشروعًا حاليًا.
- اتّبِع معالج الإعداد لإنشاء مشروعك.
إعداد "منطق الذكاء الاصطناعي" من Firebase في مشروعك على Firebase
- في وحدة تحكّم Firebase، انتقِل إلى مشروعك.
- في الشريط الجانبي الأيمن، انقر على الذكاء الاصطناعي.
- في القائمة المنسدلة الخاصة بالذكاء الاصطناعي، اختَر منطق الذكاء الاصطناعي.
- في بطاقة "منطق الذكاء الاصطناعي في Firebase"، انقر على البدء.
- اتّبِع التعليمات لتفعيل Gemini Developer API لمشروعك.
تثبيت FlutterFire CLI
تسهّل أداة FlutterFire CLI عملية إعداد Firebase في تطبيقات Flutter:
dart pub global activate flutterfire_cli
إضافة Firebase إلى تطبيق Flutter
- أضِف حزمتَي Firebase الأساسية وFirebase AI Logic إلى مشروعك:
flutter pub add firebase_core firebase_ai
- نفِّذ أمر إعداد FlutterFire:
flutterfire configure
سيؤدي هذا الأمر إلى ما يلي:
- يُطلب منك اختيار مشروع Firebase الذي أنشأته للتو
- تسجيل تطبيقات Flutter في Firebase
- إنشاء ملف
firebase_options.dart
يتضمّن إعدادات مشروعك
سيرصد الأمر تلقائيًا الأنظمة الأساسية التي اخترتها (iOS وAndroid وmacOS وWindows والويب) ويضبطها بشكل مناسب.
الإعدادات الخاصة بالنظام الأساسي
تتطلّب Firebase إصدارات دنيا أعلى من الإصدارات التلقائية في Flutter. ويتطلّب أيضًا إذنًا بالوصول إلى الشبكة للتواصل مع خوادم Firebase AI Logic.
ضبط أذونات macOS
في نظام التشغيل macOS، عليك تفعيل إذن الوصول إلى الشبكة في أذونات تطبيقك:
- افتح
macos/Runner/DebugProfile.entitlements
وأضِف:
macos/Runner/DebugProfile.entitlements
<key>com.apple.security.network.client</key>
<true/>
- افتح أيضًا
macos/Runner/Release.entitlements
وأضِف الإدخال نفسه.
ضبط إعدادات iOS
بالنسبة إلى نظام التشغيل iOS، عدِّل الحد الأدنى للإصدار في أعلى ios/Podfile
:
ios/Podfile
# Firebase requires at least iOS 15.0
platform :ios, '15.0'
إنشاء موفّري نماذج 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: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.
-
firebaseAppProvider
: تهيئة Firebase باستخدام إعدادات مشروعك -
geminiModelProvider
: لإنشاء مثيل لنموذج Gemini التوليدي -
chatSessionProvider
: إنشاء جلسة محادثة مع نموذج Gemini والحفاظ عليها
تضمن التعليق التوضيحي keepAlive: true
على جلسة المحادثة استمرارها طوال دورة حياة التطبيق، ما يحافظ على سياق المحادثة.
تنفيذ خدمة Gemini Chat
أنشئ ملفًا جديدًا lib/services/gemini_chat_service.dart
لتنفيذ خدمة المحادثة:
lib/services/gemini_chat_service.dart
import 'dart:async';
import 'package:colorist_ui/colorist_ui.dart';
import 'package:firebase_ai/firebase_ai.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import '../providers/gemini.dart';
part 'gemini_chat_service.g.dart';
class GeminiChatService {
GeminiChatService(this.ref);
final Ref ref;
Future<void> sendMessage(String message) async {
final chatSession = await ref.read(chatSessionProvider.future);
final chatStateNotifier = ref.read(chatStateProvider.notifier);
final logStateNotifier = ref.read(logStateProvider.notifier);
chatStateNotifier.addUserMessage(message);
logStateNotifier.logUserText(message);
final llmMessage = chatStateNotifier.createLlmMessage();
try {
final response = await chatSession.sendMessage(Content.text(message));
final responseText = response.text;
if (responseText != null) {
logStateNotifier.logLlmText(responseText);
chatStateNotifier.appendToMessage(llmMessage.id, responseText);
}
} catch (e, st) {
logStateNotifier.logError(e, st: st);
chatStateNotifier.appendToMessage(
llmMessage.id,
"\nI'm sorry, I encountered an error processing your request. "
"Please try again.",
);
} finally {
chatStateNotifier.finalizeMessage(llmMessage.id);
}
}
}
@riverpod
GeminiChatService geminiChatService(Ref ref) => GeminiChatService(ref);
تتيح لك هذه الخدمة:
- يقبل رسائل المستخدمين ويرسلها إلى Gemini API
- تعديل واجهة المحادثة باستخدام الردود من النموذج
- تسجيل جميع عمليات التواصل لتسهيل فهم سير عمل نموذج اللغة الكبير الحقيقي
- التعامل مع الأخطاء من خلال تقديم ملاحظات مناسبة للمستخدم
ملاحظة: ستكون نافذة السجلّ مطابقة تقريبًا لنافذة المحادثة في هذه المرحلة. سيصبح السجلّ أكثر إثارة للاهتمام عند تقديم عمليات استدعاء الدوال ثم الردود المتدفقة.
إنشاء رمز Riverpod
نفِّذ أمر build runner لإنشاء رمز Riverpod اللازم:
dart run build_runner build --delete-conflicting-outputs
سيؤدي ذلك إلى إنشاء ملفات .g.dart
التي يحتاجها Riverpod ليعمل.
تعديل ملف main.dart
تعديل ملف lib/main.dart
لاستخدام خدمة Gemini Chat الجديدة:
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),
),
);
}
}
في ما يلي التغييرات الرئيسية في هذا التحديث:
- استبدال خدمة الردّ الصوتي بخدمة محادثة مستندة إلى Gemini API
- إضافة شاشات التحميل والخطأ باستخدام نمط
AsyncValue
في Riverpod مع طريقةwhen
- ربط واجهة المستخدم بخدمة المحادثة الجديدة من خلال
sendMessage
تشغيل التطبيق
شغِّل التطبيق باستخدام الأمر التالي:
flutter run -d DEVICE
استبدِل DEVICE
بالجهاز المستهدَف، مثل macos
أو windows
أو chrome
أو رقم تعريف الجهاز.
عند كتابة رسالة، سيتم إرسالها إلى Gemini API، وستتلقّى ردًا من النموذج اللغوي الكبير بدلاً من سماع صدى صوتك. ستعرض لوحة السجلّ التفاعلات مع واجهة برمجة التطبيقات.
فهم التواصل مع النماذج اللغوية الكبيرة
لنتعرّف على ما يحدث عند التواصل مع Gemini API:
مسار التواصل
- إدخال المستخدم: يُدخل المستخدم نصًا في واجهة المحادثة
- تنسيق الطلب: ينسّق التطبيق النص كعنصر
Content
لواجهة Gemini API - التواصل مع واجهة برمجة التطبيقات: يتم إرسال النص إلى Gemini API من خلال Firebase AI Logic
- معالجة النموذج اللغوي الكبير: يعالِج نموذج Gemini النص وينشئ ردًا
- التعامل مع الردود: يتلقّى التطبيق الردّ ويعدّل واجهة المستخدم
- التسجيل: يتم تسجيل جميع عمليات التواصل لضمان الشفافية
جلسات المحادثة وسياق المحادثة
تحتفظ جلسة الدردشة مع Gemini بالسياق بين الرسائل، ما يتيح إجراء تفاعلات حوارية. وهذا يعني أنّ النموذج اللغوي الكبير "يتذكّر" المراسلات السابقة في الجلسة الحالية، ما يتيح إجراء محادثات أكثر اتساقًا.
يضمن التعليق التوضيحي keepAlive: true
على موفّر جلسة المحادثة استمرار هذا السياق طوال دورة حياة التطبيق. هذا السياق الثابت ضروري للحفاظ على سير المحادثة بشكل طبيعي مع النموذج اللغوي الكبير.
ما هي الخطوات التالية؟
في هذه المرحلة، يمكنك أن تسأل Gemini API أي شيء، إذ لا توجد قيود على ما سيردّ عليه. على سبيل المثال، يمكنك أن تطلب منه تقديم ملخّص عن "حروب الوردتين"، وهو موضوع لا صلة له بغرض تطبيق الألوان.
في الخطوة التالية، ستنشئ طلب نظام لتوجيه Gemini في تفسير أوصاف الألوان بفعالية أكبر. سيوضّح لك هذا كيفية تخصيص سلوك نموذج لغوي كبير (LLM) لتلبية احتياجات خاصة بالتطبيق والتركيز على إمكاناته في نطاق تطبيقك.
تحديد المشاكل وحلّها
مشاكل في إعدادات Firebase
في حال مواجهة أخطاء عند إعداد Firebase:
- التأكّد من إنشاء ملف
firebase_options.dart
بشكل صحيح - تأكَّد من أنّك قد رقّيت إلى خطة Blaze للوصول إلى Firebase AI Logic
أخطاء الوصول إلى واجهة برمجة التطبيقات
في حال تلقّي أخطاء عند الوصول إلى Gemini API، اتّبِع الخطوات التالية:
- التأكّد من إعداد الفوترة بشكلٍ صحيح في مشروع Firebase
- تأكَّد من تفعيل "منطق الذكاء الاصطناعي من Firebase" وCloud AI API في مشروعك على Firebase
- التحقّق من اتصال الشبكة وإعدادات جدار الحماية
- تأكَّد من أنّ اسم النموذج (
gemini-2.0-flash
) صحيح ومتوفّر
مشاكل سياق المحادثة
إذا لاحظت أنّ Gemini لا يتذكّر سياق الحديث السابق من المحادثة:
- تأكَّد من أنّ الدالة
chatSession
مزوّدة بالتعليق التوضيحي@Riverpod(keepAlive: true)
- تأكَّد من إعادة استخدام جلسة المحادثة نفسها لجميع عمليات تبادل الرسائل
- التأكّد من بدء جلسة المحادثة بشكل صحيح قبل إرسال الرسائل
المشاكل المتعلّقة بمنصّة معيّنة
بالنسبة إلى المشاكل الخاصة بمنصة معيّنة:
- iOS/macOS: التأكّد من ضبط الأذونات المناسبة وإعداد الحد الأدنى للإصدارات
- Android: التأكّد من ضبط الحد الأدنى لإصدار حزمة تطوير البرامج (SDK) بشكلٍ صحيح
- الاطّلاع على رسائل الخطأ الخاصة بالمنصة في وحدة التحكّم
المفاهيم الرئيسية التي تم تعلّمها
- إعداد Firebase في تطبيق Flutter
- ضبط Firebase AI Logic للوصول إلى Gemini
- إنشاء موفّري Riverpod للخدمات غير المتزامنة
- تنفيذ خدمة دردشة تتواصل مع نموذج لغوي كبير
- التعامل مع حالات واجهة برمجة التطبيقات غير المتزامنة (التحميل، الخطأ، البيانات)
- فهم تدفّق التواصل والجلسات في النماذج اللغوية الكبيرة
4. كتابة طلبات فعّالة للحصول على أوصاف الألوان
في هذه الخطوة، ستنشئ طلب نظام وتنفّذه لتوجيه Gemini في تفسير أوصاف الألوان. طلبات النظام هي طريقة فعّالة لتخصيص سلوك النماذج اللغوية الكبيرة لمهام معيّنة بدون تغيير الرمز البرمجي.
ما ستتعرّف عليه في هذه الخطوة
- فهم طلبات النظام وأهميتها في تطبيقات النماذج اللغوية الكبيرة
- صياغة طلبات فعّالة لمهام خاصة بمجال معيّن
- تحميل طلبات النظام واستخدامها في تطبيق Flutter
- توجيه نموذج لغوي كبير لتقديم ردود منسَّقة باستمرار
- اختبار كيفية تأثير طلبات النظام على سلوك النموذج اللغوي الكبير
التعرّف على طلبات النظام
قبل التعرّف على كيفية تنفيذها، دعونا نفهم ما هي طلبات النظام وسبب أهميتها:
ما هي طلبات النظام؟
طلب النظام هو نوع خاص من التعليمات المقدَّمة إلى نموذج لغوي كبير يحدّد السياق وإرشادات السلوك وتوقّعات الردود. على عكس رسائل المستخدمين، فإنّ طلبات النظام:
- تحديد دور النموذج اللغوي الكبير وشخصيته
- تحديد المعرفة أو القدرات المتخصصة
- تقديم تعليمات التنسيق
- ضبط قيود على الردود
- وصف كيفية التعامل مع سيناريوهات مختلفة
يمكنك اعتبار طلب النظام بمثابة "الوصف الوظيفي" للنموذج اللغوي الكبير، فهو يحدد طريقة تفاعل النموذج خلال المحادثة.
أهمية طلبات النظام
تُعدّ تعليمات النظام ضرورية لإنشاء تفاعلات متّسقة ومفيدة مع النماذج اللغوية الكبيرة (LLM) لأنّها:
- ضمان الاتّساق: توجيه النموذج لتقديم الردود بتنسيق متّسق
- تحسين مدى الصلة بالموضوع: ركِّز النموذج على مجالك المحدّد (في حالتك، الألوان).
- وضع حدود: تحديد ما يجب أن يفعله النموذج وما يجب ألا يفعله
- تحسين تجربة المستخدم: إنشاء نمط تفاعل أكثر طبيعيةً وفائدةً
- تقليل المعالجة اللاحقة: الحصول على ردود بتنسيقات يسهل تحليلها أو عرضها
بالنسبة إلى تطبيق Colorist، عليك أن تطلب من النموذج اللغوي الكبير تفسير أوصاف الألوان باستمرار وتقديم قيم أحمر أخضر أزرق بتنسيق معيّن.
إنشاء مادة عرض لطلب من النظام
أولاً، عليك إنشاء ملف طلب نظام سيتم تحميله في وقت التشغيل. يتيح لك هذا الأسلوب تعديل الطلب بدون إعادة تجميع تطبيقك.
أنشئ ملفًا جديدًا assets/system_prompt.md
يتضمّن المحتوى التالي:
assets/system_prompt.md
# Colorist System Prompt
You are a color expert assistant integrated into a desktop app called Colorist. Your job is to interpret natural language color descriptions and provide the appropriate RGB values that best represent that description.
## Your Capabilities
You are knowledgeable about colors, color theory, and how to translate natural language descriptions into specific RGB values. When users describe a color, you should:
1. Analyze their description to understand the color they are trying to convey
2. Determine the appropriate RGB values (values should be between 0.0 and 1.0)
3. Respond with a conversational explanation and explicitly state the RGB values
## How to Respond to User Inputs
When users describe a color:
1. First, acknowledge their color description with a brief, friendly response
2. Interpret what RGB values would best represent that color description
3. Always include the RGB values clearly in your response, formatted as: `RGB: (red=X.X, green=X.X, blue=X.X)`
4. Provide a brief explanation of your interpretation
Example:
User: "I want a sunset orange"
You: "Sunset orange is a warm, vibrant color that captures the golden-red hues of the setting sun. It combines a strong red component with moderate orange tones.
RGB: (red=1.0, green=0.5, blue=0.25)
I've selected values with high red, moderate green, and low blue to capture that beautiful sunset glow. This creates a warm orange with a slightly reddish tint, reminiscent of the sun low on the horizon."
## When Descriptions are Unclear
If a color description is ambiguous or unclear, please ask the user clarifying questions, one at a time.
## Important Guidelines
- Always keep RGB values between 0.0 and 1.0
- Always format RGB values as: `RGB: (red=X.X, green=X.X, blue=X.X)` for easy parsing
- Provide thoughtful, knowledgeable responses about colors
- When possible, include color psychology, associations, or interesting facts about colors
- Be conversational and engaging in your responses
- Focus on being helpful and accurate with your color interpretations
فهم بنية طلب النظام
دعونا نحلّل ما يفعله هذا الطلب:
- تعريف الدور: يحدّد النموذج اللغوي الكبير على أنّه "مساعد خبير في الألوان"
- شرح المهمة: تحدّد المهمة الأساسية على أنّها ترجمة أوصاف الألوان إلى قيم RGB
- تنسيق الردّ: يحدّد بدقة كيفية تنسيق قيم الأحمر والأخضر والأزرق لضمان الاتساق
- مثال على التبادل: يقدّم مثالاً ملموسًا على نمط التفاعل المتوقّع
- التعامل مع الحالات الهامشية: يوضّح كيفية التعامل مع الأوصاف غير الواضحة
- القيود والإرشادات: تضبط الحدود، مثل الحفاظ على قيم النموذج اللوني أحمر أخضر أزرق (RGB) بين 0.0 و1.0
يضمن هذا الأسلوب المنظَّم أن تكون ردود النموذج اللغوي الكبير متسقة وغنية بالمعلومات ومنسَّقة بطريقة يسهل تحليلها إذا أردت استخراج قيم 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: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: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
نفِّذ أمر أداة إنشاء التطبيقات لإنشاء رمز Riverpod المطلوب:
dart run build_runner build --delete-conflicting-outputs
تشغيل التطبيق واختباره
الآن، شغِّل تطبيقك:
flutter run -d DEVICE
جرِّب اختبارها باستخدام أوصاف ألوان مختلفة:
- "أريد لونًا أزرق سماويًا"
- "أريد لونًا أخضر داكنًا"
- "أريد صورة لغروب الشمس بلون برتقالي ساطع"
- "أريد لون الخزامى الطازج"
- "أريد لونًا أزرق داكنًا مثل لون المحيط"
ستلاحظ أنّ Gemini يردّ الآن بشروحات حوارية حول الألوان بالإضافة إلى قيم الأحمر والأخضر والأزرق المنسّقة بشكل متّسق. لقد وجّه طلب النظام النموذج اللغوي الكبير بشكل فعّال لتقديم نوع الردود التي تحتاج إليها.
جرِّب أيضًا أن تطلب منه محتوًى خارج سياق الألوان. على سبيل المثال، الأسباب الرئيسية لحروب الوردتين يجب أن تلاحظ اختلافًا عن الخطوة السابقة.
أهمية هندسة الطلبات للمهام المتخصصة
تتطلّب طلبات النظام مهارة وفهمًا. وهي جزء أساسي من عملية دمج النماذج اللغوية الكبيرة، ويمكن أن تؤثّر بشكل كبير في مدى فائدة النموذج لتطبيقك المحدّد. ما فعلته هنا هو شكل من أشكال هندسة الطلبات، أي تخصيص التعليمات لجعل النموذج يتصرف بطرق تناسب احتياجات تطبيقك.
تتضمّن هندسة الطلبات الفعّالة ما يلي:
- تعريف واضح للدور: تحديد الغرض من استخدام النموذج اللغوي الكبير
- تعليمات صريحة: توضيح الطريقة التي يجب أن يردّ بها النموذج اللغوي الكبير
- أمثلة ملموسة: عرض أمثلة على الردود الجيدة بدلاً من مجرد إخبار المستخدمين بها
- التعامل مع الحالات الهامشية: توجيه النموذج اللغوي الكبير بشأن كيفية التعامل مع السيناريوهات الغامضة
- مواصفات التنسيق: التأكّد من أنّ الردود منظَّمة بطريقة موحَّدة وقابلة للاستخدام
يحوّل طلب النظام الذي أنشأته الإمكانات العامة في Gemini إلى مساعد متخصص في تفسير الألوان، ويقدّم ردودًا منسّقة خصيصًا لتلبية احتياجات تطبيقك. هذا نمط قوي يمكنك تطبيقه على العديد من المجالات والمهام المختلفة.
ما هي الخطوات التالية؟
في الخطوة التالية، ستستند إلى هذه الأساسيات من خلال إضافة تعريفات الدوال التي تسمح للنموذج اللغوي الكبير (LLM) ليس فقط باقتراح قيم RGB، بل أيضًا باستدعاء الدوال في تطبيقك لضبط اللون مباشرةً. يوضّح هذا كيف يمكن لنماذج اللغات الكبيرة أن تسدّ الفجوة بين اللغة الطبيعية وميزات التطبيق الملموسة.
تحديد المشاكل وحلّها
مشاكل تحميل مواد العرض
في حال مواجهة أخطاء عند تحميل طلب النظام، يُرجى اتّباع الخطوات التالية:
- تأكَّد من أنّ
pubspec.yaml
يسرد دليل مواد العرض بشكل صحيح - تأكَّد من أنّ المسار في
rootBundle.loadString()
يتطابق مع موقع ملفك - تشغيل
flutter clean
متبوعًا بـflutter pub get
لإعادة تحميل حزمة مواد العرض
الردود غير المتسقة
إذا لم تلتزم النماذج اللغوية الكبيرة بشكلٍ متّسق بتعليمات التنسيق، اتّبِع الخطوات التالية:
- محاولة توضيح متطلبات التنسيق بشكل أكبر في طلب النظام
- إضافة المزيد من الأمثلة لتوضيح النمط المتوقّع
- تأكَّد من أنّ التنسيق الذي تطلبه مناسب للنموذج
تقييد معدّل الزيارات على واجهة برمجة التطبيقات
في حال مواجهة أخطاء متعلّقة بالحدّ الأقصى لعدد الطلبات:
- يُرجى العِلم أنّ خدمة Firebase AI Logic لها حدود استخدام.
- ننصحك بتنفيذ منطق إعادة المحاولة باستخدام خوارزمية الرقود الأسي الثنائي
- التحقّق من عدم وجود أي مشاكل متعلّقة بالحصة في "وحدة تحكّم Firebase"
المفاهيم الرئيسية التي تم تعلّمها
- فهم دور وأهمية طلبات النظام في تطبيقات النماذج اللغوية الكبيرة
- صياغة طلبات فعّالة تتضمّن تعليمات وأمثلة وقيودًا واضحة
- تحميل طلبات النظام واستخدامها في تطبيق Flutter
- توجيه سلوك النماذج اللغوية الكبيرة لتنفيذ مهام خاصة بمجال معيّن
- استخدام هندسة الطلبات لتشكيل ردود النماذج اللغوية الكبيرة
توضّح هذه الخطوة كيف يمكنك تخصيص سلوك النموذج اللغوي الكبير بشكل كبير بدون تغيير الرمز، وذلك ببساطة من خلال تقديم تعليمات واضحة في طلب النظام.
5- بيانات الدوال لأدوات النموذج اللغوي الكبير
في هذه الخطوة، ستبدأ عملية إتاحة إمكانية اتّخاذ Gemini إجراءات في تطبيقك من خلال تنفيذ تعريفات الدوال. تتيح هذه الميزة الفعّالة للنموذج اللغوي الكبير (LLM) اقتراح قيم RGB وضبطها في واجهة مستخدم تطبيقك من خلال طلبات أدوات متخصّصة. ومع ذلك، سيتطلّب ذلك تنفيذ الخطوة التالية للاطّلاع على طلبات النموذج اللغوي الكبير في تطبيق Flutter.
ما ستتعرّف عليه في هذه الخطوة
- التعرّف على ميزة "استدعاء الدوال" في النماذج اللغوية الكبيرة ومزاياها لتطبيقات Flutter
- تحديد تعريفات الدوال المستندة إلى المخطط لـ Gemini
- دمج تعريفات الدوال مع نموذج Gemini
- تعديل طلب النظام للاستفادة من إمكانات الأدوات
التعرّف على ميزة "استدعاء الدوال"
قبل تنفيذ تعريفات الدوال، دعونا نفهم ماهيتها وسبب أهميتها:
ما هي ميزة "استدعاء الدوال"؟
تتيح ميزة "استدعاء الدوال" (المعروفة أحيانًا باسم "استخدام الأدوات") للنماذج اللغوية الكبيرة إجراء ما يلي:
- التعرّف على الحالات التي يمكن فيها الاستفادة من استدعاء وظيفة معيّنة عند تلقّي طلب من المستخدم
- إنشاء عنصر JSON منظَّم يتضمّن المَعلمات اللازمة لهذه الدالة
- اسمح لتطبيقك بتنفيذ الدالة مع هذه المَعلمات
- تلقّي نتيجة الدالة ودمجها في الردّ
بدلاً من أن يكتفي النموذج اللغوي الكبير بوصف الإجراءات المطلوب اتّخاذها، تتيح ميزة "استدعاء الدوال البرمجية" للنموذج اللغوي الكبير تنفيذ إجراءات ملموسة في تطبيقك.
أهمية ميزة "استدعاء الدوال" لتطبيقات Flutter
تتيح ميزة "استدعاء الدوال" إنشاء جسر قوي بين اللغة الطبيعية وميزات التطبيق:
- الإجراء المباشر: يمكن للمستخدمين وصف ما يريدونه بلغة طبيعية، ويردّ التطبيق بإجراءات ملموسة
- الناتج المنظَّم: ينتج النموذج اللغوي الكبير بيانات منظَّمة وواضحة بدلاً من نص يحتاج إلى تحليل.
- العمليات المعقّدة: تتيح للنموذج اللغوي الكبير الوصول إلى البيانات الخارجية أو إجراء العمليات الحسابية أو تعديل حالة التطبيق
- تجربة مستخدم أفضل: إنشاء تكامل سلس بين المحادثة والوظائف
في تطبيق Colorist، تتيح ميزة "استدعاء الدوال" للمستخدمين قول "أريد لونًا أخضر غامقًا"، وسيتم تعديل واجهة المستخدم على الفور بهذا اللون، بدون الحاجة إلى تحليل قيم RGB من النص.
تحديد تعريفات الدوال
أنشئ ملفًا جديدًا lib/services/gemini_tools.dart
لتحديد تعريفات الدوال:
lib/services/gemini_tools.dart
import 'package:firebase_ai/firebase_ai.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'gemini_tools.g.dart';
class GeminiTools {
GeminiTools(this.ref);
final Ref ref;
FunctionDeclaration get setColorFuncDecl => FunctionDeclaration(
'set_color',
'Set the color of the display square based on red, green, and blue values.',
parameters: {
'red': Schema.number(description: 'Red component value (0.0 - 1.0)'),
'green': Schema.number(description: 'Green component value (0.0 - 1.0)'),
'blue': Schema.number(description: 'Blue component value (0.0 - 1.0)'),
},
);
List<Tool> get tools => [
Tool.functionDeclarations([setColorFuncDecl]),
];
}
@riverpod
GeminiTools geminiTools(Ref ref) => GeminiTools(ref);
فهم تعريفات الدوال
دعونا نوضّح ما يفعله هذا الرمز:
- تسمية الدالة: يمكنك تسمية الدالة
set_color
للإشارة بوضوح إلى الغرض منها - وصف الدالة: يجب تقديم وصف واضح يساعد النموذج اللغوي الكبير على معرفة متى يجب استخدامه
- تعريفات المَعلمات: يمكنك تحديد المَعلمات المنظَّمة مع أوصافها الخاصة:
red
: المكوّن الأحمر من النموذج اللوني أحمر أخضر أزرق، ويتم تحديده كرقم بين 0.0 و1.0green
: المكوّن الأخضر من نموذج RGB، ويتم تحديده كرقم بين 0.0 و1.0-
blue
: مكوّن اللون الأزرق في النموذج اللوني أحمر أخضر أزرق، ويتم تحديده كرقم بين 0.0 و1.0
- أنواع المخططات: يمكنك استخدام
Schema.number()
للإشارة إلى أنّ هذه القيم عددية. - مجموعة الأدوات: يمكنك إنشاء قائمة أدوات تحتوي على تعريف الدالة.
يساعد هذا النهج المنظَّم نموذج اللغة الكبير Gemini على فهم ما يلي:
- الوقت الذي يجب فيه استدعاء هذه الدالة
- المَعلمات التي يجب توفيرها
- القيود التي تنطبق على هذه المَعلمات (مثل نطاق القيم)
تعديل موفّر نموذج 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: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 في التعرّف على الوظائف المتاحة له.
تعديل طلب النظام
عليك الآن تعديل طلب النظام لتوجيه النموذج اللغوي الكبير بشأن استخدام أداة 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
في ما يلي التغييرات الرئيسية التي تم إجراؤها على طلب النظام:
- مقدّمة عن الأداة: بدلاً من طلب قيم RGB منسَّقة، يمكنك الآن إخبار النموذج اللغوي الكبير عن الأداة
set_color
- العملية المعدَّلة: يمكنك تغيير الخطوة 3 من "تنسيق القيم في الاستجابة" إلى "استخدام الأداة لضبط القيم".
- مثال معدَّل: توضّح كيف يجب أن يتضمّن الردّ طلب استخدام أداة بدلاً من نص منسَّق
- إزالة متطلبات التنسيق: بما أنّك تستخدم استدعاءات الدوال المنظَّمة، لم يعُد عليك الالتزام بتنسيق نصّ محدّد.
يوجه هذا الطلب المعدَّل النموذج اللغوي الكبير إلى استخدام ميزة "استدعاء الدوال" بدلاً من مجرد تقديم قيم RGB في شكل نصي.
إنشاء رمز Riverpod
نفِّذ أمر أداة إنشاء التطبيقات لإنشاء رمز Riverpod المطلوب:
dart run build_runner build --delete-conflicting-outputs
تشغيل التطبيق
في هذه المرحلة، سينشئ Gemini محتوًى يحاول استخدام ميزة "استدعاء الدوال البرمجية"، ولكن لم يتم بعد تنفيذ معالجات لعمليات استدعاء الدوال البرمجية. عند تشغيل التطبيق ووصف لون، سترى Gemini يستجيب كما لو أنّه استدعى أداة، ولكن لن تظهر أي تغييرات في الألوان في واجهة المستخدم حتى الخطوة التالية.
شغِّل تطبيقك:
flutter run -d DEVICE
جرِّب وصف لون، مثل "أزرق المحيط العميق" أو "أخضر الغابة"، ولاحظ الردود. تحاول النماذج اللغوية الكبيرة استدعاء الدوال المحدّدة أعلاه، ولكن لم يرصد الرمز استدعاءات الدوال بعد.
عملية استدعاء الدوال
في ما يلي توضيح لما يحدث عندما يستخدم Gemini ميزة "استدعاء الدوال":
- اختيار الوظيفة: يقرّر النموذج اللغوي الكبير ما إذا كان طلب الوظيفة سيكون مفيدًا استنادًا إلى طلب المستخدم.
- إنشاء المَعلمات: ينشئ النموذج اللغوي الكبير قيم المَعلمات التي تتناسب مع مخطط الدالة.
- تنسيق استدعاء الدالة: يرسل النموذج اللغوي الكبير (LLM) عنصرًا منظَّمًا لاستدعاء الدالة في رده.
- معالجة التطبيق: سيتلقّى تطبيقك هذا الاستدعاء وينفّذ الوظيفة ذات الصلة (التي سيتم تنفيذها في الخطوة التالية).
- دمج الردود: في المحادثات المتعددة الأدوار، يتوقّع النموذج اللغوي الكبير أن يتم عرض نتيجة الدالة
في الحالة الحالية لتطبيقك، تحدث الخطوات الثلاث الأولى، ولكن لم تنفّذ بعد الخطوتين 4 أو 5 (التعامل مع طلبات الدوال)، وهو ما ستفعله في الخطوة التالية.
تفاصيل فنية: كيف يقرّر Gemini متى يستخدم الدوال؟
يتّخذ Gemini قرارات ذكية بشأن وقت استخدام الوظائف استنادًا إلى ما يلي:
- نية المستخدم: ما إذا كان سيتم تنفيذ طلب المستخدم على أفضل وجه من خلال دالة
- مدى صلة الوظيفة بالمهمة: مدى تطابق الوظائف المتاحة مع المهمة
- مدى توفّر المَعلمات: ما إذا كان بإمكانها تحديد قيم المَعلمات بدقة
- تعليمات النظام: إرشادات من طلب النظام حول استخدام الوظائف
من خلال تقديم بيانات واضحة عن الوظائف وتعليمات النظام، تكون قد أعددت Gemini للتعرّف على طلبات وصف الألوان كفرص لاستخدام وظيفة set_color
.
ما هي الخطوات التالية؟
في الخطوة التالية، ستنفّذ معالِجات لعمليات استدعاء الدوال الواردة من Gemini. سيؤدي ذلك إلى إكمال الدائرة، ما يسمح لأوصاف المستخدمين بتفعيل تغييرات فعلية في الألوان في واجهة المستخدم من خلال طلبات الدوال التي تقدّمها النماذج اللغوية الكبيرة.
تحديد المشاكل وحلّها
مشاكل في تعريف الدوال
في حال مواجهة أخطاء في تعريفات الدوال:
- التأكّد من أنّ أسماء المَعلمات وأنواعها تتطابق مع ما هو متوقّع
- التأكّد من أنّ اسم الدالة واضح ومعبّر
- التأكّد من أنّ وصف الدالة يوضّح الغرض منها بدقة
مشاكل في الطلبات من النظام
إذا لم تحاول النماذج اللغوية الكبيرة استخدام الدالة:
- تأكَّد من أنّ طلب النظام يوجّه النموذج اللغوي الكبير بوضوح إلى استخدام أداة
set_color
- تأكَّد من أنّ المثال في طلب النظام يوضّح كيفية استخدام الدالة
- حاوِل أن تكون التعليمات أكثر وضوحًا بشأن كيفية استخدام الأداة
مشاكل عامة
في حال مواجهة مشاكل أخرى:
- تحقَّق من وحدة التحكّم بحثًا عن أي أخطاء متعلّقة بتعريفات الدوال
- تأكَّد من تمرير الأدوات إلى النموذج بشكلٍ سليم
- التأكّد من أنّ جميع الرموز البرمجية التي تم إنشاؤها باستخدام Riverpod محدَّثة
المفاهيم الرئيسية التي تم تعلّمها
- تحديد تعريفات الدوال لتوسيع إمكانات النماذج اللغوية الكبيرة في تطبيقات Flutter
- إنشاء مخططات مَعلمات لجمع البيانات المنظَّمة
- دمج تعريفات الدوال مع نموذج Gemini
- تعديل طلبات النظام لتشجيع استخدام الوظائف
- فهم طريقة اختيار النماذج اللغوية الكبيرة للدوال واستدعائها
توضّح هذه الخطوة كيف يمكن لنماذج اللغات الكبيرة سدّ الفجوة بين إدخال اللغة الطبيعية واستدعاء الدوال المنظَّمة، ما يمهّد الطريق للتكامل السلس بين المحادثة وميزات التطبيق.
6. تنفيذ معالجة الأدوات
في هذه الخطوة، ستنفّذ معالِجات لطلبات الدوال الواردة من Gemini. بهذا يكتمل مسار التواصل بين المدخلات باللغة الطبيعية وميزات التطبيق الملموسة، ما يسمح للنموذج اللغوي الكبير بالتلاعب مباشرةً بواجهة المستخدم استنادًا إلى أوصاف المستخدمين.
ما ستتعرّف عليه في هذه الخطوة
- فهم مسار استدعاء الدوال الكامل في تطبيقات النماذج اللغوية الكبيرة
- معالجة طلبات الدوال من Gemini في تطبيق Flutter
- تنفيذ معالجات الدوال التي تعدّل حالة التطبيق
- التعامل مع ردود الدوال وإرجاع النتائج إلى النموذج اللغوي الكبير
- إنشاء تدفق تواصل كامل بين النموذج اللغوي الكبير وواجهة المستخدم
- تسجيل طلبات الدوال وردودها لضمان الشفافية
فهم مسار استدعاء الدوال
قبل البدء في التنفيذ، دعنا نتعرّف على مسار معالجة استدعاء الدوال الكامل:
سير العمل الكامل
- إدخال المستخدم: يصف المستخدم لونًا بلغة طبيعية (مثلاً "أخضر غامق")
- معالجة النموذج اللغوي الكبير: يحلّل Gemini الوصف ويقرّر استدعاء الدالة
set_color
- إنشاء طلبات الدوال: ينشئ Gemini ملف JSON منظَّمًا يتضمّن مَعلمات (قيم الأحمر والأخضر والأزرق)
- تلقّي طلبات تنفيذ الدوال: يتلقّى تطبيقك هذه البيانات المنظَّمة من Gemini
- تنفيذ الدالة: ينفّذ تطبيقك الدالة باستخدام المَعلمات المقدَّمة
- تعديل الحالة: تعدّل الدالة حالة تطبيقك (تغيير اللون المعروض)
- إنشاء الرد: تعرض الدالة النتائج للنموذج اللغوي الكبير
- دمج الردود: يدمج النموذج اللغوي الكبير هذه النتائج في رده النهائي.
- تحديث واجهة المستخدم: تتفاعل واجهة المستخدم مع تغيير الحالة، وتعرض اللون الجديد
تُعدّ دورة التواصل الكاملة أساسية لدمج النماذج اللغوية الكبيرة بشكل سليم. عندما يطلب نموذج لغوي كبير تنفيذ وظيفة، لا يرسل الطلب ببساطة وينتهي الأمر. بدلاً من ذلك، ينتظر أن ينفّذ تطبيقك الدالة ويعرض النتائج. بعد ذلك، يستخدم النموذج اللغوي الكبير هذه النتائج لصياغة الرد النهائي، ما يؤدي إلى إنشاء سير محادثة طبيعي يقرّ بالإجراءات المتّخذة.
تنفيذ معالِجات الدوال
لنحدّث ملف lib/services/gemini_tools.dart
لإضافة معالجات لمكالمات الدوال:
lib/services/gemini_tools.dart
import 'package:colorist_ui/colorist_ui.dart';
import 'package:firebase_ai/firebase_ai.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'gemini_tools.g.dart';
class GeminiTools {
GeminiTools(this.ref);
final Ref ref;
FunctionDeclaration get setColorFuncDecl => FunctionDeclaration(
'set_color',
'Set the color of the display square based on red, green, and blue values.',
parameters: {
'red': Schema.number(description: 'Red component value (0.0 - 1.0)'),
'green': Schema.number(description: 'Green component value (0.0 - 1.0)'),
'blue': Schema.number(description: 'Blue component value (0.0 - 1.0)'),
},
);
List<Tool> get tools => [
Tool.functionDeclarations([setColorFuncDecl]),
];
Map<String, Object?> handleFunctionCall( // Add from here
String functionName,
Map<String, Object?> arguments,
) {
final logStateNotifier = ref.read(logStateProvider.notifier);
logStateNotifier.logFunctionCall(functionName, arguments);
return switch (functionName) {
'set_color' => handleSetColor(arguments),
_ => handleUnknownFunction(functionName),
};
}
Map<String, Object?> handleSetColor(Map<String, Object?> arguments) {
final colorStateNotifier = ref.read(colorStateProvider.notifier);
final red = (arguments['red'] as num).toDouble();
final green = (arguments['green'] as num).toDouble();
final blue = (arguments['blue'] as num).toDouble();
final functionResults = {
'success': true,
'current_color': colorStateNotifier
.updateColor(red: red, green: green, blue: blue)
.toLLMContextMap(),
};
final logStateNotifier = ref.read(logStateProvider.notifier);
logStateNotifier.logFunctionResults(functionResults);
return functionResults;
}
Map<String, Object?> handleUnknownFunction(String functionName) {
final logStateNotifier = ref.read(logStateProvider.notifier);
logStateNotifier.logWarning('Unsupported function call $functionName');
return {
'success': false,
'reason': 'Unsupported function call $functionName',
};
} // To here.
}
@riverpod
GeminiTools geminiTools(Ref ref) => GeminiTools(ref);
فهم معالجات الدوال
في ما يلي تفصيل لما تفعله معالجات الدوال هذه:
handleFunctionCall
: هو أداة إرسال مركزية تعمل على:- تسجيل استدعاء الدالة لتحقيق الشفافية في لوحة السجلّ
- توجيه الطلبات إلى المعالج المناسب استنادًا إلى اسم الدالة
- تعرض هذه الدالة استجابة منظَّمة سيتم إرسالها إلى النموذج اللغوي الكبير
handleSetColor
: المعالج المحدّد لوظيفةset_color
الذي:- تستخرِج قيم النموذج اللوني أحمر أخضر أزرق (RGB) من خريطة الوسيطات
- تحويلها إلى الأنواع المتوقّعة (أرقام مضاعفة)
- تعديل حالة لون التطبيق باستخدام
colorStateNotifier
- تنشئ هذه الدالة استجابة منظَّمة تتضمّن حالة النجاح ومعلومات اللون الحالي.
- تسجيل نتائج الدالة لتصحيح الأخطاء
-
handleUnknownFunction
: معالج احتياطي للوظائف غير المعروفة التي:- تسجيل تحذير بشأن الدالة غير المتوافقة
- إرجاع ردّ يفيد بحدوث خطأ إلى النموذج اللغوي الكبير
تُعدّ وظيفة handleSetColor
مهمة بشكل خاص لأنّها تسدّ الفجوة بين فهم اللغة الطبيعية في النموذج اللغوي الكبير والتغييرات الملموسة في واجهة المستخدم.
تعديل خدمة Gemini Chat لمعالجة طلبات الوظائف والردود
الآن، لنعدّل الملف lib/services/gemini_chat_service.dart
لمعالجة استدعاءات الدوال من ردود النموذج اللغوي الكبير وإرسال النتائج مرة أخرى إلى النموذج اللغوي الكبير:
lib/services/gemini_chat_service.dart
import 'dart:async';
import 'package:colorist_ui/colorist_ui.dart';
import 'package:firebase_ai/firebase_ai.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import '../providers/gemini.dart';
import 'gemini_tools.dart'; // Add this import
part 'gemini_chat_service.g.dart';
class GeminiChatService {
GeminiChatService(this.ref);
final Ref ref;
Future<void> sendMessage(String message) async {
final chatSession = await ref.read(chatSessionProvider.future);
final chatStateNotifier = ref.read(chatStateProvider.notifier);
final logStateNotifier = ref.read(logStateProvider.notifier);
chatStateNotifier.addUserMessage(message);
logStateNotifier.logUserText(message);
final llmMessage = chatStateNotifier.createLlmMessage();
try {
final response = await chatSession.sendMessage(Content.text(message));
final responseText = response.text;
if (responseText != null) {
logStateNotifier.logLlmText(responseText);
chatStateNotifier.appendToMessage(llmMessage.id, responseText);
}
if (response.functionCalls.isNotEmpty) { // Add from here
final geminiTools = ref.read(geminiToolsProvider);
final functionResultResponse = await chatSession.sendMessage(
Content.functionResponses([
for (final functionCall in response.functionCalls)
FunctionResponse(
functionCall.name,
geminiTools.handleFunctionCall(
functionCall.name,
functionCall.args,
),
),
]),
);
final responseText = functionResultResponse.text;
if (responseText != null) {
logStateNotifier.logLlmText(responseText);
chatStateNotifier.appendToMessage(llmMessage.id, responseText);
}
} // To here.
} catch (e, st) {
logStateNotifier.logError(e, st: st);
chatStateNotifier.appendToMessage(
llmMessage.id,
"\nI'm sorry, I encountered an error processing your request. "
"Please try again.",
);
} finally {
chatStateNotifier.finalizeMessage(llmMessage.id);
}
}
}
@riverpod
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);
}
}
هذا الرمز:
- للتحقّق مما إذا كان ردّ النموذج اللغوي الكبير يتضمّن أي طلبات لتنفيذ وظائف
- لكل استدعاء دالة، يتم استدعاء الطريقة
handleFunctionCall
مع اسم الدالة ووسيطاتها - تجمع نتائج كل استدعاء دالة
- إعادة هذه النتائج إلى النموذج اللغوي الكبير باستخدام
Content.functionResponses
- معالجة ردّ النموذج اللغوي الكبير على نتائج الدالة
- تعديل واجهة المستخدم باستخدام نص الردّ النهائي
يؤدي ذلك إلى إنشاء سير عمل ذهاب وإياب:
- المستخدم → النموذج اللغوي الكبير: يطلب لونًا
- النموذج اللغوي الكبير → التطبيق: استدعاء الدوال مع المَعلمات
- التطبيق → المستخدم: تم عرض لون جديد
- التطبيق → النموذج اللغوي الكبير: نتائج الدالة
- النموذج اللغوي الكبير → المستخدم: الرد النهائي الذي يتضمّن نتائج الدالة
إنشاء رمز Riverpod
نفِّذ أمر أداة إنشاء التطبيقات لإنشاء رمز Riverpod المطلوب:
dart run build_runner build --delete-conflicting-outputs
تشغيل المسار الكامل واختباره
الآن، شغِّل تطبيقك:
flutter run -d DEVICE
جرِّب إدخال أوصاف مختلفة للألوان:
- "أريد لونًا أحمر قرمزيًا داكنًا"
- "أريد لونًا أزرق سماويًا مهدئًا"
- "أريد معرفة لون أوراق النعناع الطازجة"
- "أريد رؤية غروب الشمس بلون برتقالي دافئ"
- "أريد لونًا أرجوانيًا ملكيًا غنيًا"
من المفترض أن يظهر لك الآن ما يلي:
- ظهور رسالتك في واجهة المحادثة
- ردّ Gemini الذي يظهر في المحادثة
- تسجيل استدعاءات الدوال في لوحة السجلّ
- تسجيل نتائج الدالة فورًا بعد
- تعديل مستطيل الألوان لعرض اللون الموصوف
- تعديل قيم النموذج اللوني أحمر أخضر أزرق (RGB) لعرض مكوّنات اللون الجديد
- ظهور ردّ Gemini النهائي، والذي غالبًا ما يتضمّن تعليقًا على اللون الذي تم ضبطه
تقدّم لوحة السجلّ إحصاءات عن العمليات التي تجري في الخلفية. وسترى ما يلي:
- طلبات الدوال التي يرسلها Gemini
- المَعلمات التي يختارها لكل قيمة RGB
- النتائج التي تعرضها الدالة
- ردود المتابعة من Gemini
أداة إرسال إشعارات حالة اللون
إنّ colorStateNotifier
الذي تستخدمه لتعديل الألوان هو جزء من حزمة colorist_ui
. وتدير ما يلي:
- اللون الحالي المعروض في واجهة المستخدِم
- سجلّ الألوان (آخر 10 ألوان)
- إشعار بالتغييرات في حالة عناصر واجهة المستخدم
عند استدعاء updateColor
باستخدام قيم RGB جديدة، سيتم تنفيذ ما يلي:
- تُنشئ عنصر
ColorData
جديدًا بالقيم المقدَّمة - تعديل اللون الحالي في حالة التطبيق
- تتم إضافة اللون إلى السجلّ
- تفعيل تعديلات واجهة المستخدم من خلال إدارة الحالة في Riverpod
تراقب مكوّنات واجهة المستخدم في حزمة colorist_ui
هذه الحالة ويتم تعديلها تلقائيًا عند تغيُّرها، ما يؤدي إلى إنشاء تجربة تفاعلية.
فهم معالجة الأخطاء
يتضمّن التنفيذ معالجة قوية للأخطاء:
- كتلة Try-catch: تغطي جميع التفاعلات مع النماذج اللغوية الكبيرة لرصد أي استثناءات
- تسجيل الأخطاء: تسجيل الأخطاء في لوحة السجلّ مع تتبُّع تسلسل استدعاء الدوال البرمجية
- ملاحظات المستخدم: تقديم رسالة خطأ سهلة الفهم في المحادثة
- تنظيف الحالة: يتم وضع اللمسات الأخيرة على حالة الرسالة حتى في حال حدوث خطأ.
يضمن ذلك بقاء التطبيق ثابتًا وتقديم ملاحظات مناسبة حتى عند حدوث مشاكل في خدمة النموذج اللغوي الكبير أو تنفيذ الوظائف.
أهمية ميزة "طلب تنفيذ وظيفة" في تحسين تجربة المستخدم
توضّح الإنجازات التي حققتها هنا كيف يمكن للنماذج اللغوية الكبيرة إنشاء واجهات طبيعية فعّالة:
- واجهة اللغة الطبيعية: يعبّر المستخدمون عن نيتهم بلغة يومية
- التفسير الذكي: يترجم النموذج اللغوي الكبير الأوصاف الغامضة إلى قيم دقيقة
- التعديل المباشر: يتم تعديل واجهة المستخدم استجابةً للغة الطبيعية
- الردود السياقية: يقدّم النموذج اللغوي الكبير سياقًا حواريًا حول التغييرات
- الحدّ من الجهد الذهني: لا يحتاج المستخدمون إلى فهم قيم الأحمر والأخضر والأزرق أو نظرية الألوان
يمكن توسيع نطاق هذا النمط من استخدام ميزة "استدعاء الدوال" في النماذج اللغوية الكبيرة لتشمل مجالات أخرى لا حصر لها تتجاوز اختيار الألوان، وذلك بهدف الربط بين اللغة الطبيعية وإجراءات واجهة المستخدم.
ما هي الخطوات التالية؟
في الخطوة التالية، ستحسِّن تجربة المستخدم من خلال تنفيذ الردود المتدفّقة. بدلاً من انتظار الرد الكامل، ستتم معالجة أجزاء النص واستدعاءات الدوال عند تلقّيها، ما يؤدي إلى إنشاء تطبيق أكثر استجابة وتفاعلية.
تحديد المشاكل وحلّها
مشاكل في استدعاء الدالة
إذا كان Gemini لا يستدعي الدوال أو كانت المَعلمات غير صحيحة:
- التأكّد من أنّ بيان الدالة يتطابق مع ما هو موصوف في طلب النظام
- التأكّد من تطابق أسماء المَعلمات وأنواعها
- تأكَّد من أنّ طلب النظام يوجّه النموذج اللغوي الكبير بشكل صريح إلى استخدام الأداة
- تأكَّد من أنّ اسم الدالة في المعالج يتطابق تمامًا مع الاسم الوارد في البيان
- فحص لوحة السجلّ للحصول على معلومات تفصيلية حول استدعاءات الدوال
مشاكل في استجابة الدالة
إذا لم يتم إرسال نتائج الدالة بشكلٍ صحيح إلى النموذج اللغوي الكبير:
- تأكَّد من أنّ الدالة تعرض خريطة منسَّقة بشكل صحيح
- تأكَّد من إنشاء Content.functionResponses بشكلٍ صحيح
- ابحث عن أي أخطاء في السجلّ مرتبطة بردود الدوال
- تأكَّد من استخدام جلسة المحادثة نفسها للردّ
مشاكل في عرض الألوان
إذا لم تظهر الألوان بشكل صحيح، اتّبِع الخطوات التالية:
- التأكّد من تحويل قيم RGB بشكلٍ صحيح إلى أرقام مضاعفة (قد يرسلها النموذج اللغوي الكبير كأعداد صحيحة)
- تأكَّد من أنّ القيم تقع في النطاق المتوقّع (من 0.0 إلى 1.0).
- تأكَّد من استدعاء أداة إشعار حالة اللون بشكل صحيح
- فحص السجلّ بحثًا عن القيم الدقيقة التي يتم تمريرها إلى الدالة
المشاكل العامة
بالنسبة إلى المشاكل العامة:
- فحص السجلات بحثًا عن أخطاء أو تحذيرات
- التأكّد من ربط Firebase AI Logic
- التحقّق من عدم تطابق الأنواع في مَعلمات الدالة
- التأكّد من أنّ جميع الرموز البرمجية التي تم إنشاؤها باستخدام Riverpod محدَّثة
المفاهيم الرئيسية التي تم تعلّمها
- تنفيذ مسار كامل لاستدعاء الدوال في Flutter
- إنشاء اتصال كامل بين نموذج لغوي كبير وتطبيقك
- معالجة البيانات المنظَّمة من ردود النماذج اللغوية الكبيرة
- إرسال نتائج الدالة مرة أخرى إلى النموذج اللغوي الكبير (LLM) لدمجها في الردود
- استخدام لوحة السجلّ للحصول على معلومات عن التفاعلات بين التطبيقات ونماذج اللغات الكبيرة
- ربط إدخالات اللغة الطبيعية بتغييرات ملموسة في واجهة المستخدم
بعد إكمال هذه الخطوة، سيوضّح تطبيقك الآن أحد أقوى أنماط دمج النماذج اللغوية الكبيرة: ترجمة مدخلات اللغة الطبيعية إلى إجراءات ملموسة في واجهة المستخدم، مع الحفاظ على محادثة متماسكة تعترف بهذه الإجراءات. يؤدي ذلك إلى إنشاء واجهة حوارية سهلة الاستخدام تبدو ساحرة للمستخدمين.
7. بث الردود لتحسين تجربة المستخدم
في هذه الخطوة، ستحسّن تجربة المستخدم من خلال تنفيذ الردود المتدفّقة من Gemini. بدلاً من انتظار إنشاء الردّ بأكمله، ستتم معالجة أجزاء النص واستدعاءات الدوال عند تلقّيها، ما يؤدي إلى إنشاء تطبيق أكثر استجابة وتفاعلية.
المواضيع التي سنتناولها في هذه الخطوة
- أهمية البث المباشر للتطبيقات المستندة إلى نماذج اللغات الكبيرة
- تنفيذ الردود التدريجية من النماذج اللغوية الكبيرة في تطبيق Flutter
- معالجة أجزاء النص عند ورودها من واجهة برمجة التطبيقات
- إدارة حالة المحادثة لتجنُّب تعارض الرسائل
- التعامل مع طلبات تنفيذ الدوال في الردود المتدفقة
- إنشاء مؤشرات مرئية للردود قيد التقدم
أهمية البث المباشر لتطبيقات النماذج اللغوية الكبيرة
قبل التنفيذ، دعنا نفهم سبب أهمية عرض الردود أثناء بثها لتقديم تجارب مستخدم ممتازة باستخدام النماذج اللغوية الكبيرة:
تحسين تجربة المستخدم
تقدّم الردود المتدفقة عدة مزايا مهمة لتجربة المستخدم، وهي:
- تقليل وقت الاستجابة المُدرَك: يرى المستخدمون النص يبدأ في الظهور على الفور (عادةً في غضون 100 إلى 300 ملي ثانية)، بدلاً من الانتظار لعدة ثوانٍ للحصول على رد كامل. ويؤدي هذا الشعور بالاستجابة الفورية إلى تحسين رضا المستخدمين بشكل كبير.
- إيقاع المحادثة الطبيعي: يتيح ظهور النص تدريجيًا محاكاة طريقة تواصل البشر، ما يؤدي إلى إنشاء تجربة حوارية أكثر طبيعية.
- معالجة المعلومات بشكل تدريجي: يمكن للمستخدمين البدء في معالجة المعلومات فور وصولها، بدلاً من أن يواجهوا كمية كبيرة من النصوص في وقت واحد.
- فرصة للتدخّل مبكرًا: في تطبيق كامل، يمكن للمستخدمين التدخّل أو إعادة توجيه نموذج اللغة الكبير إذا لاحظوا أنّه يتّجه في اتجاه غير مفيد.
- تأكيد مرئي للنشاط: يقدّم النص المتدفّق ملاحظات فورية بأنّ النظام يعمل، ما يقلّل من الشك.
المزايا الفنية
بالإضافة إلى تحسينات تجربة المستخدم، يقدّم البث مزايا فنية:
- تنفيذ الوظيفة مبكرًا: يمكن رصد طلبات الوظائف وتنفيذها فور ظهورها في مصدر البيانات، بدون انتظار الردّ الكامل.
- تعديلات تدريجية على واجهة المستخدم: يمكنك تعديل واجهة المستخدم بشكل تدريجي عند وصول معلومات جديدة، ما يؤدي إلى إنشاء تجربة أكثر ديناميكية.
- إدارة حالة المحادثة: يوفّر البث إشارات واضحة حول اكتمال الردود أو استمرارها قيد المعالجة، ما يتيح إدارة الحالة بشكل أفضل.
- تقليل مخاطر انتهاء المهلة: مع الردود غير المتدفقة، قد يؤدي إنشاء المحتوى لفترة طويلة إلى انتهاء مهلة الاتصال. تتيح ميزة البث إنشاء الاتصال في وقت مبكر والحفاظ عليه.
في تطبيق 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/legacy.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(chatStateProvider.notifier);
final logStateNotifier = ref.read(logStateProvider.notifier);
if (conversationState == ConversationState.busy) { // Add from here...
logStateNotifier.logWarning(
"Can't send a message while a conversation is in progress",
);
throw Exception(
"Can't send a message while a conversation is in progress",
);
}
final conversationStateNotifier = ref.read(
conversationStateProvider.notifier,
);
conversationStateNotifier.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(chatStateProvider.notifier);
final logStateNotifier = ref.read(logStateProvider.notifier);
final blockText = block.text;
if (blockText != null) {
logStateNotifier.logLlmText(blockText);
chatStateNotifier.appendToMessage(llmMessageId, blockText);
}
if (block.functionCalls.isNotEmpty) {
final geminiTools = ref.read(geminiToolsProvider);
final responseStream = chatSession.sendMessageStream(
Content.functionResponses([
for (final functionCall in block.functionCalls)
FunctionResponse(
functionCall.name,
geminiTools.handleFunctionCall(
functionCall.name,
functionCall.args,
),
),
]),
);
await for (final response in responseStream) {
final responseText = response.text;
if (responseText != null) {
logStateNotifier.logLlmText(responseText);
chatStateNotifier.appendToMessage(llmMessageId, responseText);
}
}
}
} // To here.
}
@riverpod
GeminiChatService geminiChatService(Ref ref) => GeminiChatService(ref);
فهم عملية تنفيذ البث
دعونا نوضّح ما يفعله هذا الرمز:
- تتبُّع حالة المحادثة:
- يتتبّع
conversationStateProvider
ما إذا كان التطبيق يعالج حاليًا ردًا - تنتقل الحالة من
idle
إلىbusy
أثناء المعالجة، ثم تعود إلىidle
- يمنع ذلك إرسال طلبات متزامنة متعددة قد تتعارض مع بعضها البعض.
- يتتبّع
- بدء البث:
- تعرض الدالة
sendMessageStream()
مجموعة من أجزاء الردود بدلاً منFuture
مع الرد الكامل - قد تحتوي كل قطعة على نص أو طلبات وظائف أو كليهما
- تعرض الدالة
- المعالجة التقدّمية:
- تعالج
await for
كل جزء عند وصوله في الوقت الفعلي - تتم إضافة النص إلى واجهة المستخدم على الفور، ما يؤدي إلى إنشاء تأثير البث
- يتم تنفيذ طلبات الدوال فور رصدها
- تعالج
- التعامل مع طلبات تنفيذ الوظائف:
- عند رصد طلب تنفيذ دالة في جزء من الرمز، يتم تنفيذها على الفور
- يتم إرسال النتائج مرة أخرى إلى النموذج اللغوي الكبير من خلال مكالمة بث أخرى.
- تتم أيضًا معالجة ردّ النموذج اللغوي الكبير على هذه النتائج بطريقة البث المباشر
- التعامل مع الأخطاء وإصلاحها:
- توفّر
try
/catch
معالجة قوية للأخطاء - يضمن الحظر
finally
إعادة ضبط حالة المحادثة بشكل صحيح - يتم دائمًا وضع اللمسات الأخيرة على الرسالة، حتى إذا حدثت أخطاء
- توفّر
يؤدي هذا التنفيذ إلى إنشاء تجربة بث سريعة الاستجابة وموثوقة مع الحفاظ على حالة المحادثة المناسبة.
تعديل الشاشة الرئيسية لربط حالة المحادثة
عدِّل ملف lib/main.dart
لتمرير حالة المحادثة إلى الشاشة الرئيسية:
lib/main.dart
import 'package:colorist_ui/colorist_ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'providers/gemini.dart';
import 'services/gemini_chat_service.dart';
void main() async {
runApp(ProviderScope(child: MainApp()));
}
class MainApp extends ConsumerWidget {
const MainApp({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final model = ref.watch(geminiModelProvider);
final conversationState = ref.watch(conversationStateProvider); // Add this line
return MaterialApp(
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
),
home: model.when(
data: (data) => MainScreen(
conversationState: conversationState, // And this line
sendMessage: (text) {
ref.read(geminiChatServiceProvider).sendMessage(text);
},
),
loading: () => LoadingScreen(message: 'Initializing Gemini Model'),
error: (err, st) => ErrorScreen(error: err),
),
);
}
}
التغيير الرئيسي هنا هو تمرير conversationState
إلى أداة MainScreen
. سيستخدم MainScreen
(الذي توفّره حزمة colorist_ui
) هذه الحالة لإيقاف إدخال النص أثناء معالجة الرد.
يؤدي ذلك إلى إنشاء تجربة مستخدم متماسكة يعكس فيها تصميم واجهة المستخدم الحالة الحالية للمحادثة.
إنشاء رمز Riverpod
نفِّذ أمر أداة إنشاء التطبيقات لإنشاء رمز Riverpod المطلوب:
dart run build_runner build --delete-conflicting-outputs
تشغيل الردود التدريجية واختبارها
تشغيل تطبيقك:
flutter run -d DEVICE
جرِّب الآن اختبار سلوك البث باستخدام أوصاف ألوان مختلفة. جرِّب أوصافًا مثل:
- "أريد رؤية اللون الأزرق المخضر الداكن للمحيط عند الغسق"
- "أريد رؤية مرجان نابض بالحياة يذكّرني بالزهور الاستوائية"
- "أريد إنشاء لون أخضر زيتوني باهت مثل ملابس الجيش القديمة"
التفاصيل الفنية لعملية البث
لنتعرّف على ما يحدث بالضبط عند بث ردّ:
إنشاء الاتصال
عند الاتصال بالرقم sendMessageStream()
، يحدث ما يلي:
- ينشئ التطبيق اتصالاً بخدمة Firebase AI Logic
- يتم إرسال طلب المستخدم إلى الخدمة
- يبدأ الخادم في معالجة الطلب
- يبقى اتصال البث مفتوحًا وجاهزًا لنقل الأجزاء
نقل الأجزاء
أثناء إنشاء Gemini للمحتوى، يتم إرسال أجزاء من خلال البث:
- يرسل الخادم أجزاء من النص أثناء إنشائها (عادةً بضع كلمات أو جمل)
- عندما يقرّر Gemini استدعاء دالة، يرسل معلومات استدعاء الدالة
- قد تتبع استدعاءات الدوال أجزاء نصية إضافية
- يستمر البث إلى أن يكتمل إنشاء الفيديو.
المعالجة التدريجية
يعالج تطبيقك كل جزء بشكل تدريجي:
- تتم إضافة كل جزء من النص إلى الرد الحالي
- يتم تنفيذ طلبات الدوال فور رصدها
- يتم تعديل واجهة المستخدم في الوقت الفعلي باستخدام نتائج النص والدالة
- يتم تتبُّع الحالة للإشارة إلى أنّ الردّ لا يزال قيد البث
إكمال البث
عند اكتمال عملية الإنشاء:
- أغلق الخادم البث
- سيتم إنهاء حلقة
await for
تلقائيًا - تم وضع علامة "مكتملة" على الرسالة
- تمت إعادة ضبط حالة المحادثة إلى "غير نشطة"
- يتم تعديل واجهة المستخدم لتعكس الحالة المكتملة
مقارنة بين البث وغير البث
لفهم مزايا البث بشكل أفضل، لنقارن بين طرق البث وطرق عدم البث:
جانب | غير متوفّر للبث | البث |
وقت الاستجابة المُدرَك | لا يرى المستخدم أي شيء إلى أن يصبح الردّ الكامل جاهزًا | يمكن للمستخدم رؤية الكلمات الأولى في غضون أجزاء من الثانية |
تجربة المستخدم | انتظار طويل يتبعه ظهور مفاجئ للنص | ظهور النص بشكل طبيعي وتدريجي |
إدارة الحالة | أبسط (تكون الرسائل إما في انتظار المراجعة أو مكتملة) | أكثر تعقيدًا (يمكن أن تكون الرسائل في حالة بث) |
تنفيذ الدالة | يحدث ذلك فقط بعد اكتمال الردّ | يحدث أثناء إنشاء الرد |
مدى تعقيد عملية التنفيذ | سهولة التنفيذ | يتطلّب إدارة حالة إضافية |
استعادة الخطأ | استجابة شاملة | قد تظل الردود الجزئية مفيدة |
تعقيد الرمز | أقل تعقيدًا | أكثر تعقيدًا بسبب معالجة البث |
بالنسبة إلى تطبيق مثل Colorist، تفوق مزايا تجربة المستخدم في البث المباشر تعقيد التنفيذ، لا سيما بالنسبة إلى تفسيرات الألوان التي قد تستغرق عدة ثوانٍ لإنشائها.
أفضل الممارسات المتعلّقة بتجربة المستخدم في البث
عند تنفيذ البث في تطبيقات النماذج اللغوية الكبيرة الخاصة بك، ننصحك باتّباع أفضل الممارسات التالية:
- مؤشرات مرئية واضحة: يجب دائمًا توفير إشارات مرئية واضحة تميّز بين الرسائل المتدفقة والرسائل الكاملة
- حظر الإدخال: إيقاف إدخال المستخدم أثناء البث لمنع الطلبات المتعددة المتداخلة
- استرداد البيانات عند حدوث خطأ: صمِّم واجهة المستخدم للتعامل مع عملية استرداد البيانات بشكل سليم في حال انقطاع البث.
- حالات الانتقال: التأكّد من سلاسة الانتقال بين حالات عدم النشاط والبث والاكتمال
- تصوّر التقدّم: ننصحك باستخدام رسومات متحركة أو مؤشرات بسيطة تعرض عملية المعالجة النشطة
- خيارات الإلغاء: في تطبيق كامل، يجب توفير طرق للمستخدمين لإلغاء عمليات إنشاء المحتوى الجارية.
- دمج نتائج الدوال: تصميم واجهة المستخدم للتعامل مع نتائج الدوال التي تظهر أثناء البث
- تحسين الأداء: تقليل عمليات إعادة إنشاء واجهة المستخدم أثناء تحديثات البث السريع
تنفّذ حزمة colorist_ui
العديد من أفضل الممارسات هذه نيابةً عنك، ولكنّها اعتبارات مهمة لأي عملية تنفيذ لنموذج لغوي كبير (LLM) يتيح البث.
ما هي الخطوات التالية؟
في الخطوة التالية، ستنفّذ عملية مزامنة نموذج اللغة الكبير من خلال إرسال إشعار إلى Gemini عندما يختار المستخدمون ألوانًا من السجلّ. سيؤدي ذلك إلى إنشاء تجربة أكثر اتساقًا حيث يكون النموذج اللغوي الكبير على دراية بالتغييرات التي يجريها المستخدم على حالة التطبيق.
تحديد المشاكل وحلّها
مشاكل معالجة البث
في حال مواجهة مشاكل في معالجة البث:
- الأعراض: ردود جزئية أو نص مفقود أو إنهاء مفاجئ للبث
- الحلّ: تحقَّق من اتصال الشبكة وتأكَّد من صحة أنماط async/await في الرمز البرمجي
- بيانات التشخيص: افحص لوحة السجلّ بحثًا عن رسائل خطأ أو تحذيرات متعلّقة بمعالجة البيانات.
- الحلّ: تأكَّد من أنّ جميع عمليات معالجة البث تستخدم معالجة الأخطاء المناسبة مع حظر
try
/catch
.
استدعاءات الدوال غير المتوفّرة
إذا لم يتم رصد استدعاءات الدوال في مصدر البيانات:
- الأعراض: يظهر النص ولكن لا يتم تعديل الألوان، أو لا يعرض السجلّ أي طلبات وظائف
- الحلّ: تحقَّق من تعليمات طلب النظام بشأن استخدام طلبات الدوال
- التشخيص: تحقَّق من لوحة السجلّ لمعرفة ما إذا كان يتم تلقّي طلبات الدوال.
- الحل: عدِّل تعليمات النظام لتوجيه النموذج اللغوي الكبير بشكل أكثر وضوحًا لاستخدام أداة
set_color
معالجة الأخطاء العامة
في حال حدوث أي مشاكل أخرى:
- الخطوة 1: التحقّق من لوحة السجلّ بحثًا عن رسائل الخطأ
- الخطوة 2: التحقّق من اتصال Firebase AI Logic
- الخطوة 3: التأكّد من أنّ جميع الرموز التي تم إنشاؤها باستخدام Riverpod هي الأحدث
- الخطوة 4: مراجعة عملية تنفيذ البث بحثًا عن أي عبارات await ناقصة
المفاهيم الرئيسية التي تم تعلّمها
- تنفيذ الردود المتدفقة باستخدام Gemini API لتوفير تجربة مستخدم أكثر استجابة
- إدارة حالة المحادثة للتعامل مع تفاعلات البث بشكل صحيح
- معالجة النصوص في الوقت الفعلي واستدعاء الدوال عند وصولها
- إنشاء واجهات مستخدم متجاوبة يتم تعديلها بشكل تدريجي أثناء البث
- التعامل مع أحداث البث المتزامنة باستخدام أنماط غير متزامنة مناسبة
- تقديم ملاحظات مرئية مناسبة أثناء عرض الردود المتدفقة
من خلال تنفيذ ميزة البث، حسّنت بشكل كبير تجربة المستخدم في تطبيق Colorist، ما أدى إلى إنشاء واجهة أكثر استجابةً وجاذبيةً تبدو وكأنّها محادثة حقيقية.
8. مزامنة السياق في النماذج اللغوية الكبيرة
في هذه الخطوة الإضافية، ستنفّذ عملية "مزامنة سياق النماذج اللغوية الكبيرة" من خلال إرسال إشعار إلى Gemini عندما يختار المستخدمون ألوانًا من السجلّ. يؤدي ذلك إلى توفير تجربة أكثر تكاملاً، حيث يكون النموذج اللغوي الكبير على دراية بإجراءات المستخدم في الواجهة، وليس فقط برسائله الواضحة.
المواضيع التي سنتناولها في هذه الخطوة
- إنشاء مزامنة سياق نموذج لغوي كبير (LLM) بين واجهة المستخدم والنموذج اللغوي الكبير
- تحويل أحداث واجهة المستخدم إلى سياق يمكن للنموذج اللغوي الكبير فهمه
- تعديل سياق المحادثة استنادًا إلى إجراءات المستخدم
- توفير تجربة متّسقة عبر طرق التفاعل المختلفة
- تحسين إدراك السياق في النماذج اللغوية الكبيرة بما يتجاوز رسائل المحادثة الواضحة
فهم مزامنة السياق في النماذج اللغوية الكبيرة
تستجيب برامج الدردشة التقليدية فقط لرسائل المستخدمين الواضحة، ما يؤدي إلى حدوث انقطاع عند تفاعل المستخدمين مع التطبيق من خلال وسائل أخرى. تعمل ميزة "مزامنة سياق النموذج اللغوي الكبير" على حلّ هذا القيد:
أهمية مزامنة السياق في النماذج اللغوية الكبيرة
عندما يتفاعل المستخدمون مع تطبيقك من خلال عناصر واجهة المستخدِم (مثل اختيار لون من السجلّ)، لا يمكن للنموذج اللغوي الكبير معرفة ما حدث إلا إذا أخبرته بذلك صراحةً. مزامنة السياق في النماذج اللغوية الكبيرة:
- الاحتفاظ بالسياق: إبقاء النموذج اللغوي الكبير على اطّلاع على جميع إجراءات المستخدم ذات الصلة
- إنشاء تجربة متسقة: تقديم تجربة متسقة حيث يدرك النموذج اللغوي الكبير التفاعلات مع واجهة المستخدم
- تعزيز الذكاء: يتيح للنموذج اللغوي الكبير الردّ بشكل مناسب على جميع إجراءات المستخدم
- تحسين تجربة المستخدم: يجعل التطبيق بأكمله يبدو أكثر تكاملاً واستجابة
- تقليل الجهد الذي يبذله المستخدم: لا يحتاج المستخدمون إلى شرح إجراءات واجهة المستخدم يدويًا
في تطبيق Colorist، عندما يختار المستخدم لونًا من السجلّ، تريد أن يتعرّف Gemini على هذا الإجراء ويعلّق بذكاء على اللون المحدّد، مع الحفاظ على وهم المساعد السلس والواعي.
تعديل خدمة Gemini Chat لتلقّي إشعارات بشأن اختيار الألوان
أولاً، ستضيف طريقة إلى GeminiChatService
لإعلام النموذج اللغوي الكبير عندما يختار المستخدم لونًا من السجلّ. تعديل ملف lib/services/gemini_chat_service.dart
:
lib/services/gemini_chat_service.dart
import 'dart:async';
import 'dart:convert'; // Add this import
import 'package:colorist_ui/colorist_ui.dart';
import 'package:firebase_ai/firebase_ai.dart';
import 'package:flutter_riverpod/legacy.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(chatStateProvider.notifier);
final logStateNotifier = ref.read(logStateProvider.notifier);
if (conversationState == ConversationState.busy) {
logStateNotifier.logWarning(
"Can't send a message while a conversation is in progress",
);
throw Exception(
"Can't send a message while a conversation is in progress",
);
}
final conversationStateNotifier = ref.read(
conversationStateProvider.notifier,
);
conversationStateNotifier.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(chatStateProvider.notifier);
final logStateNotifier = ref.read(logStateProvider.notifier);
final blockText = block.text;
if (blockText != null) {
logStateNotifier.logLlmText(blockText);
chatStateNotifier.appendToMessage(llmMessageId, blockText);
}
if (block.functionCalls.isNotEmpty) {
final geminiTools = ref.read(geminiToolsProvider);
final responseStream = chatSession.sendMessageStream(
Content.functionResponses([
for (final functionCall in block.functionCalls)
FunctionResponse(
functionCall.name,
geminiTools.handleFunctionCall(
functionCall.name,
functionCall.args,
),
),
]),
);
await for (final response in responseStream) {
final responseText = response.text;
if (responseText != null) {
logStateNotifier.logLlmText(responseText);
chatStateNotifier.appendToMessage(llmMessageId, responseText);
}
}
}
}
}
@riverpod
GeminiChatService geminiChatService(Ref ref) => GeminiChatService(ref);
الإضافة الرئيسية هي طريقة notifyColorSelection
التي:
- تأخذ عنصر
ColorData
يمثّل اللون المحدّد - ترميزها بتنسيق JSON يمكن تضمينه في رسالة
- إرسال رسالة منسَّقة خصيصًا إلى النموذج اللغوي الكبير تشير إلى اختيار المستخدم
- إعادة استخدام طريقة
sendMessage
الحالية للتعامل مع الإشعار
يساعد هذا الأسلوب في تجنُّب التكرار من خلال الاستفادة من البنية الأساسية الحالية لمعالجة الرسائل.
تعديل التطبيق الرئيسي لربط إشعارات اختيار الألوان
الآن، عدِّل ملف lib/main.dart
لتمرير وظيفة إشعار اختيار اللون إلى الشاشة الرئيسية:
lib/main.dart
import 'package:colorist_ui/colorist_ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'providers/gemini.dart';
import 'services/gemini_chat_service.dart';
void main() async {
runApp(ProviderScope(child: MainApp()));
}
class MainApp extends ConsumerWidget {
const MainApp({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final model = ref.watch(geminiModelProvider);
final conversationState = ref.watch(conversationStateProvider);
return MaterialApp(
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
),
home: model.when(
data: (data) => MainScreen(
conversationState: conversationState,
notifyColorSelection: (color) { // Add from here...
ref.read(geminiChatServiceProvider).notifyColorSelection(color);
}, // To here.
sendMessage: (text) {
ref.read(geminiChatServiceProvider).sendMessage(text);
},
),
loading: () => LoadingScreen(message: 'Initializing Gemini Model'),
error: (err, st) => ErrorScreen(error: err),
),
);
}
}
التغيير الرئيسي هو إضافة معاودة الاتصال notifyColorSelection
، ما يربط حدث واجهة المستخدم (اختيار لون من السجلّ) بنظام الإشعارات الخاص بنموذج اللغة الكبير.
تعديل طلب النظام
الآن، عليك تعديل طلب النظام لتوجيه النموذج اللغوي الكبير بشأن كيفية الاستجابة لإشعارات اختيار الألوان. عدِّل ملف assets/system_prompt.md
باتّباع الخطوات التالية:
assets/system_prompt.md
# Colorist System Prompt
You are a color expert assistant integrated into a desktop app called Colorist. Your job is to interpret natural language color descriptions and set the appropriate color values using a specialized tool.
## Your Capabilities
You are knowledgeable about colors, color theory, and how to translate natural language descriptions into specific RGB values. You have access to the following tool:
`set_color` - Sets the RGB values for the color display based on a description
## How to Respond to User Inputs
When users describe a color:
1. First, acknowledge their color description with a brief, friendly response
2. Interpret what RGB values would best represent that color description
3. Use the `set_color` tool to set those values (all values should be between 0.0 and 1.0)
4. After setting the color, provide a brief explanation of your interpretation
Example:
User: "I want a sunset orange"
You: "Sunset orange is a warm, vibrant color that captures the golden-red hues of the setting sun. It combines a strong red component with moderate orange tones."
[Then you would call the set_color tool with approximately: red=1.0, green=0.5, blue=0.25]
After the tool call: "I've set a warm orange with strong red, moderate green, and minimal blue components that is reminiscent of the sun low on the horizon."
## When Descriptions are Unclear
If a color description is ambiguous or unclear, please ask the user clarifying questions, one at a time.
## When Users Select Historical Colors
Sometimes, the user will manually select a color from the history panel. When this happens, you'll receive a notification about this selection that includes details about the color. Acknowledge this selection with a brief response that recognizes what they've done and comments on the selected color.
Example notification:
User: "User selected color from history: {red: 0.2, green: 0.5, blue: 0.8, hexCode: #3380CC}"
You: "I see you've selected an ocean blue from your history. This tranquil blue with a moderate intensity has a calming, professional quality to it. Would you like to explore similar shades or create a contrasting color?"
## Important Guidelines
- Always keep RGB values between 0.0 and 1.0
- Provide thoughtful, knowledgeable responses about colors
- When possible, include color psychology, associations, or interesting facts about colors
- Be conversational and engaging in your responses
- Focus on being helpful and accurate with your color interpretations
الإضافة الرئيسية هي قسم "عندما يختار المستخدمون ألوانًا سابقة"، الذي يتيح ما يلي:
- يشرح هذا الطلب مفهوم إشعارات اختيار السجلّ للنموذج اللغوي الكبير
- تقدّم هذه السمة مثالاً على شكل هذه الإشعارات
- عرض مثال على ردّ مناسب
- تحديد التوقعات بشأن الإقرار بالاختيار والتعليق على اللون
يساعد ذلك النموذج اللغوي الكبير على فهم كيفية الردّ بشكل مناسب على هذه الرسائل الخاصة.
إنشاء رمز Riverpod
نفِّذ أمر أداة إنشاء التطبيقات لإنشاء رمز Riverpod المطلوب:
dart run build_runner build --delete-conflicting-outputs
تشغيل ميزة "مزامنة السياق مع النماذج اللغوية الكبيرة" واختبارها
تشغيل تطبيقك:
flutter run -d DEVICE
يتضمّن اختبار مزامنة السياق في النموذج اللغوي الكبير ما يلي:
- أولاً، أنشئ بعض الألوان من خلال وصفها في المحادثة
- "أريد رؤية لون أرجواني زاهٍ"
- "أريد لونًا أخضر داكنًا"
- "أريد لونًا أحمر زاهيًا"
- بعد ذلك، انقر على إحدى الصور المصغّرة للألوان في شريط السجلّ.
عليك مراعاة ما يلي:
- يظهر اللون المحدّد في الشاشة الرئيسية
- تظهر رسالة مستخدم في المحادثة تشير إلى اختيار اللون
- يردّ النموذج اللغوي الكبير من خلال الإقرار بالاختيار والتعليق على اللون
- يبدو التفاعل بأكمله طبيعيًا ومتماسكًا
يؤدي ذلك إلى إنشاء تجربة سلسة يكون فيها النموذج اللغوي الكبير على دراية بالرسائل المباشرة وتفاعلات واجهة المستخدم ويردّ عليها بشكل مناسب.
طريقة عمل ميزة "مزامنة السياق في النماذج اللغوية الكبيرة"
في ما يلي تفاصيل فنية حول طريقة عمل هذه المزامنة:
تدفّق البيانات
- إجراء المستخدم: ينقر المستخدم على لون في شريط السجلّ
- حدث واجهة المستخدم: يرصد التطبيق المصغّر
MainScreen
هذا الاختيار - تنفيذ معاودة الاتصال: يتم تشغيل معاودة الاتصال
notifyColorSelection
- إنشاء الرسالة: يتم إنشاء رسالة بتنسيق خاص تتضمّن بيانات الألوان
- معالجة النموذج اللغوي الكبير: يتم إرسال الرسالة إلى Gemini الذي يتعرّف على التنسيق
- الردّ حسب السياق: يردّ Gemini بشكل مناسب استنادًا إلى طلب النظام
- تعديل على واجهة المستخدم: يظهر الردّ في المحادثة، ما يوفّر تجربة متكاملة
تسلسل البيانات
أحد الجوانب الرئيسية لهذا الأسلوب هو كيفية تسلسل بيانات الألوان:
'User selected color from history: ${json.encode(color.toLLMContextMap())}'
تحوّل الطريقة toLLMContextMap()
(المقدَّمة من خلال الحزمة colorist_ui
) العنصر ColorData
إلى خريطة تتضمّن خصائص مفاتيح يمكن للنموذج اللغوي الكبير فهمها. يشمل ذلك عادةً ما يلي:
- قيم النموذج اللوني أحمر أخضر أزرق (RGB)
- تمثيل الرمز السداسي العشري
- أي اسم أو وصف مرتبط باللون
من خلال تنسيق هذه البيانات بشكل متّسق وتضمينها في الرسالة، تضمن حصول النموذج اللغوي الكبير على جميع المعلومات التي يحتاجها للردّ بشكل مناسب.
تطبيقات أوسع لميزة "مزامنة السياق" في النماذج اللغوية الكبيرة
يمكن استخدام نمط إعلام النموذج اللغوي الكبير بأحداث واجهة المستخدم في العديد من التطبيقات الأخرى غير اختيار الألوان، ومنها:
حالات الاستخدام الأخرى
- تغييرات الفلتر: إعلام النموذج اللغوي الكبير عندما يطبّق المستخدمون فلاتر على البيانات
- أحداث التنقّل: إعلام النموذج اللغوي الكبير عندما يتنقّل المستخدمون إلى أقسام مختلفة
- تغييرات في الاختيار: تعديل النموذج اللغوي الكبير عندما يختار المستخدمون عناصر من القوائم أو الجداول
- تعديلات على الإعدادات المفضّلة: إخبار النموذج اللغوي الكبير عندما يغيّر المستخدمون الإعدادات أو الإعدادات المفضّلة
- معالجة البيانات: إرسال إشعار إلى النموذج اللغوي الكبير عند إضافة المستخدمين للبيانات أو تعديلها أو حذفها
في كل حالة، يبقى النمط كما هو:
- رصد حدث واجهة المستخدم
- تسلسل البيانات ذات الصلة
- إرسال إشعار منسَّق خصيصًا إلى النموذج اللغوي الكبير
- توجيه النموذج اللغوي الكبير للردّ بشكل مناسب من خلال طلب النظام
أفضل الممارسات لمزامنة السياق في النماذج اللغوية الكبيرة
استنادًا إلى عملية التنفيذ، إليك بعض أفضل الممارسات لمزامنة السياق الفعّالة في النماذج اللغوية الكبيرة:
1. اعتماد تنسيق موحَّد
استخدِم تنسيقًا متسقًا للإشعارات حتى تتمكّن النماذج اللغوية الكبيرة من التعرّف عليها بسهولة:
"User [action] [object]: [structured data]"
2. سياق غني
تضمين تفاصيل كافية في الإشعارات لكي يستجيب النموذج اللغوي الكبير بذكاء بالنسبة إلى الألوان، يعني ذلك قيم الأحمر والأخضر والأزرق ورموز Hex وأي خصائص أخرى ذات صلة.
3- تعليمات واضحة
قدِّم تعليمات واضحة في طلب النظام حول كيفية التعامل مع الإشعارات، ويُفضّل أن تكون مصحوبة بأمثلة.
4. الدمج الطبيعي
صمِّم الإشعارات لتظهر بشكل طبيعي في المحادثة، وليس كمقاطعات فنية.
5- الإشعارات الانتقائية
إشعار النموذج اللغوي الكبير فقط بالإجراءات ذات الصلة بالمحادثة ليس من الضروري إبلاغ المستخدم بكل حدث في واجهة المستخدم.
تحديد المشاكل وحلّها
المشاكل المتعلّقة بالإشعارات
إذا لم يستجِب النموذج اللغوي الكبير بشكل صحيح لخيارات الألوان:
- التأكّد من أنّ تنسيق رسالة الإشعار يتطابق مع ما هو موضح في طلب النظام
- التأكّد من تسلسل بيانات الألوان بشكلٍ سليم
- التأكّد من أنّ طلب النظام يتضمّن تعليمات واضحة للتعامل مع عمليات التحديد
- البحث عن أي أخطاء في خدمة المحادثة عند إرسال الإشعارات
إدارة السياق
إذا بدا أنّ النموذج اللغوي الكبير يفقد السياق:
- التأكّد من الاحتفاظ بجلسة المحادثة بشكلٍ سليم
- التأكّد من أنّ حالات المحادثة تنتقل بشكلٍ سليم
- التأكّد من إرسال الإشعارات من خلال جلسة المحادثة نفسها
المشاكل العامة
بالنسبة إلى المشاكل العامة:
- فحص السجلات بحثًا عن أخطاء أو تحذيرات
- التأكّد من ربط Firebase AI Logic
- التحقّق من عدم تطابق الأنواع في مَعلمات الدالة
- التأكّد من أنّ جميع الرموز البرمجية التي تم إنشاؤها باستخدام Riverpod محدَّثة
المفاهيم الرئيسية التي تم تعلّمها
- إنشاء مزامنة سياق النموذج اللغوي الكبير بين واجهة المستخدم والنموذج اللغوي الكبير
- تحويل أحداث واجهة المستخدم إلى سياق مناسب للنماذج اللغوية الكبيرة
- توجيه سلوك النماذج اللغوية الكبيرة لأنماط التفاعل المختلفة
- توفير تجربة متسقة في التفاعلات التي تتضمّن الرسائل وتلك التي لا تتضمّنها
- تعزيز إدراك النماذج اللغوية الكبيرة لحالة التطبيق الأوسع
من خلال تنفيذ ميزة "مزامنة السياق في نماذج اللغات الكبيرة"، يمكنك إنشاء تجربة متكاملة حقًا، حيث يبدو نموذج اللغة الكبيرة وكأنّه مساعد مدرك ومتجاوب وليس مجرد أداة لإنشاء النصوص. يمكن تطبيق هذا النمط على عدد لا يحصى من التطبيقات الأخرى لإنشاء واجهات أكثر طبيعية وسهولة في الاستخدام مستندة إلى الذكاء الاصطناعي.
9- تهانينا!
لقد أكملت برنامج Colorist التدريبي بنجاح. 🎉
الميزات التي أنشأتها
لقد أنشأت تطبيق Flutter يعمل بكامل وظائفه ويتضمّن Google's Gemini API لتفسير أوصاف الألوان باللغة الطبيعية. يمكن لتطبيقك الآن إجراء ما يلي:
- معالجة أوصاف اللغة الطبيعية، مثل "برتقالي عند الغروب" أو "أزرق المحيط العميق"
- استخدام Gemini لترجمة هذه الأوصاف بذكاء إلى قيم النموذج اللوني أحمر أخضر أزرق
- عرض الألوان المفسّرة في الوقت الفعلي باستخدام الردود المتدفّقة
- التعامل مع تفاعلات المستخدمين من خلال كلّ من المحادثة وعناصر واجهة المستخدم
- الحفاظ على الوعي السياقي في مختلف طرق التفاعل
الخطوة التالية
بعد أن أتقنت أساسيات دمج Gemini مع Flutter، إليك بعض الطرق لمواصلة رحلتك:
تحسين تطبيق Colorist
- لوحات الألوان: إضافة وظيفة لإنشاء أنظمة ألوان متطابقة أو متكاملة
- الإدخال الصوتي: دمج ميزة التعرّف على الكلام لتوفير أوصاف لفظية للألوان
- إدارة السجلّ: إضافة خيارات لتسمية مجموعات الألوان وتنظيمها وتصديرها
- الطلبات المخصّصة: إنشاء واجهة للمستخدمين لتخصيص طلبات النظام
- الإحصاءات المتقدّمة: تتبُّع الأوصاف التي تحقّق أفضل أداء أو تسبّب صعوبات
استكشاف المزيد من ميزات Gemini
- المدخلات المتعدّدة الوسائط: إضافة مدخلات صور لاستخراج الألوان من الصور
- إنشاء المحتوى: يمكنك استخدام Gemini لإنشاء محتوى مرتبط بالألوان، مثل الأوصاف أو القصص
- تحسينات على ميزة "استدعاء الدوال": إنشاء عمليات تكامل أكثر تعقيدًا للأدوات باستخدام دوال متعددة
- إعدادات السلامة: استكشاف إعدادات السلامة المختلفة وتأثيرها في الردود
تطبيق هذه الأنماط على نطاقات أخرى
- تحليل المستندات: إنشاء تطبيقات يمكنها فهم المستندات وتحليلها
- المساعدة في الكتابة الإبداعية: إنشاء أدوات كتابة مزوّدة باقتراحات مستندة إلى نماذج اللغات الكبيرة
- أتمتة المهام: تصميم تطبيقات تحوّل اللغة الطبيعية إلى مهام مبرمَجة
- التطبيقات المستندة إلى المعرفة: إنشاء أنظمة خبرة في مجالات معيّنة
الموارد
في ما يلي بعض المراجع القيّمة لمواصلة التعلّم:
المستندات الرسمية
دورة ودليل حول كتابة الطلبات
المنتدى
Observable Flutter Agentic series
في الحلقة رقم 59، يستكشف "كريغ لابينز" و"أندرو بروغدن" هذا الدرس العملي، مع تسليط الضوء على الأجزاء المثيرة للاهتمام في عملية إنشاء التطبيق.
في الحلقة رقم 60، انضم إلى "كريغ" و"أندرو" مرة أخرى وهما يضيفان إمكانات جديدة إلى تطبيق الدرس العملي ويواجهان صعوبة في جعل النماذج اللغوية الكبيرة تنفّذ ما يُطلب منها.
في الحلقة رقم 61، ينضم كريس سيلز إلى "كريغ" لتقديم نظرة جديدة على تحليل عناوين الأخبار وإنشاء صور ذات صلة بها.
الملاحظات
يسرّنا معرفة رأيك بشأن هذا الدرس العملي. يُرجى تقديم الملاحظات من خلال:
نشكرك على إكمال هذا الدرس العملي، ونأمل أن تواصل استكشاف الإمكانات الرائعة التي تجمع بين Flutter والذكاء الاصطناعي.