Files
view-mate/docs/litertlm-openassistant-notes.md
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

159 lines
21 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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. Приложение лимит картинок не задаёт.