AAYMA
RU
Войти

HackAlem Minutes

Локальный помощник секретаря: распознаёт запись совещания (каз./рус.), делит по говорящим, извлекает поручения, решения и саммари с дословными…

Доведённый продукт
Команда «Hot-team» 20💬 0 комментариевс 23 сентября 2026 г.

О проекте

Команда: Hot-team
Кейс HackAlem.ai: Инновации — Самрук-Казына
Стадия: Доведённый продукт

Задача кейса: «Хаттама»: протокол и поручения по аудиозаписи совещания (каз./рус./смешанная речь)

Суть

Локальный помощник секретаря: распознаёт запись совещания (каз./рус.), делит по говорящим, извлекает поручения, решения и саммари с дословными цитатами, после подтверждения экспортирует протокол в DOCX/PDF/JSON.

Как устроено

ASR через Whisper large-v3-turbo (MLX на Mac, faster-whisper в Docker) или казахскую Sybyrla; диаризация pyannote Community-1 с выравниванием по границам слов; анализ поручений и тем через локальную Qwen3 (MLX или Ollama), каждое поручение подтверждается цитатой. Человек редактирует ответственных, сроки и статусы; есть канбан поручений, голосовые профили, опциональные Telegram-уведомления, хранение в SQLite.

Ключевые решения

  • Whisper large-v3-turbo
  • faster-whisper
  • MLX
  • pyannote speaker diarization
  • grounded quote extraction
  • two-pass RU/KK comparison
  • voice profiles
  • DOCX/PDF export

Особенности

Полностью локальный контур без передачи текста наружу; два профиля (нативный Apple Silicon и Docker x86/ARM) с закреплёнными по digest образами и хешами зависимостей. Честно описаны ограничения ASR на смешанной и телефонной речи, отдельный бенчмарк Sybyrla.

Стек

Python · MLX · Ollama · Docker · SQLite · JavaScript · Telegram Bot API · uv

Модели

Whisper large-v3-turbo · Sybyrla · pyannote Community-1 · Qwen3-4B-4bit · Ollama


Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-acdb35bd-hot-team.

README

README.md

HackAlem Minutes

Локальный помощник секретаря: аудио/видео → транскрипт с говорящими → поручения и саммари → проверка → PDF/DOCX.

Жюри: с чего начать

  1. Выберите один способ запуска: Docker — Windows, Linux, macOS или нативный Mac Apple Silicon. Не устанавливайте оба профиля без необходимости.
  2. До демонстрации примите условия pyannote, подготовьте Read-токен Hugging Face, скачайте модели и дождитесь успешной диагностики. Эти действия требуют интернета и времени; при первом запуске модели не поставляются с Git-репозиторием.
  3. Для закрытого контура оставьте TELEGRAM_BOT_TOKEN= пустым. Telegram — внешний сервис, отправляющий текст поручений. Он не нужен для основного сценария и несовместим со строгим запретом передачи текста наружу.
  4. Откройте http://127.0.0.1:8000 на том же компьютере, где запущено приложение. Встроенная авторизация отсутствует; публикация в общую сеть не предусмотрена.
  5. Выполните проверку основного сценария сначала на короткой записи. Система готова к демонстрации, когда получены транскрипт, голоса, протокол и открывающиеся DOCX/PDF.
ПрофильМоделиЧто проверено
Нативный Apple SiliconMLX Whisper Turbo по умолчанию; Sybyrla после отдельной подготовки; pyannote; Qwen3 MLXПолный локальный сценарий на Mac M2 16 ГБ; рабочая демонстрация использует Sybyrla
Docker x86_64 / ARM64faster-whisper Turbo, pyannote, Qwen3 через локальный OllamaСборка ARM64 и тесты приложения; полный запуск моделей в контейнере и запуск на других ОС не подтверждены

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

Что работает в MVP

  • Загрузка MP3, WAV, M4A, MP4, WEBM, OGG, FLAC и MOV; до 250 МБ и 60 минут.
  • Запись микрофона в браузере (при поддержке MediaRecorder и разрешении пользователя).
  • Локальное распознавание Whisper: казахский, русский или автоопределение; редактирование текста и прослушивание по таймкодам.
  • Локальная диаризация pyannote Community-1; имена голосов необязательны, явные самопредставления распознаются при анализе.
  • Локальные поручения, решения и саммари через Qwen3. Поручения сопровождаются проверяемой дословной цитатой.
  • Редактирование ответственных, сроков, статусов. Отдельное подтверждение протокола человеком.
  • Экспорт DOCX, PDF (со встроенным шрифтом для русского/казахского) и JSON.
  • История совещаний, общий список поручений, локальное хранение в SQLite и удаление записей.

Это прототип, а не гарантия точности распознавания. Смешанная речь, телефонное качество, имена и числа требуют проверки. В частности, на предоставленном телефонном фрагменте обычный Whisper ошибается. Два прохода ҚАЗ/RU доступны как режим сравнения; надёжного автоматического объединения вариантов нет. Sybyrla используется в демонстрации на Mac, но не является моделью по умолчанию в Docker.

Для жюри — запуск через Docker

Один и тот же контейнер предназначен для Windows (Docker Desktop / WSL2), macOS (Intel / Apple Silicon) и Linux, на процессорах x86_64 или ARM64. Python, FFmpeg, PyTorch и Ollama уже включены в образ; устанавливать их на хост не нужно. Это CPU-профиль: на Mac он медленнее нативного MLX.

Статус проверки Docker (23.09.2026): образ Linux ARM64 собран на Docker Desktop / Mac M2; внутри контейнера прошли 8 тестов API и экспортов. Полная загрузка моделей и обработка записи в Docker не проверены из-за ограниченного места на машине разработчика. Windows, Linux-хост и AMD64 ещё не проверены на реальном оборудовании. Нативный полный ИИ-процесс проверен на Mac M2.

Если на вашем Mac уже работает нативная установка и осталось мало места, используйте её: Docker создаёт отдельные копии зависимостей и моделей.

Что подготовить до запуска

  • Git и работающий Docker Desktop с Linux containers (на Linux — Docker Engine + Compose v2).
  • Рекомендуется 16 ГБ RAM на компьютере, не менее 8 ГБ выделенной памяти Docker и 30 ГБ свободного места на время первой сборки и скачивания моделей. Скорость CPU зависит от оборудования.
  • Интернет для первого скачивания образов и моделей. Последующая обработка выполняется локально.
  • Аккаунт Hugging Face с принятыми условиями pyannote Community-1 и Read-токеном. Это внешнее условие доступа к модели; Docker его не отменяет.

Первое развёртывание

git clone https://github.com/BAITC-Hacks/hack-acdb35bd-hot-team.git
cd hack-acdb35bd-hot-team
docker version
docker compose version

У docker version должны отображаться Client и Server. Если Server недоступен — сначала запустите Docker Desktop.

Скопируйте .env_example в .env:

  • macOS/Linux: cp .env_example .env
  • Windows PowerShell: Copy-Item .env_example .env

Откройте .env в редакторе, заполните HF_TOKEN=... и сохраните. Остальные параметры для контейнера уже заданы в Compose; настройки MLX из .env их не переопределяют.

Далее одинаковые команды на всех системах:

docker compose build app
docker compose run --rm setup
docker compose up -d app

Дождитесь успешного завершения каждой команды. Первая сборка и загрузка весов могут занять несколько минут или дольше в зависимости от соединения. Команда setup должна завершиться сообщением Models and runtime are ready.; ненулевой код выхода означает, что причину нужно исправить, прежде чем запускать приложение.

Откройте http://127.0.0.1:8000. Проверка состояния:

docker compose ps
docker compose logs --tail=80 app

Статус контейнера должен стать healthy. Загрузите короткую запись и пройдите этапы 1 → 2, затем скачайте PDF/DOCX (подробный сценарий ниже).

Проверка моделей без запуска веб-интерфейса

docker compose run --rm app check

Команда использует уже скачанные модели, проверяет локальный Ollama, библиотеки, папку данных и экспорт, затем завершает работу. Успех: Models and runtime are ready.. Не запускайте её параллельно с обработкой длинной записи, чтобы не расходовать память.

Повторный запуск, остановка и данные

docker compose up -d app
docker compose down

Модели и записи хранятся в отдельных Docker volumes и сохраняются после остановки и пересборки. Не используйте docker compose down -v, если хотите сохранить данные. Обновление кода: git pull, docker compose build app, docker compose up -d app. Команду setup повторяют только при изменении моделей или незавершённой загрузке.

Токен используется только контейнером первоначальной подготовки, не передаётся сервису приложения и не встраивается в образ. После успешной подготовки его можно удалить из .env.

Сколько ждать

Первоначальная сборка и скачивание нескольких гигабайт весов зависят от интернета. Обработка не обязана идти быстрее записи: CPU-профиль Docker медленнее MLX. На контрольном транскрипте тематический анализ на M2 занял около 8 минут. Не нажимайте запуск повторно, пока показан текущий этап; начинайте проверку с короткой записи.

Если запуск не удался

СимптомЧто сделать
Cannot connect to the Docker daemonЗапустить Docker Desktop; на Windows выбрать Linux containers и проверить WSL2
Порт 8000 занятДобавить APP_PORT=8001 в .env, повторить docker compose up -d app, открыть порт 8001
GatedRepoError / 401 / 403 при setupПринять условия pyannote под аккаунтом владельца токена; проверить Read-доступ к gated repositories; повторить setup
Модель не подготовлена / контейнер остановилсяПосмотреть docker compose logs --tail=80 app, успешно завершить docker compose run --rm setup
Exit 137 / нехватка памятиУвеличить память Docker; закрыть тяжёлые приложения; начать с записи на 30–60 секунд
Прервалась загрузкаПовторить ту же команду; модели сохраняются в volume, уже скачанные файлы используются повторно

Проверка библиотек и экспортов без скачивания моделей:

docker compose run --rm --no-deps --entrypoint python app -m pytest -q

Образы Python и Ollama закреплены по digest. Python-зависимости CPU-профиля закреплены с хешами в docker/requirements-amd64.txt и docker/requirements-arm64.txt; CUDA-пакеты не устанавливаются. Порт Ollama не публикуется на хост, облачные функции Ollama отключены.

Быстрый запуск на Mac Apple Silicon

Проверенная среда разработки: MacBook Air M2, 16 ГБ RAM, Python 3.12. Модели запускаются последовательно в отдельных процессах, чтобы освобождать память. Для установки нужен интернет и несколько гигабайт места под зависимости и веса (рекомендуется не менее 12 ГБ свободного места). Для обработки после загрузки моделей интернет не нужен.

1. Установите инструменты

Потребуются Git, uv и FFmpeg. Если установлен Homebrew:

brew install uv ffmpeg

2. Получите код и зависимости

git clone https://github.com/BAITC-Hacks/hack-acdb35bd-hot-team.git
cd hack-acdb35bd-hot-team
uv sync --frozen --python 3.12 --extra mac --extra diarization --extra dev
cp .env_example .env

uv.lock фиксирует версии зависимостей. Не копируйте .env в репозиторий и не публикуйте токены.

3. Скачайте модели распознавания и анализа

uv run --no-sync python scripts/download_models.py asr llm

По умолчанию: mlx-community/whisper-large-v3-turbo и mlx-community/Qwen3-4B-4bit. Веса сохраняются в пользовательском кэше Hugging Face, а не в Git. Скачивание моделей — отдельный этап установки; скрипт не читает записи совещаний.

3а. Чтобы воспроизвести демонстрацию с Sybyrla на Mac

Сначала выполните установку зависимостей и загрузку Qwen3 выше. Затем выполните шаги «Подготовка» из инструкции Sybyrla: загрузка фиксированной версии и преобразование в MLX FP16. Это одноразовая операция, которой нужен дополнительный запас диска: около 6,2 ГБ исходных весов и 3,1 ГБ результата одновременно.

После успешного преобразования измените существующие строки в .env:

ASR_BACKEND=mlx
ASR_MODEL=models/sybyrla-mlx-fp16
LLM_BACKEND=mlx

Путь относителен корню проекта. Не переключайте ASR_MODEL, пока каталог с преобразованной моделью не готов. Затем подготовьте pyannote и запустите диагностику по следующим шагам. Для Docker эту MLX-модель использовать нельзя.

4. Подготовьте диаризацию

  1. Зарегистрируйтесь/войдите в Hugging Face.
  2. Примите условия доступа к pyannote Community-1.
  3. Создайте токен Read. Для fine-grained токена нужен доступ к public gated repositories.
  4. В локальном .env заполните HF_TOKEN=....
  5. Выполните:
uv run --no-sync python scripts/download_models.py diarization

Токен нужен для первоначального скачивания. После установки модели его можно убрать из .env. Без доступа к pyannote работают остальные этапы, но обязательная автоматическая диаризация не будет выполнена.

5. Проверьте окружение и запустите приложение

Рекомендуемый запуск с диагностикой зависимостей, моделей и занятого порта:

uv run --no-sync python scripts/start.py

Отдельная диагностика: uv run --no-sync python scripts/doctor.py. Автоматическая установка нативного окружения: uv run --no-project --python 3.12 python scripts/setup.py; существующий .env сохраняется.

Прямой запуск без диагностики:

uv run --no-sync uvicorn app.main:app --host 127.0.0.1 --port 8000

Откройте http://127.0.0.1:8000. Остановка — Ctrl+C в терминале. Запускайте один процесс Uvicorn, без --workers и без --reload во время обработки.

6. Проверьте основной сценарий

  1. Нажмите «Проверить модели»: все три модели должны быть скачаны.
  2. Нажмите «Новое совещание», укажите название и дату; участников через запятую можно добавить при необходимости.
  3. Загрузите запись либо запишите микрофон и нажмите «Создать совещание». Для первого прогона достаточно 20–60 секунд с явным поручением: «Айдана, подготовь отчёт к пятнице». Перед записью уведомите участников о записи и ИИ-транскрибации.
  4. Нажмите «1. Распознать и разделить голоса». Для казахской речи с русскими вставками начните с «Казахский + RU», затем проверьте результат.
  5. Дождитесь завершения обоих шагов, затем исправьте ошибки текста и сохраните транскрипт. Подписывать каждый голос не обязательно.
  6. Нажмите «2. Создать протокол». Проверьте саммари, решения и поручения по цитатам и аудиофрагментам. Обращения («Айдана, отправь отчёт») и связанные реплики используются для определения исполнителя и срока. Раскройте «Почему этот исполнитель и срок», чтобы увидеть дополнительный контекст.
  7. Уточните ответственных и календарные даты. Отметьте «Проверено» у каждого поручения, затем подтвердите протокол.
  8. Скачайте PDF или DOCX. Без подтверждения документ явно помечен как черновик.

Повторное распознавание или сохранение транскрипта сбрасывает производный протокол, чтобы старые поручения не выдавались за результат нового текста. Экспорт использует только сохранённую версию: сохраните изменения перед скачиванием.

DOCX и PDF оформлены по предоставленным образцам протоколов: лист Letter с полями 25,4 мм, заголовок «Протокол совещания», тема, диалог с выделенными именами, саммари с новой страницы и таблица «Поручение / Ответственный / Срок» с голубой шапкой. Сохраняется отметка о черновике/подтверждении. Неизвестные ответственные и сроки отмечаются явно. Название организации, должности и числовые показатели из образцов не подставляются автоматически. Число страниц зависит от содержания; Word может заменять шрифты. PDF содержит встроенные шрифты Liberation, скачивать их отдельно не нужно. Полные данные, таймкоды и статусы поручений остаются в JSON. Шаблон app/templates/protocol.docx содержит только оформление, без исходных текстов совещаний.

Запись встречи с микрофона

В форме «Новое совещание» есть инструкция и кнопка «Записать микрофон».

  1. Откройте приложение на компьютере с микрофоном через http://127.0.0.1:8000 (или HTTPS при отдельном размещении). Доступ по обычному HTTP через IP другого компьютера может блокировать микрофон.
  2. Укажите название и дату встречи. Уведомите участников о записи и ИИ-транскрибации.
  3. Нажмите «Записать микрофон» и разрешите доступ в браузере. Используется выбранное браузером устройство ввода.
  4. Убедитесь, что показано «Идёт запись…», и говорите рядом с микрофоном. Не закрывайте форму и страницу до сохранения встречи.
  5. Нажмите «Остановить запись». Когда появится «Запись микрофона готова», нажмите «Создать совещание». Выбирать файл отдельно не нужно.
  6. В карточке встречи прослушайте запись, нажмите «1. Распознать и разделить голоса», затем «2. Создать протокол». Обработка запускается после записи; потоковой расшифровки нет.

Для проверки жюри: запишите 10–20 секунд с коротким поручением, остановите запись, создайте встречу и проверьте воспроизведение, затем пройдите обработку и экспорт.

Записывается только микрофон. Системный звук и голоса удалённых участников Teams/Zoom/Google Meet автоматически не захватываются; для такого сценария загрузите готовую запись онлайн-встречи. Бот-участник и захват звука вкладки пока не реализованы.

Если доступ запрещён, проверьте разрешение микрофона для сайта в браузере и для самого браузера в настройках системы. Проверьте выбранное устройство ввода и выключатель микрофона, обновите страницу и начните запись заново. Браузер должен поддерживать getUserMedia и MediaRecorder; при отсутствии поддержки используйте загрузку файла.

Тестовая запись без персональных данных

Реальные записи и их транскрипты не включены в репозиторий. Для проверки можно записать двумя разными голосами следующий синтетический сценарий:

  • Асет: «Айдана, подготовь отчёт по бюджету к пятнице».
  • Айдана: «Жақсы, отчётты жұма күні жіберемін».
  • Асет: «Решили перенести презентацию на следующую неделю».

Ожидаемый смысл: одно поручение Айдане подготовить отчёт, срок «к пятнице», отдельное решение о переносе презентации. Повторное подтверждение не должно создавать дубликат поручения. Диаризация должна выделить два голоса; имена назначает пользователь. Сверьте фактический результат с записью — это не заранее подставленный ответ.

Настройки

ПеременнаяНазначение
DATA_DIRПапка SQLite и аудиозаписей; по умолчанию ./data
ASR_BACKENDmlx для Apple Silicon; cpu для faster-whisper
ASR_MODELHugging Face ID или путь к локальной модели нужного backend
LLM_BACKENDmlx по умолчанию; альтернативно локальный ollama
LLM_MODELMLX-модель для поручений и саммари
DIAR_MODELpyannote Community-1
HF_TOKENТолько для скачивания модели с ограниченным доступом
OLLAMA_URL / OLLAMA_MODELАдрес loopback-сервера и имя локальной модели

Перезапустите сервер после изменения .env.

Ручной запуск на Linux / Windows без Docker

Для жюри рекомендуется Docker выше. Ручной путь ниже предусмотрен в коде, но не проверен на физической Linux/Windows машине; здесь пользователь самостоятельно устанавливает зависимости. Установите Python 3.12, uv, FFmpeg и локальный Ollama, затем:

uv sync --frozen --python 3.12 --extra cpu --extra diarization --extra dev

В .env укажите:

ASR_BACKEND=cpu
ASR_MODEL=dropbox-dash/faster-whisper-large-v3-turbo
LLM_BACKEND=ollama
OLLAMA_URL=http://127.0.0.1:11434
OLLAMA_MODEL=qwen3:4b

Запустите Ollama и выполните ollama pull qwen3:4b, затем uv run --no-sync python scripts/download_models.py asr diarization. Остальные шаги запуска совпадают. CPU может обрабатывать запись заметно медленнее её длительности. Внешние адреса Ollama запрещены: допускается только loopback.

Архитектура и данные

Браузер → FastAPI → SQLite + локальная WAV-запись
                      ↓ очередь, один этап одновременно
               отдельный Python-процесс
               Whisper / pyannote / Qwen3
                      ↓
             проверка человеком → DOCX/PDF
  • Интерфейс — HTML/CSS/JavaScript без CDN и внешних шрифтов; отдельная сборка Node.js не нужна.
  • app/main.py — HTTP API, загрузка, очередь и локальный интерфейс.
  • app/worker.py — локальные модели, один процесс на этап.
  • app/analysis.py — инструкции извлечения и проверка цитат.
  • app/export.py — экспорт протокола.
  • data/meetings.sqlite3, data/<id>/audio.wav — пользовательские данные, исключены из Git.
  • Во время инференса исходящие TCP-соединения к внешним адресам блокируются; разрешены только локальные сервисы (loopback).
  • При обработке включены HF_HUB_OFFLINE=1, TRANSFORMERS_OFFLINE=1, отключена телеметрия Hugging Face и pyannote.
  • Приложение не имеет авторизации: предназначено только для одного пользователя на 127.0.0.1. Не публикуйте порт в интернет.
  • Файлы на диске не шифруются приложением. Для реальных записей используйте защищённый компьютер; на демонстрации — анонимизированные или синтетические данные.

Проверки

uv run --no-sync pytest -q

Проверяются неверные файлы, ссылки на несуществующие реплики, неподтверждённые поручения, сброс устаревшего протокола, некорректные таймкоды, блокировка изменений во время обработки, сторонние Origin и экспорт с казахскими буквами. Эти тесты не скачивают модели. Реальное качество ASR и диаризации проверяется отдельной записью по сценарию выше.

Канбан поручений

В разделе «Все поручения» зажмите карточку за значок ⠿ или заголовок и перенесите в колонку «К выполнению», «В работе» или «Выполнено». Колонка назначения подсвечивается, статус сохраняется автоматически. Можно переносить и в пустую колонку. Перенос внутри той же стадии ничего не меняет. Альтернатива для клавиатуры и сенсорных устройств — список «Статус» в карточке. Черновики заблокированы до проверки поручений и подтверждения протокола; во время обработки встречи перемещение также недоступно.

Откройте «Поручения»: карточки автоматически собираются из сохранённых протоколов всех встреч. Колонки: «К выполнению», «В работе», «Выполнено». На карточке показаны исполнитель, срок, признак просрочки и ссылка на встречу. Поиск работает по задаче, исполнителю и названию встречи. Статус меняется через меню или перетаскиванием; на узком экране доска прокручивается горизонтально.

Черновики видны, но смена статуса доступна после проверки поручения и подтверждения протокола. Изменение статуса сохраняется в самом поручении, а не в отдельной копии для доски. Выполнение через Telegram отображается при обновлении доски; выполнение на доске отменяет ожидающие напоминания. Возврат выполненной задачи в работу сам по себе не отправляет новое уведомление и не восстанавливает отменённое напоминание. Доска автоматически обновляется, когда пользователь не работает с карточкой; есть кнопка «Обновить».

Старые поручения получают постоянные идентификаторы при запуске без изменения содержания. Прежний статус open отображается как «К выполнению». При повторной генерации протокола его поручения заменяются; история прежних карточек пока не ведётся. Устаревшее перемещение при уже изменённом статусе отклоняется с обновлением доски.

Проверка точности распознавания

Для измерения качества на своей записи экспортируйте встречу в JSON и вручную расшифруйте тот же полный аудиофрагмент в UTF-8 .txt. Затем выполните:

uv run --no-sync python scripts/evaluate_asr.py --reference /path/to/reference.txt --hypothesis /path/to/meeting.json

Команда печатает WER (ошибки слов) и CER (ошибки букв) для основного текста и, если есть, полного RU-прохода. Регистр, пунктуация и пробелы нормализуются; казахские буквы сохраняются. Меньше — лучше; WER может превышать 1 при большом числе лишних слов. Сравнение имеет смысл только для одинакового диапазона аудио без последующего ручного редактирования гипотезы. Эти метрики не измеряют правильность говорящих или поручений. Реальные эталоны и экспорты храните вне Git.

Известные ограничения

  • Боты-участники Teams/Zoom/Meet и захват звука вкладки пока не реализованы. Запись микрофона записывает микрофон, а не системный звук.
  • Запись микрофона обрабатывается после остановки; потоковый транскрипт пока отсутствует.
  • Языковая настройка kk/ru не гарантирует качество code-switching; имена, сроки и числа требуют проверки.
  • Новое распознавание сохраняет таймкоды слов (MLX и CPU). Диаризация сопоставляет слова с интервалами голосов и разделяет реплику при смене говорящего. Равное перекрытие отмечается как неопределённый участник; недостаточное перекрытие — как сомнительный фрагмент. Одновременная речь по-прежнему требует ручной проверки.
  • Для старых транскриптов запустите «Распознать и разделить голоса» заново, чтобы получить таймкоды слов. Это сбросит исправления текста и производный протокол. Реплики без таймкодов слов сопоставляются целиком. Исправление текста или временных границ удаляет устаревшие таймкоды слов; переименование участника сохраняет их.
  • Оба ASR-профиля используют temperature=0.0 без случайных повторных попыток декодирования. Эвристика MLX hallucination_silence_threshold отключена: в проверке смешанной речи она могла скрыть большую часть неудачного прохода. Отдельный VAD на MLX пока не подключён; в CPU-профиле включён VAD faster-whisper. Качество текста и временных границ не гарантируется.
  • Календарная дата вводится/подтверждается пользователем; модель извлекает исходное выражение срока.
  • Анализ ограничен 28 000 символами входного транскрипта с разметкой. Длинные встречи нужно делить на части; тихого обрезания нет.
  • Telegram-уведомления и отложенные напоминания реализованы (см. ниже); интеграция с СЭД отсутствует.
  • Очередь находится в памяти. После перезапуска прерванные задания получают ошибку и требуют повторного запуска.
  • Режим «Сравнить ҚАЗ / RU · два прохода» экспериментальный: одна локальная модель выполняет сначала KK-, затем RU-проход по всей записи. Основным остаётся KK-текст; отличающиеся RU-варианты показываются под репликами и помечаются для проверки. Это увеличивает время обработки. Сопоставление идёт по середине временного интервала слова; слова в промежутках между основными репликами могут не попасть в сравнение. Автоматического выбора «правильного» варианта нет: прослушайте аудио и отредактируйте основной текст. Новые модели не требуются.
  • Саммари и решения формируются из коротких выводов с дословными цитатами и ссылками на реплики. Неподтверждённые выводы и поручения исключаются с предупреждением, точные дубли удаляются, явные циклические повторы отбрасываются. В интерфейсе блок «Основания саммари и решений» позволяет перейти к аудио. Точная цитата подтверждает наличие источника, но не правильность интерпретации: результат всё равно проверяет человек.
  • При ручном изменении саммари его прежние основания удаляются после сохранения; для неизменённых решений основания сохраняются. Несохранённые исправления ещё не меняют сохранённые цитаты-основания.

Если что-то не работает

  • GatedRepoError: проверьте, что условия pyannote приняты именно аккаунтом владельца токена и токен имеет право чтения.
  • Модель не скачана: выполните соответствующий scripts/download_models.py; проверьте, что запуск идёт под тем же пользователем и с тем же кэшем Hugging Face.
  • Ошибка обработки: проверьте зависимости командой uv sync --frozen --extra mac --extra diarization --extra dev, наличие свободной памяти и длину записи. Повторите этап.
  • Нет микрофона: используйте Chrome/Safari на localhost и разрешите доступ; альтернативно загрузите файл.
  • Порт занят: выберите другой порт, например --port 8001, и откройте соответствующий адрес.

Лицензии компонентов

Лицензии моделей и библиотек сохраняются у их авторов: Whisper (MIT), pyannote Community-1 (CC-BY-4.0 и условия доступа), Qwen3 — см. карточку выбранной модели. Шрифт Noto Sans распространяется по SIL Open Font License; текст лицензии находится в app/fonts/OFL.txt.

Проверено на этой версии

На MacBook Air M2 16 ГБ проведён сквозной прогон 60-секундного телефонного фрагмента с заблокированными внешними TCP-соединениями: ASR — 28,9 с, диаризация — 43,2 с (2 спикера), анализ — 18,9 с. Время зависит от записи и состояния компьютера; это не оценка точности. На отдельном синтетическом смешанном транскрипте проверено извлечение одного поручения с ответственным, исходным сроком и точной цитатой. В браузере проверены редактирование даты и подтверждение; PDF/DOCX просмотрены после рендеринга. Этот ранний прогон относится к прежнему анализу одним запросом. В актуальной версии проходят 63 автоматических теста; тематический анализ выполняет несколько запросов и занимает больше времени. Запись микрофона реализована, но отдельный акустический прогон через микрофон пока не проводился.

Telegram: уведомления исполнителям

Опциональная интеграция: без токена приложение работает как раньше. Распознавание и анализ остаются локальными, но текст поручения, имя исполнителя и срок отправляются через внешний сервис Telegram. Аудио, транскрипт, саммари и цитаты бот не отправляет.

  1. В Telegram откройте @BotFather, выполните /newbot, задайте имя и уникальный username.
  2. Добавьте токен только в локальный .env: TELEGRAM_BOT_TOKEN=.... Не отправляйте его в чат и не коммитьте.
  3. Перезапустите приложение. В Docker: docker compose up -d --force-recreate app. Дополнительные библиотеки, публичный адрес и webhook не нужны: бот получает команды через long polling. Используйте один экземпляр приложения с этим токеном; существующий webhook другого приложения нужно предварительно отключить.
  4. Сохраните поручения с явно назначенными исполнителями. В совещании нажмите «Telegram · уведомления», выберите исполнителя и создайте персональную ссылку. Передайте её именно этому человеку: владение ссылкой позволяет подключить его поручения.
  5. Исполнитель открывает ссылку и нажимает Start. Ссылка одноразовая, действует 24 часа и относится к конкретному совещанию. Одинаковые имена в разных совещаниях не связываются автоматически. Для разных людей с одинаковым именем используйте полное имя или уникальную подпись.
  6. Нажмите «Обновить подключения». Проверьте поручения и подтвердите протокол, затем нажмите «Отправить поручения». Кнопка отправляет сохранённые открытые поручения подключённым исполнителям. Первичная отправка запускается явно; дальнейшее напоминание исполнитель может назначить кнопкой в сообщении.
  7. Исполнитель может отправить /stop, чтобы удалить все свои подключения к этому боту. Новая персональная ссылка заменяет прежнюю привязку исполнителя в выбранном совещании.

Успешно отправленные неизменённые поручения не дублируются при повторном нажатии или перезапуске. Изменение текста/срока образует новое уведомление. При обрыве соединения результат может быть неизвестен: интерфейс покажет это, автоматического повтора не будет. Проверьте чат вручную; доставка «ровно один раз» Telegram API не гарантируется. Статус «отправлено» означает принятие Telegram, а не прочтение человеком.

Для работы бота компьютер и приложение должны быть включены и иметь доступ к api.telegram.org. Привязки, курсор обновлений и журнал отправки хранятся в локальной SQLite / Docker volume. Токен не попадает в образ или ответы API. Проверка без реальных сообщений: uv run --no-sync python -m pytest -q; Telegram в тестах заменён заглушкой. Реальную доставку нужно проверить после настройки своего токена.

Кнопки и отложенные напоминания

В новых уведомлениях есть «✅ Выполнено» и «⏰ Напомнить позже» — на 15 минут, 1 час или 24 часа. Выполнение меняет статус поручения в приложении: обновите страницу, чтобы увидеть его. Кнопки доступны только привязанному исполнителю; после изменения задачи или отмены подтверждения протокола старые кнопки недействительны.

Выбранное напоминание сохраняется в SQLite. Проверка происходит примерно каждые 20 секунд, пока приложение включено. После выключения просроченное напоминание отправится при следующем запуске. Выполненные поручения и отключённые подключения исключаются. При неопределённом результате отправки автоматического повтора нет: предупреждение видно в окне Telegram приложения; новый срок можно выбрать кнопкой в сообщении.

macOS и папки iCloud

Для разработки и демонстрации предпочтительна обычная локальная папка, например ~/Projects/hackalem, вне синхронизируемых Documents/Desktop. iCloud может выгружать даже файлы .venv и Git; это вызывает зависания импорта и ошибки сертификатов. scripts/start.py направляет кэш Python в ~/.cache/hackalem/python-cache, вне папки проекта. Если библиотеки уже повреждены/выгружены, остановите приложение и восстановите зависимости из доступного локального кэша: uv sync --frozen --offline --reinstall --extra mac --extra diarization --extra dev. Если нужных пакетов в кэше нет, повторите без --offline при наличии интернета. Это не скачивает модели заново.

Имена и исполнители

Исполнитель поручения и говорящий — разные сущности: поручение можно адресовать человеку или отделу, которых нет в списке участников. Анализ использует явные обращения, назначения и обязательства; подтверждает дополнительные цитаты по исходным репликам. Простое упоминание имени не должно считаться назначением. Проверка цитат не гарантирует правильную интерпретацию модели: все поручения остаются черновиками до проверки.

Имя голоса автоматически заполняется только по явному «Меня зовут …» / «Менің атым …» в несомнительной реплике. Само обращение к человеку не переименовывает голос говорящего. Ручные имена сохраняются. При ошибке распознавания имени или неоднозначности секретарь может уточнить конкретное поручение; предварительно подписывать все голоса не требуется. Для ранее созданного протокола новый анализ запускается кнопкой «Создать протокол» и заменяет прежний черновик.

Использование Sybyrla на Mac

Для отдельного локального сравнения InflexionLab/sybyrla с текущим Whisper Turbo подготовлены инструкция и ограничения. Эксперимент не меняет .env или рабочую модель. Реальные записи, транскрипты и веса в Git не входят.

Для рабочего профиля Sybyrla сначала выполните скачивание и конвертацию по инструкции выше, затем задайте в локальном .env:

ASR_BACKEND=mlx
ASR_MODEL=models/sybyrla-mlx-fp16

Перезапустите приложение через uv run --no-sync python scripts/start.py. В /api/health поле models.asr.name должно быть models/sybyrla-mlx-fp16, а cached — true. В интерфейсе начните с «Казахский + RU» (язык kk). Новые результаты сохраняют asr_model и asr_backend, чтобы было видно, какой моделью они получены. Старые расшифровки автоматически не меняются; повторное распознавание заменит текст и сбросит производный протокол.

Этот профиль проверен на Mac Apple Silicon и не запускается через MLX на Windows/Linux. Для жюри на других системах остаётся описанный CPU/Docker-профиль Whisper Turbo. Значение в .env_example сохраняет базовый путь установки; выбор Sybyrla нужно сделать явно после конвертации.

Прогресс обработки

На странице встречи отображаются процент, текущий шаг и прошедшее время; список встреч также показывает процент активного этапа. Состояние хранится в базе и обновляется в интерфейсе каждые 2,5 секунды. Можно переходить между встречами или обновлять страницу.

  • Распознавание: доля обработанного аудио (MLX — кадры, CPU — временные границы сегментов); режим ҚАЗ/RU учитывает оба прохода. Последние проценты отведены сохранению. На загрузке модели и внутри длинного фрагмента процент может не меняться.
  • Разделение голосов: «≈ %», оценка по шагам поиска речи, подсчёта/сравнения голосов и сопоставления текста. Вес шагов условный, это не прогноз оставшегося времени.
  • Протокол: «≈ %», этапы загрузки, чтения, генерации и проверки цитат. Длина ответа заранее неизвестна, поэтому во время генерации процент остаётся на отметке шага, а MLX показывает растущий счётчик токенов. Для Ollama показывается ожидание локальной модели.
  • 100% выставляется только при успешном завершении и сохранении. Ошибка/прерывание не считаются завершением. Прогресс относится к текущему выбранному этапу, а не сразу ко всему совещанию.

Единый этап «Речь и голоса»

Кнопка «1. Распознать и разделить голоса» запускает Sybyrla/настроенный ASR, затем pyannote автоматически. Перед стартом проверяется наличие обеих моделей. Модели работают в отдельных последовательных процессах, чтобы освобождать память. Общий индикатор «≈ %» отводит распознаванию 0–70%, разделению 70–99%; 100% появляется после сохранения готового транскрипта с голосами. Это оценка по шагам, а не времени.

При ошибке распознавания цепочка останавливается. При ошибке разделения распознанный текст остаётся доступным; повторный запуск общей кнопки снова выполняет оба шага. После проверки текста используйте «2. Создать протокол». API сохраняет отдельные transcribe/diarize для диагностики; основной интерфейс отправляет stage=prepare.

Удаление встреч

В списке совещаний нажмите значок корзины справа внутри карточки нужной встречи (кнопка также доступна внутри встречи). Окно подтверждения показывает название и последствия. «Отмена» или Escape сохраняет встречу. Удаление безвозвратно убирает запись, транскрипт, протокол, поручения с доски, подключения и напоминания Telegram. Уже отправленные сообщения в Telegram остаются. Пока встреча в очереди или обрабатывается, удаление заблокировано и в интерфейсе, и в API.

Срочность и направления поручений

При создании нового протокола категории предлагаются локальными правилами по тексту и проверенным цитатам. Для существующего протокола нажмите «Определить срочность и направление» (сначала сохраните правки). Повторная классификация заменяет категории и снимает подтверждение протокола. Проверьте результат и подтвердите его снова.

  • Срочность: высокая, обычная, не определена. Высокая — при явных маркерах «срочно», «шұғыл» и аналогичных либо подтверждённой дате не позднее завтра, включая просрочку. «Не срочно» / «шұғыл емес» учитываются отдельно. Без основания категория остаётся неопределённой. Относительные сроки автоматически в календарную дату не превращаются.
  • Направления: финансы, закупки, юридические вопросы, безопасность, производство, ИТ, персонал. Несколько совпавших направлений или отсутствие ключевых слов дают «Не определено».
  • Это объяснимые эвристики, а не гарантированное понимание контекста. Основание показано у поручения; категории можно исправить вручную и сохранить. Срочность не пересчитывается с каждым новым днём: повторите классификацию при необходимости. Просрочка на канбане вычисляется отдельно по текущей дате.
  • Канбан показывает категории и позволяет фильтровать по ним. JSON сохраняет категории и основания; шаблон PDF/DOCX остаётся в согласованном трёхколоночном виде.

Локальное узнавание участников по голосу

Используется сравнение голосовых векторов уже установленной pyannote Community-1. Дополнительные модели и внешние API не нужны. Это экспериментальный поиск похожего сохранённого голоса, не аутентификация и не доказательство личности. Шум, короткая речь, другой микрофон и похожие голоса могут приводить к ошибкам.

  1. Выполните «Распознать и разделить голоса». В новых результатах сохраняются голосовые признаки участников. Для старой встречи без признаков потребуется повторная обработка, которая сбросит текущий протокол.
  2. Прослушайте реплики и убедитесь, кому принадлежит голос. Откройте «Голосовые профили · узнать участников» рядом с транскриптом.
  3. Введите проверенное имя или согласованный псевдоним, отметьте разрешение участника на сохранение профиля и нажмите «Сохранить голосовой профиль». Нужно минимум 10 секунд речи без одновременных голосов. Для замены профиля сначала удалите старый.
  4. После обработки другой встречи откройте ту же панель. Приложение сравнит голоса с профилями на этом компьютере. Косинусное сходство не является вероятностью правильного имени. Предварительные пороги: сходство ≥ 0,80 и отрыв от второго кандидата ≥ 0,10. Неоднозначные совпадения и один профиль для нескольких голосов отклоняются. Пороги ещё не откалиброваны на независимом наборе записей.
  5. Проверьте предложение по аудио, нажмите «Подставить имя в редактор», затем «Сохранить текст». Изменение транскрипта сбросит прежний протокол; сформируйте его заново. Имя говорящего не делает его автоматически исполнителем поручения.
  6. Удалить сохранённый профиль можно в этой панели. Это прекращает будущий поиск по нему, но не стирает уже подтверждённые имена из протоколов.

Голосовые признаки — чувствительные данные. Признаки встречи хранятся в data/<meeting-id>/voice-samples.json и удаляются вместе со встречей. Отдельно и только после согласия сохраняются именные профили в таблице voice_profiles локальной SQLite-базы. Удаление встречи не удаляет именной профиль: используйте кнопку в панели. Векторы не выдаются API профилей, не попадают в экспорт и Git. Локальная база не зашифрована приложением; для чувствительных данных требуются защита диска и доступ к компьютеру. Сравнение голоса и классификация не обращаются в Telegram или внешние сервисы.

Проверки этой функции: 54 автоматических теста приложения проходят; в отдельной тестовой базе браузером проверены классификация, фильтр канбана, сохранение и удаление синтетического профиля. На двух 30-секундных фрагментах реальной записи локальная модель выдала голосовые векторы размерности 256; проверено их сравнение. Это техническая проверка, не измерение точности идентификации на разных записях.

Тематический протокол и полнота поручений

«Создать протокол» сначала определяет темы, затем отдельно анализирует небольшие фрагменты каждой темы с соседними репликами для контекста. Явные переходы повестки имеют приоритет над дроблением модели. Каждый фрагмент проверяется на отдельные поручения, в том числе нумерованные списки и уточнения срока в следующей реплике. Проверка точных цитат сохраняется; испорченные повторяющиеся фрагменты не используются как основание. Некорректный ответ модели останавливает обработку, а не молча пропускает фрагмент. Этот анализ медленнее прежнего единственного запроса; прогресс показывает текущую тему и фрагмент.

В протоколе можно редактировать название и саммари каждой темы, а у поручения — выбирать тему. Канбан сохраняет общий список задач с названием темы. DOCX/PDF выводят «Тема 1 — …», саммари и отдельную таблицу поручений, затем следующий блок. Старые протоколы без тем по-прежнему открываются и экспортируются; для нового формата повторите только «Создать протокол», повторное распознавание не требуется. Это заменит текущий протокол, поэтому предварительно скачайте JSON, если хотите сохранить ручные исправления.

Структура по темам не гарантирует полноты: ошибки в аудиотранскрипте, именах, географии и числах требуют сверки с записью. Образцы из ТЗ используются как контроль ожидаемого результата, а не как источник для подстановки фактов.

Контрольный прогон тематического анализа на сохранённом транскрипте: 2 темы, 10 поручений вместо 2 в прежнем результате; у 10 поручений есть исполнитель, у 9 — срок. Полнота и правильность на других записях этим не доказаны. Генерация на Mac M2 заняла около 8 минут; проверка привязки цитат доработана и повторно выполнена на сохранённых ответах той же модели. Саммари тем строится из фактических выдержек исходного текста, чтобы перефразирование не меняло смысл. Имена, география и один испорченный срок требуют проверки по аудио. Автотесты: 63; DOCX/PDF просмотрены после рендеринга, редактирование темы поручения проверено в браузере.

Звёзды
⭐ 0
Форки
0
Язык
Python
Последний push
в прошлом месяце
Коммиты
25
Авторы
1
Файлы
63
Ветки
1

Авторы коммитов

Asset Kudaibergenov 25

История коммитов

  • 2026-09-23 17:38fix: use available CTranslate2 Whisper Turbo repositoryAsset Kudaibergenov
  • 2026-09-23 17:22docs: prepare project submission copyAsset Kudaibergenov
  • 2026-09-23 17:22docs: clarify jury setup and verify export prerequisitesAsset Kudaibergenov
  • 2026-09-23 17:18fix: recover grounded tasks and export protocols by agenda topicAsset Kudaibergenov
  • 2026-09-23 16:55feat: classify tasks and suggest identities from local voice profilesAsset Kudaibergenov
  • 2026-09-23 16:44feat: make kanban drag and drop visible and resilientAsset Kudaibergenov
  • 2026-09-23 16:42docs: explain microphone recording in meeting formAsset Kudaibergenov
  • 2026-09-23 16:40feat: match protocol exports to supplied document layoutAsset Kudaibergenov
Все коммиты (25)
  • 2026-09-23 16:30style: place trash icon inside meeting cardsAsset Kudaibergenov
  • 2026-09-23 16:29feat: expose meeting deletion in list with confirmationAsset Kudaibergenov
  • 2026-09-23 16:21feat: combine transcription and diarization into one workflowAsset Kudaibergenov
  • 2026-09-23 16:17feat: show persistent progress for meeting processing stagesAsset Kudaibergenov
  • 2026-09-23 16:12feat: document and verify active Sybyrla Mac profileAsset Kudaibergenov
  • 2026-09-23 16:06feat: add reproducible local Sybyrla MLX comparisonAsset Kudaibergenov
  • 2026-09-23 15:59fix: extract task owners beyond speaker roster and show contextAsset Kudaibergenov
  • 2026-09-23 15:35feat: add meeting task kanban with shared Telegram statusAsset Kudaibergenov
  • 2026-09-23 15:26feat: compare RU-KK hypotheses and ground meeting analysis in quotesAsset Kudaibergenov
  • 2026-09-23 15:10docs: align MVP priorities with jury evaluation criteriaAsset Kudaibergenov
  • 2026-09-23 15:09fix: allow local restart while old sockets leave TIME_WAITAsset Kudaibergenov
  • 2026-09-23 15:08feat: align meeting speakers at word boundariesAsset Kudaibergenov
  • 2026-09-23 14:57fix: keep Python cache local and record end-to-end validationAsset Kudaibergenov
  • 2026-09-23 14:46feat: add Telegram completion buttons and persistent snooze remindersAsset Kudaibergenov
  • 2026-09-23 14:36feat: add Telegram task notifications and Docker deployment guideAsset Kudaibergenov
  • 2026-09-23 14:11feat: add local meeting minutes MVP with transcription, diarization and exportsAsset Kudaibergenov
  • 2026-09-23 13:44docs: initialize local meeting assistant MVPAsset Kudaibergenov
Структура репозитория (63 файлов)
.dockerignore
.env_example
.gitignore
AGENTS.md
Dockerfile
README.md
app/__init__.py
app/analysis.py
app/classification.py
app/config.py
app/export.py
app/fonts/Liberation-LICENSE.txt
app/fonts/LiberationSans-Regular.ttf
app/fonts/LiberationSerif-Bold.ttf
app/fonts/LiberationSerif-Italic.ttf
app/fonts/LiberationSerif-Regular.ttf
app/fonts/NotoSans-Regular.ttf
app/fonts/OFL.txt
app/main.py
app/progress.py
app/schemas.py
app/speech.py
app/static/app.js
app/static/index.html
app/static/style.css
app/store.py
app/telegram_bot.py
app/templates/protocol.docx
app/topic_analysis.py
app/voices.py
app/worker.py
compose.yaml
docker/entrypoint.py
docker/requirements-amd64.txt
docker/requirements-arm64.in
docker/requirements-arm64.txt
docker/requirements.in
docs/DESIGN_REFERENCES.md
docs/JURY_CRITERIA.md
docs/SUBMISSION.md
docs/SYBYRLA_BENCHMARK.md
docs/VALIDATION.md
pyproject.toml
scripts/benchmark_mlx_asr.py
scripts/convert_whisper_hf_mlx.py
scripts/doctor.py
scripts/download_models.py
scripts/evaluate_asr.py
scripts/setup.py
scripts/start.py
tests/conftest.py
tests/test_analysis_quality.py
tests/test_app.py
tests/test_asr_metrics.py
tests/test_classification_voices.py
tests/test_combined.py
tests/test_kanban.py
tests/test_progress.py
tests/test_speech.py
tests/test_telegram.py
tests/test_topics.py
third_party/MLX_EXAMPLES_LICENSE.txt
uv.lock

Обсуждение 0 комментариев

Войдите, чтобы комментировать

Пока нет комментариев — будьте первым.