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` (чтобы ты подставил провайдеры).