Cómo hicimos que LLMs económicos fueran fiables con DynamoDB: validadores de herramientas + evals
El asistente de IA de DynoTable se ejecuta con tus propias credenciales de , lo que significa que tú eliges el modelo, y mucha gente elige uno económico. Así que no ajustamos el asistente contra un modelo de frontera. Nuestro suelo es Amazon Nova Lite, un modelo que cuesta un error de redondeo por consulta y falla de maneras en las que los modelos de frontera no fallan.
Aquí está el remate de toda esta entrada: en un escenario de —una unión que necesita la tabla de clave compuesta como base— Nova Lite obtuvo un 0 %. No cambiamos de modelo, no añadimos ejemplos de few-shot, no hicimos fine-tuning. Reescribimos el mensaje de error de un validador para decir exactamente qué hacer en su lugar. Pasó al 100 %, resolviendo la unión en el paso siguiente.
El modelo nunca fue el cuello de botella. El mensaje sí.
Esta es la historia de ingeniería detrás de esa lección: los validadores que rodean cada llamada de herramienta, las cosas que deliberadamente no validamos y el arnés de evals que arbitra cada cambio. Si estás construyendo un sobre un modelo de presupuesto —sobre DynamoDB o cualquier otra cosa— la mayor parte se transfiere.
Por qué los modelos económicos fallan específicamente con DynamoDB
DynamoDB es un objetivo hostil para un modelo pequeño porque la superficie de consulta
parece SQL y no lo es. PartiQL acepta
sintaxis SELECT pero no admite JOIN, ni GROUP BY, ni DISTINCT, ni
LIKE, y un modelo que aprendió SQL de internet los emite todos con
confianza.
Los fallos que observamos de verdad, cada uno de una ejecución real registrada:
- Argumentos
limitomitidos. Los omiten habitualmente el límite de filas opcional de una herramienta de consulta. El backend devuelve encantado todo, un resultado de herramienta de 200 KB aterriza en la conversación y el siguiente turno del modelo muere en el techo de contexto: unaValidationExceptiona mitad de bucle, achacada al componente equivocado. - Construcciones no admitidas.
JOIN,GROUP BY, agregados en PartiQL: sentencias que DynamoDB nunca ejecutará, emitidas porque serían correctas en Postgres. - Intercambios de identificadores. Nova Lite, ante
SELECT * FROM customersy la instrucción «añade un WHERE para Alemania», a veces devolvíaSELECT * FROM orders WHERE …: una tabla que el usuario nunca mencionó. La sentencia se parsea perfectamente. Nada sintáctico la atrapa. - Enrutamiento incorrecto de herramientas. Preguntado por un archivo adjunto, Nova Lite llamaba a la herramienta de listado de pestañas abiertas buscando archivos en pestañas: rara vez usaba la mecánica de búsqueda de herramientas, así que cualquier cosa que no fuera directamente visible no existía.
Ninguno de estos es hipotético. Cada uno impulsó una barrera de protección concreta más abajo.
Valida en el límite de la herramienta, y haz del error una lección
La decisión arquitectónica más importante: la validación vive dentro del paso de ejecución de la herramienta, no en el esquema de entrada. Un rechazo de esquema es un callejón sin salida: el bucle ve un error de tipo y no aprende nada. Una herramienta que se ejecuta, comprueba y devuelve un error estructurado le da al modelo un resultado de herramienta normal al que puede reaccionar en el paso siguiente.
Y ese error se escribe para el modelo, no para un registro humano. Los errores de nuestra herramienta de exportación se leen así, al pie de la letra:
startExport requires exactly one of {tabId} or {sql}.
FIX: call listOpenTabs and pass {tabId}, or pass {sql} with a single SELECT.startExport {sql} must be a read-only single SELECT.
FIX: remove any INSERT/UPDATE/DELETE/DDL and pass one SELECT statement.Los fallos de validación de consultas llevan un prefijo validation: —
validation: parse-error: …, validation: partiql-unsupported: JOIN is not supported by DynamoDB PartiQL.— que el prompt del sistema establece como la
señal de arreglar la sentencia en lugar de reintentar con la misma entrada. Un
mensaje de rechazo es una señal de enseñanza. Antes de adoptar la convención FIX:,
los mismos fallos terminaban con el modelo disculpándose y diciéndole
al usuario que pulsara él mismo el botón de exportar. Después, se autocorrige y
completa la tarea.
El corolario tardó más en aprenderse: dónde vive el ejemplo concreto es un eje de diseño. Si un error lo atrapa un validador en tiempo de ejecución, mantén el prompt del sistema genérico y deja que el mensaje de error dinámico lleve las tablas concretas y la solución: el prompt se mantiene pequeño y la lección llega justo cuando hace falta. Solo los errores que ningún validador puede atrapar (como enrutar una pregunta de agregación a la herramienta correcta en primer lugar) se ganan un ejemplo concreto en el propio prompt.
Parsea las cosas que vas a rechazar
Rechazar JOIN con un genérico syntax error at offset 27 no enseña
nada. Así que nuestro parser de PartiQL escrito a mano hace algo ligeramente
perverso: parsea deliberadamente construcciones que DynamoDB no admite
en un árbol sintáctico válido, solo para que un recorredor pueda emitir un diagnóstico específico
y enseñable. Todo al pie de la letra, todo de cara al modelo:
JOIN is not supported by DynamoDB PartiQL.GROUP BY is not supported by DynamoDB PartiQL.TOP N is not supported. Use the API limit parameter.IN list has 120 items. DynamoDB PartiQL caps IN at 50 values (PK column) or 100 values (non-key column).IS NOT NULL is not supported in DynamoDB PartiQL. Use attribute_exists.lower(path) is not a DynamoDB PartiQL function. Use … instead.— con el reemplazo resuelto por función.
Cada mensaje nombra la construcción y la vía de escape. Combinado con la regla del prompt «si PartiQL rechaza una construcción no admitida, cambia al workbench de SQL de inmediato», un modelo económico se recupera de sus instintos de Postgres en un solo paso en lugar de dar bandazos.
Sabe qué NO validar
El nivel consciente del esquema de nuestro validador de SQL resuelve las referencias a tablas y columnas
contra las descripciones de tabla reales que la app ya tiene. Al
principio también marcaba las referencias a atributos que DynamoDB no declaraba, lo que
suena correcto y es exactamente incorrecto. El attributeDefinitions de DynamoDB solo
lista los atributos clave, así que la comprobación rechazaba WHERE <non-key> = …: la
forma de consulta legítima más común que existe. Esa comprobación ahora es una
advertencia que nunca bloquea.
Cada validador es una apuesta a que la forma rechazada es más probablemente incorrecta que correcta. Cuando el modelo de datos no puede sostener la comprobación, no hagas la apuesta.
Los topes de resultados como comportamiento, no solo como seguridad
La solución para el estallido del limit omitido no fue «añadir el límite al
prompt» (los modelos económicos lo omiten igual). Los resultados de las herramientas de consulta se topan de forma dura —
10 filas o 5 KB, lo que llegue primero, siempre manteniendo al menos la
primera fila— y el aviso de truncamiento es él mismo prescriptivo:
Showing 10 of 4,200. To filter: re-run with WHERE.
To view in UI: emit proposeOpenTable.
To aggregate: use runWorkbenchSql GROUP BY.Los topes están intencionadamente muy por debajo de lo que el contexto podría sostener. El objetivo
es de comportamiento: forzar al modelo a entregar al usuario una vista de tabla real o una
agregación exacta, nunca a enumerar filas en el chat. La pista de recuperación
nombra las herramientas exactas, así que el tope enseña el enrutamiento en el momento en que el
modelo lo necesita. El mismo sobre está reflejado byte a byte en el arnés de
evals, y un escenario de regresión fija truncated: true contra una
tabla de 10 000 elementos para que el tope no pueda desaparecer silenciosamente.

Las evals son el árbitro
Nada de lo anterior se diseñó por intuición y se mantuvo por fe. Cada cláusula de prompt, mensaje de validador y tope está controlado por una suite de evals que ejecuta bucles de agente reales contra un DynamoDB Local sembrado —herramientas reales, ejecución de consultas real, los constructores de prompts y validadores de producción importados directamente en lugar de reimplementados— puntuados por comprobaciones binarias sobre el rastro de herramientas, el estado final de la base de datos y el texto.
Lo que las evals atraparon y la intuición se perdió:
- Un prompt «más limpio» y más genérico hizo retroceder un escenario del 100 % al 0 %. A nosotros nos leía mejor. El modelo del suelo discrepó. Solo la eval lo notó.
- Los consejos de prompts para modelos de frontera son activamente incorrectos para los económicos. La guía publicada dice suavizar el lenguaje CRITICAL/MUST porque los modelos grandes se sobreactivan con él. Los modelos económicos cumplen de menos y necesitan la versión firme. Ahora sometemos a eval cualquier consejo prestado en lugar de aplicarlo por fe.
- Las aserciones estrictas de herramientas castigaban el comportamiento correcto. Un puntuador que exigía
que el modelo llamara a la herramienta
askpara pedir aclaración pasaba un 19 % en toda la escala de modelos: casi todo «fallo» era un modelo que había pedido correctamente aclaración en texto. El puntuador ahora acepta cualquiera de las dos. Puntúa la intención de cara al usuario, no el mecanismo. - La escala de modelos se ganó su estrechamiento. Empezamos amplios —Mistral,
AI21, GLM, Qwen, Nova Pro, más— y la recortamos a dos peldaños (Nova Lite,
Claude Haiku) por razones que solo las ejecuciones reales sacan a la luz: una familia costaba ~10×
por llamada porque Bedrock no le ofrecía caché de prompts; Nova Pro
alucinaba nombres de atributos concatenados como
customers.customerNameen uniones abiertas. Y una familia tuvo que ser vetada de plano: la API unificada de Bedrock acepta encantada definiciones de herramientas para modelos cuyo adaptador luego emite las llamadas de función como texto plano, algo que los metadatos de la API no te dirán. Lo encontramos con una sonda de humo de pago, no con una especificación.
Dos disciplinas mantienen la suite honesta. Las tablas de ejemplo nunca son iguales a las tablas de prueba: los ejemplos del prompt usan nombres neutrales que no existen en los datos sembrados, y las evals se ejecutan sobre tablas distintas, así que un acierto demuestra que el modelo generalizó el patrón, no que memorizó nuestro ejemplo. Y el modo de vigilancia de 0 $ rechaza las fixtures grabadas: las salidas de modelo reproducidas pasarían en verde aserciones que dependen de actividad real de herramientas, así que el nivel gratuito valida solo el cableado, y cualquier cosa que reclame una puntuación tuvo que gastar tokens reales en un modelo real.
La lista de comprobación, si estás construyendo uno de estos
- Elige tu modelo del suelo primero y somételo siempre a eval; los modelos de frontera esconden tus errores.
- Pon la validación en el paso de ejecución de la herramienta; devuelve errores sobre los que el bucle pueda actuar, nunca callejones sin salida de esquema.
- Escribe cada rechazo como
qué está mal+FIX: acción exacta siguiente, e interpola en él las tablas reales del usuario. - Parsea lo que pretendes rechazar, para que el diagnóstico pueda ser específico.
- Audita cada validador contra tu modelo de datos: borra los que rechazan formas legítimas.
- Topa los resultados de las herramientas por debajo de lo que cabe, y haz que el aviso de truncamiento enrute al modelo hacia la siguiente herramienta correcta.
- Ejecuta las evals como bucles reales contra un backend real (local), reutilizando las rutas de código de producción; mantén los datos de ejemplo disjuntos de los de prueba; haz de la eval —no del gusto— el árbitro de cada cambio de prompt y mensaje.
¿Importa algo de esto si usas un modelo de frontera?
Menos a menudo, pero sí. Los validadores son fundamentales en todos los niveles: un
modelo de frontera sigue sin poder ejecutar JOIN en DynamoDB, y los errores prescriptivos
solo se consumen más rápido. En nuestras propias ejecuciones, el comportamiento de emisión de chips de un
modelo de clase de frontera cayó una vez al 0 % bajo una redacción de prompt que los
modelos más pequeños manejaban: el arnés de evals también atrapó eso. La ingeniería de
fiabilidad no es un impuesto de los modelos pequeños; los modelos pequeños simplemente hacen que la factura
llegue antes.
Dónde se ejecuta esto
Todo esto se distribuye dentro del asistente de DynoTable y su catálogo de herramientas controladas: consultas en lenguaje natural conscientes del esquema con tus propias credenciales de Bedrock, agregaciones exactas de tabla completa y escrituras que solo aterrizan en un área de preparación revisable. Los agentes externos obtienen el mismo conjunto de herramientas validado a través del servidor MCP: cómo construimos eso de forma segura es su propia historia, desde OAuth hasta el aislamiento de credenciales.
Y cuando quieras determinismo en lugar de generación —la consulta que vas a comprometer al código— sáltate el modelo por completo: el Expression Builder ensambla la petición exacta a mano.


