Koog Android示例应用:移动端AI代理开发实战
还在为移动端AI应用开发而头疼?面对模型集成、工具调用、状态管理的多重挑战,传统开发方式往往需要大量样板代码和复杂的架构设计。Koog框架通过纯Kotlin实现的AI代理框架,为Android开发者提供了革命性的解决方案。**读完本文你将获得:**- ✅ Koog框架核心概念与架构解析- ✅ Android端AI代理完整开发流程- ✅ 计算器与天气代理实战代码示例- ✅ Jetpack...
·
Koog Android示例应用:移动端AI代理开发实战
痛点:移动端AI应用开发的复杂性挑战
还在为移动端AI应用开发而头疼?面对模型集成、工具调用、状态管理的多重挑战,传统开发方式往往需要大量样板代码和复杂的架构设计。Koog框架通过纯Kotlin实现的AI代理框架,为Android开发者提供了革命性的解决方案。
读完本文你将获得:
- ✅ Koog框架核心概念与架构解析
- ✅ Android端AI代理完整开发流程
- ✅ 计算器与天气代理实战代码示例
- ✅ Jetpack Compose与Koog集成最佳实践
- ✅ 移动端AI应用性能优化技巧
Koog框架架构解析
核心组件架构
Android端集成架构
环境搭建与项目配置
Gradle依赖配置
// build.gradle.kts
dependencies {
implementation("ai.koog:agents-core:1.0.0")
implementation("ai.koog:prompt-executor-openai-client:1.0.0")
implementation("ai.koog:agents-features-event-handler:1.0.0")
// Android相关依赖
implementation("androidx.compose.ui:ui:1.6.0")
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.2")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
}
AndroidManifest权限配置
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
计算器代理实战开发
工具类定义
object CalculatorTools {
abstract class CalculatorTool(
name: String,
description: String,
) : Tool<CalculatorTool.Args, CalculatorTool.Result>() {
@Serializable
data class Args(val a: Float, val b: Float) : ToolArgs
@Serializable
@JvmInline
value class Result(val result: Float) : ToolResult {
override fun toStringDefault(): String = result.toString()
}
final override val argsSerializer = Args.serializer()
final override val descriptor = ToolDescriptor(
name = name,
description = description,
requiredParameters = listOf(
ToolParameterDescriptor("a", "First number", ToolParameterType.Float),
ToolParameterDescriptor("b", "Second number", ToolParameterType.Float)
)
)
}
// 具体工具实现
object PlusTool : CalculatorTool("plus", "Adds a and b") {
override suspend fun execute(args: Args): Result = Result(args.a + args.b)
}
object MinusTool : CalculatorTool("minus", "Subtracts b from a") {
override suspend fun execute(args: Args): Result = Result(args.a - args.b)
}
object MultiplyTool : CalculatorTool("multiply", "Multiplies a and b") {
override suspend fun execute(args: Args): Result = Result(args.a * args.b)
}
object DivideTool : CalculatorTool("divide", "Divides a by b") {
override suspend fun execute(args: Args): Result = Result(args.a / args.b)
}
}
代理提供者实现
object CalculatorAgentProvider : AgentProvider {
override val title: String = "Calculator"
override val description: String = "Hi, I'm a calculator agent, I can do math"
override suspend fun provideAgent(
appSettings: AppSettings,
onToolCallEvent: suspend (String) -> Unit,
onErrorEvent: suspend (String) -> Unit,
onAssistantMessage: suspend (String) -> String,
): AIAgent<String, String> {
val openAiToken = appSettings.getCurrentSettings().openAiToken
require(openAiToken.isNotEmpty()) { "OpenAI token is not configured." }
val executor = simpleOpenAIExecutor(openAiToken)
val toolRegistry = ToolRegistry {
tool(CalculatorTools.PlusTool)
tool(CalculatorTools.MinusTool)
tool(CalculatorTools.MultiplyTool)
tool(CalculatorTools.DivideTool)
tool(ExitTool)
}
val strategy = strategy(title) {
val nodeRequestLLM by nodeLLMRequestMultiple()
val nodeAssistantMessage by node<String, String> { message ->
onAssistantMessage(message)
}
val nodeExecuteToolMultiple by nodeExecuteMultipleTools(parallelTools = true)
edge(nodeStart forwardTo nodeRequestLLM)
edge(nodeRequestLLM forwardTo nodeExecuteToolMultiple onMultipleToolCalls { true })
edge(nodeRequestLLM forwardTo nodeAssistantMessage transformed { it.first() } onAssistantMessage { true })
edge(nodeAssistantMessage forwardTo nodeRequestLLM)
}
val agentConfig = AIAgentConfig(
prompt = prompt("calculator") {
system("""
You are a calculator agent. Use tools to solve math problems.
Provide clear answers and ask for next problem until user stops.
""".trimIndent())
},
model = OpenAIModels.Chat.GPT4o,
maxAgentIterations = 50
)
return AIAgent(
promptExecutor = executor,
strategy = strategy,
agentConfig = agentConfig,
toolRegistry = toolRegistry,
) {
handleEvents {
onToolCall { ctx ->
onToolCallEvent("Tool ${ctx.tool.name}, args ${ctx.toolArgs}")
}
onAgentRunError { ctx ->
onErrorEvent("${ctx.throwable.message}")
}
}
}
}
}
ViewModel状态管理
class AgentDemoViewModel(
application: Application,
private val agentProvider: AgentProvider
) : AndroidViewModel(application) {
private val _uiState = MutableStateFlow(AgentDemoUiState(
title = agentProvider.title,
messages = listOf(Message.SystemMessage(agentProvider.description))
))
val uiState: StateFlow<AgentDemoUiState> = _uiState.asStateFlow()
fun sendMessage() {
val userInput = _uiState.value.inputText.trim()
if (userInput.isEmpty()) return
_uiState.update {
it.copy(
messages = it.messages + Message.UserMessage(userInput),
inputText = "",
isInputEnabled = false,
isLoading = true
)
}
viewModelScope.launch {
runAgent(userInput)
}
}
private suspend fun runAgent(userInput: String) {
withContext(Dispatchers.IO) {
try {
val agent = agentProvider.provideAgent(
appSettings = AppSettings(application),
onToolCallEvent = { message ->
viewModelScope.launch {
_uiState.update {
it.copy(messages = it.messages + Message.ToolCallMessage(message))
}
}
},
onErrorEvent = { errorMessage ->
viewModelScope.launch {
_uiState.update {
it.copy(
messages = it.messages + Message.ErrorMessage(errorMessage),
isInputEnabled = true,
isLoading = false
)
}
}
},
onAssistantMessage = { message ->
_uiState.update {
it.copy(
messages = it.messages + Message.AgentMessage(message),
isInputEnabled = true,
isLoading = false,
userResponseRequested = true
)
}
// 等待用户响应逻辑
}
)
val result = agent.run(userInput)
_uiState.update {
it.copy(
messages = it.messages + Message.ResultMessage(result),
isInputEnabled = false,
isLoading = false,
isChatEnded = true
)
}
} catch (e: Exception) {
_uiState.update {
it.copy(
messages = it.messages + Message.ErrorMessage("Error: ${e.message}"),
isInputEnabled = true,
isLoading = false
)
}
}
}
}
}
Jetpack Compose UI实现
消息类型定义
sealed class Message {
data class UserMessage(val text: String) : Message()
data class AgentMessage(val text: String) : Message()
data class SystemMessage(val text: String) : Message()
data class ErrorMessage(val text: String) : Message()
data class ToolCallMessage(val text: String) : Message()
data class ResultMessage(val text: String) : Message()
}
data class AgentDemoUiState(
val title: String = "Agent Demo",
val messages: List<Message> = emptyList(),
val inputText: String = "",
val isInputEnabled: Boolean = true,
val isLoading: Boolean = false,
val isChatEnded: Boolean = false,
val userResponseRequested: Boolean = false,
val currentUserResponse: String? = null
)
Compose界面组件
@Composable
fun AgentDemoScreen(
viewModel: AgentDemoViewModel,
onBack: () -> Unit
) {
val uiState by viewModel.uiState.collectAsState()
Scaffold(
topBar = {
TopAppBar(
title = { Text(uiState.title) },
navigationIcon = {
IconButton(onClick = onBack) {
Icon(Icons.Default.ArrowBack, contentDescription = "Back")
}
}
)
}
) { padding ->
Column(
modifier = Modifier
.padding(padding)
.fillMaxSize()
) {
// 消息列表
LazyColumn(
modifier = Modifier.weight(1f),
reverseLayout = true
) {
items(uiState.messages.reversed()) { message ->
MessageItem(message = message)
}
}
// 输入区域
if (uiState.isInputEnabled) {
InputArea(
text = uiState.inputText,
onTextChange = viewModel::updateInputText,
onSend = viewModel::sendMessage,
isLoading = uiState.isLoading
)
}
// 重启按钮
if (uiState.isChatEnded) {
Button(
onClick = viewModel::restartChat,
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
) {
Text("Restart Chat")
}
}
}
}
}
@Composable
fun MessageItem(message: Message) {
val (backgroundColor, textColor) = when (message) {
is Message.UserMessage -> Color.Blue to Color.White
is Message.AgentMessage -> Color.Green to Color.White
is Message.SystemMessage -> Color.Gray to Color.White
is Message.ErrorMessage -> Color.Red to Color.White
is Message.ToolCallMessage -> Color.Yellow to Color.Black
is Message.ResultMessage -> Color.Cyan to Color.Black
}
Box(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
) {
Text(
text = when (message) {
is Message.UserMessage -> "You: ${message.text}"
is Message.AgentMessage -> "Agent: ${message.text}"
is Message.SystemMessage -> "System: ${message.text}"
is Message.ErrorMessage -> "Error: ${message.text}"
is Message.ToolCallMessage -> "Tool: ${message.text}"
is Message.ResultMessage -> "Result: ${message.text}"
},
color = textColor,
modifier = Modifier
.background(backgroundColor, RoundedCornerShape(8.dp))
.padding(16.dp)
)
}
}
性能优化与最佳实践
内存管理策略
| 策略 | 实现方式 | 效果 |
|---|---|---|
| 工具懒加载 | 按需初始化工具实例 | 减少启动内存占用 |
| 会话压缩 | 自动清理历史消息 | 控制内存增长 |
| 结果缓存 | 缓存常用计算结果 | 减少重复计算 |
| 连接池 | 复用网络连接 | 降低连接开销 |
网络优化技巧
// 使用OkHttp连接池优化
val okHttpClient = OkHttpClient.Builder()
.connectionPool(ConnectionPool(5, 5, TimeUnit.MINUTES))
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build()
val executor = simpleOpenAIExecutor(
apiKey = openAiToken,
httpClient = okHttpClient
)
错误处理与重试机制
class RetryAwareExecutor(
private val delegate: PromptExecutor,
private val maxRetries: Int = 3
) : PromptExecutor by delegate {
override suspend fun execute(prompt: Prompt): PromptResult {
var lastException: Exception? = null
repeat(maxRetries) { attempt ->
try {
return delegate.execute(prompt)
} catch (e: IOException) {
lastException = e
delay((attempt + 1) * 1000L) // 指数退避
} catch (e: Exception) {
lastException = e
break
}
}
throw lastException ?: Exception("Unknown error")
}
}
实战应用场景扩展
电商推荐代理
object RecommendationAgentProvider : AgentProvider {
override val title: String = "Recommendation Agent"
override val description: String = "I can recommend products based on your preferences"
override suspend fun provideAgent(
appSettings: AppSettings,
onToolCallEvent: suspend (String) -> Unit,
onErrorEvent: suspend (String) -> Unit,
onAssistantMessage: suspend (String) -> String
): AIAgent<String, String> {
val toolRegistry = ToolRegistry {
tool(ProductSearchTool)
tool(PriceComparisonTool)
tool(UserPreferenceTool)
tool(ExitTool)
}
// 策略配置和代理创建逻辑
}
}
多模态代理集成
object MultiModalAgentProvider : AgentProvider {
override suspend fun provideAgent(
// 参数省略
): AIAgent<String, String> {
val toolRegistry = ToolRegistry {
tool(ImageAnalysisTool)
tool(TextToSpeechTool)
tool(VisionProcessingTool)
tool(ExitTool)
}
// 支持图像和文本的多模态处理
}
}
总结与展望
Koog框架为Android开发者提供了强大的AI代理开发能力,通过纯Kotlin实现确保了代码的一致性和可维护性。本文详细介绍了:
- 架构设计:分层清晰的组件架构,便于扩展和维护
- 工具开发:灵活的工具定义机制,支持各种业务场景
- 状态管理:完善的ViewModel状态管理,确保UI一致性
- 性能优化:多层次的性能优化策略,提升用户体验
未来Koog将继续在以下方向发力:
- 🔥 更强大的多模态支持
- 🚀 更高效的模型推理优化
- 📱 更完善的移动端生态集成
立即开始你的移动端AI代理开发之旅,让Koog助力你的应用智能化升级!
三连支持:点赞👍 + 收藏⭐ + 关注👀 下期预告:《Koog高级特性:自定义策略图与分布式代理集群》
更多推荐



所有评论(0)