Files
view-mate/docs/litertlm-openassistant-notes.md
T
subochev 7527cf8848 LLM: интерфейс + две реализации (Qwen удалённо / Gemma 4 E2B локально) + переключатель
- LlmClient интерфейс; RemoteLlmClient (OkHttp, llm.binom.pw) и LocalLlmClient (LiteRT-LM, Gemma 4 E2B)
- LocalLlm: Gemma4E2B (SHA-256, GPU/CPU-бэкенды), buildLocalLlmPrompt (SYSTEM→systemInstruction, TOOL→user), GemmaModelDownloader (стриминг, прогресс, фолбэк источников), спекулятивное декодирование
- LlmPrefs (персистентность выбора), PhoneApp: фабрика по llmChoice, пересоздание ассистента, скачивание/отмена
- UI: GlassesInfoScreen — выбор сервер/локальная + прогресс; ChatScreen — индикатор активной модели
- Тесты: +12 (LlmLocalTest) — phone 154 зелёных
2026-08-23 02:52:22 +03:00

21 KiB
Raw Blame History

OpenAssistant (vayun-mathur/Modern-Apps) — локальные нейронки на Android

Источник: https://github.com/vayun-mathur/Modern-Apps/tree/main/openassistant Клон: ~/WORK/modern-apps/ (depth 1). Разбор 2026-08-10.

Текстовые альтернативы E2B в формате .litertlm (проверено 2026-08-10, полка litert-community)

Вопрос: есть ли ТЕКСТОВЫЕ модели заметно умнее E2B (2B) за те же/чуть большие токены, тот же формат.

Модель Парам. Контекст Размер Декод на 8 Gen 3 Оценка
Gemma 4 E2B (текущая) 2B 32K ~0.8 ГБ 20-35 т/с (спекуляция вкл) база сравнения
Qwen3-8B (mixed int4) 8B 2048 (!) 4.66 ГБ ~10-12 т/с (оценка, телеф. бенча нет) заметно умнее, но контекст урезан вдрызг
Phi-4-mini-instruct 3.8B 4096 3.9 ГБ 10.39 т/с GPU (S24 Ultra, 8 Gen 3, динамик int8) умнее, в 2 раза медленнее
Ministral-3-3B-Reasoning 3.3B 4096 ~2.2 ГБ нет телеф. бенча reasoning, текст, но медленный по природе
DeepSeek-R1-Distill-Qwen-1.5B 1.5B 4096 1.83 ГБ 26.35 т/с CPU (S24 Ultra) не умнее, другой класс (reasoning)
Gemma2-2B-IT 2B — — — доступ ограничен (restricted)
Gemma3-4B-IT 4B — — — мультимодальная, не подходит по критерию

ВЫВОД: за "умнее + текст" платишь скоростью и контекстом. Phi-4-mini — единственный с честным замером на 8 Gen 3 (10.39 т/с — вдвое медленнее 20-25). Qwen3-8B — умнее всех, но 2048 контекст и ~10 т/с. E2B остаётся оптимумом для "незаметной" скорости (20+ т/с).

Две нейронки + токенайзер

1. Чат-LLM: Gemma 4 E2B (2B) — файл gemma-4-E2B-it.litertlm

  • Формат .litertlm (LiteRT LM, Google; бывш. AI Edge / MediaPipe LLM Inference)
  • Библиотека: com.google.ai.edge.litertlm:litertlm-android:0.14.0
  • Запуск: Engine(EngineConfig(modelPath, backend=GPU(), visionBackend=GPU(), audioBackend=CPU(), cacheDir)) + engine.initialize()
  • Speculative decoding: ExperimentalFlags.enableSpeculativeDecoding = true
  • Мультимодальный ввод: Content.Text / Content.ImageFile / Content.AudioFile (голос — WavRecorder, обрабатывается на CPU)
  • Источник: huggingface.co/litert-community/gemma-4-E2B-it-litert-lm
  • SHA256: 181938105e0eefd105961417e8da75903eacda102c4fce9ce90f50b97139a63c
  • На диске: gemma4-2b.litertlm в getExternalFilesDir(null)

2. Эмбеддер SigLIP2-base (patch16, 224) — семантический поиск по фото

  • Служит фото-приложению через ResultReceiver (IPC): text/image/info-запросы
  • ONNX Runtime: com.microsoft.onnxruntime:onnxruntime-android:1.27.0
  • Два файла: vision_model_fp16.onnx (картинки), text_model_int8.onnx (текст)
  • Вектора 768-мерные (dim читается из модели), L2-нормированные, косинус
  • Источник: huggingface.co/onnx-community/siglip2-base-patch16-224-ONNX
  • SHA256 vision: a1959f7bd3993a607e48839f6d01e25b876fe76afda301b028b78eef68aabd95
  • SHA256 text: 3a0603d3a00c05a80a6ded4743c16aaac7b1e62cdcc7e362e7ce418659b96400
  • ВАЖНО: из выходов брать pooler_output (ранг-2), НЕ last_hidden_state (ранг-3), иначе вектора бессмысленны
  • Препроцессинг картинки: ресайз прямо в 224x224 (без center-crop), (px-127.5)/127.5, NCHW RGB
  • Сессии ONNX однопоточные (setIntraOpNumThreads(1)) — бережёт батарею

3. Токенайзер SentencePiece (Gemma tokenizer.model, ~4MB, 256k vocab)

  • Файл: siglip2_tokenizer.model (SHA256 61a7b147390c64585d6c3543dd6fc636906c9af3865a5548f27f31aee1d4c8e2)
  • Нужен ТОЛЬКО текстовой башне SigLIP2 (litertlm не отдаёт token id наружу)
  • Реализация без protobuf-зависимости: ручной парсинг ModelProto (pieces = field 1; piece=1 string, score=2 float, type=3 enum; индекс == token id)
  • Кодирование: Viterbi Unigram сегментация + byte fallback (<0xNN>, Gemma шлёт все 256 байтовых писов)
  • Препроцессинг текста: lowercase → NFKC → схлопнуть пробелы → dummy-prefix пробел → ▁ (U+2581)
  • SEQ_LEN = 64 (падинг padId=0, обрезка)
  • Риск: совпадение с transformers Siglip2Processor — первое, что проверять при повторе

Как это работает (архитектура)

  • Foreground-сервис InferenceService, 3 очереди: standard (чат), intent (JSON-извлечение), embedding (SigLIP2 — отдельный consumer, не ждёт LLM)
  • Модели качаются ТОЛЬКО с зеркала https://data.vayunmathur.com/models/ (никакого HF-фолбэка, supply-chain митигация), SHA-256 проверка, Content-Length + ETag
  • Чат: история из Room-БД → ConversationConfig(systemInstruction, initialMessages, tools, automaticToolCalling=true) → sendMessageAsync стриминг
  • Инструменты (AssistantToolSet): add_to_memory / get_memories / remove_memory и др., automaticToolCalling=true
  • Intent-режим: system prompt "data extraction engine", стриминг, tryExtractLargestJson + валидация по JSON Schema, досрочный HALT через CancellationException("HALT"), таймаут 45с, очередь-дроп по 45с
  • Версионирование: cleanupStaleSiglipModels по SiglipEmbedder.MODEL_VERSION; legacy gemma4.litertlm / gemma4-4b.litertlm удаляются при старте
  • Сборка: noCompress += "onnx"; packaging pickFirsts: libLiteRtTopKOpenClSampler.so, libc++_shared.so

Ключевой вывод (что ценно для нас)

Главный урок не в архитектуре, а в скорости: автор довёл локальную нейронку на телефоне до >=20-25 ток/с — пользователь не видит задержки, ассистент отвечает "на скидку" (вживую, без заметных пауз). Это рецепт быстрого запуска нейронки на устройстве:

  • Модель 2B (Gemma 4 E2B) в оптимизированном формате LiteRT LM — компактная, но живая
  • GPU-бэкенд для text/vision (OpenCL), CPU только для аудио
  • Speculative decoding включён (ExperimentalFlags) — дёшево ускоряет генерацию
  • Одна сессия движка, переиспользуется; очереди разнесены, эмбеддинги не блокируют чат
  • Токенайзер ручной (без protobuf), эмбеддер на ONNX — но это обвязка, не узкое место

Т.е. заявка на повтор: 2B-модель + LiteRT LM + GPU + speculative decoding = быстрый локальный ассистент.

Ответы на вопросы (2026-08-10)

Что за формат .litertlm и можно ли заменить модель

  • .litertlm — контейнер LiteRT-LM (Google; бывш. MediaPipe LLM Inference / AI Edge): внутри квантованная модель (смесь 2/4/8 бит, text-only вес ~0.8GB), эмбеддинги (1.12GB, memory-mapped), токенайзер, плюс движок даёт KV-cache менеджмент, промпт-темплейт, function calling
  • ЗАМЕНИТЬ МОЖНО, и это просто: готовые .litertlm в litert-community на HF (десятки): Qwen3 0.6B/1.7B/8B, Qwen2.5 0.5B/1.5B, TinyLlama-1.1B, Phi-4-mini, SmolLM-135M, DeepSeek-R1-Distill-Qwen-1.5B (reasoning), Gemma3-1B/4B, FunctionGemma-270M (function calling), Nanbeige-3B, Ministral-3-3B, Mage-VL (VLM), FastVLM-0.5B. Свои модели — через LiteRT Torch Generative API (ai-edge-litert, Python)
  • В openassistant замена = поменять URL+SHA256 в ModelUrls и файл. SigLIP2-токенайзер от LLM не зависит
  • Ограничения при замене: (а) speculative decoding вшит в сборку модели — у Gemma 4 E2B есть, у других не факт; (б) Content.ImageFile/AudioFile работают только у мультимодальных сборок; (в) tools/automaticToolCalling — у Gemma/Qwen/Phi есть, не у всех
  • Для скорости на слабом железе: Qwen3-0.6B / SmolLM-135M / Qwen2.5-0.5B (быстрее, но глупее); компромисс: Qwen3-1.7B, Gemma3-1B; русский текст у Qwen обычно лучше

Мультимодальность Gemma 4 E2B

  • ДА: текст + картинки + аудио в одном сообщении. В коде: Content.Text / Content.ImageFile / Content.AudioFile; text/vision на GPU, audio на CPU
  • Из README: картинки/аудио класть ПЕРЕД текстом в промпте; переменное разрешение картинок через visual token budget (токенов на картинку); vision/audio части грузятся по требованию
  • В openassistant intent-режим шлёт imagePaths + userText одним Messages.user — живой пример смешанного ввода

Контекст

  • Полная Gemma 4 — до 256K токенов (model card). On-device litertlm-сборка: "supports up to 32k context length" — 32K токенов
  • Бенчмарки litert-community: 1024 prefill + 256 decode, контекст 2048; S26 Ultra GPU: decode 52 ток/с (baseline), 66-92 ток/с со speculative decoding; prefill 3808 ток/с; TTFT 0.3с
  • Приложение лимита не задаёт (ConversationConfig без maxTokens) — контекстом рулит движок (KV-cache менеджмент); история из Room идёт в initialMessages, упор в 32k движок разруливает сам
  • 20-25 ток/с автора — нижняя граница "незаметно", на флагмане официально 46-92

Скорость на Snapdragon 8 Gen 3 (12 ГБ RAM)

  • УСТРОЙСТВО АВТОРА (подтверждено 2026-08-10): Honor Magic V3, Snapdragon 8 Gen 3, 12 ГБ RAM. (Сначала назвал Gen 2 — ложный след, снят с протокола.)
  • Официальная карточка litert-community НЕ даёт 8 Gen 3 — бенчмарки только на S26 Ultra (чип 2026, новее): GPU decode 52.1 ток/с baseline, 66.5-91.7 со speculative decoding; prefill 3808 ток/с; TTFT 0.3с; CPU: decode 46.9, prefill 557, TTFT 1.8с. Память: GPU-бэкенд 676 МБ, CPU 1733 МБ
  • 8 Gen 3 (2023, Adreno 750, напр. S24 Ultra US): официальных цифр нет; сторонние замеры (MindStudio): E2B 20-35 ток/с, E4B 12-20 ток/с. Прим.: у MindStudio Pixel 9 приписан к 8 Gen 3 ошибочно (там Tensor G4); настоящий 8 Gen 3 — S24 Ultra (US) и другие
  • Память 12 ГБ — с большим запасом: модель на GPU ~676-800 МБ RSS (text-only), эмбеддинги 1.12 ГБ memory-mapped (не в RSS), vision/audio по требованию. Ограничение не в RAM, а в скорости GPU/пропускной способности
  • Вывод: заявленные автором 20-25 ток/с — реалистичная нижняя/средняя граница вилки для 8 Gen 3; со speculative decoding можно ждать заметно больше (порядка 50+ на флагмане, по аналогии с S26 Ultra)

Спекулятивное декодирование (MTP) на Snapdragon 8 Gen 3

  • Что это: LLM обычно выдаёт 1 токен за проход сети. Спекулятивное декодирование: маленький черновик-предсказатель набрасывает сразу N следующих токенов, большая модель проверяет их ОДНИМ проходом — если угадал, получил N токенов ценой одного прохода. Ускорение до ~3x без потери качества
  • У Gemma 4 это НЕ внешняя надстройка, а встроенная архитектура — Multi-Token Prediction (MTP): черновик-головы сидят прямо в чекпоинте модели (ai.google.dev/gemma/docs/mtp). Спекуляция у Gemma 4 работает автоматически при поддержке рантайма; флаг лишь включает рантайм-путь
  • В openassistant флаг ПОДТВЕРЖДЁН в коде: InferenceService.kt:486 — ExperimentalFlags.enableSpeculativeDecoding = true перед engine.initialize()
  • ВЫВОД: 20-25 ток/с автора НЕ означают, что спекуляция выключена. Наоборот — она включена. У автора Honor Magic V3 = Snapdragon 8 Gen 3 (Adreno 750): вилка со спекуляцией ~20-35 ток/с (MindStudio), без неё ~10-15 (вдвое меньше). Его 20-25 — ровно середина ожидаемого С включённой спекуляцией, ближе к нижней границе (зависит от термальных лимитов складного корпуса)
  • Как проверить на живом телефоне: поставить enableSpeculativeDecoding = false, замерить — падение вдвое = она работала
  • 8 Gen 3 (2023, Adreno 750): сторонние замеры E2B 20-35 ток/с; на 8 Gen 2 (2022, Adreno 740) было бы ниже на ~15-20%

Как повторить (чеклист)

  1. Скачать 4 файла с HF (см. SHA256 выше), разложить под зеркало /models/
  2. Android: litertlm-android 0.14.0 + onnxruntime-android 1.27.0 (+ kotlinx-coroutines 1.11.0 — иначе close$default на SendChannel)
  3. Backend: GPU для text/vision, CPU для audio
  4. Из SigLIP2 ONNX читать pooler_output, не last_hidden_state

Бонус: что ещё в монорепо Modern-Apps

findfamily (UWB-локатор), keyboard (хангыль-композитор), maps, games (Stockfish через JitPack ncnn), библиотеки: downloadservice, room, image.

Raspberry Pi 5: да, работает

Вопрос: потянет ли Gemma 4 E2B Raspberry Pi 5? Ответ: да, и это официально.

Формат и рантайм:

  • LiteRT-LM теперь официально кросс-платформенный: Android, iOS, Web, Desktop, IoT (Raspberry Pi). Есть официальный CLI (uvx-установка), который гоняет .litertlm прямо на Pi — без конвертации. GitHub: google-ai-edge/LiteRT-LM.
  • Google сам пишет «Try Gemma4-E4B with MTP on Linux, macOS, Windows or Raspberry Pi with the LiteRT-LM CLI».

Реальные замеры на RPi 5 (8 ГБ, 4×Cortex-A76 @ 2.4 ГГц, без GPU-акселератора):

  1. Ollama (GGUF): первый токен ~3-4 сек, генерация ~8-12 t/s. Модель ~1.5 ГБ.
  2. Стоковый llama.cpp (Q4): ~6.6 t/s.
  3. mkturkcan/mote (оптимизированный llama.cpp + MTP-спекуляция + A76-ядра + KleidiAI): 11.2 t/s на обычном тексте, до 16 на шаблонном, до 20.4 в turbo на повторах (логи/CSV). Устанавливается одной командой, ничего не собирается на Pi.
  4. Официальный LiteRT-LM CLI: цифры не подтверждены, но движок тот же — ожидаемо в районе 6-12 t/s.

Важно про контекст на Pi:

  • Модель заявляет 128K контекста, но KV-кэш на 128K не влезает в 8 ГБ. На RPi 5 (8 ГБ) реально 4K-8K токенов, дальше — своп и обвал скорости. На 16 ГБ — комфортно ~32K.
  • Мультимодальность (текст+картинки+видео) на Pi работает, но медленнее телефона.

Вывод: на RPi 5 E2B даёт ~8-12 t/s (сток) или ~11-20 t/s (оптимизированный mote). Это в 2-3 раза медленнее телефона (20-25 t/s), но для офлайн-ассистента, пакетной обработки и домашних экспериментов — достаточно. Воспринимается как «медленно, но читаемо», а не как «зависло».

Батарея и скорость обработки картинок (Honor Magic V3, 8 Gen 3, 12 ГБ)

Батарея

  • Точных ватт на Android под GPU-нагрузкой НЕТ ни у кого: Battery Manager API Android ненадёжен под нагрузкой, iOS API не отдаёт (arxiv 2603.23640, "LLM Inference at the Edge", бенчмарк на S24 Ultra = тот же 8 Gen 3).
  • Ориентиры из той статьи: S24 Ultra idle ~0.8 Вт, max ~12 Вт (система целиком). Qwen 1.5B через MLC-LLM: 9.93 ток/с sustained, префилл 25 сек (OpenCL-оверхед MLC), термальный фейл на 6-й итерации подряд: GPU freq упала до 231 МГц, 78.3°C — жёсткий флор от термогубернатора, а не плавный DVFS как у iPhone.
  • Энергия на токен на edge-платформах: ~270-300 мДж/токен (Hailo-10H 1.87 Вт / 6.9 ток/с; RTX 4050 ноут 34 Вт / 131.7 ток/с).
  • Honor Magic V3: батарея 5150 мАч ≈ 19.9 Вт·ч.
  • Прикидка: Gemma 4 E2B на 8 Gen 3 ~22 ток/с при ~4-5 Вт системы → 0.2 Дж/токен ≈ 56 мкВт·ч/токен. Непрерывная генерация ≈ 4-4.5 часа на всю батарею (20-25%/час). Сценарий ассистента (5-10 мин генерации в час) → 1.5-4%/час — почти незаметно.
  • Термальный капкан 8 Gen 3: непрерывная нагрузка без пауз → троттлинг, скорость падает до ~2 раз. Magic V3 тонкий — отвод тепла хуже, троттлинг раньше. Паузы между запросами обязательны (раз в несколько минут — ок).
  • LiteRT-LM оптимизирован под Adreno лучше MLC-LLM (это гугловский движок), поэтому 20-25 ток/с у автора — уже с учётом реального троттлинга.

Картинки (видео-поток с камеры)

  • Gemma 4: переменное разрешение через токен-бюджет: 70/140/280/560/1120 токенов на картинку. Google прямо советует для кадров видео НИЗКИЙ бюджет (70) ради скорости. Механика: 9 патчей на токен (3x3 среднее).
  • Каждый кадр = вижн-энкодер + префилл токенов бюджета. Порядок: 0.3-1.5 сек на кадр на 8 Gen 3 (оценка, точного бенчмарка нет).
  • 30 fps НЕВОЗМОЖНО: 70 токенов/кадр × 30 = 2100 ток/с префилла — префилл на телефоне ~200-500 ток/с. Реалистично: кадр раз в 2-5 сек, бюджет 70-140.
  • Контекст 32K: при 140 ток/кадр ≈ 230 кадров в окне, при 70 ≈ 450. Кадр раз в 2 сек → 8-15 минут непрерывного окна → нужен скользящий буфер кадров.
  • Для "где я иду/еду": модель получает кадр + вопрос ("где я?"), отвечает за 1-3 сек. Рабочий вариант — периодический анализ, а не поток. Сколько картинок за сообщение принимает on-device сборка — проверять на практике (в доке: reasoning across multiple images).
  • Код автора: Content.ImageFile(path) шлёт файлы как есть, бэкенд GPU для текста+зрения (InferenceService.kt:493-494), аудио CPU. Приложение лимит картинок не задаёт.