一、 引言:KMP的崛起与全栈愿景
- KMP (Kotlin Multiplatform) 的定位:从"共享业务逻辑"到"全平台统一开发框架"的演进。
- 全栈开发的新定义:在移动端(Android/iOS)、Web前端、后端乃至新兴的AI Agent领域,使用同一套核心技术与语言栈。
- 本文目标:探讨如何以KMP为核心,构建一套从传统移动应用到智能AI Agent的全栈技术体系。
二、 基石篇:KMP核心技术精要与移动端实践
- 2.1 KMP架构深度解析
- 共享模块(Common)的设计哲学与最佳实践。
- 预期与实际声明(
expect/actual):平衡通用性与平台特性。 - 资源管理、序列化与网络请求在KMP中的统一方案。
- 2.2 从Android单平台到iOS覆盖
- 案例:将一个成熟的Android应用通过KMP快速扩展到iOS。
- 平台间UI协调策略:共享ViewModel与状态管理。
- 性能考量与调试技巧。
- 2.3 状态管理、数据流与架构模式
- 在KMP中应用MVI/MVVM。
- 使用
kotlinx.coroutines与Flow实现跨平台响应式数据流。 - 共享业务逻辑的测试策略。
三、 拓展篇:进军Web与后端,实现真正全栈
- 3.1 KMP for Web(Compose for Web)
- 使用Compose跨平台框架编写Web UI。
- 共享视图逻辑与状态,实现UI层复用。
- 与前端生态(React, Vue)的互操作可能性。
- 3.2 KMP for Backend(Ktor)
- 使用Kotlin/Ktor构建后端API服务。
- 共享数据模型、验证逻辑与DTO。
- 实现一套API,同时服务移动端、Web端与第三方。
- 3.3 全栈数据流闭环
- 从移动端到服务端:共享的序列化(如kotlinx.serialization)。
- 统一的错误处理与网络状态管理。
- 身份认证与授权逻辑的跨平台共享。
四、 融合篇:当KMP遇见AI Agent
- 4.1 AI Agent开发的核心挑战
- 异构工具调用与统一编排。
- 状态管理、记忆与上下文维护。
- 与现有业务系统的集成。
- 4.2 KMP作为AI Agent的"大脑"与"中枢神经"
- 共享"大脑"(逻辑层):用KMP编写Agent的核心决策、工具调用编排、记忆管理逻辑。
- 多模态"感官"与"执行器"(平台层) :
- Android/iOS Agent:集成设备传感器、系统API、本地模型(ML Kit)。
- Web Agent:操作DOM、调用浏览器API、与WebSocket服务通信。
- 后端/云Agent:执行长时间任务、访问数据库、调用外部服务。
- 4.3 技术实现蓝图
-
架构设计:基于KMP的插件化Agent架构。
-
工具抽象层:定义统一的工具接口(
expect),各平台提供具体实现(actual)。 -
工具抽象层:定义统一的工具接口(
expect),各平台提供具体实现(actual)。示例:跨平台「发送通知」工具接口与实现
在共享模块(
commonMain)中定义expect接口:kotlin// shared/src/commonMain/kotlin/com/example/agent/tools/NotificationTool.kt package com.example.agent.tools import kotlinx.coroutines.CoroutineScope // 1. Expected interface (declared in the shared module) expect class NotificationTool(coroutineScope: CoroutineScope) : AgentTool { override val name: String override val description: String override suspend fun execute(params: Map<String, String>): String }在 Android 平台模块(
androidMain)中提供actual实现:kotlin// androidApp/src/androidMain/kotlin/com/example/agent/tools/NotificationTool.kt package com.example.agent.tools import android.app.NotificationChannel import android.app.NotificationManager import android.content.Context import android.os.Build import androidx.core.app.NotificationCompat import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext // 2. Android actual implementation actual class NotificationTool actual constructor( private val coroutineScope: CoroutineScope ) : AgentTool { actual override val name: String = "send_notification" actual override val description: String = "Send system notifications on Android devices" actual override suspend fun execute(params: Map<String, String>): String { return withContext(Dispatchers.Main) { val context = getAndroidContext() // Assume platform-specific way to get Context val title = params["title"] ?: "Agent Notification" val message = params["message"] ?: "You have a new message" val channelId = "agent_channel" val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager // Create notification channel (Android O+) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel( channelId, "Agent Notifications", NotificationManager.IMPORTANCE_DEFAULT ).apply { description = "For receiving AI Agent notifications" } notificationManager.createNotificationChannel(channel) } val notification = NotificationCompat.Builder(context, channelId) .setSmallIcon(android.R.drawable.ic_dialog_info) .setContentTitle(title) .setContentText(message) .setPriority(NotificationCompat.PRIORITY_DEFAULT) .build() notificationManager.notify(System.currentTimeMillis().toInt(), notification) "Android notification sent: $title - $message" } } private fun getAndroidContext(): Context { // In real projects, can be obtained via dependency injection or platform-specific APIs return android.app.Application().applicationContext } }在 iOS 平台模块(
iosMain)中提供actual实现:kotlin// iosApp/src/iosMain/kotlin/com/example/agent/tools/NotificationTool.kt package com.example.agent.tools import kotlinx.coroutines.CoroutineScope import platform.UserNotifications.* // 3. iOS actual implementation actual class NotificationTool actual constructor( private val coroutineScope: CoroutineScope ) : AgentTool { actual override val name: String = "send_notification" actual override val description: String = "Send local notifications on iOS devices" actual override suspend fun execute(params: Map<String, String>): String { val title = params["title"] ?: "Agent Notification" val message = params["message"] ?: "You have a new message" val content = UNMutableNotificationContent().apply { this.title = title this.body = message this.sound = UNNotificationSound.defaultSound } val request = UNNotificationRequest( identifier = "agent_notification_${System.currentTimeMillis()}", content = content, trigger = null // Trigger immediately ) val center = UNUserNotificationCenter.currentNotificationCenter() center.addNotificationRequest(request) { error -> error?.let { println("iOS notification sending failed: $it") } } return "iOS notification sent: $title - $message" } }在 Web 平台模块(
jsMain)中提供actual实现:kotlin// webApp/src/jsMain/kotlin/com/example/agent/tools/NotificationTool.kt package com.example.agent.tools import kotlinx.coroutines.CoroutineScope import org.w3c.dom.Notification import org.w3c.dom.NotificationOptions import org.w3c.dom.NotificationPermission import kotlin.browser.window // 4. Web actual implementation actual class NotificationTool actual constructor( private val coroutineScope: CoroutineScope ) : AgentTool { actual override val name: String = "send_notification" actual override val description: String = "Send Web notifications in the browser" actual override suspend fun execute(params: Map<String, String>): String { val title = params["title"] ?: "Agent Notification" val message = params["message"] ?: "You have a new message" return when (Notification.permission) { "granted" -> { val options = NotificationOptions( body = message, icon = "/icon.png" // Optional icon ) Notification(title, options) "Web notification sent: $title - $message" } "denied" -> "Web notification permission denied" else -> { // Request permission Notification.requestPermission().then { permission -> if (permission == "granted") { val options = NotificationOptions(body = message) Notification(title, options) } } "Web notification permission requested" } } } }This example demonstrates the typical application of KMP's
expect/actualmechanism in AI Agent tool abstraction: the shared module defines a unified interface, each platform provides native implementations, enabling the Agent to invoke the "send notification" functionality across platforms. -
通信机制:Agent内部状态同步与跨平台事件总线。
-
案例设想:一个能同时在手机端接收语音指令、在Web端检索信息、在服务端处理数据的个人助理Agent。
-
五、 实战篇:构建一个微型全栈AI Agent Demo
-
5.1 项目结构与模块划分
shared: 核心Agent逻辑、工具定义、数据模型。androidApp: Android UI与平台工具实现(如发送通知)。iosApp: iOS UI与平台工具实现。webApp: Compose for Web UI与浏览器工具实现。backend: Ktor服务器,提供API并实现服务端工具(如发送邮件)。
-
5.2 核心代码片段
- 共享模块中Agent核心循环的Kotlin实现。
- 平台特定工具(如Android通知、Web Fetch API调用)的
actual实现。 - 使用Ktor构建一个简单的Agent任务提交与状态查询API。
示例:共享模块中的Agent核心循环
kotlin// Defined in the shared module package com.example.agent.shared import kotlinx.coroutines.* import kotlinx.coroutines.flow.* // 1. State definition sealed class AgentState { object Idle : AgentState() data class Thinking(val thought: String) : AgentState() data class Acting(val toolName: String, val input: Map<String, String>) : AgentState() data class Observing(val result: String) : AgentState() data class Error(val message: String) : AgentState() object Finished : AgentState() } // 2. Tool abstraction interface AgentTool { val name: String val description: String suspend fun execute(params: Map<String, String>): String } // 3. Event definition sealed class AgentEvent { data class UserInputReceived(val input: String) : AgentEvent() data class ToolExecutionCompleted(val toolName: String, val result: String) : AgentEvent() object StopRequested : AgentEvent() } // 4. Core Agent class class SimpleAgent( private val tools: List<AgentTool>, private val scope: CoroutineScope = CoroutineScope(Dispatchers.Default) ) { // State flow: read-only state exposed for UI observation private val _state = MutableStateFlow<AgentState>(AgentState.Idle) val state: StateFlow<AgentState> = _state.asStateFlow() // Event channel: for receiving external events (user input, tool execution completion, etc.) private val _events = Channel<AgentEvent>(Channel.UNLIMITED) val events: Flow<AgentEvent> = _events.receiveAsFlow() // Core loop coroutine: Job reference for handling the event loop private var coreJob: Job? = null /** * Starts the Agent's core event loop. * This method launches a coroutine that continuously listens to events in the events flow, * and performs corresponding state transitions and business logic based on event types. * When to call: Typically called immediately after Agent initialization to start listening for user input. */ fun start() { coreJob = scope.launch { events.collect { event -> when (event) { is AgentEvent.UserInputReceived -> { // State transition: Idle/Finished → Thinking _state.value = AgentState.Thinking("Parsing user instruction: ${event.input}") // 1. Parse instruction, plan tool call sequence val plan = planToolCalls(event.input) // 2. Execute tools in order for (step in plan) { // State transition: Thinking/Observing → Acting _state.value = AgentState.Acting(step.toolName, step.params) val tool = tools.find { it.name == step.toolName } val result = tool?.execute(step.params) ?: "Tool not found: ${step.toolName}" // State transition: Acting → Observing _state.value = AgentState.Observing(result) // Send tool execution completion event, can be used to trigger subsequent processing or memory updates _events.send(AgentEvent.ToolExecutionCompleted(step.toolName, result)) } // State transition: Observing → Finished (all tools executed) _state.value = AgentState.Finished } is AgentEvent.ToolExecutionCompleted -> { // Process tool execution results, can update memory or trigger next steps // Memory systems, result processing logic, etc. can be integrated here println("Tool ${event.toolName} execution completed, result: ${event.result}") } AgentEvent.StopRequested -> { // State transition: any state → Finished _state.value = AgentState.Finished cancel() // Cancel current coroutine, stop event loop } } } } } /** * Stops the Agent's core event loop. * Cancels the core coroutine and resets state to Idle. * When to call: When the Agent needs to be stopped actively (e.g., app exit, manual stop by user). */ fun stop() { coreJob?.cancel() _state.value = AgentState.Idle } /** * Sends an event to the Agent. * @param event The event object to send, of type AgentEvent * When to call: Called when external components (e.g., UI layer, other services) need to trigger Agent behavior, * such as user input, scheduled tasks, etc. */ fun sendEvent(event: AgentEvent) { scope.launch { _events.send(event) } } /** * Plans tool call sequence (example implementation). * Determines which tools to call and in what order based on user input content. * @param input Raw string of user input * @return List of tool call steps to execute in order * When to call: Automatically called when processing UserInputReceived event in the start() method. * Note: This is a simple rule engine example; in real projects, can be replaced with LLM calls or more complex planners. */ private suspend fun planToolCalls(input: String): List<ToolCallStep> { // LLM calls or rule engines can be integrated here return when { input.contains("提醒") -> listOf(ToolCallStep("send_notification", mapOf("message" to input))) input.contains("查询") -> listOf(ToolCallStep("web_search", mapOf("query" to input))) else -> emptyList() } } } // Tool call step data class data class ToolCallStep(val toolName: String, val params: Map<String, String>)This code demonstrates the core state machine, event-driven loop, and basic framework for tool call orchestration in an Agent. All logic resides in the
sharedmodule and can be reused across platforms. -
5.3 运行与演示
- 在Android模拟器、iOS模拟器、浏览器和后端服务中同时启动Agent。
- 演示一个跨平台连贯任务(如:"在手机上提醒我,然后去网上查一下KMP的最新版本,最后把结果发到我的邮箱")。
六、 挑战、展望与最佳实践
- 当前挑战:社区生态成熟度、特定平台性能优化、调试复杂度。
- 未来展望:KMP在嵌入式、桌面端及更多场景的可能性;与编译时AI(KSP)的结合。
- 给开发者的建议:渐进式采用策略、团队技能树培养、架构设计原则。
七、 结语
- KMP不仅是技术选型,更是一种追求效率与一致性的工程思想。
- 从Android到AI Agent的全栈之路,是Kotlin语言生命力与KMP技术潜力的集中体现。
- 拥抱变化,用统一的技术栈应对多元化的终端与智能时代。