1. Добро пожаловать, разработчики ИИ-агентов!
В этой лабораторной работе вы научитесь создавать агентов ИИ на Java с помощью Agents Development Kit (ADK) для Java . Мы выйдем за рамки простых вызовов API LLM и создадим автономных агентов ИИ, которые смогут рассуждать, планировать, использовать инструменты и работать сообща для решения сложных задач.
Вы начнете с погашения кредитов Google Cloud, настройки своей среды Google Cloud, затем создадите своего первого простого агента и постепенно добавите более продвинутые возможности, такие как пользовательские инструменты, веб-поиск и многоагентная оркестровка.
Чему вы научитесь
- Как создать простого ИИ-агента, управляемого личностью.
- Как предоставить агентам специальные и встроенные инструменты (например, Google Search).
- Как добавить собственные инструменты, реализованные на Java.
- Как объединить несколько агентов в мощные последовательные, параллельные и циклические рабочие процессы.
Что вам понадобится
- Веб-браузер, который мы будем использовать в режиме инкогнито.
- Личный аккаунт Gmail.
- Новый проект Google Cloud, связанный с вашим личным аккаунтом Gmail.
- Платежный аккаунт, созданный с использованием погашенных кредитов Google Cloud.
- Инструмент командной строки git для проверки исходного кода.
- Java 17+ и Apache Maven.
- Текстовый редактор или IDE, например IntelliJ IDEA или VS Code.
Встроенный редактор VS Code можно использовать в Cloud Shell, в консоли Google Cloud.
2. Настройка: ваша среда
Получение кредитов Google Cloud для участия в семинаре
За участие в семинаре под руководством инструктора вы получите ссылку на веб-сайт, где вы сможете запросить облачные кредиты Google для использования во время семинара.
- Используйте личную учетную запись Google : важно использовать личную учетную запись Google (например, адрес gmail.com), так как корпоративные или школьные адреса электронной почты не подойдут .
- Используйте Google Chrome в режиме инкогнито : это рекомендуется для создания чистого сеанса и предотвращения конфликтов с другими учетными записями Google.
- Используйте специальную ссылку на событие : следует использовать специальную ссылку на событие, которая выглядит примерно так: https://trygcp.dev/event/xxx , за которой следует код события (в данном примере «xxx»).
- Примите условия обслуживания : после входа в систему вам будут представлены условия обслуживания Google Cloud Platform, которые вам необходимо принять для продолжения.
- Создайте новый проект : необходимо создать новый пустой проект из Google Cloud Console .
- Привязать платежный аккаунт : привязать недавно созданный проект к платежному аккаунту.
- Подтверждение кредита : в следующем видео показано, как подтвердить, что кредит применен к проекту, проверив раздел «Кредиты» на странице выставления счетов.
Не забудьте посмотреть это видео , в котором объясняется, как использовать и использовать кредиты.
Создайте и настройте свой ключ API
Для аутентификации ваших агентов ADK AI с API Gemini для этой лабораторной работы вам понадобится ключ API, связанный с вашим проектом Google Cloud.
- Сгенерируйте ключ API:
- Перейдите в Google AI Studio и нажмите ссылку «Получить ключ API» в нижней части левой панели.
- Выберите Проекты , а затем нажмите кнопку Импорт проектов .
- Найдите и выберите проект Google Cloud, который вы хотите импортировать, затем нажмите кнопку Импорт .
- После импорта проекта перейдите на страницу «Ключи API» из меню панели инструментов и создайте ключ API в только что импортированном проекте.
- Запишите ключ API .
- Установите переменную среды: вашему агенту необходим доступ к этому ключу. Стандартный способ — установить переменную среды с именем
GOOGLE_API_KEY
.
- macOS / Linux: Откройте терминал и выполните следующую команду, заменив
"your-api-key"
на только что скопированный ключ. Чтобы сделать это постоянным, добавьте эту строку в файл запуска вашей оболочки (например,~/.bash_profile
,~/.zshrc
).
export GOOGLE_API_KEY="your-api-key"
- Windows (командная строка): Откройте новую командную строку и выполните:
setx GOOGLE_API_KEY "your-api-key"
- Чтобы изменения вступили в силу, вам потребуется перезапустить командную строку.
- Windows (PowerShell): Откройте терминал PowerShell и выполните:
$env:GOOGLE_API_KEY="your-api-key"
- Чтобы сделать это изменение постоянным в PowerShell, вам необходимо добавить его в скрипт профиля.
3. Начало работы: ваш первый агент
Лучший способ начать новый проект — использовать шаблон ADK for Java GitHub . Он предоставляет структуру проекта и все необходимые зависимости.
Если у вас есть учетная запись Github, вы можете сделать следующее: Use this template
> Create a new repository
, а затем извлечь код локально с помощью команды git clone
.
Вот скриншот, показывающий верхнее правое меню для использования шаблона.
Другой подход — просто клонировать этот репозиторий напрямую, с помощью:
git clone https://github.com/glaforge/adk-java-maven-template.git
Затем cd
в adk-java-maven-template
.
Чтобы проверить, что вы готовы приступить к написанию своего первого ИИ-агента на Java, убедитесь, что вы можете скомпилировать код в этом проекте с помощью:
mvn compile
Шаг кода: дружелюбный агент учителя естественных наук
Базовым строительным блоком ADK является класс LlmAgent
. Представьте его как ИИ с определённой личностью и целью, управляемый большой языковой моделью. Позже мы добавим дополнительные возможности с помощью инструментов и сделаем его ещё мощнее, тесно взаимодействуя с другими подобными агентами.
Давайте создадим новый класс Java в пакете com.example.agent
и назовем его ScienceTeacher
.
Это «Привет, мир!» в создании агента. Мы создаём простого агента в образе учителя естественных наук.
// src/main/java/com/example/agent/ScienceTeacher.java
package com.example.agent;
import com.google.adk.agents.LlmAgent;
import com.google.adk.web.AdkWebServer;
public class ScienceTeacher {
public static void main(String[] args) {
AdkWebServer.start(
LlmAgent.builder()
.name("science-teacher")
.description("A friendly science teacher")
.instruction("""
You are a science teacher for teenagers.
You explain science concepts in a simple, concise and direct way.
""")
.model("gemini-2.5-flash")
.build()
);
}
}
ИИ-агент настраивается с помощью метода LlmAgent.builder()
. Параметры name()
, description()
и model()
являются обязательными, и для придания агенту определённой индивидуальности и правильного поведения необходимо предоставлять подробные инструкции с помощью метода instruction()
.
Здесь мы решили использовать модель Gemini 2.5 Flash, но вы также можете попробовать Gemini 2.5 Pro для более сложных задач.
Этот агент обёрнут в метод AdkWebServer.start()
. Это так называемый интерфейс чата ADK Dev UI . Он позволяет общаться с агентом через обычный интерфейс чата. Кроме того, он очень полезен, если вы хотите понять, что происходит «под капотом», например, все события, происходящие в системе, запросы и ответы, отправляемые в LLM.
Чтобы скомпилировать и запустить этот агент локально, выполните следующую команду:
mvn compile exec:java -Dexec.mainClass=com.example.agent.ScienceTeacher
Затем перейдите в браузере по адресу http://localhost:8080 . Вы увидите интерфейс, показанный на скриншоте ниже. Задавайте своему агенту вопросы, связанные с наукой.
4. Предоставление агентам инструментов
Зачем агентам инструменты? Магистратура LLM обладает мощным потенциалом, но их знания заморожены во времени, и они не могут взаимодействовать с внешним миром. Инструменты — это мост. Они позволяют агенту получать доступ к информации в режиме реального времени (например, к ценам на акции или новостям), обращаться к закрытым API и выполнять любые действия, которые можно запрограммировать на Java.
Шаг кода: создание пользовательского инструмента ( StockTicker
)
Здесь мы предоставляем нашему агенту инструмент для поиска цен на акции. Агент считает, что когда пользователь запрашивает цену, он должен вызвать наш Java-метод.
// src/main/java/com/example/agent/StockTicker.java
package com.example.agent;
import com.google.adk.agents.LlmAgent;
import com.google.adk.tools.Annotations.Schema;
import com.google.adk.tools.FunctionTool;
import com.google.adk.web.AdkWebServer;
import java.util.Map;
public class StockTicker {
public static void main(String[] args) {
AdkWebServer.start(
LlmAgent.builder()
.name("stock_agent")
.instruction("""
You are a stock exchange ticker expert.
When asked about the stock price of a company,
use the `lookup_stock_ticker` tool to find the information.
""")
.model("gemini-2.5-flash")
.tools(FunctionTool.create(StockTicker.class, "lookupStockTicker"))
.build()
);
}
@Schema(
name = "lookup_stock_ticker",
description = "Lookup stock price for a given company or ticker"
)
public static Map<String, String> lookupStockTicker(
@Schema(name = "company_name_or_stock_ticker", description = "The company name or stock ticker")
String ticker) {
// ... (logic to return a stock price)
}
}
Чтобы сделать агентов умнее и дать им возможность взаимодействовать с миром (или с вашим собственным кодом, API, сервисами и т. д.), вы можете настроить агента на использование инструментов, в частности инструментов пользовательского кода, с помощью метода tools()
, передав ему FunctionTool.create(...)
.
FunctionTool
необходимы класс и имя метода, указывающие на ваш собственный статический метод. Также можно передать экземпляр класса и имя метода экземпляра этого объекта.
Важно указать name
и description
как метода, так и его параметров с помощью аннотации @Schema
, поскольку эта информация будет использоваться базовой LLM для определения того, когда и как следует вызывать данный метод.
Не менее важно помочь LLM, дав чёткие инструкции о том, как и когда использовать инструмент. Модель, возможно, разберётся в этом сама, но если вы дадите подробные объяснения в методе instruction()
, ваша функция будет вызываться правильно, то вероятность её вызова повысится.
Этот метод должен возвращать Map
. Обычно идея заключается в том, чтобы вернуть карту с ключом, представляющим результат, например, stock_price
, и связать с ним значение цены акций. В конечном итоге можно добавить дополнительную пару ключей «успех/истина» для подтверждения успешного выполнения операции. В случае ошибки следует вернуть карту с ключом, например, error
, и связать сообщение об ошибке с соответствующим значением. Это помогает LLM понять, был ли вызов успешным или нет по какой-либо причине.
- В случае успеха вернуть:
{"stock_price": 123}
- В случае ошибки вернуть:
{"error": "Impossible to retrieve stock price for XYZ"}
Затем запустите этот класс с помощью следующей команды:
mvn compile exec:java -Dexec.mainClass=com.example.agent.StockTicker
5. Возможности поиска Google для получения актуальной информации
ADK для Java включает в себя ряд мощных инструментов, среди которых — GoogleSearchTool
. С помощью этого инструмента ваш агент может запросить использование Google Search для поиска релевантной информации и достижения своей цели.
Действительно, знания LLM заморожены во времени: они обучались до определённой даты («даты окончания») на данных, которые были актуальны на момент сбора информации. Это означает, что LLM может не знать о недавних событиях, или их знания могут быть ограниченными и поверхностными, и поисковая система может освежить их память или помочь им узнать больше по теме.
Давайте посмотрим на этот простой агент поиска новостей:
// src/main/java/com/example/agent/LatestNews.java
package com.example.agent;
import java.time.LocalDate;
import com.google.adk.agents.LlmAgent;
import com.google.adk.tools.GoogleSearchTool;
import com.google.adk.web.AdkWebServer;
public class LatestNews {
public static void main(String[] args) {
AdkWebServer.start(LlmAgent.builder()
.name("news-search-agent")
.description("A news search agent")
.instruction("""
You are a news search agent.
Use the `google_search` tool
when asked to search for recent events and information.
Today is \
""" + LocalDate.now())
.model("gemini-2.5-flash")
.tools(new GoogleSearchTool())
.build());
}
}
Обратите внимание, как мы передали экземпляр инструмента поиска с помощью: tools(new GoogleSearchTool())
. Это позволяет нашему агенту быстро получать доступ к последней информации, которую можно найти в интернете. Кроме того, в подсказке указана дата, которая может помочь магистрам права понять, когда вопросы касаются информации из прошлого, а требуется поиск более актуальной информации.
Затем запустите этот класс с помощью следующей команды:
mvn compile exec:java -Dexec.mainClass=com.example.agent.LatestNews
Не стесняйтесь экспериментировать с подсказками, запрашивать разные результаты с точки зрения стиля, краткости, фокуса и т. д.
Шаг кода: поисковый агент как инструмент
Вместо того чтобы передавать GoogleSearchTool
агенту напрямую в качестве инструмента, вы можете создать специальный поисковый агент, который инкапсулирует функциональность поиска, и предоставить этот агент в качестве инструмента агенту более высокого уровня.
Это продвинутая концепция, позволяющая делегировать сложные функции (например, поиск и суммирование результатов) специализированному субагенту. Такой подход часто полезен для более сложных рабочих процессов, поскольку встроенные инструменты невозможно использовать с инструментами, написанными на основе пользовательского кода.
// src/main/java/com/example/agent/SearchAgentAsTool.java
package com.example.agent;
import java.time.LocalDate;
import com.google.adk.agents.LlmAgent;
import com.google.adk.tools.AgentTool;
import com.google.adk.tools.GoogleSearchTool;
import com.google.adk.web.AdkWebServer;
public class SearchAgentAsTool {
public static void main(String[] args) {
// 1. Define the specialized Search Agent
LlmAgent searchAgent = LlmAgent.builder()
.name("news-search-agent-tool")
.description("Searches for recent events and provides a concise summary.")
.instruction("""
You are a concise information retrieval specialist.
Use the `google_search` tool to find information.
Always provide the answer as a short,
direct summary, without commentary.
Today is \
""" + LocalDate.now())
.model("gemini-2.5-flash")
.tools(new GoogleSearchTool()) // This agent uses the Google Search Tool
.build();
// 2. Wrap the Search Agent as a Tool
AgentTool searchTool = AgentTool.create(searchAgent);
// 3. Define the Main Agent that uses the Search Agent Tool
AdkWebServer.start(LlmAgent.builder()
.name("main-researcher")
.description("Main agent for answering complex, up-to-date questions.")
.instruction("""
You are a sophisticated research assistant.
When the user asks a question that requires up-to-date or external information,
you MUST use the `news-search-agent-tool` to get the facts before answering.
After the tool returns the result, synthesize the final answer for the user.
""")
.model("gemini-2.5-flash")
.tools(searchTool) // This agent uses the Search Agent as a tool
.build()
);
}
}
Ключевую концепцию здесь играет строка AgentTool.create(searchAgent)
. Она регистрирует весь searchAgent
(с собственной внутренней логикой, подсказками и инструментами) как единый вызываемый инструмент для mainAgent
. Это обеспечивает модульность и возможность повторного использования.
Запустите этот класс с помощью следующей команды:
mvn compile exec:java -Dexec.mainClass=com.example.agent.SearchAgentAsTool
На повседневные вопросы агент будет отвечать, используя собственную базу знаний, но если его спросят о недавних событиях, он делегирует поиск специализированному поисковому агенту, использующему инструмент поиска Google.
6. Освоение агентских рабочих процессов
Для решения сложных задач одного агента недостаточно. Когда перед студентами ставят цель, состоящую из слишком большого количества подзадач, с обширным подсказкой, объясняющей слишком много деталей, или с доступом к огромному количеству функций, они испытывают трудности, а их производительность и точность снижаются.
Ключ к успеху — принцип «разделяй и властвуй», основанный на использовании нескольких специализированных агентов. К счастью, ADK включает в себя несколько встроенных специализированных агентов:
- Обычный агент с
subAgent()
для делегирования задач, -
SequentialAgent
, для выполнения задач в определенной последовательности, -
ParallelAgent
, для параллельного выполнения агентов, -
LoopAgent
, обычно для прохождения процесса уточнения столько раз, сколько необходимо.
Каковы основные варианты использования, а также плюсы и минусы каждого рабочего процесса? Взгляните на таблицу ниже. Но знайте, что настоящий эффект достигается при сочетании нескольких из них!
Рабочий процесс | Класс АДК | Вариант использования | Плюсы | Минусы |
Субагенты | | Управляемые пользователем гибкие задачи, где следующий шаг не всегда известен. | Высокая гибкость, диалоговый подход, отлично подходит для ботов, взаимодействующих с пользователем. | Менее предсказуем, полагается на рассуждения LLM для управления потоком. |
Последовательный | | Фиксированные, многоэтапные процессы, в которых порядок имеет решающее значение. | Предсказуемый, надежный, легко отлаживаемый, гарантирует порядок. | Негибкий, может работать медленнее, если задачи можно распараллелить. |
Параллельный | | Сбор данных из нескольких источников или выполнение независимых задач. | Высокая эффективность, значительно сокращает задержку для задач, связанных с вводом-выводом. | Все задачи выполняются; без коротких замыканий. Менее подходит для задач с зависимостями. |
Петля | | Итеративное уточнение, самокоррекция или процессы, которые повторяются до тех пор, пока не будет выполнено условие. | Эффективен для комплексного решения проблем, позволяет агентам улучшить свою работу. | Может привести к бесконечным циклам, если проект не был тщательно спроектирован (всегда используйте maxIterations!). |
7. Агентские рабочие процессы — делегирование с помощью субагентов
Агент-супервайзер может делегировать отдельные задачи субагентам. Например, представьте себе агента интернет-магазина, который делегирует вопросы, связанные с заказом, одному агенту («Какой статус у моего заказа?»), а вопросы, связанные с послепродажным обслуживанием, — другому («Я не знаю, как это включить!»). Именно этот вариант использования мы и рассмотрим.
// src/main/java/com/example/agent/SupportAgent.java
package com.example.agent;
import com.google.adk.agents.LlmAgent;
import com.google.adk.web.AdkWebServer;
public class SupportAgent {
public static void main(String[] args) {
LlmAgent topicSearchAgent = LlmAgent.builder()
.name("order-agent")
.description("Order agent")
.instruction("""
Your role is to help our customers
with all the questions they may have about their orders.
Always respond that the order has been received, prepared,
and is now out for delivery.
""")
.model("gemini-2.5-flash")
.build();
LlmAgent socialMediaAgent = LlmAgent.builder()
.name("after-sale-agent")
.description("After sale agent")
.instruction("""
You are an after sale agent,
helping customers with the product they received.
When a customer has a problem,
suggest the person to switch the product off and on again.
""")
.model("gemini-2.5-flash")
.build();
AdkWebServer.start(LlmAgent.builder()
.name("support-agent")
.description("Customer support agent")
.instruction("""
Your role is help our customers.
Call the `order-agent` for all questions related to order status.
Call the `after-sale-agent` for inquiries about the received product.
""")
.model("gemini-2.5-flash")
.subAgents(socialMediaAgent, topicSearchAgent)
.build()
);
}
}
Ключевой строкой здесь является вызов метода subAgents()
, передающего два подагента, чья конкретная роль будет выполняться отдельно друг от друга.
Запустите приведенный выше пример с помощью следующей команды:
mvn compile exec:java -Dexec.mainClass=com.example.agent.SupportAgent
Такая концепция делегирования задач субагентам отражает эффективное управление персоналом, при котором хороший менеджер (субагент-руководитель) полагается на специализированных сотрудников (субагентов) для выполнения конкретных задач, в которых они обладают большей компетенцией. Супервайзеру не нужно знать все тонкости каждого процесса; вместо этого он разумно направляет запрос клиента (например, запрос на заказ или решение технической проблемы) наиболее квалифицированному «члену команды», обеспечивая более качественный и эффективный ответ, чем мог бы предоставить один специалист-универсал. Более того, эти субагенты могут полностью сосредоточиться на своих индивидуальных заданиях, не разбираясь во всей сложности комплексного процесса.
8. Агентские рабочие процессы — конвейер
Когда порядок операций имеет значение, используйте SequentialAgent
. Он подобен конвейеру, выполняющему подагенты в фиксированном порядке, где каждый шаг может зависеть от предыдущего.
Давайте представим, что английский поэт сотрудничает с англо-французским переводчиком, чтобы сначала создать стихотворения на английском языке, а затем перевести их на французский:
// src/main/java/com/example/agent/PoetAndTranslator.java
package com.example.agent;
import com.google.adk.agents.LlmAgent;
import com.google.adk.agents.SequentialAgent;
import com.google.adk.web.AdkWebServer;
public class PoetAndTranslator {
public static void main(String[] args) {
LlmAgent poet = LlmAgent.builder()
.name("poet-agent")
.description("Poet writing poems")
.model("gemini-2.5-flash")
.instruction("""
You are a talented poet,
who writes short and beautiful poems.
""")
.outputKey("poem")
.build();
LlmAgent translator = LlmAgent.builder()
.name("translator-agent")
.description("English to French translator")
.model("gemini-2.5-flash")
.instruction("""
As an expert English-French translator,
your role is to translate the following poem into French,
ensuring the poem still rhymes even after translation:
{poem}
""")
.outputKey("translated-poem")
.build();
AdkWebServer.start(SequentialAgent.builder()
.name("poet-and-translator")
.subAgents(poet, translator)
.build());
}
}
Запустите пример, чтобы получить стихотворение на английском языке, а затем перевести его на французский, с помощью следующей команды:
mvn compile exec:java -Dexec.mainClass=com.example.agent.PoetAndTranslator
Такое систематическое разложение сложных задач на более мелкие, упорядоченные подзадачи обеспечивает более детерминированный и надежный процесс, значительно повышая вероятность успешного результата по сравнению с опорой на одного широкомасштабного агента.
Эффективное разложение задачи на последовательность подзадач (когда это возможно и когда это имеет смысл) имеет решающее значение для достижения более детерминированных и успешных результатов, поскольку позволяет структурировать прогресс и управлять зависимостями между шагами.
9. Агентные рабочие процессы — параллельная работа
Когда задачи независимы, ParallelAgent
обеспечивает значительное повышение эффективности, выполняя их одновременно. В следующем примере мы даже объединим SequentialAgent
с ParallelAgent
: сначала выполняются параллельные задачи, а затем финальный агент суммирует результаты параллельных задач.
Давайте создадим компанию-детектива, чья работа будет заключаться в поиске информации о:
- Профиль компании (генеральный директор, штаб-квартира, девиз и т. д.)
- Последние новости о компании.
- Подробная информация о финансовых показателях компании.
// src/main/java/com/example/agent/CompanyDetective.java
package com.example.agent;
import com.google.adk.agents.LlmAgent;
import com.google.adk.agents.ParallelAgent;
import com.google.adk.agents.SequentialAgent;
import com.google.adk.tools.GoogleSearchTool;
import com.google.adk.web.AdkWebServer;
public class CompanyDetective {
public static void main(String[] args) {
var companyProfiler = LlmAgent.builder()
.name("company-profiler")
.description("Provides a general overview of a company.")
.instruction("""
Your role is to provide a brief overview of the given company.
Include its mission, headquarters, and current CEO.
Use the Google Search Tool to find this information.
""")
.model("gemini-2.5-flash")
.tools(new GoogleSearchTool())
.outputKey("profile")
.build();
var newsFinder = LlmAgent.builder()
.name("news-finder")
.description("Finds the latest news about a company.")
.instruction("""
Your role is to find the top 3-4 recent news headlines for the given company.
Use the Google Search Tool.
Present the results as a simple bulleted list.
""")
.model("gemini-2.5-flash")
.tools(new GoogleSearchTool())
.outputKey("news")
.build();
var financialAnalyst = LlmAgent.builder()
.name("financial-analyst")
.description("Analyzes the financial performance of a company.")
.instruction("""
Your role is to provide a snapshot of the given company's recent financial performance.
Focus on stock trends or recent earnings reports.
Use the Google Search Tool.
""")
.model("gemini-2.5-flash")
.tools(new GoogleSearchTool())
.outputKey("financials")
.build();
var marketResearcher = ParallelAgent.builder()
.name("market-researcher")
.description("Performs comprehensive market research on a company.")
.subAgents(
companyProfiler,
newsFinder,
financialAnalyst
)
.build();
var reportCompiler = LlmAgent.builder()
.name("report-compiler")
.description("Compiles a final market research report.")
.instruction("""
Your role is to synthesize the provided information into a coherent market research report.
Combine the company profile, latest news, and financial analysis into a single, well-formatted report.
## Company Profile
{profile}
## Latest News
{news}
## Financial Snapshot
{financials}
""")
.model("gemini-2.5-flash")
.build();
AdkWebServer.start(SequentialAgent.builder()
.name("company-detective")
.description("Collects various information about a company.")
.subAgents(
marketResearcher,
reportCompiler
).build());
}
}
Как обычно, запустить агент можно с помощью следующей команды:
mvn compile exec:java -Dexec.mainClass=com.example.agent.CompanyDetective
Этот агент демонстрирует мощное сочетание рабочих процессов, при этом как параллельные, так и последовательные агенты эффективно используются благодаря распараллеливанию информационного исследования и синтеза.
10. Агентные рабочие процессы — итеративное уточнение
Для задач, требующих цикла «генерация → проверка → уточнение», используйте LoopAgent
. Он автоматизирует итеративное улучшение до достижения цели. Подобно SequentialAgent
, LoopAgent
последовательно вызывает подагент, но возвращается к началу цикла. Именно LLM, используемый внутри агента, решает, следует ли запрашивать вызов специального инструмента, встроенного в exit_loop
, для остановки выполнения цикла.
Приведённый ниже пример уточнения кода с использованием LoopAgent
автоматизирует уточнение кода: генерация, проверка, исправление. Это имитирует процесс разработки кода человеком. Генератор кода сначала генерирует запрошенный код, сохраняет его в состоянии агента под ключом generated_code
. Затем проверяющий код проверяет сгенерированный код и либо предоставляет обратную связь (под ключом feedback
), либо вызывает инструмент выхода из цикла для досрочного завершения итерации.
Давайте посмотрим на код:
// src/main/java/com/example/agent/CodeRefiner.java
package com.example.agent;
import com.google.adk.agents.LlmAgent;
import com.google.adk.agents.LoopAgent;
import com.google.adk.agents.SequentialAgent;
import com.google.adk.tools.ExitLoopTool;
import com.google.adk.web.AdkWebServer;
public class CodeRefiner {
public static void main(String[] args) {
var codeGenerator = LlmAgent.builder()
.name("code-generator")
.description("Writes and refines code based on a request and feedback.")
.instruction("""
Your role is to write a Python function based on the user's request.
In the first turn, write the initial version of the code.
In subsequent turns, you will receive feedback on your code.
Your task is to refine the code based on this feedback.
Previous feedback (if any):
{feedback?}
""")
.model("gemini-2.5-flash")
.outputKey("generated_code")
.build();
var codeReviewer = LlmAgent.builder()
.name("code-reviewer")
.description("Reviews code and decides if it's complete or needs more work.")
.instruction("""
Your role is to act as a senior code reviewer.
Analyze the provided Python code for correctness, style, and potential bugs.
Code to review:
{generated_code}
If the code is perfect and meets the user's request,
you MUST call the `exit_loop` tool.
Otherwise, provide constructive feedback for the `code-generator to improve the code.
""")
.model("gemini-2.5-flash")
.outputKey("feedback")
.tools(ExitLoopTool.INSTANCE)
.build();
var codeRefinerLoop = LoopAgent.builder()
.name("code-refiner-loop")
.description("Iteratively generates and reviews code until it is correct.")
.subAgents(
codeGenerator,
codeReviewer
)
.maxIterations(3) // Safety net to prevent infinite loops
.build();
var finalPresenter = LlmAgent.builder()
.name("final-presenter")
.description("Presents the final, accepted code to the user.")
.instruction("""
The code has been successfully generated and reviewed.
Present the final version of the code to the user in a clear format.
Final Code:
{generated_code}
""")
.model("gemini-2.5-flash")
.build();
AdkWebServer.start(SequentialAgent.builder()
.name("code-refiner-assistant")
.description("Manages the full code generation and refinement process.")
.subAgents(
codeRefinerLoop,
finalPresenter)
.build());
}
}
Запустите этот агент с помощью следующей команды:
mvn compile exec:java -Dexec.mainClass=com.example.agent.CodeRefiner
Циклы обратной связи/уточнения, реализованные с помощью LoopAgent
, незаменимы для решения задач, требующих итеративного улучшения и самокоррекции, точно имитируя когнитивные процессы человека. Этот шаблон проектирования особенно полезен для задач, где исходный результат редко бывает идеальным, таких как генерация кода, творческое написание, итерации дизайна или сложный анализ данных. Пропуская результат через специализированного агента-рецензента, предоставляющего структурированную обратную связь, генерирующий агент может непрерывно совершенствовать свою работу до тех пор, пока не будет достигнут предопределенный критерий завершения, что приводит к заметно более высокому качеству и надежности конечных результатов, чем при однопроходном подходе.
11. Поздравляем!
Вы успешно создали и исследовали различные ИИ-агенты, от простых диалоговых агентов до сложных многоагентных систем. Вы изучили основные концепции ADK для Java: определение агентов с помощью инструкций, предоставление им инструментов и организация их в мощные рабочие процессы.
Что дальше?
- Изучите официальный репозиторий ADK для Java на GitHub .
- Дополнительную информацию о фреймворке можно найти в его документации .
- Узнайте в этой серии блогов о различных рабочих процессах агентов и о различных доступных инструментах .
- Изучите подробнее другие встроенные инструменты и расширенные обратные вызовы.
- Управляйте контекстом, состоянием и артефактами для более содержательного и мультимодального взаимодействия.
- Внедряйте и применяйте плагины, которые подключаются к жизненному циклу ваших агентов.
- Попробуйте создать собственного агента, решающего реальную проблему!
1. Добро пожаловать, разработчики ИИ-агентов!
В этой лабораторной работе вы научитесь создавать агентов ИИ на Java с помощью Agents Development Kit (ADK) для Java . Мы выйдем за рамки простых вызовов API LLM и создадим автономных агентов ИИ, которые смогут рассуждать, планировать, использовать инструменты и работать сообща для решения сложных задач.
Вы начнете с погашения кредитов Google Cloud, настройки своей среды Google Cloud, затем создадите своего первого простого агента и постепенно добавите более продвинутые возможности, такие как пользовательские инструменты, веб-поиск и многоагентная оркестровка.
Чему вы научитесь
- Как создать простого ИИ-агента, управляемого личностью.
- Как предоставить агентам специальные и встроенные инструменты (например, Google Search).
- Как добавить собственные инструменты, реализованные на Java.
- Как объединить несколько агентов в мощные последовательные, параллельные и циклические рабочие процессы.
Что вам понадобится
- Веб-браузер, который мы будем использовать в режиме инкогнито.
- Личный аккаунт Gmail.
- Новый проект Google Cloud, связанный с вашим личным аккаунтом Gmail.
- Платежный аккаунт, созданный с использованием погашенных кредитов Google Cloud.
- Инструмент командной строки git для проверки исходного кода.
- Java 17+ и Apache Maven.
- Текстовый редактор или IDE, например IntelliJ IDEA или VS Code.
Встроенный редактор VS Code можно использовать в Cloud Shell, в консоли Google Cloud.
2. Настройка: ваша среда
Получение кредитов Google Cloud для участия в семинаре
За участие в семинаре под руководством инструктора вы получите ссылку на веб-сайт, где вы сможете запросить облачные кредиты Google для использования во время семинара.
- Используйте личную учетную запись Google : важно использовать личную учетную запись Google (например, адрес gmail.com), так как корпоративные или школьные адреса электронной почты не подойдут .
- Используйте Google Chrome в режиме инкогнито : это рекомендуется для создания чистого сеанса и предотвращения конфликтов с другими учетными записями Google.
- Используйте специальную ссылку на событие : следует использовать специальную ссылку на событие, которая выглядит примерно так: https://trygcp.dev/event/xxx , за которой следует код события (в данном примере «xxx»).
- Примите условия обслуживания : после входа в систему вам будут представлены условия обслуживания Google Cloud Platform, которые вам необходимо принять для продолжения.
- Создайте новый проект : необходимо создать новый пустой проект из Google Cloud Console .
- Привязать платежный аккаунт : привязать недавно созданный проект к платежному аккаунту.
- Подтверждение кредита : в следующем видео показано, как подтвердить, что кредит применен к проекту, проверив раздел «Кредиты» на странице выставления счетов.
Не забудьте посмотреть это видео , в котором объясняется, как использовать и использовать кредиты.
Создайте и настройте свой ключ API
Для аутентификации ваших агентов ADK AI с API Gemini для этой лабораторной работы вам понадобится ключ API, связанный с вашим проектом Google Cloud.
- Сгенерируйте ключ API:
- Перейдите в Google AI Studio и нажмите ссылку «Получить ключ API» в нижней части левой панели.
- Выберите Проекты , а затем нажмите кнопку Импорт проектов .
- Найдите и выберите проект Google Cloud, который вы хотите импортировать, затем нажмите кнопку Импорт .
- После импорта проекта перейдите на страницу «Ключи API» из меню панели инструментов и создайте ключ API в только что импортированном проекте.
- Запишите ключ API .
- Установите переменную среды: вашему агенту необходим доступ к этому ключу. Стандартный способ — установить переменную среды с именем
GOOGLE_API_KEY
.
- macOS / Linux: Откройте терминал и выполните следующую команду, заменив
"your-api-key"
на только что скопированный ключ. Чтобы сделать это постоянным, добавьте эту строку в файл запуска вашей оболочки (например,~/.bash_profile
,~/.zshrc
).
export GOOGLE_API_KEY="your-api-key"
- Windows (командная строка): Откройте новую командную строку и выполните:
setx GOOGLE_API_KEY "your-api-key"
- Чтобы изменения вступили в силу, вам потребуется перезапустить командную строку.
- Windows (PowerShell): Откройте терминал PowerShell и выполните:
$env:GOOGLE_API_KEY="your-api-key"
- Чтобы сделать это изменение постоянным в PowerShell, вам необходимо добавить его в скрипт профиля.
3. Начало работы: ваш первый агент
Лучший способ начать новый проект — использовать шаблон ADK for Java GitHub . Он предоставляет структуру проекта и все необходимые зависимости.
Если у вас есть учетная запись Github, вы можете сделать следующее: Use this template
> Create a new repository
, а затем извлечь код локально с помощью команды git clone
.
Вот скриншот, показывающий верхнее правое меню для использования шаблона.
Другой подход — просто клонировать этот репозиторий напрямую, с помощью:
git clone https://github.com/glaforge/adk-java-maven-template.git
Затем cd
в adk-java-maven-template
.
Чтобы проверить, что вы готовы приступить к написанию своего первого ИИ-агента на Java, убедитесь, что вы можете скомпилировать код в этом проекте с помощью:
mvn compile
Шаг кода: дружелюбный агент учителя естественных наук
Базовым строительным блоком ADK является класс LlmAgent
. Представьте его как ИИ с определённой личностью и целью, управляемый большой языковой моделью. Позже мы добавим дополнительные возможности с помощью инструментов и сделаем его ещё мощнее, тесно взаимодействуя с другими подобными агентами.
Давайте создадим новый класс Java в пакете com.example.agent
и назовем его ScienceTeacher
.
Это «Привет, мир!» в создании агента. Мы создаём простого агента в образе учителя естественных наук.
// src/main/java/com/example/agent/ScienceTeacher.java
package com.example.agent;
import com.google.adk.agents.LlmAgent;
import com.google.adk.web.AdkWebServer;
public class ScienceTeacher {
public static void main(String[] args) {
AdkWebServer.start(
LlmAgent.builder()
.name("science-teacher")
.description("A friendly science teacher")
.instruction("""
You are a science teacher for teenagers.
You explain science concepts in a simple, concise and direct way.
""")
.model("gemini-2.5-flash")
.build()
);
}
}
ИИ-агент настраивается с помощью метода LlmAgent.builder()
. Параметры name()
, description()
и model()
являются обязательными, и для придания агенту определённой индивидуальности и правильного поведения необходимо предоставлять подробные инструкции с помощью метода instruction()
.
Здесь мы решили использовать модель Gemini 2.5 Flash, но вы также можете попробовать Gemini 2.5 Pro для более сложных задач.
Этот агент обёрнут в метод AdkWebServer.start()
. Это так называемый интерфейс чата ADK Dev UI . Он позволяет общаться с агентом через обычный интерфейс чата. Кроме того, он очень полезен, если вы хотите понять, что происходит «под капотом», например, все события, происходящие в системе, запросы и ответы, отправляемые в LLM.
Чтобы скомпилировать и запустить этот агент локально, выполните следующую команду:
mvn compile exec:java -Dexec.mainClass=com.example.agent.ScienceTeacher
Затем перейдите в браузере по адресу http://localhost:8080 . Вы увидите интерфейс, показанный на скриншоте ниже. Задавайте своему агенту вопросы, связанные с наукой.
4. Предоставление агентам инструментов
Зачем агентам инструменты? Магистратура LLM обладает мощным потенциалом, но их знания заморожены во времени, и они не могут взаимодействовать с внешним миром. Инструменты — это мост. Они позволяют агенту получать доступ к информации в режиме реального времени (например, к ценам на акции или новостям), обращаться к закрытым API и выполнять любые действия, которые можно запрограммировать на Java.
Шаг кода: создание пользовательского инструмента ( StockTicker
)
Здесь мы предоставляем нашему агенту инструмент для поиска цен на акции. Агент считает, что когда пользователь запрашивает цену, он должен вызвать наш Java-метод.
// src/main/java/com/example/agent/StockTicker.java
package com.example.agent;
import com.google.adk.agents.LlmAgent;
import com.google.adk.tools.Annotations.Schema;
import com.google.adk.tools.FunctionTool;
import com.google.adk.web.AdkWebServer;
import java.util.Map;
public class StockTicker {
public static void main(String[] args) {
AdkWebServer.start(
LlmAgent.builder()
.name("stock_agent")
.instruction("""
You are a stock exchange ticker expert.
When asked about the stock price of a company,
use the `lookup_stock_ticker` tool to find the information.
""")
.model("gemini-2.5-flash")
.tools(FunctionTool.create(StockTicker.class, "lookupStockTicker"))
.build()
);
}
@Schema(
name = "lookup_stock_ticker",
description = "Lookup stock price for a given company or ticker"
)
public static Map<String, String> lookupStockTicker(
@Schema(name = "company_name_or_stock_ticker", description = "The company name or stock ticker")
String ticker) {
// ... (logic to return a stock price)
}
}
Чтобы сделать агентов умнее и дать им возможность взаимодействовать с миром (или с вашим собственным кодом, API, сервисами и т. д.), вы можете настроить агента на использование инструментов, в частности инструментов пользовательского кода, с помощью метода tools()
, передав ему FunctionTool.create(...)
.
FunctionTool
необходимы класс и имя метода, указывающие на ваш собственный статический метод. Также можно передать экземпляр класса и имя метода экземпляра этого объекта.
Важно указать name
и description
как метода, так и его параметров с помощью аннотации @Schema
, поскольку эта информация будет использоваться базовой LLM для определения того, когда и как следует вызывать данный метод.
Не менее важно помочь LLM, дав чёткие инструкции о том, как и когда использовать инструмент. Модель, возможно, разберётся в этом сама, но если вы дадите подробные объяснения в методе instruction()
, ваша функция будет вызываться правильно, то вероятность её вызова повысится.
Этот метод должен возвращать Map
. Обычно идея заключается в том, чтобы вернуть карту с ключом, представляющим результат, например, stock_price
, и связать с ним значение цены акций. В конечном итоге можно добавить дополнительную пару ключей «успех/истина» для подтверждения успешного выполнения операции. В случае ошибки следует вернуть карту с ключом, например, error
, и связать сообщение об ошибке с соответствующим значением. Это помогает LLM понять, был ли вызов успешным или нет по какой-либо причине.
- В случае успеха вернуть:
{"stock_price": 123}
- В случае ошибки вернуть:
{"error": "Impossible to retrieve stock price for XYZ"}
Затем запустите этот класс с помощью следующей команды:
mvn compile exec:java -Dexec.mainClass=com.example.agent.StockTicker
5. Возможности поиска Google для получения актуальной информации
ADK для Java включает в себя ряд мощных инструментов, среди которых — GoogleSearchTool
. С помощью этого инструмента ваш агент может запросить использование Google Search для поиска релевантной информации и достижения своей цели.
Действительно, знания LLM заморожены во времени: они обучались до определённой даты («даты окончания») на данных, которые были актуальны на момент сбора информации. Это означает, что LLM может не знать о недавних событиях, или их знания могут быть ограниченными и поверхностными, и поисковая система может освежить их память или помочь им узнать больше по теме.
Давайте посмотрим на этот простой агент поиска новостей:
// src/main/java/com/example/agent/LatestNews.java
package com.example.agent;
import java.time.LocalDate;
import com.google.adk.agents.LlmAgent;
import com.google.adk.tools.GoogleSearchTool;
import com.google.adk.web.AdkWebServer;
public class LatestNews {
public static void main(String[] args) {
AdkWebServer.start(LlmAgent.builder()
.name("news-search-agent")
.description("A news search agent")
.instruction("""
You are a news search agent.
Use the `google_search` tool
when asked to search for recent events and information.
Today is \
""" + LocalDate.now())
.model("gemini-2.5-flash")
.tools(new GoogleSearchTool())
.build());
}
}
Notice how we passed an instance of the search tool with: tools(new GoogleSearchTool())
. This is what gives our agent the ability to get up to speed with the latest information that can be found on the web. Also, the prompt specified the date of the day, as it might help the LLM understand when questions are about past information and the lookup of more recent information is required.
Then run this class with the following command:
mvn compile exec:java -Dexec.mainClass=com.example.agent.LatestNews
Feel free to play with the prompt, to ask for different outcomes in terms of style, of conciseness, or focus, etc.
Code Step: The Search Agent as a Tool
Instead of passing the GoogleSearchTool
to an agent directly as a tool, you can create a dedicated search agent that encapsulates the search functionality and expose that agent as a tool to a higher-level agent.
This is an advanced concept that allows you to delegate complex behaviors (like searching and summarizing the results) to a specialized sub-agent. This approach is often useful for more complex workflows, as built-in tools can't be used with custom code-based tools.
// src/main/java/com/example/agent/SearchAgentAsTool.java
package com.example.agent;
import java.time.LocalDate;
import com.google.adk.agents.LlmAgent;
import com.google.adk.tools.AgentTool;
import com.google.adk.tools.GoogleSearchTool;
import com.google.adk.web.AdkWebServer;
public class SearchAgentAsTool {
public static void main(String[] args) {
// 1. Define the specialized Search Agent
LlmAgent searchAgent = LlmAgent.builder()
.name("news-search-agent-tool")
.description("Searches for recent events and provides a concise summary.")
.instruction("""
You are a concise information retrieval specialist.
Use the `google_search` tool to find information.
Always provide the answer as a short,
direct summary, without commentary.
Today is \
""" + LocalDate.now())
.model("gemini-2.5-flash")
.tools(new GoogleSearchTool()) // This agent uses the Google Search Tool
.build();
// 2. Wrap the Search Agent as a Tool
AgentTool searchTool = AgentTool.create(searchAgent);
// 3. Define the Main Agent that uses the Search Agent Tool
AdkWebServer.start(LlmAgent.builder()
.name("main-researcher")
.description("Main agent for answering complex, up-to-date questions.")
.instruction("""
You are a sophisticated research assistant.
When the user asks a question that requires up-to-date or external information,
you MUST use the `news-search-agent-tool` to get the facts before answering.
After the tool returns the result, synthesize the final answer for the user.
""")
.model("gemini-2.5-flash")
.tools(searchTool) // This agent uses the Search Agent as a tool
.build()
);
}
}
The AgentTool.create(searchAgent)
line is the key concept here. It registers the entire searchAgent
(with its own internal logic, prompt, and tools) as a single callable tool for the mainAgent
. This promotes modularity and reusability.
Run this class with the following command:
mvn compile exec:java -Dexec.mainClass=com.example.agent.SearchAgentAsTool
For mundane questions, the agent will reply from his own knowledge base, but when asked about recent events, it will delegate the search to the specialized search agent using the Google Search tool.
6. Mastering Agentic Workflows
For complex problems, a single agent is not enough. When given a goal that consists of too many sub-tasks, with a huge prompt explaining with too much detail, or with access to a huge number of functions, LLMs struggle, and their performance and accuracy degrade.
The key is to divide and conquer by orchestrating multiple specialized agents. Fortunately, ADK comes with different built-in specialized agents:
- Normal agent with
subAgent()
to delegate tasks to, -
SequentialAgent
, to do tasks in a sequence, -
ParallelAgent
, to execute agents in parallel, -
LoopAgent
, typically for going through a refinement process as many times as needed.
What are the key use cases, and the pros and cons of each workflow, please have a look at the table below. But know that the real power will actually come from the combination of several of them!
Рабочий процесс | ADK Class | Вариант использования | Плюсы | Минусы |
Sub-Agents | | User-driven, flexible tasks where the next step is not always known. | High flexibility, conversational, great for user-facing bots. | Less predictable, relies on LLM reasoning for flow control. |
Последовательный | | Fixed, multi-step processes where order is critical. | Predictable, reliable, easy to debug, guarantees order. | Inflexible, can be slower if tasks could be parallelized. |
Параллельный | | Gathering data from multiple sources or running independent tasks. | Highly efficient, significantly reduces latency for I/O-bound tasks. | All tasks run; no short-circuiting. Less suitable for tasks with dependencies. |
Петля | | Iterative refinement, self-correction, or processes that repeat until a condition is met. | Powerful for complex problem-solving, enables agents to improve their own work. | Can lead to infinite loops if not designed carefully (always use maxIterations!). |
7. Agentic Workflows — Delegation with Sub-Agents
A supervisor agent can delegate particular tasks to sub-agents. For example, imagine the agent for an e-commerce website that would delegate order related questions to one agent ("what's the status of my order?") and the after sale service questions to another agent ("I don't know how to turn it on!"). This is the use case we are going to discuss.
// src/main/java/com/example/agent/SupportAgent.java
package com.example.agent;
import com.google.adk.agents.LlmAgent;
import com.google.adk.web.AdkWebServer;
public class SupportAgent {
public static void main(String[] args) {
LlmAgent topicSearchAgent = LlmAgent.builder()
.name("order-agent")
.description("Order agent")
.instruction("""
Your role is to help our customers
with all the questions they may have about their orders.
Always respond that the order has been received, prepared,
and is now out for delivery.
""")
.model("gemini-2.5-flash")
.build();
LlmAgent socialMediaAgent = LlmAgent.builder()
.name("after-sale-agent")
.description("After sale agent")
.instruction("""
You are an after sale agent,
helping customers with the product they received.
When a customer has a problem,
suggest the person to switch the product off and on again.
""")
.model("gemini-2.5-flash")
.build();
AdkWebServer.start(LlmAgent.builder()
.name("support-agent")
.description("Customer support agent")
.instruction("""
Your role is help our customers.
Call the `order-agent` for all questions related to order status.
Call the `after-sale-agent` for inquiries about the received product.
""")
.model("gemini-2.5-flash")
.subAgents(socialMediaAgent, topicSearchAgent)
.build()
);
}
}
The key line here is where the subAgents()
method is called, passing in the two sub-agents whose specific role will be handled separately by each other.
Run the example above with the following command:
mvn compile exec:java -Dexec.mainClass=com.example.agent.SupportAgent
This concept of delegating tasks to sub-agents mirrors effective human management, where a good manager (the supervisor agent) relies on specialized employees (the sub-agents) to handle specific tasks for which they have greater expertise. The supervisor doesn't need to know the minutiae of every process; instead, it intelligently routes a customer's request (like an order inquiry or a technical problem) to the most qualified 'team member,' ensuring a higher quality and more efficient response than one generalist could provide alone. Furthermore, these sub-agents can fully concentrate on their individual assignments without needing to understand the entirety of the complex overarching process.
8. Agentic Workflows — The Assembly Line
When the order of operations matters, use a SequentialAgent
. It's like an assembly line, executing sub-agents in a fixed order where each step can depend on the previous one.
Let's imagine an English poet collaborates with an English-French translator to create poems first in English and then translate them into French:
// src/main/java/com/example/agent/PoetAndTranslator.java
package com.example.agent;
import com.google.adk.agents.LlmAgent;
import com.google.adk.agents.SequentialAgent;
import com.google.adk.web.AdkWebServer;
public class PoetAndTranslator {
public static void main(String[] args) {
LlmAgent poet = LlmAgent.builder()
.name("poet-agent")
.description("Poet writing poems")
.model("gemini-2.5-flash")
.instruction("""
You are a talented poet,
who writes short and beautiful poems.
""")
.outputKey("poem")
.build();
LlmAgent translator = LlmAgent.builder()
.name("translator-agent")
.description("English to French translator")
.model("gemini-2.5-flash")
.instruction("""
As an expert English-French translator,
your role is to translate the following poem into French,
ensuring the poem still rhymes even after translation:
{poem}
""")
.outputKey("translated-poem")
.build();
AdkWebServer.start(SequentialAgent.builder()
.name("poet-and-translator")
.subAgents(poet, translator)
.build());
}
}
Run the example to get a poem in English, then translated into French, with the following command:
mvn compile exec:java -Dexec.mainClass=com.example.agent.PoetAndTranslator
This systematic decomposition of complex tasks into smaller, ordered sub-tasks ensures a more deterministic and dependable process, significantly enhancing the likelihood of a successful outcome compared to relying on a single, broadly purposed agent.
Effectively decomposing a task into a sequence of sub-tasks (when possible and when it makes sense) is crucial for achieving more deterministic and successful outcomes, as it allows for structured progression and dependency management between steps.
9. Agentic Workflows — Working in Parallel
When tasks are independent, a ParallelAgent
provides a huge efficiency boost by running them simultaneously. In the following example, we'll even be combining a SequentialAgent
with a ParallelAgent
: the parallel tasks run first, and then a final agent summarizes the outcome of the parallel tasks.
Let's build a company detective whose job will be to search for information about:
- The profile of the company (CEO, headquarters, motto, etc.)
- The latest news about the company.
- Details about the financials of the company.
// src/main/java/com/example/agent/CompanyDetective.java
package com.example.agent;
import com.google.adk.agents.LlmAgent;
import com.google.adk.agents.ParallelAgent;
import com.google.adk.agents.SequentialAgent;
import com.google.adk.tools.GoogleSearchTool;
import com.google.adk.web.AdkWebServer;
public class CompanyDetective {
public static void main(String[] args) {
var companyProfiler = LlmAgent.builder()
.name("company-profiler")
.description("Provides a general overview of a company.")
.instruction("""
Your role is to provide a brief overview of the given company.
Include its mission, headquarters, and current CEO.
Use the Google Search Tool to find this information.
""")
.model("gemini-2.5-flash")
.tools(new GoogleSearchTool())
.outputKey("profile")
.build();
var newsFinder = LlmAgent.builder()
.name("news-finder")
.description("Finds the latest news about a company.")
.instruction("""
Your role is to find the top 3-4 recent news headlines for the given company.
Use the Google Search Tool.
Present the results as a simple bulleted list.
""")
.model("gemini-2.5-flash")
.tools(new GoogleSearchTool())
.outputKey("news")
.build();
var financialAnalyst = LlmAgent.builder()
.name("financial-analyst")
.description("Analyzes the financial performance of a company.")
.instruction("""
Your role is to provide a snapshot of the given company's recent financial performance.
Focus on stock trends or recent earnings reports.
Use the Google Search Tool.
""")
.model("gemini-2.5-flash")
.tools(new GoogleSearchTool())
.outputKey("financials")
.build();
var marketResearcher = ParallelAgent.builder()
.name("market-researcher")
.description("Performs comprehensive market research on a company.")
.subAgents(
companyProfiler,
newsFinder,
financialAnalyst
)
.build();
var reportCompiler = LlmAgent.builder()
.name("report-compiler")
.description("Compiles a final market research report.")
.instruction("""
Your role is to synthesize the provided information into a coherent market research report.
Combine the company profile, latest news, and financial analysis into a single, well-formatted report.
## Company Profile
{profile}
## Latest News
{news}
## Financial Snapshot
{financials}
""")
.model("gemini-2.5-flash")
.build();
AdkWebServer.start(SequentialAgent.builder()
.name("company-detective")
.description("Collects various information about a company.")
.subAgents(
marketResearcher,
reportCompiler
).build());
}
}
As usual, you can run the agent with the following command:
mvn compile exec:java -Dexec.mainClass=com.example.agent.CompanyDetective
This agent demonstrates a powerful combination of workflows, with both parallel and sequential agents put to good use, in an efficient way thanks to parallelization of information research and synthesis.
10. Agentic Workflows — Iterative Refinement
For tasks requiring a "generate → review → refine" cycle, use a LoopAgent
. It automates iterative improvement until a goal is met. Similarly to the SequentialAgent
, the LoopAgent
will call sub-agent serially, but it will loop back at the beginning. It is the LLM used internally by the agent that will decide whether or not to request the call to a special tool, the exit_loop
built-in tool, to stop the execution of the loop.
The code refiner example below, using a LoopAgent
, automates code refinement: generate, review, correct. This mimics human development. A code generator first generates the requested code, saves it in the agent state under the generated_code
key. A code reviewer then reviews the generated code, and either provides feedback (under the feedback
key), or calls an exit loop tool to end the iteration early.
Let's have a look at the code:
// src/main/java/com/example/agent/CodeRefiner.java
package com.example.agent;
import com.google.adk.agents.LlmAgent;
import com.google.adk.agents.LoopAgent;
import com.google.adk.agents.SequentialAgent;
import com.google.adk.tools.ExitLoopTool;
import com.google.adk.web.AdkWebServer;
public class CodeRefiner {
public static void main(String[] args) {
var codeGenerator = LlmAgent.builder()
.name("code-generator")
.description("Writes and refines code based on a request and feedback.")
.instruction("""
Your role is to write a Python function based on the user's request.
In the first turn, write the initial version of the code.
In subsequent turns, you will receive feedback on your code.
Your task is to refine the code based on this feedback.
Previous feedback (if any):
{feedback?}
""")
.model("gemini-2.5-flash")
.outputKey("generated_code")
.build();
var codeReviewer = LlmAgent.builder()
.name("code-reviewer")
.description("Reviews code and decides if it's complete or needs more work.")
.instruction("""
Your role is to act as a senior code reviewer.
Analyze the provided Python code for correctness, style, and potential bugs.
Code to review:
{generated_code}
If the code is perfect and meets the user's request,
you MUST call the `exit_loop` tool.
Otherwise, provide constructive feedback for the `code-generator to improve the code.
""")
.model("gemini-2.5-flash")
.outputKey("feedback")
.tools(ExitLoopTool.INSTANCE)
.build();
var codeRefinerLoop = LoopAgent.builder()
.name("code-refiner-loop")
.description("Iteratively generates and reviews code until it is correct.")
.subAgents(
codeGenerator,
codeReviewer
)
.maxIterations(3) // Safety net to prevent infinite loops
.build();
var finalPresenter = LlmAgent.builder()
.name("final-presenter")
.description("Presents the final, accepted code to the user.")
.instruction("""
The code has been successfully generated and reviewed.
Present the final version of the code to the user in a clear format.
Final Code:
{generated_code}
""")
.model("gemini-2.5-flash")
.build();
AdkWebServer.start(SequentialAgent.builder()
.name("code-refiner-assistant")
.description("Manages the full code generation and refinement process.")
.subAgents(
codeRefinerLoop,
finalPresenter)
.build());
}
}
Run this agent with the following command:
mvn compile exec:java -Dexec.mainClass=com.example.agent.CodeRefiner
Feedback/refine loops, implemented using the LoopAgent
, are indispensable for solving problems that require iterative improvement and self-correction, closely mimicking human cognitive processes. This design pattern is particularly useful for tasks where the initial output is rarely perfect, such as code generation, creative writing, design iteration, or complex data analysis. By cycling the output through a specialized reviewer agent that provides structured feedback, the generating agent can continuously refine its work until a predefined completion criterion is met, leading to demonstrably higher quality and more reliable final results than a single-pass approach.
11. Congratulations!
You've successfully built and explored a variety of AI agents, from simple conversationalists to complex, multi-agent systems. You've learned the core concepts of the ADK for Java: defining agents with instructions, empowering them with tools, and orchestrating them into powerful workflows.
Что дальше?
- Explore the official ADK for Java GitHub repository .
- Learn more about the framework in its documentation .
- Read about the various agentic workflows in this blog series and about the various tools available.
- Dive deeper into the other built-in tools and advanced callbacks.
- Handle context, state, and artifacts, for richer and multimodal interactions.
- Implement and apply plugins that plug into the lifecycle of your agents.
- Try building your own agent that solves a real-world problem!
1. Welcome, AI agent developers!
In this codelab, you'll learn how to build AI agents in Java , using the Agents Development Kit (ADK) for Java . We'll move beyond simple Large Language Model (LLM) API calls to create autonomous AI agents that can reason, plan, use tools, and work together to solve complex problems.
You will start by redeeming the Google Cloud credits, setting up your Google Cloud environment, then building your first simple agent, and progressively adding more advanced capabilities like custom tools, web search, and multi-agent orchestration.
Чему вы научитесь
- How to create a basic, persona-driven AI agent.
- How to empower agents with custom and built-in tools (like Google Search).
- How to add your own tools implemented in Java.
- How to orchestrate multiple agents into powerful sequential, parallel, and looping workflows.
Что вам понадобится
- A web browser that we'll use in Incognito mode.
- A personal Gmail account.
- A new Google Cloud project associated with your personal Gmail account.
- A billing account created with the redeemed Google Cloud credits.
- The git command-line tool to check out the source code.
- Java 17+ and Apache Maven.
- A text editor or IDE, such as IntelliJ IDEA or VS Code.
It's possible to use the built-in VS Code editor in Cloud Shell, in the Google Cloud console.
2. Setup: Your Environment
Claiming Google Cloud credits for the workshop
For an instructor-led workshop, you will have received a link to the website where you can claim Google cloud credits to use for the workshop.
- Use a personal Google account : It is important to use a personal Google account (like a gmail.com address) as corporate or school email addresses will not work .
- Use Google Chrome in incognito mode : This is recommended to create a clean session and prevent conflicts with other Google accounts.
- Use the special event link : A special link for the event, which looks something like https://trygcp.dev/event/xxx followed by an event code (here "xxx" in this example), should be used.
- Accept the terms of service : After signing in, you will be presented with the Google Cloud Platform terms of service, which you need to accept to continue.
- Create a new project : A new empty project must be created from the Google Cloud Console .
- Link a billing account : Link the newly created project to a billing account.
- Confirm the credit : The following video shows how to confirm that the credit is applied to the project by checking the "credits" section in the billing page.
Feel free to check out this video which explains how to redeem and apply the credits.
Create and Configure Your API Key
To authenticate your ADK AI agents with the Gemini API for this codelab, you'll use an API key associated with your Google Cloud project.
- Generate an API Key:
- Go to Google AI Studio and click on the "Get API key" link at the bottom of the left side panel.
- Select Projects and then click the Import projects button.
- Search for and select the Google Cloud project you want to import, then select the Import button.
- Once the project is imported, go to the API Keys page from the Dashboard menu, and create an API key in the project you just imported.
- Make note of the API key .
- Set the Environment Variable: Your agent needs to access this key. The standard way is by setting an environment variable named
GOOGLE_API_KEY
.
- macOS / Linux: Open your terminal and run the following command, replacing
"your-api-key"
with the key you just copied. To make this permanent, add this line to your shell's startup file (eg,~/.bash_profile
,~/.zshrc
).
export GOOGLE_API_KEY="your-api-key"
- Windows (Command Prompt): Open a new command prompt and run:
setx GOOGLE_API_KEY "your-api-key"
- You will need to restart your command prompt for this change to take effect.
- Windows (PowerShell): Open a PowerShell terminal and run:
$env:GOOGLE_API_KEY="your-api-key"
- To make this change permanent in PowerShell, you'll need to add it to your profile script.
3. Getting Started: Your First Agent
The best way to start a new project is by using the ADK for Java GitHub template . It provides the project structure and all the necessary dependencies.
If you have a Github account, you can do the following: Use this template
> Create a new repository
, then checkout the code locally with the git clone
command.
Here is a screenshot showing the top-right menu for using the template.
The other approach is to simply clone that repository directly, with:
git clone https://github.com/glaforge/adk-java-maven-template.git
Then cd
into adk-java-maven-template
.
To check that you are ready to get started with coding your first AI agent in Java, ensure you can compile the code in this project with:
mvn compile
Code Step: A Friendly Science Teacher Agent
The fundamental building block in ADK is the LlmAgent
class. Think of it as an AI with a specific personality and goal, powered by a Large Language Model. We'll later add more capabilities via tools, and make it more powerful by collaborating hand-in-hand with other similar agents.
Let's create a new Java class, in the com.example.agent
package, and call it ScienceTeacher
.
This is the "Hello, World!" of agent creation. We're defining a simple agent with the persona of a science teacher.
// src/main/java/com/example/agent/ScienceTeacher.java
package com.example.agent;
import com.google.adk.agents.LlmAgent;
import com.google.adk.web.AdkWebServer;
public class ScienceTeacher {
public static void main(String[] args) {
AdkWebServer.start(
LlmAgent.builder()
.name("science-teacher")
.description("A friendly science teacher")
.instruction("""
You are a science teacher for teenagers.
You explain science concepts in a simple, concise and direct way.
""")
.model("gemini-2.5-flash")
.build()
);
}
}
The AI agent is configured via the LlmAgent.builder()
method. The name()
, description()
, and model()
parameters are mandatory, and to give a specific personality and proper behavior to your agent, you should always give detailed guidance via the instruction()
method.
Here we chose to use the Gemini 2.5 Flash model, but feel free to try Gemini 2.5 Pro as well, for more complicated tasks.
This agent is wrapped within the AdkWebServer.start()
method. This is the so-called ADK Dev UI chat interface. It allows you to converse with the agent via a typical chat interface. Furthermore, it's of great help if you want to understand what's going on under the hood, like all the events that are flowing through the system, the requests and responses sent to the LLM.
To compile and run this agent locally, run the following command:
mvn compile exec:java -Dexec.mainClass=com.example.agent.ScienceTeacher
Then head over to your browser at http://localhost:8080 . You should see the UI as shown in the screenshot below. Go ahead and ask science related questions to your agent.
4. Empowering Agents with Tools
Why do agents need tools? LLMs are powerful, but their knowledge is frozen in time and they can't interact with the outside world. Tools are the bridge. They allow an agent to access real-time information (like stock prices or news), query private APIs, or perform any action you can code in Java.
Code Step: Creating a Custom Tool ( StockTicker
)
Here, we give our agent a tool to look up stock prices. The agent reasons that when a user asks for a price, it should call our Java method.
// src/main/java/com/example/agent/StockTicker.java
package com.example.agent;
import com.google.adk.agents.LlmAgent;
import com.google.adk.tools.Annotations.Schema;
import com.google.adk.tools.FunctionTool;
import com.google.adk.web.AdkWebServer;
import java.util.Map;
public class StockTicker {
public static void main(String[] args) {
AdkWebServer.start(
LlmAgent.builder()
.name("stock_agent")
.instruction("""
You are a stock exchange ticker expert.
When asked about the stock price of a company,
use the `lookup_stock_ticker` tool to find the information.
""")
.model("gemini-2.5-flash")
.tools(FunctionTool.create(StockTicker.class, "lookupStockTicker"))
.build()
);
}
@Schema(
name = "lookup_stock_ticker",
description = "Lookup stock price for a given company or ticker"
)
public static Map<String, String> lookupStockTicker(
@Schema(name = "company_name_or_stock_ticker", description = "The company name or stock ticker")
String ticker) {
// ... (logic to return a stock price)
}
}
To make agents smarter and give them the ability to interact with the world (or with your own code, APIs, services, etc.) you can configure the agent to use tools, and in particular custom code tools, via the tools()
method, passing it a FunctionTool.create(...)
.
The FunctionTool
needs a class and a method name pointing at your own static method — it's also possible to pass an instance of a class and the name of an instance method of that object.
It is important to specify the name
and description
of both the method, and its parameters, via the @Schema
annotation, as this information will be used by the underlying LLM to figure out when and how it should call a given method.
Equally important, it's better to help the LLM with clear instructions about how and when to use the tool. The model may be able to figure it out on its own, but if you give explicit explanations in the instruction()
method, your function has higher chances to be called appropriately.
This method should return a Map
. Usually, the idea is to return a map with a key representing the result, like stock_price
, and associate the value of the stock price to it. Eventually, you can add an extra success / true key pair to signal the success of the operation. And in case of error, you should return a map with a key called, for example, error
, and associate the error message in the associated value. This helps the LLM understand if the call succeeded or failed for some reason.
- On success, return:
{"stock_price": 123}
- On error, return:
{"error": "Impossible to retrieve stock price for XYZ"}
Then run this class with the following command:
mvn compile exec:java -Dexec.mainClass=com.example.agent.StockTicker
5. The power of Google Search for up-to-date information
ADK for Java comes with a handful of powerful tools, among which is the GoogleSearchTool
. With this tool, your agent can request the use of Google Search to find relevant information to reach its goal.
Indeed, the knowledge of an LLM is frozen in time: it has been trained until a certain date (the "cut-off date") with data that is also as up-to-date as when the information was collected. This means that LLMs may not necessarily know about recent events, or their knowledge may be limited and shallow, and the help of a search engine might refresh their memory or teach them more about the topic.
Let's have a look at this simple news search agent:
// src/main/java/com/example/agent/LatestNews.java
package com.example.agent;
import java.time.LocalDate;
import com.google.adk.agents.LlmAgent;
import com.google.adk.tools.GoogleSearchTool;
import com.google.adk.web.AdkWebServer;
public class LatestNews {
public static void main(String[] args) {
AdkWebServer.start(LlmAgent.builder()
.name("news-search-agent")
.description("A news search agent")
.instruction("""
You are a news search agent.
Use the `google_search` tool
when asked to search for recent events and information.
Today is \
""" + LocalDate.now())
.model("gemini-2.5-flash")
.tools(new GoogleSearchTool())
.build());
}
}
Notice how we passed an instance of the search tool with: tools(new GoogleSearchTool())
. This is what gives our agent the ability to get up to speed with the latest information that can be found on the web. Also, the prompt specified the date of the day, as it might help the LLM understand when questions are about past information and the lookup of more recent information is required.
Then run this class with the following command:
mvn compile exec:java -Dexec.mainClass=com.example.agent.LatestNews
Feel free to play with the prompt, to ask for different outcomes in terms of style, of conciseness, or focus, etc.
Code Step: The Search Agent as a Tool
Instead of passing the GoogleSearchTool
to an agent directly as a tool, you can create a dedicated search agent that encapsulates the search functionality and expose that agent as a tool to a higher-level agent.
This is an advanced concept that allows you to delegate complex behaviors (like searching and summarizing the results) to a specialized sub-agent. This approach is often useful for more complex workflows, as built-in tools can't be used with custom code-based tools.
// src/main/java/com/example/agent/SearchAgentAsTool.java
package com.example.agent;
import java.time.LocalDate;
import com.google.adk.agents.LlmAgent;
import com.google.adk.tools.AgentTool;
import com.google.adk.tools.GoogleSearchTool;
import com.google.adk.web.AdkWebServer;
public class SearchAgentAsTool {
public static void main(String[] args) {
// 1. Define the specialized Search Agent
LlmAgent searchAgent = LlmAgent.builder()
.name("news-search-agent-tool")
.description("Searches for recent events and provides a concise summary.")
.instruction("""
You are a concise information retrieval specialist.
Use the `google_search` tool to find information.
Always provide the answer as a short,
direct summary, without commentary.
Today is \
""" + LocalDate.now())
.model("gemini-2.5-flash")
.tools(new GoogleSearchTool()) // This agent uses the Google Search Tool
.build();
// 2. Wrap the Search Agent as a Tool
AgentTool searchTool = AgentTool.create(searchAgent);
// 3. Define the Main Agent that uses the Search Agent Tool
AdkWebServer.start(LlmAgent.builder()
.name("main-researcher")
.description("Main agent for answering complex, up-to-date questions.")
.instruction("""
You are a sophisticated research assistant.
When the user asks a question that requires up-to-date or external information,
you MUST use the `news-search-agent-tool` to get the facts before answering.
After the tool returns the result, synthesize the final answer for the user.
""")
.model("gemini-2.5-flash")
.tools(searchTool) // This agent uses the Search Agent as a tool
.build()
);
}
}
The AgentTool.create(searchAgent)
line is the key concept here. It registers the entire searchAgent
(with its own internal logic, prompt, and tools) as a single callable tool for the mainAgent
. This promotes modularity and reusability.
Run this class with the following command:
mvn compile exec:java -Dexec.mainClass=com.example.agent.SearchAgentAsTool
For mundane questions, the agent will reply from his own knowledge base, but when asked about recent events, it will delegate the search to the specialized search agent using the Google Search tool.
6. Mastering Agentic Workflows
For complex problems, a single agent is not enough. When given a goal that consists of too many sub-tasks, with a huge prompt explaining with too much detail, or with access to a huge number of functions, LLMs struggle, and their performance and accuracy degrade.
The key is to divide and conquer by orchestrating multiple specialized agents. Fortunately, ADK comes with different built-in specialized agents:
- Normal agent with
subAgent()
to delegate tasks to, -
SequentialAgent
, to do tasks in a sequence, -
ParallelAgent
, to execute agents in parallel, -
LoopAgent
, typically for going through a refinement process as many times as needed.
What are the key use cases, and the pros and cons of each workflow, please have a look at the table below. But know that the real power will actually come from the combination of several of them!
Рабочий процесс | ADK Class | Вариант использования | Плюсы | Минусы |
Sub-Agents | | User-driven, flexible tasks where the next step is not always known. | High flexibility, conversational, great for user-facing bots. | Less predictable, relies on LLM reasoning for flow control. |
Последовательный | | Fixed, multi-step processes where order is critical. | Predictable, reliable, easy to debug, guarantees order. | Inflexible, can be slower if tasks could be parallelized. |
Параллельный | | Gathering data from multiple sources or running independent tasks. | Highly efficient, significantly reduces latency for I/O-bound tasks. | All tasks run; no short-circuiting. Less suitable for tasks with dependencies. |
Петля | | Iterative refinement, self-correction, or processes that repeat until a condition is met. | Powerful for complex problem-solving, enables agents to improve their own work. | Can lead to infinite loops if not designed carefully (always use maxIterations!). |
7. Agentic Workflows — Delegation with Sub-Agents
A supervisor agent can delegate particular tasks to sub-agents. For example, imagine the agent for an e-commerce website that would delegate order related questions to one agent ("what's the status of my order?") and the after sale service questions to another agent ("I don't know how to turn it on!"). This is the use case we are going to discuss.
// src/main/java/com/example/agent/SupportAgent.java
package com.example.agent;
import com.google.adk.agents.LlmAgent;
import com.google.adk.web.AdkWebServer;
public class SupportAgent {
public static void main(String[] args) {
LlmAgent topicSearchAgent = LlmAgent.builder()
.name("order-agent")
.description("Order agent")
.instruction("""
Your role is to help our customers
with all the questions they may have about their orders.
Always respond that the order has been received, prepared,
and is now out for delivery.
""")
.model("gemini-2.5-flash")
.build();
LlmAgent socialMediaAgent = LlmAgent.builder()
.name("after-sale-agent")
.description("After sale agent")
.instruction("""
You are an after sale agent,
helping customers with the product they received.
When a customer has a problem,
suggest the person to switch the product off and on again.
""")
.model("gemini-2.5-flash")
.build();
AdkWebServer.start(LlmAgent.builder()
.name("support-agent")
.description("Customer support agent")
.instruction("""
Your role is help our customers.
Call the `order-agent` for all questions related to order status.
Call the `after-sale-agent` for inquiries about the received product.
""")
.model("gemini-2.5-flash")
.subAgents(socialMediaAgent, topicSearchAgent)
.build()
);
}
}
The key line here is where the subAgents()
method is called, passing in the two sub-agents whose specific role will be handled separately by each other.
Run the example above with the following command:
mvn compile exec:java -Dexec.mainClass=com.example.agent.SupportAgent
This concept of delegating tasks to sub-agents mirrors effective human management, where a good manager (the supervisor agent) relies on specialized employees (the sub-agents) to handle specific tasks for which they have greater expertise. The supervisor doesn't need to know the minutiae of every process; instead, it intelligently routes a customer's request (like an order inquiry or a technical problem) to the most qualified 'team member,' ensuring a higher quality and more efficient response than one generalist could provide alone. Furthermore, these sub-agents can fully concentrate on their individual assignments without needing to understand the entirety of the complex overarching process.
8. Agentic Workflows — The Assembly Line
When the order of operations matters, use a SequentialAgent
. It's like an assembly line, executing sub-agents in a fixed order where each step can depend on the previous one.
Let's imagine an English poet collaborates with an English-French translator to create poems first in English and then translate them into French:
// src/main/java/com/example/agent/PoetAndTranslator.java
package com.example.agent;
import com.google.adk.agents.LlmAgent;
import com.google.adk.agents.SequentialAgent;
import com.google.adk.web.AdkWebServer;
public class PoetAndTranslator {
public static void main(String[] args) {
LlmAgent poet = LlmAgent.builder()
.name("poet-agent")
.description("Poet writing poems")
.model("gemini-2.5-flash")
.instruction("""
You are a talented poet,
who writes short and beautiful poems.
""")
.outputKey("poem")
.build();
LlmAgent translator = LlmAgent.builder()
.name("translator-agent")
.description("English to French translator")
.model("gemini-2.5-flash")
.instruction("""
As an expert English-French translator,
your role is to translate the following poem into French,
ensuring the poem still rhymes even after translation:
{poem}
""")
.outputKey("translated-poem")
.build();
AdkWebServer.start(SequentialAgent.builder()
.name("poet-and-translator")
.subAgents(poet, translator)
.build());
}
}
Run the example to get a poem in English, then translated into French, with the following command:
mvn compile exec:java -Dexec.mainClass=com.example.agent.PoetAndTranslator
This systematic decomposition of complex tasks into smaller, ordered sub-tasks ensures a more deterministic and dependable process, significantly enhancing the likelihood of a successful outcome compared to relying on a single, broadly purposed agent.
Effectively decomposing a task into a sequence of sub-tasks (when possible and when it makes sense) is crucial for achieving more deterministic and successful outcomes, as it allows for structured progression and dependency management between steps.
9. Agentic Workflows — Working in Parallel
When tasks are independent, a ParallelAgent
provides a huge efficiency boost by running them simultaneously. In the following example, we'll even be combining a SequentialAgent
with a ParallelAgent
: the parallel tasks run first, and then a final agent summarizes the outcome of the parallel tasks.
Let's build a company detective whose job will be to search for information about:
- The profile of the company (CEO, headquarters, motto, etc.)
- The latest news about the company.
- Details about the financials of the company.
// src/main/java/com/example/agent/CompanyDetective.java
package com.example.agent;
import com.google.adk.agents.LlmAgent;
import com.google.adk.agents.ParallelAgent;
import com.google.adk.agents.SequentialAgent;
import com.google.adk.tools.GoogleSearchTool;
import com.google.adk.web.AdkWebServer;
public class CompanyDetective {
public static void main(String[] args) {
var companyProfiler = LlmAgent.builder()
.name("company-profiler")
.description("Provides a general overview of a company.")
.instruction("""
Your role is to provide a brief overview of the given company.
Include its mission, headquarters, and current CEO.
Use the Google Search Tool to find this information.
""")
.model("gemini-2.5-flash")
.tools(new GoogleSearchTool())
.outputKey("profile")
.build();
var newsFinder = LlmAgent.builder()
.name("news-finder")
.description("Finds the latest news about a company.")
.instruction("""
Your role is to find the top 3-4 recent news headlines for the given company.
Use the Google Search Tool.
Present the results as a simple bulleted list.
""")
.model("gemini-2.5-flash")
.tools(new GoogleSearchTool())
.outputKey("news")
.build();
var financialAnalyst = LlmAgent.builder()
.name("financial-analyst")
.description("Analyzes the financial performance of a company.")
.instruction("""
Your role is to provide a snapshot of the given company's recent financial performance.
Focus on stock trends or recent earnings reports.
Use the Google Search Tool.
""")
.model("gemini-2.5-flash")
.tools(new GoogleSearchTool())
.outputKey("financials")
.build();
var marketResearcher = ParallelAgent.builder()
.name("market-researcher")
.description("Performs comprehensive market research on a company.")
.subAgents(
companyProfiler,
newsFinder,
financialAnalyst
)
.build();
var reportCompiler = LlmAgent.builder()
.name("report-compiler")
.description("Compiles a final market research report.")
.instruction("""
Your role is to synthesize the provided information into a coherent market research report.
Combine the company profile, latest news, and financial analysis into a single, well-formatted report.
## Company Profile
{profile}
## Latest News
{news}
## Financial Snapshot
{financials}
""")
.model("gemini-2.5-flash")
.build();
AdkWebServer.start(SequentialAgent.builder()
.name("company-detective")
.description("Collects various information about a company.")
.subAgents(
marketResearcher,
reportCompiler
).build());
}
}
As usual, you can run the agent with the following command:
mvn compile exec:java -Dexec.mainClass=com.example.agent.CompanyDetective
This agent demonstrates a powerful combination of workflows, with both parallel and sequential agents put to good use, in an efficient way thanks to parallelization of information research and synthesis.
10. Agentic Workflows — Iterative Refinement
For tasks requiring a "generate → review → refine" cycle, use a LoopAgent
. It automates iterative improvement until a goal is met. Similarly to the SequentialAgent
, the LoopAgent
will call sub-agent serially, but it will loop back at the beginning. It is the LLM used internally by the agent that will decide whether or not to request the call to a special tool, the exit_loop
built-in tool, to stop the execution of the loop.
The code refiner example below, using a LoopAgent
, automates code refinement: generate, review, correct. This mimics human development. A code generator first generates the requested code, saves it in the agent state under the generated_code
key. A code reviewer then reviews the generated code, and either provides feedback (under the feedback
key), or calls an exit loop tool to end the iteration early.
Let's have a look at the code:
// src/main/java/com/example/agent/CodeRefiner.java
package com.example.agent;
import com.google.adk.agents.LlmAgent;
import com.google.adk.agents.LoopAgent;
import com.google.adk.agents.SequentialAgent;
import com.google.adk.tools.ExitLoopTool;
import com.google.adk.web.AdkWebServer;
public class CodeRefiner {
public static void main(String[] args) {
var codeGenerator = LlmAgent.builder()
.name("code-generator")
.description("Writes and refines code based on a request and feedback.")
.instruction("""
Your role is to write a Python function based on the user's request.
In the first turn, write the initial version of the code.
In subsequent turns, you will receive feedback on your code.
Your task is to refine the code based on this feedback.
Previous feedback (if any):
{feedback?}
""")
.model("gemini-2.5-flash")
.outputKey("generated_code")
.build();
var codeReviewer = LlmAgent.builder()
.name("code-reviewer")
.description("Reviews code and decides if it's complete or needs more work.")
.instruction("""
Your role is to act as a senior code reviewer.
Analyze the provided Python code for correctness, style, and potential bugs.
Code to review:
{generated_code}
If the code is perfect and meets the user's request,
you MUST call the `exit_loop` tool.
Otherwise, provide constructive feedback for the `code-generator to improve the code.
""")
.model("gemini-2.5-flash")
.outputKey("feedback")
.tools(ExitLoopTool.INSTANCE)
.build();
var codeRefinerLoop = LoopAgent.builder()
.name("code-refiner-loop")
.description("Iteratively generates and reviews code until it is correct.")
.subAgents(
codeGenerator,
codeReviewer
)
.maxIterations(3) // Safety net to prevent infinite loops
.build();
var finalPresenter = LlmAgent.builder()
.name("final-presenter")
.description("Presents the final, accepted code to the user.")
.instruction("""
The code has been successfully generated and reviewed.
Present the final version of the code to the user in a clear format.
Final Code:
{generated_code}
""")
.model("gemini-2.5-flash")
.build();
AdkWebServer.start(SequentialAgent.builder()
.name("code-refiner-assistant")
.description("Manages the full code generation and refinement process.")
.subAgents(
codeRefinerLoop,
finalPresenter)
.build());
}
}
Run this agent with the following command:
mvn compile exec:java -Dexec.mainClass=com.example.agent.CodeRefiner
Feedback/refine loops, implemented using the LoopAgent
, are indispensable for solving problems that require iterative improvement and self-correction, closely mimicking human cognitive processes. This design pattern is particularly useful for tasks where the initial output is rarely perfect, such as code generation, creative writing, design iteration, or complex data analysis. By cycling the output through a specialized reviewer agent that provides structured feedback, the generating agent can continuously refine its work until a predefined completion criterion is met, leading to demonstrably higher quality and more reliable final results than a single-pass approach.
11. Congratulations!
You've successfully built and explored a variety of AI agents, from simple conversationalists to complex, multi-agent systems. You've learned the core concepts of the ADK for Java: defining agents with instructions, empowering them with tools, and orchestrating them into powerful workflows.
Что дальше?
- Explore the official ADK for Java GitHub repository .
- Learn more about the framework in its documentation .
- Read about the various agentic workflows in this blog series and about the various tools available.
- Dive deeper into the other built-in tools and advanced callbacks.
- Handle context, state, and artifacts, for richer and multimodal interactions.
- Implement and apply plugins that plug into the lifecycle of your agents.
- Try building your own agent that solves a real-world problem!