| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
| |
README; rename scripts
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Cuarta herramienta: captura la pantalla y responde sobre lo que hay en ella.
Aprovecha el mismo proyector multimodal que ya usaba la cámara.
Como las dos hacen lo mismo —conseguir un JPEG, preguntarle al modelo, devolver
texto— y sólo cambian en de dónde salen los píxeles, se unifican tras
VisionTool y un trait FrameSource. La captura vive en scripts/capturar-
pantalla.sh, que detecta grim, spectacle, maim, ImageMagick o scrot y reduce con
ffmpeg: añadir un compositor es editar el guion, no recompilar.
El ajuste que decide todo es la resolución, y no coincide con la de la cámara
porque el problema no es el mismo: una escena se entiende, un texto hay que
leerlo. Medido con tipografía de interfaz de 13 px y preguntando por datos
concretos, a 1280 px acierta 3 de 3 en 7,6 s; a 960, 2 de 3; a 640, 1 de 3.
Y a 640 px no falla diciendo que no lee: dijo que el error era «no se pudo abrir
el archivo involution» y que la reunión era «a las 10:00», cuando ponía
/dev/video0 y 15:30. Para un asistente de voz, decir una hora equivocada con
aplomo es peor que tardar cuatro segundos más, así que 1280 px por defecto
aunque cueste el triple que la cámara. Al prompt se le añade además que no
complete lo que no se distinga, y funciona: en una prueba real contestó que el
texto de la barra de direcciones era ilegible en vez de inventárselo.
Con cuatro herramientas declaradas el riesgo era confundir pantalla con cámara.
No pasa: 18 de 20, con cero confusiones entre ambas.
Corregido de paso un fallo del lanzador de procesos que la captura destapó.
Esperaba con try_wait en bucle sin vaciar las tuberías, así que un hijo que
escribiera más de los 64 KB del búfer se quedaba bloqueado y moría por plazo
vencido aunque estuviera trabajando. Los tres usos anteriores cabían de sobra
—una fecha, un JSON, un fotograma de 9 KB—; la primera captura, de 174 KB, no.
Ahora se vacían en hilos aparte, con una prueba por tubería.
Ese lanzador es además nuevo: había tres copias del mismo patrón —plazo
máximo, sin shell de por medio, distinguir fallo de cuelgue— en la shell, la
búsqueda y la cámara. Ahora está una sola vez en asist_core::proc.
Claude-Session: https://claude.ai/code/session_01KSfMfDsRwNAaXcCA6V5RTe
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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
|
|
|
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 -> 585 ms hasta el primer audio.
- La plantilla de chat del modelo abre <think> y no lo cierra nunca, sin
variable que lo apague: 8630 ms -> 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
|