cd ../blog-dev
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.

01

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.
02

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.
03

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.
04

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.
05

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.
06

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.