إرشادات الجودة الخاصة بـ AppFunctions

‫AppFunctions هي المكافئ العملي لخوادم MCP على Android. فهي تتيح لك مشاركة أجزاء من منطق تطبيقك مع وكلاء الذكاء الاصطناعي والمساعدين.

يستخدم الوكلاء الأوصاف لاختيار AppFunction التي تلبي الطلب. وتعمل عملية الاختيار هذه مثل عملية العثور على أدوات MCP التقليدية، حيث تستخدم تطبيقات الذكاء الاصطناعي أوصاف خوادم وأدوات MCP لتوجيه خياراتها. يعمل تطبيقك كخادم MCP لنظام Android، وتعمل كل دالة AppFunction كإحدى أدواته. قدِّم الأوصاف التالية:

  • وصف التطبيق (خادم MCP لنظام Android) وصف التطبيق هو نقطة الدخول الأولى للوكلاء لفهم ما يمكن لتطبيقك فعله. ولمساعدة الوكلاء في اكتشاف تطبيقك، ابدأ بالمجال الأساسي واذكر بإيجاز المهام التي تتيح AppFunctions إنجازها.
    • اكتب وصف التطبيق بنفسك في ملف XML للبيانات الوصفية للتطبيق.
    • استخدِم وصف التطبيق لوصف ما لا يمكن لأوصاف الدوال الفردية وصفه، مثل ترتيب استدعاء الدوال.
  • أوصاف AppFunction (الأدوات): يشرح كل وصف دالة سلوك دالة AppFunction واحدة. تتألف أوصاف الدوال بشكل عام من فعل، مثل تعديل أو إنشاء أو حجز، ومورد، مثل الإعدادات أو القناة أو الغرفة.

إرشادات حول وصف الدوال

تعتمد الوكلاء على أوصاف الدوال لتحديد الدالة التي تناسب الطلب وكيفية استدعائها.

كتابة أوصاف موجزة وواضحة لـ AppFunction

كلما كان الوصف أقل غموضًا، زادت موثوقية اختيار الوكيل للدالة المناسبة للطلب. يسرد الجدول التالي ما يجب فعله وما يجب تجنبه في أوصاف الوظائف:

الإجراءات المطلوبة الإجراءات غير المُوصى بها
صِف الغرض من الدالة ونتيجة تشغيلها بلغة واضحة ومحايدة. لا تستخدِم الأوصاف كطلبات. توجّه الأوامر، مثل "يجب أن تقول" و"يجب أن تطلب من المستخدم"، الوكيل إلى كيفية التصرف بدلاً من وصف الوظيفة.
ابدأ الأوصاف بالمعلومات الأساسية واجعلها موجزة. لا تملأ الأوصاف بعبارات حشو، مثل "تُستخدم هذه الدالة لـ". يجعل الحشو الوصف أطول دون جعله أكثر وضوحًا.
يجب أن تعكس بدقة ما يفعله الرمز البرمجي، بما في ذلك مَعلماته ونتائجه. على سبيل المثال، لا ينبغي أن تقوم دالة موصوفة بأنها للقراءة فقط بكتابة أو حذف البيانات. لا تستخدِم أسماء مضلِّلة، مثل دالة باسم fetchData تحذف بيانات المستخدمين.
اكتب أوصاف الدوال بتنسيق KDoc، بما في ذلك المَعلمات ونوع القيمة التي تم إرجاعها. اتّبِع دليل أسلوب Kotlin للتسمية، واجعل الأوصاف مفهومة بدون معرفة طريقة التنفيذ. لا تستخدِم الأحرف اللاتينية الكبيرة بشكل مفرط أو الرموز الخاصة لجذب الانتباه إلى أجزاء من الوصف، مثل **CRITICAL** أو VERY IMPORTANT.
استخدِم أسماء يمكن لشخص عادي قراءتها لـ AppFunctions.
يجب أن تكون واضحًا ومباشرًا بشأن وظيفة كل AppFunction. لا تقدِّم تعليمات إجرائية تتضمّن وظائف أخرى، مثل "ابحث عن جهة الاتصال أولاً". يستخدم الوكلاء وصف تطبيقك لتخطيط الطلبات على مستوى الوظائف، لذا يُرجى وصف هذا الترتيب هناك بدلاً من ذلك.
صِف ما يمكن أن يتوقّعه الوكيل من الدالة، باستخدام لغة مثل "إذا كان لديك X، فستحصل على Y".
لنفترض أنّ الوكلاء لديهم تعليماتهم الخاصة لاستخدام AppFunctions بشكل عام. يصف وصف الدالة ما تفعله الدالة، وتوضّح تعليمات الوكيل كيفية استخدامها. لا تحاول إلغاء التعليمات على مستوى الوكيل أو تضمينها في أوصاف الوظائف.

الكتابة مع وضع التغليف في الاعتبار

يجب أن يكون وصف كل دالة مستقلاً بذاته، بحيث يوجّه الوكيل في استخدام هذه الدالة دون غيرها. يسرد الجدول التالي إرشادات التغليف:

الإجراءات المطلوبة الإجراءات غير المُوصى بها
اكتب أوصافًا محدّدة وموجزة للدوال، مع تحديد غرض واحد واضح لكل دالة. على سبيل المثال، "إنشاء المهام الشخصية والعثور عليها وحذفها". لا تكتب وصفًا عامًا جدًا، فقد يؤدي ذلك إلى استدعاء الوكيل للدالة في أوقات غير مناسبة أو لأسباب خاطئة.
لا تنشئ دوال AppFunctions متعددة الأغراض، ولا تعرض العديد من دوال AppFunctions لحالة الاستخدام نفسها. من المرجّح أن يختار الوكلاء الدالة الخاطئة عندما يكون لعدة دوال أغراض متشابهة.
لا تُشِر إلى سلوك واجهات AppFunctions الأخرى في تطبيقك أو في التطبيقات الأخرى، ولا تحاول التأثير فيه. يمكن أن يؤدي ذلك إلى إنشاء تبعيات غير مقصودة وآثار جانبية.

الإجراءات المسموح بها ومشاركة البيانات

يُرجى اتّباع هذه الإرشادات لحماية خصوصية المستخدمين وأمانهم عندما يستخدم الوكلاء وظائف تطبيقك:

الإجراءات المطلوبة الإجراءات غير المُوصى بها
يجب تنفيذ المهام الآمنة وغير المدمّرة فقط التي يؤديها المستخدم عادةً في تطبيقك، حيث يمكن للوكيل تقديم مَعلمات واضحة من طلب المستخدم، مثل إنشاء مهمة بعنوان أو البحث عن ملاحظات.
مشاركة المعلومات غير الشخصية أو السرية للغاية فقط، أو البيانات التي يوافق المستخدم صراحةً على مشاركتها لتنفيذ الإجراء لا تمنح الوكيل إمكانية وصول واسعة النطاق إلى بيانات المستخدم أو موارد النظام.
لا تصمِّم دوال AppFunctions تتطلّب أو تعرض كلمات مرور أو تفاصيل مالية أو مفاتيح خاصة أو معلومات شخصية أخرى شديدة الحساسية.
تنفيذ الإجراءات التي طلبها المستخدم، حتى إذا تم تشغيلها لاحقًا أو في الخلفية، مثل إرسال رسالة في وقت يختاره المستخدم لا تنفِّذ إجراءات لم يطلبها المستخدم، مثل المهام التي تبدأ من تلقاء نفسها أو التي تتجاوز الطلب.
ركِّز على الوظائف التي تستفيد من اللغة الطبيعية، مثل المهام التي يسهل التعبير عنها في المحادثة مقارنةً بالتنقّل في واجهة المستخدم، على سبيل المثال، "[وكيل الذكاء الاصطناعي]، هل يمكنك مساعدتي في العثور على بعض الأوقات المتاحة في الأسبوع المقبل في تقويمي، ثم اقتراح رحلة جوية إلى شيكاغو في أحد هذه الأيام؟"
على أشكال الأجهزة الأخرى غير الهواتف، يجب احترام خصوصية المستخدم على الأجهزة المشتركة، مثل أجهزة التلفزيون وAndroid Auto ونظام التشغيل Android Automotive. لا تفصح عن معلومات أو تنفّذ إجراءات لا تتيحها واجهة مستخدم تطبيقك على شكل الجهاز هذا.
حدِّد الغرض من الدالة بوضوح وبشكل فريد. لا تتظاهر بأنّك أداة أو خدمة أخرى. يجب ألا يكون اسم الدالة ووصفها مشابهَين بشكل مربك للأدوات الأخرى، ما قد يؤدي إلى اختيار الوكيل للدالة عن طريق الخطأ.

تصميم المَعلمات وقيم الإرجاع

يمنع التصميم الدقيق للمَعلمات والقيم المعروضة الغموض ويقلل من احتمالية إجراء مكالمات غير صالحة. اتّبِع القواعد التالية:

الإجراءات المطلوبة الإجراءات غير المُوصى بها
حدِّد المَعلمات للبيانات التي تحتاجها الدالة فقط. لا تطلب معلومات لا تستخدمها الدالة. يؤدي ذلك إلى تقليل عرض البيانات وتبسيط استخدام الدالة.
استخدِم أسماء مَعلمات تعكس الإدخال المتوقّع، مثل taskId أو searchQuery أو recipientEmail. لا تستخدِم أسماء مثل full_context أو conversation_history. هذه الأسماء غامضة لأنّ الوكلاء يستخدمون هذه المصطلحات لسياقهم الخاص وسجلّ المحادثات. فقد يسيء الوكيل تفسيرها ويمرّر كمية كبيرة من البيانات غير الضرورية إلى الدالة، ما يؤدي إلى استخراج البيانات.
استخدِم الأنواع التي تتيحها AppFunctions بدلاً من تحديد الأنواع الخاصة بك، مثل LocalDate أو LocalTime أو LocalDateTime أو Instant للتواريخ والأوقات، وUri للروابط. وتمثّل المكتبة هذه الأنواع بالطريقة نفسها في كل تطبيق، ما يتيح للوكلاء الحصول على تنسيق متّسق. للاطّلاع على القائمة الكاملة للأنواع المتوافقة، يُرجى مراجعة AppFunctionSerializable.
إذا لم تكن المَعلمة ضرورية لغرض الدالة، اجعلها اختيارية. يمنح ذلك الوكيل مرونة عندما يكون لديه معلومات جزئية فقط من المستخدم. لمزيد من المعلومات، يُرجى الاطّلاع على المَعلمات الاختيارية.
إذا كانت AppFunction تعدّل الموارد الحالية، تأكَّد من أنّ كل مَعلمة يمكنها التمييز بين "الضبط على قيمة فارغة" و"عدم التغيير". للاطّلاع على مثال، يُرجى مراجعة التعديلات على الموارد الحالية.
للسماح للوكيل بفتح شاشة معيّنة في تطبيقك، يجب عرض PendingIntent لهذه الشاشة. يمكن للوكيل تشغيل PendingIntent كما هو، بدون إنشاء نية لتطبيقك. يمكنك إنشاؤه باستخدام نية صريحة وFLAG_IMMUTABLE حتى لا يتمكّن الوكيل من تغيير وجهته.

المعلمات الاختيارية

يمكن أن تكون الأنواع التي تقبل القيم الفارغة والأنواع الأساسية وأنواع القوائم والمصفوفات فقط اختيارية. عندما يحذف الوكيل مَعلمة اختيارية، تمرّر المكتبة القيمة التلقائية للنوع، وهي null أو صفر أو false أو فارغة، بدلاً من القيمة التلقائية التي تحدّدها. يمكنك حلّ القيم التلقائية المفيدة في نص الدالة، كما في المثال التالي:

/**
 * Creates a note.
 *
 * @param title The title of the note.
 * @param content The body text of the note.
 * @param attachments The files to attach to the note.
 * @param owner The ID of the user who owns the note.
 * @return The created note.
 */
@AppFunctionDeclaration(isDescribedByKDoc = true)
suspend fun createNote(
    title: String, // Required
    content: String? = null, // Optional
    attachments: List<Attachment> = emptyList(), // Optional
    owner: String? = null, // Optional
): Note = withContext(Dispatchers.IO) {
    noteRepository.createNote(
        title = title,
        // The library ignores declared default values and passes null for
        // omitted nullable parameters, so resolve meaningful defaults here.
        content = content ?: "",
        attachments = attachments,
        owner = owner ?: currentUserId(),
    )
}

تعديلات على المراجع الحالية

يستخدم المثال التالي برنامج تضمين SetField يقبل القيم الفارغة لكل حقل يمكن للوكيل تعديله. تعني القيمة الفارغة SetField "عدم التغيير"، وتعني القيمة SetField(null) "الضبط على قيمة فارغة":

/** A new value for a field of an existing item. */
@AppFunctionSerializable(isDescribedByKDoc = true)
data class SetField<T>(
    /** The new value of the field. */
    val value: T,
)

/**
 * Updates an existing note. Omitted fields keep their values.
 *
 * @param noteId The ID of the note to update.
 * @param title The new title of the note.
 * @param content The new body text of the note.
 * @return The updated note.
 */
@AppFunctionDeclaration(isDescribedByKDoc = true)
suspend fun updateNote(
    noteId: String,
    title: SetField<String>? = null,
    content: SetField<String?>? = null,
): Note = withContext(Dispatchers.IO) {
    val note = noteRepository.getNote(noteId)
        ?: throw AppFunctionElementNotFoundException(
            "No note found with ID $noteId"
        )
    noteRepository.updateNote(
        noteId = noteId,
        // A null SetField means "don't change", and SetField(null) means
        // "set to null", so don't use ?: for nullable fields.
        title = title?.value ?: note.title,
        content = if (content != null) content.value else note.content,
    )
}

معالجة الأخطاء

عند تعذُّر إجراء مكالمة، يتلقّى الوكيل الاستثناء الذي تعرضه الدالة، بما في ذلك نوعه ورسالته. اتّبِع هذه القواعد ليتمكّن الوكيل من التعافي من الخطأ أو إبلاغ المستخدم به:

الإجراءات المطلوبة الإجراءات غير المُوصى بها
يجب طرح الفئة الفرعية AppFunctionException التي تتطابق مع السبب، مع رسالة تصف المشكلة بلغة بسيطة، على سبيل المثال، AppFunctionInvalidArgumentException عندما تكون الوسيطة غير صالحة، أو AppFunctionElementNotFoundException عندما لا يكون العنصر المطلوب متوفّرًا. يخبر النوع الوكيل بنوع الخطأ الذي حدث، وتمنحه الرسالة التفاصيل التي يحتاجها للتعافي، مثل تصحيح وسيطة أو إخبار المستخدم. يوضّح مثال updateNote في تعديلات على الموارد الحالية هذا النمط. لا تسمح للاستثناءات الأخرى بالهروب من دالتك. يتلقّاها الوكيل على النحو التالي: AppFunctionAppUnknownException، وهي لا تحدّد نوع الخطأ.