1. Witamy, deweloperzy agentów AI!
Z tego laboratorium dowiesz się, jak tworzyć agentów AI w Javie za pomocą zestawu Agents Development Kit (ADK) dla Javy. Wyjdziemy poza proste wywołania interfejsu API dużego modelu językowego (LLM), aby tworzyć autonomiczne agenty AI, które mogą wnioskować, planować, korzystać z narzędzi i współpracować ze sobą w celu rozwiązywania złożonych problemów.
Zaczniesz od wykorzystania środków w Google Cloud i skonfigurowania środowiska Google Cloud, a następnie utworzysz pierwszego prostego agenta i stopniowo dodasz do niego bardziej zaawansowane funkcje, takie jak narzędzia niestandardowe, wyszukiwanie w internecie i orkiestracja wielu agentów.
Czego się nauczysz
- Jak utworzyć podstawowego agenta AI opartego na personie.
- Jak udostępniać agentom narzędzia niestandardowe i wbudowane (np. wyszukiwarkę Google).
- Jak dodać własne narzędzia zaimplementowane w języku Java.
- Jak koordynować pracę wielu agentów w zaawansowanych sekwencyjnych, równoległych i zapętlonych przepływach pracy.
Czego potrzebujesz
- Przeglądarka internetowa, której będziemy używać w trybie incognito.
- osobiste konto Gmail;
- nowy projekt Google Cloud powiązany z Twoim osobistym kontem Gmail;
- Konto rozliczeniowe utworzone przy użyciu wykorzystanych środków Google Cloud.
- Narzędzie wiersza poleceń Git do sprawdzania kodu źródłowego.
- Java 17 lub nowsza i Apache Maven.
- Edytor tekstu lub IDE, np. IntelliJ IDEA lub VS Code.
W konsoli Google Cloud możesz używać wbudowanego edytora VS Code w Cloud Shell.
2. Konfiguracja: środowisko
Otrzymywanie środków w Google Cloud na potrzeby warsztatów
W przypadku warsztatów prowadzonych przez instruktora otrzymasz link do witryny, w której możesz odebrać środki w Google Cloud do wykorzystania podczas warsztatów.
- Użyj osobistego konta Google: ważne jest, aby używać osobistego konta Google (np. adresu gmail.com), ponieważ firmowe lub szkolne adresy e-mail nie będą działać.
- Używaj Google Chrome w trybie incognito: zalecamy to rozwiązanie, aby utworzyć czystą sesję i zapobiec konfliktom z innymi kontami Google.
- Użyj specjalnego linku do wydarzenia: należy użyć specjalnego linku do wydarzenia, który wygląda mniej więcej tak: https://trygcp.dev/event/xxx, a po nim kod wydarzenia (w tym przykładzie „xxx”).
- Zaakceptuj warunki korzystania z usługi: po zalogowaniu się wyświetlą się warunki korzystania z usługi Google Cloud Platform, które musisz zaakceptować, aby kontynuować.
- Utwórz nowy projekt: nowy pusty projekt musi zostać utworzony w konsoli Google Cloud.
- Połącz konto rozliczeniowe: połącz nowo utworzony projekt z kontem rozliczeniowym.
- Potwierdź przyznanie środków: poniższy film pokazuje, jak potwierdzić, że środki zostały przyznane projektowi. W tym celu należy sprawdzić sekcję „Środki” na stronie płatności.
Możesz obejrzeć ten film, aby dowiedzieć się, jak wykorzystać środki.
Tworzenie i konfigurowanie klucza interfejsu API
Aby uwierzytelnić agentów AI ADK w interfejsie Gemini API w tym samouczku, użyjesz klucza interfejsu API powiązanego z Twoim projektem Google Cloud.
- Wygeneruj klucz interfejsu API:
- Otwórz Google AI Studio i kliknij link „Uzyskaj klucz interfejsu API” u dołu panelu po lewej stronie.
- Kliknij kolejno Projekty i przycisk Importuj projekty.
- Wyszukaj i wybierz projekt Google Cloud, który chcesz zaimportować, a potem kliknij przycisk Importuj.
- Po zaimportowaniu projektu otwórz stronę Klucze interfejsów API w menu Panel i utwórz klucz interfejsu API w zaimportowanym projekcie.
- Zanotuj klucz interfejsu API.
- Ustaw zmienną środowiskową: Twój agent musi mieć dostęp do tego klucza. Standardowym sposobem jest ustawienie zmiennej środowiskowej o nazwie
GOOGLE_API_KEY
.
- macOS / Linux: otwórz terminal i uruchom to polecenie, zastępując
"your-api-key"
skopiowanym kluczem. Aby to zrobić na stałe, dodaj ten wiersz do pliku startowego powłoki (np.~/.bash_profile
,~/.zshrc
).
export GOOGLE_API_KEY="your-api-key"
- Windows (wiersz polecenia): otwórz nowy wiersz polecenia i uruchom:
setx GOOGLE_API_KEY "your-api-key"
- Aby ta zmiana została zastosowana, musisz ponownie uruchomić wiersz poleceń.
- Windows (PowerShell): otwórz terminal PowerShell i uruchom:
$env:GOOGLE_API_KEY="your-api-key"
- Aby ta zmiana była trwała w PowerShellu, musisz dodać ją do skryptu profilu.
3. Pierwsze kroki: Twój pierwszy agent
Najlepszym sposobem na rozpoczęcie nowego projektu jest użycie szablonu ADK for Java na GitHubie. Zawiera strukturę projektu i wszystkie niezbędne zależności.
Jeśli masz konto GitHub, możesz wykonać te czynności: Use this template
> Create a new repository
, a następnie pobrać kod lokalnie za pomocą polecenia git clone
.
Poniżej znajdziesz zrzut ekranu przedstawiający menu w prawym górnym rogu, które umożliwia użycie szablonu.
Innym podejściem jest po prostu sklonowanie tego repozytorium bezpośrednio za pomocą polecenia:
git clone https://github.com/glaforge/adk-java-maven-template.git
Następnie cd
do adk-java-maven-template
.
Aby sprawdzić, czy możesz zacząć pisać kod pierwszego agenta AI w Javie, upewnij się, że możesz skompilować kod w tym projekcie za pomocą:
mvn compile
Krok z kodem: przyjazny nauczyciel nauk ścisłych
Podstawowym elementem ADK jest klasa LlmAgent
. Możesz sobie wyobrazić, że jest to AI o określonej osobowości i celu, która korzysta z dużego modelu językowego. W późniejszym czasie dodamy więcej funkcji za pomocą narzędzi i zwiększymy jego możliwości, współpracując z innymi podobnymi agentami.
Utwórzmy nową klasę Java w pakiecie com.example.agent
i nazwijmy ją ScienceTeacher
.
To odpowiednik „Hello, World!” w przypadku tworzenia agentów. Definiujemy prostego agenta o osobowości nauczyciela nauk ścisłych.
// 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()
);
}
}
Agent AI jest konfigurowany za pomocą metody LlmAgent.builder()
. Parametry name()
, description()
i model()
są obowiązkowe. Aby nadać agentowi konkretną osobowość i odpowiednie zachowanie, zawsze podawaj szczegółowe wskazówki za pomocą metody instruction()
.
W tym przypadku wybraliśmy model Gemini 2.5 Flash, ale w przypadku bardziej skomplikowanych zadań możesz też wypróbować Gemini 2.5 Pro.
Ten agent jest opakowany w metodę AdkWebServer.start()
. Jest to tzw. interfejs czatu ADK Dev UI. Umożliwia prowadzenie rozmowy z agentem w typowym interfejsie czatu. Jest to też bardzo przydatne, jeśli chcesz zrozumieć, co się dzieje w tle, np. jakie zdarzenia przepływają przez system oraz jakie żądania i odpowiedzi są wysyłane do LLM.
Aby skompilować i uruchomić tego agenta lokalnie, uruchom to polecenie:
mvn compile exec:java -Dexec.mainClass=com.example.agent.ScienceTeacher
Następnie otwórz przeglądarkę i wpisz adres http://localhost:8080. Powinien być widoczny interfejs pokazany na zrzucie ekranu poniżej. Zadaj agentowi pytania związane z nauką.
4. Zapewnianie agentom narzędzi
Dlaczego agenci potrzebują narzędzi? LLM to potężne narzędzia, ale ich wiedza jest zamrożona w czasie i nie mogą wchodzić w interakcje ze światem zewnętrznym. Narzędzia są pomostem. Umożliwiają one agentowi dostęp do informacji w czasie rzeczywistym (np. cen akcji lub wiadomości), wysyłanie zapytań do prywatnych interfejsów API lub wykonywanie dowolnych działań, które można zaprogramować w języku Java.
Krok z kodem: tworzenie narzędzia niestandardowego (StockTicker
)
W tym przypadku udostępniamy agentowi narzędzie do sprawdzania cen akcji. Agent wnioskuje, że gdy użytkownik zapyta o cenę, powinien wywołać naszą metodę 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)
}
}
Aby zwiększyć inteligencję agentów i umożliwić im interakcję ze światem (lub z własnym kodem, interfejsami API, usługami itp.), możesz skonfigurować agenta tak, aby używał narzędzi, a w szczególności narzędzi z kodem niestandardowym, za pomocą metody tools()
, przekazując mu FunctionTool.create(...)
.
Element FunctionTool
wymaga nazwy klasy i metody wskazującej Twoją własną metodę statyczną. Możesz też przekazać instancję klasy i nazwę metody instancji tego obiektu.
Ważne jest, aby określić name
i description
zarówno metody, jak i jej parametrów za pomocą adnotacji @Schema
, ponieważ te informacje będą używane przez bazowy LLM do określania, kiedy i jak należy wywołać daną metodę.
Równie ważne jest, aby przekazać modelowi LLM jasne instrukcje dotyczące tego, jak i kiedy używać narzędzia. Model może sam to ustalić, ale jeśli podasz wyraźne wyjaśnienia w instruction()
method, Twoja funkcja ma większe szanse na prawidłowe wywołanie.
Ta metoda powinna zwrócić wartość Map
. Zwykle chodzi o zwrócenie mapy z kluczem reprezentującym wynik, np. stock_price
, i powiązanie z nim wartości ceny akcji. Możesz też dodać dodatkową parę klucz-wartość success / true, aby zasygnalizować powodzenie operacji. W przypadku błędu należy zwrócić mapę z kluczem o nazwie np. error
i powiązać z nim komunikat o błędzie. Pomoże to LLM zrozumieć, czy wywołanie się powiodło, czy też z jakiegoś powodu nie.
- W przypadku powodzenia zwraca:
{"stock_price": 123}
- W przypadku błędu zwróć:
{"error": "Impossible to retrieve stock price for XYZ"}
Następnie uruchom tę klasę za pomocą tego polecenia:
mvn compile exec:java -Dexec.mainClass=com.example.agent.StockTicker
5. Wyszukiwarka Google jako źródło aktualnych informacji
ADK dla Javy zawiera kilka przydatnych narzędzi, w tym GoogleSearchTool
. Dzięki temu narzędziu agent może poprosić o użycie wyszukiwarki Google, aby znaleźć odpowiednie informacje i osiągnąć swój cel.
Wiedza modelu LLM jest zamrożona w czasie: został on wytrenowany do określonej daty („daty odcięcia”) na podstawie danych, które są aktualne w momencie ich zebrania. Oznacza to, że duże modele językowe niekoniecznie muszą znać najnowsze wydarzenia, a ich wiedza może być ograniczona i powierzchowna. Wyszukiwarka może im przypomnieć lub nauczyć ich więcej na dany temat.
Przyjrzyjmy się temu prostemu agentowi wyszukiwania wiadomości:
// 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());
}
}
Zwróć uwagę, jak przekazaliśmy instancję narzędzia wyszukiwania za pomocą: tools(new GoogleSearchTool())
. Dzięki temu nasz agent może szybko zapoznać się z najnowszymi informacjami dostępnymi w internecie. Prompt zawierał też datę, ponieważ może to pomóc LLM zrozumieć, kiedy pytania dotyczą informacji z przeszłości i kiedy wymagane jest wyszukanie nowszych informacji.
Następnie uruchom tę klasę za pomocą tego polecenia:
mvn compile exec:java -Dexec.mainClass=com.example.agent.LatestNews
Możesz eksperymentować z promptem, aby uzyskać różne wyniki pod względem stylu, zwięzłości, tematu itp.
Krok z kodem: agent wyszukiwania jako narzędzie
Zamiast przekazywać GoogleSearchTool
bezpośrednio do agenta jako narzędzie, możesz utworzyć dedykowanego agenta wyszukiwania, który będzie zawierać funkcję wyszukiwania, i udostępnić tego agenta jako narzędzie agentowi wyższego poziomu.
Jest to zaawansowana koncepcja, która umożliwia delegowanie złożonych działań (takich jak wyszukiwanie i podsumowywanie wyników) do wyspecjalizowanego subagenta. To podejście jest często przydatne w przypadku bardziej złożonych przepływów pracy, ponieważ wbudowanych narzędzi nie można używać z narzędziami opartymi na kodzie niestandardowym.
// 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()
);
}
}
Kluczowym pojęciem jest tutaj AgentTool.create(searchAgent)
. Rejestruje cały interfejs searchAgent
(z własną logiką wewnętrzną, promptem i narzędziami) jako pojedyncze narzędzie wywoływane przez mainAgent
. Sprzyja to modułowości i możliwości ponownego wykorzystania.
Uruchom tę klasę za pomocą tego polecenia:
mvn compile exec:java -Dexec.mainClass=com.example.agent.SearchAgentAsTool
Na proste pytania agent odpowie na podstawie własnej bazy wiedzy, ale gdy zostanie zapytany o ostatnie wydarzenia, przekaże wyszukiwanie wyspecjalizowanemu agentowi wyszukiwania za pomocą narzędzia wyszukiwarki Google.
6. Opanowanie przepływów pracy agenta
W przypadku złożonych problemów jeden pracownik obsługi klienta nie wystarczy. Gdy model LLM otrzyma cel składający się ze zbyt wielu podzadań, z ogromnym promptem zawierającym zbyt wiele szczegółów lub z dostępem do zbyt wielu funkcji, ma problemy, a jego wydajność i dokładność maleją.
Kluczem jest dzielenie i współpraca, czyli koordynowanie pracy wielu wyspecjalizowanych agentów. Na szczęście pakiet ADK zawiera różne wbudowane agenty specjalistyczne:
- zwykły agent z
subAgent()
do delegowania zadań, SequentialAgent
, aby wykonywać zadania w określonej kolejności,ParallelAgent
, aby wykonywać agentów równolegle,LoopAgent
, zwykle w celu przeprowadzenia procesu udoskonalania tyle razy, ile to konieczne.
Główne przypadki użycia oraz zalety i wady każdego z tych procesów znajdziesz w tabeli poniżej. Pamiętaj jednak, że prawdziwa siła tkwi w połączeniu kilku z nich.
Workflow | Zajęcia ADK | Use Case | Zalety | Wady |
Sub-agenci |
| Zadania elastyczne, w których to użytkownik decyduje o dalszych krokach. | Duża elastyczność, konwersacyjność, świetne rozwiązanie dla botów, z których korzystają użytkownicy. | Mniej przewidywalne, polega na rozumowaniu LLM w zakresie kontroli przepływu. |
Sekwencyjny |
| Stałe, wieloetapowe procesy, w których kolejność ma kluczowe znaczenie. | Przewidywalne, niezawodne, łatwe do debugowania, gwarantuje kolejność. | Nieelastyczne, może działać wolniej, jeśli zadania można wykonywać równolegle. |
Równolegle |
| Zbieranie danych z wielu źródeł lub wykonywanie niezależnych zadań. | Wysoka wydajność, znaczne skrócenie czasu oczekiwania w przypadku zadań związanych z operacjami wejścia/wyjścia. | Wszystkie zadania są wykonywane, nie ma skracania. Mniej odpowiednie w przypadku zadań z zależnościami. |
Zapętl |
| Iteracyjne udoskonalanie, samokorekta lub procesy, które powtarzają się do momentu spełnienia określonego warunku. | Skuteczny w rozwiązywaniu złożonych problemów, umożliwia pracownikom zespołu pomocy ulepszanie własnej pracy. | Jeśli nie zostanie starannie zaprojektowana, może prowadzić do nieskończonych pętli (zawsze używaj parametru maxIterations!). |
7. Przepływy pracy agenta – delegowanie zadań za pomocą subagentów
Agent nadzorujący może przekazywać określone zadania podległym agentom. Wyobraź sobie na przykład agenta na stronie e-commerce, który przekazuje pytania dotyczące zamówień jednemu agentowi („Jaki jest stan mojego zamówienia?”), a pytania dotyczące obsługi posprzedażowej innemu agentowi („Nie wiem, jak to włączyć!”). To jest przypadek użycia, który omówimy.
// 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()
);
}
}
Kluczowy wiersz to miejsce, w którym wywoływana jest metoda subAgents()
. Przekazuje ona 2 podagenty, których konkretne role będą obsługiwane oddzielnie.
Uruchom powyższy przykład za pomocą tego polecenia:
mvn compile exec:java -Dexec.mainClass=com.example.agent.SupportAgent
Koncepcja delegowania zadań do podagentów odzwierciedla skuteczne zarządzanie ludźmi, w którym dobry menedżer (agent nadzorujący) polega na wyspecjalizowanych pracownikach (podagentach), którzy wykonują określone zadania, w których mają większą wiedzę. Kierownik nie musi znać szczegółów każdego procesu. Zamiast tego inteligentnie kieruje prośbę klienta (np. zapytanie dotyczące zamówienia lub problem techniczny) do najbardziej wykwalifikowanego „członka zespołu”, zapewniając wyższą jakość i większą skuteczność odpowiedzi niż w przypadku, gdyby odpowiadał tylko jeden specjalista. Ponadto podagenci mogą w pełni skoncentrować się na swoich indywidualnych zadaniach bez konieczności zrozumienia całego złożonego procesu.
8. Przepływy pracy agenta – linia montażowa
Gdy kolejność działań ma znaczenie, użyj SequentialAgent
. Działa to jak linia montażowa, która wykonuje podagenty w ustalonej kolejności, a każdy krok może zależeć od poprzedniego.
Wyobraźmy sobie, że angielski poeta współpracuje z tłumaczem z języka angielskiego na francuski, aby tworzyć wiersze najpierw w języku angielskim, a potem tłumaczyć je na francuski:
// 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());
}
}
Uruchom przykład, aby uzyskać wiersz w języku angielskim, a następnie przetłumaczony na język francuski, za pomocą tego polecenia:
mvn compile exec:java -Dexec.mainClass=com.example.agent.PoetAndTranslator
Systematyczny podział złożonych zadań na mniejsze, uporządkowane podzadania zapewnia bardziej deterministyczny i niezawodny proces, co znacznie zwiększa prawdopodobieństwo osiągnięcia sukcesu w porównaniu z poleganiem na jednym, ogólnym agencie.
Skuteczne dzielenie zadania na sekwencję podzadań (w miarę możliwości i potrzeb) ma kluczowe znaczenie dla uzyskania bardziej deterministycznych i skutecznych wyników, ponieważ umożliwia uporządkowany postęp i zarządzanie zależnościami między poszczególnymi krokami.
9. Przepływy pracy agenta – praca równoległa
Gdy zadania są niezależne, ParallelAgent
znacznie zwiększa wydajność, ponieważ wykonuje je jednocześnie. W tym przykładzie połączymy nawet SequentialAgent
z ParallelAgent
: najpierw zostaną wykonane zadania równoległe, a potem agent podsumuje ich wyniki.
Stwórzmy detektywa firmowego, którego zadaniem będzie wyszukiwanie informacji o:
- Profil firmy (prezes, siedziba, motto itp.)
- Najnowsze informacje o firmie.
- Szczegóły dotyczące finansów firmy.
// 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());
}
}
Jak zwykle możesz uruchomić agenta za pomocą tego polecenia:
mvn compile exec:java -Dexec.mainClass=com.example.agent.CompanyDetective
Ten agent demonstruje potężne połączenie przepływów pracy, w którym zarówno agenci równolegli, jak i sekwencyjni są dobrze wykorzystywani w efektywny sposób dzięki równoległemu wyszukiwaniu i syntezie informacji.
10. Przepływy pracy agentów – iteracyjne udoskonalanie
W przypadku zadań wymagających cyklu „generowanie → sprawdzanie → ulepszanie” używaj LoopAgent
. Automatyzuje proces iteracyjnego ulepszania, aż do osiągnięcia celu. Podobnie jak w przypadku SequentialAgent
, LoopAgent
będzie wywoływać podagenta szeregowo, ale będzie wracać na początek. To model LLM używany wewnętrznie przez agenta zdecyduje, czy poprosić o wywołanie specjalnego narzędzia, czyli exit_loop
narzędzia wbudowanego, aby zatrzymać wykonanie pętli.
Poniższy przykład narzędzia do ulepszania kodu, które korzysta z LoopAgent
, automatyzuje ulepszanie kodu: generowanie, sprawdzanie i poprawianie. Naśladuje to rozwój człowieka. Generator kodu najpierw generuje żądany kod i zapisuje go w stanie agenta pod kluczem generated_code
. Następnie osoba sprawdzająca kod weryfikuje wygenerowany kod i albo przekazuje opinię (pod klawiszem feedback
), albo wywołuje narzędzie do zamykania pętli, aby wcześniej zakończyć iterację.
Przyjrzyjmy się kodowi:
// 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());
}
}
Uruchom tego agenta za pomocą tego polecenia:
mvn compile exec:java -Dexec.mainClass=com.example.agent.CodeRefiner
Pętle opinii i ulepszania, zaimplementowane za pomocą funkcji LoopAgent
, są niezbędne do rozwiązywania problemów, które wymagają iteracyjnego ulepszania i samokorekty, co ściśle naśladuje ludzkie procesy poznawcze. Ten wzorzec projektowy jest szczególnie przydatny w przypadku zadań, w których początkowe dane wyjściowe rzadko są idealne, takich jak generowanie kodu, kreatywne pisanie, iteracja projektu czy złożona analiza danych. Dzięki przekazywaniu wyników do specjalistycznego agenta weryfikującego, który przekazuje strukturalne opinie, agent generujący może stale ulepszać swoją pracę, aż do spełnienia określonego kryterium ukończenia. Prowadzi to do znacznie wyższej jakości i bardziej wiarygodnych wyników końcowych niż w przypadku podejścia jednoprzebiegowego.
11. Gratulacje!
Udało Ci się utworzyć i wypróbować różne agenty AI – od prostych rozmówców po złożone systemy wieloagentowe. Poznałeś(-aś) podstawowe koncepcje ADK for Java: definiowanie agentów za pomocą instrukcji, udostępnianie im narzędzi i organizowanie ich w zaawansowane przepływy pracy.
Co dalej?
- Zapoznaj się z oficjalnym repozytorium ADK dla Javy w GitHubie.
- Więcej informacji o tym frameworku znajdziesz w jego dokumentacji.
- Więcej informacji o różnych przepływach pracy opartych na agentach znajdziesz w tej serii artykułów na blogu, a o różnych dostępnych narzędziach – w tym artykule.
- Poznaj bliżej inne wbudowane narzędzia i zaawansowane wywołania zwrotne.
- Obsługuj kontekst, stan i artefakty, aby uzyskać bogatsze i wielomodalne interakcje.
- Wdrażaj i stosuj wtyczki, które są zintegrowane z cyklem życia Twoich agentów.
- Spróbuj utworzyć własnego agenta, który rozwiązuje rzeczywisty problem.