1. خوش آمدید، توسعه دهندگان عامل هوش مصنوعی!
در این لبه کد، نحوه ساخت عوامل هوش مصنوعی در جاوا را با استفاده از کیت توسعه عامل (ADK) برای جاوا یاد خواهید گرفت. ما فراتر از فراخوانی های ساده API مدل زبان بزرگ (LLM) خواهیم رفت تا عوامل هوش مصنوعی مستقلی ایجاد کنیم که می توانند استدلال کنند، برنامه ریزی کنند، از ابزارها استفاده کنند و برای حل مشکلات پیچیده با هم کار کنند.
شما با بازخرید اعتبار Google Cloud، راهاندازی محیط Google Cloud، سپس ساختن اولین عامل ساده خود و افزودن تدریجی قابلیتهای پیشرفتهتر مانند ابزارهای سفارشی، جستجوی وب، و هماهنگی چند عاملی شروع میکنید.
چیزی که یاد خواهید گرفت
- نحوه ایجاد یک عامل هوش مصنوعی مبتنی بر شخصیت.
- نحوه توانمندسازی نمایندگان با ابزارهای سفارشی و داخلی (مانند جستجوی گوگل).
- چگونه ابزارهای پیاده سازی شده خود را در جاوا اضافه کنید.
- چگونه چندین عامل را در جریان های کاری قدرتمند متوالی، موازی و حلقه ای هماهنگ کنیم.
آنچه شما نیاز دارید
- یک مرورگر وب که در حالت ناشناس از آن استفاده خواهیم کرد.
- یک حساب شخصی جیمیل
- یک پروژه جدید Google Cloud مرتبط با حساب شخصی جیمیل شما.
- یک حساب صورتحساب ایجاد شده با اعتبارات 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 خود با Gemini API برای این آزمایشگاه کد، از یک کلید API مرتبط با پروژه Google Cloud خود استفاده خواهید کرد.
- یک کلید API ایجاد کنید:
- به Google AI Studio بروید و روی پیوند «دریافت کلید API» در پایین پانل سمت چپ کلیک کنید.
- Projects را انتخاب کرده و سپس روی دکمه Import projects کلیک کنید.
- پروژه Google Cloud را که می خواهید وارد کنید را جستجو و انتخاب کنید ، سپس دکمه Import را انتخاب کنید.
- هنگامی که پروژه وارد شد، از منوی داشبورد به صفحه کلیدهای API بروید و یک کلید API در پروژه ای که به تازگی وارد کرده اید ایجاد کنید.
- کلید API را یادداشت کنید .
- Set the Environment Variable: نماینده شما باید به این کلید دسترسی داشته باشد. راه استاندارد با تنظیم یک متغیر محیطی به نام
GOOGLE_API_KEY
است.
- macOS / Linux: ترمینال خود را باز کنید و دستور زیر را اجرا کنید و کلیدی را که به تازگی کپی کرده اید جایگزین
"your-api-key"
کنید. برای دائمی کردن آن، این خط را به فایل راه اندازی پوسته خود اضافه کنید (به عنوان مثال،~/.bash_profile
،~/.zshrc
).
export GOOGLE_API_KEY="your-api-key"
- Windows (Command Prompt): یک خط فرمان جدید باز کنید و اجرا کنید:
setx GOOGLE_API_KEY "your-api-key"
- برای اعمال این تغییر، باید خط فرمان خود را مجددا راه اندازی کنید.
- ویندوز (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
.
برای بررسی اینکه آیا آماده شروع کدنویسی اولین عامل هوش مصنوعی خود در جاوا هستید، مطمئن شوید که میتوانید کد را در این پروژه با موارد زیر کامپایل کنید:
mvn compile
مرحله کد: یک نماینده معلم دوستانه علوم
بلوک اصلی ساختمان در ADK کلاس LlmAgent
است. به آن به عنوان یک هوش مصنوعی با شخصیت و هدف خاص فکر کنید که توسط یک مدل زبان بزرگ طراحی شده است. ما بعداً قابلیتهای بیشتری را از طریق ابزارها اضافه خواهیم کرد و با همکاری دست به دست با سایر عوامل مشابه، آن را قدرتمندتر خواهیم کرد.
بیایید یک کلاس جاوا جدید در بسته 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 را انتخاب کردیم، اما برای کارهای پیچیده تر، راحت تر Gemini 2.5 Pro را نیز امتحان کنید.
این عامل در متد AdkWebServer.start()
پیچیده شده است. این به اصطلاح رابط چت ADK Dev UI است. این به شما امکان می دهد از طریق یک رابط چت معمولی با نماینده مکالمه کنید. علاوه بر این، اگر میخواهید بفهمید در زیر سرپوش چه اتفاقی میافتد، کمک بزرگی است، مانند همه رویدادهایی که در سیستم جریان دارند، درخواستها و پاسخهای ارسال شده به LLM.
برای کامپایل و اجرای این عامل به صورت محلی، دستور زیر را اجرا کنید:
mvn compile exec:java -Dexec.mainClass=com.example.agent.ScienceTeacher
سپس به مرورگر خود در http://localhost:8080 بروید. شما باید UI را همانطور که در تصویر زیر نشان داده شده است ببینید. ادامه دهید و سوالات مربوط به علم را از نماینده خود بپرسید.
4. توانمندسازی عوامل با ابزار
چرا نمایندگان به ابزار نیاز دارند؟ LLM ها قدرتمند هستند، اما دانش آنها در زمان منجمد شده است و نمی توانند با دنیای خارج تعامل داشته باشند. ابزارها پل هستند. آنها به یک عامل اجازه می دهند به اطلاعات بلادرنگ (مانند قیمت سهام یا اخبار)، پرس و جو از APIهای خصوصی یا انجام هر عملی که می توانید در جاوا کدنویسی کنید، دسترسی داشته باشد.
مرحله کد: ایجاد یک ابزار سفارشی ( StockTicker
)
در اینجا، ما به عامل خود ابزاری می دهیم تا قیمت سهام را جستجو کند. دلیل این عامل این است که وقتی کاربر قیمت می پرسد، باید متد جاوا ما را فراخوانی کند.
// 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. قدرت جستجوی گوگل برای اطلاعات به روز
ADK برای جاوا با تعداد انگشت شماری از ابزارهای قدرتمند ارائه می شود که یکی از آنها GoogleSearchTool
است. با استفاده از این ابزار، نماینده شما می تواند استفاده از جستجوی گوگل را برای یافتن اطلاعات مرتبط برای رسیدن به هدف خود درخواست کند.
در واقع، دانش یک 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())
ارسال کردیم. این همان چیزی است که به نماینده ما این توانایی را میدهد تا با آخرین اطلاعاتی که در وب یافت میشود، سریعتر شود. همچنین، اعلان تاریخ روز را مشخص میکند، زیرا ممکن است به LLM کمک کند تا بفهمد چه زمانی سؤالات مربوط به اطلاعات گذشته است و جستجوی اطلاعات جدیدتر مورد نیاز است.
سپس این کلاس را با دستور زیر اجرا کنید:
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. تسلط بر گردش کار عامل
برای مشکلات پیچیده، یک عامل واحد کافی نیست. هنگامی که هدفی شامل وظایف فرعی بیش از حد، با توضیحی عظیم با جزئیات بیش از حد، یا دسترسی به تعداد زیادی از عملکردها، LLM ها با مشکل مواجه می شوند و عملکرد و دقت آنها کاهش می یابد.
نکته کلیدی این است که با سازماندهی چندین عامل تخصصی تقسیم و تسخیر کنید . خوشبختانه، ADK با عوامل تخصصی داخلی مختلف ارائه می شود:
- عامل عادی با
subAgent()
برای واگذاری وظایف به -
SequentialAgent
، برای انجام کارها به صورت متوالی، -
ParallelAgent
، برای اجرای عوامل به صورت موازی، -
LoopAgent
، معمولاً برای گذراندن یک فرآیند پالایش به تعداد دفعات مورد نیاز.
موارد استفاده کلیدی، و مزایا و معایب هر گردش کار چیست، لطفاً به جدول زیر نگاهی بیندازید. اما بدانید که قدرت واقعی در واقع از ترکیب چندین مورد از آنها به دست خواهد آمد!
گردش کار | کلاس ADK | استفاده از مورد | جوانب مثبت | منفی |
عوامل فرعی | | وظایف کاربر محور و انعطاف پذیر که مرحله بعدی همیشه مشخص نیست. | انعطاف پذیری بالا، مکالمه، عالی برای روبات های رو به رو کاربر. | کمتر قابل پیش بینی است، برای کنترل جریان به استدلال LLM متکی است. |
متوالی | | فرآیندهای چند مرحله ای ثابت که در آن نظم حیاتی است. | قابل پیش بینی، قابل اعتماد، اشکال زدایی آسان، نظم را تضمین می کند. | انعطاف ناپذیر است، اگر بتوان وظایف را موازی کرد، می تواند کندتر باشد. |
موازی | | جمع آوری داده ها از منابع متعدد یا اجرای وظایف مستقل. | بسیار کارآمد، تأخیر برای کارهای I/O-bound را به میزان قابل توجهی کاهش می دهد. | تمام وظایف اجرا می شود. بدون اتصال کوتاه برای کارهای با وابستگی کمتر مناسب است. |
حلقه | | پالایش تکراری، خود تصحیح یا فرآیندهایی که تکرار می شوند تا زمانی که یک شرط برآورده شود. | قدرتمند برای حل مشکلات پیچیده، عوامل را قادر می سازد تا کار خود را بهبود بخشند. | اگر با دقت طراحی نشود، می تواند به حلقه های بی نهایت منجر شود (همیشه از 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
، پالایش کد را خودکار میکند: ایجاد، بررسی، تصحیح. این رشد انسانی را تقلید می کند. یک مولد کد ابتدا کد درخواستی را تولید می کند و آن را در حالت agent در زیر کلید 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 برای جاوا را یاد گرفته اید: تعریف عامل ها با دستورالعمل ها، توانمندسازی آنها با ابزارها، و هماهنگ سازی آنها در جریان های کاری قدرتمند.
بعدش چی؟
- ADK رسمی برای مخزن جاوا GitHub را کاوش کنید.
- درباره چارچوب در مستندات آن بیشتر بیاموزید.
- در مورد گردش کارهای مختلف نمایندگی در این مجموعه وبلاگ و در مورد ابزارهای مختلف موجود بخوانید.
- در سایر ابزارهای داخلی و تماس های پیشرفته بیشتر غوطه ور شوید.
- برای تعاملات غنی تر و چندوجهی، زمینه، وضعیت و مصنوعات را مدیریت کنید.
- پلاگین هایی را که به چرخه حیات عوامل شما متصل می شوند، پیاده سازی و اعمال کنید.
- سعی کنید نماینده خود را بسازید که یک مشکل دنیای واقعی را حل کند!
1. خوش آمدید، توسعه دهندگان عامل هوش مصنوعی!
در این لبه کد، نحوه ساخت عوامل هوش مصنوعی در جاوا را با استفاده از کیت توسعه عامل (ADK) برای جاوا یاد خواهید گرفت. ما فراتر از فراخوانی های ساده API مدل زبان بزرگ (LLM) خواهیم رفت تا عوامل هوش مصنوعی مستقلی ایجاد کنیم که می توانند استدلال کنند، برنامه ریزی کنند، از ابزارها استفاده کنند و برای حل مشکلات پیچیده با هم کار کنند.
شما با بازخرید اعتبار Google Cloud، راهاندازی محیط Google Cloud، سپس ساختن اولین عامل ساده خود و افزودن تدریجی قابلیتهای پیشرفتهتر مانند ابزارهای سفارشی، جستجوی وب، و هماهنگی چند عاملی شروع میکنید.
چیزی که یاد خواهید گرفت
- نحوه ایجاد یک عامل هوش مصنوعی مبتنی بر شخصیت.
- نحوه توانمندسازی نمایندگان با ابزارهای سفارشی و داخلی (مانند جستجوی گوگل).
- چگونه ابزارهای پیاده سازی شده خود را در جاوا اضافه کنید.
- چگونه چندین عامل را در جریان های کاری قدرتمند متوالی، موازی و حلقه ای هماهنگ کنیم.
آنچه شما نیاز دارید
- یک مرورگر وب که در حالت ناشناس از آن استفاده خواهیم کرد.
- یک حساب شخصی جیمیل
- یک پروژه جدید Google Cloud مرتبط با حساب شخصی جیمیل شما.
- یک حساب صورتحساب ایجاد شده با اعتبارات 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 خود با Gemini API برای این آزمایشگاه کد، از یک کلید API مرتبط با پروژه Google Cloud خود استفاده خواهید کرد.
- یک کلید API ایجاد کنید:
- به Google AI Studio بروید و روی پیوند «دریافت کلید API» در پایین پانل سمت چپ کلیک کنید.
- Projects را انتخاب کرده و سپس روی دکمه Import projects کلیک کنید.
- پروژه Google Cloud را که می خواهید وارد کنید را جستجو و انتخاب کنید ، سپس دکمه Import را انتخاب کنید.
- هنگامی که پروژه وارد شد، از منوی داشبورد به صفحه کلیدهای API بروید و یک کلید API در پروژه ای که به تازگی وارد کرده اید ایجاد کنید.
- کلید API را یادداشت کنید .
- Set the Environment Variable: نماینده شما باید به این کلید دسترسی داشته باشد. راه استاندارد با تنظیم یک متغیر محیطی به نام
GOOGLE_API_KEY
است.
- macOS / Linux: ترمینال خود را باز کنید و دستور زیر را اجرا کنید و کلیدی را که به تازگی کپی کرده اید جایگزین
"your-api-key"
کنید. برای دائمی کردن آن، این خط را به فایل راه اندازی پوسته خود اضافه کنید (به عنوان مثال،~/.bash_profile
،~/.zshrc
).
export GOOGLE_API_KEY="your-api-key"
- Windows (Command Prompt): یک خط فرمان جدید باز کنید و اجرا کنید:
setx GOOGLE_API_KEY "your-api-key"
- برای اعمال این تغییر، باید خط فرمان خود را مجددا راه اندازی کنید.
- ویندوز (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
.
برای بررسی اینکه آیا آماده شروع کدنویسی اولین عامل هوش مصنوعی خود در جاوا هستید، مطمئن شوید که میتوانید کد را در این پروژه با موارد زیر کامپایل کنید:
mvn compile
مرحله کد: یک نماینده معلم دوستانه علوم
بلوک اصلی ساختمان در ADK کلاس LlmAgent
است. به آن به عنوان یک هوش مصنوعی با شخصیت و هدف خاص فکر کنید که توسط یک مدل زبان بزرگ طراحی شده است. ما بعداً قابلیتهای بیشتری را از طریق ابزارها اضافه خواهیم کرد و با همکاری دست به دست با سایر عوامل مشابه، آن را قدرتمندتر خواهیم کرد.
بیایید یک کلاس جاوا جدید در بسته 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 را انتخاب کردیم، اما برای کارهای پیچیده تر، راحت تر Gemini 2.5 Pro را نیز امتحان کنید.
این عامل در متد AdkWebServer.start()
پیچیده شده است. این به اصطلاح رابط چت ADK Dev UI است. این به شما امکان می دهد از طریق یک رابط چت معمولی با نماینده مکالمه کنید. علاوه بر این، اگر میخواهید بفهمید در زیر سرپوش چه اتفاقی میافتد، کمک بزرگی است، مانند همه رویدادهایی که در سیستم جریان دارند، درخواستها و پاسخهای ارسال شده به LLM.
برای کامپایل و اجرای این عامل به صورت محلی، دستور زیر را اجرا کنید:
mvn compile exec:java -Dexec.mainClass=com.example.agent.ScienceTeacher
سپس به مرورگر خود در http://localhost:8080 بروید. شما باید UI را همانطور که در تصویر زیر نشان داده شده است ببینید. ادامه دهید و سوالات مربوط به علم را از نماینده خود بپرسید.
4. توانمندسازی عوامل با ابزار
چرا نمایندگان به ابزار نیاز دارند؟ LLM ها قدرتمند هستند، اما دانش آنها در زمان منجمد شده است و نمی توانند با دنیای خارج تعامل داشته باشند. ابزارها پل هستند. آنها به یک عامل اجازه می دهند به اطلاعات بلادرنگ (مانند قیمت سهام یا اخبار)، پرس و جو از APIهای خصوصی یا انجام هر عملی که می توانید در جاوا کدنویسی کنید، دسترسی داشته باشد.
مرحله کد: ایجاد یک ابزار سفارشی ( StockTicker
)
در اینجا، ما به عامل خود ابزاری می دهیم تا قیمت سهام را جستجو کند. دلیل این عامل این است که وقتی کاربر قیمت می پرسد، باید متد جاوا ما را فراخوانی کند.
// 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. قدرت جستجوی گوگل برای اطلاعات به روز
ADK برای جاوا با تعداد انگشت شماری از ابزارهای قدرتمند ارائه می شود که یکی از آنها GoogleSearchTool
است. با استفاده از این ابزار، نماینده شما می تواند استفاده از جستجوی گوگل را برای یافتن اطلاعات مرتبط برای رسیدن به هدف خود درخواست کند.
در واقع، دانش یک 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())
ارسال کردیم. 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.
بیایید نگاهی به کد بیندازیم:
// 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.
بیایید نگاهی به کد بیندازیم:
// 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!