О проекте
Команда: 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.mdHATTAMA.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 у секретаря; работает с любой платформой | Выбрать вкладку и включить «Поделиться звуком вкладки»; голос секретаря добавляется с микрофона |
| Микрофон в переговорной | Очная встреча | Доступ к микрофону компьютера |
Как это работает:
- Звук поступает на backend как PCM 16 кГц по WebSocket.
- Silero VAD (sherpa-onnx, CPU) выделяет речь и режет её на фрагменты 8–25 секунд по паузам; тишина и шум не отправляются в распознавание.
- Каждый фрагмент распознаётся
til-asr; таймкоды слов сдвигаются на начало фрагмента, текст появляется в интерфейсе обычно через 10–30 секунд. - По кнопке «Завершить» или когда встреча закончилась запись становится обычным WAV в хранилище, а транскрипт сохраняется как уже распознанный.
- Дальше работает тот же конвейер, что и для загруженной записи: диаризация, извлечение и проверка поручений, протокол. Повторного распознавания нет.
Надёжность: при перезапуске сервера записанная часть сохраняется и доступна для повтора; сессия без звука завершается сама; ограничены длительность (3 часа) и число одновременных сессий (2).
Бот для Teams, Zoom и Google Meet
Отдельный сервис meeting-bot в compose.yaml: Chromium на виртуальном дисплее, у каждой встречи свой виртуальный звуковой вход PulseAudio. Бот записывает только то, что проигрывает веб-клиент встречи, поэтому способ передачи звука платформой не важен. Собственный микрофон бота — пустой источник, реального микрофона в контейнере нет.
Что делает бот:
- Открывает ссылку, вводит имя «HATTAMA.AI Секретарь», выключает камеру и микрофон, просит войти.
- Ждёт в зале ожидания до 10 минут, сообщает статус в интерфейс.
- После входа пишет в чат встречи: «Ведётся запись и расшифровка совещания ИИ-секретарём HATTAMA.AI».
- Передаёт звук и список участников в backend; интерфейс показывает, кто сейчас в встрече.
- Выходит, когда встреча закончилась или секретарь нажал «Завершить».
Архитектура интеграции: 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 |
| Backend | Python 3.12, FastAPI, Pydantic 2, SQLAlchemy 2, WebSocket (wsproto), fpdf2, python-docx |
| Данные | PostgreSQL 16 (JSONB, версионные миграции), MinIO (S3) |
| Frontend | React 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
Сценарий работы
- Введите название и реальную дату совещания. Дата загрузки не обязательно совпадает с датой встречи.
- Выберите запись и подтвердите, что участники уведомлены об обработке.
- Дождитесь распознавания, анализа и проверки. Статус обновляется автоматически.
- Проверьте саммари, ответственных и сроки; прослушайте спорные цитаты.
- Исправьте поля и отметьте проверенные поручения. Можно менять статус на «В работе» / «Выполнено».
- Сохраните или экспортируйте. Экспорт сначала сохраняет текущие правки.
Неопределённый срок остаётся пустым. «На следующей неделе», «после совещания» и «к среде», если встреча в среду, не преобразуются в произвольную дату. Точная цитата доказывает наличие текста, но сама по себе не доказывает правильность интерпретации модели.
Конфигурация
| Переменная | Значение по умолчанию | Назначение |
|---|---|---|
TILQAZYNA_BASE_URL | https://router.tilqazyna.kz/v1 | Адрес сервиса, который команда обозначила как свой self-hosted |
TILQAZYNA_API_KEY | пусто | Ключ только на backend |
ASR_MODEL | til-asr | Распознавание с words=true |
TEXT_MODEL | qwen | Извлечение и второй проход проверки |
TEXT_BASE_URL | пусто | OpenAI-compatible endpoint командного self-hosted Qwen; адрес должен быть доступен backend (при локальном туннеле — соответствующий 127.0.0.1 endpoint) |
DATA_DIR | data | SQLite, аудио и локальные результаты |
MAX_UPLOAD_MB | 100 | Ограничение размера записи |
API_TIMEOUT_SECONDS | 900 | Таймаут каждого обращения к моделям |
ADMIN_EMAIL / ADMIN_PASSWORD / ADMIN_NAME | пусто | Первый администратор, создаётся только если активного администратора нет |
SESSION_HOURS | 12 | Время жизни сессии после входа |
REGISTRATION_MODE | approval | approval: заявка ждёт администратора; open: сразу роль «Участник»; closed: регистрация отключена |
SEED_PASSWORD | пусто | Пароль командных учётных записей из backend/seed/users.json; без него они не создаются |
SEED_FILE | backend/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_BUCKET | hattama | Bucket создаётся при запуске, если его нет |
MINIO_SECURE | false | HTTPS к хранилищу |
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. Поэтому акустическое разделение говорящих вынесено в опциональный локальный адаптер.
- На отдельной машине подготовки получите веса
pyannote/speaker-diarization-community-1по официальной инструкции. Для скачивания требуется принятие условий владельцем аккаунта. - Установите
pyannote.audio4.x и необходимые ему PyTorch / FFmpeg согласно совместимости вашей ОС. Базовыеrequirements.txtих не устанавливают. - Перенесите полный каталог модели в закрытый контур и укажите
DIARIZATION_MODEL_PATH. - Запретите исходящие соединения за пределы разрешённой инфраструктуры; при необходимости задайте
HF_HUB_OFFLINE=1иPYANNOTE_METRICS_ENABLED=0. - После обработки назначьте имена акустическим меткам в интерфейсе. Тембр голоса сам по себе не сообщает ФИО.
Адаптер использует 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}/review | version, analysis, speaker_names; конфликт возвращает 409 |
| PUT | /api/meetings/{id}/people | chair_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; журнал фиксирует действия, но не хранит прежние версии текста. Для демонстрации реальных записей нужна анонимизация.
Авторы коммитов
История коммитов
- 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
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.