ESP32-S3 Bench Guide
ESP32-S3 N16R8 bench guide: live pinout, lessons, simulators and the bench log.
翻訳待ち
No updates detected yet. GitHub releases will appear here.
Loading the full README. The saved excerpt is shown below.
Интерактивная голосовая сказка: ребёнок 3–7 лет отвечает голосом и влияет на сюжет, реплики на казахском с русским подстрочником. Подробнее об идее — IDEA.md. Открыть http://localhost:3000 — это библиотека (выбор сказки или урока); сама сказка — http://localhost:3000/story.html. Панель оператора (скрыта от ребёнка) — клавиша `, значок ⚙ в шапке, либо параметр ?op=1 в URL (/story.html?op=1).Read the full README ↗
Интерактивная голосовая сказка: ребёнок 3–7 лет отвечает голосом и влияет на сюжет, реплики на казахском с русским подстрочником. Подробнее об идее — IDEA.md.
| Каталог | Назначение |
|---|---|
public/ | детское приложение (статика), деплоится Vercel: index.html — библиотека (главный экран), story.html — сказка/урок |
api/ | Vercel Edge functions (transcribe.js, classify.js) |
server/ | локальный Bun-сервер: server.js, bins.js |
lib/ | модули без DOM/Bun-зависимостей (blocklist, STT-подсказки, выбор транскрипта, классификатор, Origin-guard) |
tools/ | prerender.py, dump-story.js, requirements.txt |
tests/ | bun test |
docs/ | сценарий, статус-отчёты, спеки |
cp .env.example .env # вписать GROQ_API_KEY
bun server/server.js
Открыть http://localhost:3000 — это библиотека (выбор сказки или урока); сама сказка —
http://localhost:3000/story.html. Панель оператора (скрыта от ребёнка) — клавиша `,
значок ⚙ в шапке, либо параметр ?op=1 в URL (/story.html?op=1).
Без GROQ_API_KEY и/или без ffmpeg/whisper/piper приложение всё ещё работает: живой STT и
live-TTS отключаются, но заранее пре-рендеренные .wav в public/audio/ и ручные кнопки
оператора (✅/🔁/⏭) позволяют пройти историю без микрофона.
Порядок разрешения бинарей: явный env → PATH → tools/.venv (для PIPER_BIN — сначала
tools/.venv, затем PATH) (см. server/bins.js).
| Переменная | По умолчанию | Если не задана/не найдена |
|---|---|---|
GROQ_API_KEY | — | нет STT (Whisper) и LLM-классификатора ответов |
GROQ_STT_MODEL | whisper-large-v3 | — |
STT_LANGS | auto,ru | — |
ALLOWED_ORIGIN_EXTRA | — | API на Vercel принимает только *.vercel.app / localhost; свой домен нужно перечислить здесь через запятую (ertegim.kz,*.ertegim.kz), иначе все запросы получат 403 (см. lib/origin-guard.js) |
GROQ_TIMEOUT_MS | 8000 локально, 12000 на Vercel | — |
PORT | 3000 | сервер слушает 3000 |
FFMPEG_BIN | ffmpeg из PATH, иначе бинарь imageio-ffmpeg из tools/.venv | нет loudnorm-препроцессинга и pitch-shift для TTS, локальный Whisper недоступен |
WHISPER_BIN | whisper-cli из PATH | только Groq STT |
WHISPER_MODEL | tools/models/ggml-small.bin, если существует | только Groq STT |
PIPER_BIN | tools/.venv/bin/piper (Windows: tools/.venv/Scripts/piper.exe), иначе piper из PATH | /api/speak отвечает 503, клиент переключается на пре-рендеренные .wav |
PIPER_VOICE_KK | tools/voices/kk_KZ-issai-high.onnx, если существует | как выше |
python3 -m venv tools/.venv
tools/.venv/bin/pip install -r tools/requirements.txt
tools/.venv/bin/python tools/prerender.py
Скачает голос Piper в tools/voices/ (с HuggingFace; если он недоступен в вашей сети — тот же
голос берётся из GitHub-релиза rhasspy/piper v0.0.2) и озвучит все реплики истории и уроков
в public/audio/. --force — переозвучить всё заново; --only id1,id2 — только перечисленные
реплики. Реплики уроков (a_*, q_a_*) рендерятся медленнее (LESSON_LENGTH_SCALE), как
просит docs/lesson-letter-a.md.
bun test
Распознавание идёт в два прохода: один и тот же клип отправляется в Groq Whisper
дважды параллельно — по одному вызову на каждый элемент STT_LANGS
(по умолчанию auto,ru), — а lib/stt-pick.js выбирает тот
результат, который лучше отвечает на вопрос узла (счёт, развилка, рифма,
поддержка), отбрасывая галлюцинации Whisper («Субтитры сделал DimaTorzok») и
помечая тихие/неуверенные записи как lowConfidence — тогда сказка мягко
переспрашивает, не дёргая LLM.
auto,ru и whisper-large-v3Раньше проходы были kk,ru на whisper-large-v3-turbo. На живом тесте с
телефона реальные казахские ответы возвращались либо пустыми, либо уверенной
казахоподобной бессмыслицей («үңі ғыңңқ») — turbo с принудительным
language=kk заставляет модель «договорить» фонетику низкоресурсного языка
вместо честного низкого confidence. Это известный режим отказа, и он хуже
пустого ответа: галлюцинация выглядит как уверенное распознавание и идёт
дальше по пайплайну.
Поэтому по умолчанию: полная модель whisper-large-v3 (точнее turbo на
низкоресурсных языках; задержка не критична — TTS/микрофон и так занимают
8-12 с под анимацией «думаю») и auto вместо kk — язык определяет сам
Whisper, а словарная подсказка (lib/stt-hints-core.js) и
isMostlyCyrillic() остаются. Каждый вопрос в story.js и так принимает
ответ на казахском или русском, так что заставлять модель выбирать
более сложный язык незачем.
STT_LANGS=kk,ru возвращает прежнее поведение (принудительный казахский),
STT_LANGS=auto — один проход. Порядок важен: при равном счёте
lib/stt-pick.js выбирает первый в списке.
Цена: два прохода — это два вызова Groq STT на каждый ответ ребёнка (вдвое
больше запросов и стоимости, чем при одном проходе); пороги noSpeechProb 0.6 и
avgLogprob −1.2 в lib/stt-pick.js взяты «на глаз» и не настроены — их и надо
откалибровать прогоном лаборатории на реальных детских записях.
Чтобы настроить это на реальных детских записях, есть локальная страница
(только bun server/server.js, на Vercel её нет):
GROQ_API_KEY=... bun server/server.js
# открыть http://localhost:3000/lab.html
Выбрать записи детей, узел (q_tracks / q_fork / q_courage / q_echo), для
счёта — trackCount, при необходимости поправить langs (подставляется из
STT_LANGS сервера — так и сравнивают kk,ru против auto,ru против auto),
вписать ожидаемый ответ напротив каждого файла и нажать «Run turbo» / «Run v3».
Таблица покажет оба транскрипта (с пометкой «запрошено→распознано», чтобы было
видно, что именно определил auto), выбранный, score, confidence и совпадение
с ожидаемым; внизу — точность и среднее время на файл для каждой модели.
This is the beginning of the README. Read the rest at the original. Read the full README ↗