AppFunctions API in Ihre App einbinden

In dieser Anleitung wird erläutert, wie Sie die AppFunctions API in Ihre Android-App einbinden, die Logik für eine Funktion implementieren und prüfen, ob die Einbindung korrekt funktioniert.

Versionskompatibilität

Für diese Implementierung muss compileSdk Ihres Projekts auf API-Level 36 oder höher festgelegt sein.

Ihre App muss nicht prüfen, ob AppFunctions unterstützt werden. Das wird automatisch in der AppFunctions Jetpack-Bibliothek erledigt. AppFunctionManager gibt eine Instanz zurück, wenn die Funktion unterstützt wird, andernfalls wird „null“ zurückgegeben.

Abhängigkeiten

Fügen Sie die erforderlichen Bibliotheksabhängigkeiten der Datei build.gradle.kts (oder „build.gradle“) Ihres Moduls hinzu und konfigurieren Sie das KSP-Plug-in im App-Modul der obersten Ebene wie unten gezeigt:

# Add this to your app module at the top level. For multi module applications,
# you only need to specify this once.
ksp {
  arg("appfunctions:aggregateAppFunctions", "true")
}

dependencies {
  implementation("androidx.appfunctions:appfunctions:1.0.0-alpha09")
  implementation("androidx.appfunctions:appfunctions-service:1.0.0-alpha09")
  // 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-alpha09")
}

AppFunctions-Logik implementieren

Wenn Sie eine AppFunction für Ihre Android-App implementieren möchten, erstellen Sie eine Klasse, die die spezifische AppFunctions-Logik implementiert. Dazu müssen serialisierbare Datenklassen für Parameter und Antworten erstellt und dann die Kernlogik in der Funktionsmethode bereitgestellt werden.

Der folgende Code zeigt eine Beispielimplementierung zum Erstellen einer Aufgabe in der TODO-App, einschließlich der Definition benutzerdefinierter Parameter und Antwort typen sowie der Hauptfunktionslogik mithilfe eines Repositorys.

package com.example.android.appfunctions


import androidx.appfunctions.AppFunctionSerializable
import androidx.appfunctions.AppFunctionContext
import androidx.appfunctions.AppFunctionElementNotFoundException
import androidx.appfunctions.AppFunctionInvalidArgumentException
import androidx.appfunctions.service.AppFunction
import javax.inject.Inject
...

// Developers can provide additional parameters in the constructor if needed.
// This requires a custom factory setup in the next step.
class TaskFunctions @Inject constructor(
  private val taskRepository: TaskRepository
) {
  /** The parameter to create the task. */
  @AppFunctionSerializable(isDescribedByKDoc = true)
  data class CreateTaskParams(
    /** The title of the task. */
    val title: String,
    /** The content of the task. */
     val content: String
  )

  /** The user-created task. */
  @AppFunctionSerializable(isDescribedByKDoc = true)
  data class Task(
    /** The ID of the task. */
    val id: String,
    /** The title of the task. */
    val title: String,
    /** The content of the task. */
    val content: String
  )

  /**
   * Creates a task based on [createTaskParams].
   *
   * @param createTaskParams The parameter to describe how to create the task.
   */
  @AppFunction(isDescribedByKDoc = true)
  suspend fun createTask(
    appFunctionContext: AppFunctionContext,
    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 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)
}

Wichtige Informationen zum Code

  • Standardmäßig wird eine AppFunction-Implementierung im Android-UI-Thread ausgeführt. Bei einem Vorgang mit langer Ausführungszeit sollte daher Folgendes berücksichtigt werden:
    • Deklarieren Sie die AppFunction als Suspend-Funktion.
    • Wechseln Sie zu einem geeigneten Coroutine-Dispatcher, wenn der Vorgang den Thread blockieren könnte.
  • Wenn isDescribedByKDoc auf true gesetzt ist, wird die Funktionsbeschreibung oder die serialisierbare Beschreibung als Teil von AppFunctionMetadata codiert, damit der Agent weiß, wie die AppFunction der App verwendet wird.

Optional: Hilt verwenden, um eine benutzerdefinierte AppFunction-Factory bereitzustellen

Wenn die AppFunction-Implementierungsklasse in ihrem Konstruktor Abhängigkeiten erfordert (wie TaskRepository im vorherigen Beispiel), müssen Sie eine benutzerdefinierte Factory bereitstellen, damit das System weiß, wie sie instanziiert wird. Dieser Schritt ist optional und nur erforderlich, wenn die Funktionsklasse Konstruktorparameter hat. In diesem Beispiel wird gezeigt, wie Sie eine benutzerdefinierte AppFunctionFactory erstellen und in Ihrer Application-Klasse konfigurieren. Dabei wird Hilt für die Abhängigkeitsinjektion verwendet.

import android.app.Application
import androidx.appfunctions.service.AppFunctionConfiguration
import com.example.android.appfunctions.TaskFunctions
import dagger.hilt.android.HiltAndroidApp
import javax.inject.Inject

@HiltAndroidApp
class TodoApplication : Application(), AppFunctionConfiguration.Provider {
  @Inject lateinit var taskFunctions: TaskFunctions
  override fun onCreate() {
    super.onCreate()
  }
  // This shows how AppFunctions works with Hilt.
  override val appFunctionConfiguration: AppFunctionConfiguration
    get() =
      AppFunctionConfiguration.Builder()
        .addEnclosingClassFactory(TaskFunctions::class.java) { taskFunctions }
        .build()
}

Optional: AppFunction-Verfügbarkeit zur Laufzeit umschalten

Verwenden Sie die AppFunctionManager API, um Funktionen explizit zu aktivieren oder zu deaktivieren, wenn Sie Ihre AppFunctions einschränken. Das kann nützlich sein, wenn bestimmte Funktionen Ihrer App nicht für alle Nutzer verfügbar sind. Durch das dynamische Aktivieren oder Deaktivieren von AppFunctions weiß das KI-System genau, welche Funktionen für den Nutzer zu einem bestimmten Zeitpunkt verfügbar sind.

So schränken Sie AppFunctions, die einen bestimmten Kontostatus erfordern, sicher ein:

Schritt 1: Funktion standardmäßig deaktivieren

Damit die Funktion nicht zugänglich ist, bevor das Funktions-Flag überprüft wurde, setzen Sie den Parameter isEnabled Ihrer @AppFunction-Annotation auf false.

@AppFunction(isEnabled = false, isDescribedByKDoc = true)
suspend fun createTask(...) { ... }

Schritt 2: Funktion zur Laufzeit dynamisch aktivieren

Für jede AppFunction-Klasse generiert der Compiler eine entsprechende Klasse mit Konstanten für die Funktions-ID (mit dem Suffix Ids). Sie können diese generierten ID-Konstanten zusammen mit der Methode setAppFunctionEnabled aus AppFunctionManagerCompat verwenden, um den aktivierten Status einer Funktion zur Laufzeit zu ändern.

import androidx.appfunctions.AppFunctionManager
// Assuming there is a hook API to observe user state or feature flags
suspend fun onFeatureEnabled() {
    try {
        AppFunctionManager.getInstance(context)
            .setAppFunctionEnabled(
                // Function ID is generated for developer to get
                TaskFunctionsIds.CREATE_TASK_ID,
                AppFunctionManagerCompat.APP_FUNCTION_STATE_ENABLED,
            )
    } catch (e: Exception) {
        // Handle exception: AppFunctions indexation may not be fully completed
        // upon initial app startup.
    }
}

suspend fun onFeatureDisabled() {
    AppFunctionManagerCompat.getInstance(context)
        .setAppFunctionEnabled(
            TaskFunctionsIds.CREATE_TASK_ID,
            AppFunctionManagerCompat.APP_FUNCTION_STATE_DISABLED,
        )
}

Überlegungen zu den Arten von Funktionen, die verfügbar gemacht werden sollen

Sicherheit hat immer höchste Priorität. Wenn Sie auswählen, welche Funktionen Ihrer App als AppFunctions verfügbar gemacht werden sollen, müssen Sie bedenken, dass System-Agents Nutzeranfragen auf dem Server verarbeiten können, um erweiterte LLM-Funktionen zu nutzen.

Um eine optimale Nutzererfahrung zu bieten und gleichzeitig die Offenlegung sensibler Informationen zu vermeiden, empfehlen wir Ihnen, diese Richtlinien zu befolgen:

  • Funktionen, die von natürlicher Sprache profitieren: Stellen Sie Aufgaben zur Verfügung, die ein Nutzer in einem Gespräch einfacher ausdrücken kann als durch manuelle UI-Navigation.
  • Eingeschränkter Zugriff: Erstellen Sie AppFunctions, die dem Agent nur Zugriff auf Daten und Aktionen gewähren, die erforderlich sind, um die spezifische Anfrage des Nutzers zu erfüllen.
  • Nicht sensible Informationen: Geben Sie nur Daten weiter, die nicht hochgradig personenbezogen oder vertraulich sind, oder Daten, deren Weitergabe der Nutzer im Rahmen der Aktion ausdrücklich zustimmt.
  • Eindeutige Bestätigung für jede destruktive Aktion: Seien Sie äußerst vorsichtig bei Funktionen, die destruktive Aktionen ausführen (z. B. Daten löschen). Der Agent kann sie zwar aufrufen, aber Ihre App sollte einen eigenen Bestätigungsschritt enthalten und eine klare, eindeutige Sprache verwenden, um die Absichten zu beschreiben. Es ist auch hilfreich, mehr als einen Bestätigungsschritt hinzuzufügen, um wirklich sicherzustellen, dass der Nutzer weiß, was er tun soll.

AppFunction-Einbindung prüfen

Mit adb shell cmd app_function können Sie prüfen, ob Sie AppFunctions korrekt eingebunden haben.

Verwenden Sie adb shell cmd app_function list-app-functions | grep --after-context 10 $myPackageName, um Details zu den AppFunctions zu sehen, die Ihre App bereitstellt.

Geben Sie in Gemini in Android Studio oder anderen Agents Ihrer Wahl eine Aufforderung wie die folgende ein.

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.