mcpField
M
mlkit-agent-api

mlkit-agent-api

github.com/android開発10.0k47 回インストール2 日前に更新
リポジトリ

mlkit-agent-api について

ML Kit GenAI Agent API を使ったオンデバイスエージェントのワークフロー実装を支援する Android 開発者向けスキルです。セッションのライフサイクル、AppFunctions によるツール呼び出し、単一ターン実行、エラー処理を扱います。

主な機能

  1. 01Agent と AgentRunner の初期化
  2. 02複数ターンのセッションと終了処理
  3. 03AppFunctions によるツール登録
  4. 04セッションを保持しない単一ターン実行
  5. 05GenAiException エラーの処理

活用シーン

  1. 01Android アプリでのオンデバイスチャット構築
  2. 02エージェントからアプリ内 AppFunctions を呼び出す
  3. 03会話履歴なしでテキストタスクを実行
  4. 04モデルや AICore サービスの利用不可に対処

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/android/skills --skill mlkit-agent-api

インストール後、Claude Code で/mlkit-agent-apiを入力して読み込まれたか確認します。

同梱ファイルreferences/session_lifecycle.mdreferences/tools_and_functions.md

詳細

AICore Developer Preview を対象としています。このスキルでは、Android アプリでオンデバイスのエージェント型ワークフローを実行するための信頼できるパターンと、障害を防ぐためのルールを提供します。


1. 前提条件とセットアップ

Gradle の依存関係
// app/build.gradle.kts
dependencies {
    // Core ML Kit GenAI Agent API
    implementation("com.google.mlkit:genai-agent:1.0.0-alpha1")

    // Android AppFunctions dependency for on-device tool declarations
    implementation("androidx.appfunctions:appfunctions:1.0.0-alpha13")
}

重要

必ず、公式の SDK インターフェースである com.google.mlkit.genai.agent.*、androidx.appfunctions.*、 com.google.mlkit.genai.prompt.*、com.google.mlkit.genai.common.* を使用してください。 ローカルのプレースホルダーインターフェースやフォールバック用のモックは宣言しないでください。


2. エージェントとランナーの最小限の初期化

Agent.builder で Agent を設定し、実行を AgentRunner でラップします。

import com.google.mlkit.genai.agent.Agent
import com.google.mlkit.genai.agent.AgentRunner

val agent = Agent.builder()
    .setName("OnDeviceAssistant")
    .setInstructions("You are a helpful on-device assistant.")
    .build()

val runner = AgentRunner(agent)
  • Agent.builder は、ツールの無限ループを防ぐため、maxTurns = 10 を自動的にデフォルト値として設定します。
  • AgentRunner は、ツール選択の決定性とスキーマ準拠を確保するため、貪欲デコード(temperature = 0.0f)をデフォルトで使用します。
  • AgentRunner(agent) は、Android アプリでの AppFunction 実行に必要なアプリケーションコンテキストを MlKitContext を使って自動的に解決します。ヘッドレステストやカスタムのコンテキストスコープでは、明示的に AgentRunner(context, agent) を指定するオーバーロードを利用できます。

3. マルチターンチャットとライフサイクル(ViewModel)

AgentSession は会話履歴をメモリ内に保持し、Parcelable ではありません。画面の回転など、Android の構成変更後も会話履歴を保持するには、AndroidX の ViewModel 内に session を保持してください。

シングルターンの一時的なクエリを実行する場合、複数のセッションを同時に管理する場合、または RAM の明示的なクリーンアップが必要な場合は、セッションのライフサイクルのガイドラインに従ってください。

package com.example.app

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.google.mlkit.genai.agent.Agent
import com.google.mlkit.genai.agent.AgentRunner
import com.google.mlkit.genai.agent.AgentSession
import com.google.mlkit.genai.agent.textTokens
import com.google.mlkit.genai.common.GenAiException
import com.google.mlkit.genai.prompt.Content
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch

class ChatViewModel : ViewModel() {
    private val agent = Agent.builder()
        .setInstructions("You are a helpful on-device assistant.")
        .build()
    private val runner = AgentRunner(agent)
    private val session: AgentSession =
        runner.getOrCreateSession("chat_session")

    private val _uiState = MutableStateFlow<ChatUiState>(ChatUiState.Idle)
    val uiState: StateFlow<ChatUiState> = _uiState.asStateFlow()

    fun sendMessage(prompt: String) {
        viewModelScope.launch {
            _uiState.value = ChatUiState.Loading("")
            try {
                // Content.builder() が必要です。runAsync は Content を受け取ります
                val content = Content.builder().text(prompt).build()
                var fullResponse = ""
                session.runAsync(content)
                    // textTokens のインポートが必要な拡張関数です
                    .textTokens()
                    .collect { token ->
                        fullResponse += token
                        _uiState.value = ChatUiState.Loading(fullResponse)
                    }
                _uiState.value = ChatUiState.Success(fullResponse)
            } catch (e: GenAiException) {
                // AI エラーによるアプリのクラッシュを防ぐため、GenAiException をキャッチします
                val err = e.localizedMessage ?: "AI error (${e.errorCode})"
                _uiState.value = ChatUiState.Error(err)
            }
        }
    }

    override fun onCleared() {
        super.onCleared()
        // ViewModel の破棄時に会話セッションを RAM から解放します
        session.close()
    }
}
ライフサイクルとコルーチンに関する重要なルール
  1. Content オブジェクト:session.runAsync と runner.runSingleTurnAsync には Content オブジェクトが必要です。必ず Content.builder().text(userPrompt).build() を使用して作成してください。

  2. textTokens 拡張関数:textTokens は Flow<AgentEvent> に対する Kotlin 拡張関数です。必ず import com.google.mlkit.genai.agent.textTokens を含めてください。

  3. セッションのクリーンアップ:会話用 RAM を解放し、保留中のタスクをキャンセルするため、必ず ViewModel.onCleared で session.close を呼び出してください。

  4. AgentEvent ストリーム:textTokens を使わずに Flow<AgentEvent> を直接収集する場合は、必ず import com.google.mlkit.genai.agent.AgentEvent を含めてください。AgentEvent.TextMessage(text, isPartial)、AgentEvent.FunctionCall(name, args)、AgentEvent.FunctionResponse(name, response) に対して照合し、when (event) 式には必ず else -> {} 分岐を含めてください(AgentEvent は将来の互換性のため、網羅的ではありません)。

    import com.google.mlkit.genai.agent.AgentEvent

    session.runAsync(content).collect { event -> when (event) { is AgentEvent.FunctionCall -> { _uiState.update { it.copy(activeToolStatus = "ツールを開始しました:${event.name}") } } is AgentEvent.FunctionResponse -> { _uiState.update { it.copy(activeToolStatus = "ツールが完了しました:${event.name}") } } is AgentEvent.TextMessage -> { _uiState.update { it.copy(responseText = it.responseText + event.text) } } else -> {} // 必須:AgentEvent は将来の互換性のため、網羅的ではありません } }


4. ツール呼び出しのレシピ

Agent で使用するすべてのツールは、Android AppFunctions (@AppFunctionDeclaration)に由来し、宣言型の方法 (enableAppFunctions(true) または addAppFunctionIds(...))か、高度な検出方法 (AppFunctionAgentTools.discover(...))で登録されます。

プライベートなアプリ内ツールに対する呼び出し元のアクセスレベルを制限する場合や、検出した AppFunctions をヒューマン・イン・ザ・ループ(HITL)確認デコレーターでラップする場合は、ツールと関数を参照してください。

オプション A:宣言的な AppFunctions のパス

AndroidX AppFunctions を使って、アプリ内でツールを宣言的に定義します。

package com.example.app.tools

import androidx.appfunctions.AppFunctionDeclaration
import androidx.appfunctions.AppFunctionSerializable

@AppFunctionSerializable
data class CalendarEvents(val date: String, val events: List<String>)

class CalendarTools {
    /**
     * YYYY-MM-DD 形式で指定した日付の、予定されているカレンダーの予定を取得します。
     *
     * @param date YYYY-MM-DD 形式の日付文字列
     */
    @AppFunctionDeclaration(isDescribedByKDoc = true)
    fun fetch_calendar(
        date: String
    ): CalendarEvents {
        return CalendarEvents(
            date = date,
            events = listOf(
                "Team Standup at 10:00 AM",
                "Product Review at 2:00 PM"
            )
        )
    }
}

Agent.Builder で AppFunctions を有効にし、AgentRunner を初期化します。

import com.google.mlkit.genai.agent.Agent
import com.google.mlkit.genai.agent.AgentRunner

val agent = Agent.builder()
    .setName("CalendarAssistant")
    .setInstructions("You assist with calendar queries.")
    // アプリのパッケージ内にある AppFunctions を自動的に検出します
    .enableAppFunctions(true)
    // オプション:特定の関数 ID またはメソッド名に絞り込みます
    .addAppFunctionIds("fetch_calendar")
    .build()

val runner = AgentRunner(agent)
  • AppFunction の契約:ツールメソッドはドメインパラメーターを直接受け取ります(フレームワークのコンテキストパラメーターは不要です)。戻り値にはプリミティブ型または @AppFunctionSerializable データクラスを指定してください(AppFunctions では Map<String, Any?> はサポートされていません)。
  • ACCESS_LEVEL_SELF による呼び出し元の制限:AppFunctions を外部の呼び出し元に公開する必要はありません。アプリ内のエージェントだけに実行を厳密に制限し(外部のシステムアシスタントや IPC 呼び出し元をブロックする)、@OptIn(ExperimentalAppFunctionsApi::class) および @AppFunctionAccessLevel(level = AppFunctionMetadata.ACCESS_LEVEL_SELF, isCompatEnforcementEnabled = true) を付与します。
オプション B:高度な検出とデコレーターパターン

エージェントに登録する前に、検出された AppFunctions を調査、フィルタリング、またはデコレートする場合(AgentTool(delegate) を使ったヒューマン・イン・ザ・ループ確認ラッパーなど)に使用します。

import com.google.mlkit.genai.agent.Agent
import com.google.mlkit.genai.agent.AgentRunner
import com.google.mlkit.genai.agent.AgentTool
import com.google.mlkit.genai.agent.AppFunctionAgentTools

// ツールを検出します(関数 ID による絞り込みはオプションです)
val discoveredTools = AppFunctionAgentTools.discover(
    enabledAppFunctionIds = listOf("fetch_calendar")
)

// オプション:AgentTool(delegate) デコレーターで機密性の高いツールをラップします
class HitlConfirmationTool(
    delegate: AgentTool,
    private val onConfirm: suspend (
        toolName: String,
        args: Map<String, Any>
    ) -> Boolean
) : AgentTool(delegate) {
    override suspend fun execute(args: Map<String, Any>): Map<String, Any?> {
        if (!onConfirm(name, args)) {
            return mapOf("status" to "CANCELLED_BY_USER")
        }
        return checkNotNull(delegate).execute(args)
    }
}

val agent = Agent.builder()
    .setName("CalendarAssistant")
    .setInstructions("You assist with calendar queries.")
    .addTools(discoveredTools)
    .build()

val runner = AgentRunner(agent)

警告

相互排他:AppFunctions は、宣言的なパス(Agent.Builder で enableAppFunctions(true) を設定)または高度なパス(AppFunctionAgentTools.discover の結果を addTools に渡す)のいずれかを使って設定してください。両方を同時に使うと警告が発生し、ツールスキーマが重複して登録されます。

非推奨のアノテーション(@Tool と @Param)

com.google.mlkit.genai.schema.annotations.* のレガシーな @Tool および @Param アノテーションと、AgentTool.fromAnnotatedObject、Agent.Builder.addTools(annotatedObject: Any)、スタンドアロンの FunctionTool は、非推奨または削除されました。Agent に追加するすべてのツールは、Agent.Builder.enableAppFunctions(true) または AppFunctionAgentTools.discover を使用して、@androidx.appfunctions.AppFunctionDeclaration に由来する必要があります。


5. 一時的な単一ターン実行

会話履歴が不要な単発のタスク(テキストの要約、データの整形、単独の質問への回答など)では、runner.runSingleTurnAsync(content) を直接呼び出します。

import com.google.mlkit.genai.agent.AgentRunner
import com.google.mlkit.genai.agent.textTokens
import com.google.mlkit.genai.prompt.Content
import kotlinx.coroutines.flow.Flow

// セッションを永続化せず、一時的に単一ターンのクエリを実行します:
fun askQuickQuestion(runner: AgentRunner, question: String): Flow<String> {
    val content = Content.builder().text(question).build()
    return runner.runSingleTurnAsync(content).textTokens()
}
  • runner.runSingleTurnAsync(content) は、Flow の完了時に一時的なセッションを自動的に作成して破棄します。
  • クリーンアップの参考情報:
    • session.close:セッションのリソースを解放し、コルーチンをキャンセルします(ViewModel.onCleared で呼び出します)。
    • session.delete または runner.deleteSession(sessionId):ランナーからセッションを完全に削除します。

6. エラー処理と GenAiException のリファレンス

デバイス上での実行時に発生する障害(モデルが利用できない場合や、最大ターン数を超過した場合など)は、GenAiException として出力またはスローされます。

import com.google.mlkit.genai.common.GenAiException
主なエラーコード
  • GenAiException.ErrorCode.MAX_TURNS_EXCEEDED(-112):推論ループを完了する前に、エージェントが maxTurns(デフォルトは 10)を超過しました。
  • GenAiException.ErrorCode.NOT_AVAILABLE(8):Gemini Nano モデルまたは AICore サービスがこのデバイスで利用できません。
  • GenAiException.ErrorCode.BUSY(9):システムまたは AICore がビジー状態であるか、バッテリーまたは温度の上限に達しています。

コメント