ВведениеДолгое время меня мучал вопрос - возможно ли запустить ИИшку у себя на телефоне, и если да, то какую. Я уверен, что об этом думали многие, но не понималВведениеДолгое время меня мучал вопрос - возможно ли запустить ИИшку у себя на телефоне, и если да, то какую. Я уверен, что об этом думали многие, но не понимал

Запускаем LLM на iPhone локально — мой опыт с Gemma 2B

Введение

Долгое время меня мучал вопрос - возможно ли запустить ИИшку у себя на телефоне, и если да, то какую. Я уверен, что об этом думали многие, но не понимали смысла, зачем тратить время на такого рода занятия. Чтож, я не выдержал и сделал мини-приложение, которое запускает Qwen / Gemma модель и общается с вами без доступа в интернет.

Задачи минимум:
1. Развернуть модель ИИ у себя на iPhone
2. Навайбкодить приложение, где можно общаться ИИ без доступа в интернет
3. Замерить потребление ресурсов моего iPhone во время работы с приложением

Что получилось в итоге

iPhoneLLM — приложение-чат, где вместо облачного ИИ отвечает модель Gemma 2B, работающая прямо на iPhone.

Характеристики:

  • Полный офлайн — Network = 0 KB/s (проверено в Xcode)

  • Скорость — 8-25 токенов/сек в зависимости от iPhone

  • Память — ~356 MB RAM

  • Модель — Gemma 2B Q4_K_M (1.5 ГБ)

Весь код проекта и инструкция для запуска: https://github.com/Chashchin-Dmitry/iPhoneLLM

Часть 1: Подготовка

Что понадобится

Компонент

Требования

Mac

macOS 13 (Ventura) или новее

Xcode

Версия 15+ (бесплатно в App Store, ~25 ГБ)

iPhone

iOS 16+, минимум 4 ГБ RAM (iPhone 12 и новее)

Apple ID

Обычный бесплатный аккаунт

Установка Xcode

Если у вас ещё нет Xcode:

  1. Открываем App Store на Mac

  2. Ищем "Xcode"

  3. Нажимаем "Получить""Установить"

  4. Ждём загрузки (~25 ГБ, может занять 30-60 минут)

  5. После установки запускаем Xcode и принимаем лицензионное соглашение

Часть 1. Скачиваем Xcode
Часть 1. Скачиваем Xcode

Часть 2: Создание проекта

Новый проект в Xcode

  1. File → New → Project (или Cmd+Shift+N)

  2. Выбираем iOS → App

  3. Нажимаем Next

Настройки проекта

  • Product Name: LocalChat

  • Team: Ваш Apple ID (Personal Team)

  • Organization Identifier: любой (например com.yourname)

  • Interface: SwiftUI

  • Language: Swift

Часть 2. Создаем проект
Часть 2. Создаем проект
Часть 2. Создаем проект
Часть 2. Создаем проект
Часть 2. Создаем проект
Часть 2. Создаем проект

Часть 3: Добавление библиотеки LLM.swift

Для работы с языковыми моделями используем библиотеку LLM.swift — это Swift-обёртка над llama.cpp.

Добавляем пакет

  1. В Xcode: File → Add Package Dependencies...

  2. В поле поиска вставляем URL:

https://github.com/eastriverlee/LLM.swift

  1. Нажимаем Enter и ждём загрузки

  2. Убеждаемся что в "Add to Target" выбран LocalChat

  3. Нажимаем Add Package

Часть 3. Добавление библиотеки LLM.swift
Часть 3. Добавление библиотеки LLM.swift

Инцидент #1: "No such module 'LLM'"

После добавления пакета при попытке сборки получаем ошибку:

No such module 'LLM'

Причина: Пакет добавлен в проект, но не подключен к target.

Решение:

  1. Нажимаем на проект LocalChat (синяя иконка)

  2. Выбираем Targets → LocalChat

  3. Переходим на вкладку Build Phases

  4. Раскрываем "Link Binary With Libraries"

  5. Нажимаем +

  6. Находим LLM (под LLM Package)

  7. Нажимаем Add

Инцидент #1: "No such module 'LLM'" - решение.
Инцидент #1: "No such module 'LLM'" - решение.

Часть 4: Пишем код

Скорее не пишем, а клонируем мой написанный проект на GitHub. Напомню, вот ссылка - https://github.com/Chashchin-Dmitry/iPhoneLLM

Структура файлов

LocalChat/ ├── LocalChatApp.swift # Точка входа (не меняем) ├── ContentView.swift # Обновляем ├── Message.swift # Создаём ├── ChatViewModel.swift # Создаём └── ChatView.swift # Создаём

Message.swift — модель сообщения

import Foundation struct Message: Identifiable, Equatable { let id = UUID() let content: String let isUser: Bool let timestamp: Date init(content: String, isUser: Bool) { self.content = content self.isUser = isUser self.timestamp = Date() } }

ChatViewModel.swift — логика работы с LLM

import Foundation import SwiftUI import LLM @MainActor class ChatViewModel: ObservableObject { @Published var messages: [Message] = [] @Published var inputText: String = "" @Published var isLoading: Bool = false @Published var isModelLoaded: Bool = false @Published var loadingStatus: String = "Загрузка модели..." @Published var currentResponse: String = "" private var bot: LLM? init() { Task { await loadModel() } } private func loadModel() async { loadingStatus = "Загружаю Gemma 2B..." guard let modelURL = Bundle.main.url( forResource: "gemma-2-2b-it-Q4_K_M", withExtension: "gguf" ) else { loadingStatus = "Модель не найдена в Bundle!" return } bot = LLM(from: modelURL, template: .gemma) isModelLoaded = true loadingStatus = "Готов к общению!" let welcome = Message( content: "Привет! Я локальная нейросеть Gemma 2B. Работаю прямо на твоём iPhone, без интернета.", isUser: false ) messages.append(welcome) } func sendMessage() async { let text = inputText.trimmingCharacters(in: .whitespacesAndNewlines) guard !text.isEmpty, let bot = bot, !isLoading else { return } let userMessage = Message(content: text, isUser: true) messages.append(userMessage) inputText = "" isLoading = true currentResponse = "" let botMessage = Message(content: "", isUser: false) messages.append(botMessage) let responseIndex = messages.count - 1 bot.update = { [weak self] _ in Task { @MainActor in guard let self = self else { return } self.currentResponse = bot.output self.messages[responseIndex] = Message( content: self.currentResponse, isUser: false ) } } await bot.respond(to: text) messages[responseIndex] = Message(content: bot.output, isUser: false) isLoading = false } func clearChat() { messages.removeAll() bot?.stop() if isModelLoaded { let welcome = Message(content: "Чат очищен. Начнём сначала!", isUser: false) messages.append(welcome) } } }

ChatView.swift — интерфейс чата

Полный код интерфейса (около 300 строк) доступен в репозитории. Основные компоненты:

  • LoadingView — экран загрузки модели

  • MessageListView — список сообщений со скроллом

  • MessageBubble — пузырь сообщения (разные стили для пользователя и бота)

  • TypingIndicator — анимация "печатает..."

  • InputBarView — поле ввода с кнопкой отправки

ContentView.swift — точка входа

import SwiftUI struct ContentView: View { var body: some View { ChatView() } }

Часть 5: Добавление модели

Скачивание модели

Модель Gemma 2B в формате GGUF (~1.5 ГБ):

Ссылка: gemma-2-2b-it-Q4_K_M.gguf

Добавление в проект

  1. Скачиваем файл .gguf

  2. Перетаскиваем в Xcode в папку LocalChat

  3. В диалоге ставим галочки:

    • Copy items if needed

    • Add to target: LocalChat

  4. Нажимаем Finish

Часть 5. Добавление модели
Часть 5. Добавление модели

Инцидент #2: "Модель не найдена в Bundle"

Если приложение показывает "Модель не найдена":

  1. Выбираем файл .gguf в левой панели Xcode

  2. В правой панели (File Inspector)

  3. Проверяем Target Membership → галочка на LocalChat

Часть 6: Настройка подписи

Добавляем Apple ID в Xcode

  1. Xcode → Settings (или Cmd+,)

  2. Вкладка Accounts

  3. Нажимаем +Apple ID

  4. Входим своим обычным Apple ID

Настраиваем подпись приложения

  1. В левой панели нажимаем на LocalChat (синяя иконка проекта)

  2. Выбираем Targets → LocalChat

  3. Вкладка Signing & Capabilities

  4. Ставим галочку "Automatically manage signing"

  5. В поле Team выбираем свой Apple ID

Инцидент #3: "Signing requires a development team"

Если появляется эта ошибка — значит не выбран Team. Решение выше.

Часть 7: Подготовка iPhone

Подключение

  1. Подключаем iPhone к Mac кабелем USB/USB-C

  2. Разблокируем iPhone

  3. При запросе "Доверять этому компьютеру?" — нажимаем Доверять

Инцидент #4: "Waiting to reconnect to iPhone"

Если Xcode не видит iPhone:

  1. Отключаем и подключаем кабель

  2. Разблокируем iPhone

  3. Пробуем другой USB-порт

  4. Перезапускаем Xcode

Включаем Developer Mode

Важно! Начиная с iOS 16, Apple требует включения режима разработчика.

На iPhone:

  1. Настройки → Конфиденциальность и безопасность

  2. Прокручиваем в самый низ

  3. Находим "Режим разработчика" (Developer Mode)

  4. Включаем переключатель

  5. Нажимаем "Перезагрузить"

  6. После перезагрузки нажимаем "Включить"

  7. Вводим пароль iPhone

Часть 7. Подготовка iPhone. Включаем Developer Mode
Часть 7. Подготовка iPhone. Включаем Developer Mode

Инцидент #5: "Developer Mode disabled"

Ошибка при запуске:

Developer Mode disabled To use iPhone for development, enable Developer Mode in Settings → Privacy & Security.

Решение: Включить режим разработчика (инструкция выше).

Часть 8: Запуск

Выбираем устройство

В верхней панели Xcode, рядом с кнопкой ▶️:

  1. Нажимаем на выпадающий список

  2. Выбираем свой iPhone (не симулятор!)

Запускаем

  1. Нажимаем ▶️ Run (или Cmd+R)

  2. Ждём компиляции (первый раз 1-2 минуты)

  3. Приложение устанавливается на iPhone

Часть 8. Запуск
Часть 8. Запуск

Инцидент #6: "Untrusted Developer"

При первом запуске iOS показывает "Ненадёжный разработчик".

На iPhone:

  1. Настройки → Основные

  2. VPN и управление устройством

  3. Находим свой Apple ID / email

  4. Нажимаем "Доверять"

После этого возвращаемся в Xcode и нажимаем Run снова.

Часть 9: Результаты

Время запустить приложение и посмотреть его работоспособность. Вот как оно выглядит на iPhone.

Часть 9: Результаты. Как выглядит приложение на рабочем столе iPhone?
Часть 9: Результаты. Как выглядит приложение на рабочем столе iPhone?

Заходим и делаем первые запросы:

Часть 9: Результаты. Экран загрузки.
Часть 9: Результаты. Экран загрузки.

Первый запрос в наш локальный ИИ на iPhone:

Часть 9: Результаты. Первый запрос-ответ
Часть 9: Результаты. Первый запрос-ответ

Замеры производительности

Использовал Debug Navigator в Xcode (Cmd+7) для мониторинга:

Во время генерации ответа:

Метрика

Значение

CPU

65%

Memory

355.9 MB

Energy Impact

Very High

Disk

400 KB/s

Network

0 KB/s

Часть 9: Результаты. Замеры производительности во время генерации ответа.
Часть 9: Результаты. Замеры производительности во время генерации ответа.

В состоянии покоя:

Метрика

Значение

CPU

0%

Memory

355.2 MB

Energy Impact

High

Disk

0 KB/s

Network

0 KB/s

Часть 9: Результаты. Замеры производительности в состоянии покоя.
Часть 9: Результаты. Замеры производительности в состоянии покоя.

Ключевые выводы

  1. Network = 0 — подтверждает полный офлайн, никаких данных не отправляется

  2. CPU 0% в покое — не разряжает батарею когда не используется

  3. ~356 MB RAM — стабильное потребление памяти

  4. После закрытия приложения — вся память освобождается

Скорость генерации

iPhone

Токенов/сек

iPhone 12

~8

iPhone 13

~10

iPhone 14

~15

iPhone 15 Pro

~25

Часть 10: Доработки

Скрытие клавиатуры по тапу

Добавил .onTapGesture для скрытия клавиатуры при нажатии на чат.

Парсинг Markdown

Добавил поддержку базового Markdown в ответах через AttributedString:

private func parseMarkdown(_ text: String) -> AttributedString { do { return try AttributedString( markdown: text, options: AttributedString.MarkdownParsingOptions( interpretedSyntax: .inlineOnlyPreservingWhitespace ) ) } catch { return AttributedString(text) } }

Альтернативные модели

Можно использовать другие GGUF модели:

Модель

Размер

RAM

Качество

Скорость

Llama 3.2 1B

0.7 ГБ

2 ГБ

Базовое

Быстрая

Gemma 2B Q4_K_S

1.3 ГБ

3 ГБ

Хорошее

Средняя

Gemma 2B Q4_K_M

1.5 ГБ

4 ГБ

Хорошее

Средняя

Phi-3 Mini

2.2 ГБ

5 ГБ

Отличное

Медленнее

Скачать можно на HuggingFace.

Возможные улучшения

Я бы докрутил в приложении следующий функционал в будущем:

  1. Добавить голосовой ввод

  2. Сохранение истории чатов

  3. Настройка системного промпта (чтобы модель не писала бред или в меньшем количестве)

Заключение

Ну что, мы добились цели. Теперь у нас есть карманный ИИ чат, которого можно мучать в самолете, в поезде и где вам только еще придет в голову его включить.

Моя основная цель была посмотреть, возможно ли вообще запустить LLM у себе на смартфоне и если да, то какое потребление ресурсов будет у iPhone при развертывании любой модели ИИ.

Надеюсь, я закрыл гештальт многим, кто думал, но не решался запустить ИИшку у себя на телефоне.


Буду рад за лайк и подписку на канал :) https://t.me/notes_from_cto


Источник

Возможности рынка
Логотип Large Language Model
Large Language Model Курс (LLM)
$0.0003457
$0.0003457$0.0003457
+4.69%
USD
График цены Large Language Model (LLM) в реальном времени
Отказ от ответственности: Статьи, размещенные на этом веб-сайте, взяты из общедоступных источников и предоставляются исключительно в информационных целях. Они не обязательно отражают точку зрения MEXC. Все права принадлежат первоисточникам. Если вы считаете, что какой-либо контент нарушает права третьих лиц, пожалуйста, обратитесь по адресу service@support.mexc.com для его удаления. MEXC не дает никаких гарантий в отношении точности, полноты или своевременности контента и не несет ответственности за любые действия, предпринятые на основе предоставленной информации. Контент не является финансовой, юридической или иной профессиональной консультацией и не должен рассматриваться как рекомендация или одобрение со стороны MEXC.