mlkit-agent-api について
ML Kit GenAI Agent API を使ったオンデバイスエージェントのワークフロー実装を支援する Android 開発者向けスキルです。セッションのライフサイクル、AppFunctions によるツール呼び出し、単一ターン実行、エラー処理を扱います。
主な機能
- 01Agent と AgentRunner の初期化
- 02複数ターンのセッションと終了処理
- 03AppFunctions によるツール登録
- 04セッションを保持しない単一ターン実行
- 05GenAiException エラーの処理
活用シーン
- 01Android アプリでのオンデバイスチャット構築
- 02エージェントからアプリ内 AppFunctions を呼び出す
- 03会話履歴なしでテキストタスクを実行
- 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()
}
}
ライフサイクルとコルーチンに関する重要なルール
-
Contentオブジェクト:session.runAsyncとrunner.runSingleTurnAsyncにはContentオブジェクトが必要です。必ずContent.builder().text(userPrompt).build()を使用して作成してください。 -
textTokens拡張関数:textTokensはFlow<AgentEvent>に対する Kotlin 拡張関数です。必ずimport com.google.mlkit.genai.agent.textTokensを含めてください。 -
セッションのクリーンアップ:会話用 RAM を解放し、保留中のタスクをキャンセルするため、必ず
ViewModel.onClearedでsession.closeを呼び出してください。 -
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 がビジー状態であるか、バッテリーまたは温度の上限に達しています。