<feed xmlns='http://www.w3.org/2005/Atom'>
<title>asist-p/crates/asist-audio, branch main</title>
<subtitle>Local voice assistant in Rust - Canary, llama.cpp and qwentts in a streaming pipeline</subtitle>
<link rel='alternate' type='text/html' href='http://git.all.ar/pub/asist-p/'/>
<entry>
<title>Translate code, comments, logs and terminal UI to English; add English README; rename scripts</title>
<updated>2026-09-26T23:20:19+00:00</updated>
<author>
<name>elvis</name>
<email>elvis@claros.ar</email>
</author>
<published>2026-09-26T23:20:19+00:00</published>
<link rel='alternate' type='text/html' href='http://git.all.ar/pub/asist-p/commit/?id=8518a63f55153e7f45fd49ad6caff5555f4e374f'/>
<id>8518a63f55153e7f45fd49ad6caff5555f4e374f</id>
<content type='text'>
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
</pre>
</div>
</content>
</entry>
<entry>
<title>Web search and camera vision</title>
<updated>2026-09-07T01:05:14+00:00</updated>
<author>
<name>elvis</name>
<email>elvis@claros.ar</email>
</author>
<published>2026-09-07T01:05:14+00:00</published>
<link rel='alternate' type='text/html' href='http://git.all.ar/pub/asist-p/commit/?id=71764752f28d31028b9c2ca486c0fc1bcea1cf95'/>
<id>71764752f28d31028b9c2ca486c0fc1bcea1cf95</id>
<content type='text'>
Dos capacidades nuevas en un crate aparte, asist-tools, registradas desde
asist-app: son el ejemplo de que el punto de extensión documentado funciona
sin tocar el orquestador.

Buscar admite tres buscadores intercambiables. Tavily devuelve una respuesta ya
redactada, que es lo que se puede leer en voz alta sin gastar otra vuelta del
modelo en resumir (0,54 s para «capital de Australia»); ddgs no necesita clave
y devuelve fragmentos que el modelo sintetiza (2,24 s). El tercero es un
backend de comando genérico: ejecuta un programa y lee JSON de su salida, así
que añadir otro buscador —o un puente a un servidor MCP— es escribir un guion.

Sobre DuckDuckGo, comprobado y no supuesto: curl contra html.duckduckgo.com
devuelve HTTP 202 con una página anti-bot y ni un resultado, y la API sin clave
api.duckduckgo.com devuelve vacío para casi todo lo que no sea una entidad de
enciclopedia. La librería ddgs —la misma que hay bajo duckduckgo-mcp— sí
funciona porque rota buscadores y cabeceras, y es la que usa
scripts/buscar-ddgs.sh.

Mirar aprovecha que el servidor ya carga el proyector multimodal: el mismo
modelo que conversa describe lo que capta la cámara. La pregunta del usuario
viaja hasta ahí, porque «¿de qué color es mi camiseta?» y «¿cuánta gente hay?»
necesitan la misma imagen y descripciones distintas. La imagen no entra en el
historial —cientos de tokens por turno para algo ya descrito— ni toca el disco.

Tres cosas más que salieron de medir, en docs/RENDIMIENTO.md:

- La resolución de captura manda en la latencia: 7,8 s a 1280x720 frente a
  2,9 s a 640x480, con la misma descripción útil. 640x480 pasa a ser el valor
  por defecto.
- Una herramienta ejecutándose en silencio deja el turno seis segundos mudo y
  parece un cuelgue. Tool::acknowledgement pronuncia una frase antes de
  ejecutar y baja el primer audio de ~8,4 s a 4,1 s.
- Con la instrucción de voz a secas en la pasada de redacción, el modelo
  anunciaba lo que acababa de hacer en vez de contar lo que averiguó, ignorando
  el resultado que tenía delante. general.tool_result_prompt lo corrige; ahí sí
  se puede añadir estilo sin riesgo, porque la llamada ya ocurrió.

Con tres herramientas declaradas el modelo elige bien entre ellas (15 de 16
medido), pero le cuesta abstenerse y busca cosas que ya sabe. Intentar
corregirlo con instrucciones empeora los aciertos sin reducir los falsos
positivos, coherente con la fragilidad ya documentada.

Corregido además un fallo que dejaba mudo al asistente tras la primera
respuesta: el anillo de reproducción no bajaba nunca su bandera de actividad,
así que el segmentador mantenía el micrófono cerrado creyendo que seguía
hablando. El segmentador se guía ahora por la cola, que no puede desfasarse, y
hay pruebas del anillo con un mando sin dispositivo detrás.

Claude-Session: https://claude.ai/code/session_01FNxz5cSdQSscJH9H7b8uGU
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Dos capacidades nuevas en un crate aparte, asist-tools, registradas desde
asist-app: son el ejemplo de que el punto de extensión documentado funciona
sin tocar el orquestador.

Buscar admite tres buscadores intercambiables. Tavily devuelve una respuesta ya
redactada, que es lo que se puede leer en voz alta sin gastar otra vuelta del
modelo en resumir (0,54 s para «capital de Australia»); ddgs no necesita clave
y devuelve fragmentos que el modelo sintetiza (2,24 s). El tercero es un
backend de comando genérico: ejecuta un programa y lee JSON de su salida, así
que añadir otro buscador —o un puente a un servidor MCP— es escribir un guion.

Sobre DuckDuckGo, comprobado y no supuesto: curl contra html.duckduckgo.com
devuelve HTTP 202 con una página anti-bot y ni un resultado, y la API sin clave
api.duckduckgo.com devuelve vacío para casi todo lo que no sea una entidad de
enciclopedia. La librería ddgs —la misma que hay bajo duckduckgo-mcp— sí
funciona porque rota buscadores y cabeceras, y es la que usa
scripts/buscar-ddgs.sh.

Mirar aprovecha que el servidor ya carga el proyector multimodal: el mismo
modelo que conversa describe lo que capta la cámara. La pregunta del usuario
viaja hasta ahí, porque «¿de qué color es mi camiseta?» y «¿cuánta gente hay?»
necesitan la misma imagen y descripciones distintas. La imagen no entra en el
historial —cientos de tokens por turno para algo ya descrito— ni toca el disco.

Tres cosas más que salieron de medir, en docs/RENDIMIENTO.md:

- La resolución de captura manda en la latencia: 7,8 s a 1280x720 frente a
  2,9 s a 640x480, con la misma descripción útil. 640x480 pasa a ser el valor
  por defecto.
- Una herramienta ejecutándose en silencio deja el turno seis segundos mudo y
  parece un cuelgue. Tool::acknowledgement pronuncia una frase antes de
  ejecutar y baja el primer audio de ~8,4 s a 4,1 s.
- Con la instrucción de voz a secas en la pasada de redacción, el modelo
  anunciaba lo que acababa de hacer en vez de contar lo que averiguó, ignorando
  el resultado que tenía delante. general.tool_result_prompt lo corrige; ahí sí
  se puede añadir estilo sin riesgo, porque la llamada ya ocurrió.

Con tres herramientas declaradas el modelo elige bien entre ellas (15 de 16
medido), pero le cuesta abstenerse y busca cosas que ya sabe. Intentar
corregirlo con instrucciones empeora los aciertos sin reducir los falsos
positivos, coherente con la fragilidad ya documentada.

Corregido además un fallo que dejaba mudo al asistente tras la primera
respuesta: el anillo de reproducción no bajaba nunca su bandera de actividad,
así que el segmentador mantenía el micrófono cerrado creyendo que seguía
hablando. El segmentador se guía ahora por la cola, que no puede desfasarse, y
hay pruebas del anillo con un mando sin dispositivo detrás.

Claude-Session: https://claude.ai/code/session_01FNxz5cSdQSscJH9H7b8uGU
</pre>
</div>
</content>
</entry>
<entry>
<title>Local voice assistant on top of Canary, llama.cpp and qwentts</title>
<updated>2026-09-06T22:23:37+00:00</updated>
<author>
<name>elvis</name>
<email>elvis@claros.ar</email>
</author>
<published>2026-09-06T22:21:16+00:00</published>
<link rel='alternate' type='text/html' href='http://git.all.ar/pub/asist-p/commit/?id=f8f98e83481e7376a235fb305a095552433f79ab'/>
<id>f8f98e83481e7376a235fb305a095552433f79ab</id>
<content type='text'>
Pipeline en Rust de hilos y canales que une los tres motores: el micrófono
alimenta un segmentador con VAD, las intervenciones cerradas van al
reconocedor, la transcripción al modelo y cada frase que este cierra sale
hacia el sintetizador sin esperar al resto de la respuesta.

Dos reglas sostienen el diseño: ninguna etapa bloquea a la anterior —quien va
sobrado descarta trabajo en lugar de acumular retraso— y todo lo que viaja por
los canales lleva el turno al que pertenece, así que interrumpir es subir el
contador y levantar dos banderas de cancelación.

Seis crates: core (configuración, eventos, HTTP, telemetría, herramientas),
audio (cpal, VAD, anillo de reproducción), asr, llm, tts y app (supervisor de
procesos y orquestador). Los motores van como submódulos fijados a un commit,
con los cambios locales en vendor/patches.

Midiendo el pipeline aparecieron tres cuellos de botella de configuración que
valieron más que cualquier cambio de código, todos documentados en
docs/RENDIMIENTO.md:

- tts-server decodificaba el audio en bloques de 24 s, de modo que el modo
  «streaming» llegaba de una pieza: 4948 ms -&gt; 585 ms hasta el primer audio.
- La plantilla de chat del modelo abre &lt;think&gt; y no lo cierra nunca, sin
  variable que lo apague: 8630 ms -&gt; 413 ms hasta el primer token, con una
  copia de la plantilla que deja el bloque cerrado de entrada.
- Cualquier indicación de estilo junto a la guía de herramientas hace que este
  modelo de 2B deje de llamarlas y se invente el dato (8/8 aciertos con la
  guía sola, 0/8 con la persona de asistente de voz). El turno alterna ahora
  entre dos instrucciones de sistema.

La ejecución de órdenes del sistema queda implementada y apagada, tras cuatro
barreras: lista blanca sobre el ejecutable, rutas rechazadas, sin shell que
interprete metacaracteres y plazo máximo.

68 pruebas unitarias sin modelos, más seis de integración que se saltan solas
si no hay servidores y se turnan la GPU: en paralelo, los dos servidores no
caben en 4 GB y miden contención en vez de latencia.

Claude-Session: https://claude.ai/code/session_01FNxz5cSdQSscJH9H7b8uGU
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Pipeline en Rust de hilos y canales que une los tres motores: el micrófono
alimenta un segmentador con VAD, las intervenciones cerradas van al
reconocedor, la transcripción al modelo y cada frase que este cierra sale
hacia el sintetizador sin esperar al resto de la respuesta.

Dos reglas sostienen el diseño: ninguna etapa bloquea a la anterior —quien va
sobrado descarta trabajo en lugar de acumular retraso— y todo lo que viaja por
los canales lleva el turno al que pertenece, así que interrumpir es subir el
contador y levantar dos banderas de cancelación.

Seis crates: core (configuración, eventos, HTTP, telemetría, herramientas),
audio (cpal, VAD, anillo de reproducción), asr, llm, tts y app (supervisor de
procesos y orquestador). Los motores van como submódulos fijados a un commit,
con los cambios locales en vendor/patches.

Midiendo el pipeline aparecieron tres cuellos de botella de configuración que
valieron más que cualquier cambio de código, todos documentados en
docs/RENDIMIENTO.md:

- tts-server decodificaba el audio en bloques de 24 s, de modo que el modo
  «streaming» llegaba de una pieza: 4948 ms -&gt; 585 ms hasta el primer audio.
- La plantilla de chat del modelo abre &lt;think&gt; y no lo cierra nunca, sin
  variable que lo apague: 8630 ms -&gt; 413 ms hasta el primer token, con una
  copia de la plantilla que deja el bloque cerrado de entrada.
- Cualquier indicación de estilo junto a la guía de herramientas hace que este
  modelo de 2B deje de llamarlas y se invente el dato (8/8 aciertos con la
  guía sola, 0/8 con la persona de asistente de voz). El turno alterna ahora
  entre dos instrucciones de sistema.

La ejecución de órdenes del sistema queda implementada y apagada, tras cuatro
barreras: lista blanca sobre el ejecutable, rutas rechazadas, sin shell que
interprete metacaracteres y plazo máximo.

68 pruebas unitarias sin modelos, más seis de integración que se saltan solas
si no hay servidores y se turnan la GPU: en paralelo, los dos servidores no
caben en 4 GB y miden contención en vez de latencia.

Claude-Session: https://claude.ai/code/session_01FNxz5cSdQSscJH9H7b8uGU
</pre>
</div>
</content>
</entry>
</feed>
