- BrainTools - https://www.braintools.ru -
Смотря на столь бурное развитие направления по применении агентов с ИИ для автоматизации OpenClaw убрала поддержку бесплатного использования модель Qwen.
Это было ограниченное количество запросов, но тем не менее – работало весьма хорошо.
Теперь это только платная подписка.
Аналогичным образом поступают и другие вендоры ИИ – какие-то LLM просто не умеют работать как агенты, какие-то подлежат кастомизации (Claude). Опять же – всё хорошее за деньги, средний лимит – 1 млн токенов для демонстрации.
Пока все смеются над тем, что только это компания где-то опоздала, на самом деле Apple заключает соглашение с Google об использовании квантованных (сжатых) версий топовых моделей от техно-гиганта.
Недавно Apple подтвердила стратегическое партнерство с Google для интеграции ИИ Gemini в свои устройства. Это решение связано со сложностями в создании нейросети такого масштаба для конкуренции с лидерами рынка.
Основные детали сделки:
Интеграция с Siri: Gemini станет основой обновленной Siri.
Модель Google будет отвечать за понимание контекста, планирование задач и обобщение информации.
Работа внутри смартфона: Apple получила доступ к технологиям Google для дистилляции. Это позволит Apple обучить собственные компактные модели на базе Gemini, которые будут работать напрямую на устройстве, обеспечивая скорость и конфиденциальность.
Вот именно это мы сейчас и опробуем, только на примере с Android.
Не секрет, что на Android можно запускать Linux-форки приложений и работать на телефоне в полноценной Linux-среде. Для этого энтузиастами был разработан эмулятор – имя ему Termux Termux Wiki [1]
Я использую это решения для запуска веб-приложений backend/frontend с Mongodb, reactjsб javascript через node.js и nginx, а также для подключения к linux/unix-системам напрямую с телефона. Всё работает локально на устройстве.
Основные плюшки:
Пакетный менеджер: Упрощает установку и управление программами с помощью pkg, аналогичного apt.
Поддержка языков программирования: Termux поддерживает Python, Ruby, Node.js, PHP, Go, Rust и другие языки.
Интеграция с Git и контроля версий: Позволяет работать с репозиториями и контролем версий.
Доступ к файловой системе Android: Удобно работает с файловой системой Android, включая внешнюю SD-карту.
Интеграция с внешними клавиатурами и терминальными клиентами: Поддержка SSH, VNC и других терминальных клиентов.
Автоматизация задач: Позволяет автоматизировать выполнение команд и скриптов.
Таким образом, Termux является мощным инструментом для пользователей Android, которые ищут возможность использовать Linux-подобную среду на своем устройстве.
Установка на Android устройство проста до безобразия не требует особых умственных усилий, в отличие от вычисления над полями Галуа Finite field – Wikipedia [2]
В качестве альтернативы и для получения более свежей версии можно выполнить установку, скачав предварительно F-Droid с официального сайта.
Открываете Termux. Видите чёрный экран с белым текстом и приглашением $ — это командная строка. Теперь вы — пользователь Linux на своём телефоне.
Вводим команду: uname - a
Консоль отзывается – всё хорошо.
pkg update && pkg upgrade -y
Процесс должен выглядеть примерно так:
pkg install -y curl wget git nodejs nginx
Это инструменты для работы с web-фреймворками. Они нам потребуются для запуска WebUI.
Многие кто погружен в эту историю уже знают про HuggingFace и аналогичные сервисы.
Одним из популярных также является Ollama – это софт для установки и запуска больших языковых моделей локально на устройство. Как правило – на серверы, ПК, ноутбуки.
Но мало кто пробовал ставить на Android – вы будете в числе первых!
wget https://github.com/ollama/ollama/releases/download/v0.5.4/ollama-linux-arm64
chmod +x ollama-linux-arm64
mv ollama-linux-arm64 $PREFIX/bin/ollama
ollama --version
Ожидаемый вывод: ollama version 0.5.4
Также можно просто набрать ollama – программа перейдет в интерактивный режим:
Gemma 3 — это новое поколение открытых мультимодальных моделей искусственного интеллекта [3] от Google DeepMind, представленное весной 2025 года.
Она оптимизирована для эффективной работы на обычных пользовательских устройствах.
Мультимодальность: Начиная с версии 4B, Gemma 3 способна одновременно понимать и обрабатывать текст, изображения и короткие видеоролики.
Доступные версии: Семейство включает модели разного размера для разных задач:
1B (1 млрд параметров): Эта модель работает только с текстом и английским языком. Подходит для мобильных устройств.
4B, 12B и 27B: Это мультимодальные модели, поддерживающие более 140 языков.
Контекстное окно: Модели поддерживают до 128 000 токенов, что позволяет анализировать очень длинные документы.
Открытость: Веса моделей доступны для свободного скачивания и использования разработчиками.
Производительность: Старшие версии (27B) в ряде тестов превосходят более массивные модели, такие как Llama-3 405B.
Оптимизация: Модель 27B требует около 62 ГБ видеопамяти при полной точности, но может работать на 15.5 ГБ в квантованном режиме.
Инструменты: Модель хорошо справляется со структурированным выводом и вызовом внешних функций, что делает её подходящей для создания автономных ИИ-агентов.
Открываем ПЕРВОЕ окно Termux (основное) и запускаем:
OLLAMA_ORIGINS="*" ollama serve
💡 Важно: флаг OLLAMA_ORIGINS="*" включает CORS. Без него веб-интерфейс не сможет обращаться к Ollama. Так мы запускаем сервер Ollama в фоне.
Открываем ВТОРОЕ окно Termux (свайп от левого края → New session):
ollama pull gemma3:1b
Увидим процесс скачивания модели:
ollama run gemma3:1b
Модель загрузится и можно к ней можно обращаться с консоли: пишем наш запрос.
Поскольку так работать вполне можно, но немного не удобно. Поэтому нам нужно научиться работать с моделькой как мы привыкли, как через приложение.
Я попробовал запустить все возможные и рекомендуемые форки для WebUI для Ollama – но ничего из этого не заработало или требовало серьезных танцев с бубнами.
Написание полноценного приложения это весьма затратный путь – я написал WebUI на базе html, javascript, который запускается в termux через nginx, а пользоваться можно просто через любой браузер в смартфоне.
Но для начала нужно немного подготовиться.
Убедимся, что наш сервис отвечат на http-запросы.
curl -X POST http://127.0.0.1:11434/api/generate
-d '{"model":"gemma3:1b","prompt":"Привет! Как тебя зовут?","stream":false}'
-H "Content-Type: application/json"
Модель отвечает. Идём дальше!
По умолчанию Nginx на termux хранит данные по следующему пути:
/usr/share/nginx/html
Создадим файл нашего веб-приложения:
nano /usr/share/nginx/html/chat.html
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Gemma 3 WebUI</title>
<style>
:root {
--bg-color: #0f172a;
--chat-bg: #1e293b;
--user-msg: #3b82f6;
--bot-msg: #334155;
--text-main: #f8fafc;
--text-muted: #94a3b8;
--accent: #60a5fa;
--border: #475569;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
background-color: var(--bg-color);
color: var(--text-main);
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
height: 100vh;
box-sizing: border-box;
}
header {
background-color: var(--chat-bg);
padding: 15px 20px;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid var(--border);
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
z-index: 10;
}
header h1 {
margin: 0;
font-size: 1.2rem;
color: var(--accent);
}
select {
background-color: var(--bg-color);
color: var(--text-main);
border: 1px solid var(--border);
padding: 8px 12px;
border-radius: 6px;
outline: none;
font-size: 0.9rem;
}
#chat-container {
flex-grow: 1;
overflow-y: auto;
padding: 20px;
display: flex;
flex-direction: column;
gap: 15px;
scroll-behavior: smooth;
}
.message {
max-width: 85%;
padding: 12px 16px;
border-radius: 16px;
font-size: 1rem;
line-height: 1.5;
word-wrap: break-word;
animation: fadeIn 0.3s ease-out;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.user {
background-color: var(--user-msg);
color: white;
align-self: flex-end;
border-bottom-right-radius: 4px;
}
.bot {
background-color: var(--bot-msg);
color: var(--text-main);
align-self: flex-start;
border-bottom-left-radius: 4px;
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
}
.error {
background-color: #ef4444;
color: white;
align-self: center;
font-size: 0.85rem;
}
.typing-indicator {
display: inline-flex;
gap: 4px;
align-items: center;
height: 20px;
}
.typing-indicator span {
width: 6px;
height: 6px;
background-color: var(--text-muted);
border-radius: 50%;
animation: bounce 1.4s infinite ease-in-out both;
}
.typing-indicator span:nth-child(1) { animation-delay: -0.32s; }
.typing-indicator span:nth-child(2) { animation-delay: -0.16s; }
@keyframes bounce {
0%, 80%, 100% { transform: scale(0); }
40% { transform: scale(1); }
}
#input-area {
background-color: var(--chat-bg);
padding: 15px;
border-top: 1px solid var(--border);
display: flex;
gap: 10px;
align-items: flex-end;
}
textarea {
flex-grow: 1;
background-color: var(--bg-color);
color: var(--text-main);
border: 1px solid var(--border);
border-radius: 12px;
padding: 12px;
font-size: 1rem;
resize: none;
outline: none;
min-height: 24px;
max-height: 120px;
font-family: inherit;
transition: border-color 0.2s;
}
textarea:focus {
border-color: var(--accent);
}
button {
background-color: var(--accent);
color: #000;
border: none;
border-radius: 12px;
padding: 0 20px;
height: 50px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: opacity 0.2s, transform 0.1s;
display: flex;
align-items: center;
justify-content: center;
}
button:active { transform: scale(0.95); }
button:disabled { opacity: 0.5; cursor: not-allowed; transform: none; }
</style>
</head>
<body>
<header>
<h1>Local LLM</h1>
<select id="model-select">
<option value="">Загрузка моделей...</option>
</select>
</header>
<div id="chat-container">
<div class="message bot">Привет! Я готова к общению.</div>
</div>
<div id="input-area">
<textarea id="prompt" rows="1" placeholder="Введите сообщение..."></textarea>
<button id="send-btn">➔</button>
</div>
<script>
const OLLAMA_URL = 'http://127.0.0.1:11434';
const chatContainer = document.getElementById('chat-container');
const promptInput = document.getElementById('prompt');
const sendBtn = document.getElementById('send-btn');
const modelSelect = document.getElementById('model-select');
let chatHistory = []; // Массив для хранения контекста диалога
// Автоматическое изменение высоты поля ввода
promptInput.addEventListener('input', function() {
this.style.height = 'auto';
this.style.height = (this.scrollHeight) + 'px';
});
// Отправка по Enter (без Shift)
promptInput.addEventListener('keydown', function(e) {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
sendMessage();
}
});
sendBtn.addEventListener('click', sendMessage);
// Асинхронная загрузка доступных моделей
async function fetchModels() {
try {
const response = await fetch(`${OLLAMA_URL}/api/tags`);
if (!response.ok) throw new Error('Network error');
const data = await response.json();
modelSelect.innerHTML = '';
if (data.models.length === 0) {
modelSelect.innerHTML = '<option>Нет установленных моделей</option>';
return;
}
data.models.forEach(model => {
const option = document.createElement('option');
option.value = model.name;
option.textContent = model.name;
// Автовыбор gemma3 если она есть
if(model.name.includes('gemma3')) option.selected = true;
modelSelect.appendChild(option);
});
} catch (error) {
console.error('Ошибка загрузки моделей:', error);
modelSelect.innerHTML = '<option value="gemma3">gemma3 (Оффлайн)</option>';
}
}
function createMessageElement(sender) {
const msgDiv = document.createElement('div');
msgDiv.className = `message ${sender}`;
chatContainer.appendChild(msgDiv);
return msgDiv;
}
function scrollToBottom() {
chatContainer.scrollTop = chatContainer.scrollHeight;
}
async function sendMessage() {
const text = promptInput.value.trim();
const selectedModel = modelSelect.value;
if (!text || !selectedModel) return;
// Блокируем ввод
promptInput.value = '';
promptInput.style.height = 'auto';
promptInput.disabled = true;
sendBtn.disabled = true;
// Добавляем сообщение пользователя
const userMsgDiv = createMessageElement('user');
userMsgDiv.textContent = text;
chatHistory.push({ role: 'user', content: text });
scrollToBottom();
// Создаем блок для ответа бота с индикатором печати
const botMsgDiv = createMessageElement('bot');
botMsgDiv.innerHTML = '<div class="typing-indicator"><span></span><span></span><span></span></div>';
scrollToBottom();
try {
const response = await fetch(`${OLLAMA_URL}/api/chat`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
model: selectedModel,
messages: chatHistory,
stream: true
})
});
if (!response.ok) throw new Error(`HTTP Error: ${response.status}`);
// Очищаем индикатор загрузки
botMsgDiv.innerHTML = '';
let fullResponse = '';
// Асинхронное чтение потока
const reader = response.body.getReader();
const decoder = new TextDecoder('utf-8');
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value, { stream: true });
const lines = chunk.split('n').filter(line => line.trim() !== '');
for (const line of lines) {
const json = JSON.parse(line);
if (json.message && json.message.content) {
fullResponse += json.message.content;
// Простая замена переносов строк для HTML
botMsgDiv.innerHTML = fullResponse.replace(/n/g, '<br>');
scrollToBottom();
}
}
}
// Сохраняем ответ в историю
chatHistory.push({ role: 'assistant', content: fullResponse });
} catch (error) {
console.error(error);
botMsgDiv.className = 'message error';
botMsgDiv.textContent = 'Ошибка подключения. Проверьте OLLAMA_ORIGINS="*" и запущен ли сервер.';
} finally {
// Разблокируем ввод
promptInput.disabled = false;
sendBtn.disabled = false;
promptInput.focus();
scrollToBottom();
}
}
// Инициализация
window.onload = fetchModels;
</script>
</body>
</html>
Вставляем код из вложения (также можно взять на моём GitHub BlackJackBander/Ollama-WebUI: Ollama-WebUI interface for works from Browser on smartphone [4]).
🔑 Ключевые фрагменты кода:
Отправка запроса к Ollama:
// Асинхронная загрузка доступных моделей
async function fetchModels() {
try {
const response = await fetch(`${OLLAMA_URL}/api/tags`);
if (!response.ok) throw new Error('Network error');
const data = await response.json();
Обработка потокового ответа (текст появляется постепенно):
// Асинхронное чтение потока
const reader = response.body.getReader();
const decoder = new TextDecoder('utf-8');
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value, { stream: true });
const lines = chunk.split('n').filter(line => line.trim() !== '');
for (const line of lines) {
const json = JSON.parse(line);
if (json.message && json.message.content) {
fullResponse += json.message.content;
// Простая замена переносов строк для HTML
botMsgDiv.innerHTML = fullResponse.replace(/n/g, '<br>');
scrollToBottom();
}
}
}
nginx -t
nginx
Если ничего не пишет, проверяем работает ли сервис и если нет – запускаем:
sv status nginx
sv up nginx
Открываем своё любимый браузер. Лично мой – Firefox за возможность отрезания рекламы везде где можно и широкой кастомизации:
В адресной строке браузера указываем адрес для доступа:
http://<ip адрес вашего устройства>:8080/chat.html
WebUI сам подхватит текущую запущенную модель.
Если всё ОК – будет выглядеть так:
Проблема 1: CORS ошибка [5] в браузере
Решение: Убедитесь, что Ollama запущен с OLLAMA_ORIGINS="*".
Проблема 2: Ошибка 405 при curl
Решение: Используйте -X POST.
Проблема 3: Порт уже занят
Решение: pkill -9 ollama или pkill -9 nginx.
Проблема 4: Termux убивает процесс в фоне
Решение: Отключите оптимизацию батареи для Termux в настройках телефона.
Я выбрал gemma3:1b потому что только она поместилась на моё устройство.
|
Модель |
Параметры |
Требования к ОЗУ |
|---|---|---|
|
|
1.5 млрд |
3+ ГБ |
|
|
4 млрд |
6+ ГБ |
|
|
1.5 млрд |
3+ ГБ |
du -sh ~/.ollama/models/ # проверить занятое место
ollama rm <имя_модели> # удалить модель
Теперь у вас есть собственный локальный ИИ-ассистент в телефоне и вы уже обошли Apple!
Вы можете:
Общаться с ним в дороге (даже в самолёте)
Использовать как офлайн-помощника для работы с текстом
Ставить другие модели (Mistral, Phi-3, CodeLlama)
Интегрировать его в свои приложения через API
ollama list # список установленных моделей
ollama pull mistral:7b # скачать другую модель
ollama rm gemma3:1b # удалить модель
ollama show gemma3:1b # информация о модели
ollama ps # Выведет информацию о запущенной модели
🔒 P.S. Насколько это безопасно?
Вся обработка данных происходит локально на вашем устройстве. Ollama не отправляет ваши запросы никуда. Вы можете выключить интернет, и всё будет работать. Ваши диалоги остаются только у вас.
Подписывайтесь на мой канал в VC.ru [6] и Telegram [7]
Будьте осторожны! Статья сгенерирована человеком :-D
Созданное решение это больше демонстрация, чем полноценное использование. Например, я не стал писать сохранение сессий чатов- это уже отдельная история.
Автор: BlackJackBander
Источник [8]
Сайт-источник BrainTools: https://www.braintools.ru
Путь до страницы источника: https://www.braintools.ru/article/28565
URLs in this post:
[1] Termux Wiki: https://wiki.termux.com/wiki/Main_Page
[2] Finite field – Wikipedia: https://en.wikipedia.org/wiki/Finite_field
[3] интеллекта: http://www.braintools.ru/article/7605
[4] BlackJackBander/Ollama-WebUI: Ollama-WebUI interface for works from Browser on smartphone: https://github.com/BlackJackBander/Ollama-WebUI
[5] ошибка: http://www.braintools.ru/article/4192
[6] VC.ru: http://VC.ru
[7] Telegram: https://api.vc.ru/v2.8/redirect?to=https%3A%2F%2Ft.me%2FDrunkenPin&postId=2811898
[8] Источник: https://habr.com/ru/articles/1021610/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1021610
Нажмите здесь для печати.