AAYMA
RU
Войти

Протокол совещаний с фиксацией поручений

Система, которая по аудио/видео совещания строит стенограмму с разделением говорящих, извлекает поручения (кто/что/срок) с проверенными цитатами и…

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

О проекте

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

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

Суть

Система, которая по аудио/видео совещания строит стенограмму с разделением говорящих, извлекает поручения (кто/что/срок) с проверенными цитатами и выгружает протокол в DOCX.

Как устроено

ffmpeg → WAV 16 кГц → локальный Vosk (vosk-model-ru и vosk-model-kz, проба языка, для шала-казахского слияние двух моделей на уровне слов) → диаризация pyannote + titanet через sherpa-onnx → разбор стенограммы Claude или локальной Ollama qwen2.5 в JSON (what/who/due/priority/quote/confidence) → код проверяет дословные цитаты, имена и превращает сроки словами в даты со статусами «просрочено/горит». Провайдеры STT и анализа спрятаны за единый контракт: три контура (полностью локальный, смешанный, облачный с Soniox).

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

  • Vosk STT
  • слияние ru/kz моделей на уровне слов
  • pyannote
  • titanet
  • sherpa-onnx
  • диаризация
  • проверка цитат кодом
  • восстановление имён говорящих из диалога
  • парсинг русских формулировок сроков
  • DOCX export

Метрики

WER 4.49 на корпусе KSC для vosk-model-kz; эталонный демо-сценарий: 3 решения и 4 поручения

Особенности

Принцип «отказ вместо выдумки»: не названные ответственный/срок остаются пустыми с низкой уверенностью; цитату ищет код, а не модель; клик по поручению подсвечивает момент в записи. Один код с тремя контурами развёртывания (закрытый/смешанный/облачный) переключается двумя строками .env.

Стек

Python · http.server · ffmpeg (imageio-ffmpeg) · HTML · sherpa-onnx

Модели

Claude claude-opus-5 · Ollama qwen2.5 · Vosk vosk-model-ru · Vosk vosk-model-kz · Soniox


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

README

README.md

Протокол совещаний с фиксацией поручений

HackAlem AI 2026 · трек 08 «Инновации» · команда First time

Совещание закончилось, все разошлись — и через неделю выясняется, что задачу никто не сделал, потому что каждый запомнил договорённость по-своему. Система слушает запись и достаёт из неё поручения: кто, что и к какому сроку.

Вот что происходит на самом деле. Из записи:

[00:54] Марат   Отлично. Тимур, по выгрузке — посмотри, пожалуйста, что там
                с таймаутом, и если это на стороне банка, напиши им в поддержку.
                Успеешь до среды?
[01:08] Тимур   До среды успею.

Получается:

Разобраться с таймаутом выгрузки отчётов Тимур · срок: среда · 00:54 · ✓ цитата проверена · уверенность 93%

А через минуту в том же разговоре:

[01:20] Айдана  Кстати, надо бы ещё посмотреть логи за прошлую неделю,
                там было несколько странных ошибок ночью.

Получается:

Посмотреть логи за прошлую неделю ответственный не назначен · срок не назван · 01:20 · уверенность 45%

Второе поручение — то, ради чего всё и построено. Модель вполне могла бы уверенно вписать сюда Айдану и «до пятницы»: звучит правдоподобно, проверять никто не станет. Она этого не делает. Имени не прозвучало — значит поля пустые.

Так же и с цитатами: каждое поручение привязано к дословному фрагменту стенограммы, и фрагмент ищет код, а не модель. Не нашёл — поручение помечено неподтверждённым. Клик по карточке подсвечивает момент в записи, откуда оно взято. Протокол, в котором можно ткнуть в любую строку и услышать, откуда она, — это совсем другой уровень доверия, чем красивый пересказ.

Дальше: сводка, решения, статусы сроков («просрочено», «горит»), выгрузка в DOCX. Распознавание речи, разделение говорящих и, при желании, разбор текста работают полностью на машине — аудио не уходит никуда.


Быстрый старт

Нужен Python 3.8 или новее.

pip install -r requirements.txt
python scripts/get_models.py
ANALYSIS_PROVIDER=demo python app.py

Откройте http://localhost:8000 и нажмите «Разобрать пример».

Проверка основного сценария за 2 минуты

Ключи не нужны. ANALYSIS_PROVIDER=demo фиксирует эталонный режим, чтобы результат совпал в точности: без него проект возьмёт то, что доступно на машине.

Ожидаемый результат — протокол планёрки, 3 решения и 4 поручения:

ЧтоКтоСрокУверенность
Макет экрана оплатыАйданачетверг95%
Таймаут выгрузки отчётовТимурсреда93%
Список клиентов для пилотаМаратпятница90%
Посмотреть логине назначенне назван45%

Дальше:

  1. Нажмите на карточку поручения — в стенограмме справа подсветится момент, из которого оно извлечено.
  2. Нажмите «Подтвердить» — статус сохранится в storage/meetings/<id>.json.
  3. Нажмите «Скачать DOCX» — протокол выгрузится файлом.

Четвёртое поручение — главное в этой проверке. В записи прозвучало «надо бы посмотреть логи», ответственный и срок названы не были. Система их не выдумывает, а честно помечает низкой уверенностью.

Своя запись. Кнопка «Загрузить запись…», любой аудио- или видеофайл. Для неё нужны локальные модели (python scripts/get_models.py) — без них система скажет об этом прямо, а не подставит демо-пример. Для разбора нужен ключ, см. раздел «Настройки».

Без браузера:

curl -s http://localhost:8000/api/health
curl -s -X POST http://localhost:8000/api/analyze -H "Content-Type: application/json" -d "{}"

Соответствие обязательному минимуму

#Требование заданияКак закрыто
1Распознавание речи участниковVosk локально, meeting/stt_local.py
2Понимание русского языкаvosk-model-ru, проверено на записях задания
3Понимание казахского языкаvosk-model-kz, WER 4.49 на корпусе KSC
4Понимание шала-казахскогостенограмма собирается из двух моделей: русская даёт основу, казахские слова подставляются из казахской
5Поручения с ответственным и срокомmeeting/pipeline.py, извлечение и проверка цитат
6Диаризация с привязкой к человекуpyannote + titanet локально, meeting/diarize.py
7Экспорт протокола в файлDOCX, meeting/export_docx.py

Сценарий 2 — контроль сроков — закрыт в meeting/deadlines.py. Срок, названный словами («до пятницы», «к пятнадцатому октября»), превращается в дату, а поручение получает статус «просрочено», «срок сегодня», «горит» или «в работе». Сводка видна в шапке совещания и попадает в DOCX. Отсчёт идёт от даты совещания, статус — от сегодняшнего дня.


Что отличает решение

Проверяемость вместо доверия модели. Каждое поручение привязано к точной цитате и таймкоду: клик по карточке подсвечивает момент, где это прозвучало. Цитату проверяет код, а не модель — если дословный фрагмент в стенограмме не найден, поручение помечается неподтверждённым и его уверенность снижается.

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

Человек принимает решение. Каждое поручение подтверждается или отклоняется.

Кто есть кто. Распознавание различает голоса, но имён не знает. Имена восстанавливаются из самого разговора: если к человеку обратились «Тимур, сможешь…», отвечающий следующим — Тимур. Имя принимается, только если оно действительно прозвучало в записи. Того, чьё имя не назвали ни разу, система показывает меткой «Спикер 1» и даёт назвать его в один клик — имя сразу расходится по стенограмме и по ответственным.


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

запись (аудио или видео, любой формат)
      │  ffmpeg -> WAV 16 кГц моно
      ▼
[ Vosk, локально ]  проба языка по началу записи; на смешанной
      │             речи казахские слова подставляются в русскую основу
      ▼
[ pyannote + titanet ]  границы речи, смена говорящего, кластеризация голосов
      ▼  сегменты: время + говорящий + текст
[ разбор совещания ]  Claude или локальная модель
      │  JSON: speakers + what / who / due / priority / quote / confidence
      ▼
[ проверка кодом ]  цитата ищется в стенограмме, таймкод берётся оттуда же;
      │             имя принимается, только если звучало; срок превращается
      │             в дату и статус контроля
      ▼
[ интерфейс ]  карточки поручений ↔ стенограмма, подтверждение,
      │         именование говорящих, экспорт DOCX
      ▼
storage/meetings/<id>.json

Технологии

СлойЧем сделано
БэкендPython 3.8+, стандартная библиотека (http.server)
Распознавание речиVosk локально: vosk-model-ru, vosk-model-kz
Разделение говорящихpyannote + titanet через sherpa-onnx, локально
Аудио и видеоffmpeg из пакета imageio-ffmpeg, отдельная установка не нужна
Разбор совещанияAnthropic Claude claude-opus-5
Разбор без облакаOllama (qwen2.5), включается ANALYSIS_PROVIDER=local
Сроки и контрольразбор русских формулировок даты, meeting/deadlines.py
ЭкспортDOCX собирается стандартной библиотекой
Интерфейсодин HTML-файл без сборки и фреймворков

Структура

app.py                     HTTP-сервер и маршруты
meeting/stt_local.py       локальное распознавание, проба языка, слияние моделей
meeting/diarize.py         разделение на говорящих
meeting/pipeline.py        выбор провайдеров, извлечение поручений, проверка цитат
meeting/deadlines.py       сроки словами -> даты и статусы контроля
meeting/analyze_local.py   разбор локальной моделью через Ollama
meeting/export_docx.py     сборка DOCX
meeting/store.py           хранилище совещаний
web/index.html             интерфейс
scripts/get_models.py      загрузка локальных моделей
samples/                   демо-стенограмма и сценарий тестовой записи

Закрытый контур

Задание запрещает передавать аудио во внешние облачные API и требует возможности развернуть решение у заказчика.

  • Распознавание речи и разделение говорящих работают полностью локально. Модели Vosk (Apache 2.0) лежат в models/, аудио не покидает машину; интернет для этой части не нужен вообще.
  • Разбор текста тоже может остаться на машине: ANALYSIS_PROVIDER=local включает локальную модель через Ollama. Качество ниже — замеры приведены ниже, мы их не прячем.
  • Загруженный файл живёт во временном файле и удаляется сразу после разбора. Результаты лежат в storage/ и не версионируются. Ключи — только в .env.

Один код — три контура

Локальный и облачный режимы — это не две версии продукта и не ветки в репозитории. Это один и тот же код с разной настройкой, и переключение занимает столько, сколько нужно на правку двух строк в .env.

КонтурSTT_PROVIDERANALYSIS_PROVIDERЧто это значит для заказчика
ЗакрытыйlocallocalНаружу не уходит ничего. Нужно железо и 6 ГБ моделей
СмешанныйlocalclaudeАудио не покидает периметр, наружу идёт только текст
ОблачныйsonioxclaudeМаксимум качества, железо не нужно

Средняя строка — самая практичная. Голос — это биометрия, и запрет обычно касается именно аудиозаписи; обезличенный текст многие организации выпускать наружу готовы. Такой контур даёт качество разбора, близкое к облачному, не вынося записи совещаний за периметр.

Работает это потому, что провайдеры спрятаны за один контракт: распознавание — это «дай файл, получи сегменты с временем, говорящим и текстом», разбор — «дай стенограмму, получи JSON с поручениями». Всё, что выше по конвейеру — проверка цитат, восстановление имён, сроки, экспорт, интерфейс — не знает, кто именно считал, и не меняется при переключении. Новый провайдер — это один модуль на пару десятков строк.

Практический смысл:

  • Продавать можно и туда, и туда. Госорган и банк получают локальную сборку, небольшой бизнес — облачную, без отдельной разработки под каждого.
  • Пилот не упирается в закупку железа. Начинают на облаке, переезжают в закрытый контур, когда служба безопасности согласует, — переезд ничего не переписывает.
  • Поставщик заменяем. Кончились кредиты, изменились цены, появилась модель получше — меняется модуль, а не продукт.
  • Провал одного пути не роняет систему. Недоступен провайдер — режимы честно показываются в интерфейсе, а демо-режим работает всегда.

Настройки

Скопируйте .env.example в .env. Всё необязательно: без настроек проект работает локально, а без моделей — в демо-режиме.

ПеременнаяНазначение
ANALYSIS_PROVIDERdemo, local (Ollama) или claude. Без неё: есть ключ Anthropic — облако, иначе локальная модель, иначе демо
ANTHROPIC_API_KEYключ для облачного разбора
STT_PROVIDERlocal (по умолчанию) или soniox
SONIOX_API_KEYнужен только при STT_PROVIDER=soniox
STT_MIXED0 отключает разбор двумя моделями, ускоряет примерно вдвое
STT_LANGUAGEзафиксировать язык (ru/kz) и пропустить пробу
DIARIZATION_THRESHOLDпорог склейки голосов, по умолчанию 0.70
DIARIZATION_SPEAKERSсколько участников ожидать; 0 — определить само
LOCAL_ANALYSIS_MODELмодель Ollama, по умолчанию qwen2.5:3b-instruct
PORTпорт сервера, по умолчанию 8000

Модели

python scripts/get_models.py скачивает компактный набор (~180 МБ) и модели разделения говорящих. Качество распознавания напрямую зависит от размера модели: компактной хватает на чистой записи, на телефонной записи в шумном помещении — нет.

python scripts/get_models.py --full

Добавляет большие модели (~3 ГБ). Скрипт сам предпочитает большую, если она есть.


API

МетодПутьНазначение
GET/api/healthстатус и текущие режимы работы
POST/api/analyze{} — демо-пример; {filename, audio_b64, title} — своя запись
GET/api/meetingsсписок разобранных совещаний
GET/api/meetings/<id>одно совещание целиком
GET/api/meetings/<id>/export.docxпротокол файлом DOCX
POST/api/meetings/<id>/items/<item_id>{"status": "confirmed"|"rejected"|"pending"}
POST/api/meetings/<id>/speakers{"from": "Спикер 1", "to": "Марат"}

У поручения в ответе есть due_date и deadline (код статуса, подпись, дней до срока), у совещания — deadline_summary.


Замеры

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

Скорость

Двухминутная запись, модели в памяти:

время
Первый разбор после старта сервера197 с
Каждый следующий67 с

Разница — загрузка трёхгигабайтной модели с диска. Поэтому модели греются в фоне при старте, а язык определяется по первым 45 секундам лёгкими моделями.

Размер модели

МодельУверенностьРаспознано говорящих
vosk-model-small-ru-0.22, 88 МБ0.8601
vosk-model-ru-0.42, 3 ГБ0.9244

На чистой записи из задания компактная модель даёт 0.913 — разница проявляется именно на сложном звуке.

Разделение говорящих

Сначала мы брали вектор голоса, который Vosk отдаёт на целую реплику. Это не работает: реплика кончается на длинной паузе, а в живом совещании внутри неё успевают высказаться двое — вектор усредняется, и все сливаются в одного.

вектор голоса Voskpyannote + titanet
Распознано участников (их трое)13
Реплик в стенограмме725

Смешанная речь

Соединять результаты двух моделей по уверенности нельзя: Vosk уверенно ошибается на чужом языке. Фраза «жарайды келісемін» превратилась у русской модели в «жара и до семин» — с уверенностью 0.896, выше, чем у казахской на том же куске.

Признак надёжнее: в казахском есть буквы (ә, ғ, қ, ң, ө, ұ, ү, һ, і), которых в русском алфавите нет, и русская модель выдать их физически не может. Слово с такими буквами — якорь казахской речи. Слияние идёт на уровне слов: русская стенограмма остаётся основой, на месте якорей встают слова казахской модели, если они перекрывают русское слово по времени больше чем наполовину и распознаны увереннее.

стенограмма
Только русская модель«…до следующего цикла жара и до семин тогда…»
После слияния«…до следующего цикла жара и до келісемін тогда…»

Заменено одно слово; «сейчас», которое казахская модель услышала как «сияқты», осталось на месте — проверка уверенности отсекла подмену.

Разбор: локально против облака

Эталон — размеченная вручную планёрка, 4 поручения с известными ответственными:

qwen2.5:3b локальноclaude-opus-5
Время12 с19 с
Найдено верно2 из 44 из 4
Выдумано лишнего10

Вывод честный: в закрытом контуре разбор работает, но качество падает, и маленькая модель склонна додумывать ответственного — ровно то, против чего построена проверка цитат. Поэтому провайдер вынесен в настройку.

Воспроизводимость

Репозиторий клонирован в чистую папку, создано новое виртуальное окружение, установлены зависимости из requirements.txt. Всё встало, проект запустился, контрольный сценарий совпал с описанным выше, DOCX выгрузился и открылся.


Ограничения

Пишем прямо, чтобы не было сюрпризов на проверке.

  • Качество упирается в запись. На чистых записях из задания локальные модели дают связную стенограмму и верные поручения. На телефонной записи в шумном зале распознавание заметно хуже, и это тянет за собой имена и ответственных.
  • Якорем казахской речи служит только слово с казахскими буквами. Казахские слова, которые пишутся русскими буквами («жарайды»), так не ловятся. На участке, где обе модели слышат мусор, слияние может подставить одно непонятное слово вместо другого.
  • Локальный разбор слабее облачного — цифры выше.
  • Разбор двумя моделями примерно вдвое дольше. Отключается STT_MIXED=0.
  • Подключение к Teams, Zoom и Google Meet не реализовано: принимается готовая запись.

Что дальше

  • Подключение к Teams, Zoom и Meet как участник и живая расшифровка по ходу встречи: у Vosk есть потоковый режим, интерфейс провайдера к этому готов.
  • Автоматическая рассылка напоминаний ответственным при приближении срока.
  • Дашборд статусов поручений и интеграция с СЭД.
  • Словарь терминов организации, чтобы имена и отраслевые слова распознавались точнее.
  • Локальная модель побольше для разбора — закрывает контур без потери качества.
  • Развёртывание в закрытом контуре «под ключ»: образ со всеми моделями, чтобы установка у заказчика не требовала интернета вообще.

Сторонние и заранее подготовленные компоненты

В соответствии с пунктами 5.4.4 и 5.4.4.2 Положения:

  • Модели: Vosk vosk-model-ru, vosk-model-kz, vosk-model-spk (Apache 2.0); pyannote segmentation и NeMo titanet в ONNX из релизов sherpa-onnx.
  • Библиотеки: vosk (Apache 2.0), sherpa-onnx (Apache 2.0), imageio-ffmpeg (BSD, включает ffmpeg под LGPL), numpy (BSD), anthropic (MIT). Остальное — стандартная библиотека Python.
  • Внешние сервисы: Anthropic Claude для разбора текста, Soniox как опциональный провайдер распознавания. Оба по умолчанию выключены.
  • Заранее подготовленное: у команды есть личный проект расшифровки аудио, из опыта которого взяты подходы к работе с распознаванием речи и к деградации на заглушки при недоступности внешнего сервиса. Доменная логика этого проекта — протокол совещания, поручения с ответственным и сроком, проверка цитат, восстановление имён, контроль сроков, экспорт — разработана в рамках соревновательной части хакатона.
  • AI-инструменты: разработка велась с использованием AI-ассистентов, что разрешено пунктом 5.4.12 Положения.

Данные

Демо-запись samples/planerka.json — постановочная, составлена командой. Персональных данных и записей реальных людей проект не содержит.

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

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

A11ert 23

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

  • 2026-09-23 17:43.env.example приведён в соответствие с README и кодомA11ert
  • 2026-09-23 17:40Сроки периодами словами: «за две недели», «на этой неделе», «через месяц»A11ert
  • 2026-09-23 17:31README: раздел про переключение между локальным и облачным контуромA11ert
  • 2026-09-23 17:28README: начинается с живого примера вместо описанияA11ert
  • 2026-09-23 17:24Честная ошибка вместо подмены: загруженная запись без моделейA11ert
  • 2026-09-23 17:20README переписан: быстрый старт в начале, замеры собраны в один разделA11ert
  • 2026-09-23 17:18Слияние моделей включено по умолчанию, порог разрыва убранA11ert
  • 2026-09-23 17:07Шала-казахский: слияние двух моделей на уровне словA11ert
Все коммиты (23)
  • 2026-09-23 16:43Детерминированный контрольный сценарий: явный выбор провайдера разбораA11ert
  • 2026-09-23 16:41Контроль сроков поручений: второй сценарий заданияA11ert
  • 2026-09-23 16:29Ускорение распознавания: проба языка и прогрев моделейA11ert
  • 2026-09-23 16:06Локальный разбор совещания через Ollama как провайдерA11ert
  • 2026-09-23 15:52Список прошлых разборов в интерфейсе и приём видеофайловA11ert
  • 2026-09-23 15:45Отдельная модель диаризации вместо векторов VoskA11ert
  • 2026-09-23 15:30README: замеры качества распознавания на компактной и большой моделяхA11ert
  • 2026-09-23 15:26Чистка стенограммы: фильтр нераспознанных слов и разбиение по паузамA11ert
  • 2026-09-23 15:18Экспорт протокола в DOCX и загрузчик локальных моделейA11ert
  • 2026-09-23 15:14Локальное распознавание речи: Vosk + локальная диаризацияA11ert
  • 2026-09-23 14:44Восстановление имён говорящих и ручное именованиеA11ert
  • 2026-09-23 14:14Сценарий тестовой записи: восемь проверочных ситуаций и ожидаемый результатA11ert
  • 2026-09-23 13:56Конвейер: стенограмма, извлечение поручений, проверка цитат по текстуA11ert
  • 2026-09-23 13:56Веб-интерфейс: карточки поручений, связь со стенограммой, подтверждениеA11ert
  • 2026-09-23 13:56Каркас проекта: README, зависимости, шаблон окруженияA11ert
Структура репозитория (19 файлов)
.claude/launch.json
.env.example
.gitignore
README.md
app.py
meeting/__init__.py
meeting/analyze_local.py
meeting/deadlines.py
meeting/diarize.py
meeting/export_docx.py
meeting/pipeline.py
meeting/store.py
meeting/stt_local.py
requirements.txt
samples/planerka.analysis.json
samples/planerka.json
samples/scenario.md
scripts/get_models.py
web/index.html

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

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

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