টুল আপনার অন-ডিভাইস এজেন্টকে অ্যাপ-মধ্যস্থ ডেটা অ্যাক্সেস করার, ব্যাকএন্ড পরিষেবার সাথে যোগাযোগ করার এবং ব্যবহারকারীর হয়ে অ্যাকশন নেওয়ার ক্ষমতা দেয়।
টুল কীভাবে কাজ করে
ব্যবহারকারী কোনও প্রম্পট জমা দিলে, AgentRunner ক্লাস চারটি ধাপ জুড়ে একটি
এজেন্টিক লুপ এক্সিকিউট করে:
- পর্যবেক্ষণ করা: Gemini Nano রেজিস্টার করা টুল ডিক্লারেশনের (এজেন্টের অ্যাকশন স্পেস) সাপেক্ষে ব্যবহারকারীর প্রম্পট এবং বর্তমান কথোপকথনের প্রসঙ্গ বিশ্লেষণ করে।
- কারণ: মডেলটি নির্ধারণ করে যে কোনও অ্যাপ-মধ্যস্থ টুলের প্রয়োজন আছে কিনা। কোনও টুল প্রয়োজন হলে, Gemini Nano ফাংশনের নাম ও টাইপ করা আর্গুমেন্ট সহ একটি স্ট্রাকচার্ড টুল ইনভোকেশন তৈরি করে।
- অ্যাক্ট:
AgentRunnerইনস্ট্যান্স অটোমেটিক এই ইনভোকেশন ইন্টারসেপ্ট করে এবং আপনার অ্যাপে সংশ্লিষ্ট Kotlin ফাংশন এক্সিকিউট করে। - মতামত মূল্যায়ন করা: টুলের আউটপুট Gemini Nano-তে ফিডব্যাক হিসেবে দেওয়া হয়। মডেলটি
ফিডব্যাক মূল্যায়ন করে এবং ব্যবহারকারীকে উত্তর দেয় বা অন্য একটি টুলকে কল করে
যতক্ষণ না একটি সমাপ্তির শর্ত পূরণ হয় (লক্ষ্য পূরণ করা বা
maxTurnsসীমায় পৌঁছানো)।
Android AppFunctions সহ ডিক্লারেটিভ টুল
টুল সংজ্ঞায়িত করার জন্য সাজেস্ট করা পদ্ধতি হল Android
AppFunctions (androidx.appfunctions) ব্যবহার করে ডিক্লারেটিভভাবে করা। AppFunctions অটোমেটিক স্কিমা
তৈরির মাধ্যমে টাইপ করা ইন-অ্যাপ ক্ষমতা ঘোষণা করার জন্য একটি স্ট্যান্ডার্ডাইজড
প্ল্যাটফর্ম মডেল প্রদান করে।
১. Gradle ডিপেন্ডেন্সি যোগ করুন
আপনার মডিউলের build.gradle.kts ফাইলে genai-agent-এর পাশাপাশি AppFunctions আর্টিফ্যাক্ট যোগ করুন:
dependencies {
implementation("com.google.mlkit:genai-agent:1.0.0-alpha1")
implementation("androidx.appfunctions:appfunctions:1.0.0-alpha13")
ksp("androidx.appfunctions:appfunctions-compiler:1.0.0-alpha13")
}
২. @AppFunctionDeclaration অ্যানোটেশন সহ টুল মেথড অ্যানোটেট করুন
@AppFunctionDeclaration(isDescribedByKDoc = true) অ্যানোটেশন ব্যবহার করে পাবলিক টুল মেথড অ্যানোটেট করুন। প্যারামিটার
বিবরণ স্ট্যান্ডার্ড KDoc @param ট্যাগে ডকুমেন্ট করা আছে।
AppFunctions-এর জন্য রিটার্ন ভ্যালু হয় প্রিমিটিভ টাইপ (যেমন
String, Int, অথবা Boolean) অথবা @AppFunctionSerializable অ্যানোটেশন সহ অ্যানোটেট করা স্ট্রাকচার্ড ডেটা ক্লাস হতে হবে।
package com.example.ai.agent.tools
import androidx.appfunctions.AppFunctionDeclaration
import androidx.appfunctions.AppFunctionSerializable
import androidx.appfunctions.AppFunctionService
import androidx.appfunctions.AppFunctionServiceEntryPoint
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
/**
* Structured details for an order.
*/
@AppFunctionSerializable
data class OrderDetails(
val orderId: String,
val status: String,
val estimatedDelivery: String?,
val itemCount: Int,
)
/**
* Result returned when cancelling an order.
*/
@AppFunctionSerializable
data class CancelOrderResult(
val orderId: String,
val cancelled: Boolean,
val message: String,
)
@AppFunctionServiceEntryPoint(
serviceName = "OrderAppFunctionService",
appFunctionXmlFileName = "order_app_functions",
)
abstract class OrderTools : AppFunctionService() {
private val orderRepository = OrderRepository()
/**
* Fetches details and delivery status for a customer order by ID.
*
* @param orderId The alphanumeric order identifier (for example, ORD-8492)
*/
@AppFunctionDeclaration(isDescribedByKDoc = true)
suspend fun fetch_order_details(
orderId: String
): OrderDetails = withContext(Dispatchers.IO) {
val order = orderRepository.findOrder(orderId)
if (order != null) {
OrderDetails(
orderId = order.id,
status = order.status,
estimatedDelivery = order.deliveryEstimate,
itemCount = order.items.size,
)
} else {
OrderDetails(
orderId = orderId,
status = "NOT_FOUND",
estimatedDelivery = null,
itemCount = 0,
)
}
}
/**
* Requests a cancellation or return for an active order.
*
* @param orderId The order identifier to cancel
* @param reason The reason provided by the user for cancellation
*/
@AppFunctionDeclaration(isDescribedByKDoc = true)
suspend fun cancel_order(
orderId: String,
reason: String? = null
): CancelOrderResult = withContext(Dispatchers.IO) {
val success = orderRepository.cancelOrder(orderId, reason)
CancelOrderResult(
orderId = orderId,
cancelled = success,
message = if (success) {
"Order cancellation processed."
} else {
"Order cannot be cancelled at this stage."
}
)
}
}
আপনার অ্যাপের AndroidManifest.xml ফাইলে জেনারেট করা পরিষেবা রেজিস্টার করুন:
<service
android:name=".OrderAppFunctionService"
android:permission="android.permission.BIND_APP_FUNCTION_SERVICE"
android:exported="true">
<property
android:name="android.app.appfunctions.schema"
android:value="app_functions_schema.xsd" />
<property
android:name="android.app.appfunctions.v2"
android:value="order_app_functions.xml" />
<intent-filter>
<action android:name="android.app.appfunctions.AppFunctionService" />
</intent-filter>
</service>
৩. ACCESS_LEVEL_SELF কনস্ট্যান্টের মাধ্যমে কলার অ্যাক্সেস সীমিত করুন
AppFunctions-কে সিস্টেম বা এক্সটার্নাল অ্যাসিস্ট্যান্টের কাছে পাবলিক হতে হয় না। Android-এ, অ্যাক্সেস কন্ট্রোল, ম্যানুয়ালি কলারের পরিচয় চেক করার পরিবর্তে কলার অ্যাক্সেস লেভেলের মাধ্যমে ম্যানেজ করা হয়।
ডিফল্ট হিসেবে (ACCESS_LEVEL_ANDROID_TRUSTED কনস্ট্যান্ট ব্যবহার করে), EXECUTE_APP_FUNCTIONS সিস্টেম অনুমতি আছে এমন প্ল্যাটফর্ম-সার্টিফায়েড অন-ডিভাইস অ্যাসিস্ট্যান্টের মাধ্যমে
AppFunction
খুঁজে পাওয়া ও এক্সিকিউট করা যেতে পারে। তবে, আপনি যখন
আপনার অ্যাপের ইন্টার্নাল এজেন্টের জন্য কঠোরভাবে উদ্দিষ্ট ক্ষমতা তৈরি করছেন (যেমন
ব্যবহারকারীর শপিং কার্ট পরিদর্শন করা, স্থানীয় টোকেন অ্যাক্সেস করা, বা অ্যাপ-মধ্যস্থ
পারফর্ম করা), আপনি ACCESS_LEVEL_SELF ধ্রুবক ব্যবহার করে একচেটিয়াভাবে আপনার নিজের অ্যাপ্লিকেশনে অ্যাক্সেস সীমাবদ্ধ করতে পারেন:
import androidx.appfunctions.AppFunctionAccessLevel
import androidx.appfunctions.AppFunctionDeclaration
import androidx.appfunctions.AppFunctionService
import androidx.appfunctions.AppFunctionServiceEntryPoint
import androidx.appfunctions.ExperimentalAppFunctionsApi
import androidx.appfunctions.metadata.AppFunctionMetadata
@AppFunctionServiceEntryPoint(
serviceName = "OrderAppFunctionService",
appFunctionXmlFileName = "order_app_functions",
)
abstract class OrderTools : AppFunctionService() {
private val orderRepository = OrderRepository()
/**
* Cancels an order. Restricted exclusively to this app's internal agent.
*/
@OptIn(ExperimentalAppFunctionsApi::class)
@AppFunctionDeclaration(isDescribedByKDoc = true)
@AppFunctionAccessLevel(
level = AppFunctionMetadata.ACCESS_LEVEL_SELF,
isCompatEnforcementEnabled = true,
)
suspend fun cancel_order(orderId: String): CancelOrderResult {
// Only your app's internal agent can invoke this function
val success = orderRepository.cancelOrder(orderId)
return CancelOrderResult(
orderId = orderId,
cancelled = success,
message = if (success) "Cancelled." else "Failed."
)
}
}
প্ল্যাটফর্মে তিন ধরনের কলার অ্যাক্সেস লেভেল কাজ করে:
ACCESS_LEVEL_SELF(100): হোস্ট অ্যাপ্লিকেশনের ক্ষেত্রে কঠোরভাবে সীমাবদ্ধ। এক্সটার্নাল অ্যাসিস্ট্যান্ট, সিস্টেম সারফেস এবং অন্যান্য অ্যাপ IPC-এর মাধ্যমে ফাংশন খুঁজে পেতে বা চালু করতে পারে না। সংবেদনশীল অপারেশন বা টুলের জন্য এই লেভেল ব্যবহার করুন যা শুধুমাত্র আপনার অ্যাপের ইন্টার্নাল এজেন্টকে পরিষেবা প্রদান করবে।ACCESS_LEVEL_SYSTEM(200):android.Manifest.permission#EXECUTE_APP_FUNCTIONS_SYSTEMঅনুমতি (যেমন, সিস্টেম লঞ্চার বা সাম্প্রতিক) এবং হোস্ট অ্যাপের কাছে থাকা বিশেষাধিকার প্রাপ্ত সিস্টেম কলারের জন্য সীমাবদ্ধ।ACCESS_LEVEL_ANDROID_TRUSTED(300, ডিফল্ট):EXECUTE_APP_FUNCTIONSঅনুমতি, সিস্টেম সারফেস ও হোস্ট অ্যাপ সহ প্ল্যাটফর্ম-সার্টিফায়েড অন-ডিভাইস এজেন্টের জন্য খোলা।
৪. এজেন্টের সাথে টুল রেজিস্টার করুন
আপনি এজেন্টের সাথে AppFunctions রেজিস্টার করতে পারবেন, হয় অটোমেটিক্যালি
enableAppFunctions() পদ্ধতি ব্যবহার করে, অথবা প্রোগ্রাম্যাটিক্যালি
AppFunctionAgentTools.discover() পদ্ধতি ব্যবহার করে:
enableAppFunctions() পদ্ধতির মাধ্যমে অটোমেটিক ডিসকভারি
সহজ অ্যাপের ক্ষেত্রে, যেখানে মডেল কল করলে AppFunctions অটোমেটিক এক্সিকিউট হয়:
import com.google.mlkit.genai.agent.Agent
import com.google.mlkit.genai.agent.AgentRunner
val agent = Agent.builder()
.setName("OrderAssistant")
.setInstructions("Assist users with order tracking.")
.enableAppFunctions(true) // Automatically discovers all AppFunctions in your app package
.setMaxTurns(10)
.build()
val runner = AgentRunner(agent)
বিকল্প হিসেবে, সব ইন-অ্যাপ ফাংশন দেখানোর পরিবর্তে নির্দিষ্ট AppFunction আইডিতে ডিসকভারি স্কোপ করতে addAppFunctionIds("fetch_order_details", "cancel_order")
মেথড কল করুন।
AppFunctionAgentTools.discover() পদ্ধতির মাধ্যমে প্রোগ্রাম্যাটিক ডিসকভারি
আপনার অ্যাপকে যখন খুঁজে পাওয়া AppFunctions পরীক্ষা করতে, ফিল্টার করতে বা এজেন্টের সাথে রেজিস্টার করার আগে সাজাতে হয়:
import com.google.mlkit.genai.agent.Agent
import com.google.mlkit.genai.agent.AgentRunner
import com.google.mlkit.genai.agent.AppFunctionAgentTools
val discoveredTools = AppFunctionAgentTools.discover(
enabledAppFunctionIds = listOf("fetch_order_details", "cancel_order") // Optional ID filter
)
val agent = Agent.builder()
.setName("OrderAssistant")
.setInstructions("Assist users with order tracking.")
.addTools(discoveredTools)
.build()
val runner = AgentRunner(agent)
ডেকোরেটর প্যাটার্ন এবং হিউম্যান ইন দ্য লুপ
অর্ডার বাতিল করা, পেমেন্ট করা বা ব্যবহারকারীর ডেটা মুছে ফেলার মতো সংবেদনশীল অপারেশন, ব্যবহারকারীর কনফার্মেশন চাওয়ার মাধ্যমে হিউম্যান ইন দ্য লুপ (HITL) বজায় রাখতে হবে। Agent ক্লাসে যোগ করা সব টুল Android AppFunctions থেকে আসে,
এবং AgentTool ক্লাস একটি ডেলিগেটিং কনস্ট্রাক্টর AgentTool(delegate) প্রদান করে যাতে আপনি
ডেকোরেটর প্যাটার্ন ব্যবহার করে খুঁজে পাওয়া AppFunction টুল র্যাপ করতে পারেন:
import com.google.mlkit.genai.agent.AgentTool
class HitlConfirmationTool(
delegate: AgentTool,
private val onConfirm: suspend (toolName: String, args: Map<String, Any>) -> Boolean
) : AgentTool(delegate) {
override suspend fun execute(args: Map<String, Any>): Map<String, Any?> {
val confirmed = onConfirm(name, args)
if (!confirmed) {
return mapOf(
"status" to "CANCELLED_BY_USER",
"message" to "Action was declined by the user."
)
}
return checkNotNull(delegate).execute(args)
}
}
টুল ডিজাইনের জন্য পেশাদার পদ্ধতি
- অ্যাকশন স্পেসের পরিধি নির্ধারণ করা: এজেন্টের অ্যাকশন স্পেস (এজেন্টের কাছে উপলভ্য টুল ও অনুমতির সেট)
addAppFunctionIds()পদ্ধতি ওACCESS_LEVEL_SELFকনস্ট্যান্ট ব্যবহার করে টাস্কের জন্য যতটা প্রয়োজন ঠিক ততটাই রাখুন। অত্যধিক বড় অ্যাকশন স্পেস অন-ডিভাইস মডেলকে আরও বেশি ত্রুটি-প্রবণ করে তোলে এবং কন্টেক্সট উইন্ডো টোকেন ব্যবহার করে। - বিবরণ স্পষ্ট হতে হবে: কোনও টুল কখন কল করতে হবে তা নির্ধারণ করতে Gemini Nano টুল ও প্যারামিটার বিবরণের উপর নির্ভর করে। টুলের উদ্দেশ্য সম্পর্কে স্পষ্ট হোন এবং প্রত্যাশিত আর্গুমেন্ট ফর্ম্যাট (যেমন তারিখ বা আইডি) উল্লেখ করুন।
- স্ট্রাকচার্ড এরর ম্যাপ রিটার্ন করুন: প্রত্যাশিত ব্যর্থতা ঘটলে
mapOf("error" to "Order not found")-এর মতো বর্ণনামূলক এরর ম্যাপ রিটার্ন করুন। এটি Gemini Nano-কে ব্যবহারকারীর কাছে সমস্যাটি সুন্দরভাবে ব্যাখ্যা করতে দেয়। কোনও টুল যদি অপ্রত্যাশিত ব্যতিক্রম দেখায়, তাহলে রানটাইম অটোমেটিক সেটি ধরে ফেলে এবং নিজেকে সংশোধন করার জন্য মডেলকে ত্রুটি মেসেজ দেখায়। - রিটার্ন পেলোড ফোকাসড রাখুন: অন-ডিভাইস মডেলে সার্ভার মডেলের তুলনায় ছোট কনটেক্সট উইন্ডো থাকে। ব্যবহারকারীর কোয়েরির উত্তর দেওয়ার জন্য শুধুমাত্র প্রাসঙ্গিক ফিল্ডগুলি দেখান, সম্পূর্ণ ডেটাবেস সারি বা নেটওয়ার্কের র-রেসপন্স দেখাবেন না।
Dispatchers.IOডিসপ্যাচারে I/O রান করুন: সবসময় নিশ্চিত করুন যে নেটওয়ার্ক অনুরোধ এবং টুলের মধ্যে রুম ডেটাবেস কোয়েরিDispatchers.IOডিসপ্যাচারে রান করে যাতে কোরুটিন কলার ব্লক না হয়।
পরবর্তী ধাপ
- সেশন ও লাইফসাইকেল ম্যানেজ করুন বিকল্পে গিয়ে অ্যাপ রোটেশন ও ক্লিন-আপ সেশন জুড়ে কীভাবে কথোপকথনের প্রসঙ্গ বজায় রাখতে হয় তা জানুন।
- স্ট্রিম ইভেন্ট ও UI ইন্টিগ্রেশন-এ স্ট্রিমিং টেক্সট টোকেন ও টুল এক্সিকিউশন স্টেট ম্যানেজ করুন।