借助工具,设备端智能体能够访问应用数据、与后端服务通信,并代表用户执行操作。
工具的工作原理
当用户提交提示时,AgentRunner 类会执行一个包含四个阶段的智能体循环:
- 观察:Gemini Nano 会根据已注册的工具声明(智能体的操作空间)分析用户的提示和当前对话上下文。
- 推理:模型确定是否需要使用应用内工具。如果需要工具,Gemini Nano 会发出结构化工具调用,其中包含函数名称和类型化实参。
- 执行:
AgentRunner实例会自动拦截此调用,并在应用中执行相应的 Kotlin 函数。 - 评估反馈:工具的输出会反馈给 Gemini Nano。模型会评估反馈,然后回答用户或调用其他工具,直到满足终止条件(完成目标或达到
maxTurns限制)。
使用 Android AppFunctions 的声明式工具
建议使用 Android AppFunctions (androidx.appfunctions) 以声明方式定义工具。AppFunctions 提供了一个标准化的平台模型,用于声明具有自动架构生成的类型化应用内功能。
1. 添加 Gradle 依赖项
在模块的 build.gradle.kts 文件中,将 AppFunctions 工件与 genai-agent 一起添加:
dependencies {
implementation("com.google.mlkit:genai-agent:1.0.0-alpha1")
implementation("androidx.appfunctions:appfunctions:1.0.0-alpha13")
ksp("androidx.appfunctions:appfunctions-compiler:1.0.0-alpha13")
}
2. 使用 @AppFunctionDeclaration 注解为工具方法添加注解
使用 @AppFunctionDeclaration(isDescribedByKDoc = true) 注解为公共工具方法添加注解。参数说明记录在标准 KDoc @param 标记中。
AppFunctions 要求返回值必须是基元类型(例如 String、Int 或 Boolean)或使用 @AppFunctionSerializable 注解进行注解的结构化数据类。
package com.example.ai.agent.tools
import androidx.appfunctions.AppFunctionDeclaration
import androidx.appfunctions.AppFunctionSerializable
import androidx.appfunctions.AppFunctionService
import androidx.appfunctions.AppFunctionServiceEntryPoint
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
/**
* Structured details for an order.
*/
@AppFunctionSerializable
data class OrderDetails(
val orderId: String,
val status: String,
val estimatedDelivery: String?,
val itemCount: Int,
)
/**
* Result returned when cancelling an order.
*/
@AppFunctionSerializable
data class CancelOrderResult(
val orderId: String,
val cancelled: Boolean,
val message: String,
)
@AppFunctionServiceEntryPoint(
serviceName = "OrderAppFunctionService",
appFunctionXmlFileName = "order_app_functions",
)
abstract class OrderTools : AppFunctionService() {
private val orderRepository = OrderRepository()
/**
* Fetches details and delivery status for a customer order by ID.
*
* @param orderId The alphanumeric order identifier (for example, ORD-8492)
*/
@AppFunctionDeclaration(isDescribedByKDoc = true)
suspend fun fetch_order_details(
orderId: String
): OrderDetails = withContext(Dispatchers.IO) {
val order = orderRepository.findOrder(orderId)
if (order != null) {
OrderDetails(
orderId = order.id,
status = order.status,
estimatedDelivery = order.deliveryEstimate,
itemCount = order.items.size,
)
} else {
OrderDetails(
orderId = orderId,
status = "NOT_FOUND",
estimatedDelivery = null,
itemCount = 0,
)
}
}
/**
* Requests a cancellation or return for an active order.
*
* @param orderId The order identifier to cancel
* @param reason The reason provided by the user for cancellation
*/
@AppFunctionDeclaration(isDescribedByKDoc = true)
suspend fun cancel_order(
orderId: String,
reason: String? = null
): CancelOrderResult = withContext(Dispatchers.IO) {
val success = orderRepository.cancelOrder(orderId, reason)
CancelOrderResult(
orderId = orderId,
cancelled = success,
message = if (success) {
"Order cancellation processed."
} else {
"Order cannot be cancelled at this stage."
}
)
}
}
在应用的 AndroidManifest.xml 文件中注册生成的服务:
<service
android:name=".OrderAppFunctionService"
android:permission="android.permission.BIND_APP_FUNCTION_SERVICE"
android:exported="true">
<property
android:name="android.app.appfunctions.schema"
android:value="app_functions_schema.xsd" />
<property
android:name="android.app.appfunctions.v2"
android:value="order_app_functions.xml" />
<intent-filter>
<action android:name="android.app.appfunctions.AppFunctionService" />
</intent-filter>
</service>
3. 使用 ACCESS_LEVEL_SELF 常量限制调用方访问权限
AppFunctions 无需对系统或外部助理公开。 在 Android 中,访问权限控制通过调用方访问权限级别进行管理,而不是通过手动调用方身份检查进行管理。
默认情况下(使用 ACCESS_LEVEL_ANDROID_TRUSTED 常量),AppFunction 可由持有 EXECUTE_APP_FUNCTIONS 系统权限且经过平台认证的设备端助理发现和执行。不过,如果您要构建仅供应用内部代理使用的功能(例如检查用户购物车、访问本地令牌或执行应用内操作),则可以使用 ACCESS_LEVEL_SELF 常量将访问权限限制为仅限您自己的应用:
import androidx.appfunctions.AppFunctionAccessLevel
import androidx.appfunctions.AppFunctionDeclaration
import androidx.appfunctions.AppFunctionService
import androidx.appfunctions.AppFunctionServiceEntryPoint
import androidx.appfunctions.ExperimentalAppFunctionsApi
import androidx.appfunctions.metadata.AppFunctionMetadata
@AppFunctionServiceEntryPoint(
serviceName = "OrderAppFunctionService",
appFunctionXmlFileName = "order_app_functions",
)
abstract class OrderTools : AppFunctionService() {
private val orderRepository = OrderRepository()
/**
* Cancels an order. Restricted exclusively to this app's internal agent.
*/
@OptIn(ExperimentalAppFunctionsApi::class)
@AppFunctionDeclaration(isDescribedByKDoc = true)
@AppFunctionAccessLevel(
level = AppFunctionMetadata.ACCESS_LEVEL_SELF,
isCompatEnforcementEnabled = true,
)
suspend fun cancel_order(orderId: String): CancelOrderResult {
// Only your app's internal agent can invoke this function
val success = orderRepository.cancelOrder(orderId)
return CancelOrderResult(
orderId = orderId,
cancelled = success,
message = if (success) "Cancelled." else "Failed."
)
}
}
该平台支持三种来电者访问权限级别:
ACCESS_LEVEL_SELF(100):严格限制为主机应用本身。外部助理、系统界面和其他应用无法通过 IPC 发现或调用该函数。对于敏感操作或仅应服务于应用内部代理的工具,请使用此级别。ACCESS_LEVEL_SYSTEM(200):仅限具有android.Manifest.permission#EXECUTE_APP_FUNCTIONS_SYSTEM权限的特权系统调用方(例如系统启动器或“最近用过”)和托管应用本身。ACCESS_LEVEL_ANDROID_TRUSTED(300,默认):向持有EXECUTE_APP_FUNCTIONS权限的平台认证设备端代理、系统界面和宿主应用开放。
4. 向智能体注册工具
您可以使用 enableAppFunctions() 方法自动向代理注册 AppFunction,也可以使用 AppFunctionAgentTools.discover() 方法以编程方式注册:
使用 enableAppFunctions() 方法自动发现
对于在模型调用时自动执行 AppFunctions 的简单应用:
import com.google.mlkit.genai.agent.Agent
import com.google.mlkit.genai.agent.AgentRunner
val agent = Agent.builder()
.setName("OrderAssistant")
.setInstructions("Assist users with order tracking.")
.enableAppFunctions(true) // Automatically discovers all AppFunctions in your app package
.setMaxTurns(10)
.build()
val runner = AgentRunner(agent)
(可选)调用 addAppFunctionIds("fetch_order_details", "cancel_order") 方法,将发现范围严格限定为特定的 AppFunction ID,而不是公开所有应用内函数。
使用 AppFunctionAgentTools.discover() 方法进行程序化发现
当您的应用需要检查发现的 AppFunction、对其进行过滤或在向代理注册之前对其进行修饰时:
import com.google.mlkit.genai.agent.Agent
import com.google.mlkit.genai.agent.AgentRunner
import com.google.mlkit.genai.agent.AppFunctionAgentTools
val discoveredTools = AppFunctionAgentTools.discover(
enabledAppFunctionIds = listOf("fetch_order_details", "cancel_order") // Optional ID filter
)
val agent = Agent.builder()
.setName("OrderAssistant")
.setInstructions("Assist users with order tracking.")
.addTools(discoveredTools)
.build()
val runner = AgentRunner(agent)
修饰器模式和人机协同
对于敏感操作(例如取消订单、付款或删除用户数据),应通过要求用户确认来保持人机协同 (HITL)。添加到 Agent 类的所有工具都源自 Android AppFunctions,并且 AgentTool 类提供了一个委托构造函数 AgentTool(delegate),以便您可以使用装饰器模式封装发现的 AppFunction 工具:
import com.google.mlkit.genai.agent.AgentTool
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?> {
val confirmed = onConfirm(name, args)
if (!confirmed) {
return mapOf(
"status" to "CANCELLED_BY_USER",
"message" to "Action was declined by the user."
)
}
return checkNotNull(delegate).execute(args)
}
}
工具设计最佳实践
- 限定操作空间:使用
addAppFunctionIds()方法和ACCESS_LEVEL_SELF常量,将智能体的操作空间(向智能体公开的工具和权限集)限制在刚好足以完成其任务的范围内。操作空间过大会使设备端模型更容易出错,并消耗上下文窗口 token。 - 在说明中明确表述:Gemini Nano 依赖于工具和参数说明来决定何时调用工具。清楚说明工具的用途,并提及预期的参数格式(例如日期或 ID)。
- 返回结构化错误映射:在发生预期故障时,返回描述性错误映射(例如
mapOf("error" to "Order not found"))。这样,Gemini Nano 就可以向用户得体地解释问题。如果工具抛出意外异常,运行时会自动捕获该异常,并将出错提示反映回模型,以便模型进行自我修正。 - 保持返回载荷的专注性:设备端模型的上下文窗口比服务器模型小。仅返回与回答用户查询相关的字段,而不是转储整个数据库行或原始网络响应。
- 在
Dispatchers.IO调度程序上运行 I/O:始终确保工具内的网络请求和 Room 数据库查询在Dispatchers.IO调度程序上执行,以防止阻塞协程调用方。
后续步骤
- 如需了解如何在应用旋转时保持对话上下文以及如何清理会话,请参阅管理会话和生命周期。
- 在流式传输事件和界面集成中处理流式传输文本 token 和工具执行状态。