diff options
Diffstat (limited to 'README.md')
| -rw-r--r-- | README.md | 163 |
1 files changed, 163 insertions, 0 deletions
diff --git a/README.md b/README.md new file mode 100644 index 0000000..deeef3c --- /dev/null +++ b/README.md @@ -0,0 +1,163 @@ +# asist-p — asistente de voz local + +Escucha por el micrófono, piensa y contesta hablando. Todo en la máquina: no +sale un byte a internet. + +Une tres motores que ya existían —**Canary** para oír, **llama.cpp** para +pensar y **qwentts.cpp** para hablar— en un pipeline de hilos y canales en +Rust donde ninguna etapa espera a la siguiente. + +``` + micrófono ──muestras──> segmentador ──intervención──> ASR ──texto──┐ + (cpal, tiempo real) (VAD, turnos) (Canary) │ + v + altavoz <──muestras── síntesis <──frases── conversación <──────────┘ + (cpal, anillo) (qwentts) (llama.cpp + herramientas) +``` + +Cada caja es un hilo; cada flecha, un canal. Dos decisiones sostienen el +resto: + +- **Nada bloquea al que va delante.** El micrófono nunca espera al + reconocedor, y el modelo nunca espera al sintetizador. Quien va sobrado + descarta trabajo en lugar de acumular retraso. +- **Se responde por frases, no por respuestas.** Cada frase sale hacia el + sintetizador en cuanto se cierra, así que el asistente empieza a hablar + mientras el modelo sigue escribiendo. Es lo que separa medio segundo de + cinco. + +## Puesta en marcha + +```bash +git clone --recursive <este-repo> && cd asist-p +scripts/bootstrap.sh # submódulos, parches, binarios y modelos +cargo run --release -- check # comprueba que está todo en su sitio +cargo run --release -- run # a hablar +``` + +`bootstrap.sh` es idempotente y no copia modelos: los enlaza desde donde ya +estén (`~/GIT-MIRRO`, `~/HF`). Si no encuentra los motores construidos, los +construye, y eso sí tarda. + +```bash +cargo run --release -- devices # listar dispositivos de audio +cargo run --release -- run --barge-in # permitir cortar al asistente +cargo run --release -- run --shell # habilitar órdenes del sistema +cargo run --release -- run --no-manage # no lanzar los servidores +``` + +Durante el desarrollo conviene dejar los servidores levantados aparte —cargar +los modelos cuesta más de un minuto— y reiniciar sólo el binario de Rust: + +```bash +scripts/servidores.sh arrancar +cargo run --release -- run --no-manage +``` + +## Cómo está organizado + +| Crate | De qué se ocupa | +|---|---| +| `asist-core` | Configuración, eventos, errores, cliente HTTP, telemetría y el registro de herramientas | +| `asist-audio` | Captura y reproducción con cpal, detección de voz, troceado en turnos | +| `asist-asr` | Canary en ONNX: ventana deslizante y transcripción final | +| `asist-llm` | llama-server: streaming SSE, historial, llamadas a herramientas | +| `asist-tts` | qwentts: síntesis en streaming y voces clonadas | +| `asist-app` | Supervisor de procesos, orquestador y binario `asistente` | + +Los tres motores viven en `vendor/` como submódulos fijados a un commit +concreto. Los cambios locales sobre ellos están en `vendor/patches/`, y +`bootstrap.sh` los aplica: sin ellos el reconocedor no se comporta como el +que se midió. + +## Configuración + +Todo está en [`config/asistente.toml`](config/asistente.toml), comentado. Los +valores que llevan una cifra en el comentario salen de una medición concreta, +no de una suposición; el detalle está en +[docs/RENDIMIENTO.md](docs/RENDIMIENTO.md). + +Lo que más se toca: + +```toml +[vad] +silence_hold = 0.8 # cuánto silencio cierra tu intervención +barge_in = false # cortar al asistente hablando encima + +[general] +history_turns = 8 # memoria de la conversación +system_prompt = "..." # carácter y estilo + +[tools] +shell = false # ejecutar órdenes del sistema +``` + +## Interrumpir y el eco del altavoz + +Por defecto el asistente es **media dúplex**: mientras habla, el micrófono +está cerrado. No es pereza: con altavoces abiertos el micrófono se oye a sí +mismo, el asistente se transcribe y se responde solo. + +Con auriculares, `--barge-in` deja hablar encima para cortarlo. El umbral se +eleva (`vad.barge_in_factor`) para que el eco no baste y una voz de verdad sí. + +## Latencia + +Un turno real, medido de punta a punta: + +| Etapa | Tiempo | +|---|---| +| Reconocimiento (2,5 s de voz) | 200 ms | +| Modelo, primer token | 1660 ms | +| Modelo, resto | 1362 ms | +| Herramientas | 1 ms | +| **Del final de tu frase al primer audio** | **3213 ms** | + +El binario mide esto en cada turno y señala la etapa más lenta. Tres ajustes +de configuración valieron más que cualquier cambio de código: + +- El TTS decodificaba en bloques de 24 s: **4948 ms → 585 ms** al primer audio. +- La plantilla del modelo dejaba `<think>` abierto: **8630 ms → 413 ms** al + primer token. +- El prompt de estilo anulaba las llamadas a herramientas: **0/8 → 8/8**. + +Los tres, con el porqué y cómo se midieron, en +[docs/RENDIMIENTO.md](docs/RENDIMIENTO.md). + +## Herramientas + +El asistente puede llamar a funciones. Viene con `hora_actual` y con +`ejecutar_comando`, esta última **apagada**: darle una shell a un modelo que +obedece a lo que oye por el micrófono es un cambio de postura de seguridad. +Cuando se enciende, sólo pasan las órdenes de una lista blanca, sin shell que +interprete metacaracteres y con un plazo máximo. + +Añadir una propia son unas veinte líneas: +[docs/EXTENDER.md](docs/EXTENDER.md). + +## Pruebas + +68 pruebas unitarias que no necesitan ni modelos ni micrófono, y seis de +integración contra los servidores de verdad. + +```bash +cargo test # unitarias, sin modelos +scripts/servidores.sh arrancar +cargo test --release -p asist-app --test integracion -- --nocapture +cargo test --release -p asist-app --test integracion -- --ignored # bucle completo +``` + +Las de integración se saltan solas si no hay servidores escuchando, y se +turnan la GPU con un mutex: los dos servidores comparten una tarjeta de 4 GB y +en paralelo miden contención en vez de latencia. + +La prueba marcada `--ignored` cierra el bucle sin micrófono: sintetiza una +frase y comprueba que el reconocedor la recupera. + +## Requisitos + +- Rust 1.85 o posterior +- CUDA para los motores en C++ (funcionan en CPU, pero muy justos) +- PipeWire o ALSA +- ~6 GB de disco para los modelos + |