Интеграция On-Device LLM (Llama.cpp) для офлайн AI-ассистента в мобильном приложении

TRUETECH занимается разработкой, поддержкой и обслуживанием мобильных приложений iOS, Android, PWA. Имеем большой опыт и экспертизу для публикации мобильных приложений в популярные маркеты Google Play, App Store, Amazon, AppGallery и другие.

Разработка и поддержка любых видов мобильных приложений:

Информационные и развлекательные мобильные приложения
Новостные приложения, игры, справочники, онлайн-каталоги, погодные, фитнес и здоровье, туристические, образовательные, социальные сети и мессенджеры, квиз, блоги и подкасты, форумы, агрегаторы
Мобильные приложения электронной коммерции
Интернет-магазины, B2B-приложения, маркетплейсы, онлайн-обменники, кэшбэк-сервисы, биржи, дропшиппинг-платформы, программы лояльности, доставка еды и товаров, платежные системы
Мобильные приложения для управления бизнес-процессами
CRM-системы, ERP-системы, управление проектами, инструменты для команды продаж, учет финансов, управление производством, логистика и доставка, управление персоналом, системы мониторинга данных
Мобильные приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, платформы предоставления электронных услуг, платформы кешбека, видеохостинги, тематические порталы, платформы онлайн-бронирования и записи, платформы онлайн-торговли

Это лишь некоторые из типы мобильных приложений, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента.

Услуги, которые мы предлагаем
Показано 1 из 1Все 1735 услуг
Интеграция On-Device LLM (Llama.cpp) для офлайн AI-ассистента в мобильном приложении
Сложный
~2-4 недели
Часто задаваемые вопросы

Наши компетенции:

Этапы разработки

Последние работы

  • image_mobile-applications_feedme_467_0.webp
    Разработка мобильного приложения для компании FEEDME
    792
  • image_mobile-applications_xoomer_471_0.webp
    Разработка мобильного приложения для компании XOOMER
    671
  • image_mobile-applications_rhl_428_0.webp
    Разработка мобильного приложения для компании RHL
    1097
  • image_mobile-applications_zippy_411_0.webp
    Разработка мобильного приложения для компании ZIPPY
    969
  • image_mobile-applications_affhome_429_0.webp
    Разработка мобильного приложения для компании Affhome
    914
  • image_mobile-applications_flavors_409_0.webp
    Разработка мобильного приложения для компании FLAVORS
    495

Интеграция On-Device LLM (Llama.cpp) для офлайн AI-ассистента в мобильном приложении

Запустить языковую модель прямо на смартфоне без интернета — это уже реальность. Llama.cpp даёт работающий инференс на CPU с опциональным Metal/Vulkan ускорением. Главный вопрос не «можно ли» а «какую модель и в каком квантовании выбрать, чтобы устройство не перегрелось через 5 минут».

Модели и их реальные требования

Llama.cpp работает с моделями в формате GGUF. Популярные варианты для мобиля:

Модель Квантование Размер RAM Скорость (iPhone 14)
Llama-3.2-1B Q4_K_M 0.8 ГБ ~1.2 ГБ 25–35 t/s
Llama-3.2-3B Q4_K_M 2.0 ГБ ~2.5 ГБ 10–15 t/s
Phi-3-mini-4k Q4_K_M 2.2 ГБ ~2.8 ГБ 8–12 t/s
Gemma-2-2B Q4_K_M 1.6 ГБ ~2.0 ГБ 12–18 t/s
Qwen2.5-1.5B Q4_K_M 1.0 ГБ ~1.4 ГБ 20–28 t/s

На iPhone SE 2nd gen (3 ГБ RAM) Llama-3.2-3B Q4 работает на пределе — OOM возможен при длинных контекстах. Безопасный выбор для широкого парка устройств — модели до 1.5–2 ГБ.

Сборка llama.cpp для iOS

# Клонируем репозиторий
git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp

# Сборка через CMake для iOS
cmake -B build-ios \
    -DCMAKE_TOOLCHAIN_FILE=ios.toolchain.cmake \
    -DPLATFORM=OS64 \  # arm64 only
    -DLLAMA_METAL=ON \  # Metal GPU ускорение
    -DLLAMA_STATIC=ON
cmake --build build-ios --config Release

Результат — libllama.a статическая библиотека. Создаём Swift Package с C-bridging header:

// llama_bridge.h
#include "llama.h"
// Обёртки для Swift-дружественного API
void* llama_create_context(const char* model_path, int n_ctx, int n_gpu_layers);
const char* llama_generate_token(void* ctx, const char* prompt);
void llama_free_context(void* ctx);

n_gpu_layers — количество слоёв, выгружаемых на Metal GPU. Значение -1 означает все слои на GPU. На iPhone 14 с 6 ГБ unified memory — ставьте -1. На устройствах с 3 ГБ — экспериментируйте: слишком много слоёв на GPU вызывает OOM.

Swift-обёртка для стриминга токенов

import Foundation

actor LlamaSession {
    private var context: OpaquePointer?
    private var model: OpaquePointer?

    func load(modelPath: String, contextSize: Int32 = 2048, gpuLayers: Int32 = -1) throws {
        var params = llama_model_default_params()
        params.n_gpu_layers = gpuLayers

        model = llama_load_model_from_file(modelPath, params)
        guard model != nil else { throw LlamaError.modelLoadFailed }

        var ctxParams = llama_context_default_params()
        ctxParams.n_ctx = UInt32(contextSize)
        ctxParams.n_batch = 512

        context = llama_new_context_with_model(model, ctxParams)
    }

    func generate(prompt: String) -> AsyncThrowingStream<String, Error> {
        AsyncThrowingStream { continuation in
            Task.detached(priority: .userInitiated) {
                // Токенизация
                var tokens = [llama_token](repeating: 0, count: 4096)
                let nTokens = llama_tokenize(self.model, prompt, Int32(prompt.utf8.count),
                                              &tokens, 4096, true, false)

                // Инференс — по одному токену
                for i in 0..<nTokens {
                    llama_batch_add(&batch, tokens[Int(i)], llama_pos(i), [0], false)
                }

                while true {
                    llama_decode(self.context, batch)
                    let nextToken = llama_sample_token_greedy(self.context, &candidates)

                    if nextToken == llama_token_eos(self.model) { break }

                    // Конвертация токена в строку
                    var buf = [Int8](repeating: 0, count: 64)
                    llama_token_to_piece(self.model, nextToken, &buf, 64, 0, true)
                    let piece = String(cString: buf)

                    continuation.yield(piece)
                }
                continuation.finish()
            }
        }
    }
}

Стриминг токенов через AsyncThrowingStream — пользователь видит текст по мере генерации, не ждёт весь ответ. Это критично для UX: 10 токенов в секунду воспринимается нормально, если текст появляется постепенно.

Android: llama.cpp через NDK

// CMakeLists.txt в jni/
add_library(llama_jni SHARED llama_jni.cpp)
target_link_libraries(llama_jni llama ggml)

// Kotlin side
class LlamaEngine {
    init { System.loadLibrary("llama_jni") }

    external fun loadModel(modelPath: String, nGpuLayers: Int): Long  // возвращает handle
    external fun generateNext(handle: Long, tokens: IntArray): String
    external fun freeModel(handle: Long)
}

На Android — Vulkan backend вместо Metal: в CMakeLists включаем LLAMA_VULKAN=ON. Поддерживается на устройствах с Vulkan 1.1+, то есть практически всё с Android 10+.

Проблема с Android: процесс не имеет ограничения памяти как целого пула — система может убить приложение (SIGKILL) при нехватке RAM без предупреждения. ComponentCallbacks2.onTrimMemory(TRIM_MEMORY_RUNNING_CRITICAL) — последний шанс освободить контекст перед убийством процесса.

Скачивание модели: прогресс и верификация

GGUF-файлы весят 1–4 ГБ. Скачиваем через URLSession (iOS) или WorkManager с DownloadManager (Android):

// iOS: Background URLSession для скачивания в фоне
let config = URLSessionConfiguration.background(withIdentifier: "model-download")
config.isDiscretionary = false
let session = URLSession(configuration: config, delegate: self, delegateQueue: nil)

let task = session.downloadTask(with: modelURL)
task.resume()

// Верификация SHA256 после скачивания
func verify(fileURL: URL, expectedHash: String) -> Bool {
    guard let data = try? Data(contentsOf: fileURL) else { return false }
    let hash = SHA256.hash(data: data)
    return hash.compactMap { String(format: "%02x", $0) }.joined() == expectedHash
}

SHA256 хэш модели публикуют в репозитории на HuggingFace — сверяем до загрузки в память. Повреждённый GGUF вызывает краш при парсинге заголовка или позже при инференсе — лучше поймать на верификации.

Тепловые ограничения

Llama.cpp на iPhone при длительной генерации разогревает устройство. iOS throttling: при перегреве система снижает тактовую частоту, скорость генерации падает с 25 t/s до 8–10 t/s. Это не баг — поведение системы.

Практическое решение: ограничивать максимальный контекст (n_ctx) до 1024–2048 для коротких сессий. Между запросами — пауза. Мониторить ProcessInfo.processInfo.thermalState на iOS:

NotificationCenter.default.addObserver(forName: ProcessInfo.thermalStateDidChangeNotification, ...) { _ in
    let state = ProcessInfo.processInfo.thermalState
    if state == .critical || state == .serious {
        // Приостановить генерацию, уведомить пользователя
    }
}

Процесс

Подбор модели под парк устройств → сборка llama.cpp под целевые платформы → Swift/Kotlin обёртка с async стримингом → реализация скачивания и верификации → UI чат-интерфейса → стресс-тест на тепловые ограничения.

Ориентиры по срокам

Одна платформа, базовый чат-интерфейс с выбранной моделью — 3–5 недель. Обе платформы, несколько моделей на выбор, фоновое скачивание, управление контекстом — 7–12 недель.