Una herramienta devuelve null. El agente responde «el valor es 0». Una consulta falla y la respuesta final incluye una cifra que no apareció en la salida. El problema no es solo que falle la herramienta: es que el agente continúe como si hubiera recibido evidencia suficiente.
Tool Evidence Guard es una biblioteca y CLI local para comprobar resultados de herramientas y afirmaciones escalares estructuradas antes de utilizarlos en una respuesta. Está escrita en Python, requiere Python 3.10 o superior y no tiene dependencias de ejecución externas.
La distinción central: OK significa que los datos cumplen un contrato. No significa que sean verdaderos.
Un contrato anterior a la respuesta
El sistema que ejecuta las herramientas define previamente qué necesita obtener: un campo concreto, un tipo, un rango o un conjunto de valores admitidos. Después captura el resultado y entrega al verificador tres elementos:
-
contract: requisitos establecidos antes de ejecutar la herramienta. -
result: salida capturada por el adaptador de confianza. -
claims: afirmaciones estructuradas que se quieren respaldar con esa salida.
Por ejemplo, si se necesita un entero no negativo en /data/value, recibir null no satisface el contrato. Tampoco autoriza a responder con un número que el resultado no contiene.
Las afirmaciones se comparan por igualdad escalar exacta, respetando el tipo. 0 y false son valores válidos, no ausencias; tampoco se tratan como intercambiables.
El contrato y la captura deben proceder de una capa de ejecución de confianza, no del modelo que redacta la respuesta. Si el modelo puede inventar ambos, un documento coherente pero falso puede pasar la validación.
Qué comprueba
La versión 0.1.0 verifica campos mediante rutas JSON Pointer bajo /data/, con tipos integer, number, string y boolean. Permite restricciones minimum, maximum y enum.
Rechaza errores declarados, campos obligatorios ausentes, tipos incorrectos y valores no utilizables conforme a sus reglas. Reconoce determinados marcadores de redacción, como [REDACTED], y rechaza las señales explícitas de truncamiento, corrupción, obsolescencia o redacción cuando están activadas.
La frescura es opcional: al configurar max_age_seconds, se exige observed_at con zona horaria y se rechazan fechas futuras. Ese timestamp debe representar la observación original; copiar hoy un dato antiguo no lo convierte en reciente.
La CLI también limita el tamaño de entrada y rechaza JSON con claves duplicadas o valores no finitos. El informe devuelve códigos de motivo, sin reproducir los valores ni los mensajes de error originales.
No es un validador JSON Schema completo ni un analizador de lenguaje natural.
Dos ejemplos reproducibles
La instalación se hace desde el repositorio. Estos comandos fijan el commit usado para las comprobaciones de este artículo:
git clone https://github.com/amurlaniakea/tool-evidence-guard.git
cd tool-evidence-guard
git checkout c7d16c3a6095c9123261b5048cf29617e64dbae0
python3 -m venv .venv
.venv/bin/python -m pip install -e .
.venv/bin/tool-evidence-guard examples/valid.json
Enter fullscreen mode Exit fullscreen mode
El primer ejemplo contiene el entero 42 y una afirmación con ese mismo valor. La ejecución devolvió:
{"retrieval_status": "OK", "reasons": [], "supported_claims": 1}
Enter fullscreen mode Exit fullscreen mode
El segundo contiene null, pero la afirmación sigue indicando 42:
.venv/bin/tool-evidence-guard examples/unusable.json
Enter fullscreen mode Exit fullscreen mode
Salida observada:
{"retrieval_status": "FAILED", "reasons": ["TYPE_MISMATCH", "CLAIM_MISMATCH"], "supported_claims": 0}
Enter fullscreen mode Exit fullscreen mode
Ambos archivos son fixtures sintéticos de demostración. No son un benchmark sobre agentes ni resultados de un servicio externo.
Los códigos de salida permiten integrar la comprobación en un proceso: 0 indica conformidad; 1, rechazo; 2, una entrada ilegible o inválida. El segundo ejemplo termina deliberadamente con código 1.
El repositorio incluye además una demostración local con lectura real de archivo, una afirmación incorrecta controlada y un archivo inexistente:
.venv/bin/python examples/local_demo.py
Enter fullscreen mode Exit fullscreen mode
En la ejecución verificada, la lectura válida produjo OK; la afirmación incorrecta produjo CLAIM_MISMATCH; el archivo inexistente produjo TOOL_ERROR y MISSING_FIELD.
Dónde integrarlo y dónde no basta
La biblioteca puede utilizarse entre la captura de una herramienta y el uso de sus datos. La aplicación que la integra debe decidir qué hacer ante un rechazo: detener ese paso, solicitar otra fuente o informar de que falta evidencia. La biblioteca no instala por sí sola esa política en un agente.
Para afirmar «la operación terminó», no basta con exigir que el envío haya devuelto status: ok. El contrato debe pedir un estado final y evidencia de una lectura posterior que compruebe el resultado esperado.
También hay límites deliberados:
- No verifica prosa libre, deducciones ni cálculos derivados.
- No autentica a la fuente ni demuestra que una operación se ejecutó realmente.
- Los identificadores de llamada correlacionan resultados; no son firmas.
- Una cadena corrupta sin una señal reconocible puede pasar.
- Un contrato insuficiente puede aceptar datos que no resuelven la tarea.
- Sin afirmaciones comprobadas,
supported_claims: 0no respalda ninguna afirmación, aunque los campos cumplan el contrato.
Actualmente es una CLI y una biblioteca, no un plugin automático de Hermes o Tars. Tampoco presento una reducción porcentual de respuestas inventadas: los tests del proyecto no miden ese efecto en modelos.
Verificación y mantenimiento
Sobre el commit indicado se volvió a ejecutar la suite:
.venv/bin/python -m unittest discover -s tests -q
Enter fullscreen mode Exit fullscreen mode
Resultado observado:
Ran 52 tests in 0.452s
OK
Enter fullscreen mode Exit fullscreen mode
Son 52 pruebas del proyecto, no una garantía universal de seguridad. El run de GitHub Actions correspondiente al merge del PR #2 también figura como completado con éxito.
El PR #1 consolidó el texto de licencia en LICENSE, completó cabeceras SPDX, migró los metadatos a PEP 639 y corrigió referencias de documentación. Fue un cambio de documentación y empaquetado, no una ampliación del verificador. La revisión independiente aportada para ese PR comprobó el diff y volvió a ejecutar los tests en un clon limpio.
El PR #2 resolvió los 15 avisos de Ruff que habían quedado fuera del alcance del PR #1. La comprobación posterior devolvió All checks passed!. En el núcleo solo cambió el tipo de excepción para timestamps no textuales y su captura correspondiente: raise TypeError junto con except (TypeError, ValueError, OverflowError), preservando el informe INVALID_TIMESTAMP en vez de propagar una excepción.
Se añadió una prueba de regresión para cinco valores no textuales. La auditoría independiente comunicada para el PR reprodujo el caso adversarial en un clon limpio: retirar TypeError del except provoca una excepción sin capturar; restaurarlo devuelve la suite a verde. No se corrigió un crash presente en la versión anterior: se evitó introducirlo al atender el aviso de estilo. Ruff limpio y 52 pruebas en verde describen estas comprobaciones, no una garantía de ausencia de defectos.
Código y documentación
Tool Evidence Guard no convierte una salida en verdad. Permite exigir algo más acotado y comprobable: que una afirmación estructurada coincida con datos capturados que satisfacen requisitos definidos de antemano.
- Repositorio
- Referencia completa en español, fijada al commit del artículo
- Licencia AGPL-3.0-or-later
¿Qué comprobación exigirías a una herramienta antes de permitir que un agente utilice su resultado?
Pedro Sordo Martínez — [email protected]
Licencia del proyecto: AGPL-3.0-or-later.