Google ADK Java
Java teams have been in an awkward spot with AI agents for a while. You can absolutely bolt tool-calling onto an existing app and get useful results. That works for many features. The trouble starts when “LLM with a tool” becomes “agent with runtime concerns” and you realize you now care about sessions, execution flow, debugging, and deployment options.
Google ADK is worth a look because it is positioned as an open-source agent development framework for building, debugging, and deploying production-scale AI agents, and it is available in Java alongside Python, TypeScript, and Go. For JVM teams, that matters on its own: you can evaluate an agent framework in Java without immediately reaching for a Python sidecar.
For Java engineers, the useful entry point is not the whole platform story. It is the first working agent:
- Java 17+
- Maven 3.9+
com.google.adk:google-adk- Gemini API via
GOOGLE_API_KEY - one
LlmAgent - one
FunctionTool - one in-memory runner
- one
Session - one stream of
Eventobjects
That is enough to answer the early question that matters most: does ADK fit your stack and your mental model?
What ADK looks like from a Java engineer’s seat
The Java path is compact and builder-oriented:
- a root agent declared as
BaseAgent - it constructs that agent with
LlmAgent.builder() - it attaches tools to the
LlmAgent - the CLI example uses
InMemoryRunner - it creates a
Session - it creates a
RunConfig - it processes emitted
Eventobjects
That is a clean first mile.
The strategic reason to care is simpler than hype: ADK has Java-specific documentation for building agents such as multi-tool agents and streaming agents, plus Java API reference material under ADK Java. That suggests there is enough surface area to justify a serious evaluation, even if you should still verify any advanced feature you plan to rely on.
My opinionated take: that is the right order of operations. Start with one small agent and one useful tool. Do not begin by assuming you need every broader framework capability on day one.
The baseline setup Java teams actually need
The baseline prerequisites are straightforward:
- Java 17 or later
- Maven 3.9 or later
- Gemini API access
GOOGLE_API_KEYset in the project environment before running the agent
For Java, the ADK package is published as the Maven dependency:
com.google.adk:google-adk
The sample dependency set also includes:
com.google.adk:google-adk-dev:1.2.0for the ADK dev web UI
A minimal dependency section, using the versions shown in the reference material, looks like this:
<dependencies>
<dependency>
<groupId>com.google.adk</groupId>
<artifactId>google-adk</artifactId>
<version>1.2.0</version>
</dependency>
<dependency>
<groupId>com.google.adk</groupId>
<artifactId>google-adk-dev</artifactId>
<version>1.2.0</version>
</dependency>
</dependencies>
The supplied notes document dependency setup and runtime flow, but not exact shell commands for launching the sample. So if you are writing internal setup docs, treat launch commands as something to verify in your own project rather than as a sourced fact here.
A minimal Java ADK agent shape
The first Java agent shape has these ingredients:
ROOT_AGENTdeclared as aBaseAgent- construction via
LlmAgent.builder() name("hello-time-agent")description(...)instruction(...)model("gemini-flash-latest")tools(FunctionTool.create(HelloTimeAgent.class, "getCurrentTime"))
The example tool is a static method:
getCurrentTime(String city)- parameter annotated with
@Schema - returns
Map<String, String>
That method is exposed to the agent through FunctionTool.create(...).
Below is an illustrative reconstruction based on the ingredients in the supplied notes. It is useful for understanding shape, but it should not be treated as a fully sourced, official project layout or complete copy of the docs.
import com.google.adk.agents.BaseAgent;
import com.google.adk.agents.LlmAgent;
import com.google.adk.tools.FunctionTool;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.Map;
public class HelloTimeAgent {
public static final BaseAgent ROOT_AGENT =
LlmAgent.builder()
.name("hello-time-agent")
.description("Answers questions about the current time in a city")
.instruction("If the user asks for the current time in a city, use the getCurrentTime tool.")
.model("gemini-flash-latest")
.tools(FunctionTool.create(HelloTimeAgent.class, "getCurrentTime"))
.build();
public static Map<String, String> getCurrentTime(
@Schema(description = "The city to get the current time for")
String city) {
// Implementation omitted here; the notes document the method shape,
// annotation, and return type rather than a full listing.
return Map.of();
}
}
I like this shape for evaluation because it keeps the moving parts visible. You can see the agent, the model string, the instruction, and the attached tool in one place.
What each Java type tells you, carefully
A lot of early confusion comes from inferring more than the docs actually say. It is better to stay precise.
BaseAgent
ROOT_AGENT is declared as BaseAgent while being constructed with LlmAgent.builder().
That is a useful fact about how the sample is written. But the supplied notes do not define the formal role or contract of BaseAgent, so it is better not to over-explain it.
LlmAgent
This is the concrete agent type shown in the ADK Java examples. ADK documentation shows a basic Java agent being created with com.google.adk.agents.LlmAgent using the builder API, including fields such as:
.name(...).model("gemini-flash-latest").instruction(...).tools(...)
The docs also show tools being attached directly to an LlmAgent during construction. That is one of the clearest parts of the Java API.
FunctionTool.create(...)
The Java example uses:
FunctionTool.create(HelloTimeAgent.class, "getCurrentTime")
That is the documented path in the sample for exposing a Java static method to the agent.
My practical opinion: this is a good evaluation path because it makes tool boundaries obvious. Even so, tool design still deserves care. If a tool contract is vague, teams often blame the model or framework first. In reality, it is often just a fuzzy interface.
@Schema
The tool parameter is annotated with @Schema, using the city parameter as the example.
That is a small but useful signal. Clear tool inputs usually make agent behavior easier to reason about. That is engineering judgment, not a special ADK guarantee, but it is still a good habit.
RunConfig
The CLI example creates a config with:
RunConfig.builder().build()
That is the documented fact. The supplied notes do not say what RunConfig controls, so it is best to treat it as part of the invocation shape and verify its supported options in the version you use.
InMemoryRunner
The CLI example uses InMemoryRunner with HelloTimeAgent.ROOT_AGENT.
That is what is documented. The notes do not define how InMemoryRunner behaves internally or how it differs from other runners, so I would avoid building an architectural theory around it from the sample alone.
Session
The example creates a session via:
runner.sessionService().createSession(runner.appName(), "user1234")
That is the documented usage in the example. The notes do not define broader session semantics, so any production interpretation should be treated as something to validate in your own design.
Event
The example runs the agent, obtains events, and prints only Event objects where event.finalResponse() is true.
That is enough to know the sample flow deals in events rather than just a single returned string, but the supplied notes do not define the full Event model or streaming protocol. So if event handling is important to your system, inspect it directly rather than assuming more than the example shows.
The runtime flow
The documented Java CLI flow is concrete enough to summarize:
- Create a
RunConfigwithRunConfig.builder().build(). - Construct an
InMemoryRunnerwithHelloTimeAgent.ROOT_AGENT. - Create a
Sessionusingrunner.sessionService().createSession(runner.appName(), "user1234"). - Run the agent with
runner.runAsync(...). - Print only the
Eventobjects whereevent.finalResponse()is true.
That is a useful evaluation path because it exposes the actual invocation shape without a lot of surrounding infrastructure.
My advice is to keep your first test close to that shape. When you are evaluating a framework, fewer layers usually mean fewer wrong conclusions.
About the dev web UI
The sample dependency set also includes com.google.adk:google-adk-dev:1.2.0 for the ADK dev web UI.
That is the safe, sourced statement. Beyond that, I would be careful. The supplied notes do not document a formal development-versus-production distinction for that UI, so if your team plans to use it, treat it as something to evaluate directly rather than something to categorize too confidently from limited notes.
Model and version caution
The Java sample uses the Gemini API and shows model("gemini-flash-latest").
That is the documented sample choice. The notes do not characterize that model in terms of speed, cost, or quality, so it is better not to project those attributes onto it here.
There is one compatibility note in the supplied research that is worth preserving precisely: ADK Java v0.3.0 and lower is not compatible with Gemini 3 Pro Preview due to thought signature changes for function calling. The guide says to use Gemini 2.5 or lower models instead on those older versions.
That is a good reminder to verify SDK and model combinations deliberately, especially if you are working with inherited code or older sample lines.
Deployment posture, stated carefully
ADK says agents can be containerized and run on your own infrastructure, or deployed to Google Cloud through Agent Runtime, Cloud Run, or GKE without requiring changes to agent code.
That is the strongest sourced deployment statement in the notes, and it is a meaningful one. It suggests the framework is not limited to a toy local loop.
Still, I would separate that product fact from operational assumptions. The supplied notes do not document Java-specific guidance for retries, timeouts, observability design, or production runtime architecture. So those concerns should be treated as normal engineering work to verify, not as implied ADK guarantees.
Practical cautions for Java teams
These are not product facts from the docs so much as disciplined evaluation advice.
1. Keep the first tool narrow
The sample tool shape is narrow: one method, one parameter, one clear purpose.
That is usually a good way to start. If your first tool tries to do too much, you can end up debugging contract ambiguity instead of the framework.
2. Do not over-interpret the sample runtime
The sample shows a working path with InMemoryRunner, Session, RunConfig, and Event. That is enough to learn the framework’s shape.
It is not enough, by itself, to answer every production question you may care about later. Treat those questions as verification work, not as assumptions filled in by intuition.
3. Be precise about versions
Say only what the notes support:
- the sample shows
com.google.adk:google-adk:1.2.0 - it also shows
com.google.adk:google-adk-dev:1.2.0
Do not imply those are latest, current, or generally recommended beyond being the versions shown.
4. Keep comparisons to other Java AI stacks as opinion
If you compare ADK with Spring-based approaches or other orchestration styles, make it clear that you are offering a mental-model comparison, not a sourced feature matrix. The supplied notes here are about ADK, not about guaranteed one-to-one comparisons.
My opinionated take
Google ADK Java looks like a credible option for Java teams that want an agent framework in the JVM. The first working path is small and concrete:
BaseAgentdeclared as the root agentLlmAgent.builder()FunctionTool.create(...)RunConfig.builder().build()InMemoryRunnerSessioncreation through the runnerEventfiltering onfinalResponse()
That is enough to understand the basic shape quickly.
The right way to evaluate it is not to claim more than the docs show. It is to take the documented sample seriously, build one small agent, verify tool behavior, inspect the event flow, and then test the deployment and operational questions that matter in your environment.
If a framework survives that kind of disciplined evaluation, it is worth taking seriously. ADK Java appears to merit that look.