Kotlin API LiteRT-LM для Android и JVM (Linux, MacOS, Windows) с такими функциями, как ускорение GPU и NPU , многомодальность и используемые инструменты .
Введение
Вот пример приложения для чата в терминале, созданного с использованием Kotlin API:
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 Gallery .
Начало работы с 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 является точкой входа в API. Инициализируйте его, указав путь к модели и конфигурацию. Не забудьте закрыть движок, чтобы освободить ресурсы.
Примечание: Метод 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 . Более подробную информацию о нативных библиотеках NPU см. в документации 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 для потоковой передачи ответов. Это рекомендуемый подход для пользователей сопрограмм.
Пример синхронного воспроизведения:
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)
Асинхронный пример с использованием потока:
Используйте sendMessageAsync (без аргумента callback), чтобы отправить сообщение модели и получить ответы через 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()
}
}
В фоновом режиме API анализирует эти аннотации и сигнатуру функции для генерации схемы в стиле OpenAPI. Эта схема описывает функциональность инструмента, параметры (включая их типы и описания из @ToolParam ) и тип возвращаемого значения в соответствии с языковой моделью.
Типы параметров
Типы параметров, аннотированных @ToolParam , могут быть String , Int , Boolean , Float , Double или List этих типов (например, List<String> ). Используйте типы, допускающие значение null (например, String? ), чтобы указать параметры, допускающие значение null. Установите значение по умолчанию, чтобы указать, что параметр является необязательным, и укажите это значение в описании в @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>
Обработка ошибок
Методы API могут генерировать исключения LiteRtLmJniException при ошибках нативного уровня или стандартные исключения Kotlin, такие как IllegalStateException , при проблемах жизненного цикла. Всегда оборачивайте вызовы API в блоки try-catch. Коллбэк onError в MessageCallback также будет сообщать об ошибках во время асинхронных операций.