HomeLab

Часть 1/2.

15 мая 2026, 07:51
Источник: noted.lol
Часть 1/2.

Как я построил голосовой AI-зеркало, которое вы можете запустить дома

Источник: Noted.lol


Написано автором проекта MirrorMate, который движим любопытством и креативностью.
Я хотел, чтобы помощник ощущался как присутствие. Что-то, что находится в том же физическом пространстве, что и я. Полузеркало оказалось идеальным интерфейсом для этого.

Демонстрация (видео)

Примечание: демонстрационное видео без звука. Прогулка с озвучиванием и более подробная информация доступны в README.
0:00
/0:22

Демонстрация MirrorMate

Что вы получите из этого поста

  • Почему MirrorMate является «голосовым» (и почему это важно для полузеркала)
  • Два практических развертывания: легкий облачный режим против полностью локального режима
  • Как работает конфигурируемый дизайн (провайдеры, RAG-память, плагины)

Введение

Я использовал умное зеркало в течение некоторого времени (в стиле MagicMirror), но «эра AI» наконец-то сделала взаимодействие настолько естественным, что я захотел перестроить его вокруг разговора: будильник → разговор → зеркало отвечает.
Это стало MirrorMate: личным помощником, который живет за полузеркалом.
Утром я просто говорю «Эй, Мира» во время чистки зубов.
Она говорит мне погоду на сегодня, мою первую встречу и стоит ли мне взять зонт.
С этого момента это перестало ощущаться как демонстрация и стало полезным.

Что такое MirrorMate (почему это весело)

MirrorMate — это личный помощник, который живет за полузеркалом.
Вы разговариваете с ним, и он отвечает — как разговор, встроенный в вашу повседневную жизнь.
С технической точки зрения, это саморазмещенное приложение, разработанное для полузеркала + дисплея.
Если вы когда-либо думали: «Я хочу свой собственный Alexa, но мой» или «дайте мне маленького JARVIS», то это то, что вам нужно.

Основные функции

  • Голосовой интерфейс: будильник «Эй, Мира»
  • Полностью локальный вариант: Ollama + VOICEVOX, облако не требуется
  • RAG-память: извлекает и хранит личные факты, извлекает их по мере необходимости
  • Многообразие провайдеров: OpenAI/Ollama, Web Speech/Whisper/faster-whisper, VOICEVOX/OpenAI TTS
  • Система плагинов: добавляйте виджеты (часы и т. д.) без изменения ядра
  • Предустановки локализации: меняйте язык и автоматически получайте региональные настройки

Если вы хотите самый быстрый путь к работающему зеркалу: начните с минимальной облачной настройки сначала, а затем переходите на полностью локальную после того, как UX станет правильным.

Модель стоимости (практический взгляд)

  • Минимальная (Pi + OpenAI): низкие начальные затраты, но вы платите за использование
  • Полностью локальная (Pi + сервер вывода): более высокие начальные затраты, но повторяющиеся затраты составляют примерно $0/месяц (исключая электричество)

Аппаратное обеспечение

MirrorMate — это программное обеспечение, но чтобы сделать его зеркалом, вам нужно стандартное оборудование для умного зеркала. Я не буду повторно объяснять все сборки (существует множество руководств по MagicMirror); вот детали, которые имели значение для меня.

Примерный список покупок

  • Полузеркало (двустороннее зеркало)
  • Дисплей (HDMI — самый простой)
  • Raspberry Pi (для интерфейса киоска) + блок питания
  • Микрофон + динамики (USB-аудио работает нормально)
  • (Необязательно) Камера (для Vision Companion)
  • Деревянная рамка / крепления / краска

Ссылочное видео (атмосфера сборки): https://youtu.be/LTuvAoSJZDY?si=Ylj8iAy0gJ90LU6T
Единственный совет, который я бы повторил: выберите дисплей первым, затем закажите полузеркало, чтобы оно соответствовало внешним размерам. Это делает конечный результат более «реальным».
Для справки, мой заказ на полузеркало (Япония, индивидуальный размер):

| Тип: Magic Mirror 3 мм (стекло) 10% пропускания
| Форма: Прямоугольник
| Размер: 255 мм (Ш) × 432 мм (В)
| Обработка краев: C-резка (с фаской)
| Цена единицы: ¥6,857
| Количество: 1
| Промежуточный итог: ¥6,857
|-------------------
| Доставка: ¥950
|-------------------
| Итого: ¥7,807
| Налог: ¥780
| ━━━━━━━━━━━
| Общая сумма: ¥8,587
| ━━━━━━━━━━━

Сейчас я использую Raspberry Pi 3 Model B+. В моей настройке Pi не запускает тяжелые AI-нагрузки — он в основном отвечает за интерфейс/аудио.

Фотографии (рама → краска → финал)

Рама (монтаж дисплея)рама

После покраскикраска

Финальное фотофинал

Архитектура

Вот общая картина.

Два режима развертывания

MirrorMate поддерживает две распространенные настройки:
1) Минимальная (Raspberry Pi + OpenAI API)
* Запустите приложение на Pi
* Используйте OpenAI для LLM/TTS/STT
* Быстро начать, но основано на затратах на использование

2) Полностью локальная (Raspberry Pi + более мощная машина)
* Запустите тяжелые сервисы в другом месте (LLM/TTS/STT/встраивания)
* Используйте локальные Ollama, VOICEVOX, faster-whisper и т. д.
* Более высокие начальные затраты, но без зависимости от облака

Я использую полностью локальный вариант.
Ключевое дизайнерское решение - держать Raspberry Pi тонким.
Он отвечает только за интерфейс и аудио I/O — все тяжелое находится в другом месте.
Это делает зеркало отзывчивым, тихим и простым в обслуживании.

┌─────────────────────────────────────────────────────────────────────┐
│                        Raspberry Pi                                 │
│  ┌───────────────┐  ┌─────────────┐  ┌────────────────────────────┐ │
│  │  Браузер      │  │  Next.js 15 │  │  SQLite + Drizzle ORM      │ │
│  │  (Chromium)   │◄─┤  Приложение  │◄─┤  - воспоминания (RAG)    │ │
│  │  + Микр./Кам  │  │  Порт 3000  │  │  - сессии                │ │
│  │  + MediaPipe  │  │             │  │  - настройки пользователя  │ │
│  └───────────────┘  └──────┬──────┘  └────────────────────────────┘ │
│         ▲                  │                                        │
│         │                  │ VPN Tailscale                        │
└─────────┼──────────────────┼────────────────────────────────────────┘
          │                  ▼
          │   ┌───────────────────────────────────────────────────────┐
          │   │                    Сервер вывода                     │
          │   │  ┌────────────┐  ┌───────────┐  ┌───────────────────┐ │
          │   │  │  Ollama    │  │ VOICEVOX  │  │  faster-whisper   │ │
          │   │  │  - LLM     │  │  TTS      │  │  STT              │ │
          │   │  │  - VLM     │  │  :50021   │  │  :8080            │ │
          │   │  │  :11434    │  │           │  │                   │ │
          │   │  └────────────┘  └───────────┘  └───────────────────┘ │
          │   │  ┌─────────────────────────────────────────────────┐  │
          │   │  │  PLaMo-Embedding-1B (Сервер встраиваний):8000   │  │
          │   │  └─────────────────────────────────────────────────┘  │
          │   └───────────────────────────────────────────────────────┘
          │
          └── Полузеркало + Монитор

Если ваш Pi и сервер вывода находятся в разных сетях, что-то вроде Tailscale упрощает процесс — особенно если вы хотите сохранить все в частном режиме и не в публичном интернете.

Технологический стек

Категория Технология
Фронтенд Next.js 15, React 19, Three.js
Бэкенд Node.js, SQLite (Drizzle ORM)
LLM Ollama (например, gpt-oss:20b), OpenAI
TTS VOICEVOX (Япония), OpenAI TTS
STT Web Speech API, OpenAI Whisper, faster-whisper
Встраивание PLaMo-Embedding-1B (через Ollama)
VLM Ollama (llava и т. д.)
Инфраструктура Docker, Tailscale

Примечания по программному обеспечению

Советы по интерфейсу для зеркала

Полупрозрачные зеркала лучше всего выглядят, когда вы «освещаете» только то, что важно. Я оставляю фон чисто черным (#000) и показываю только текст/значки. Для аватара я намеренно упростил его — слишком детализированные дизайны, как правило, выглядят ненадежно или ломаются в анимации.

┌────────────────────────────────────────────────────┐
│                                                    │
│   ┌──────────┐                                     │
│   │ 10:30 AM │  ← Плагин часов (вверху слева)      │
│   │ Jan 18   │                                     │
│   └──────────┘                                     │
│                                                    │
│                      ╭───╮                         │
│                     ( ◠‿◠ )  ← Аватар (в центре)   │
│                      ╰───╯                         │
│                                                    │
│   ┌────────────────────────────────────────────┐   │
│   │ "Доброе утро! Сегодня солнечно..."          │   │
│   └────────────────────────────────────────────┘   │
│           ↑ Текст ответа (внизу)                  │
│                                                    │
│   Фон: Чисто черный (#000000)                     │
└────────────────────────────────────────────────────┘

Поток анимации (упрощенный):

IDLE → LISTENING → THINKING → SPEAKING → LINGERING → IDLE

Конфигурация на основе YAML (почему это важно)

Я хотел поменять LLM, TTS и STT, как Lego — не трогая код приложения. MirrorMate основан на конфигурации. Вы можете менять провайдеров, не заходя в код.
config/app.yaml:

app:
  locale: "ja" # или "en"

config/providers.yaml (пример):

providers:
  llm:
    provider: ollama # openai или ollama
    ollama:
      model: gpt-oss:20b
      baseUrl: "http://studio:11434"

  tts:
    provider: voicevox # openai или voicevox
    voicevox:
      speaker: 2
      baseUrl: "http://studio:50021"

  stt:
    provider: web # openai, local или web

  embedding:
    provider: ollama
    ollama:
      model: plamo-embedding-1b

Память на основе RAG («личная» часть)

Это функция, которая меня больше всего интересовала: ассистент должен помнить вещи (осторожно). Три типа памяти:
* Профиль : долговременные предпочтения/черты (например, «любит кофе», «ранняя птица»)
* Эпизод : недавние события (например, «вчера смотрел фильм», «командировка на следующей неделе»)
* Знания : фактические заметки (например, «Срок проекта X — конец января»)

Система извлекает память из разговоров, хранит встраивания и извлекает соответствующие элементы на следующем шаге.

providers:
  memory:
    enabled: true
    rag:
      topK: 8
      threshold: 0.3
    extraction:
      autoExtract: true
      minConfidence: 0.5

Предустановки локали

Изменение app.locale обновляет множество настроек одновременно.
Когда ja:
* Часовой пояс: Asia/Tokyo
* Место для прогноза погоды: Токио
* Формат времени: 24 часа
* Язык STT: ja-JP

Когда en:
* Часовой пояс: America/Los_Angeles
* Место для прогноза погоды: Сан-Франциско
* Формат времени: 12 часов
* Язык STT: en-US

Конфигурация персонажа

Личность ассистента также определяется в YAML:

character:
  name: "Mira"
  description: "Дружелюбный зеркальный ассистент"
  personality:
    - "Добрый и теплый"
    - "Любопытный"
  speech_style:
    - "Непринужденный и доступный"
    - "Сохранять ответы краткими"
  background: |
    Вы — ИИ-ассистент, живущий в зеркале.
    Помогайте пользователю в повседневной жизни.

Движок правил

Вы можете определить рабочие процессы, срабатывающие по ключевым словам:

rules:
  morning_greeting:
    description: Подводить итоги дня при утреннем приветствии
    triggers:
      keywords:
        - おはよう
        - good morning
    actions:
      - module: time
      - module: weather
      - module: calendar
    response_hint: |
      Объясните следующее дружелюбно:
      - Погода сегодня
      - Расписание на сегодня

Плагины

Плагины позволяют вам расширять функционал, не перегружая основное приложение.
Плагин часов:

plugins:
  clock:
    source: github:orangekame3/mirrormate-clock-plugin
    enabled: true
    position: top-left
    config:
      showSeconds: false
      showDate: true

Vision Companion (на основе камеры):

plugins:
  vision-companion:
    source: local:vision-companion
    enabled: true
    position: hidden

Встроенные интеграции

  • Погода (Open-Meteo)
  • Календарь (Google Calendar)
  • Веб-поиск (Tavily)
  • Напоминания (опрос)
  • Дополнительное деление через Discord

По моему опыту, как только ассистент получает ваш «ежедневный контекст» (календарь, погода, напоминания), он перестает быть демонстрацией и начинает ощущаться как что-то, что вы действительно используете.

Настройка (быстрый путь)

Если вы просто хотите быстро увидеть, как это работает (Pi + OpenAI):

  • Установите llm/stt/tts на openai в config/providers.yaml
  • Добавьте свои API ключи в .env
  • Упростите задачу для Pi: отображение интерфейса и работа с аудио

Клонировать

git clone https://github.com/orangekame3/mirrormate.git
cd mirrormate

Установить зависимости

bun install

Настроить окружение

cp .env.example .env

Запустить

bun dev

Docker:

docker compose up -d

Перейти на полностью локальный

  • Запустите Ollama / VOICEVOX / faster-whisper / сервер встраивания на отдельной машине
  • Укажите Pi на эти конечные точки через HTTP
  • Если сети различаются, подключите через Tailscale (избегайте раскрытия служб публично)

Заключение

Создание MirrorMate заставило меня осознать, насколько иначе ощущается ассистент, когда он физически присутствует в вашем пространстве — не просто еще одно приложение или динамик. Если вам нравится строить вещи, которые живут в вашем окружении, а не только на вашем экране, вам может понравиться MirrorMate. Если это звучит интересно, взгляните на репозиторий:
https://github.com/orangekame3/mirrormate