Google 发布了 ADK for Kotlin 和 ADK for Android 的 0.1.0 版本。
以后不仅有SDK、NDK还有ADK。
ADK for Kotlin 用在 Kotlin 服务端和通用 JVM 场景,ADK for Android 用在 Android App 里,主要处理端侧模型和 App 内上下文。
添加依赖
在 Android 工程里先加这个依赖:
bash
dependencies {
implementation("com.google.adk:google-adk-kotlin-core-android:0.1.0")
}
如果是普通 Kotlin 后端或 JVM 项目,用的是 google-adk-kotlin-core。需要通过注解生成工具代码时,还要加 KSP processor:
bash
dependencies {
implementation("com.google.adk:google-adk-kotlin-core:0.1.0")
ksp("com.google.adk:google-adk-kotlin-processor:0.1.0")
}
0.1.0 还是实验版本。它现在更像 Demo 工程里的尝鲜依赖,不适合直接接正式业务。Agent 调用失败以后返回什么,模型不可用时怎么降级,工具有没有权限,这些都要在 App 代码里处理。
Agent 写法
写 Agent 时会用到 LlmAgent。一个 Agent 里会放模型、名称、描述、系统指令、工具和子 Agent。下面是一个简化后的旅行助手。
旅行助手里有一个云端 orchestrator,它先理解用户的旅行问题,再把订票确认、酒店信息、租车信息这类任务交给设备上的子 Agent 处理。订票确认可能来自用户本地文档,这一步可以交给 Gemini Nano 这类端侧模型解析。代码只看核心字段:
bash
val orchestrator = LlmAgent(
name = "trip_orchestrator",
model = Gemini(apiKey = apiKey, name = MODEL_NAME),
instruction = Instruction(
"""
You are a travel assistant.
Use get_trip_details to read the trip plan.
Delegate booking checks to local sub agents when needed.
""".trimIndent()
),
tools = listOf(GetTripDetailsTool(tripId)),
subAgents = listOf(carRentalPipeline, hotelPipeline),
// 其他路由限制参数先省略,具体语义要看官方源码和 demo
)
这段代码里,model 决定当前 Agent 调哪个模型,instruction 决定它怎么理解任务,tools 是可调用能力,subAgents 是它可以转交任务的下游 Agent。官方示例里还有一些 transfer 相关参数,这里不按名字展开解释,等真正看源码或 demo 时再确认。
以前在 App 里接一个大模型,很多时候只是把用户输入发给云端接口,再把回复展示出来。换成 Agent 以后,ViewModel 里那些"先查行程、再判断订单、最后拼回复"的分支会往 Agent 侧挪,App 侧更像是在等一个最终的 UI State。
放到 Android 里以后,工具调用会碰到线程和生命周期。比如工具里要读取本地行程文件,就要处理文件不存在和读取耗时;如果订单信息来自本地数据库,也不要在主线程里直接查询。
工具调用
ADK for Kotlin 支持用 @Tool 和 @Param 描述函数工具。工具本质上是把本地函数暴露给 Agent,让模型在合适的时候调用它。
比如把一个订单查询能力交给 Agent,先看服务类:
bash
class TripService {
@Tool
fun getBookingStatus(
@Param("Booking id from the user's trip document")
bookingId: String
): String {
return repository.findBookingStatus(bookingId)
}
}
@Tool 和 @Param 只是描述工具,还不是完整接入。官方 Kotlin 示例里会通过 google-adk-kotlin-processor 做 KSP 生成,然后才能在 Agent 里使用类似 generatedTools() 的结果。
也就是说,如果代码里写成下面这样还不够:
bash
dependencies {
implementation("com.google.adk:google-adk-kotlin-core:0.1.0")
ksp("com.google.adk:google-adk-kotlin-processor:0.1.0")
}
Android 工程还要配置 KSP 插件。实际项目里一般会在根工程或版本目录里声明 com.google.devtools.ksp,再在模块里应用插件。KSP 版本要和 Kotlin 版本对齐,这里不写死版本,老项目直接照搬很容易卡在 Gradle Sync。
再配置 Agent 时,generatedTools() 不是手写出来的普通方法,而是注解处理后的产物。没有 KSP 插件和 processor 这两步,直接照抄这段代码大概率会编译不过。
bash
val bookingAgent = LlmAgent(
name = "booking_agent",
description = "Checks booking status from local trip data.",
model = Gemini(apiKey = apiKey, name = "gemini-2.5-flash"),
instruction = Instruction("Use tools to verify booking status."),
tools = TripService().generatedTools()
)
这里的 @Param 不只是给开发者看的注释,它会影响模型理解参数含义。参数从哪里来、格式是什么、失败时返回什么,都要写清楚。
如果工具函数里要读本地行程文件,这些逻辑仍然走原来的 Android 代码。Agent 只拿工具返回的结果,不应该跳过文件不存在、读取失败和权限拒绝这些分支。
Session 和 Memory
这里还有一个容易踩坑的地方:状态。一个 Agent 问用户补充信息,另一个 Agent 要知道前面已经确认过什么;工具调用失败以后,后面的 Agent 也要知道这一步失败了。否则上游吐了个空结果,下游还继续拼 prompt,最后用户看到的就是一段像正常回答的幻觉回复。
ADK 里有 Session state 和 Memory service。Session state 更像短期状态,服务于当前会话里的上下文共享;Memory service 更偏长期记忆,可以保存跨会话的信息。
在 Android 工程里使用这些能力时,不要简单理解成 SharedPreferences 或聊天记录。Agent 状态可能包含用户输入、模型输出、工具调用参数、本地数据摘要和错误信息。哪些能落盘,哪些只能留在内存,哪些不能进日志,需要提前定规则。
最后
我真的学不动了。