Rechaza JSON ambiguo de IA antes de automatizar con Python
En esta página
Un asistente de IA puede devolver algo que parece JSON, pero deja al flujo de trabajo con dos etiquetas contradictorias o una confianza que no es un número. Analizar el texto sin errores no basta: el decodificador predeterminado de Python acepta claves repetidas y conserva el último valor. Python documenta este comportamiento.
Crea un filtro local que rechace la ambigüedad antes de enviar una respuesta a la automatización. Necesitas Python 3.10 o posterior, sin paquetes adicionales ni cuenta de un modelo. Los ejemplos son pruebas sintéticas del analizador, no respuestas medidas de una IA.
1. Define el contrato de recepción
Para un prototipo que clasifica incidencias, exige un objeto con exactamente dos campos:
{"label":"billing","confidence":0.8}
Las etiquetas permitidas son billing, technical y other. La confianza debe ser un número finito entre 0 y 1, ambos incluidos; no se aceptan booleanos ni cadenas numéricas. Limita cada respuesta UTF-8 a 16.384 bytes. Son reglas de este flujo, no requisitos universales de JSON.
RFC 8259 recomienda nombres únicos en cada objeto y advierte que el tratamiento de duplicados varía. Su gramática numérica excluye NaN e Infinity. Rechaza esos casos en vez de adivinar la intención del modelo. Como con las restricciones de integridad de SQL, define las reglas de aceptación antes de usar los datos.
2. Guarda el validador estricto
Guarda este código como check_ai_json.py en un directorio de trabajo nuevo. Lee un archivo de respuesta con tamaño limitado; nunca ejecuta código escrito por un modelo ni asigna una incidencia.
import json
import math
import sys
def unique_object(pairs):
result = {}
for key, value in pairs:
if key in result:
raise ValueError(f"duplicate key: {key}")
result[key] = value
return result
def reject_constant(token):
raise ValueError(f"non-JSON constant: {token}")
def load_reply(path):
with open(path, "rb") as stream:
payload = stream.read(16_385)
if len(payload) > 16_384:
raise ValueError("response exceeds 16384 bytes")
try:
data = json.loads(payload.decode("utf-8"),
object_pairs_hook=unique_object,
parse_constant=reject_constant)
except json.JSONDecodeError as error:
raise ValueError("invalid JSON syntax") from error
if not isinstance(data, dict) or set(data) != {"label", "confidence"}:
raise ValueError("expected only label and confidence")
if type(data["label"]) is not str or data["label"] not in {
"billing", "technical", "other"
}:
raise ValueError("unknown label")
score = data["confidence"]
if type(score) not in (int, float):
raise ValueError("confidence must be a number, not bool or string")
if not 0 <= score <= 1 or not math.isfinite(score):
raise ValueError("confidence must be finite and between 0 and 1")
return data
if __name__ == "__main__":
if len(sys.argv) != 2:
raise SystemExit("Usage: python3 check_ai_json.py RESPONSE.json")
try:
result = load_reply(sys.argv[1])
except (OSError, ValueError, RecursionError) as error:
raise SystemExit(f"FAIL: {error}")
print(f"PASS: structure accepted; label={result['label']}")
Los mecanismos del decodificador inspeccionan los pares originales y rechazan constantes ajenas a JSON. También detectan claves equivalentes escritas con escapes. Para validar números, recuerda que los booleanos de Python son subtipos de los enteros: la comprobación del tipo exacto los excluye deliberadamente.
El intervalo se comprueba antes de llamar a isfinite, evitando convertir enteros enormes fuera de rango. El número JSON 1e400, sintácticamente válido, puede desbordar la representación flotante predeterminada del decodificador; aun así debe incumplir nuestra regla de confianza.
3. Crea casos válidos y negativos
Guarda este código junto al validador como make_json_fixtures.py. Ejecútalo en el directorio nuevo: crea json-demo y se niega a reutilizar una carpeta existente. Los casos adversos siguen el mismo principio que las pruebas pequeñas de aceptación para regex.
from pathlib import Path
root = Path("json-demo")
root.mkdir()
good = '{"label":"billing","confidence":0.8}'
cases = {
"good": good,
"duplicate": '{"label":"billing","label":"technical","confidence":0.8}',
"nan": good.replace("0.8", "NaN"),
"infinity": good.replace("0.8", "Infinity"),
"overflow": good.replace("0.8", "1e400"),
"boolean": good.replace("0.8", "true"),
"quoted": good.replace("0.8", '"0.8"'),
"extra": good[:-1] + ',"note":"hello"}',
"prose": good + " Here is your answer.",
"array": "[" + good + "]",
"escaped": r'{"label":"billing","la\u0062el":"other","confidence":0.8}',
}
for name, text in cases.items():
with (root / f"{name}.json").open("x", encoding="utf-8") as stream:
stream.write(text)
Ejecuta estos comandos en Bash. Deja set -e desactivado porque diez fallos son intencionados.
python3 make_json_fixtures.py
for case in good duplicate nan infinity overflow boolean quoted extra prose array escaped; do
python3 check_ai_json.py "json-demo/$case.json"
echo "exit=$?"
done
4. Valida los resultados observados
En las pruebas locales con Python 3.11.4, good imprime lo siguiente en stdout, deja stderr vacío y termina con código 0:
PASS: structure accepted; label=billing
Los demás casos terminan con código 1 y stdout vacío. Su stderr contiene una línea:
| Caso | Stderr |
|---|---|
duplicate, escaped |
FAIL: duplicate key: label |
nan |
FAIL: non-JSON constant: NaN |
infinity |
FAIL: non-JSON constant: Infinity |
overflow |
FAIL: confidence must be finite and between 0 and 1 |
boolean, quoted |
FAIL: confidence must be a number, not bool or string |
extra, array |
FAIL: expected only label and confidence |
prose |
FAIL: invalid JSON syntax |
El bucle de Bash imprime cada código de salida por separado. Los once archivos de entrada permanecieron intactos durante estas comprobaciones. Antes de integrar el validador, exige que pase el caso válido y que fallen todos los negativos.
Superar la prueba no indica si billing es la etiqueta correcta. Mídelo por separado con datos etiquetados y una matriz de confusión para clasificar incidencias. Una cifra de confianza proporcionada por un modelo no es automáticamente una probabilidad calibrada.
5. Resuelve problemas sin reparar datos en silencio
- Envoltorios Markdown o explicaciones finales: vuelve a pedir un objeto JSON sin envoltorios. No extraigas un fragmento conveniente ignorando el resto.
- Claves duplicadas: regenera la respuesta. Conservar un valor descarta silenciosamente información contradictoria.
- Números entre comillas o booleanos: corrige el contrato de salida en vez de convertirlos en números.
- Campos inesperados: crea una nueva versión del contrato de recepción y actualiza los casos antes de permitir claves nuevas.
- Sigue aceptando duplicados con strict=True: esa opción controla los caracteres de control sin escapar, no la unicidad de las claves. Usa los mecanismos anteriores.
Mantén este filtro separado de las acciones posteriores. El límite de bytes es una precaución del tutorial, no una barrera completa de aislamiento de recursos o seguridad. La recepción de datos en producción también necesita límites de recursos adecuados y una vía de revisión para las respuestas rechazadas.
Conclusión práctica
Trata el JSON de una IA como datos no fiables. Rechaza primero la sintaxis ambigua y los valores de campo no válidos; después evalúa si el contenido aceptado es útil y correcto. Conserva la respuesta original y el motivo del rechazo para depurar problemas.
Artículos relacionados
Revisa planes de renombrado de IA sin cambiar archivos
Evalúa probabilidades de IA con la puntuación de Brier en Python
Detecta texto repetido entre entrenamiento y prueba antes de evaluar una IA
Evalúa la clasificación de incidencias con una matriz de confusión
Prueba expresiones regulares de IA con casos pequeños en Python
Prueba el SQL generado por IA con pequeños juegos de datos de SQLite
Evalúa transcripciones de IA con la tasa de error por palabra
Cómo evaluar la recuperación RAG con Recall@k y MRR
Por Qué el Duck Typing Hace que Python Sea Tan Poderoso
Dominando 7 Algoritmos Esenciales para Estructuras de Datos en Python
Guía para Principiantes para Elegir el Framework de Python Adecuado para tus Necesidades
Guía Completa para Principiantes sobre Booleanos en Programación Python
Últimos artículos
Evalúa campos extraídos por IA con precisión y recall de coincidencia exacta
Comprueba las rutas propuestas por IA antes de escribir con Python
Evita actualizaciones de IA obsoletas con un control de versión en SQLite
Comprueba las dependencias de tareas de IA antes de programarlas con Python
Revisa los cambios JSON de la IA con una lista de campos permitidos
Combina resultados RAG con fusión de rangos recíprocos
Comprueba las horas de calendario de IA antes de crear eventos
Comprueba citas textuales de IA con fuentes locales y Python
Comprueba las cifras de resúmenes de IA con una tabla de referencia
Elige umbrales de revisión de IA con cobertura y tasas de error
Comprueba los marcadores de traducciones de IA con Python