A local-first CLI for practicing conversational English with an AI tutor. Everything runs on your machine — your voice, your transcripts, and your conversations never leave it.
Type or talk, and the tutor answers in text and speech.
- How it works
- Tech stack
- Requirements
- Installation
- Usage
- Configuration
- Where your data lives
- Project structure
- Development
- Status and roadmap
- License
LocalTeacher chains four local tools together. Nothing is sent to a remote API: the only network traffic is downloading the tools and models on first run.
Text conversation:
your text -> tutor prompt -> Ollama -> reply printed -> Piper -> ffplay -> your speakers
Voice conversation:
mic -> FFmpeg (16 kHz mono WAV) -> whisper.cpp -> transcript
|
v
tutor prompt -> Ollama -> reply printed -> Piper -> ffplay
The tutor layer is deliberately text-in / text-out. Audio input becomes text before the tutor is called, and speech synthesis happens after the reply is printed — so if playback fails you still get your answer, just with a warning.
On startup, the app walks a chain of checks and fixes anything that is missing, asking before it installs:
- Config and state files
- User config (native language, level, correction style, correction mode)
- Ollama installed → service running → a valid local model selected
- FFmpeg installed → a working microphone input discovered and saved
- whisper.cpp built → Whisper model downloaded
- Piper installed → voice model downloaded
whisper.cpp, Piper, and their models are pinned to specific versions and verified against hardcoded SHA-256 checksums before use. Temporary recording-*.wav files are cleaned up at startup, on exit, and on Ctrl+C / SIGTERM.
| Layer | Technology | Role |
|---|---|---|
| App | Go 1.26 (stdlib CLI, no framework) | Session loop, wizards, orchestration |
| LLM | Ollama | Local tutor replies over http://localhost:11434/api/chat |
| Speech-to-text | whisper.cpp v1.9.1 |
whisper-cli transcribes WAV → text |
| Text-to-speech | Piper 2023.11.14-2 |
Tutor reply → WAV (en_US-lessac-medium) |
| Audio I/O | FFmpeg / ffplay |
Mic discovery, recording, playback |
| System info | gopsutil/v4 | CPU, RAM, disk, OS, best-effort GPU detection |
| Concurrency | golang.org/x/sync/errgroup |
Parallel system probing |
| CI | GitHub Actions | gofmt, go vet, govulncheck, tests on Linux/macOS/Windows |
The model picker suggests: llama3.2:3b (default), qwen2.5:3b, qwen2.5:7b, and llama3.1:8b.
You need to have installed:
- Go 1.26+
- FFmpeg (includes
ffplay) — microphone recording and audio playback - curl, tar, and cmake — used to build whisper.cpp on first run
- A working microphone and speakers
Installed for you on first run (with your confirmation):
- Ollama — via the official install script (Linux),
brew(macOS), orwinget(Windows) - whisper.cpp — downloaded and compiled locally
- Piper — prebuilt release for your platform
- Models — Whisper
ggml-base.en(~148 MB) and the Piper voice (~63 MB)
Disk budget: roughly 2–5 GB depending on the Ollama model you pick, plus ~250 MB for the speech models and the whisper.cpp build.
git clone https://github.com/FaceNach/LocalTeacher.git
cd LocalTeacher
go build -o localteacher ./cmd
./localteacherOr run it straight from source:
go run ./cmdThe first run is the slow one: it may build whisper.cpp and download models. After that, startup is just a series of quick checks.
Start the app and you'll get a prompt.
- Type a message → the tutor replies in text and reads it aloud.
- Press Enter on an empty line (or type
r) → start recording. Press Enter again to stop; your speech is transcribed and answered.
Commands:
| Command | What it does |
|---|---|
r or empty Enter |
Start / stop voice recording |
/help |
Show the available commands |
/config |
Review and change your settings without restarting |
exit, /exit |
Quit LocalTeacher |
Ctrl+C also exits cleanly and removes temporary audio files.
Set on first run and editable any time with /config:
| Setting | Options |
|---|---|
| Native language | English, Spanish, Portuguese, French, Italian, German |
| Level | Beginner, Intermediate, Advanced |
| Correction style | Gentle, Balanced, Immersive, Detailed, Strict |
| Correction mode | Important, All, None |
| Ollama model | Any model available locally |
| Audio input | Detected microphone backend and device |
Changes made through /config are validated, saved, and applied to the live session — the tutor prompt, the Ollama client, and the recorder are rebuilt in place, so no restart is needed. (Settings can't be changed while a recording is in progress.)
The tutor's behavior comes from a system prompt templated with these values: keep the conversation natural, adapt to the user's level, correct kindly and briefly, and always end with one follow-up question.
| What | Location |
|---|---|
| User config | <user config dir>/localteacher/userconfig.json |
| System state | <user config dir>/localteacher/system_state.json |
| whisper.cpp + model | <user config dir>/localteacher/whisper/ |
| Piper + voice | <user config dir>/localteacher/piper/ |
| Temporary WAVs | <user cache dir>/localteacher/temp/ |
<user config dir> is ~/.config on Linux, ~/Library/Application Support on macOS, and %AppData% on Windows.
cmd/
main.go # entry point: cleanup, signal handling, app.Build().Run()
internal/
app/ # CLI session: wiring, command table, input loop
startup/ # first-run setup and prerequisite checks
setup/
config/ # user config read/validate/atomic save
options/ # languages, levels, correction styles, model options
wizard/ # interactive wizards + the /config menu
service/ # TeacherService: coordinates tutor, Ollama, audio
tutor/ # system prompt generation and tutor rules
ollama/ # install checks, service, model listing/pull, chat client
audio/ # high-level audio facade
ffmpeg/ # mic discovery, WAV recording, ffplay playback
input/ # recorder start/stop state for the selected mic
whisper/ # whisper.cpp install, model download, transcription
piper/ # Piper install, voice download, synthesis
platform/ # CPU, RAM, disk, OS, arch and GPU detection
fsutil/ # paths, disk space, SHA-256 verification
cleaning/ # temporary WAV cleanup
defaultsConst/ # names, URLs, checksums, timeouts
ui/ # terminal output (with control-character sanitizing)
Design notes:
- External tools are wrapped behind small interfaces (
audioRecorder,audioTranscriber,speechSynthesizer,audioPlayer,llmClient), so the session and service layers are testable with fakes. - Every external command runs through
exec.CommandContextwith explicit arguments and timeouts. - Model replies and transcripts are stripped of terminal control characters before printing, so a model can't inject ANSI escape sequences into your terminal.
go test ./... # unit tests
gofmt -l . # formatting check
go vet ./... # vetTests use fakes rather than real FFmpeg, whisper.cpp, Ollama, microphones, or network access, and redirect config/cache locations with environment variables and t.TempDir(). CI runs formatting, vet, govulncheck, and the full test suite on Linux, macOS, and Windows for every push to main and every pull request.
See AGENTS.md for the working notes and conventions used while building this.
This is a learning project, built incrementally, with unit tests across most packages.
Working today: text chat, voice-to-voice conversation, spoken replies, in-app settings menu, cross-platform setup wizards, checksum-verified installs.
Not implemented yet:
- Conversation memory / session history beyond the current turn
- Streaming Ollama responses
MIT — see LICENSE.
Una CLI local-first para practicar conversación en inglés con un tutor de IA. Todo corre en tu máquina: tu voz, tus transcripciones y tus conversaciones nunca salen de ahí.
Escribí o hablá, y el tutor te responde por texto y en voz.
- Cómo funciona
- Tecnologías
- Requisitos
- Instalación
- Uso
- Configuración
- Dónde se guardan tus datos
- Estructura del proyecto
- Desarrollo
- Estado y próximos pasos
- Licencia
LocalTeacher encadena cuatro herramientas locales. No se envía nada a ninguna API remota: el único tráfico de red es la descarga de las herramientas y los modelos en la primera ejecución.
Conversación por texto:
tu texto -> prompt del tutor -> Ollama -> respuesta impresa -> Piper -> ffplay -> tus parlantes
Conversación por voz:
micrófono -> FFmpeg (WAV mono 16 kHz) -> whisper.cpp -> transcripción
|
v
prompt del tutor -> Ollama -> respuesta impresa -> Piper -> ffplay
La capa del tutor es a propósito texto-entra / texto-sale. El audio se convierte en texto antes de llamar al tutor, y la síntesis de voz ocurre después de imprimir la respuesta: si falla la reproducción igual ves tu respuesta, solo que con una advertencia.
Al iniciar, la app recorre una cadena de verificaciones y arregla lo que falte, preguntando antes de instalar:
- Archivos de configuración y estado
- Config de usuario (idioma nativo, nivel, estilo y modo de corrección)
- Ollama instalado → servicio corriendo → modelo local válido seleccionado
- FFmpeg instalado → micrófono funcional detectado y guardado
- whisper.cpp compilado → modelo de Whisper descargado
- Piper instalado → modelo de voz descargado
whisper.cpp, Piper y sus modelos están fijados a versiones específicas y se verifican contra checksums SHA-256 hardcodeados antes de usarse. Los WAV temporales (recording-*.wav) se limpian al iniciar, al salir y con Ctrl+C / SIGTERM.
| Capa | Tecnología | Rol |
|---|---|---|
| App | Go 1.26 (CLI con stdlib, sin framework) | Loop de sesión, wizards, orquestación |
| LLM | Ollama | Respuestas del tutor vía http://localhost:11434/api/chat |
| Voz a texto | whisper.cpp v1.9.1 |
whisper-cli transcribe WAV → texto |
| Texto a voz | Piper 2023.11.14-2 |
Respuesta del tutor → WAV (en_US-lessac-medium) |
| Audio I/O | FFmpeg / ffplay |
Detección de micrófono, grabación, reproducción |
| Info del sistema | gopsutil/v4 | CPU, RAM, disco, SO y detección de GPU (best-effort) |
| Concurrencia | golang.org/x/sync/errgroup |
Sondeo del sistema en paralelo |
| CI | GitHub Actions | gofmt, go vet, govulncheck y tests en Linux/macOS/Windows |
El selector de modelos sugiere: llama3.2:3b (por defecto), qwen2.5:3b, qwen2.5:7b y llama3.1:8b.
Tenés que tener instalado:
- Go 1.26+
- FFmpeg (incluye
ffplay) — grabación de micrófono y reproducción de audio - curl, tar y cmake — se usan para compilar whisper.cpp en la primera ejecución
- Un micrófono y parlantes funcionando
Se instalan solos en la primera ejecución (con tu confirmación):
- Ollama — con el script oficial (Linux),
brew(macOS) owinget(Windows) - whisper.cpp — se descarga y compila localmente
- Piper — release precompilada para tu plataforma
- Modelos — Whisper
ggml-base.en(~148 MB) y la voz de Piper (~63 MB)
Espacio en disco: entre 2 y 5 GB según el modelo de Ollama que elijas, más unos 250 MB para los modelos de voz y la compilación de whisper.cpp.
git clone https://github.com/FaceNach/LocalTeacher.git
cd LocalTeacher
go build -o localteacher ./cmd
./localteacherO directamente desde el código fuente:
go run ./cmdLa primera ejecución es la lenta: puede compilar whisper.cpp y descargar modelos. Después de eso, el arranque es solo una serie de chequeos rápidos.
Arrancá la app y vas a ver el prompt.
- Escribí un mensaje → el tutor responde por texto y lo lee en voz alta.
- Enter en una línea vacía (o escribí
r) → empieza a grabar. Enter otra vez para parar: tu voz se transcribe y se responde.
Comandos:
| Comando | Qué hace |
|---|---|
r o Enter vacío |
Inicia / detiene la grabación de voz |
/help |
Muestra los comandos disponibles |
/config |
Revisa y cambia tus ajustes sin reiniciar |
exit, /exit |
Sale de LocalTeacher |
Ctrl+C también sale limpio y borra los archivos de audio temporales.
Se define en la primera ejecución y se puede editar en cualquier momento con /config:
| Ajuste | Opciones |
|---|---|
| Idioma nativo | Inglés, Español, Portugués, Francés, Italiano, Alemán |
| Nivel | Principiante, Intermedio, Avanzado |
| Estilo de corrección | Gentle, Balanced, Immersive, Detailed, Strict |
| Modo de corrección | Important, All, None |
| Modelo de Ollama | Cualquier modelo disponible localmente |
| Entrada de audio | Backend y dispositivo de micrófono detectados |
Los cambios hechos con /config se validan, se guardan y se aplican a la sesión en curso: el prompt del tutor, el cliente de Ollama y el grabador se reconstruyen en el momento, así que no hace falta reiniciar. (No se pueden cambiar los ajustes mientras hay una grabación activa.)
El comportamiento del tutor sale de un system prompt armado con estos valores: mantener la conversación natural, adaptarse al nivel del usuario, corregir con amabilidad y brevedad, y terminar siempre con una pregunta de seguimiento.
| Qué | Ubicación |
|---|---|
| Config de usuario | <dir de config>/localteacher/userconfig.json |
| Estado del sistema | <dir de config>/localteacher/system_state.json |
| whisper.cpp + modelo | <dir de config>/localteacher/whisper/ |
| Piper + voz | <dir de config>/localteacher/piper/ |
| WAV temporales | <dir de caché>/localteacher/temp/ |
<dir de config> es ~/.config en Linux, ~/Library/Application Support en macOS y %AppData% en Windows.
cmd/
main.go # punto de entrada: limpieza, señales, app.Build().Run()
internal/
app/ # sesión CLI: wiring, tabla de comandos, loop de entrada
startup/ # setup inicial y verificación de prerequisitos
setup/
config/ # lectura, validación y guardado atómico de la config
options/ # idiomas, niveles, estilos de corrección, modelos
wizard/ # wizards interactivos + menú de /config
service/ # TeacherService: coordina tutor, Ollama y audio
tutor/ # generación del system prompt y reglas del tutor
ollama/ # instalación, servicio, modelos y cliente de chat
audio/ # fachada de audio de alto nivel
ffmpeg/ # detección de micrófono, grabación WAV, playback ffplay
input/ # estado start/stop del grabador para el micrófono elegido
whisper/ # instalación de whisper.cpp, modelo y transcripción
piper/ # instalación de Piper, voz y síntesis
platform/ # detección de CPU, RAM, disco, SO, arquitectura y GPU
fsutil/ # rutas, espacio en disco, verificación SHA-256
cleaning/ # limpieza de WAV temporales
defaultsConst/ # nombres, URLs, checksums y timeouts
ui/ # salida por terminal (sanitizando caracteres de control)
Notas de diseño:
- Las herramientas externas están envueltas detrás de interfaces chicas (
audioRecorder,audioTranscriber,speechSynthesizer,audioPlayer,llmClient), así las capas de sesión y servicio se pueden testear con fakes. - Todo comando externo corre con
exec.CommandContext, con argumentos explícitos y timeouts. - A las respuestas del modelo y a las transcripciones se les quitan los caracteres de control antes de imprimirlas, para que un modelo no pueda inyectar secuencias ANSI en tu terminal.
go test ./... # tests unitarios
gofmt -l . # chequeo de formato
go vet ./... # vetLos tests usan fakes en lugar de FFmpeg, whisper.cpp, Ollama, micrófonos o red reales, y redirigen las rutas de config/caché con variables de entorno y t.TempDir(). La CI corre formato, vet, govulncheck y toda la suite de tests en Linux, macOS y Windows en cada push a main y en cada pull request.
En AGENTS.md están las notas de trabajo y las convenciones usadas para construir esto.
Este es un proyecto de aprendizaje, construido de forma incremental, con tests unitarios en la mayoría de los paquetes.
Ya funciona: chat por texto, conversación voz a voz, respuestas habladas, menú de ajustes dentro de la app, wizards de setup multiplataforma e instalaciones verificadas por checksum.
Todavía no está implementado:
- Memoria de conversación / historial de sesión más allá del turno actual
- Respuestas de Ollama en streaming
MIT — ver LICENSE.