
A Deep Dive into Building a Multi-Tool Patent Analysis Agent with ADK Java, Cloud Run, and AlloyDB
Exciting news! The Agent Development Kit (ADK) now has robust Java SDK support, empowering you to build sophisticated, stateful, and tool-driven AI agents directly in your favorite language. Gone are the days of complex boilerplate or wrestling with low-level LLM integrations. With ADK Java, you can focus on crafting intelligent agent logic and seamlessly connect to the tools and services your applications need.
To demonstrate the power and ease of use of the Java ADK, we’ve built a Patent Analysis Agent. This isn’t just a “Hello, World!” — it’s a practical example showcasing:
- Multi-tool architecture
Agents that can decide which tool to use based on the user’s request.
2. Stateful conversations
Using ADK’s InvocationContext to pass information between turns.
3. External service integration
Fetching data from a database (AlloyDB with ScaNN) via a Cloud Run Function.
4. Cloud-native deployment
Running the entire agent on Cloud Run.
Patent Analysis Agent assists the user in finding contextually relevant patents to their search text and upon asking, provides a clear and concise explanation and additional details if required, for a selected patent. Ready to see how it’s done? Let’s dive in!
Our Patent Analysis Agent brings together a few key Google Cloud technologies, all orchestrated by the ADK Java SDK:
- Java Agent Development Kit (ADK)
The star of the show! Handles agent definition, tool management, LLM interaction (with Gemini), session state, and asynchronous event processing.
2. Google Cloud Run
a. Hosts our ADK-powered Patent Analysis Agent application, providing a scalable, serverless environment.
b. Hosts a separate Java Cloud Run Function that acts as an API layer for our database.
3. AlloyDB for PostgreSQL
Our database for storing patent data. We leverage its powerful vector search capabilities with the ScaNN index, inline filtering, and recall evaluation features to find contextually relevant patents with high performance and high recall.
4. Gemini 2.5 (or 2.0) flash/ pro model
The underlying large language model powering the agent’s understanding and decision-making.
The goal is simple. Allow a user to search for patents based on a textual description and then get a detailed explanation of a specific patent from the search results.
- User Query for Patents:
The user asks for patents related to a topic (e.g., “Patents related to sentiment analysis”).
2. ADK Agent — Tool Selection:
The ADK agent, guided by its instructions and the Gemini LLM, selects the getPatents tool.
3. getPatents Tool Execution:
This tool (implemented as a Java method) calls our Java Cloud Run Function.
4. The Cloud Run Function is called:
It executes a vector search query against AlloyDB using the user’s search text. AlloyDB (with ScaNN, inline filtering) efficiently finds the top N relevant patent abstracts. The results are returned to the ADK agent.
5. Agent Responds:
The agent presents the list of patent titles and abstracts to the user. The results are also stored in the session state using InvocationContext.
6. User Query for Explanation:
User provides a patent ID from the previous results (e.g., “<
7. ADK Agent — Tool Selection:
The agent selects the explainPatent tool.
8. explainPatent Tool Execution:
This tool retrieves the full patent details (specifically the abstract in this demo) for the given ID from the session state (thanks to InvocationContext).
9. The tool then passes this abstract to the LLM
This is done via an implicit ADK behavior based on agent instructions) to generate a summary.
10. Agent Responds:
The agent provides a user-friendly explanation of the selected patent.
Getting started with the ADK Java SDK is straightforward. You’ll primarily need to:
- Add Dependencies:
Include the google-adk and google-adk-dev (for the Web UI) artifacts in your pom.xml.
com.google.adk
google-adk
0.1.0
com.google.adk
google-adk-dev
0.1.0
Make sure to reference the pom.xml from the source repository as there are other dependencies and configurations that are needed for the application to be able to run.
2. Configure Your Project:
Ensure your Java version (17+ recommended) and Maven compiler settings are correctly configured in your pom.xml. You can configure your project to follow the below structure:
adk-agents/
└—— pom.xml
└—— src/
└—— main/
└—— java/
└—— agents/
└—— App.java
3. Defining the Agent and Its Tools (App.java):
This is where the magic of the ADK Java SDK shines. We define our agent, its capabilities (instructions), and the tools it can use.
Find a simplified version of a few code snippets of the main agent class here. For the full project refer to the project repo here.
// App.java (Simplified Snippets)
package agents;import com.google.adk.agents.LlmAgent;
import com.google.adk.agents.BaseAgent;
import com.google.adk.agents.InvocationContext;
import com.google.adk.tools.Annotations.Schema;
import com.google.adk.tools.FunctionTool;
// ... other imports
public class App {
static FunctionTool searchTool = FunctionTool.create(App.class, "getPatents");
static FunctionTool explainTool = FunctionTool.create(App.class, "explainPatent");
public static BaseAgent ROOT_AGENT = initAgent();
public static BaseAgent initAgent() {
return LlmAgent.builder()
.name("patent-search-agent")
.description("Patent Search agent")
.model("gemini-2.0-flash-001") // Specify your desired Gemini model
.instruction(
"""
You are a helpful patent search assistant capable of 2 things:
// ... complete instructions ...
""")
.tools(searchTool, explainTool)
.outputKey("patents") // Key to store tool output in session state
.build();
}
// --- Tool: Get Patents ---
public static Map getPatents(
@Schema(name="searchText",description = "The search text for which the user wants to find matching patents")
String searchText) {
try {
String patentsJson = vectorSearch(searchText); // Calls our Cloud Run Function
return Map.of("status", "success", "report", patentsJson);
} catch (Exception e) {
// Log error
return Map.of("status", "error", "report", "Error fetching patents.");
}
}
// --- Tool: Explain Patent (Leveraging InvocationContext) ---
public static Map explainPatent(
@Schema(name="patentId",description = "The patent id for which the user wants to get more explanation for, from the database")
String patentId,
@Schema(name="ctx",description = "The list of patent abstracts from the database from which the user can pick the one to get more explanation for")
InvocationContext ctx) { // Note the InvocationContext
try {
// Retrieve previous patent search results from session state
String previousResults = (String) ctx.session().state().get("patents");
if (previousResults != null && !previousResults.isEmpty()) {
// Logic to find the specific patent abstract from 'previousResults' by 'patentId'
String[] patentEntries = previousResults.split("\n\n\n\n");
for (String entry : patentEntries) {
if (entry.contains(patentId)) { // Simplified check
// The agent will then use its instructions to summarize this 'report'
return Map.of("status", "success", "report", entry);
}
}
}
return Map.of("status", "error", "report", "Patent ID not found in previous search.");
} catch (Exception e) {
// Log error
return Map.of("status", "error", "report", "Error explaining patent.");
}
}
public static void main(String[] args) throws Exception {
InMemoryRunner runner = new InMemoryRunner(ROOT_AGENT);
// ... (Session creation and main input loop - shown in your source)
}
}
Key ADK Java Code Components Highlighted:
- LlmAgent.builder(): Fluent API for configuring your agent.
- .instruction(…): Provides the core prompt and guidelines for the LLM, including when to use which tool.
- FunctionTool.create(App.class, “methodName”): Easily registers your Java methods as tools the agent can invoke. The method name string must match an actual public static method.
- @Schema(description = …): Annotates tool parameters, helping the LLM understand what inputs each tool expects. This description is crucial for accurate tool selection and parameter filling.
- InvocationContext ctx: Passed automatically to tool methods, giving access to session state (ctx.session().state()), user information, and more.
- .outputKey(“patents”): When a tool returns data, ADK can automatically store it in the session state under this key. This is how explainPatent can access the results from getPatents.
7. VECTOR_SEARCH_ENDPOINT: This is a variable that holds the core functional logic for the contextual Q&A for the user in the patent search use case, for which you need to set an updated deployed endpoint value once you implement the Java Cloud Run Function step (next section).
To keep our agent logic clean and to provide a reusable API for database access, we created a simple Java Cloud Run Function. This function takes a search term, queries AlloyDB, and returns the results.
You can find the source code for the Cloud Run Functions, in this repo here. Database setup and instructions can be found here.
AlloyDB Vector Search Magic
The real power in our patent retrieval comes from AlloyDB’s advanced vector search capabilities:
embedding(‘text-embedding-005’, ? )::vector:
This generates a vector embedding for the user’s searchText on the fly using a Google Cloud embedding model.
<=> (Cosine Distance Operator):
Finds patent abstract_embeddings that are semantically closest (most similar) to the query embedding.
ScaNN Index:
We assume a ScaNN index is created on the abstract_embeddings column (CREATE INDEX … USING scann (abstract_embeddings cosine) …). ScaNN (Scalable Nearest Neighbors) provides highly efficient approximate nearest neighbor search.
Inline Filtering (via SET scann.enable_inline_filtering = on):
As detailed in our previous blog post on AlloyDB Quality Control, enabling inline filtering allows AlloyDB to evaluate metadata filters (e.g., WHERE num_claims > 15 from the other blog) during the ScaNN index scan. This massively improves performance for queries with medium selectivity by pruning candidates while the distance is being calculated for the candidates matching the filters. While not explicitly used in this simplified Cloud Function query, it’s a powerful feature to combine with vector search.
Recall Evaluation (evaluate_query_recall function):
AlloyDB also allows you to measure the trade-off between speed (from aNN) and accuracy (kNN) using this function. This lets you tune your index and query parameters for the optimal balance, as discussed in the linked blog.
By abstracting this database logic into a Cloud Run Function, our ADK agent tool (getPatents) simply makes an HTTP call, keeping the agent’s code focused on orchestration.
One of the critical features for building useful agents is managing state across multiple turns of a conversation. ADK’s InvocationContext makes this straightforward.
In our App.java:
- When initAgent() is defined, we use .outputKey(“patents”). This tells ADK that when a tool (like getPatents) returns data in its report field, that data should be stored in the session state under the key “patents”.
- In the explainPatent tool method, we inject InvocationContext ctx:
public static Map explainPatent(
@Schema(description = "...") String patentId, InvocationContext ctx) {
String previousResults = (String) ctx.session().state().get("patents");
// ... use previousResults ...
}
This allows the explainPatent tool to access the patent list fetched by the getPatents tool in a previous turn, making the conversation stateful and coherent.
You’ll need to export two environment variables:
- a Gemini key that you can get from AI Studio,
- a variable to specify we’re not using Vertex AI this time.
export GOOGLE_GENAI_USE_VERTEXAI=FALSE
export GOOGLE_API_KEY=AIzaSyDF...
To launch this first agent, use the following Maven command in your terminal:
mvn compile exec:java -DmainClass="agents.App"
For this you would see the interactive response from the agent in your terminal.
Deploying your ADK Java agent to Cloud Run is similar to deploying any other Java application:
- Dockerfile: Create a Dockerfile to package your Java application. You can copy the content for Docker file from the repo.
- Build & Push Docker Image: Use Google Cloud Build and Artifact Registry.
- You can perform the above steps & deploy to Cloud Run in just one command:
gcloud run depoy --source . --set-env-vars GOOGLE_API_KEY=<>
Similarly, you’d deploy your Java Cloud Run Function (gcfv2.PatentSearch). Alternatively, you can create and deploy the Java Cloud Run Function for the database logic directly from the Cloud Run Function console.
The ADK comes with a handy Web UI for local testing and debugging of your agent. When you run your App.java locally (e.g., mvn exec:java -Dexec.mainClass=”agents.App” if configured, or just running the main method), the ADK typically starts a local web server.
The ADK Web UI allows you to:
- Send messages to your agent.
- See the events (user message, tool call, tool response, LLM response).
- Inspect session state.
- View logs and traces.
This is invaluable during development for understanding how your agent processes requests and uses its tools. This assumes your mainClass in pom.xml is set to com.google.adk.web.AdkWebServer and your agent is registered with it, or you are running a local test runner that exposes this.
When running your App.java with its InMemoryRunner and Scanner for console input, you’re testing the core agent logic. The Web UI is a separate component for a more visual debugging experience, often used when ADK is serving your agent over HTTP.
You can use the following Maven command from your root directory to launch the SpringBoot local server:
mvn compile exec:java -Dexec.args="--adk.agents.source-dir=src/main/java/ --logging.level.com.google.adk.dev=TRACE --logging.level.com.google.adk.demo.agents=TRACE"
The interface is accessible at the URL you see as output for the above command if you’re running the AdkWebServer from google-adk-dev or if it is Cloud Run deployed, you should be able to access it from the Cloud Run deployed link.
You should be able to see the result in an interactive interface.
Check out the video below for our deployed Patent Agent:
This Patent Analysis Agent is just one example of what you can build with the ADK Java SDK. The combination of ADK’s agent orchestration capabilities, the power of Gemini models, seamless tool integration, and the robustness of the Java ecosystem opens up a world of possibilities for systems like Intelligent customer support bots, Data analysis and reporting agents, Code generation and explanation tools, Complex workflow automation agents, and much more!
We’re incredibly excited to see what the Java community will build with the Agent Development Kit.
Get Started Today!
ADK Documentation:
https://google.github.io/adk-docs/
Patent Analysis Agent Source Code: https://github.com/AbiramiSukumaran/patent-search-agent
Getting Started with ADK Java:
https://glaforge.dev/posts/2025/05/20/writing-java-ai-agents-with-adk-for-java-getting-started/
Join the ADK Community:
https://www.reddit.com/r/agentdevelopmentkit
Happy agent building!
Source Credit: https://medium.com/google-cloud/build-powerful-stateful-ai-agents-in-java-with-agent-development-kit-adk-0f7e2cd3d094?source=rss—-e52cf94d98af—4