AppFunctions adalah padanan praktis dari server MCP di Android. Alat ini memungkinkan Anda membagikan bagian logika aplikasi Anda kepada agen dan asisten AI.
Agen menggunakan deskripsi untuk memilih AppFunction yang memenuhi permintaan. Proses pemilihan ini berfungsi seperti penemuan alat MCP tradisional, tempat aplikasi AI menggunakan deskripsi alat dan server MCP untuk memandu pilihannya. Aplikasi Anda bertindak sebagai server MCP Android, dan setiap AppFunction bertindak sebagai salah satu alatnya. Berikan deskripsi berikut:
- Deskripsi aplikasi (server MCP Android). Deskripsi aplikasi Anda adalah
titik entri pertama bagi agen untuk memahami fungsi aplikasi Anda. Untuk membantu
agen menemukan aplikasi Anda, mulailah dengan domain utamanya dan cantumkan secara ringkas
tugas yang dapat diselesaikan oleh AppFunctions.
- Tulis sendiri deskripsi aplikasi, dalam file XML metadata aplikasi Anda.
- Gunakan deskripsi aplikasi untuk menjelaskan hal yang tidak dapat dijelaskan oleh deskripsi fungsi individual, seperti urutan pemanggilan fungsi.
- Deskripsi AppFunction (alat). Setiap deskripsi fungsi menjelaskan perilaku satu AppFunction. Deskripsi fungsi umumnya disusun berdasarkan kata kerja, seperti update, create, atau book, dan resource, seperti settings, channel, atau room.
Panduan deskripsi fungsi
Agen mengandalkan deskripsi fungsi untuk memutuskan fungsi mana yang sesuai dengan permintaan dan cara memanggilnya.
Menulis deskripsi AppFunction yang ringkas dan tidak ambigu
Semakin tidak ambigu deskripsinya, semakin andal agen dapat memilih fungsi yang tepat untuk permintaan. Tabel berikut mencantumkan hal yang harus dilakukan dan yang harus dihindari dalam deskripsi fungsi:
| Anjuran | Larangan |
|---|---|
| Deskripsikan tujuan fungsi dan hasil dari menjalankannya, dalam bahasa yang jelas dan netral. | Jangan gunakan deskripsi sebagai perintah. Perintah seperti "Anda HARUS mengatakan" dan "Anda harus meminta pengguna untuk" menginstruksikan agen cara berperilaku, bukan menjelaskan fungsi. |
| Letakkan informasi penting di bagian awal deskripsi, dan buat deskripsi tetap ringkas. | Jangan mengisi deskripsi dengan kata-kata pengisi, seperti "Fungsi ini digunakan untuk". Kata pengisi membuat deskripsi lebih panjang tanpa membuatnya lebih jelas. |
| Mencerminkan secara akurat fungsi kode, termasuk parameter dan output-nya. Misalnya, fungsi yang dideskripsikan sebagai hanya baca tidak boleh menulis atau menghapus data. | Jangan gunakan nama yang menyesatkan, seperti fungsi bernama
fetchData yang menghapus data pengguna. |
| Tulis deskripsi fungsi dalam format KDoc, termasuk parameter dan jenis nilai yang ditampilkan. Ikuti panduan gaya Kotlin untuk penamaan, dan buat deskripsi yang dapat dipahami tanpa pengetahuan tentang implementasi. | Jangan gunakan huruf besar atau karakter khusus yang berlebihan untuk menarik
perhatian ke bagian deskripsi, seperti **CRITICAL**
atau VERY IMPORTANT. |
| Gunakan nama yang dapat dibaca manusia untuk AppFunction. | |
| Jelaskan secara jelas dan langsung fungsi setiap AppFunction. | Jangan memberikan petunjuk prosedural yang melibatkan fungsi lain, seperti "Telusuri kontak terlebih dahulu". Agen menggunakan deskripsi aplikasi Anda untuk merencanakan panggilan di seluruh fungsi, jadi jelaskan urutan tersebut di sana. |
| Deskripsikan apa yang dapat diharapkan agen dari fungsi tersebut, dengan bahasa seperti "Jika Anda memiliki X, maka Y". | |
| Asumsikan bahwa agen memiliki petunjuknya sendiri untuk menggunakan AppFunctions secara umum. Deskripsi fungsi Anda menjelaskan fungsi yang dijalankan, dan petunjuk agen mencakup cara menggunakannya. | Jangan mencoba mengganti atau menyematkan petunjuk tingkat agen dalam deskripsi fungsi. |
Menulis dengan mempertimbangkan enkapsulasi
Pastikan setiap deskripsi fungsi bersifat mandiri, sehingga dapat memandu agen dalam menggunakan fungsi tersebut dan tidak ada fungsi lain. Tabel berikut mencantumkan panduan enkapsulasi:
| Anjuran | Larangan |
|---|---|
| Tulis deskripsi fungsi yang spesifik dan terfokus, dengan satu tujuan yang jelas. Misalnya, "membuat, menemukan, dan menghapus tugas pribadi". | Jangan menulis deskripsi yang terlalu luas, yang dapat menyebabkan agen memanggil fungsi Anda pada waktu yang tidak tepat atau karena alasan yang salah. |
| Jangan membuat AppFunction multiguna, atau mengekspos banyak AppFunction untuk kasus penggunaan yang sama. Agen lebih cenderung memilih fungsi yang salah jika beberapa fungsi memiliki intent yang serupa. | |
| Jangan merujuk atau mencoba memengaruhi perilaku AppFunction lain di aplikasi Anda atau di aplikasi lain. Hal ini dapat menimbulkan dependensi dan efek samping yang tidak diinginkan. |
Tindakan yang diizinkan dan berbagi data
Ikuti panduan ini untuk melindungi privasi dan keamanan pengguna saat agen menggunakan fungsi aplikasi Anda:
| Anjuran | Larangan |
|---|---|
| Hanya lakukan tugas yang aman dan tidak merusak yang biasanya dilakukan pengguna di aplikasi Anda, dengan agen dapat memberikan parameter yang jelas dari permintaan pengguna—misalnya, membuat tugas dengan judul atau menelusuri catatan. | |
| Hanya bagikan informasi yang tidak terlalu pribadi atau rahasia, atau data yang secara eksplisit diizinkan oleh pengguna untuk dibagikan untuk tindakan tersebut. | Jangan beri agen akses luas ke data pengguna atau resource sistem. |
| Jangan mendesain AppFunction yang memerlukan atau menampilkan sandi, detail keuangan, kunci pribadi, atau informasi pribadi yang sangat sensitif lainnya. | |
| Melakukan tindakan yang diminta pengguna, meskipun tindakan tersebut dijalankan nanti atau di latar belakang—misalnya, mengirim pesan pada waktu yang dipilih pengguna. | Jangan melakukan tindakan yang tidak diminta pengguna, seperti tugas yang dimulai sendiri atau yang melampaui permintaan. |
| Fokus pada fungsi yang memanfaatkan bahasa alami, seperti tugas yang lebih mudah diungkapkan dalam percakapan daripada melalui navigasi UI—misalnya, "[Agen AI], bisakah Anda membantu saya menemukan beberapa waktu yang tersedia minggu depan di kalender saya, lalu menyarankan penerbangan ke Chicago pada salah satu hari tersebut?" | |
| Pada faktor bentuk selain ponsel, hormati privasi pengguna di perangkat bersama, seperti TV, Android Auto, dan Android Automotive OS. | Jangan mengungkapkan informasi atau melakukan tindakan yang tidak didukung oleh UI aplikasi Anda pada faktor bentuk tersebut. |
| Identifikasi tujuan fungsi Anda dengan jelas dan unik. | Jangan berpura-pura menjadi alat atau layanan lain. Nama dan deskripsi fungsi Anda tidak boleh mirip dengan alat lain, yang dapat menyebabkan agen salah memilih fungsi Anda. |
Desain parameter dan nilai return
Desain parameter dan nilai yang ditampilkan yang cermat akan mencegah ambiguitas dan mengurangi kemungkinan panggilan yang tidak valid. Ikuti konvensi berikut:
| Anjuran | Larangan |
|---|---|
| Tentukan parameter hanya untuk data yang diperlukan fungsi Anda. | Jangan meminta informasi yang tidak digunakan oleh fungsi. Hal ini meminimalkan eksposur data dan menyederhanakan penggunaan fungsi. |
Gunakan nama parameter yang mencerminkan input yang diharapkan, seperti
taskId, searchQuery, atau
recipientEmail. |
Jangan gunakan nama seperti full_context atau
conversation_history. Nama tersebut ambigu karena agen
menggunakan istilah ini untuk konteks dan histori percakapannya sendiri. Agen
dapat salah menafsirkannya dan meneruskan data dalam jumlah besar yang tidak diperlukan ke
fungsi Anda, sehingga menyebabkan pemindahan data yang tidak sah. |
Gunakan jenis yang didukung AppFunctions, bukan menentukan jenis Anda sendiri—misalnya, LocalDate, LocalTime, LocalDateTime, atau Instant untuk tanggal dan waktu, serta Uri untuk link. Library ini merepresentasikan jenis ini dengan cara yang sama di setiap aplikasi, sehingga agen mendapatkan format yang konsisten. Untuk mengetahui daftar lengkap
jenis yang didukung, lihat
AppFunctionSerializable. |
|
| Jika parameter tidak penting untuk tujuan fungsi, jadikan parameter tersebut opsional. Hal ini memberikan fleksibilitas kepada agen saat hanya memiliki informasi parsial dari pengguna. Untuk mengetahui informasi selengkapnya, lihat Parameter opsional. | |
| Jika AppFunction Anda mengupdate resource yang ada, pastikan setiap parameter dapat membedakan antara "setel ke null" dan "jangan ubah". Sebagai contoh, lihat Update pada resource yang ada. | |
Agar agen dapat membuka layar tertentu di aplikasi Anda, tampilkan
PendingIntent
untuk layar tersebut. Agen dapat meluncurkan PendingIntent apa adanya, tanpa membuat intent untuk aplikasi Anda. Buat dengan intent eksplisit dan FLAG_IMMUTABLE agar agen tidak dapat mengubah tujuannya. |
Parameter opsional
Hanya jenis nullable, primitif, daftar, dan array yang dapat bersifat opsional. Jika agen
menghilangkan parameter opsional, library akan meneruskan nilai default
jenis—null, nol, false, atau kosong—bukan default yang Anda deklarasikan.
Selesaikan default yang bermakna dalam isi fungsi, seperti dalam contoh berikut:
/**
* 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(),
)
}
Pembaruan pada resource yang ada
Contoh berikut menggunakan wrapper SetField nullable untuk setiap kolom yang dapat diperbarui oleh agen. SetField null berarti "jangan ubah", dan
SetField(null) berarti "tetapkan ke 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,
)
}
Penanganan error
Jika panggilan gagal, agen akan menerima pengecualian yang ditampilkan fungsi Anda, termasuk jenis dan pesannya. Ikuti konvensi ini agar agen dapat pulih dari kegagalan atau melaporkannya kepada pengguna:
| Anjuran | Larangan |
|---|---|
Lemparkan subclass
AppFunctionException
yang cocok dengan penyebabnya, dengan pesan yang menjelaskan masalah
dalam bahasa yang jelas—misalnya,
AppFunctionInvalidArgumentException
saat argumen tidak valid, atau
AppFunctionElementNotFoundException
saat item yang diminta tidak ada. Jenis ini memberi tahu agen jenis kegagalan yang terjadi, dan pesan memberikan detail yang diperlukan untuk pemulihan, seperti mengoreksi argumen atau memberi tahu pengguna. Contoh
updateNote di
Pembaruan pada resource yang ada menunjukkan pola
ini. |
Jangan biarkan pengecualian lain lolos dari fungsi Anda. Agen menerimanya sebagai AppFunctionAppUnknownException, yang tidak mengidentifikasi jenis kegagalan. |