Buddy — frases locales y equiposBuddy — local lines & teams

tres decisiones para hoythree decisions for today

1¿Cómo habla el buddy sin internet?How does the buddy talk without internet?

Con WiFi, el buddy usa un LLM en la nube y dice cosas inteligentes. Sin WiFi —o mientras espera la respuesta— tiene que decir algo. Hay dos formas de conseguirlo, y no son excluyentes. With WiFi, the buddy uses a cloud LLM and says clever things. Without WiFi — or while waiting for the reply — it still has to say something. There are two ways to get that, and they are not mutually exclusive.

A · Banco de frasesLine bank

un archivo de texto con frases escritas por nosotrosa text file of lines we write ourselves
happy: ¡Ooh, un amigo! Hoy es el mejor día.
sleepy: Cinco minutos más. Bueno, diez.
A favorFor
En contraAgainst

B · Modelo de IA propioOur own AI model

una red neuronal diminuta, entrenada por nosotros, corriendo en el ESP32-S3a tiny neural network, trained by us, running on the ESP32-S3

No responde preguntas ni sabe datos. Hace una sola cosa: dado un estado de ánimo, inventa una frase corta con nuestra voz.It answers no questions and knows no facts. It does one thing: given a mood, it invents a short line in our voice.

A favorFor
En contraAgainst
Lo que ya está medido en la placaWhat is already measured on the board
4.85×
más rápido tras optimizar el kernel (31 → 152 tokens/s)faster after the kernel work (31 → 152 tokens/s)
~0.3 s
para una frase de 15 palabras, dentro del firmware realfor a 15-word line, inside the real firmware
~10 min
por experimento de entrenamiento: caben varios en una tardeper training experiment: several fit in one evening

Traducción: la velocidad ya no es el problema. La pregunta que queda es si un modelo tan pequeño suena bien — y eso se decide leyéndolo, no discutiéndolo.Translation: speed is no longer the problem. The remaining question is whether a model this small sounds good — and that is decided by reding, not by arguing.

El punto clave: no es A o BThe key point: it isn't A or B

El banco de frases ES el material de entrenamiento del modelo. Son el mismo archivo en dos etapas: primero se usa tal cual (funciona ya, calidad garantizada), y después ese mismo archivo entrena el modelo. Escribir frases no es apostar por una opción — es el trabajo que hace falta para las dos. The line bank IS the model's training data. Same file, two stages: first it ships as-is (works today, guaranteed quality), then that same file trains the model. Writing lines is not betting on one option — it's the work both need.

Lo que se decide hoyWhat we decide today
  1. ¿Quién escribe frases?Who writes lines? Es lo único que bloquea a las dos opciones.It's the only thing blocking both options.
  2. ¿Invertimos una tarde en probar el modelo?Do we spend one evening trying the model? Sesión previa opcional, para quien tenga curiosidad.Optional pre-session, for whoever is curious.
  3. Y luego se decide escuchandoThen decide by listeningbanco, modelo, o híbrido (banco como base + modelo para variedad). Los tres van detrás del mismo evento, así que cambiar de opinión después es configuración, no rearquitectura.bank, model, or hybrid (bank as the floor + model for variety). All three sit behind the same event, so changing our minds later is config, not re-architecture.

2Las 8 emociones propuestasThe 8 proposed emotions

Esto es lo que el buddy hace hoy en la placa — dibujado con los valores reales del firmware, no un boceto. Son una propuesta: el grupo decide el set final. This is what the buddy does today on the board — drawn from the real firmware values, not a sketch. They are a proposal: the group settles the final set.

Faltan al menos dosAt least two are missing

El bucle de voz las necesita sí o sí: cuando sueltas el botón hay 1,5–3 s de espera, y la cara tiene que decir algo durante ese hueco. Sin esto, el buddy parece colgado.The voice loop needs these regardless: releasing the button leaves a 1.5–3 s gap, and the face has to say something during it. Without them the buddy looks frozen.

Sin diseñar todavía — buen primer encargo para el equipo de personalidad.Not designed yet — a good first task for the personality team.

Por qué hay que cerrarlo prontoWhy this needs settling early

Los nombres de las emociones son vocabulario compartido por cinco sitios: los ojos, el anillo LED, los reflejos de los packs, los prefijos del banco de frases y —si entrenamos— las etiquetas del modelo. The emotion names are shared vocabulary across five places: the eyes, the LED ring, pack reflexes, the line-bank prefixes and — if we train — the model's labels.

Cambiar el set después de escribir las frases significa reescribirlas; después de entrenar, significa reentrenar. Barato hoy, caro en la sesión 3. Changing the set after the lines are written means rewriting them; after training, retraining. Cheap today, expensive by session 3.

Lo que se decideWhat we decide
  1. ¿Sobra alguna de las 8?Do any of the 8 go?
  2. ¿Falta alguna además de pensando/escuchando?Any missing beyond thinking/listening?
  3. ¿Los nombres y colores convencen?Do the names and colors convince?

Antes que nada: ¿qué es un estado? Cuatro formas de nombrarlo

Hoy la lista son emociones (feliz, triste, enfadado). No tiene por qué serlo. Esta es la decisión de fondo, y hay que tomarla antes que cualquier otra — porque cambia cómo se escriben las frases, cómo se diseñan las caras y qué se congela dentro del modelo.

Opción A · emociones lo de hoy
feliztristeenfadadocurioso
feliz: ¡Ooh, un amigo! Hoy es el mejor día.
A favor
En contra
Opción B · nivel de energía
upsettledown(+ spike?)
up: ¡Ooh, un amigo! Hoy es el mejor día.
A favor
En contra
Opción C · registro (cómo se dice)
brightflatsharp softhushedurgent
sharp: Ah. Un humano. Qué ilusión.
A favor
En contra
Opción D · estado de interacción
idlelisteningthinking reactingsleeping
thinking: A ver, déjame pensar un momento…
A favor
En contra

La propuesta: no elegir una — separar en dos capas

Las cuatro opciones compiten solo si tiene que haber una sola lista. Y no tiene por qué, porque hay dos cosas distintas que cambian a ritmos muy distintos:

Capa modelo — se congela

Pocos registros abstractos (estilo B o C). Se quedan grabados dentro del modelo al entrenar: cambiarlos = reentrenar.

Los nombres van como texto normal (bright:), no como símbolos especiales. Así, añadir un registro nuevo más adelante es un reentrenado corto — no rehacer el vocabulario ni tocar el firmware.

Capa pack — siempre editable

Expresiones con nombre, ilimitadas, las define el grupo. Cada una apunta a un registro.

Quien quiera travieso lo declara: cara + color + registro sharp. Sin reentrenar y sin recompilar.

Por qué importa tanto: si metemos feliz, triste, enfadado dentro del modelo, hemos clavado la lista de emociones en un binario — el sitio más dictatorial posible, peor incluso que tenerla en C++, que al menos alguien puede editar. Lo abstracto se congela; lo que el grupo discute se queda en datos.

El orden que implica
  1. Primero el grupo decide las expresiones con nombre — la parte divertida, sin restricciones técnicas.
  2. Después se diseña el set de registros para servirlas (pocos y abstractos).
  3. Al final el corpus, porque codifica las dos decisiones anteriores y es lo caro de rehacer.

Cómo funcionaría de verdad: A y C a la vez

Supongamos el desenlace más probable: el grupo quiere emociones con nombre (opción A), porque son las que se entienden y se disfrutan; y el modelo se entrena con registros (opción C), porque son pocos y estables. No hay que elegir — así encajan.

Capa modelo · 6 registros · se congela al entrenar
bright · flat · sharp · soft · hushed · urgent (seis basta para empezar; caben más luego) # el corpus se etiqueta por REGISTRO, nunca por emoción. # Por eso no hay que reescribirlo cuando alguien # inventa una expresión nueva. bright: ¡Ooh, un amigo! Hoy es el mejor día. sharp: Ah. Un humano. Qué ilusión. soft: No pasa nada. Aquí sigo.
Capa pack · expresiones con nombre · editable siempre
# packs/base/expressions.json { "feliz": { "registro":"bright", "color":"#28eb78", "ojos":{"w":26,"h":30,"lift":14}, "parpadeo":3000 }, "triste": { "registro":"soft", "color":"#3268ff", "ojos":{"w":24,"h":26,"open":75,"ceja":-1} }, "enfadado": { "registro":"sharp", "color":"#ff3219", "ojos":{"w":28,"h":28,"ceja":1} } }

Un pack completo

Esto es todo lo que hay dentro de un pack. Un directorio, se comprime y se pasa: eso es un «cartucho». Ningún archivo necesita compilarse.

La estructura
mi-buddy/ ├─ pack.json quién es y cómo piensa ├─ reflexes/ │ └─ main.be qué hace ante cada evento ├─ faces/ │ ├─ emotions.json las expresiones con nombre │ └─ sprites/ imágenes, si las hay ├─ lines/ el banco, por registro │ ├─ bright.txt │ ├─ sharp.txt │ └─ soft.txt └─ sounds/ pitidos y efectos

lines/ es la parte nueva: hoy el formato de packs no la tiene. Es justo lo que hay que añadirle si sale adelante el banco de frases.

pack.json · la identidad
{ "id": "mi-buddy", "name": "El buddy de Marta", "api_level": 1, "language": "es", "brain": { "system_prompt": "Eres un bicho sarcástico pero cariñoso. Menos de 20 palabras." }, "expressions": { "emotion_map": "faces/emotions.json" } }
faces/emotions.json · la capa pack
{ "feliz": { "registro":"bright", "color":"#28eb78", "ojos":{"w":26,"h":30,"lift":14} }, "travieso": { "registro":"sharp", "color":"#c060ff", "ojos":{"w":26,"h":30,"open":55,"ceja":1} } }
reflexes/main.be · el paso 2, entero
# Se sube desde la web del buddy y se edita en caliente: # ni recompilar ni reflashear. Este archivo ES la personalidad. pokes = 0 buddy.on("touch.pet", def (ev) pokes = 0 # acariciar perdona buddy.face.emotion("feliz") buddy.led.mood("excited") end) buddy.on("touch.poke", def (ev) pokes += 1 if pokes >= 3 # tiene memoria: se harta buddy.face.emotion("enfadado") buddy.show("HMPH.") else buddy.face.emotion("sorprendido") end end) buddy.on("nfc.tag", def (ev) buddy.face.emotion("curioso") buddy.say("Te han enseñado una tarjeta " + ev['payload']) end)

Pruébalo: del gesto a la frase

Pulsa un gesto y sigue el recorrido. Es exactamente la lógica del main.be de arriba — incluido que se enfada al tercer toque, porque los reflejos tienen memoria.

* timer.idle_5m todavía no existe en el firmware — los otros tres sí. Está en el plan, y es un ejemplo de evento nuevo que alguien tendrá que definir.

Qué puede cambiar cada uno

El coste depende de de dónde salen las frases. Cambiar la cara o el color es igual de gratis en los dos casos; cambiar cómo habla no.

Quiero…Qué tocoSi usa el bancoSi usa el modelo
Otras palabras
que hable como yo
mis frases al momento
editas el archivo y ya
ajuste corto
minutos, sobre el modelo base — no se empieza de cero
Otro aspecto
mi "feliz" en rosa y parpadeo lento
color y parpadeo al momentoal momento
Otro carácter
que mi "feliz" suene sarcástico
registro: bright → sharp al momentoal momento
Una expresión que no existe
"travieso", "orgulloso", lo que sea
una entrada nueva apuntando a un registro que ya existe al momentoal momento
Una forma de hablar nueva
un registro que no está entre los 6
frases nuevas con ese prefijo al momento ajuste corto

Nada de esto recompila el firmware — el binario es el mismo en todos los casos. Las dos casillas naranjas son el mismo trabajo: darle al modelo ejemplos nuevos y dejarlo aprender unos minutos.

Lo que revela esa tabla

El banco es más personalizable que el modelo, y conviene decirlo claro: con el banco, cambiar tu voz es editar un archivo; con el modelo, tu voz vive dentro de los pesos y hay que reentrenar un poco para moverla. Todo lo demás — cara, color, carácter, expresiones nuevas — es igual de libre en los dos.

Por eso el híbrido es tan buena opción: tus frases van al banco y salen al instante, y el modelo aporta variedad infinita con la voz base del grupo. Cada uno cubre justo el punto flojo del otro.

El pack de Marta · solo lo que cambia
# packs/marta/expressions.json { "feliz": { "registro":"sharp", "color":"#ff66cc", "parpadeo":4200 }, "travieso": { "registro":"sharp", "color":"#c060ff", "ojos":{"w":26,"h":30,"open":55,"ceja":1} } } # packs/marta/reflexes/main.be # puede cambiar hasta QUÉ expresión dispara una caricia bus.on("touch.pet", def (e) bus.publish("face.emotion", "travieso") end)

Resultado

El buddy de Marta se pone travieso cuando lo acarician, en morado, con los ojos entornados y soltando la frase con retintín. El de Daniel es un cachorro verde que se derrite.

Mismo firmware. Mismo modelo. El mismo binario, byte a byte. Lo único distinto son dos archivos de texto dentro de su pack.

Y lo importante: travieso no existía cuando entrenamos el modelo, y funciona igual — porque apunta a sharp, que sí existía. Esa es toda la gracia de separar las dos capas.

Pendiente relacionado: el anillo LED hoy solo entiende calm, excited y thinking, que no coincide ni con la lista de la cara ni con los registros. Sea cual sea la decisión, hay que alinear los tres.

3Equipos y líderesTeams & leaders

Seis equipos. Cada uno necesita una persona que lo lidere — no que haga todo el trabajo, sino que sepa cómo va su parte (ver abajo qué implica). Nadie está casado con un solo equipo: se puede flotar. Six teams. Each needs one person to lead it — not to do all the work, but to know where their part stands (what that means, below). Nobody is married to one team: floating is fine.

Firmware y arquitecturaFirmware & architecture

C/C++, ESP-IDF · el equipo más grandeC/C++, ESP-IDF · the biggest surface
Handshake: Web UI (la API la consumen ellos)Handshake: Web UI (they consume the API)

VozVoice

1–2 personas comprometidas hasta la sesión 41–2 people committed through session 4
lo más arriesgado de v1riskiest v1 item — por eso es equipo propio y no un apéndice — hence its own team, not an appendix

Web UI / PWAWeb UI / PWA

JS/CSS · Vite + Tailwind · nada de C++JS/CSS · Vite + Tailwind · zero C++
Handshake: Firmware (define la API primero)Handshake: Firmware (defines the API first)

ElectrónicaElectronics

soldadura, protoboard, alimentaciónsoldering, protoboard, power
Handshake: CAD (medidas y sujeciones)Handshake: CAD (dimensions and mounts)

CAD y carcasaCAD & case

diseño 3D e impresión3D design and printing
empezar prontostart early — imprimir lleva días, no horas — printing takes days, not hours

Personalidad y contenidoPersonality & content

imagen · sonido · palabras — sin programarimage · sound · words — no coding
la puerta de entrada más fácileasiest way in

Qué implica liderar (y qué no)What leading means (and doesn't)

Sí: saber cómo va tu parte, traer las decisiones al grupo, asegurarte de que cada tarea tiene dueño, y enseñar algo funcionando al cerrar cada sesión. Yes: know where your track stands, bring decisions to the group, make sure each task has an owner, and show something working at each session close.

No: hacer todo el trabajo, ser quien más sabe del tema, ni decidir en solitario. Lo que se propone aquí es un punto de partida — el grupo lo corrige. No: do all the work, be the biggest expert, or decide alone. What's proposed here is a starting point — the group amends it.

Cómo no pisarnos: el contrato del bus

Dentro del buddy nadie llama a nadie: todo son eventos con nombre (touch.pet, face.emotion). Es justo lo que permite que los equipos avancen en paralelo — voz puede construir sobre voice.* aunque firmware siga con otra cosa.

La regla: cada equipo es dueño de su prefijo. Crear un evento tuyo no pide permiso; cambiar o borrar el de otro, sí. Firmware es dueño del contrato (convenciones y registro), no de todos los eventos.

Hoy hay 15 eventos. Lista completa, dueños y agujeros conocidos: docs/event-registry.md.

4El hardware: qué tenemos y cómo se conecta

Referencia para los equipos de electrónica y CAD. Lo verde ya está comprado, lo ámbar falta por pedir, y lo gris son decisiones del grupo.

Ya lo tenemos · pedido
Por pedir / decidir
Por resolver · hoy o en la sesión 1

Regla de potencia: el pico son ~1,5 A a 5 V y no puede pasar por el devkit. Alimentación por USB-C de panel a un raíl de 5 V propio; el USB del devkit se queda para depurar.

El cableado que ya está definido

Generado desde los valores reales de Kconfig.projbuild: es lo que el firmware espera hoy. Lo punteado aún no tiene pines asignados — primera tarea de electrónica junto a voz.

Trampas del S3 que explican estos pines: GPIO 33–37 son la PSRAM octal, 19/20 son USB, 26–32 la flash y 0/3/45/46 son pines de arranque. Ninguno se puede usar. Por eso la pantalla vive en 7–12 y el RC522 en 38–42.

Números de rendimiento medidos en la placa real (spike tinylm-s3). Detalle técnico en docs/training-workshop.md y docs/param-explorer.html. Performance numbers measured on the real board (spike tinylm-s3). Technical detail in docs/training-workshop.md and docs/param-explorer.html.