aicert.study

CCAR-F Escenario 6 en la práctica: un JSON Schema garantiza forma, nunca honestidad

8 de octubre de 2026

CCAR-F Escenario 6 en la práctica: un JSON Schema garantiza forma, nunca honestidad

Último artículo de la serie de 6 partes sobre los escenarios del CCA-F — código fuente completo de cada escenario: labs.

El escenario, tal como está en el exam guide

Un sistema extrae información de documentos no estructurados, valida la salida con schemas JSON y mantiene alta precisión, manejando casos extremos e integrando con sistemas downstream.

DominioPesoDónde aparece en este escenario
Prompt Engineering & Structured Output20%Un JSON Schema de tool_use, más una segunda capa de validación que detecta lo que el schema no puede
Context Management & Reliability15%Qué pasa río abajo cuando un campo no se puede extraer con confianza

La lectura obvia de este escenario está incompleta

"Usar tool_use para sacarle JSON estructurado a Claude" es lo básico — necesario, y tampoco es lo que separa un aprobado de un reprobado aquí. La propia frase del exam guide, manteniendo alta precisión al manejar casos extremos, es la prueba real: ¿qué hace el sistema cuando un documento es ambiguo, no cuando está limpio?

Un JSON Schema garantiza forma. No puede garantizar honestidad.

El EXTRACTION_TOOL_INPUT_SCHEMA de labs es un JSON Schema simple entregado a Claude vía tool_choice: vendor es string | null, fieldsNeedingReview es un array de nombres de campo. Eso garantiza que la respuesta tenga la forma correcta. No garantiza — y estructuralmente no puede garantizar — que la respuesta sea internamente consistente. Nada en JSON Schema puede expresar "si vendor es null, "vendor" también debe aparecer en fieldsNeedingReview". Eso es una relación entre dos campos, y JSON Schema valida campos individualmente.

ExtractionSchema (un schema de zod, verificado después de que llega la respuesta de tool_use) añade exactamente esa verificación vía superRefine: cualquier campo anulable que sea null pero no esté en fieldsNeedingReview falla la validación. Este es todo el escenario en una frase: un schema por sí solo no impide que un modelo devuelva un null de apariencia confiada que nunca marcó como incierto — una segunda capa, que verifica una relación que la primera no puede expresar estructuralmente, sí lo impide.

Dos modos de fallo, un único destino seguro

route.ts solo permite auto_process cuando la validación pasó y fieldsNeedingReview está vacío. Tanto un resultado honesto de "algunos campos son ambiguos" como un resultado que viola la invariante ("esto reclama una confianza que no se ganó") terminan en human_review — y, de forma crítica, el caso de violación de invariante devuelve result: null a quien llama, en lugar de intentar rescatar parte de una extracción autocontradictoria. Adivinar qué mitad de un resultado roto confiar es peor que admitir que ninguna de las dos es confiable. El mismo instinto de "degradar al estado seguro, no propagar un valor roto" que el manejo de hallazgos mal formados del Escenario 5 — aquí el costo de adivinar mal es un sistema downstream actuando silenciosamente sobre datos malos, no un comentario de PR perdido.

Tres fixtures, tres formas distintas de ser ambiguo

  • Limpio — proveedor, fecha, moneda y total inequívocos. Debe ir a auto_process.
  • Ambiguo — una fecha que podría ser dos días distintos (03/04/2026), un proveedor ilegible, ningún símbolo de moneda en ningún lado.
  • Multi-moneda — ítems de línea en monedas inconsistentes y un "ver factura de liquidación final" explícito en lugar de un total, probando si el modelo marca totalAmount en vez de sumar unidades incompatibles en un número que parece válido pero no significa nada.

Ejecútalo tú mismo

git clone https://github.com/aicertstudy/labs
cd labs/ccar-f/scenarios/scenario-6-structured-data-extraction
npm install
npm test    # 8 pruebas pasando — la invariante y el enrutamiento fail-safe, sin necesitar clave de API

cp .env.example .env   # agrega tu ANTHROPIC_API_KEY
npm run extract -- fixtures/invoice-ambiguous.txt

Solo npm test ya ejercita la parte que vale la pena entender primero — la invariante y el fail-safe de enrutamiento — con cero llamadas a la API.

Pon a prueba tus conocimientos

1. ¿Por qué el JSON Schema de tool_use por sí solo no puede garantizar que un vendor nulo siempre esté en fieldsNeedingReview?

Respuesta: JSON Schema valida la forma de cada campo de manera independiente; no tiene una construcción para una relación condicional entre los valores de dos campos distintos.

"Si el campo A es X, el campo B debe contener Y" es una restricción entre campos, no una restricción de tipo sobre un único campo. El JSON Schema puro (y por extensión un input_schema de tool_use) puede describir cómo luce cada campo individualmente, no una regla que abarque dos de ellos.

2. ¿Qué devuelve route() cuando el input bruto de tool_use viola la invariante null/fieldsNeedingReview — un resultado parcial de mejor esfuerzo, o nada?

Respuesta: nada — result: null, enrutado a human_review, sin ningún intento de rescatar o confiar parcialmente en los datos autocontradictorios.

Una extracción internamente inconsistente no indica cuál de sus afirmaciones creer. Devolver un resultado parcial o de "mejor conjetura" requeriría decidir qué mitad de una afirmación rota es la mitad confiable — una decisión para la cual el sistema no tiene ninguna base de principios.

3. En invoice-ambiguous.txt, ¿por qué la fecha 03/04/2026 merece una marca en fieldsNeedingReview aunque sí se extraiga una fecha?

Respuesta: porque el formato es genuinamente ambiguo (día-primero vs. mes-primero), así que cualquier valor único elegido es una conjetura disfrazada de extracción, no una lectura confiada.

La invariante solo captura mecánicamente los casos de null-sin-marcar; capturar "extrajo un valor, pero el valor en sí es una moneda al aire" depende de que el prompt pida exactamente eso (ver el system prompt de extract.ts: marcar un campo incluso no nulo si no hay confianza de que sea correcto) — el schema puede garantizar el caso nulo, pero un caso genuinamente ambiguo y no nulo depende de que el modelo siga esa instrucción.

4. ¿Por qué invoice-multi-currency.txt prueba algo significativamente distinto de invoice-ambiguous.txt?

Respuesta: prueba si el modelo evita combinar silenciosamente unidades incompatibles en un número que parece válido pero no tiene sentido, en lugar de probar si detecta un único campo ausente o ilegible.

Dato ambiguo-pero-presente (un proveedor ilegible) y dato inconsistente-pero-presente (monedas mezcladas sumadas como si fueran una sola) son formas de fallo distintas. La primera trata de reconocer ausencia; la segunda trata de reconocer que un valor calculado puede ser válido según el schema (un número bien formado) y aun así estar sustancialmente equivocado.

5. Si eliminaras la verificación superRefine de ExtractionSchema, ¿qué prueba específica en route.test.ts empezaría a fallar, y por qué?

Respuesta: la prueba "fails safe to human review... when the raw extraction violates the schema invariant" — porque sin superRefine, un vendor nulo con fieldsNeedingReview vacío pasaría la validación y se enrutaría a auto_process en lugar de human_review.

Esa prueba construye exactamente el caso que la invariante existe para atrapar. Elimina la verificación que lo atrapa, y el schema acepta silenciosamente lo que antes era un fallo de validación — que es precisamente el modo de fallo del que trata todo el escenario.

Simulacro gratuito del CCA-F, cubriendo los 5 dominios: aicert.study/certifications/ccaf.

Artefactos prácticos

Ver el schema de extracción + invariante en GitHub

¿Listo para practicar para Claude Certified Architect — Foundations (CCAR-F)?

Haz un simulacro de muestra gratis y mira tu desempeño, dominio por dominio.

Probar el simulacro