১. 📖 ভূমিকা
এই কোডল্যাবটি এজেন্ট ডেভেলপমেন্ট কিট (ADK) তে মাল্টিমোডাল টুল ইন্টারঅ্যাকশন কীভাবে ডিজাইন করতে হয় তা দেখায়। এটি একটি নির্দিষ্ট প্রবাহ যেখানে আপনি এজেন্টকে একটি টুলের ইনপুট হিসাবে আপলোড করা ফাইলটি উল্লেখ করতে চান এবং টুল প্রতিক্রিয়া দ্বারা উত্পাদিত ফাইলের বিষয়বস্তুও বুঝতে চান। তাই নীচের স্ক্রিনশটে দেখানো ইন্টারঅ্যাকশন সম্ভব। এই টিউটোরিয়ালে আমরা এমন একটি এজেন্ট তৈরি করতে যাচ্ছি যা ব্যবহারকারীকে তাদের পণ্য শোকেসের জন্য আরও ভাল ছবি সম্পাদনা করতে সহায়তা করতে সক্ষম।
কোডল্যাবের মাধ্যমে, আপনি নিম্নরূপ ধাপে ধাপে পদ্ধতি ব্যবহার করবেন:
- গুগল ক্লাউড প্রজেক্ট প্রস্তুত করুন
- কোডিং পরিবেশের জন্য কাজের ডিরেক্টরি সেটআপ করুন
- ADK ব্যবহার করে এজেন্ট শুরু করুন
- জেমিনি ২.৫ ফ্ল্যাশ ইমেজ দ্বারা চালিত ছবি সম্পাদনা করার জন্য ব্যবহার করা যেতে পারে এমন একটি টুল ডিজাইন করুন
- ব্যবহারকারীর ছবি আপলোড পরিচালনা করার জন্য একটি কলব্যাক ফাংশন ডিজাইন করুন, এটিকে আর্টিফ্যাক্ট হিসেবে সংরক্ষণ করুন এবং এজেন্টে প্রসঙ্গ হিসেবে যোগ করুন।
- একটি টুল রেসপন্স দ্বারা উৎপাদিত ছবি পরিচালনা করার জন্য একটি কলব্যাক ফাংশন ডিজাইন করুন, এটিকে আর্টিফ্যাক্ট হিসেবে সংরক্ষণ করুন এবং এজেন্টে প্রসঙ্গ হিসেবে যোগ করুন।
স্থাপত্যের সংক্ষিপ্ত বিবরণ
এই কোডল্যাবের সামগ্রিক মিথস্ক্রিয়া নিম্নলিখিত চিত্রে দেখানো হয়েছে

পূর্বশর্ত
- পাইথনের সাথে কাজ করা আরামদায়ক
- (ঐচ্ছিক) এজেন্ট ডেভেলপমেন্ট কিট (ADK) সম্পর্কে মৌলিক কোডল্যাব
তুমি কি শিখবে
- আর্টিফ্যাক্ট পরিষেবা অ্যাক্সেস করার জন্য কলব্যাক প্রসঙ্গ কীভাবে ব্যবহার করবেন
- সঠিক মাল্টিমোডাল ডেটা প্রচারের মাধ্যমে কীভাবে টুল ডিজাইন করবেন
- before_model_callback এর মাধ্যমে আর্টিফ্যাক্ট প্রসঙ্গ যোগ করার জন্য এজেন্ট llm অনুরোধটি কীভাবে পরিবর্তন করবেন
- জেমিনি ২.৫ ফ্ল্যাশ ইমেজ ব্যবহার করে কীভাবে ছবি সম্পাদনা করবেন
তোমার যা লাগবে
- ক্রোম ওয়েব ব্রাউজার
- একটি জিমেইল অ্যাকাউন্ট
- বিলিং অ্যাকাউন্ট সক্ষম থাকা একটি ক্লাউড প্রজেক্ট
সকল স্তরের ডেভেলপারদের জন্য (নতুনদের সহ) তৈরি এই কোডল্যাবটি তার নমুনা অ্যাপ্লিকেশনে পাইথন ব্যবহার করে। তবে, উপস্থাপিত ধারণাগুলি বোঝার জন্য পাইথন জ্ঞানের প্রয়োজন নেই।
২. 🚀 ওয়ার্কশপ ডেভেলপমেন্ট সেটআপ প্রস্তুত করা
ধাপ ১: ক্লাউড কনসোলে অ্যাক্টিভ প্রজেক্ট নির্বাচন করুন
গুগল ক্লাউড কনসোলে , প্রজেক্ট সিলেক্টর পৃষ্ঠায়, একটি গুগল ক্লাউড প্রোজেক্ট নির্বাচন করুন বা তৈরি করুন (আপনার কনসোলের উপরের বাম অংশটি দেখুন)

এটিতে ক্লিক করুন, এবং আপনি আপনার সমস্ত প্রকল্পের তালিকা দেখতে পাবেন যেমন এই উদাহরণে,

লাল বাক্সে যে মানটি নির্দেশিত তা হল PROJECT ID এবং এই মানটি পুরো টিউটোরিয়াল জুড়ে ব্যবহার করা হবে।
আপনার ক্লাউড প্রোজেক্টের জন্য বিলিং সক্ষম আছে কিনা তা নিশ্চিত করুন। এটি পরীক্ষা করতে, আপনার উপরের বাম বারে বার্গার আইকন ☰ এ ক্লিক করুন যা নেভিগেশন মেনু দেখায় এবং বিলিং মেনুটি খুঁজে বের করুন।

যদি আপনি বিলিং / ওভারভিউ শিরোনামের অধীনে "গুগল ক্লাউড প্ল্যাটফর্ম ট্রায়াল বিলিং অ্যাকাউন্ট" দেখতে পান ( আপনার ক্লাউড কনসোলের উপরের বাম অংশ ), তাহলে আপনার প্রকল্পটি এই টিউটোরিয়ালের জন্য ব্যবহারের জন্য প্রস্তুত। যদি না হয়, তাহলে এই টিউটোরিয়ালের শুরুতে ফিরে যান এবং ট্রায়াল বিলিং অ্যাকাউন্টটি রিডিম করুন।

ধাপ ২: ক্লাউড শেলের সাথে পরিচিত হোন
আপনি বেশিরভাগ টিউটোরিয়ালের জন্য ক্লাউড শেল ব্যবহার করবেন, গুগল ক্লাউড কনসোলের উপরে অ্যাক্টিভেট ক্লাউড শেল ক্লিক করুন। যদি এটি আপনাকে অনুমোদনের জন্য অনুরোধ করে, তাহলে অনুমোদনে ক্লিক করুন।


ক্লাউড শেলের সাথে সংযুক্ত হয়ে গেলে, আমাদের পরীক্ষা করতে হবে যে শেল (অথবা টার্মিনাল) ইতিমধ্যেই আমাদের অ্যাকাউন্টের সাথে প্রমাণীকরণ করা হয়েছে কিনা।
gcloud auth list
যদি আপনি নীচের উদাহরণের মতো আপনার ব্যক্তিগত জিমেইল আউটপুট দেখতে পান, তাহলে সবকিছু ঠিক আছে।
Credentialed Accounts
ACTIVE: *
ACCOUNT: alvinprayuda@gmail.com
To set the active account, run:
$ gcloud config set account `ACCOUNT`
যদি না হয়, তাহলে আপনার ব্রাউজার রিফ্রেশ করার চেষ্টা করুন এবং অনুরোধ করা হলে Authorize- এ ক্লিক করুন (সংযোগ সমস্যার কারণে এটি ব্যাহত হতে পারে)
এরপর, আমাদের এটাও পরীক্ষা করতে হবে যে শেলটি ইতিমধ্যেই আপনার কাছে থাকা সঠিক PROJECT ID- তে কনফিগার করা আছে কিনা। যদি আপনি দেখেন যে টার্মিনালে $ আইকনের আগে ( ) এর ভিতরে একটি মান আছে (নীচের স্ক্রিনশটে, মানটি "adk-multimodal-tool" ) তাহলে এই মানটি আপনার সক্রিয় শেল সেশনের জন্য কনফিগার করা প্রকল্পটি দেখায়।

যদি দেখানো মানটি ইতিমধ্যেই সঠিক থাকে, তাহলে আপনি পরবর্তী কমান্ডটি এড়িয়ে যেতে পারেন। তবে যদি এটি সঠিক না হয় বা অনুপস্থিত থাকে, তাহলে নিম্নলিখিত কমান্ডটি চালান।
gcloud config set project <YOUR_PROJECT_ID>
তারপর, Github থেকে এই কোডল্যাবের জন্য টেমপ্লেট ওয়ার্কিং ডিরেক্টরিটি ক্লোন করুন, নিম্নলিখিত কমান্ডটি চালান। এটি adk-multimodal-tool ডিরেক্টরিতে ওয়ার্কিং ডিরেক্টরি তৈরি করবে।
git clone https://github.com/alphinside/adk-mcp-multimodal.git adk-multimodal-tool
ধাপ ৩: ক্লাউড শেল এডিটরের সাথে পরিচিত হোন এবং অ্যাপ্লিকেশন ওয়ার্কিং ডিরেক্টরি সেটআপ করুন
এখন, আমরা কিছু কোডিং কাজ করার জন্য আমাদের কোড এডিটর সেট আপ করতে পারি। এর জন্য আমরা ক্লাউড শেল এডিটর ব্যবহার করব।
ওপেন এডিটর বোতামে ক্লিক করুন, এটি একটি ক্লাউড শেল এডিটর খুলবে। ![168eacea651b086c.png - [অনলাইন].](https://codelabs.developers.google.com/static/adk-multimodal-tool-part-1/img/168eacea651b086c.png?hl=bn)
এরপর, ক্লাউড শেল এডিটরের উপরের অংশে যান এবং File->Open Folder এ ক্লিক করুন, আপনার ব্যবহারকারীর নাম ডিরেক্টরিটি খুঁজুন এবং adk-multimodal-tool ডিরেক্টরিটি খুঁজুন তারপর OK বোতামে ক্লিক করুন। এটি নির্বাচিত ডিরেক্টরিটিকে প্রধান কার্যকরী ডিরেক্টরি হিসাবে পরিণত করবে। এই উদাহরণে, ব্যবহারকারীর নাম হল alvinprayuda , তাই ডিরেক্টরি পাথটি নীচে দেখানো হয়েছে।


এখন, আপনার ক্লাউড শেল এডিটর ওয়ার্কিং ডিরেক্টরিটি দেখতে এইরকম হওয়া উচিত ( adk-multimodal-tool এর ভিতরে)

এবার এডিটরের জন্য টার্মিনাল খুলুন। মেনু বারে Terminal -> New Terminal এ ক্লিক করে অথবা Ctrl + Shift + C ব্যবহার করে এটি করতে পারেন। এটি ব্রাউজারের নীচের অংশে একটি টার্মিনাল উইন্ডো খুলবে।

আপনার বর্তমান সক্রিয় টার্মিনালটি adk-multimodal-tool ওয়ার্কিং ডিরেক্টরির ভিতরে থাকা উচিত। আমরা এই কোডল্যাবে Python 3.12 ব্যবহার করব এবং Python সংস্করণ এবং ভার্চুয়াল পরিবেশ তৈরি এবং পরিচালনা করার প্রয়োজনীয়তা সহজ করার জন্য uv python প্রজেক্ট ম্যানেজার ব্যবহার করব। এই uv প্যাকেজটি ইতিমধ্যেই ক্লাউড শেলে প্রি-ইন্সটল করা আছে।
.venv ডিরেক্টরিতে ভার্চুয়াল পরিবেশের জন্য প্রয়োজনীয় নির্ভরতা ইনস্টল করতে এই কমান্ডটি চালান।
uv sync --frozen
এই টিউটোরিয়ালের জন্য ঘোষিত নির্ভরতাগুলি দেখতে pyproject.toml চেক করুন, যা হল google-adk, and python-dotenv ।
এখন, আমাদের নীচে দেখানো কমান্ডের মাধ্যমে প্রয়োজনীয় API গুলি সক্রিয় করতে হবে। এতে কিছুটা সময় লাগতে পারে।
gcloud services enable aiplatform.googleapis.com
কমান্ডটি সফলভাবে কার্যকর করার পরে, আপনি নীচের দেখানো বার্তার মতো একটি বার্তা দেখতে পাবেন:
Operation "operations/..." finished successfully.
৩. 🚀 ADK এজেন্ট চালু করুন
এই ধাপে, আমরা ADK CLI ব্যবহার করে আমাদের এজেন্টটি শুরু করব, নিম্নলিখিত কমান্ডটি চালাব।
uv run adk create product_photo_editor \
--model gemini-2.5-flash \
--project your-project-id \
--region us-central1
এই কমান্ডটি আপনাকে আপনার এজেন্টের জন্য প্রয়োজনীয় কাঠামো দ্রুত প্রদান করতে সাহায্য করবে যা নীচে দেখানো হয়েছে:
product_photo_editor/ ├── __init__.py ├── .env ├── agent.py
এরপর, আমাদের প্রোডাক্ট ফটো এডিটর এজেন্ট প্রস্তুত করা যাক। প্রথমে, রিপোজিটরিতে ইতিমধ্যেই অন্তর্ভুক্ত prompt.py টি আপনার পূর্বে তৈরি করা এজেন্ট ডিরেক্টরিতে কপি করুন।
cp prompt.py product_photo_editor/prompt.py
তারপর, product_photo_editor/agent.py খুলুন এবং নিম্নলিখিত কোড দিয়ে কন্টেন্টটি পরিবর্তন করুন
from google.adk.agents.llm_agent import Agent
from product_photo_editor.prompt import AGENT_INSTRUCTION
root_agent = Agent(
model="gemini-2.5-flash",
name="product_photo_editor",
description="""A friendly product photo editor assistant that helps small business
owners edit and enhance their product photos. Perfect for improving photos of handmade
goods, food products, crafts, and small retail items""",
instruction=AGENT_INSTRUCTION,
)
এখন, আপনার বেস ফটো এডিটর এজেন্ট থাকবে যার সাথে আপনি ইতিমধ্যেই চ্যাট করে আপনার ছবির জন্য পরামর্শ চাইতে পারবেন। আপনি এই কমান্ড ব্যবহার করে এর সাথে ইন্টারঅ্যাক্ট করার চেষ্টা করতে পারেন।
uv run adk web --port 8080
এটি নিম্নলিখিত উদাহরণের মতো আউটপুট তৈরি করবে, যার অর্থ আমরা ইতিমধ্যেই ওয়েব ইন্টারফেস অ্যাক্সেস করতে পারছি।
INFO: Started server process [xxxx] INFO: Waiting for application startup. +-----------------------------------------------------------------------------+ | ADK Web Server started | | | | For local testing, access at http://127.0.0.1:8080. | +-----------------------------------------------------------------------------+ INFO: Application startup complete. INFO: Uvicorn running on http://127.0.0.1:8080 (Press CTRL+C to quit)
এখন, এটি পরীক্ষা করার জন্য আপনি URL-এ Ctrl + ক্লিক করতে পারেন অথবা আপনার Cloud Shell Editor-এর উপরের অংশে Web Preview বোতামে ক্লিক করতে পারেন এবং Preview on port 8080 নির্বাচন করতে পারেন।

আপনি নিম্নলিখিত ওয়েব পৃষ্ঠাটি দেখতে পাবেন যেখানে আপনি উপরের বাম দিকের ড্রপ ডাউন বোতামে (আমাদের ক্ষেত্রে এটি product_photo_editor হওয়া উচিত) উপলব্ধ এজেন্ট নির্বাচন করতে পারেন এবং বটের সাথে ইন্টারঅ্যাক্ট করতে পারেন। চ্যাট ইন্টারফেসে নিম্নলিখিত ছবিটি আপলোড করার চেষ্টা করুন এবং নিম্নলিখিত প্রশ্নগুলি জিজ্ঞাসা করুন।
what is your suggestion for this photo?

আপনি নীচের চিত্রের মতো একই রকম ইন্টারঅ্যাকশন দেখতে পাবেন।

আপনি ইতিমধ্যেই কিছু পরামর্শ চাইতে পারেন, তবে বর্তমানে এটি আপনার জন্য সম্পাদনা করতে পারবে না। চলুন পরবর্তী ধাপে যাই, এজেন্টকে সম্পাদনা সরঞ্জাম দিয়ে সজ্জিত করা।
৪. 🚀 এলএলএম অনুরোধের প্রসঙ্গ পরিবর্তন - ব্যবহারকারীর আপলোড করা ছবি
আমরা চাই আমাদের এজেন্ট কোন আপলোড করা ছবি সম্পাদনা করতে চায় তা নির্বাচন করার ক্ষেত্রে নমনীয় থাকুক। তবে LLM টুলগুলি সাধারণত str বা int এর মতো সহজ ডেটা টাইপ প্যারামিটার গ্রহণ করার জন্য ডিজাইন করা হয়। এটি মাল্টিমোডাল ডেটার জন্য একটি সম্পূর্ণ ভিন্ন ডেটা টাইপ যা সাধারণত বাইট ডেটা টাইপ হিসাবে বিবেচিত হয়, তাই আমাদের সেই ডেটা পরিচালনা করার জন্য Artifacts ধারণার সাথে জড়িত একটি কৌশলের প্রয়োজন হবে। তাই, টুলস প্যারামিটারে সম্পূর্ণ বাইট ডেটা প্রদান করার পরিবর্তে, আমরা আর্টিফ্যাক্ট আইডেন্টিফায়ার নাম গ্রহণ করার জন্য টুলটি ডিজাইন করব।
এই কৌশলটিতে 2টি ধাপ থাকবে:
- LLM অনুরোধটি সংশোধন করুন যাতে প্রতিটি আপলোড করা ফাইল একটি আর্টিফ্যাক্ট শনাক্তকারীর সাথে যুক্ত হয় এবং এটিকে LLM-এ প্রসঙ্গ হিসাবে যুক্ত করুন।
- ইনপুট প্যারামিটার হিসেবে আর্টিফ্যাক্ট আইডেন্টিফায়ার গ্রহণ করার জন্য টুলটি ডিজাইন করুন।
প্রথম ধাপটি করা যাক, LLM অনুরোধটি পরিবর্তন করার জন্য, আমরা ADK কলব্যাক বৈশিষ্ট্যটি ব্যবহার করব। বিশেষ করে এজেন্ট LLM-এ প্রসঙ্গ পাঠানোর ঠিক আগে আমরা before_model_callback যোগ করব। আপনি নীচের ছবিতে চিত্রটি দেখতে পারেন। 
এটি করার জন্য, প্রথমে নিম্নলিখিত কমান্ড ব্যবহার করে একটি নতুন ফাইল product_photo_editor/model_callbacks.py তৈরি করুন।
touch product_photo_editor/model_callbacks.py
তারপর, ফাইলটিতে নিম্নলিখিত কোডটি কপি করুন
# product_photo_editor/model_callbacks.py
from google.adk.agents.callback_context import CallbackContext
from google.adk.models import LlmResponse, LlmRequest
from google.genai.types import Part
import hashlib
from typing import List
async def before_model_modifier(
callback_context: CallbackContext, llm_request: LlmRequest
) -> LlmResponse | None:
"""Modify LLM request to include artifact references for images."""
for content in llm_request.contents:
if not content.parts:
continue
modified_parts = []
for idx, part in enumerate(content.parts):
# Handle user-uploaded inline images
if part.inline_data:
processed_parts = await _process_inline_data_part(
part, callback_context
)
# Default: keep part as-is
else:
processed_parts = [part]
modified_parts.extend(processed_parts)
content.parts = modified_parts
async def _process_inline_data_part(
part: Part, callback_context: CallbackContext
) -> List[Part]:
"""Process inline data parts (user-uploaded images).
Returns:
List of parts including artifact marker and the image.
"""
artifact_id = _generate_artifact_id(part)
# Save artifact if it doesn't exist
if artifact_id not in await callback_context.list_artifacts():
await callback_context.save_artifact(filename=artifact_id, artifact=part)
return [
Part(
text=f"[User Uploaded Artifact] Below is the content of artifact ID : {artifact_id}"
),
part,
]
def _generate_artifact_id(part: Part) -> str:
"""Generate a unique artifact ID for user uploaded image.
Returns:
Hash-based artifact ID with proper file extension.
"""
filename = part.inline_data.display_name or "uploaded_image"
image_data = part.inline_data.data
# Combine filename and image data for hash
hash_input = filename.encode("utf-8") + image_data
content_hash = hashlib.sha256(hash_input).hexdigest()[:16]
# Extract file extension from mime type
mime_type = part.inline_data.mime_type
extension = mime_type.split("/")[-1]
return f"usr_upl_img_{content_hash}.{extension}"
before_model_modifier ফাংশনটি নিম্নলিখিত কাজগুলি করে:
-
llm_request.contentsভেরিয়েবলটি অ্যাক্সেস করুন এবং কন্টেন্টটি পুনরাবৃত্তি করুন - অংশটিতে ইনলাইন_ডেটা (আপলোড করা ফাইল / ছবি) আছে কিনা তা পরীক্ষা করুন, যদি হ্যাঁ হয় তবে ইনলাইন ডেটা প্রক্রিয়া করুন।
- inline_data এর জন্য শনাক্তকারী তৈরি করুন, এই উদাহরণে আমরা একটি কন্টেন্ট হ্যাশ শনাক্তকারী তৈরি করতে ফাইলের নাম + ডেটার সংমিশ্রণ ব্যবহার করছি।
- আর্টিফ্যাক্ট আইডিটি ইতিমধ্যেই আছে কিনা তা পরীক্ষা করুন, যদি না থাকে, তাহলে আর্টিফ্যাক্ট আইডি ব্যবহার করে আর্টিফ্যাক্টটি সংরক্ষণ করুন।
- নিম্নলিখিত ইনলাইন ডেটার আর্টিফ্যাক্ট আইডেন্টিফায়ার সম্পর্কে প্রসঙ্গ প্রদানকারী টেক্সট প্রম্পট অন্তর্ভুক্ত করার জন্য অংশটি পরিবর্তন করুন।
এরপর, এজেন্টকে কলব্যাকের সুবিধা দিতে product_photo_editor/agent.py পরিবর্তন করুন।
from google.adk.agents.llm_agent import Agent
from product_photo_editor.model_callbacks import before_model_modifier
from product_photo_editor.prompt import AGENT_INSTRUCTION
root_agent = Agent(
model="gemini-2.5-flash",
name="product_photo_editor",
description="""A friendly product photo editor assistant that helps small business
owners edit and enhance their product photos for online stores, social media, and
marketing. Perfect for improving photos of handmade goods, food products, crafts, and small retail items""",
instruction=AGENT_INSTRUCTION,
before_model_callback=before_model_modifier,
)
এখন, আমরা আবার এজেন্টের সাথে যোগাযোগ করার চেষ্টা করতে পারি।
uv run adk web --port 8080
এবং আবার ফাইল আপলোড করার চেষ্টা করুন এবং চ্যাট করুন, আমরা পরীক্ষা করতে পারি যে আমরা LLM অনুরোধের প্রসঙ্গ সফলভাবে সংশোধন করেছি কিনা।
![51404c0704f86ffa.png - [অনলাইন].](https://codelabs.developers.google.com/static/adk-multimodal-tool-part-1/img/51404c0704f86ffa.png?hl=bn)

এটি হল একটি উপায় যার মাধ্যমে আমরা LLM-কে মাল্টিমোডাল ডেটার ক্রম এবং সনাক্তকরণ সম্পর্কে বলতে পারি। এখন আসুন এমন একটি টুল তৈরি করি যা এই তথ্য ব্যবহার করবে।
৫. 🚀 মাল্টিমোডাল টুল ইন্টারঅ্যাকশন
এখন, আমরা এমন একটি টুল প্রস্তুত করতে পারি যা তার ইনপুট প্যারামিটার হিসাবে আর্টিফ্যাক্ট আইডি নির্দিষ্ট করে। নতুন ফাইল তৈরি করতে নিম্নলিখিত কমান্ডটি চালান product_photo_editor/custom_tools.py
touch product_photo_editor/custom_tools.py
এরপর, নিচের কোডটি product_photo_editor/custom_tools.py তে কপি করুন।
# product_photo_editor/custom_tools.py
from google import genai
from dotenv import load_dotenv
import os
from google.adk.tools import ToolContext
import logging
load_dotenv()
client = genai.Client(
vertexai=True,
project=os.getenv("GOOGLE_CLOUD_PROJECT"),
location=os.getenv("GOOGLE_CLOUD_LOCATION"),
)
async def edit_product_asset(
tool_context: ToolContext,
change_description: str,
image_artifact_ids: list = [],
) -> dict[str, str]:
"""Modify an existing product photo or combine multiple product photos.
This tool lets you make changes to product photos. You can:
- Edit a single photo (change background, lighting, colors, etc.)
- Combine multiple products into one photo (arrange them side by side, create bundles, etc.)
**IMPORTANT**:
- Make ONE type of change per tool call (background OR lighting OR props OR arrangement)
- For complex edits, chain multiple tool calls together
- BE AS DETAILED AS POSSIBLE in the change_description for best results!
Args:
change_description: What do you want to do? BE VERY DETAILED AND SPECIFIC!
**The more details you provide, the better the result.**
Focus on ONE type of change, but describe it thoroughly.
For BACKGROUND changes:
- "change background to soft pure white with subtle gradient from top to bottom, clean and minimal aesthetic"
- "replace background with rustic dark wood table surface with natural grain texture visible, warm brown tones"
For ADDING PROPS:
- "add fresh pink roses and eucalyptus leaves arranged naturally around the product on the left and right sides,
with some petals scattered in front"
- "add fresh basil leaves and cherry tomatoes scattered around the product naturally"
For LIGHTING changes:
- "add soft natural window light coming from the left side at 45 degree angle, creating gentle shadows on the
right side, warm morning atmosphere"
- "increase brightness with soft diffused studio lighting from above, eliminating harsh shadows"
For ARRANGEMENT/POSITIONING:
- "reposition product to be perfectly centered in frame with equal space on all sides"
- "arrange these three products in a horizontal line, evenly spaced with 2 inches between each"
Note: When combining multiple products, you can include background/lighting in the initial arrangement since it's
one cohesive setup
image_artifact_ids: List of image IDs to edit or combine.
- For single image: provide a list with one item (e.g., ["product.png"])
- For multiple images: provide a list with multiple items (e.g., ["product1.png", "product2.png"])
Use multiple images to combine products into one photo.
Returns:
dict with keys:
- 'tool_response_artifact_id': Artifact ID for the edited image
- 'tool_input_artifact_ids': Comma-separated list of input artifact IDs
- 'edit_prompt': The full edit prompt used
- 'status': Success or error status
- 'message': Additional information or error details
"""
try:
# Validate input
if not image_artifact_ids:
return {
"status": "error",
"tool_response_artifact_id": "",
"tool_input_artifact_ids": "",
"edit_prompt": change_description,
"message": "No images provided. Please provide image_artifact_ids as a list.",
}
# Load all images
image_artifacts = []
for img_id in image_artifact_ids:
artifact = await tool_context.load_artifact(filename=img_id)
if artifact is None:
logging.error(f"Artifact {img_id} not found")
return {
"status": "error",
"tool_response_artifact_id": "",
"tool_input_artifact_ids": "",
"edit_prompt": change_description,
"message": f"Artifact {img_id} not found",
}
image_artifacts.append(artifact)
# Build edit prompt
if len(image_artifacts) > 1:
full_edit_prompt = (
f"{change_description}. "
f"Combine these {len(image_artifacts)} product images together. "
"IMPORTANT: Preserve each product's original appearance, shape, color, and design as faithfully as possible. "
"Only modify for aesthetic enhancements (lighting, background, composition) or viewing angle adjustments. "
"Do not alter the core product features, branding, or characteristics."
)
else:
full_edit_prompt = (
f"{change_description}. "
"IMPORTANT: Preserve the product's original appearance, shape, color, and design as faithfully as possible. "
"Only modify for aesthetic enhancements (lighting, background, composition) or viewing angle adjustments. "
"Do not alter the core product features, branding, or characteristics."
)
# Build contents list: all images followed by the prompt
contents = image_artifacts + [full_edit_prompt]
response = await client.aio.models.generate_content(
model="gemini-2.5-flash-image",
contents=contents,
config=genai.types.GenerateContentConfig(
response_modalities=["Image"]
),
)
artifact_id = ""
logging.info("Gemini Flash Image: response.candidates: ", response.candidates)
for part in response.candidates[0].content.parts:
if part.inline_data is not None:
artifact_id = f"edited_img_{tool_context.function_call_id}.png"
await tool_context.save_artifact(filename=artifact_id, artifact=part)
input_ids_str = ", ".join(image_artifact_ids)
return {
"status": "success",
"tool_response_artifact_id": artifact_id,
"tool_input_artifact_ids": input_ids_str,
"edit_prompt": full_edit_prompt,
"message": f"Image edited successfully using {len(image_artifacts)} input image(s)",
}
except Exception as e:
logging.error(e)
input_ids_str = ", ".join(image_artifact_ids) if image_artifact_ids else ""
return {
"status": "error",
"tool_response_artifact_id": "",
"tool_input_artifact_ids": input_ids_str,
"edit_prompt": change_description,
"message": f"Error editing image: {str(e)}",
}
টুল কোডটি নিম্নলিখিত কাজগুলি করে:
- টুল ডকুমেন্টেশন টুলটি ব্যবহার করার সর্বোত্তম অনুশীলন সম্পর্কে বিস্তারিত বর্ণনা করে
- যাচাই করুন যে image_artifact_ids তালিকাটি খালি নেই
- প্রদত্ত আর্টিফ্যাক্ট আইডি ব্যবহার করে tool_context থেকে সমস্ত ছবির আর্টিফ্যাক্ট লোড করুন।
- বিল্ড এডিট প্রম্পট: (বহু-চিত্র) একত্রিত করার জন্য নির্দেশাবলী যুক্ত করুন অথবা (একক-চিত্র) পেশাদারভাবে সম্পাদনা করুন।
- শুধুমাত্র ইমেজ আউটপুট সহ জেমিনি 2.5 ফ্ল্যাশ ইমেজ মডেলটি কল করুন এবং জেনারেট করা ইমেজটি এক্সট্র্যাক্ট করুন।
- সম্পাদিত ছবিটি নতুন আর্টিফ্যাক্ট হিসেবে সংরক্ষণ করুন।
- স্ট্রাকচার্ড রেসপন্স রিটার্ন করুন: স্ট্যাটাস, আউটপুট আর্টিফ্যাক্ট আইডি, ইনপুট আইডি, ফুল প্রম্পট এবং মেসেজ
অবশেষে, আমরা আমাদের এজেন্টকে এই টুলটি দিয়ে সজ্জিত করতে পারি। product_photo_editor/agent.py এর কন্টেন্টটি নীচের কোডে পরিবর্তন করুন।
from google.adk.agents.llm_agent import Agent
from product_photo_editor.custom_tools import edit_product_asset
from product_photo_editor.model_callbacks import before_model_modifier
from product_photo_editor.prompt import AGENT_INSTRUCTION
root_agent = Agent(
model="gemini-2.5-flash",
name="product_photo_editor",
description="""A friendly product photo editor assistant that helps small business
owners edit and enhance their product photos for online stores, social media, and
marketing. Perfect for improving photos of handmade goods, food products, crafts, and small retail items""",
instruction=AGENT_INSTRUCTION,
tools=[
edit_product_asset,
],
before_model_callback=before_model_modifier,
)
এখন, আমাদের এজেন্ট আমাদের ছবি সম্পাদনা করতে ৮০% প্রস্তুত, আসুন আমরা এর সাথে যোগাযোগ করার চেষ্টা করি।
uv run adk web --port 8080
এবং আসুন নিচের ছবিটি আবার ভিন্ন প্রম্পট দিয়ে চেষ্টা করি:
put these muffins in a white plate aesthetically

তুমি হয়তো এভাবে মিথস্ক্রিয়া দেখতে পাবে এবং অবশেষে এজেন্টকে তোমার জন্য কিছু ছবি সম্পাদনা করতে দেখতে পাবে।

যখন আপনি ফাংশন কলের বিবরণ পরীক্ষা করবেন, তখন এটি ব্যবহারকারীর আপলোড করা ছবির আর্টিফ্যাক্ট শনাক্তকারী প্রদান করবে।

এখন, এজেন্ট আপনাকে ছবির ধাপে ধাপে উন্নতি করতে সাহায্য করতে পারে। এটি পরবর্তী সম্পাদনা নির্দেশিকাতে সম্পাদিত ছবিটিও ব্যবহার করতে পারে কারণ আমরা টুল প্রতিক্রিয়ায় আর্টিফ্যাক্ট শনাক্তকারী প্রদান করি।
তবে বর্তমান অবস্থায়, এজেন্টটি আসলে সম্পাদিত চিত্রের ফলাফল দেখতে এবং বুঝতে পারে না, যেমনটি আপনি উপরের উদাহরণ থেকে দেখতে পাচ্ছেন। এর কারণ হল আমরা এজেন্টকে যে টুল রেসপন্স দিই তা কেবল আর্টিফ্যাক্ট আইডি, বাইট কন্টেন্ট নয়, এবং দুর্ভাগ্যবশত আমরা বাইট কন্টেন্ট সরাসরি টুল রেসপন্সের ভিতরে রাখতে পারি না, এটি একটি ত্রুটি তৈরি করবে। তাই টুল রেসপন্স ফলাফল থেকে ইনলাইন ডেটা হিসাবে বাইট কন্টেন্ট যোগ করার জন্য আমাদের কলব্যাকের ভিতরে আরেকটি লজিক শাখা থাকা প্রয়োজন।
৬. 🚀 এলএলএম অনুরোধের প্রসঙ্গ পরিবর্তন - ফাংশন রেসপন্স ইমেজ
টুলের প্রতিক্রিয়ার পরে সম্পাদিত চিত্র বাইট ডেটা যোগ করার জন্য আমাদের before_model_modifier কলব্যাকটি পরিবর্তন করা যাক যাতে আমাদের এজেন্ট ফলাফলটি সম্পূর্ণরূপে বুঝতে পারে।
product_photo_editor/model_callbacks.py খুলুন এবং কন্টেন্টটি এমনভাবে পরিবর্তন করুন যাতে এটি নীচের মত দেখায়।
# product_photo_editor/model_callbacks.py
from google.adk.agents.callback_context import CallbackContext
from google.adk.models import LlmResponse, LlmRequest
from google.genai.types import Part
import hashlib
from typing import List
async def before_model_modifier(
callback_context: CallbackContext, llm_request: LlmRequest
) -> LlmResponse | None:
"""Modify LLM request to include artifact references for images."""
for content in llm_request.contents:
if not content.parts:
continue
modified_parts = []
for idx, part in enumerate(content.parts):
# Handle user-uploaded inline images
if part.inline_data:
processed_parts = await _process_inline_data_part(
part, callback_context
)
# Handle function response parts for image generation/editing
elif part.function_response:
if part.function_response.name in [
"edit_product_asset",
]:
processed_parts = await _process_function_response_part(
part, callback_context
)
else:
processed_parts = [part]
# Default: keep part as-is
else:
processed_parts = [part]
modified_parts.extend(processed_parts)
content.parts = modified_parts
async def _process_inline_data_part(
part: Part, callback_context: CallbackContext
) -> List[Part]:
"""Process inline data parts (user-uploaded images).
Returns:
List of parts including artifact marker and the image.
"""
artifact_id = _generate_artifact_id(part)
# Save artifact if it doesn't exist
if artifact_id not in await callback_context.list_artifacts():
await callback_context.save_artifact(filename=artifact_id, artifact=part)
return [
Part(
text=f"[User Uploaded Artifact] Below is the content of artifact ID : {artifact_id}"
),
part,
]
def _generate_artifact_id(part: Part) -> str:
"""Generate a unique artifact ID for user uploaded image.
Returns:
Hash-based artifact ID with proper file extension.
"""
filename = part.inline_data.display_name or "uploaded_image"
image_data = part.inline_data.data
# Combine filename and image data for hash
hash_input = filename.encode("utf-8") + image_data
content_hash = hashlib.sha256(hash_input).hexdigest()[:16]
# Extract file extension from mime type
mime_type = part.inline_data.mime_type
extension = mime_type.split("/")[-1]
return f"usr_upl_img_{content_hash}.{extension}"
async def _process_function_response_part(
part: Part, callback_context: CallbackContext
) -> List[Part]:
"""Process function response parts and append artifacts.
Returns:
List of parts including the original function response and artifact.
"""
artifact_id = part.function_response.response.get("tool_response_artifact_id")
if not artifact_id:
return [part]
artifact = await callback_context.load_artifact(filename=artifact_id)
return [
part, # Original function response
Part(
text=f"[Tool Response Artifact] Below is the content of artifact ID : {artifact_id}"
),
artifact,
]
উপরের পরিবর্তিত কোডে আমরা নিম্নলিখিত কার্যকারিতাগুলি যুক্ত করি:
- একটি পার্ট একটি ফাংশন রেসপন্স কিনা এবং কন্টেন্ট পরিবর্তনের অনুমতি দেওয়ার জন্য এটি আমাদের টুলের নামের তালিকায় আছে কিনা তা পরীক্ষা করুন।
- যদি টুল রেসপন্স থেকে আর্টিফ্যাক্ট আইডেন্টিফায়ার বিদ্যমান থাকে, তাহলে আর্টিফ্যাক্ট কন্টেন্ট লোড করুন।
- টুল রেসপন্স থেকে সম্পাদিত ছবির ডেটা অন্তর্ভুক্ত করার জন্য কন্টেন্টটি পরিবর্তন করুন।
এখন, আমরা পরীক্ষা করতে পারি যে এজেন্ট টুল রেসপন্স থেকে সম্পাদিত ছবিটি সম্পূর্ণরূপে বুঝতে পেরেছে কিনা।

দারুন, এখন আমাদের কাছে ইতিমধ্যেই একটি এজেন্ট আছে যা আমাদের নিজস্ব কাস্টম টুলের সাহায্যে মাল্টিমোডাল ইন্টারঅ্যাকশন ফ্লো সমর্থন করে।
এখন, আপনি আরও জটিল প্রবাহের মাধ্যমে এজেন্টের সাথে ইন্টারঅ্যাক্ট করার চেষ্টা করতে পারেন, যেমন ছবি উন্নত করার জন্য একটি নতুন আইটেম (আইস ল্যাটে) যোগ করা।


৭. ⭐ সারাংশ
এবার আসুন এই কোডল্যাবের সময় আমরা ইতিমধ্যে কী করেছি তা আবার দেখি, এখানে মূল শিক্ষাটি দেওয়া হল:
- মাল্টিমোডাল ডেটা হ্যান্ডলিং: টুল আর্গুমেন্ট বা প্রতিক্রিয়ার মাধ্যমে সরাসরি কাঁচা বাইট ডেটা পাস করার পরিবর্তে ADK-এর আর্টিফ্যাক্টস পরিষেবা ব্যবহার করে LLM প্রসঙ্গ প্রবাহের মধ্যে মাল্টিমোডাল ডেটা (যেমন ছবি) পরিচালনা করার কৌশল শিখেছি।
-
before_model_callbackব্যবহার:LlmRequestLLM-এ পাঠানোর আগে আটকাতে এবং সংশোধন করতেbefore_model_callbackব্যবহার করা হয়েছে। আমরা নিম্নলিখিত প্রবাহে ট্যাপ করেছি:
- ব্যবহারকারীর আপলোড: ব্যবহারকারীর আপলোড করা ইনলাইন ডেটা সনাক্ত করার জন্য বাস্তবায়িত লজিক, এটি একটি অনন্যভাবে চিহ্নিত আর্টিফ্যাক্ট (যেমন,
usr_upl_img_...) হিসাবে সংরক্ষণ করুন, এবং আর্টিফ্যাক্ট আইডি উল্লেখ করে প্রম্পট প্রসঙ্গে টেক্সট ইনজেক্ট করুন, LLM কে টুল ব্যবহারের জন্য সঠিক ফাইল নির্বাচন করতে সক্ষম করুন। - টুল রেসপন্স: নির্দিষ্ট টুল ফাংশন রেসপন্স সনাক্ত করার জন্য বাস্তবায়িত লজিক যা আর্টিফ্যাক্ট তৈরি করে (যেমন, সম্পাদিত ছবি), নতুন সংরক্ষিত আর্টিফ্যাক্ট লোড করে (যেমন,
edited_img_...), এবং আর্টিফ্যাক্ট আইডি রেফারেন্স এবং ছবির বিষয়বস্তু উভয়ই সরাসরি প্রসঙ্গ স্ট্রিমে ইনজেক্ট করে।
- কাস্টম টুল ডিজাইন: একটি কাস্টম পাইথন টুল (
edit_product_asset) তৈরি করা হয়েছে যা একটিimage_artifact_idsতালিকা (স্ট্রিং আইডেন্টিফায়ার) গ্রহণ করে এবং Artifacts পরিষেবা থেকে প্রকৃত ছবির ডেটা পুনরুদ্ধার করতেToolContextব্যবহার করে। - ইমেজ জেনারেশন মডেল ইন্টিগ্রেশন: বিস্তারিত টেক্সট বর্ণনার উপর ভিত্তি করে ইমেজ এডিটিং করার জন্য কাস্টম টুলের মধ্যে জেমিনি ২.৫ ফ্ল্যাশ ইমেজ মডেলকে ইন্টিগ্রেটেড করা হয়েছে।
- ক্রমাগত মাল্টিমোডাল ইন্টারঅ্যাকশন: নিশ্চিত করা হয়েছে যে এজেন্ট তার নিজস্ব টুল কলের (সম্পাদিত চিত্র) ফলাফল বুঝতে এবং পরবর্তী নির্দেশাবলীর জন্য সেই আউটপুটকে ইনপুট হিসাবে ব্যবহার করে একটি অবিচ্ছিন্ন সম্পাদনা অধিবেশন বজায় রাখতে পারে।
৮. ➡️ পরবর্তী চ্যালেঞ্জ
ADK মাল্টিমোডাল টুল ইন্টারঅ্যাকশনের প্রথম পর্ব শেষ করার জন্য আপনাকে অভিনন্দন। এই টিউটোরিয়ালে আমরা কাস্টম টুল ইন্টারঅ্যাকশনের উপর আলোকপাত করব। এখন আপনি মাল্টিমোডাল MCP টুলসেটের সাথে কীভাবে ইন্টারঅ্যাক্ট করতে পারেন তার পরবর্তী ধাপে যেতে প্রস্তুত। পরবর্তী ল্যাবে যান।
৯. 🧹 পরিষ্কার করা
এই কোডল্যাবে ব্যবহৃত রিসোর্সের জন্য আপনার Google ক্লাউড অ্যাকাউন্টে চার্জ এড়াতে, এই পদক্ষেপগুলি অনুসরণ করুন:
- গুগল ক্লাউড কনসোলে, রিসোর্স পরিচালনা পৃষ্ঠায় যান।
- প্রকল্পের তালিকায়, আপনি যে প্রকল্পটি মুছতে চান তা নির্বাচন করুন এবং তারপরে মুছুন ক্লিক করুন।
- ডায়ালগে, প্রজেক্ট আইডি টাইপ করুন, এবং তারপর প্রজেক্টটি মুছে ফেলতে Shut down এ ক্লিক করুন।