Revisa los cambios JSON de la IA con una lista de campos permitidos
En esta página
Un asistente de IA al que pides desactivar las notificaciones por correo también podría “mejorar” la configuración de reintentos. Ambos documentos pueden ser JSON válido y cumplir el mismo esquema. Importa tanto qué cambió como que el resultado pueda analizarse.
Construye un control de solo lectura que compare una configuración original con su reemplazo propuesto y rechace cambios fuera de un único campo autorizado. Este tutorial usa Python 3.10 o posterior, módulos de la biblioteca estándar y casos de prueba locales sintéticos. No llama a ningún modelo ni modifica archivos.
1. Define el cambio permitido
Guarda el documento original y la lista de permisos en un estado controlado por la aplicación. Pide al asistente un documento candidato completo que conserve todos los campos excepto notifications.email. No permitas que el candidato redefina sus propios permisos.
Usa el JSON Pointer exacto /notifications/email, no un prefijo como /notifications. RFC 6901 define las rutas JSON Pointer y sus escapes: una barra dentro de una clave se convierte en ~1, y una tilde en ~0. Por tanto, una clave literal llamada a/b es distinta de las claves anidadas a y b.
Este control presupone documentos JSON pequeños, ya validados, con claves de texto y números finitos. Antes de conectar respuestas del modelo sin procesar, aplica controles estrictos de entrada JSON. El decodificador predeterminado de Python acepta claves duplicadas; su documentación de JSON explica los mecanismos de personalización pertinentes.
2. Guarda el control de comparación
Guarda lo siguiente como audit_json_edits.py. Los objetos se comparan recursivamente. Añadir o eliminar miembros cuenta como cambio, incluso si el valor eliminado era nulo. Los arrays son indivisibles: cambiar cualquier elemento o su orden marca el campo del array completo.
La comparación mediante serialización ordenada ignora el orden de las claves de los objetos y distingue booleanos de números, también dentro de arrays. Además, considera distintos 1 y 1.0. Es una regla conservadora de este flujo, no una definición universal de igualdad JSON ni una canonicalización criptográfica.
import json
from copy import deepcopy
BASELINE = {
"notifications": {"email": True},
"retry_limit": 3,
"channels": ["email", "sms"],
}
ALLOWED = {"/notifications/email"}
def serialized(value):
return json.dumps(value, sort_keys=True, allow_nan=False)
def changed_paths(before, after, path=""):
if isinstance(before, dict) and isinstance(after, dict):
changes = set()
for key in sorted(before.keys() | after.keys()):
token = key.replace("~", "~0").replace("/", "~1")
child = path + "/" + token
if key not in before or key not in after:
changes.add(child)
else:
changes.update(changed_paths(before[key], after[key], child))
return changes
return {path} if serialized(before) != serialized(after) else set()
def decision(candidate):
changes = changed_paths(BASELINE, candidate)
blocked = changes - ALLOWED
if blocked:
return "REJECT " + ", ".join(p or "(root)" for p in sorted(blocked))
if type(candidate["notifications"].get("email")) is not bool:
return "REJECT email must be boolean"
return "ALLOW " + (", ".join(sorted(changes)) or "(unchanged)")
approved = deepcopy(BASELINE)
approved["notifications"]["email"] = False
overreach = deepcopy(approved)
overreach["retry_limit"] = 9
missing = deepcopy(BASELINE)
del missing["notifications"]["email"]
wrong_type = deepcopy(BASELINE)
wrong_type["notifications"]["email"] = 0
for name, candidate in [
("unchanged", BASELINE),
("approved", approved),
("overreach", overreach),
("missing", missing),
("wrong_type", wrong_type),
]:
print(f"{name}: {decision(candidate)}")
assert changed_paths({"a/~b": 1}, {"a/~b": 2}) == {"/a~1~0b"}
assert changed_paths({"x": [True]}, {"x": [1]}) == {"/x"}
assert changed_paths({"x": None}, {}) == {"/x"}
assert changed_paths(BASELINE, []) == {""}
assert changed_paths(BASELINE, deepcopy(BASELINE)) == set()
print("edge checks: PASS")
La comprobación de permisos y la de valores tienen objetivos distintos. Cambiar solo una ruta autorizada tampoco permite sustituir su booleano por cero ni eliminar el campo obligatorio.
3. Ejecuta y valida los casos de prueba
Desde el directorio que contiene el script, ejecuta:
python3 audit_json_edits.py
Salida estándar esperada:
unchanged: ALLOW (unchanged)
approved: ALLOW /notifications/email
overreach: REJECT /retry_limit
missing: REJECT email must be boolean
wrong_type: REJECT email must be boolean
edge checks: PASS
Este ejemplo se comprobó localmente con Python 3.11.4: código de salida 0, sin salida de error estándar y exactamente el resultado mostrado. Estos resultados prueban el control, no la fiabilidad de ningún modelo.
El caso de cambio excesivo contiene la modificación de correo solicitada y otra no relacionada en el límite de reintentos. Se rechaza la propuesta completa; no apliques automáticamente su parte “buena”. Las aserciones cubren claves escapadas, un cambio de booleano a número dentro de un array, la eliminación de un nulo, el reemplazo del documento completo y una copia sin cambios. Sigue el mismo enfoque de casos negativos al probar SQL generado por IA.
4. Resuelve problemas antes de conectar la escritura
- Se rechaza una ruta padre: sustituir el objeto completo de notificaciones por un escalar cambia
/notifications. El permiso sobre una hoja no autoriza ese reemplazo. - Se rechaza un cambio de array: permite el array completo solo si ese permiso más amplio es intencional; este ejemplo no autoriza índices individuales.
- Se permite no cambiar nada: el control comprueba el alcance, no la finalización de la tarea. Exige por separado que el valor final del correo sea
Falsecuando la tarea requiera desactivarlo. - La entrada es demasiado grande o profunda: recházala antes de comparar. Este ejemplo didáctico recursivo no es una barrera de seguridad para entradas sin límites.
En una integración real, valida el esquema completo del candidato, comprueba la autorización y detén el proceso ante errores de análisis o comparación. Mantén inmutable el candidato comprobado y rechaza originales desactualizados mediante una comprobación de versión dentro de la transacción de escritura. La aprobación debe corresponder al mismo documento que finalmente guardes. Una vista previa de renombrado de archivos sin escritura aplica la misma separación entre propuesta y ejecución.
Conclusión práctica
Un esquema indica qué puede contener un documento; una lista de campos permitidos indica qué puede cambiar esta operación concreta. Comprueba ambos, revisa la diferencia propuesta y mantén la escritura separada del modelo.
Artículos relacionados
Rechaza JSON ambiguo de IA antes de automatizar con Python
Revisa planes de renombrado de IA sin cambiar archivos
Compara versiones de prompts de IA con una prueba de regresión en Python
Comprueba citas textuales de IA con fuentes locales y Python
Comprueba las cifras de resúmenes de IA con una tabla de referencia
Comprueba las horas de calendario de IA antes de crear eventos
Comprueba los marcadores de traducciones de IA con Python
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
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 la clasificación de incidencias con una matriz de confusión
Ú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
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
Compara versiones de prompts de IA con una prueba de regresión en Python