claudio.cool
CCAR-FLecciones

CCAR-F · Ingeniería de prompts

Salida estructurada con tool_use y JSON Schema

⏱ 3 min de lecturaBorrador

Un sistema extrae datos de facturas pidiendo “devolvé un JSON con estos campos”. El 3% de las respuestas no se puede parsear: una coma de más, un comentario antes del JSON. Pedir el formato en el texto deja la sintaxis en manos de que el modelo cumpla una instrucción.

La salida como herramienta

La forma más confiable de obtener salida que cumple un esquema es el uso de herramientas (tool use): definís una herramienta de extracción, por ejemplo extract_invoice, cuyo input_schema es un esquema JSON (JSON schema) con los campos que querés. El modelo responde con un bloque tool_use y sus argumentos (input) son tus datos estructurados. No hace falta ejecutar nada: leés el input y listo. Esto elimina los errores de sintaxis.

Qué tool_choice usar

Valor Qué hace el modelo Cuándo
auto Puede llamar una herramienta o responder con texto Conversación normal; no garantiza la extracción
any Tiene que llamar una herramienta, pero elige cuál Varios esquemas (factura, recibo, nota de crédito) y no sabés qué tipo de documento llega
{"type": "tool", "name": "extract_metadata"} Tiene que llamar esa herramienta Una extracción puntual que tiene que correr sí o sí, por ejemplo antes de los pasos de enriquecimiento

Con auto, el modelo puede contestar “Acá tenés los datos:” en texto y tu pipeline se rompe. Si necesitás la llamada siempre, auto es la respuesta equivocada. (Algunos modelos recientes no admiten forzar la herramienta; ahí la documentación propone auto con el modo estricto de herramientas o salidas estructuradas.)

Lo que el esquema no garantiza

El esquema garantiza estructura y tipos, no que los valores sean ciertos. Siguen pasando errores semánticos (semantic errors):

  • líneas que no suman el total;
  • la fecha de emisión en el campo de vencimiento;
  • un número con el tipo correcto pero mal leído.

Esos errores se detectan con validaciones de contenido aparte, en código (lo ve la lección siguiente).

Diseñar el esquema para que no invente

  • Campos anulables (nullable fields) para lo que puede faltar. Si due_date es obligatorio y el contrato no tiene vencimiento, el modelo inventa una fecha para cumplir. Con "type": ["string", "null"], devuelve null.
  • Enum con “other” + detalle para categorías extensibles: category: ["hardware", "software", "services", "other"] más un category_detail de texto libre que se completa cuando elige “other”. Así no fuerza un caso raro a una categoría equivocada.
  • “unclear” para lo ambiguo: si el documento muestra dos estados contradictorios, mejor un valor que lo diga que una elección al azar.
  • Requerido con array vacío permitido para listas: separa “esta factura no tiene líneas” de “el campo no vino”.
  • Un campo para problemas de extracción (documento ilegible, página faltante): reporta en vez de adivinar.
  • Anidar solo lo necesario: cada nivel extra es más difícil de completar bien.

Normalizar formatos

El esquema dice que total es un número; el documento dice “1.250,00 $”. Sumá reglas de normalización en el prompt junto al esquema estricto (decimales con punto, fechas en YYYY-MM-DD, monedas como código ISO) y un enum de códigos ISO para la moneda en vez de un string libre que vuelve como “USD”, “usd” o “US$”.

Las trampas típicas

  • Pedir JSON en el prompt o “validar el JSON” con otro modelo en vez de usar una herramienta.
  • tool_choice: auto cuando la extracción tiene que ocurrir siempre.
  • Creer que el esquema estricto evita sumas que no cierran.
  • Hacer obligatorio un dato que a veces no está.

En el examen

En el escenario de extracción de datos, si falla el parseo, la respuesta es una herramienta con esquema y la llamada forzada. Si el modelo inventa valores, el campo pasa a ser anulable. Si hay categorías que no entran, “other” con detalle. Si hay varios tipos de documento desconocidos, any; si un paso tiene que correr primero, la herramienta forzada por nombre.

Pregunta rápida

Una pregunta del simulacro sobre este dominio. Cuenta para tu progreso.

A food delivery platform classifies every customer message into one of twelve intents before routing it. The prompt runs about 500,000 times a day, and the platform team measures consistency on a labelled sample of 2,000 messages.

To fix some unstable intents, a developer added eight few-shot examples to the prompt. Consistency on the labelled sample went from 86% to 93%, but the extra input tokens raised the daily cost sharply, and median latency went up enough that the routing service now misses its target at peak times.

Nobody has checked which of the eight examples actually contribute to the improvement. A product manager has suggested looking into fine-tuning at some point next year.

What is the best next step?

Practicá este dominio en el simulacro →