AAYMA
RU
Войти

HATTAMA.AI

Полноценная система автопротоколирования совещаний: распознавание каз./рус./смешанной речи, диаризация, извлечение поручений с цитатами и сроками…

Доведённый продукт
Команда «13Lab» 21💬 0 комментариевс 22 сентября 2026 г.

О проекте

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

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

Суть

Полноценная система автопротоколирования совещаний: распознавание каз./рус./смешанной речи, диаризация, извлечение поручений с цитатами и сроками, онлайн-режим, бот для Teams/Zoom/Meet, экспорт DOCX/PDF.

Как устроено

Аудио (файл или live PCM 16 кГц по WebSocket, нарезка Silero VAD на 8–25 с) распознаётся self-hosted Til-Qazyna til-asr с таймкодами слов, диаризация — pyannote Community-1 на GPU. Поручения извлекаются двумя проходами LLM (qwen, извлечение и проверка) с проверкой точной цитаты и детерминированным расчётом сроков от даты встречи; секретарь подтверждает каждое поручение. FastAPI + PostgreSQL + MinIO, React-фронт за nginx, RBAC с 5 ролями, всё поднимается через docker compose.

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

  • Til-Qazyna til-asr
  • pyannote Community-1 diarization
  • Silero VAD (sherpa-onnx)
  • two-pass LLM extraction
  • quote grounding
  • deterministic deadline parsing
  • RBAC

Особенности

Бот-участник для Teams/Zoom/Google Meet через Chromium на виртуальном дисплее с PulseAudio и единым интерфейсом MeetingConnector; полностью закрытый контур без внешних облачных API; клик по источнику позиционирует аудио.

Стек

Python · FastAPI · SQLAlchemy · PostgreSQL · MinIO · React · TypeScript · nginx · Docker Compose · Chromium/PulseAudio bot · fpdf2 · python-docx

Модели

Til-Qazyna til-asr · qwen (self-hosted) · pyannote Community-1


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

README

README.md

HATTAMA.AI — протоколы совещаний и контроль поручений

HackAlem AI · команда 13Lab: Mukhammed Erzhanuly, Arman Nurken, Darkhan Omirbay.

HATTAMA.AI превращает запись совещания в редактируемое саммари, решения и поручения с ответственными, сроками и цитатами. Нажатие на источник открывает нужное место транскрипта и позиционирует аудио. Секретарь проверяет результат и выгружает DOCX, Markdown или JSON.

HATTAMA.AI закрывает весь обязательный минимум ТЗ и сверх него: онлайн-совещания в реальном времени, бота для Microsoft Teams, Zoom и Google Meet, роли и журнал действий, утверждение протоколов и полный стек в закрытом контуре через docker compose. Распознавание — self-hosted Til-Qazyna, диаризация — pyannote на собственном GPU, извлечение поручений — LLM с проверкой цитат и сроков. Секретарь остаётся последней инстанцией: каждое поручение подтверждается человеком.

Запуск для жюри

cp .env.example .env   # укажите TILQAZYNA_API_KEY
docker compose up --build

Сервис migrate перед стартом сам готовит базу: применяет миграции, создаёт bucket, администратора, командную учётную запись и загружает три демо-совещания с записями (SEED_DEMO=1), включая казахско-русскую встречу «ozimiz» с эталонным транскриптом в backend/seed/demo/ozimiz.reference.txt.

ВходПарольРоль
[email protected]change-me-admin-passwordАдминистратор: полный доступ
[email protected]hattama-team-2026Секретарь

Откройте http://127.0.0.1:8080.

Соответствие техническому заданию

Кейс «Система автопротоколирования совещаний с фиксацией поручений» (владелец задачи: АО «Самрук-Қазына»).

Обязательный минимум

Требование ТЗСтатусКак реализовано
Распознавание речи участников✅Self-hosted Til-Qazyna til-asr с таймкодами слов. Для загруженных записей — целиком, для онлайн-совещаний — по фрагментам речи в реальном времени
Русский язык✅Проверено на двух записях кейса: транскрипт, поручения, саммари
Казахский язык✅Поддерживается моделью til-asr; в онлайн-сессиях распознаётся казахская речь. WER на размеченном наборе ещё не измерен
Шала-казахский (смешанная речь)✅Та же модель обрабатывает смешанную речь в одной записи; метрики качества ещё не измерены
Поручения с ответственным и сроком✅Два прохода LLM (извлечение и проверка) → проверка точной цитаты из транскрипта → детерминированный расчёт сроков от даты встречи. Неоднозначные сроки не превращаются в даты. Секретарь подтверждает каждое поручение
Диаризация с привязкой к участнику✅pyannote Community-1 на GPU, выравнивание меток по словам, пометка спорных фрагментов. Секретарь сопоставляет голоса с именами и назначает исполнителя из списка пользователей; бот дополнительно собирает список участников платформы
Экспорт протокола PDF/DOCX✅PDF (кириллица и казахские буквы), DOCX, Markdown, JSON. При утверждении копии DOCX и PDF сохраняются в хранилище

Вход и выход

Требование ТЗСтатусКак реализовано
Аудио/видеозапись совещания✅MP3, WAV, M4A, OGG, FLAC, WebM, MP4 до 100 МБ
Live-поток✅Захват вкладки браузера с совещанием (Teams, Zoom, Meet в браузере) и микрофон переговорной для очных встреч; расшифровка появляется во время встречи
Подключение участником Teams / Zoom / Google Meet✅Бот «HATTAMA.AI Секретарь» входит во встречу по ссылке как участник, записывает звук и передаёт его в конвейер. Teams — гостем без аккаунта (проверено на реальной встрече), Zoom — через веб-клиент, Google Meet — с аккаунтом бота
Транскрипт, поручения, саммари, файл протокола✅Редактируемый протокол, поручения с источниками и таймкодами, выгрузка

Сценарии

Сценарий ТЗСтатусКак реализовано
1. Секретарь включает запись → участники уведомлены → поручения → саммари → контроль✅Онлайн-сессия или загрузка; обязательное подтверждение уведомления участников; бот пишет в чат встречи, что ведётся запись и расшифровка ИИ; утверждение протокола; исполнители видят свои поручения и меняют статус
2. Контроль сроков исполнения✅Сроки рассчитываются от даты встречи; просроченные поручения выделяются в протоколе и в «Моих поручениях»; исполнитель сам ведёт статус «Открыто → В работе → Выполнено»

Бонусные задачи

БонусЧто сделано
Развёртывание в закрытом контуре (on-premise)Весь стек поднимается одной командой docker compose up: frontend, backend, PostgreSQL, MinIO, бот. Распознавание и LLM — self-hosted Til-Qazyna, внешние облачные ИИ-API не используются
Дашборд со статусами порученийСтатусы «Открыто / В работе / Выполнено», подсветка просрочки, статистика по каждому совещанию, личный раздел «Мои поручения», аналитика админ-панели
Онлайн-совещания в реальном времениЗахват вкладки браузера и микрофона переговорной; расшифровка появляется во время встречи
Бот-участник для Teams, Zoom и Google MeetЕдиный интерфейс MeetingConnector, адаптер для каждой платформы, уведомление о записи в чате встречи
Утверждение протокола и архивУтверждённые DOCX и PDF сохраняются в MinIO; возврат на доработку с сохранением истории
Роли, регистрация, журналПять ролей с правами на каждое совещание, регистрация с одобрением администратором, журнал всех действий, админ-панель
Проверка источниковКаждое поручение подтверждено точной цитатой и таймкодом; клик по источнику открывает место в записи

Ограничения ТЗ

ОграничениеКак соблюдено
Передача аудио/текста во внешние облачные API запрещенаАудио и текст уходят только на self-hosted Til-Qazyna и собственный GPU-сервис диаризации; бот работает внутри нашего контура
Приватность и защита данныхРоли и права на каждое совещание, журнал действий, отзываемые сессии, CSP, внутренние сервисы не публикуются наружу, записи и секреты не попадают в Git

Онлайн-совещания

Раздел «Онлайн-совещание» в меню (секретарь, председатель, администратор). Три источника звука:

ИсточникКогда использоватьЧто нужно
Пригласить ботаВстреча в Teams, Zoom или Google Meet; бот входит как участникСсылка на встречу; организатор впускает бота из зала ожидания
Захватить вкладкуВстреча открыта в Chrome или Edge у секретаря; работает с любой платформойВыбрать вкладку и включить «Поделиться звуком вкладки»; голос секретаря добавляется с микрофона
Микрофон в переговорнойОчная встречаДоступ к микрофону компьютера

Как это работает:

  1. Звук поступает на backend как PCM 16 кГц по WebSocket.
  2. Silero VAD (sherpa-onnx, CPU) выделяет речь и режет её на фрагменты 8–25 секунд по паузам; тишина и шум не отправляются в распознавание.
  3. Каждый фрагмент распознаётся til-asr; таймкоды слов сдвигаются на начало фрагмента, текст появляется в интерфейсе обычно через 10–30 секунд.
  4. По кнопке «Завершить» или когда встреча закончилась запись становится обычным WAV в хранилище, а транскрипт сохраняется как уже распознанный.
  5. Дальше работает тот же конвейер, что и для загруженной записи: диаризация, извлечение и проверка поручений, протокол. Повторного распознавания нет.

Надёжность: при перезапуске сервера записанная часть сохраняется и доступна для повтора; сессия без звука завершается сама; ограничены длительность (3 часа) и число одновременных сессий (2).

Бот для Teams, Zoom и Google Meet

Отдельный сервис meeting-bot в compose.yaml: Chromium на виртуальном дисплее, у каждой встречи свой виртуальный звуковой вход PulseAudio. Бот записывает только то, что проигрывает веб-клиент встречи, поэтому способ передачи звука платформой не важен. Собственный микрофон бота — пустой источник, реального микрофона в контейнере нет.

Что делает бот:

  1. Открывает ссылку, вводит имя «HATTAMA.AI Секретарь», выключает камеру и микрофон, просит войти.
  2. Ждёт в зале ожидания до 10 минут, сообщает статус в интерфейс.
  3. После входа пишет в чат встречи: «Ведётся запись и расшифровка совещания ИИ-секретарём HATTAMA.AI».
  4. Передаёт звук и список участников в backend; интерфейс показывает, кто сейчас в встрече.
  5. Выходит, когда встреча закончилась или секретарь нажал «Завершить».

Архитектура интеграции: backend работает только с интерфейсом MeetingConnector (backend/app/integrations/base.py); GoogleMeetConnector, TeamsConnector и ZoomConnector — его реализации, код каждой платформы изолирован в services/meeting-bot/bot/adapters/. Переход на официальные API платформ (Zoom RTMS, Microsoft Graph, Meet Media API) меняет только коннектор; распознавание, анализ и протокол остаются прежними.

Безопасность: бот открывает только ссылки meet.google.com, teams.microsoft.com, teams.live.com и *.zoom.us по HTTPS; сервис бота и его канал /internal/bot/... доступны только внутри сети compose и защищены BOT_TOKEN.

Состояние по платформам:

ПлатформаВходЧто нужно
Microsoft TeamsГостем без аккаунтаСсылка на встречу, где разрешён гостевой вход (например, teams.live.com)
ZoomГостем через веб-клиентВключить у организатора «Join from your browser»
Google MeetС аккаунтом GoogleОднократный вход: python -m bot.login google_meet сохраняет сессию в secrets/ (не попадает в Git)

Роли и права

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

Технологии

СлойТехнологии
Распознавание и анализTil-Qazyna til-asr и qwen (self-hosted), pyannote Community-1 на GPU, Silero VAD через sherpa-onnx
BackendPython 3.12, FastAPI, Pydantic 2, SQLAlchemy 2, WebSocket (wsproto), fpdf2, python-docx
ДанныеPostgreSQL 16 (JSONB, версионные миграции), MinIO (S3)
FrontendReact 19, TypeScript, Vite, TanStack Query, React Router, Tailwind CSS 4, AudioWorklet
БотPlaywright, Chromium, Xvfb, PulseAudio
РазвёртываниеDocker Compose, nginx

Архитектура

flowchart LR
    subgraph Входы
        Upload[Загрузка записи]
        Tab[Вкладка браузера]
        Mic[Микрофон переговорной]
        Bot[Бот: Teams / Zoom / Meet]
    end
    Upload --> API[FastAPI]
    Tab -->|WebSocket PCM| Live[Онлайн-сессия]
    Mic -->|WebSocket PCM| Live
    Bot -->|внутренний WebSocket| Live
    Live --> VAD[Silero VAD] --> ASR1[til-asr по фрагментам]
    API --> ASR2[til-asr]
    ASR1 --> Pipe[Конвейер протокола]
    ASR2 --> Pipe
    Pipe --> Diar[pyannote на GPU]
    Diar --> LLM[qwen: извлечение и проверка]
    LLM --> Check[Цитаты, сроки, схема]
    Check --> DB[(PostgreSQL + MinIO)]
    DB --> UI[React: проверка секретарём]
    UI --> Export[PDF / DOCX / MD / JSON]

Структура

КаталогСодержимое
backend/FastAPI: пакет app, тесты, скрипты, requirements.txt, Dockerfile
frontend/Веб-интерфейс: React 19, TypeScript, Vite, TanStack Query, React Router, Tailwind CSS; в Docker раздаётся через nginx
docs/Архитектура, проверка, сценарий демонстрации
compose.yamlПолный стек: frontend (nginx), backend, PostgreSQL, MinIO

Быстрый запуск через Docker

Пошаговая инструкция для команды с логинами и решением частых проблем: docs/TEAM_SETUP.md.

Нужен только Docker с Compose v2.

cp .env.example .env

Сервисы распознавания Til-Qazyna и LLM Qwen3.6-27B размещены на ресурсах команды; это не вызовы OpenAI API или общедоступного Qwen endpoint. Доступ к инфраструктуре H200 организован через NITEC ZTNA/Teleport (tsh); для использования удалённых сервисов заранее получите доступ и поднимите нужный туннель. Инструкция для диаризации и H200: docs/H200.md. В .env укажите TILQAZYNA_API_KEY и адреса доступных сервисов. Остальные значения в примере рабочие для локального запуска; замените пароли, если стек доступен кому-то кроме вас. Затем:

docker compose up --build

Перед backend один раз запускается сервис migrate: применяет миграции схемы, создаёт bucket, первого администратора и общие учётные записи команды. Затем стартуют backend и frontend.

Учётная записьПарольРоль
ADMIN_EMAIL из .env (в примере [email protected])ADMIN_PASSWORDАдминистратор
[email protected]SEED_PASSWORD (в примере hattama-team-2026)Секретарь

Командные записи перечислены в backend/seed/users.json; добавьте туда строку, чтобы у всех после git pull и перезапуска появилась новая учётная запись. Существующие записи и их пароли не перезаписываются. Пароли из примера годятся только для локального стека: замените их, если он доступен по сети.

Откройте http://127.0.0.1:8080 и войдите одной из учётных записей выше. OpenAPI: http://127.0.0.1:8080/docs. Администратор создаёт остальных пользователей. Порт меняется переменной APP_PORT.

Поднимаются четыре сервиса: frontend (nginx раздаёт собранный интерфейс и проксирует /api и /docs), backend (наружу не публикуется), PostgreSQL 16 (не публикуется) и MinIO (консоль на 127.0.0.1:9001). Метаданные, пользователи и журнал хранятся в PostgreSQL, записи и утверждённые протоколы в bucket MinIO; данные сохраняются в named volumes между перезапусками. Образ MinIO берётся из quay.io, так как на Docker Hub он больше не публикуется. В образ приложения входит шрифт DejaVu для PDF. Приложение использует root-учётную запись MinIO; для production заведите отдельного пользователя с доступом только к своему bucket.

Остановить: docker compose down. Удалить вместе с данными: docker compose down -v.

Миграции

Схема меняется только миграциями в backend/app/migrations.py: каждая выполняется один раз по порядку и записывается в таблицу schema_migrations. Чтобы изменить схему, обновите backend/app/schema.py и добавьте новый шаг в конец списка; уже выпущенные шаги не редактируются. Вручную:

docker compose run --rm migrate          # в Docker
cd backend && python -m scripts.migrate  # локально

Backend также применяет недостающие миграции при старте, поэтому SQLite-режим и тесты работают без отдельного шага.

Локальная разработка без Docker

Нужны Python 3.12 или 3.13. Все команды backend выполняются из каталога backend/.

cd backend
python3.12 -m venv .venv
source .venv/bin/activate
python -m pip install -r requirements.txt
set -a; source ../.env; set +a
uvicorn app.main:create_app --factory --host 127.0.0.1 --port 8000

.env загружается через shell или Docker Compose, а не автоматически приложением. Без DATABASE_URL и MINIO_ENDPOINT приложение работает на SQLite и локальных файлах в DATA_DIR (по умолчанию backend/data). Старая база meetings.sqlite3 переносится в новую схему автоматически, прежняя таблица остаётся как meetings_legacy.

Запускайте один процесс Uvicorn без --workers и без --reload во время обработки. Очередь выполняется внутри этого процесса. При перезапуске незавершённые задания получают статус ошибки и доступны для ручного повтора.

При первом запуске, если активного администратора нет, он создаётся из ADMIN_EMAIL / ADMIN_PASSWORD (пароль не короче 10 символов). Альтернатива без переменных окружения:

python -m scripts.create_user --email [email protected] --name "Администратор" --role admin

Интерфейс в режиме разработки (нужен Node.js 22.22 или новее; Vite проксирует /api на backend 127.0.0.1:8000):

cd frontend
npm install
npm run dev        # http://127.0.0.1:5173

Сценарий работы

  1. Введите название и реальную дату совещания. Дата загрузки не обязательно совпадает с датой встречи.
  2. Выберите запись и подтвердите, что участники уведомлены об обработке.
  3. Дождитесь распознавания, анализа и проверки. Статус обновляется автоматически.
  4. Проверьте саммари, ответственных и сроки; прослушайте спорные цитаты.
  5. Исправьте поля и отметьте проверенные поручения. Можно менять статус на «В работе» / «Выполнено».
  6. Сохраните или экспортируйте. Экспорт сначала сохраняет текущие правки.

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

Конфигурация

ПеременнаяЗначение по умолчаниюНазначение
TILQAZYNA_BASE_URLhttps://router.tilqazyna.kz/v1Адрес сервиса, который команда обозначила как свой self-hosted
TILQAZYNA_API_KEYпустоКлюч только на backend
ASR_MODELtil-asrРаспознавание с words=true
TEXT_MODELqwenИзвлечение и второй проход проверки
TEXT_BASE_URLпустоOpenAI-compatible endpoint командного self-hosted Qwen; адрес должен быть доступен backend (при локальном туннеле — соответствующий 127.0.0.1 endpoint)
DATA_DIRdataSQLite, аудио и локальные результаты
MAX_UPLOAD_MB100Ограничение размера записи
API_TIMEOUT_SECONDS900Таймаут каждого обращения к моделям
ADMIN_EMAIL / ADMIN_PASSWORD / ADMIN_NAMEпустоПервый администратор, создаётся только если активного администратора нет
SESSION_HOURS12Время жизни сессии после входа
REGISTRATION_MODEapprovalapproval: заявка ждёт администратора; open: сразу роль «Участник»; closed: регистрация отключена
SEED_PASSWORDпустоПароль командных учётных записей из backend/seed/users.json; без него они не создаются
SEED_FILEbackend/seed/users.jsonДругой файл с командными учётными записями
DIARIZATION_MODEL_PATHпустоЛокальный каталог модели pyannote
DATABASE_URLпустоPostgreSQL, например postgresql://user:[скрыто]@host:5432/hattama; пусто означает SQLite в DATA_DIR
MINIO_ENDPOINTпустоАдрес MinIO / S3, например minio:9000; пусто означает файлы в DATA_DIR
MINIO_ACCESS_KEY / MINIO_SECRET_KEYпустоУчётные данные хранилища
MINIO_BUCKEThattamaBucket создаётся при запуске, если его нет
MINIO_SECUREfalseHTTPS к хранилищу
PDF_FONT_PATH / PDF_FONT_BOLD_PATHпустоTTF со шрифтом кириллицы; без них ищутся DejaVu, Arial

В проверенной конфигурации LLM — qorytyn-qwen3.6-27b на командном H200; задайте её адрес через TEXT_BASE_URL и имя через TEXT_MODEL. Til-Qazyna и Qwen доступны только из разрешённой сети/через настроенный доступ NITEC ZTNA, а не как общедоступные endpoints. Не помещайте токены ZTNA, API-ключи или пароли в .env.example, Git или README. Приложение не использует OpenAI/NVIDIA API и не тратит их кредиты.

Диаризация

Для проекта сравнили NVIDIA NeMo TitaNet и pyannote и выбрали pyannote как основной вариант диаризации: он интегрирован в текущий GPU-процесс обработки. Это инженерный выбор команды, а не заявление о доказанном превосходстве по DER: сопоставимый количественный бенчмарк на размеченном наборе пока не опубликован.

Предоставленный контракт Til-Qazyna описывает timestamps слов, но не speaker labels. Поэтому акустическое разделение говорящих вынесено в опциональный локальный адаптер.

  1. На отдельной машине подготовки получите веса pyannote/speaker-diarization-community-1 по официальной инструкции. Для скачивания требуется принятие условий владельцем аккаунта.
  2. Установите pyannote.audio 4.x и необходимые ему PyTorch / FFmpeg согласно совместимости вашей ОС. Базовые requirements.txt их не устанавливают.
  3. Перенесите полный каталог модели в закрытый контур и укажите DIARIZATION_MODEL_PATH.
  4. Запретите исходящие соединения за пределы разрешённой инфраструктуры; при необходимости задайте HF_HUB_OFFLINE=1 и PYANNOTE_METRICS_ENABLED=0.
  5. После обработки назначьте имена акустическим меткам в интерфейсе. Тембр голоса сам по себе не сообщает ФИО.

Адаптер использует exclusive diarization при её наличии и максимальное пересечение с интервалом текстового сегмента. Это приближение: если сегмент содержит несколько реплик, нужна более точная разбивка по словам. Без модели интерфейс честно показывает «Говорящий не определён». Идентификация голоса не устанавливает ФИО: метки говорящих необходимо сопоставить с участниками вручную. См. настройку GPU-сервиса. Полноту и качество диаризации проверяйте на размеченных записях до использования в реальной работе.

Результаты анализа продукта: проверка двух сохранённых совещаний с qorytyn-qwen3.6-27b показала, что структурированные поручения и их источники извлекаются, но исполнители и сроки всё ещё требуют человеческой проверки; качество саммари и сохранение всех числовых фактов нестабильны между прогонами. Остались ошибки ASR в именах, регионах и датах, а также детали, отсутствующие в транскрипте. Эталонные DOCX не передавались модели. Методика, ограничения и результаты: docs/INTEGRATION_VALIDATION.md и docs/OWNER_SUMMARY_FOLLOWUP.md. Это оценка двух конкретных записей, не общий benchmark.

Роли и доступ

Вход по email и паролю (scrypt). Сессия хранится в базе как хеш токена и отзывается при выходе, смене пароля или отключении пользователя. После пяти неудачных попыток вход для этого email и адреса блокируется на 15 минут.

РольЧто может
АдминистраторПолный доступ: всё, что может секретарь, по всем совещаниям, плюс пользователи, роли, заявки, журнал и админ-панель
СекретарьЗагружает записи, проверяет и правит черновики, назначает председателя, участников и исполнителей, утверждает и выгружает протоколы
ПредседательВсё, что секретарь, но только для своих совещаний (создал сам или назначен председателем), без удаления
УчастникЧитает утверждённые протоколы совещаний, в которых указан участником; без аудио и полной JSON-выгрузки
АудиторВидит журнал действий и метаданные совещаний, но не содержание

Регистрация на странице /register создаёт учётную запись только с ролью «Участник»; роль выбрать нельзя. В режиме approval вход открывается после того, как администратор одобрит заявку в админ-панели и назначит роль, а отклонённая заявка удаляется. С одного адреса принимается не больше пяти регистраций в час.

Админ-панель (/admin) показывает заявки на регистрацию, число пользователей по ролям и совещаний по статусам, состояние базы, хранилища, сервиса моделей, PDF и диаризации, применённые миграции и последние действия из журнала. Администратор попадает в неё сразу после входа.

Любой пользователь, назначенный исполнителем поручения, после утверждения протокола видит свои поручения в /api/me/actions и может менять их статус. Утверждённый протокол закрыт для правок, пока секретарь или председатель не вернёт его на доработку. Журнал фиксирует входы, загрузки, правки, утверждения, прослушивание аудио, выгрузки и удаления.

Тесты

Из каталога backend/:

python -m unittest discover -s tests -v

Из каталога frontend/:

npm run typecheck && npm run lint && npm test && npm run build

Тесты не требуют сети и не используют настоящие ключи. Они проверяют вход и сессии, матрицу ролей, утверждение протокола, статусы поручений исполнителями, журнал действий, ограничение загрузок, подтверждение уведомления, конфликт версий, структуру DOCX, проверку цитат, сроки, миграцию старой базы и отсутствие выдуманных timestamps.

Проверка на PostgreSQL и MinIO (тест удаляет и пересоздаёт таблицы в указанной базе):

docker run -d --name hattama-test-pg -e POSTGRES_USER=hattama -e POSTGRES_PASSWORD=test-secret -e POSTGRES_DB=hattama -p 127.0.0.1:55432:5432 postgres:16-alpine
docker run -d --name hattama-test-minio -e MINIO_ROOT_USER=hattama -e MINIO_ROOT_PASSWORD=test-secret-minio -p 127.0.0.1:59000:9000 quay.io/minio/minio server /data
HATTAMA_TEST_DATABASE_URL=postgresql://hattama:[email protected]:55432/hattama \
HATTAMA_TEST_MINIO_ENDPOINT=127.0.0.1:59000 HATTAMA_TEST_MINIO_ACCESS_KEY=hattama \
HATTAMA_TEST_MINIO_SECRET_KEY=test-secret-minio python -m unittest tests.test_integration -v

Реальный прогон с сервером моделей:

python -m scripts.smoke /path/to/meeting.mp3 --meeting-date 2026-09-23

Он передаёт аудио и транскрипт только на настроенный TILQAZYNA_BASE_URL, сохраняет встречу локально и возвращает ненулевой код при ошибке. Запускайте CLI-прогон до запуска веб-сервера, чтобы не смешивать владельцев очереди. Эталонные PDF не подаются модели и не подменяют результат распознавания.

Архитектура и API

Описание модулей, состояний, ограничений и развития: ARCHITECTURE.md. Сценарий защиты: DEMO.md.

МетодПутьНазначение
GET/api/healthГотовность процесса и наличие настроек; без входа
POST/api/auth/login / logout / passwordВход, выход, смена пароля
GET/api/auth/meТекущий пользователь, роль и права
GET / POST/api/auth/registration / /api/auth/registerРежим регистрации / заявка name, email, password
GET/api/admin/overviewДанные админ-панели (администратор)
POST / DELETE/api/admin/registrations/{id}/approve / /api/admin/registrations/{id}Одобрить заявку с ролью role / отклонить
GET / POST / PATCH/api/users, /api/users/{id}Управление пользователями (администратор)
GET/api/users/directoryСписок имён для назначения председателя, участников и исполнителей
GET/api/auditЖурнал действий, фильтр meeting_id (администратор, аудитор)
GET / POST/api/meetingsДоступные совещания / загрузка multipart: title, meeting_date, recording_consent, audio
GET / DELETE/api/meetings/{id}Состояние и права пользователя / удаление вместе с записью
GET/api/meetings/{id}/audioИсходная запись
POST/api/meetings/{id}/retryПовтор после ошибки
PUT/api/meetings/{id}/reviewversion, analysis, speaker_names; конфликт возвращает 409
PUT/api/meetings/{id}/peoplechair_id, participant_ids
POST/api/meetings/{id}/approve / reopenУтверждение с проверкой version / возврат на доработку
PATCH/api/meetings/{id}/actions/{action_id}Статус поручения
GET/api/meetings/{id}/export/{format}docx, md, pdf, json из текущего состояния
GET/api/meetings/{id}/approved/{format}Копия docx / pdf, сохранённая в хранилище при последнем утверждении
GET/api/me/actionsПоручения текущего пользователя в утверждённых протоколах

Все маршруты кроме health, login и регистрации требуют Authorization: Bearer <token>.

Защита данных

Аудио, транскрипты и результаты находятся в data/, исключённом из Git и Docker build context. Ключи не передаются браузеру. Вывод модели отображается React как текст, HTML из ответов не вставляется; nginx и API отдают строгий Content-Security-Policy. Имя файла генерируется сервером. Перенаправления провайдера отключены. Персональные учётные записи и роли работают в одном рабочем пространстве; разделения организаций и SSO пока нет.

Для настоящего закрытого контура модели ASR и LLM должны находиться в разрешённой сети заказчика. Название self-hosted само по себе не гарантирует сетевую изоляцию: адрес, логи и политики хранения проверяются при развёртывании. Прототип не шифрует SQLite и аудио на диске, не содержит политики удаления и SSO; журнал фиксирует действия, но не хранит прежние версии текста. Для демонстрации реальных записей нужна анонимизация.

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

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

DarkhanOmirbay 23erzhanuly 11Rrmanoo 8Darkhan Omirbay 2

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

  • 2026-09-23 17:58docs: README presents covered requirements and bonus tasksDarkhanOmirbay
  • 2026-09-23 17:55Merge remote-tracking branch 'origin/main'DarkhanOmirbay
  • 2026-09-23 17:55feat: demo data for the jury, README coverage of the technical taskDarkhanOmirbay
  • 2026-09-23 17:54docs: clarify self-hosted model accesserzhanuly
  • 2026-09-23 17:51Merge branch 'main' of https://github.com/BAITC-Hacks/hack-e311b3ab-13labDarkhanOmirbay
  • 2026-09-23 17:49fix: ground summary deadlines and suggest explicit assigneeserzhanuly
  • 2026-09-23 17:49docs: refresh project READMEerzhanuly
  • 2026-09-23 17:49docs: record diarization model choice and findingserzhanuly
Все коммиты (44)
  • 2026-09-23 17:48feat(admin): create meetings from the admin panelDarkhanOmirbay
  • 2026-09-23 17:47feat(rbac): administrators get full access to every meetingDarkhanOmirbay
  • 2026-09-23 17:40fix(bot): do not leave Teams on transient lobby textDarkhanOmirbay
  • 2026-09-23 17:33merge: teammate analysis and refreshed UI with live meetings and meeting botDarkhanOmirbay
  • 2026-09-23 17:31fix(bot): Teams guest join (wait for pre-join, stable selectors, DOM click fallback)DarkhanOmirbay
  • 2026-09-23 17:26fix(frontend): nginx re-resolves the backend through Docker DNSDarkhanOmirbay
  • 2026-09-23 17:23feat: add admin analytics dashboard and integrate refreshed meeting UIRrmanoo
  • 2026-09-23 17:17feat(bot): meeting bot for Google Meet, Teams and Zoom web clientsDarkhanOmirbay
  • 2026-09-23 17:10fix: preserve summary coverage and review inferred ownerserzhanuly
  • 2026-09-23 17:04feat: integrate grounded meeting analysis and source reviewMukhammed Erzhanuly
  • 2026-09-23 16:53feat(live): live meeting sessions with browser tab and room microphone captureDarkhanOmirbay
  • 2026-09-23 15:54feat: integrate H200 speaker diarization with the updated frontendRrmanoo
  • 2026-09-23 15:36docs: team setup guide for running with docker composeDarkhanOmirbay
  • 2026-09-23 15:32feat: self-registration with admin approval and an admin panelDarkhanOmirbay
  • 2026-09-23 15:25feat(db): versioned migrations, pre-run migrate service and team seed accountDarkhanOmirbay
  • 2026-09-23 15:21chore: rename project to HATTAMA.AIDarkhanOmirbay
  • 2026-09-23 15:19feat(frontend): React app served by nginx in docker composeDarkhanOmirbay
  • 2026-09-23 15:07refactor: move the Python service into backend/DarkhanOmirbay
  • 2026-09-23 15:01feat(storage): PostgreSQL, MinIO and PDF protocolsDarkhanOmirbay
  • 2026-09-23 14:57feat(rbac): enforce roles, sessions and audit log across the APIDarkhanOmirbay
  • 2026-09-23 14:54merge: RBAC foundation modules into mainDarkhanOmirbay
  • 2026-09-23 14:52feat(rbac): add roles, permissions, password hashing and storage interfaceDarkhanOmirbay
  • 2026-09-23 14:44Delete inital_plan.mdRrmanoo
  • 2026-09-23 14:03docs: preserve meeting assistant plan and case materialsDarkhanOmirbay
  • 2026-09-23 13:59Create inital_plan.mdRrmanoo
  • 2026-09-23 13:58merge: preserve team README update with MVPerzhanuly
  • 2026-09-23 13:57feat: add Qorytyn meeting protocol MVPerzhanuly
  • 2026-09-23 12:19docs: add test commit markerRrmanoo
  • 2026-09-23 11:45docs: add Mukhamed is here messageerzhanuly
  • 2026-09-23 11:42Merge remote-tracking branch 'origin/main'erzhanuly
  • 2026-09-23 11:36docs: add Mukhammed Erzhanuly to teamerzhanuly
  • 2026-09-23 10:53Update README.mdRrmanoo
  • 2026-09-23 10:51Add GitHub connection check to READMEDarkhan Omirbay
  • 2026-09-23 10:44Add team members to READMEDarkhan Omirbay
  • 2026-09-23 00:46Update README.mdRrmanoo
  • 2026-09-23 00:45Update README.mdRrmanoo
Структура репозитория (177 файлов)
.env.example
.gitignore
README.md
backend/.dockerignore
backend/Dockerfile
backend/app/__init__.py
backend/app/access.py
backend/app/blobs.py
backend/app/config.py
backend/app/deadlines.py
backend/app/diarization.py
backend/app/exports.py
backend/app/integrations/__init__.py
backend/app/integrations/base.py
backend/app/integrations/bot.py
backend/app/integrations/browser_capture.py
backend/app/integrations/events.py
backend/app/integrations/factory.py
backend/app/integrations/registry.py
backend/app/live/__init__.py
backend/app/live/manager.py
backend/app/live/vad.py
backend/app/live/windows.py
backend/app/main.py
backend/app/migrations.py
backend/app/models.py
backend/app/pipeline.py
backend/app/quality.py
backend/app/rbac.py
backend/app/routes/__init__.py
backend/app/routes/bot.py
backend/app/routes/live.py
backend/app/schema.py
backend/app/security.py
backend/app/seed.py
backend/app/source_review.py
backend/app/store.py
backend/requirements.txt
backend/scripts/__init__.py
backend/scripts/create_user.py
backend/scripts/evaluate_saved.py
backend/scripts/migrate.py
backend/scripts/smoke.py
backend/seed/demo/audio/2beefa24ab6c4332bc0349d19b52962b.wav
backend/seed/demo/audio/31aa70f7ffae4b9d98db0edf41c878df.wav
backend/seed/demo/audio/517a11224d2b4075bc2e2361fc43d0d8.mp3
backend/seed/demo/meetings.json
backend/seed/demo/ozimiz.reference.txt
backend/seed/users.json
backend/tests/__init__.py
backend/tests/helpers.py
backend/tests/test_app.py
backend/tests/test_bot.py
backend/tests/test_dashboard.py
backend/tests/test_diarization.py
backend/tests/test_integration.py
backend/tests/test_live.py
backend/tests/test_merged_analysis.py
backend/tests/test_migrations.py
backend/tests/test_protocols.py
backend/tests/test_quality.py
backend/tests/test_rbac.py
backend/tests/test_registration.py
backend/tests/test_source_review.py
backend/tests/test_storage.py
backend/tests/test_text_endpoint.py
compose.yaml
docs/ARCHITECTURE.md
docs/DEMO.md
docs/H200.md
docs/INTEGRATION_VALIDATION.md
docs/OWNER_SUMMARY_FOLLOWUP.md
docs/QA_FRONTEND_HANDOFF.md
docs/SOURCE_REVIEW.md
docs/TEAM_SETUP.md
docs/TESTING.md
frontend/.dockerignore
frontend/.gitignore
frontend/Dockerfile
frontend/eslint.config.js
frontend/index.html
frontend/nginx/default.conf
frontend/nginx/security-headers.conf
frontend/package-lock.json
frontend/package.json
frontend/public/brand/conversation-to-protocol.png
frontend/public/brand/integrations/README.md
frontend/public/brand/integrations/gmeet.svg
frontend/public/brand/integrations/teams.svg
frontend/public/brand/integrations/zoom.svg
frontend/public/brand/mark.svg
frontend/public/favicon.svg
frontend/public/pcm-worklet.js
frontend/src/App.tsx
frontend/src/api/client.test.ts
frontend/src/api/client.ts
frontend/src/api/queries.ts
frontend/src/api/types.ts
frontend/src/auth/AuthProvider.tsx
frontend/src/auth/context.ts
frontend/src/auth/guards.tsx
frontend/src/components/AuthLayout.tsx
frontend/src/components/Brand.tsx
frontend/src/components/Dialog.tsx
frontend/src/components/Integrations.test.tsx
frontend/src/components/Integrations.tsx
frontend/src/components/LandingPage.test.tsx
frontend/src/components/LandingPage.tsx
frontend/src/components/Layout.tsx
frontend/src/components/PasswordDialog.tsx
frontend/src/components/ToastProvider.tsx
frontend/src/components/toast-context.ts
frontend/src/components/ui.tsx
frontend/src/features/admin/AdminMetrics.test.tsx
frontend/src/features/admin/AdminMetrics.tsx
frontend/src/features/qa/MeetingQA.test.tsx
frontend/src/features/qa/MeetingQA.tsx
frontend/src/features/qa/demo.ts
frontend/src/index.css
frontend/src/lib/format.test.ts
frontend/src/lib/format.ts
frontend/src/lib/labels.ts
frontend/src/lib/platform.test.ts
frontend/src/lib/platform.ts
frontend/src/live/capture.test.ts
frontend/src/live/capture.ts
frontend/src/live/useCapture.ts
frontend/src/live/useLiveEvents.ts
frontend/src/main.tsx
frontend/src/pages/AdminPage.test.tsx
frontend/src/pages/AdminPage.tsx
frontend/src/pages/AuditPage.tsx
frontend/src/pages/LoginPage.test.tsx
frontend/src/pages/LoginPage.tsx
frontend/src/pages/MeetingsPage.tsx
frontend/src/pages/NewMeetingPage.tsx
frontend/src/pages/NotFoundPage.tsx
frontend/src/pages/RegisterPage.test.tsx
frontend/src/pages/RegisterPage.tsx
frontend/src/pages/TasksPage.tsx
frontend/src/pages/UsersPage.tsx
frontend/src/pages/live/LiveStartPage.tsx
frontend/src/pages/live/LiveView.test.tsx
frontend/src/pages/live/LiveView.tsx
frontend/src/pages/meeting/ActionCard.test.tsx
frontend/src/pages/meeting/ActionCard.tsx
frontend/src/pages/meeting/MeetingPage.tsx
frontend/src/pages/meeting/MeetingWorkspace.test.tsx
frontend/src/pages/meeting/MeetingWorkspace.tsx
frontend/src/pages/meeting/PeoplePanel.tsx
frontend/src/pages/meeting/SourceReviewPanel.test.tsx
frontend/src/pages/meeting/SourceReviewPanel.tsx
frontend/src/pages/meeting/TranscriptPanel.tsx
frontend/src/test/fetch.ts
frontend/src/test/render.tsx
frontend/src/test/setup.ts
frontend/src/test/websocket.ts
frontend/tsconfig.json
frontend/vite.config.ts
scripts/gpu-tunnel.sh
services/diarization/requirements.txt
services/diarization/server.py
services/diarization/start.sh
services/meeting-bot/Dockerfile
services/meeting-bot/bot/__init__.py
services/meeting-bot/bot/adapters/__init__.py
services/meeting-bot/bot/adapters/base.py
services/meeting-bot/bot/adapters/google_meet.py
services/meeting-bot/bot/adapters/teams.py
services/meeting-bot/bot/adapters/zoom.py
services/meeting-bot/bot/app.py
services/meeting-bot/bot/audio.py
services/meeting-bot/bot/browser.py
services/meeting-bot/bot/login.py
services/meeting-bot/bot/session.py
services/meeting-bot/requirements.txt
services/meeting-bot/start.sh
Ветки (3)

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

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

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