Добавьте API AppFunctions в свое приложение.

В этом руководстве объясняется, как интегрировать API AppFunctions в ваше Android-приложение, реализовать логику для функции и проверить корректность работы интеграции.

Совместимость версий

Для данной реализации требуется, чтобы compileSdk вашего проекта был установлен на уровень API 36 или выше.

Вашему приложению не требуется проверять поддержку AppFunctions; это автоматически обрабатывается библиотекой AppFunctions Jetpack. AppFunctionManager возвращает экземпляр, если функция поддерживается, и возвращает null, если нет.

Зависимости

Добавьте необходимые зависимости библиотек в файл build.gradle.kts (или build.gradle) вашего модуля и настройте плагин KSP в главном модуле вашего приложения, как показано ниже:

dependencies {
  implementation("androidx.appfunctions:appfunctions:1.0.0-alpha10")
  // If this project uses any Kotlin source, use Kotlin Symbol Processing (KSP)
  // See Add the KSP plugin to your project
  ksp("androidx.appfunctions:appfunctions-compiler:1.0.0-alpha10")
}

Реализуйте логику AppFunctions.

Для реализации AppFunction в вашем Android-приложении создайте класс, реализующий конкретную логику AppFunction. Это включает в себя создание сериализуемых классов данных для параметров и ответов, а затем предоставление основной логики внутри метода функции.

Приведенный ниже код демонстрирует пример реализации создания задачи в приложении TODO , включая определение пользовательских параметров и типов ответов, а также логику основной функции с использованием репозитория.

@RequiresApi(36)
@AndroidEntryPoint
@AppFunctionServiceEntryPoint(
    serviceName = "TaskAppFunctionService",
    appFunctionXmlFileName = "task_app_function_service",
)
abstract class BaseTaskAppFunctionService : AppFunctionService() {
    @Inject internal lateinit var taskRepository: TaskRepository

    /**
     * Creates a task based on [createTaskParams].
     *
     * @param createTaskParams The parameter to describe how to create the task.
     */
    @AppFunction(isDescribedByKDoc = true)
    suspend fun createTask(
        createTaskParams: CreateTaskParams,
    ): Task = withContext(Dispatchers.IO) {
        // Developers can use predefined exceptions to let the agent know
        // why it failed.
        if (createTaskParams.title == null && createTaskParams.content == null) {
            throw AppFunctionInvalidArgumentException("Title or content should be non-null")
        }

        val id = taskRepository.createTask(
            createTaskParams.title,
            createTaskParams.content
        )

        return@withContext taskRepository
            .getTask(id)
            ?.toTask()
            ?: throw AppFunctionElementNotFoundException("Task not found for ID = $id")
    }

    // Maps internal TaskEntity
    private fun TaskEntity.toTask() = Task(id = id, title = title, content = description)
}

Основные моменты, касающиеся кода.

  • По умолчанию реализация AppFunction выполняется в потоке пользовательского интерфейса Android. Поэтому длительная операция должна выполнять следующие действия:
    • Объявите AppFunction как функцию приостановки.
    • Переключитесь на подходящий диспетчер сопрограмм, если операция может заблокировать поток.
  • Если isDescribedByKDoc установлен в true , описание функции или сериализуемое описание кодируется как часть AppFunctionMetadata , чтобы помочь агенту понять, как использовать AppFunction приложения.

Объявите службу AppFunction в вашем манифесте.

Зарегистрируйте сгенерированное KSP объявление службы и свойство app_metadata в манифесте вашего модуля, например, в файле src/main/AndroidManifest.xml . Компилятор KSP сгенерирует конкретный класс службы ( TaskAppFunctionService ), расширяющий ваш абстрактный класс точки входа, вместе с соответствующей XML-схемой в каталоге assets/ .

<service
    android:name="com.example.snippets.ai.TaskAppFunctionService"
    android:permission="android.permission.BIND_APP_FUNCTION_SERVICE"
    android:exported="true"
    tools:targetApi="36">
    <property
        android:name="android.app.appfunctions.schema"
        android:value="app_functions_schema.xsd" />
    <property
        android:name="android.app.appfunctions.v2"
        android:value="task_app_function_service.xml" />
    <intent-filter>
        <action android:name="android.app.appfunctions.AppFunctionService" />
    </intent-filter>
</service>
<property
    android:name="android.app.appfunctions.app_metadata"
    android:resource="@xml/app_metadata" />

Необязательно: Включение/выключение доступности функций приложения во время выполнения.

Используйте API AppFunctionManager для явного включения или отключения функций при ограничении доступа к вашим AppFunctions. Ограничение доступа может быть полезно, когда определенные функции вашего приложения недоступны всем пользователям. Динамически включая или отключая AppFunctions, интеллектуальная система точно знает, какие функции доступны пользователю в любой момент времени.

Для безопасного доступа к функциям приложения, требующим определенного состояния учетной записи, выполните два шага:

Шаг 1. Отключите эту функцию по умолчанию.

Чтобы функция не была доступна до проверки флага вашей функции, установите параметр isEnabled вашей аннотации @AppFunction в false .

@AppFunction(isEnabled = false, isDescribedByKDoc = true)
suspend fun createTask(
    createTaskParams: CreateTaskParams,
): Task = TODO()

Шаг 2. Динамическое включение функции во время выполнения.

Для каждого класса AppFunction компилятор генерирует соответствующий класс, содержащий константы идентификаторов функций (используя суффикс Ids ). Вы можете использовать эти сгенерированные константы идентификаторов вместе с методом setAppFunctionEnabled из класса AppFunctionManagerCompat , чтобы изменить состояние включения функции во время выполнения.

suspend fun onFeatureEnabled(context: Context) {
    try {
        AppFunctionManager.getInstance(context)
            ?.setAppFunctionEnabled(
                BaseTaskAppFunctionServiceIds.CREATE_TASK_ID,
                AppFunctionManager.APP_FUNCTION_STATE_ENABLED,
            )
    } catch (e: Exception) {
        // Handle exception: AppFunctions indexation may not be fully completed
        // upon initial app startup.
    }
}

suspend fun onFeatureDisabled(context: Context) {
    try {
        AppFunctionManager.getInstance(context)
            ?.setAppFunctionEnabled(
                BaseTaskAppFunctionServiceIds.CREATE_TASK_ID,
                AppFunctionManager.APP_FUNCTION_STATE_DISABLED,
            )
    } catch (e: Exception) {
        // Handle exception
    }
}

Рассмотрение типов функциональных возможностей, которые следует предоставить.

Безопасность всегда имеет первостепенное значение. При выборе того, какие возможности вашего приложения сделать доступными в качестве AppFunctions, важно помнить, что системные агенты могут обрабатывать запросы пользователей на сервере, чтобы использовать расширенные возможности LLM.

Для обеспечения удобного пользовательского интерфейса и предотвращения разглашения конфиденциальной информации мы рекомендуем следовать этим рекомендациям:

  • Функциональность, использующая естественный язык : Предоставляйте пользователю возможность решать задачи, которые ему будет проще описать в разговоре, чем с помощью ручной навигации по интерфейсу.
  • Ограничение доступа : Создавайте функции приложения, которые предоставляют агенту доступ только к тем данным и действиям, которые необходимы для выполнения конкретного запроса пользователя.
  • Неконфиденциальная информация : Передавайте только данные, которые не являются строго личными или конфиденциальными, или данные, на передачу которых пользователь дал явное согласие в контексте данного действия.
  • Однозначное подтверждение любого деструктивного действия : Будьте предельно осторожны с функциями, выполняющими деструктивные действия (например, удаление данных). Хотя агент может их вызывать, ваше приложение должно включать собственный шаг подтверждения и использовать четкий, недвусмысленный язык, описывающий намерения. Также полезно добавить несколько шагов подтверждения, чтобы действительно убедиться, что пользователь понимает, что от него требуется.

Проверьте интеграцию AppFunction.

Чтобы проверить правильность интеграции AppFunctions, можно использовать adb shell cmd app_function .

Используйте adb shell cmd app_function list-app-functions | grep --after-context 10 $myPackageName чтобы просмотреть подробную информацию о функциях приложения, предоставляемых вашим приложением.

Вы также можете выполнить AppFunction непосредственно из командной строки, используя его явный идентификатор ( "$enclosingClassName#$methodName" ):

adb shell "cmd app_function execute-app-function \
  --package com.example.android.appfunctions \
  --function 'com.example.android.appfunctions.BaseTaskAppFunctionService#createTask' \
  --parameters '{\"createTaskParams\": {\"title\": \"Buy milk\", \"content\": \"From grocery store\"}}'"

Чтобы оценить возможности Android MCP на практике и проверить сквозные рабочие процессы без каких-либо запросов, установите и запустите приложение AppFunctions testing agent для Android на своем устройстве.

Если вы проверяете интеграцию с помощью чат-помощников, таких как Gemini, в Android Studio, используйте навык разработки AppFunctions или предоставьте запрос, подобный следующему:

Execute `adb shell cmd app_function` to learn how the tool works, then act as a
chat agent aiming to invoke AppFunctions to fulfil user prompts for this app.
Rely on the AppFunction description as instructions.

Переход с более старых версий API

В версии 1.0.0-alpha10 в AppFunctions была введена архитектура @AppFunctionServiceEntryPoint реализуемая на этапе компиляции, которая объединяет зависимости библиотек и заменяет устаревшие поставщики конфигурации ( AppFunctionConfiguration.Provider ).

Если ваше приложение в настоящее время использует более раннюю версию AppFunctions (например, 1.0.0-alpha09), вы можете автоматизировать миграцию с помощью навыка агента AppFunctions в среде разработки с искусственным интеллектом, такой как Gemini в Android Studio. Навык содержит специальные правила миграции, которые помогают агенту объединить зависимости сборки, создать необходимую обертку сервиса @AppFunctionServiceEntryPoint , отделить параметры контекста и обновить объявления манифеста.

Для запуска автоматической миграции с помощью вашего ИИ-агента используйте, например, следующее приглашение:

Use the AppFunctions migration skill to upgrade my app's AppFunctions implementation from 1.0.0-alpha09 to the 1.0.0-alpha10 @AppFunctionServiceEntryPoint architecture.