On this page
A support chatbot that can only talk is limited. One that can look up your order is useful. Function calling bridges the two: you describe functions to the model, it returns a structured request to call one with arguments, your code runs it, and the model uses the result to answer.
The function-calling loop
Declaring tools (Firebase AI Logic, Kotlin)
val getOrderStatus = FunctionDeclaration(
name = "getOrderStatus",
description = "Get the delivery status of one of the signed-in user's orders.",
parameters = mapOf("orderId" to Schema.string("Order ID like A1042")),
)
val findNearestStore = FunctionDeclaration(
name = "findNearestStore",
description = "Find the nearest store to the user's current city.",
parameters = mapOf("city" to Schema.string("City name, e.g. Mohali")),
)
val model = Firebase.ai(backend = GenerativeBackend.googleAI()).generativeModel(
modelName = "gemini-3.8-flash", // check current names
tools = listOf(Tool.functionDeclarations(listOf(getOrderStatus, findNearestStore))),
systemInstruction = content { text("You help with orders and stores. Use tools for facts; never guess order data.") },
)Handling calls and returning results
class ToolRunner(
private val orders: OrderRepository, // only returns the CURRENT user's orders
private val stores: StoreRepository,
) {
suspend fun run(call: FunctionCallPart): JsonObject = when (call.name) {
"getOrderStatus" -> {
val id = call.args["orderId"]?.jsonPrimitive?.contentOrNull
?.takeIf { it.matches(Regex("^[A-Z][0-9]{3,10}$")) } // validate arguments
?: return error("Invalid order ID")
val order = orders.findForCurrentUser(id) ?: return error("Order not found")
buildJsonObject { put("status", order.status.name); put("eta", order.eta?.toString()) }
}
"findNearestStore" -> {
val city = call.args["city"]?.jsonPrimitive?.contentOrNull?.take(60) ?: return error("City required")
val store = stores.nearest(city) ?: return error("No store found")
buildJsonObject { put("name", store.name); put("address", store.address) }
}
else -> error("Unknown function") // allow-list
}
private fun error(msg: String) = buildJsonObject { put("error", msg) }
}
suspend fun ask(chat: Chat, runner: ToolRunner, userText: String): String {
var response = chat.sendMessage(userText)
repeat(3) { // cap the loop
val calls = response.functionCalls
if (calls.isEmpty()) return response.text.orEmpty()
val results = calls.map { FunctionResponsePart(it.name, runner.run(it)) }
response = chat.sendMessage(content(role = "function") { results.forEach { part(it) } })
}
return response.text ?: "Sorry, I couldn't complete that."
}
// val chat = model.startChat()Class and property names follow the Firebase AI Logic Kotlin SDK. Check the function-calling docs for your version, since details such as role names and part types can change.
Safety rules for tool use
- Allow-list functions, and never execute arbitrary names or code from the model.
- Validate every argument (type, format, ranges), just like untrusted user input.
- Authorise in your code: the tool queries only the signed-in user's data.
- Confirm sensitive actions: payments, cancellations, sending messages and deleting data need an explicit user confirmation in the UI, not just model intent.
- Limit the loop: cap iterations and time.
- Read-only first: start with lookups, and add actions carefully.
sealed interface PendingAction {
data class CancelOrder(val orderId: String) : PendingAction
}
// The tool doesn't cancel; it returns {"requiresConfirmation": true} and the UI shows:
// "Cancel order A1042? [Cancel order] [Keep order]". Only the button triggers the real API call.Use cases
Support assistant
Order status, return eligibility and store lookup, all read-only with user scoping.
Voice commands
"Remind me at 6" โ createReminder(time, title) โ confirm โ create.
Smart filters
"Show red shoes under 3000" โ applyFilters(...), which only changes UI state.
Frequently asked questions
Can the model call functions on its own without my code?
No. The model only returns a request. Nothing runs unless your code executes it.
Function calling or structured output?
Structured output returns data in a schema in one step. Function calling lets the model decide whether and which tool to use, often over multiple turns.
How does this relate to Android AppFunctions?
AppFunctions let your app expose functions to system-level AI agents. In-app function calling is your own assistant calling your own code. See AppFunctions.
Conclusion
Function calling turns AI from a talker into a helper: declare narrow tools, validate and authorise every call in your code, return structured results, cap the loop, and require user confirmation for actions. The model proposes, and your app decides.
Next step: measure and improve answer quality with Evaluating AI Responses and Reducing Hallucinations.
Official references
๐ฑ Apps built by Yunok Tech
Real Android apps on Google Play, free to download.
Walzi4K wallpapers for lock and home screenGet it โธ
NotiVaultNotification history: recover dismissed notificationsGet it โธ
Ready to build real Android applications?
Every AI feature still runs inside an Android app that has to be fast, reliable and well structured. Strengthen the Kotlin, Jetpack Compose and architecture skills that AI integrations depend on, then apply these AI concepts with confidence.
Move beyond theory and strengthen your Android development skills with Yunoktech's Android course. Learn core concepts, practical implementation, modern Android development techniques, and the reasoning behind building maintainable applications.
Explore the Android course and take your next step as an Android developer.