Чат с базой ваших UX-исследований. Загрузите отчеты, сырые данные и записи интервью — и задавайте вопросы. Ответы ссылаются на конкретную страницу, строки или таймкод источника.
🇬🇧 English version: README.en.md
Статус: рабочий MVP, используется внутри команды исследователей.
- База знаний из исследований. Исследование — это название, дата и любое число файлов: PDF-отчеты, CSV с сырыми данными, аудио- и видеозаписи интервью. Файлы автоматически режутся на фрагменты, векторизуются и индексируются; медиа транскрибируется с таймкодами.
- Ответы только по источникам, с цитатами. Каждый тезис помечен маркером
[n]. Клик открывает панель источника: страницу PDF, строки CSV или фрагмент интервью с мини-плеером, который перематывает на нужное место. Если в базе ничего нет — ассистент честно говорит об этом, а не выдумывает. - Честные источники. Цитаты — снимки: если исследование удалили, старые чаты покажут, на что ссылались, с пометкой «источник удален», а в новых ответах удаленное больше не появится.
- Чаты с историей. Уточняющие вопросы переформулируются с учетом контекста, даты в вопросе мягко приоритизируют исследования того периода, номера цитат сквозные внутри чата, ответы рендерятся как Markdown. Чатом можно поделиться по ссылке и отозвать доступ.
- Любые модели. Три слота — чат, эмбеддинги, транскрибация — каждый указывает на любой OpenAI-совместимый API: OpenAI, OpenRouter, локальные Ollama/vLLM, GigaChat и т.д. Конфиг перечитывается на лету; при смене модели эмбеддингов — переиндексация в один клик.
- Админка (Супердоступ). Управление исследованиями и файлами (с пометками к файлам, которые улучшают поиск), выдача доступа пользователям, переключение моделей.
- Опциональная локальная транскрибация. Контейнер с GigaAM дает русское распознавание речи с таймкодами и нормализацией чисел полностью на вашей машине — аудио никуда не уходит.
Нужны Docker с Compose v2 и API-ключ любого OpenAI-совместимого провайдера.
git clone <этот репозиторий> hypotenium && cd hypotenium
cp .env.example .env # впишите ключ в LLM_API_KEY
docker compose up -dОткройте http://localhost:3000 и войдите как admin@example.com / admin12345 (смените оба значения в .env, прежде чем давать доступ кому-то еще). Затем Супердоступ → Исследования: добавьте исследование с парой файлов, дождитесь обработки и задайте вопрос.
Первый запуск собирает два образа (3–5 минут). Данные лежат в docker-volume'ах (pgdata, files-data) и переживают перезапуски и пересборки.
Исследования добавляют администраторы в разделе Супердоступ → Исследования (пункт «Супердоступ» внизу бокового меню виден только админам).
- Нажмите «+ Добавить исследование».
- Заполните название и дату проведения. Дата важна: по ней работает мягкий фильтр, когда в вопросе упоминается период («что говорили в 2024 году»). Комментарий к исследованию необязателен.
- Прикрепите файлы — сколько угодно за раз: PDF-отчеты, CSV с сырыми данными (кодировка и разделитель определяются автоматически), аудио и видео интервью (до 500 МБ каждый). К каждому файлу можно написать комментарий: что внутри, какой сегмент, кто респондент. Комментарий попадает в контекст поиска, поэтому чем точнее пометка — тем лучше находятся нужные фрагменты.
- Сохраните форму. Файлы уйдут в фоновую обработку: извлечение текста, транскрибация медиа, нарезка на фрагменты, эмбеддинги. Статус виден в таблице («в очереди» → «обработка…» → «готово»), список обновляется сам. Транскрибация часового интервью занимает минуты, остальное — секунды.
Внутри исследования можно дописать файлы позже, поменять комментарии (индекс по файлу пересчитается) и удалить исследование целиком — тогда оно исчезает из поиска, а в старых чатах его цитаты помечаются как «источник удален».
Главный экран — поле вопроса. Спрашивайте как коллегу: «Почему пользователи бросают оформление карты?», «Что респонденты говорили про уведомления в интервью 2024 года?». Ассистент ищет по всей базе и отвечает только по найденному: каждый тезис помечен номером [n], клик по нему открывает панель источника — страницу PDF, строки CSV или момент интервью с плеером. Если в базе ничего нет, он так и скажет, а не додумает.
Уточняющие вопросы задавайте в том же чате — контекст сохраняется. Кнопка «Поделиться» дает ссылку на чат для других пользователей сервиса; доступ можно отозвать.
- Первый администратор создается автоматически из
ADMIN_EMAIL/ADMIN_PASSWORDв.env. - Остальные регистрируются сами по ссылке «Зарегистрироваться» на экране входа (email + пароль) и попадают в статус «ожидает доступа» — базу они пока не видят.
- Администратор в Супердоступ → Администрирование нажимает «Выдать доступ». Там же можно назначить еще одного админа («Сделать админом»), снять права или удалить пользователя вместе с его чатами. Последнего администратора снять нельзя.
- Все допущенные пользователи видят одну общую базу исследований; чаты у каждого свои, пока ими не поделились.
Модели задаются в файле config/models.yaml — три слота: chat (ответы), embedding (поисковый индекс), transcription (аудио → текст). Файл перечитывается на лету: сохранили — следующий запрос уже идет через новую модель, перезапуск не нужен.
| Слот | Что делает | Требования |
|---|---|---|
chat |
ответы, переформулировка запроса, названия чатов | любой chat-completions endpoint со стримингом |
embedding |
векторный индекс для поиска | любой embeddings endpoint; размерность определяется автоматически |
transcription |
аудио/видео → текст | endpoint /audio/transcriptions; сегменты verbose_json дают таймкоды |
Подойдет любой провайдер с OpenAI-совместимым API. В файле есть готовые примеры — достаточно раскомментировать и подставить ключ:
- OpenAI (по умолчанию): ключ в
LLM_API_KEYв.env. - Агрегаторы (OpenRouter, polza.ai, Together): меняете
base_urlи имя модели, ключ — тот же${LLM_API_KEY}или своя переменная из.env. - Локальные модели через Ollama:
base_url: http://host.docker.internal:11434/v1, ключ не нужен. Вместе с локальным транскрибером получается полностью офлайн-вариант. - GigaChat: у него собственная OAuth-авторизация, она поддержана —
provider: gigachat, ключ вGIGACHAT_AUTH_KEY. - Локальная транскрибация GigaAM (русская речь, таймкоды, аудио не покидает машину): добавьте
COMPOSE_PROFILES=gigaamв.env, выполнитеdocker compose up -dи направьте слотtranscriptionнаhttp://transcriber:9000/v1. Образ тяжелый (~1 ГБ + веса), поэтому по умолчанию выключен.
Два правила:
- Смена embedding-модели делает индекс несовместимым. Ничего не ломается: в Супердоступ → Исследования появится желтая плашка с кнопкой «Переиндексировать» — она пересчитает эмбеддинги всех фрагментов новой моделью в фоне, без повторной транскрибации. Пока идет пересчет, старые фрагменты не участвуют в поиске.
- Личные настройки держите в
config/models.local.yaml. Он в.gitignoreи имеет приоритет надmodels.yaml, так что при обновлении репозитория ваши ключи и выбор моделей не затрутся.
Параметры поиска там же, в секции rag: top_k — сколько фрагментов передавать модели, min_similarity — порог отсечения нерелевантных, history_messages — сколько сообщений чата учитывать в контексте.
загрузка ──► worker: извлечение (pypdf / csv / ffmpeg + ASR) ──► чанки ──► эмбеддинги ──► pgvector
вопрос ──► переформулировка с историей ──► векторный поиск (+ мягкий фильтр дат) ──► LLM с нумерованными источниками ──► стрим с цитатами [n] ──► снимки цитат в БД
- Бэкенд: FastAPI, SQLAlchemy 2 (async), PostgreSQL 16 + pgvector, воркеры arq на Redis. Ответы стримятся через SSE.
- Фронтенд: Next.js 15 (App Router), чистый CSS без UI-фреймворка.
- Обработка файлов: постраничное извлечение PDF с чисткой колонтитулов, определение кодировки и разделителя CSV, извлечение и нарезка аудио через ffmpeg, фильтр «мусорных» фрагментов, пометки к файлам в контексте эмбеддинга.
- Цитаты: модель ссылается на позиции в списке источников, сервер на лету переводит их в сквозные номера чата и сохраняет снимок (исследование, файл, локатор, полный текст фрагмента) при каждом сообщении.
| Сервис | Порт | Роль |
|---|---|---|
web |
3000 | фронтенд Next.js |
api |
8000 | бэкенд FastAPI, Swagger на /docs |
worker |
— | фоновая обработка (транскрибация, эмбеддинги, переиндексация) |
postgres |
5432 | PostgreSQL + pgvector |
redis |
— | очередь задач |
transcriber |
127.0.0.1:9000 | опциональный локальный GigaAM (профиль gigaam) |
Полное техническое задание с принятыми решениями, моделью угроз и планом второй итерации — в docs/TZ.md.
В чате есть система модулей, которые ведут собственный сценарий поверх RAG. Первый — брифинг задачи на исследование: когда в базе нет ответа, ассистент предлагает завести задачу, задает заказчику по одному вопросу, проверяет гипотезы по критериям NN/g, методологическую часть предлагает сам и сохраняет задачу в Markdown в tasks/. По умолчанию выключен (tools.research_task: false в config/models.yaml), пока сценарий дошлифовывается — включите, чтобы попробовать.
Для ноутбука или внутреннего сервера достаточно docker compose up. Перед тем как открывать сервис наружу:
- Задайте случайный
JWT_SECRETи настоящийADMIN_PASSWORDв.env(пока стоят дефолты, API пишет предупреждение в лог). - Спрячьте оба сервиса за HTTPS (любой reverse proxy) и пропишите публичные адреса в
FRONTEND_ORIGIN/NEXT_PUBLIC_API_URL, затем пересоберитеweb(адрес API зашивается при сборке). - Добавьте rate-limit на
/auth/loginна прокси. - Если используете GigaChat с
verify_ssl: false— вместо этого установите в образ сертификаты НУЦ Минцифры. - Загруженные файлы лежат в volume
files-dataв открытом виде — решите, нужно ли шифрование at rest.
За пределы вашей инфраструктуры уходят только запросы, которые вы настроили: чат и эмбеддинги — к провайдерам из models.yaml; с локальным транскрибером аудио обрабатывается на вашей машине.
- Гибридный поиск (BM25 + векторы, RRF) для точных чисел, годов и названий продуктов.
- Графики по сырым CSV-данным прямо в ответах.
- Проверка цитат после генерации (ловить пересказ, выданный за цитату).
- Доведение инструмента брифинга задач.
- Английский интерфейс / i18n.
- Разбор PDF с графиками и сложной версткой через VLM.
Issues и pull request'ы приветствуются — см. CONTRIBUTING.md.
MIT.