Python
Junior
chatgpt
Ответы
Ниже — рабочая «схема проекта» онлайн AI-репетитора английского на FastAPI: из каких сервисов/модулей состоит, как течёт аудио/текст, где лежит state machine, и какие компоненты нужны, чтобы это было устойчиво в проде.
## 1) Главная развилка: текстовый режим vs голосовой realtime
**Вариант A (быстрее в запуске):**
Пользователь пишет/читает текст → STT/TTS по кнопке, без стриминга. Проще, дешевле, меньше багов.
**Вариант B (как “звонок”): realtime voice**
* Двусторонний аудиострим (WebRTC / WebSocket)
* VAD (детект речи), streaming STT, streaming TTS
* Жёсткие тайминги/буферы, обработка перебиваний (barge-in)
Дальше схема будет для **B (realtime)**, потому что она покрывает и A тоже (A = упрощение).
---
## 2) Высокоуровневая архитектура (блоки)
**Client (Web / Mobile)**
* UI урока (чат + карточки упражнений)
* WebRTC/WebSocket аудио
* локальный VAD (опционально) + индикаторы “я слушаю/я говорю”
* проигрывание TTS + barge-in (прерывание озвучки, если пользователь заговорил)
**FastAPI Backend**
1. **Auth + Users** (JWT, refresh, roles)
2. **Lesson Orchestrator (Session Service)** — создаёт/держит сессию урока
3. **State Machine (Dialogue Engine)** — управляет сценарием, переходами, ошибками
4. **ASR Service (SST/STT)** — streaming распознавание
5. **TTS Service** — streaming синтез
6. **LLM/Coach Service** — генерация ответов + педагогическая логика
7. **Content Service** — упражнения, планы уроков, словари, темы
8. **Memory/Progress Service** — прогресс, ошибки, слова, SRS
9. **Observability** — логи/метрики/трейсы, запись “транскрипт+таймкоды”
**Хранилища**
* Postgres: пользователи, уроки, прогресс
* Redis: состояние сессий, очереди, rate limits
* Object storage: аудиозаписи, артефакты, отчёты
---
## 3) Поток данных в realtime уроке (step-by-step)
### 3.1 Входящее аудио пользователя
1. Client отправляет аудио чанки (WebRTC или WS)
2. **VAD** режет на «сегменты речи» (start/end)
3. **Streaming STT** выдаёт partial + final транскрипт
4. Final сегмент уходит в **Dialogue Engine** (FSM)
### 3.2 Диалог и педагогика
FSM получает событие: `USER_UTTERANCE_FINAL(text, timestamps, confidence)`
Дальше:
* нормализация текста (punctuation, casing)
* определение намерения / тип упражнения (free talk vs drill)
* оценка ответа (grammar/vocab/pronunciation proxy по тексту)
* решение: задать уточняющий вопрос / дать фидбек / перейти к следующему шагу
* формирование `AssistantPlan`:
* `assistant_text`
* `corrections` (ошибки + объяснения)
* `next_prompt` (что спросить дальше)
* `ui_cards` (варианты ответа, слова, упражнения)
### 3.3 Исходящее аудио ассистента
1. LLM вернул текст ассистента
2. **Streaming TTS** генерит аудио чанки
3. Client проигрывает
4. Если пользователь начал говорить → **barge-in**:
* клиент/сервер шлёт `INTERRUPT_TTS`
* TTS поток останавливается
* FSM фиксирует “ассистент прерван”
---
## 4) State Machine (FSM) — сердце проекта
FSM лучше делать **явной**, а не «всё в одном промпте».
### 4.1 Состояния (пример)
* `IDLE` — нет урока
* `WARMUP` — разогрев (small talk)
* `TOPIC_INTRO(topic)` — ввод темы
* `DRILL_GRAMMAR(rule)` — отработка правила
* `DRILL_VOCAB(set)` — слова
* `ROLEPLAY(scene)` — ролеплей
* `LISTENING` — слушание (ассистент читает, пользователь отвечает)
* `RECAP` — итог урока
* `END`
### 4.2 События
* `USER_AUDIO_START`, `USER_AUDIO_END`
* `USER_UTTERANCE_PARTIAL(text)`
* `USER_UTTERANCE_FINAL(text)`
* `TTS_STARTED`, `TTS_FINISHED`, `TTS_INTERRUPTED`
* `TIMEOUT_NO_SPEECH`
* `USER_REQUEST_HELP`, `USER_CHANGE_LEVEL`, `USER_SWITCH_TOPIC`
### 4.3 Контекст FSM (то, что хранится в Redis)
* `level`, `goals`, `topic`
* текущий шаг/упражнение
* последние N реплик
* “ошибки текущего урока”
* буферы аудио/тайминги
* флаги: `assistant_speaking`, `user_speaking`
### 4.4 Выход FSM
FSM не должен “сам говорить”. Он должен отдавать **план действий**:
* `actions`:
* `CALL_LLM(prompt, tools…)`
* `SAY(text, voice_style)`
* `SHOW_UI(cards)`
* `UPDATE_PROGRESS(items)`
* `TRANSITION(new_state)`
---
## 5) LLM слой: разнести “говорить” и “думать”
Очень полезно делить ответы LLM на:
1. **Teacher Brain (structured JSON)** — решение, оценка, следующий шаг
2. **Teacher Voice (final text)** — то, что реально произнесём
Т.е. LLM сначала возвращает структуру (типа `AssistantPlan`), а потом вы генерируете “озвучиваемый текст” строго из неё (или вторым вызовом).
---
## 6) ASR/TTS модули: что именно нужно
### STT (SST)
* streaming режим
* partial results (для UI “я слышу…”)
* final segments (для FSM)
* таймкоды (если есть) → для подсветки
**Обязательно в проде:**
* фильтр тишины/шума (VAD)
* retries на обрыв
* ограничение длины сегмента (например 15–20с)
### TTS
* streaming аудио чанками
* поддержка “interrupt”
* отдельный буфер на клиенте (чтобы не дёргалось)
* голоса/стили: friendly / strict / slow / clear
---
## 7) Минимальная структура репозитория (FastAPI)
Примерно так:
* `app/`
* `api/`
* `routes_auth.py`
* `routes_sessions.py`
* `routes_realtime.py` *(ws/webrtc signaling)*
* `core/`
* `config.py`
* `logging.py`
* `security.py`
* `domain/`
* `models.py` *(Pydantic схемы: events, state, plans)*
* `fsm/`
* `machine.py`
* `states.py`
* `reducers.py` *(event -> actions)*
* `lesson/`
* `planner.py` *(контент/план урока)*
* `scoring.py` *(оценки/ошибки)*
* `services/`
* `stt.py`
* `tts.py`
* `llm.py`
* `memory.py`
* `content.py`
* `infra/`
* `db.py` *(SQLAlchemy/SQLModel)*
* `redis.py`
* `storage.py`
* `workers/`
* `tasks.py` *(Celery/RQ/Arq для фоновых задач)*
* `tests/`
---
## 8) Протокол событий между клиентом и сервером (WS)
Делайте единый JSON протокол:
**client → server**
* `audio.chunk` (base64/bytes ref)
* `user.interrupt_tts`
* `user.set_level`
* `session.start/stop`
**server → client**
* `asr.partial`, `asr.final`
* `assistant.plan` (JSON: corrections, cards, next)
* `tts.audio.chunk`
* `state.updated` (текущее состояние FSM)
---
## 9) Что обязательно заложить сразу (чтобы не умереть в проде)
* **Latency budget**: целиться в “ответ начал звучать” < 800–1200мс после окончания речи.
* **Barge-in**: без него голосовой репетитор бесит.
* **Rate limits & cost controls**: LLM/TTS дорогие.
* **Failover режим**: если TTS упал → текстом; если STT упал → “наберите текстом”.
* **Логи с correlation_id** на всю сессию.
* **Хранение транскриптов** (для прогресса и “последний урок”).
---
## 10) “MVP за неделю” (самый простой, но правильный)
1. WS с событиями (без WebRTC)
2. VAD на сервере
3. Streaming STT → final text
4. FSM: `WARMUP -> ROLEPLAY -> RECAP`
5. LLM возвращает `AssistantPlan` (JSON)
6. Streaming TTS озвучивает только `assistant_text`
7. Postgres: user, session, transcript, mistakes
---
Если хочешь, я могу сразу:
* набросать **Pydantic-схемы** для `Event`, `State`, `AssistantPlan`
* и дать **скелет кода FastAPI**: WS endpoint + простая FSM + заглушки `stt/tts/llm` (чтобы ты подставил провайдеры).