واجهة برمجة تطبيقات Kotlin الخاصة بمكتبة LiteRT-LM لنظام التشغيل Android وJVM (Linux وMacOS وWindows) مع ميزات مثل تسريع وحدة معالجة الرسومات ووحدة المعالجة العصبية والوسائط المتعددة واستخدام الأدوات
مقدمة
في ما يلي نموذج لتطبيق محادثة على الجهاز الطرفي تم إنشاؤه باستخدام واجهة برمجة تطبيقات Kotlin:
import com.google.ai.edge.litertlm.*
suspend fun main() {
Engine.setNativeMinLogSeverity(LogSeverity.ERROR) // Hide log for TUI app
val engineConfig = EngineConfig(modelPath = "/path/to/model.litertlm")
Engine(engineConfig).use { engine ->
engine.initialize()
engine.createConversation().use { conversation ->
while (true) {
print("\n>>> ")
conversation.sendMessageAsync(readln()).collect { print(it) }
}
}
}
}

لتجربة المثال أعلاه، استنسِخ المستودع وشغِّله باستخدام example/Main.kt:
bazel run -c opt //kotlin/java/com/google/ai/edge/litertlm/example:main -- <abs_model_path>
تتوفّر نماذج .litertlm على
منتدى HuggingFace LiteRT. استخدمت الصورة المتحركة أعلاه نموذج Gemma3-1B-IT.
للحصول على نموذج Android، يمكنك الاطّلاع على تطبيق معرض Google AI Edge.
بدء استخدام Gradle
على الرغم من أنّ LiteRT-LM تم تطويره باستخدام Bazel، إلا أنّنا نوفّر حِزم Maven لمستخدمي Gradle/Maven.
1. إضافة اعتمادية Gradle
dependencies {
// For Android
implementation("com.google.ai.edge.litertlm:litertlm-android:latest.release")
// For JVM (Linux, MacOS, Windows)
implementation("com.google.ai.edge.litertlm:litertlm-jvm:latest.release")
}
يمكنك العثور على الإصدارات المتاحة على Google Maven في litertlm-android و litertlm-jvm.
يمكن استخدام latest.release للحصول على أحدث إصدار.
2. إعداد المحرّك
Engine هي نقطة الدخول إلى واجهة برمجة التطبيقات. يجب تهيئته باستخدام مسار النموذج وإعداداته. تذكَّر إغلاق المحرّك لتحرير الموارد.
ملاحظة: قد تستغرق طريقة engine.initialize() وقتًا طويلاً
(مثلاً، ما يصل إلى 10 ثوانٍ) لتحميل النموذج. يُنصح بشدة باستدعاء هذه الدالة في سلسلة محادثات أو روتين فرعي في الخلفية لتجنُّب حظر سلسلة محادثات واجهة المستخدِم.
import com.google.ai.edge.litertlm.Backend
import com.google.ai.edge.litertlm.Engine
import com.google.ai.edge.litertlm.EngineConfig
val engineConfig = EngineConfig(
modelPath = "/path/to/your/model.litertlm", // Replace with your model path
backend = Backend.GPU(), // Or Backend.NPU(nativeLibraryDir = "...")
// Optional: Pick a writable dir. This can improve 2nd load time.
// cacheDir = "/tmp/" or context.cacheDir.path (for Android)
)
val engine = Engine(engineConfig)
engine.initialize()
// ... Use the engine to create a conversation ...
// Close the engine when done
engine.close()
على نظام التشغيل Android، لاستخدام الخلفية المستندة إلى وحدة معالجة الرسومات، يجب أن يطلب التطبيق المكتبات الأصلية ذات الصلة بشكل صريح عن طريق إضافة ما يلي إلى AndroidManifest.xml داخل العلامة <application>:
<application>
<uses-native-library android:name="libvndksupport.so" android:required="false"/>
<uses-native-library android:name="libOpenCL.so" android:required="false"/>
</application>
لاستخدام الخلفية البرمجية NPU، قد تحتاج إلى تحديد الدليل الذي يحتوي على مكتبات NPU. على نظام التشغيل Android، إذا كانت المكتبات مجمّعة مع تطبيقك، اضبطها على context.applicationInfo.nativeLibraryDir. لمزيد من التفاصيل حول المكتبات الأصلية لوحدة المعالجة العصبية، يُرجى الاطّلاع على LiteRT-LM
NPU.
val engineConfig = EngineConfig(
modelPath = modelPath,
backend = Backend.NPU(nativeLibraryDir = context.applicationInfo.nativeLibraryDir)
)
3- إنشاء محادثة
بعد إعداد المحرّك، أنشئ مثيلاً من Conversation. يمكنك تقديم ConversationConfig لتخصيص سلوكه.
import com.google.ai.edge.litertlm.ConversationConfig
import com.google.ai.edge.litertlm.Message
import com.google.ai.edge.litertlm.SamplerConfig
// Optional: Configure the system instruction, initial messages, sampling
// parameters, etc.
val conversationConfig = ConversationConfig(
systemInstruction = Contents.of("You are a helpful assistant."),
initialMessages = listOf(
Message.user("What is the capital city of the United States?"),
Message.model("Washington, D.C."),
),
samplerConfig = SamplerConfig(topK = 10, topP = 0.95, temperature = 0.8),
)
val conversation = engine.createConversation(conversationConfig)
// Or with default config:
// val conversation = engine.createConversation()
// ... Use the conversation ...
// Close the conversation when done
conversation.close()
تنفّذ Conversation AutoCloseable، لذا يمكنك استخدام الحظر use لإدارة الموارد تلقائيًا للمحادثات التي تتم لمرة واحدة أو لفترة قصيرة:
engine.createConversation(conversationConfig).use { conversation ->
// Interact with the conversation
}
4. إرسال الرسائل
تتوفّر ثلاث طرق لإرسال الرسائل:
sendMessage(contents): Message: طلب متزامن يتم حظره إلى أن يعرض النموذج ردًا كاملاً. ويكون ذلك أبسط للتفاعلات الأساسية بين الطلب والاستجابة.-
sendMessageAsync(contents, callback): طلب غير متزامن لبث الردود. هذه الطريقة أفضل للطلبات التي تستغرق وقتًا طويلاً أو عندما تريد عرض الرد أثناء إنشائه. -
sendMessageAsync(contents): Flow<Message>: دالة غير متزامنة تعرض مسار Kotlin لبث الردود. هذا هو الأسلوب المقترَح لمستخدمي Coroutine.
مثال على التزامن:
import com.google.ai.edge.litertlm.Content
import com.google.ai.edge.litertlm.Message
print(conversation.sendMessage("What is the capital of France?"))
مثال غير متزامن مع ردّ اتصال:
استخدِم sendMessageAsync لإرسال رسالة إلى النموذج وتلقّي الردود
من خلال دالة ردّ الاتصال.
import com.google.ai.edge.litertlm.Content
import com.google.ai.edge.litertlm.Message
import com.google.ai.edge.litertlm.MessageCallback
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
val callback = object : MessageCallback {
override fun onMessage(message: Message) {
print(message)
}
override fun onDone() {
// Streaming completed
}
override fun onError(throwable: Throwable) {
// Error during streaming
}
}
conversation.sendMessageAsync("What is the capital of France?", callback)
مثال غير متزامن باستخدام Flow:
استخدِم sendMessageAsync (بدون وسيطة معاودة الاتصال) لإرسال رسالة إلى النموذج
وتلقّي الردود من خلال Kotlin Flow.
import com.google.ai.edge.litertlm.Content
import com.google.ai.edge.litertlm.Message
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.launch
// Within a coroutine scope
conversation.sendMessageAsync("What is the capital of France?")
.catch { ... } // Error during streaming
.collect { print(it.toString()) }
5- تعدُّد الوسائط
يمكن أن تحتوي عناصر Message على أنواع مختلفة من Content، بما في ذلك Text وImageBytes وImageFile وAudioBytes وAudioFile.
// Initialize the `visionBackend` and/or the `audioBackend`
val engineConfig = EngineConfig(
modelPath = "/path/to/your/model.litertlm", // Replace with your model path
backend = Backend.CPU(), // Or Backend.GPU() or Backend.NPU(...)
visionBackend = Backend.GPU(), // Or Backend.NPU(...)
audioBackend = Backend.CPU(), // Or Backend.NPU(...)
)
// Sends a message with multi-modality.
// See the Content class for other variants.
conversation.sendMessage(Contents.of(
Content.ImageFile("/path/to/image"),
Content.AudioBytes(audioBytes), // ByteArray of the audio
Content.Text("Describe this image and audio."),
))
6. تحديد الأدوات واستخدامها
هناك طريقتان لتحديد الأدوات:
- باستخدام دوال Kotlin (ننصح بها في معظم الحالات)
- باستخدام مواصفات Open API (التحكّم الكامل في مواصفات الأداة وتنفيذها)
تحديد الأدوات باستخدام دوال Kotlin
يمكنك تحديد دوال Kotlin مخصّصة كأدوات يمكن للنموذج استدعاؤها لتنفيذ إجراءات أو جلب معلومات.
أنشئ فئة تنفّذ ToolSet وأضِف التعليق التوضيحي @Tool إلى الطرق و@ToolParam إلى المَعلمات.
import com.google.ai.edge.litertlm.Tool
import com.google.ai.edge.litertlm.ToolParam
class SampleToolSet: ToolSet {
@Tool(description = "Get the current weather for a city")
fun getCurrentWeather(
@ToolParam(description = "The city name, e.g., San Francisco") city: String,
@ToolParam(description = "Optional country code, e.g., US") country: String? = null,
@ToolParam(description = "Temperature unit (celsius or fahrenheit). Default: celsius") unit: String = "celsius"
): Map<String, Any> {
// In a real application, you would call a weather API here
return mapOf("temperature" to 25, "unit" to unit, "condition" to "Sunny")
}
@Tool(description = "Get the sum of a list of numbers.")
fun sum(
@ToolParam(description = "The numbers, could be floating point.") numbers: List<Double>,
): Double {
return numbers.sum()
}
}
في الخلفية، تفحص واجهة برمجة التطبيقات هذه التعليقات التوضيحية وتوقيع الدالة
لإنشاء مخطط على نمط OpenAPI. يصف هذا المخطط وظائف الأداة ومعلَماتها (بما في ذلك أنواعها وأوصافها من @ToolParam) ونوع الإرجاع للنموذج اللغوي.
أنواع المَعلمات
يمكن أن تكون أنواع المَعلمات التي تمّت إضافة التعليقات التوضيحية إليها باستخدام @ToolParam هي String أو Int أو Boolean أو Float أو Double أو List من هذه الأنواع (مثلاً، List<String>).
استخدِم أنواعًا تقبل القيم الخالية (مثل String?) للإشارة إلى المَعلمات التي تقبل القيم الخالية. اضبط قيمة تلقائية للإشارة إلى أنّ المَعلمة اختيارية، واذكر القيمة التلقائية في الوصف في @ToolParam.
نوع القيمة التي يتم عرضها
يمكن أن يكون نوع الإرجاع لدالة الأدوات أي نوع من أنواع Kotlin. سيتم تحويل النتيجة إلى عنصر JSON قبل إرسالها مرة أخرى إلى النموذج.
- يتم تحويل أنواع
Listإلى مصفوفات JSON. - يتم تحويل أنواع
Mapإلى عناصر JSON. - يتم تحويل الأنواع الأساسية (
StringوNumberوBoolean) إلى نوع JSON أساسي مطابق. - يتم تحويل الأنواع الأخرى إلى سلاسل باستخدام الطريقة
toString().
بالنسبة إلى البيانات المنظَّمة، ننصح بعرض Map أو فئة بيانات سيتم تحويلها إلى عنصر JSON.
تحديد الأدوات باستخدام مواصفات OpenAPI
بدلاً من ذلك، يمكنك تحديد أداة من خلال تنفيذ فئة OpenApiTool وتقديم وصف الأداة كسلسلة JSON متوافقة مع مواصفات Open API. تكون هذه الطريقة مفيدة إذا كان لديك مخطط OpenAPI لأداتك أو إذا كنت بحاجة إلى تحكّم دقيق في تعريف الأداة.
import com.google.ai.edge.litertlm.OpenApiTool
class SampleOpenApiTool : OpenApiTool {
override fun getToolDescriptionJsonString(): String {
return """
{
"name": "addition",
"description": "Add all numbers.",
"parameters": {
"type": "object",
"properties": {
"numbers": {
"type": "array",
"items": {
"type": "number"
}
},
"description": "The list of numbers to sum."
},
"required": [
"numbers"
]
}
}
""".trimIndent() // Tip: trim to save tokens
}
override fun execute(paramsJsonString: String): String {
// Parse paramsJsonString with your choice of parser/deserializer and
// execute the tool.
// Return the result as a JSON string
return """{"result": 1.4142}"""
}
}
تسجيل الأدوات
أدرِج أمثلة على أدواتك في ConversationConfig.
val conversation = engine.createConversation(
ConversationConfig(
tools = listOf(
tool(SampleToolSet()),
tool(SampleOpenApiTool()),
),
// ... other configs
)
)
// Send messages that might trigger the tool
conversation.sendMessageAsync("What's the weather like in London?", callback)
سيقرّر النموذج متى يجب استدعاء الأداة استنادًا إلى المحادثة. يتم تلقائيًا إعادة إرسال نتائج تنفيذ الأداة إلى النموذج لإنشاء الرد النهائي.
استخدام الأدوات يدويًا
بشكلٍ تلقائي، تنفّذ LiteRT-LM تلقائيًا طلبات استخدام الأدوات التي ينشئها النموذج، ويتم إرسال نتائج تنفيذ الأداة تلقائيًا إلى النموذج لإنشاء الرد التالي.
إذا أردت تنفيذ الأدوات يدويًا وإرسال النتائج إلى النموذج، يمكنك ضبط automaticToolCalling في ConversationConfig على false.
val conversation = engine.createConversation(
ConversationConfig(
tools = listOf(
tool(SampleOpenApiTool()),
),
automaticToolCalling = false,
)
)
في حال إيقاف ميزة "استدعاء الأدوات تلقائيًا"، عليك تنفيذ الأدوات يدويًا وإرسال النتائج إلى النموذج في رمز تطبيقك. execute
لن يتم استدعاء طريقة OpenApiTool تلقائيًا عندما يتم ضبط
automaticToolCalling على false.
// Send a message that triggers a tool call.
val responseMessage = conversation.sendMessage("What's the weather like in London?")
// The model returns a Message with `toolCalls` populated.
if (responseMessage.toolCalls.isNotEmpty()) {
val toolResponses = mutableListOf<Content.ToolResponse>()
// There can be multiple tool calls in a single response.
for (toolCall in responseMessage.toolCalls) {
println("Model wants to call: ${toolCall.name} with arguments: ${toolCall.arguments}")
// Execute the tool manually with your own logic. `executeTool` is just an example here.
val toolResponseJson = executeTool(toolCall.name, toolCall.arguments)
// Collect tool responses.
toolResponses.add(Content.ToolResponse(toolCall.name, toolResponseJson))
}
// Use Message.tool to create the tool response message.
val toolResponseMessage = Message.tool(Contents.of(toolResponses))
// Send the tool response message to the model.
val finalMessage = conversation.sendMessage(toolResponseMessage)
println("Final answer: ${finalMessage.text}") // e.g., "The weather in London is 25c."
}
مثال
لتجربة استخدام الأداة، استنسِخ المستودع وشغِّله باستخدام example/ToolMain.kt:
bazel run -c opt //kotlin/java/com/google/ai/edge/litertlm/example:tool -- <abs_model_path>
خطأ أثناء المعالجة
يمكن أن تعرض طرق واجهة برمجة التطبيقات LiteRtLmJniException أخطاء من الطبقة الأصلية أو استثناءات Kotlin العادية، مثل IllegalStateException، للمشاكل المتعلّقة بدورة الحياة.
احرص دائمًا على تضمين طلبات البيانات من واجهة برمجة التطبيقات في كتل try-catch. ستُبلغ الدالة onError callback في
MessageCallback أيضًا عن الأخطاء التي تحدث أثناء العمليات غير المتزامنة.