Blog Infos
Author
Published
Topics
, , , ,
Published

 

Introduction

The future of mobile experiences is shifting from static apps to intelligent, proactive agents. As AI continues to evolve, so does our approach to building applications that don’t just wait for user input — but act on behalf of the user, adapt to their needs, and collaborate in meaningful ways. This transformation introduces the concept of agentic applications — apps that possess autonomy, memory, and reasoning.

In this post, we’ll explore what it means to make an Android application agentic, why it’s an important step forward, and we will create a simple Android Application that simulates money transfer.

What’s an AI agent and what makes it different from an LLM

An AI Agent is a system that’s operates in a controlled environment to achieve specific goals within that environment in an autonomous way, it’s a collection of Tools. Models, and a Strategy.

For instance, it could be a personal assistant that schedule meetings for you by accessing your emails, calendar, and contact list.

An LLM is a trained AI model that only produces text, it replies to prompts in general, but unlike agents, it doesn’t take action.

What makes an application agentic

Simply put, when it can understand user intent and not just respond to user’s inputs and taps.

For instance, when I tell it “Send $100 to my wife”, it can do this automatically and do all the required steps, which may involves accessing my contacts list, getting my wife’s phone number, and call a function that sends money by phone number.

Why it’s important

It’s a great improvement in the user experience as it reduces the user efforts, imagine yourself wanting to order a pizza from a restaurant that has 4+ stars reviews, and within 20 minutes of your location, using one of the meal delivery apps.

Steps involved if the app is not agentic:

  1. Go to restaurants listing
  2. Filter with 4+ reviews and within 20 minutes
  3. Select the restaurant
  4. Create your order
  5. Confirm delivery details

While the application is agentic, you only do one step.

“Order a pepperoni pizza from the first 4+ star restaurant and can arrive within 20 minutes. use my usual address for delivery.”

How can you do it.

JetBrains recently released a framework for building AI agents that entirely developed in Kotlin, Koog, and that’s what we’re going to use to develop our android application.

Here’s what’s going to happen in the application:

 

First, we will need to declare Koog dependency in build.gradle(app module).

implementation("ai.koog:koog-agents:0.3.0")

Next, we need to create a ChatScreen:

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Send
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel

data class ChatMessage(val message: String, val isUser: Boolean)
@Composable
fun ChatScreen(viewModel: MainViewModel = hiltViewModel()) {
    var userInput by remember { mutableStateOf(TextFieldValue("")) }
    val chatHistory = remember { mutableStateListOf<ChatMessage>() }
    val message = viewModel.newMessage.collectAsStateWithLifecycle()
    LaunchedEffect(message.value) {
        chatHistory.add(ChatMessage(message.value, false))
    }
    Column(modifier = Modifier
        .fillMaxSize()
        .padding(8.dp)) {
        
        LazyColumn(
            modifier = Modifier
                .weight(1f)
                .fillMaxWidth(),
            reverseLayout = true
        ) {
            items(chatHistory.reversed()) { msg ->
                ChatBubble(message = msg)
                Spacer(modifier = Modifier.height(4.dp))
            }
        }

        HorizontalDivider()

        
        Row(
            modifier = Modifier
                .fillMaxWidth()
                .padding(top = 8.dp),
            verticalAlignment = Alignment.CenterVertically
        ) {
            TextField(
                value = userInput,
                onValueChange = { userInput = it },
                modifier = Modifier.weight(1f),
                placeholder = { Text("Type your message...") }
            )
            IconButton(
                onClick = {
                    val input = userInput.text.trim()
                    if (input.isNotEmpty()) {
                        chatHistory.add(ChatMessage(input, true))
                        userInput = TextFieldValue("") 
                        viewModel.runPrompt(input)
                    }
                }
            ) {
                Icon(Icons.Default.Send, contentDescription = "Send")
            }
        }
    }
}

@Composable
fun ChatBubble(message: ChatMessage) {
    val backgroundColor = if (message.isUser) MaterialTheme.colorScheme.primary else Color.LightGray
    val alignment = if (message.isUser) Alignment.TopEnd else Alignment.TopStart
    val textColor = if (message.isUser) Color.White else Color.Black

    Box(
        contentAlignment = alignment,
        modifier = Modifier.fillMaxWidth()
    ) {
        Box(
            modifier = Modifier
                .background(color = backgroundColor, shape = MaterialTheme.shapes.medium)
                .padding(12.dp)
                .widthIn(max = 280.dp)
        ) {
            Text(
                text = message.message,
                color = textColor
            )
        }
    }
}

Next, setting up our MainViewModel, which for the sake of simplicity, it’ll contains all the logic:

import ai.koog.agents.core.agent.AIAgent
import ai.koog.agents.core.tools.ToolRegistry
import ai.koog.agents.core.tools.reflect.asTools
import ai.koog.prompt.executor.clients.openai.OpenAIModels
import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.eraqi.tools.MoneyTransferTool
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class MainViewModel  @Inject constructor() :ViewModel() {
val apiKey = "" // Your OpenApi api key should be put here
private val _newMessage = MutableStateFlow("")
val newMessage = _newMessage.asStateFlow()
val moneyTransferTool = MoneyTransferTool()
val agent = AIAgent(
       executor = simpleOpenAIExecutor(apiKey),
       systemPrompt = "You're a banking assistant, you can send and receive money",
       llmModel = OpenAIModels.Chat.GPT4o,
       toolRegistry = ToolRegistry{
           tools(moneyTransferTool.asTools())
       }
   )

We need to go through a few concepts here in order for this code to make sense:

  • MoneyTransferTool: This is a class that contains some functions that are visibile for the Agent to execute
  • Executer: Just defines which LLM I’m going to use to execute this specific task
  • System Prompt: This is a definition of the job of the LLM, the one that I have above is kind of generic, for instance, if you asked this Agent for a medical advice, it would advise you, but If I changed the system prompt to the following:

 

systemPrompt = "You're a banking assistant, you can send and receive money, if you're asked any question that is unrelated to banking, say I don't know"

 

It will reply like this:

 

  • Tool Registry: Evey agent gets access to tools to use, tools are basically functions that do specific job, each of these function gets two important annotations @Tool and @LLMDescriptionTool just declares it as a tool to make it visible to the LLM, and LLMDescription describes what this function does, to the LLM

This is what runPrompt looks like:

fun runPrompt(message: String){

    viewModelScope.launch {
        try {
            val result = agent.run(message)
            _newMessage.value = result
        }catch (exception: Exception){
            exception.printStackTrace()
        }
    }
}

And this what MoneyTransferTools looks like:

import ai.koog.agents.core.tools.annotations.LLMDescription
import ai.koog.agents.core.tools.annotations.Tool
import ai.koog.agents.core.tools.reflect.ToolSet
import android.util.Log


@LLMDescription("Send and receive money")
class MoneyTransferTool: ToolSet {

    val TAG = "MoneyTransferTool "

    @Tool
    @LLMDescription("Sending an amount of money to a user")
    fun sendMoney(@LLMDescription("user name that should receive the money") user: String,
                 @LLMDescription("The amount of money to be sent") amount: String):String{


        Log.d(TAG, "Sent $amount to $user")
        return "Done"
    }

}

Job Offers

Job Offers

There are currently no vacancies.

OUR VIDEO RECOMMENDATION

, ,

Kobweb:Creating websites in Kotlin leveraging Compose HTML

Kobweb is a Kotlin web framework that aims to make web development enjoyable by building on top of Compose HTML and drawing inspiration from Jetpack Compose.
Watch Video

Kobweb:Creating websites in Kotlin leveraging Compose HTML

David Herman
Ex-Googler, author of Kobweb

Kobweb:Creating websites in Kotlin leveraging Compose HTML

David Herman
Ex-Googler, author o ...

Kobweb:Creating websites in Kotlin leveraging Compose HTML

David Herman
Ex-Googler, author of Kob ...

Jobs

Note that it has to extend the ToolSet interface and also annotated with LLMDescription to make it easy for the LLM to know which tools it’s going to use for each request.

Also remember, what’s returned from the tool is going to be returned to the LLM and based on the value it will understand if it was successfully executed or not, for instance if an error happened, we should return an error message and the LLM will rephrase it in a way that is more readable and understandable for the user.

Summary

This was a simple introduction to Agentic Android Apps and Koog, building an AI agent has more challenges that you will need to handle, like history, and memory, Koog has support for that, and many more features.

References

Overview — Koog

JetBrains/koog: Koog is the official Kotlin framework for building and running robust, scalable and production-ready AI agents across all platforms — from backend services to Android, JVM, and even in-browser environments. Koog is based on our AI products expertise and provides proven solutions for complex LLM and AI problems

This article was previously published on proandroiddev.com.

Menu