使用函数调用

借助机器学习套件的直接模型访问功能,您可以使用函数调用,让 Gemini Nano 模型输出结构化函数执行请求(工具调用),而不是纯文本回答。借助函数调用,您可以将设备端生成式 AI 模型连接到外部工具、API 和本地应用功能。

主要功能

函数调用提供以下关键功能:

  • 单轮函数调用:Gemini Nano 预测一个或多个工具调用 (FunctionCall),您的应用在本地执行这些函数,流程完成,无需后续模型响应。
  • 多轮函数调用:Gemini Nano 预测一个或多个工具调用 (FunctionCall),您的应用在本地执行函数,并将结果传回 (FunctionResponsePart) 以检索更多工具调用或最终合成的文本回答。

实现函数调用时,您可以利用以下功能:

  • 程序化工具定义:使用机器学习套件 GenAI Schema API (com.google.mlkit:genai-schema) 在运行时构建动态 FunctionDeclaration 和 FunctionSchema 工具定义。
  • 结构化实参解析:直接从模型回答候选项中接收强类型实参值,以执行本地应用逻辑。

示例结果

函数调用支持单轮和多轮流程。在这两种情况下,模型都会预测一个或多个由函数名称和实参组成的工具调用。对于多轮对话流程,在收到应用的执行结果后,应用可以向模型发送另一个请求,其中包含完整的对话历史记录,以便获取更多工具调用或用户的最终文本回答。

执行流程 用户提示 模型输出
单轮 “将客厅灯的亮度调到 80%。” 工具调用:
turn_on_light(room="living room", brightness=80)
单轮 “打开‘相机’应用。” 工具调用:
open_app(appName="Camera")
多轮 “旧金山的天气怎么样?请用摄氏度回答。” 中间工具调用:
get_current_weather(location="San Francisco, CA", unit="celsius")

向用户显示的最终模型输出:
In SF it's currently 25 degrees Celsius.

前提条件

如需使用函数调用,您必须满足以下设备和账号要求:

如需确认您是否满足这些要求,请检查 AICore 应用中是否提供了 Gemini Nano 4 Fast 模型:打开 AICore 应用,点击选择模型下拉菜单,然后检查是否列出了 Gemini Nano 4 Fast。如果模型不存在,则表示设备支持或账号注册存在问题,函数调用将无法正常运行。

此外,您的项目还必须满足以下要求:

  • Kotlin 2.3.0 版或更高版本
  • 最低 SDK (minSdk) 版本 26 或更高版本

开始使用

以下是开始使用函数调用的步骤。

  1. 在项目级 settings.gradle.kts(或 settings.gradle)文件中,将 mavenCentral() 添加到 repositories {} 代码块。

    dependencyResolutionManagement {
        repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
        repositories {
            google()
            mavenCentral()
        }
    }
    
  2. 在模块级 build.gradle.kts(或 build.gradle 文件)中,添加 ML Kit Prompt API 和架构依赖项:

    Kotlin

    plugins {
        id("com.android.application")
        id("org.jetbrains.kotlin.android")
    }
    
    dependencies {
        // ML Kit GenAI Prompt and Schema SDKs
        implementation("com.google.mlkit:genai-prompt:1.0.0-beta5")
        implementation("com.google.mlkit:genai-schema:1.0.0-alpha2")
    }
    

    Groovy

    plugins {
        id 'com.android.application'
        id 'org.jetbrains.kotlin.android'
    }
    
    dependencies {
        // ML Kit GenAI Prompt and Schema SDKs
        implementation 'com.google.mlkit:genai-prompt:1.0.0-beta5'
        implementation 'com.google.mlkit:genai-schema:1.0.0-alpha2'
    }
    
  3. 在模块级 build.gradle(或 build.gradle.kts 文件)中,将 minSdk 版本设置为 26 或更高版本。

    Kotlin

    android {
        defaultConfig {
            // ...
            minSdk = 26
            // ...
        }
    }
    

    Groovy

    android {
        defaultConfig {
            // ...
            minSdk 26
            // ...
        }
    }
    

如何使用函数调用 API

如需实现函数调用,请执行以下步骤:

  1. 定义工具。
  2. 选择模型。
  3. 将工具附加到请求中。
  4. 处理工具调用(单轮或多轮)。

以下部分将通过一个示例来演示这些步骤。

定义工具

使用 com.google.mlkit.genai.schema.tools 软件包中的 FunctionDeclaration、FunctionSchema 和 FunctionType 以编程方式构建工具架构。(如需在编译时使用 Android 的 @AppFunction 注解以声明方式定义工具并自动执行,请参阅定义工具。)

例如,您可以定义一个面向操作的工具(例如打开智能灯,以便单轮执行):

import com.google.mlkit.genai.schema.tools.FunctionDeclaration
import com.google.mlkit.genai.schema.tools.FunctionSchema
import com.google.mlkit.genai.schema.tools.FunctionType

val turnOnLightTool = FunctionDeclaration(
    name = "turn_on_light",
    description = "Turn on the lights in a room and set the brightness",
    parameters = FunctionSchema(
        type = FunctionType.OBJECT,
        properties = mapOf(
            "room" to FunctionSchema(
                type = FunctionType.STRING,
                description = "The room name"
            ),
            "brightness" to FunctionSchema(
                type = FunctionType.INTEGER,
                description = "Brightness level from 0 to 100"
            )
        ),
        required = listOf("room")
    )
)

或者定义一个数据检索工具(例如,获取当前天气以进行多轮合成):

import com.google.mlkit.genai.schema.tools.FunctionDeclaration
import com.google.mlkit.genai.schema.tools.FunctionSchema
import com.google.mlkit.genai.schema.tools.FunctionType

val getWeatherTool = FunctionDeclaration(
    name = "get_current_weather",
    description = "Get the current weather for a location",
    parameters = FunctionSchema(
        type = FunctionType.OBJECT,
        properties = mapOf(
            "location" to FunctionSchema(
                type = FunctionType.STRING,
                description = "The city and state, e.g. San Francisco, CA"
            ),
            "unit" to FunctionSchema(
                type = FunctionType.STRING,
                description = "The temperature unit, either 'celsius' or 'fahrenheit'"
            )
        ),
        required = listOf("location")
    )
)

选择模型

函数调用需要 Gemini Nano 4。如需访问 Gemini Nano 4,请使用模型选择 API 将 GenerativeModel 实例配置为以 ModelReleaseStage.PREVIEW 和 ModelPreference.FAST 为目标。

import com.google.mlkit.genai.prompt.Generation
import com.google.mlkit.genai.prompt.ModelPreference
import com.google.mlkit.genai.prompt.ModelReleaseStage
import com.google.mlkit.genai.prompt.generationConfig
import com.google.mlkit.genai.prompt.modelConfig

// Target the preview fast model (Gemini Nano 4)
val config = generationConfig {
    modelConfig = modelConfig {
        releaseStage = ModelReleaseStage.PREVIEW
        preference = ModelPreference.FAST
    }
}
val generativeModel = Generation.getClient(config)

将工具附加到请求

创建 GenerateContentRequest 对象时,请在请求构建器中将 FunctionDeclaration 列表传递给 setTools()。

例如,如需为带有系统指令的单轮操作请求附加工具,请执行以下操作:

import com.google.mlkit.genai.prompt.SystemInstruction
import com.google.mlkit.genai.prompt.TextPart
import com.google.mlkit.genai.prompt.generateContentRequest

val requestText = "Turn on the living room lights and set brightness to 50"

val request = generateContentRequest(
    SystemInstruction("You are a helpful home automation assistant."),
    TextPart(requestText)
) {
    // Attach the FunctionDeclaration tool schemas
    setTools(listOf(turnOnLightTool))
    temperature = 0.2f
}

// Run non-streaming inference
val response = generativeModel.generateContent(request)

或者,如需为多轮查询附加 FunctionDeclaration 列表,请执行以下操作:

import com.google.mlkit.genai.prompt.SystemInstruction
import com.google.mlkit.genai.prompt.TextPart
import com.google.mlkit.genai.prompt.generateContentRequest

val availableTools = listOf(getWeatherTool)

// Use the generateContentRequest DSL
val request = generateContentRequest(
    TextPart("What is the weather in San Francisco, CA in celsius?")
) {
    setTools(availableTools)
}
val response = generativeModel.generateContent(request)

处理工具调用

当 Gemini Nano 决定调用工具时,模型的回答候选对象会在 candidate.functionCalls 中包含一个或多个 FunctionCall 对象。如需处理工具调用,您的应用必须先检查响应候选内容,以验证是否预测了任何工具调用:

import com.google.mlkit.genai.common.FunctionArgValue
import com.google.mlkit.genai.prompt.Content
import com.google.mlkit.genai.prompt.FunctionCallPart
import com.google.mlkit.genai.prompt.FunctionResponsePart
import com.google.mlkit.genai.prompt.GenerateContentRequest
import com.google.mlkit.genai.prompt.TextPart
import com.google.mlkit.genai.prompt.content

val candidate = response.candidates.firstOrNull()
val functionCalls = candidate?.functionCalls ?: emptyList()

if (functionCalls.isNotEmpty()) {
    // Process tool calls using either the single-turn or multi-turn flow
    // described in the following sections
}

单轮函数调用

对于应用直接执行请求的操作导向型任务(例如“打开相机应用”或“打开客厅灯”),请使用单轮函数调用。应用在本地执行函数后,流程即告完成,无需后续模型响应。

for (functionCall in functionCalls) {
    val name = functionCall.name
    val args = functionCall.arguments // Map<String, FunctionArgValue>

    when (name) {
        "open_app" -> {
            val appName = (args["appName"] as? FunctionArgValue.StringValue)?.value
            launchApp(appName)
        }
        "turn_on_light" -> {
            val room = (args["room"] as? FunctionArgValue.StringValue)?.value
            val brightness = (args["brightness"] as? FunctionArgValue.IntValue)?.value ?: 100
            setLightState(room, brightness)
        }
        else -> Log.w(TAG, "Unhandled tool call: $name")
    }
}

多轮函数调用

当用户希望根据工具检索到的数据获得自然语言回答时(例如,“旧金山的天气如何?”),请使用多轮函数调用。

在多轮对话流程中,您的应用会执行以下操作:

  1. 在本地执行请求的函数。
  2. 将结果打包到 FunctionResponsePart 对象中。
  3. 将模型的调用(MODEL 角色)和您的执行结果(USER 角色)附加到对话历史记录中。
  4. 向 Gemini Nano 发送后续请求,以合成最终的自然语言回答。
// 1. Execute each predicted tool call and collect results
val responseParts = mutableListOf<FunctionResponsePart>()

for (functionCall in functionCalls) {
    val responsePart: FunctionResponsePart = when {
        functionCall.name == "get_current_weather" -> {
            val location = (functionCall.arguments["location"] as? FunctionArgValue.StringValue)?.value ?: ""
            val unit = (functionCall.arguments["unit"] as? FunctionArgValue.StringValue)?.value ?: "celsius"

            val weatherData = fetchWeather(location, unit) // Local network/API call

            // Package the tool execution result as a key-value map
            FunctionResponsePart(
                id = functionCall.id,
                name = functionCall.name,
                response = mapOf("result" to weatherData)
            )
        }

        else -> throw IllegalArgumentException("Unknown tool: ${functionCall.name}")
    }
    responseParts.add(responsePart)
}

// 2. Append turns to conversation history to construct the multi-turn context
val history = request.contents.toMutableList().apply {
    // Turn 1: Model's request containing the predicted FunctionCallPart object
    add(
        content(Content.Role.MODEL) {
            for (call in functionCalls) {
                addPart(FunctionCallPart(call))
            }
        }
    )
    // Turn 2: User's response containing the executed FunctionResponsePart object
    add(
        content(Content.Role.USER) {
            for (responsePart in responseParts) {
                addPart(responsePart)
            }
        }
    )
}

// 3. Send the updated history back to Gemini Nano for final text synthesis
val followUpRequest = GenerateContentRequest.Builder(history)
    .setTools(availableTools) // Include tools in case the model requires subsequent tool calls
    .build()

val finalResponse = generativeModel.generateContent(followUpRequest)
val answer = finalResponse.candidates.firstOrNull()?.text

Log.d(TAG, "Final Model Response: $answer")

示例代码

如需查看 Android 应用中函数调用的完整工作示例,请参阅此机器学习套件示例应用。

已知问题

  • 直接模型访问 (genai-prompt) 中的 AndroidX AppFunctions (@AppFunction):尚不支持直接通过 Prompt API (com.google.mlkit:genai-prompt:1.0.0-beta5) 发现和执行编译时 @AppFunction 工具;genai-prompt 库中的工具必须使用 FunctionDeclaration 类以编程方式定义。如需立即使用 @AppFunction 工具,请使用Agentic Workflows (com.google.mlkit:genai-agent)。
  • 使用流式请求 (generateContentStream) 时,工具调用不会逐个 token 进行流式传输。相反,一旦模型完成生成工具调用,就会发出完整的 FunctionCall 对象(通过 StreamingCallback.onNewFunctionCall 或在最终流式传输的 GenerateContentResponse.candidates[0].functionCalls 中)。