decisionesv1
Decisiones del TPV
por voz · v1
Las seis decisiones técnicas detrás del TPV por voz: por qué solo se escribe en Supabase al confirmar, por qué se manda el transcript con un schema en lugar de usar function calling, por qué se filtran las muletillas antes de llamar al modelo, por qué el system prompt no pasa de ocho reglas, por qué la voz y el menú manual usan el mismo formato de línea y por qué cada turno se reemplaza entero en lugar de aplicar un diff.
Solo se escribe en Supabase al pulsar Confirmar
- Contexto
- En un turno de voz es muy frecuente que el camarero rectifique a mitad de dictado ("no, mejor solo uno"). Cada cambio recompone las líneas que ya iban en ese turno.
- Decisión
- Las líneas del turno se mantienen en useState dentro del componente. Solo se hace POST a Supabase cuando el camarero pulsa Confirmar.
- Alternativa descartada
- Escritura optimista al servidor tras cada respuesta del modelo. Probada y descartada: cada rectificación obligaba a deshacer la escritura anterior, y la latencia que sentía el camarero acababa siendo peor que mantener el draft en cliente.
- Consecuencia
- Lo que tarda en aparecer una línea en pantalla depende del modelo, no de la red. Confirmar pasa a ser el único punto donde algo se escribe de verdad, así que los reintentos y los errores se manejan en un solo sitio.
Una sola llamada al modelo con un JSON schema fijo
- Contexto
- El modelo tiene que devolver la comanda completa del turno (itemKey, variantKey, quantity, note) con un tiempo de respuesta predecible para poder usarlo decenas de veces por servicio.
- Decisión
- Una llamada a Vertex AI pidiendo respuesta en JSON con un schema fijo. La respuesta trae el pedido entero del turno.
- Alternativa descartada
- Function calling. Probado primero. Las latencias eran irregulares (entre 800 ms y 2 s sin patrón claro) y, cuando algo iba mal, costaba más entender qué había pasado.
- Consecuencia
- Tiempo de respuesta predecible, el schema vive en un único sitio y el prompt no tiene firmas de funciones que mantener sincronizadas con el cliente.
Las muletillas se filtran antes de llamar al modelo
- Contexto
- El transcript que devuelve Speechmatics llega lleno de muletillas (eh, em, pues...) que no aportan nada al pedido y solo alargan el prompt.
- Decisión
- cleanTranscriptForLlm tiene una lista cerrada de muletillas castellanas y las quita en el cliente, justo antes del POST a Vertex AI.
- Alternativa descartada
- Dejar que el modelo las ignore por su cuenta. Funciona, pero alarga el prompt y por tanto la latencia. Limpiarlo antes es más barato y predecible.
- Consecuencia
- Prompts más cortos y estables. La lista habrá que ampliarla cuando entren acentos cerrados o palabras en catalán o vasco; es la decisión que más papeletas tiene de envejecer.
El system prompt no pasa de ocho reglas
- Contexto
- En iteraciones anteriores el system prompt llegó a tener catorce reglas. A partir de la novena se notaba que la regla de variantes y la de cantidad por defecto se pisaban entre sí.
- Decisión
- El system prompt se queda en ocho reglas. Cualquier regla nueva tiene que justificar su sitio y, casi siempre, sustituye a una que ya estaba. La cantidad por defecto (1) está escrita como una de esas reglas, no como default del schema.
- Alternativa descartada
- Mover los defaults al schema (por ejemplo, default 1 para quantity). Probado: el modelo a veces respeta los defaults del schema y a veces no. La misma regla escrita en el prompt funciona mucho mejor.
- Consecuencia
- El prompt se lee de un vistazo. Cualquier regla nueva obliga a revisar las que ya estaban antes de añadirla.
La voz y el menú manual usan el mismo formato de línea
- Contexto
- Hay dos formas de añadir líneas al pedido: dictado por voz y selección manual en el menú del TPV. Las dos terminan en el mismo confirmador.
- Decisión
- Voz y menú manual construyen exactamente el mismo tipo (ManualDraftLine) y se confirman por el mismo camino.
- Alternativa descartada
- Dos modelos de datos paralelos que se unifican al final. Cada diferencia entre estructuras se traduciría en errores al pasar de uno a otro.
- Consecuencia
- Si la voz falla (ruido del local, una palabra que el modelo no reconoce, latencia mala), el camarero termina el pedido por menú sin cambiar de flujo. El sistema sigue funcionando con la voz caída.
Cada turno se reemplaza entero, no se aplica un diff
- Contexto
- El modelo tiene que reflejar correcciones a mitad de dictado. Pedirle un diff sobre el turno anterior obliga a inferir qué línea sustituye a cuál, y esa inferencia se rompe a menudo.
- Decisión
- Al pulsar el botón se genera un voiceSessionId (UUID). Cuando el modelo responde, applyRefinedDraft borra las líneas previas con ese id y mete las nuevas como bloque. Siempre se trabaja con el estado final del turno.
- Alternativa descartada
- Pedir al modelo un diff sobre el turno anterior. Probado en el primer prototipo: una rectificación a mitad de frase ("no, mejor solo uno") rompía el diff porque el modelo no marcaba qué línea sustituía a cuál.
- Consecuencia
- Más datos por turno (estado completo en lugar del cambio), pero comportamiento robusto frente a rectificaciones, que es lo que pasa todo el rato.