# Minimización de datos en agentes de IA para llamadas de herramientas más seguras

Un agente de IA no debería recibir el registro de un cliente solo porque quizá necesite un dato concreto más adelante. Cada llamada de herramienta necesita un contrato más pequeño: la acción, los campos mínimos para ejecutarla y una salida que indique al agente qué ocurrió sin entregarle otra cantidad de datos del cliente.

Los equipos suelen filtrar datos en el punto de unión entre un agente capaz y una API interna cómoda. Le dan al agente una consulta general de clientes, devuelven toda la respuesta y llaman a ese resultado contexto útil. Esa decisión convierte cada prompt posterior, transcripción, reintento, traza de depuración y resultado de herramienta en un lugar donde el registro puede propagarse.

La solución no es un prompt ingenioso para ocultar datos. Es una disciplina de diseño: definir cada acción antes de exponerla, imponer una solicitud limitada en el límite de ejecución y hacer que las respuestas sin procesar del sistema de origen no estén disponibles para el agente.

## El permiso de una herramienta no da derecho a todo el registro

El permiso para llamar a una API y el permiso para ver todos los campos que esa API puede devolver son decisiones distintas. Los equipos las mezclan porque una cuenta de servicio suele poder leer un objeto amplio, mientras que una herramienta del agente necesita solo una parte pequeña.

Imagina un agente que debe decidir si enviar un recordatorio de pago. El servicio de entrega puede necesitar una dirección del destinatario, un identificador de plantilla y una referencia de factura. El agente quizá solo necesite `eligible: true`, un nombre seguro para mostrar y una referencia de acción. No necesita el historial de pagos, las notas del cliente, la información fiscal ni la dirección que el remitente utiliza internamente.

Una herramienta general `get_customer` crea una tentación peligrosa. Cuando existe, los prompts empiezan a usarla para trabajos que no tienen relación porque parece más barato que crear una herramienta de acción adecuada. Después la gente intenta compensarlo con instrucciones como «no expongas campos sensibles». Las instrucciones no eliminan campos de una respuesta JSON.

Mantén separadas estas tres preguntas:

- ¿Puede este proceso de agente iniciar la acción?
- ¿Qué campos debe recibir el ejecutor para realizarla?
- ¿Qué datos debe recibir el agente cuando termine?

Cada pregunta debe producir su propia restricción. El control de acceso responde a la primera. La validación de la solicitud responde a la segunda. La configuración de la respuesta responde a la tercera. Un único token de API para todo y un endpoint JSON flexible no responden bien a ninguna.

Esta distinción importa especialmente cuando un agente usa una herramienta repetidamente. Una consulta amplia hecha una sola vez puede parecer tolerable en una demostración. En una ejecución real, el agente puede repetirla después de un reintento, citar su salida en una solicitud posterior o pasarla a otra herramienta. El primer campo innecesario se convierte en muchas copias innecesarias.

## Crea el mapa de datos alrededor de una acción, no de una tabla de base de datos

Un mapa de datos útil empieza con un verbo y un efecto externo. «Leer cliente» no es una acción adecuada para este propósito. «Confirmar si una factura está vencida» y «crear una etiqueta de envío» sí lo son, porque cada una tiene un destinatario, un propósito y un resultado esperado concretos.

Para cada herramienta propuesta, escribe un registro breve antes de crear el esquema:

| Elemento | Ejemplo: enviar recordatorio de pago |
|---|---|
| Iniciador | Un proceso de agente de soporte de facturación |
| Efecto | Envía una plantilla aprobada a un destinatario elegible |
| Entrada mínima | `invoice_ref`, `template_code` |
| Consulta confiable | Dirección del destinatario, preferencia de idioma y reglas de elegibilidad |
| Resultado visible para el agente | `sent`, `suppressed` o `needs_human_review` |
| Entrada prohibida | Dirección de correo, historial de pagos, notas de cuenta y objeto completo del cliente |
| Salida prohibida | Dirección de entrega, respuesta sin procesar del proveedor y datos de pago |

La columna de consulta confiable hace el trabajo difícil. Identifica la información que el ejecutor puede necesitar, pero el agente no. Mueve esa consulta detrás del límite. El agente envía una referencia de factura y un servicio bajo tu control resuelve el destinatario solo después de comprobar la acción.

No conviertas `invoice_ref` en una copia disimulada de una dirección de correo ni en un identificador compuesto que contenga el nombre del cliente. Las referencias opacas reducen la exposición incidental, pero no vuelven privado un sistema automáticamente. Si una referencia permite consultar el objeto de un cliente, sigue necesitando autorización, caducidad y una restricción de audiencia.

El Reglamento General de Protección de Datos de la Unión Europea expresa claramente el principio relevante en el artículo 5(1)(c): los datos personales deben ser adecuados, pertinentes y limitados a lo necesario para su finalidad. La frase «para su finalidad» es donde los equipos de ingeniería suelen descuidarse. La finalidad no es «ayudar al agente a completar su trabajo». Es la operación concreta en el límite, como enviar un recordatorio o abrir un caso de soporte.

Un mapa también muestra los campos que nunca deberían cruzar el límite en ninguna dirección. Las notas de texto libre merecen su propia fila. Suelen contener correos pegados, documentos de identidad, credenciales, información de salud y la queja sin filtrar de un cliente. Un campo genérico `notes` no tiene un significado delimitado, así que no tiene lugar en una solicitud rutinaria de herramienta.

## Los esquemas estrictos detienen los campos de conveniencia antes de la ejecución

Un esquema debe rechazar los campos que la acción no necesita. Ignorar silenciosamente las propiedades adicionales parece una opción flexible, pero oculta una filtración durante las pruebas y hace creer a los desarrolladores que el campo llegó a su destino.

Supón que un agente necesita solicitar una revisión de reembolso. Este contrato de solicitud solo permite una referencia de caso y un motivo seleccionado. Rechaza importes, direcciones y notas arbitrarias controlados por el cliente.

```json
{
  "name": "request_refund_review",
  "description": "Create a review task for an existing support case.",
  "input_schema": {
    "type": "object",
    "additionalProperties": false,
    "required": ["case_ref", "reason_code"],
    "properties": {
      "case_ref": {
        "type": "string",
        "pattern": "^case_[A-Za-z0-9]{16}$"
      },
      "reason_code": {
        "type": "string",
        "enum": ["duplicate_charge", "service_not_received", "other"]
      }
    }
  }
}
```

Una solicitud con `customer_email`, `shipping_address`, `amount` o `conversation_text` debe fallar durante la validación con un resultado explícito como este:

```json
{
  "error": "invalid_request",
  "message": "Unexpected property: customer_email"
}
```

Ese error indica al agente que debe usar el contrato y avisa al desarrollador de que un campo no deseado llegó al límite. No repitas el valor rechazado en el error. Los gestores de errores han causado más filtraciones que muchas rutas de producción porque serializan toda la solicitud fallida para depurarla.

La validación del esquema por sí sola no protege los campos propiedad del servidor. Una solicitud puede incluir un `case_ref` con un formato válido que pertenezca a otra cuenta, o un `reason_code` válido junto con una referencia de acción emitida para otro agente. El ejecutor debe vincular la referencia con quien la emitió, la acción prevista y su periodo de validez. Piensa en una referencia de acción como un recibo con derecho a canje, no como una clave primaria pública de base de datos.

Usa un creador de solicitudes cuando el agente parta de un contexto no confiable o demasiado amplio. Debe extraer los campos permitidos y crear un objeto nuevo. No tomes un objeto grande para eliminar unas pocas claves peligrosas conocidas. La redacción mediante listas de bloqueo falla cuando aparece un campo nuevo, cambia la forma de un objeto anidado o un desarrollador llama a la herramienta con un alias de nombre diferente.

```python
ALLOWED_REASONS = {"duplicate_charge", "service_not_received", "other"}

def build_refund_request(case_ref, reason_code):
    if not isinstance(case_ref, str) or not case_ref.startswith("case_"):
        raise ValueError("invalid case_ref")
    if reason_code not in ALLOWED_REASONS:
        raise ValueError("invalid reason_code")
    return {"case_ref": case_ref, "reason_code": reason_code}
```

Esta pequeña función evita un fallo común: pasar un diccionario `case` completo a una biblioteca cliente porque la biblioteca acepta argumentos de palabra clave arbitrarios. El objeto de retorno explícito es aburrido. En un límite de datos de clientes, aburrido es bueno.

## La salida de una herramienta necesita su propio contrato

La salida sin procesar de una herramienta forma parte del contexto del agente, aunque la herramienta nunca reciba datos de clientes como entrada. Trata cada respuesta como material que el agente puede citar, conservar, transformar o enviar a otro sistema.

La respuesta de un proveedor después de crear un envío puede incluir la dirección completa del destinatario, su número de teléfono, información de la cuenta del transportista, datos de la etiqueta, detalles de enrutamiento y campos internos de diagnóstico. El agente normalmente solo necesita una referencia del envío y saber si puede decirle al cliente que el pedido está en camino.

Define por separado la respuesta para el agente y el resultado para el servicio:

```json
{
  "status": "created",
  "shipment_ref": "ship_Q7J4K2P8",
  "customer_message_allowed": true
}
```

El servicio de ejecución puede guardar o pasar la respuesta detallada del transportista allí donde la necesite el personal operativo. No debería devolverla porque hacerlo resulte cómodo para depurar. Si un operador necesita un comprobante, dale una interfaz protegida para obtenerlo. No uses la transcripción del agente como base de datos de resolución de problemas.

Las respuestas de error necesitan el mismo tratamiento. Una API externa puede devolver la dirección rechazada, un número de cuenta o una parte citada de la solicitud. Conviértela en un código de error limitado para el agente, como `recipient_unavailable`, `reference_invalid` o `provider_retryable`. Guarda los detalles de diagnóstico protegidos en un sistema destinado a operadores.

La especificación del Model Context Protocol define las herramientas como funciones invocables con entradas y resultados estructurados. Esa estructura ofrece a los desarrolladores un lugar claro para imponer tipos de respuesta. Una herramienta que devuelve un volcado de texto o JSON arbitrario pierde esa ventaja. Un esquema de respuesta limitado también facilita las pruebas del comportamiento del agente, porque la siguiente decisión solo puede depender de campos conocidos.

Evita devolver un campo llamado `details` si no puedes indicar su estructura exacta y sus reglas de sensibilidad. Una salida ambigua se vuelve permanente. Alguien pondrá allí el resultado sin procesar durante un incidente y después olvidará eliminarlo.

## La redacción, los seudónimos y el secreto son controles distintos

Sustituir un nombre por un token no significa que los datos se hayan vuelto seguros para circular. Un token estable que pueda relacionarse con una base de datos de clientes sigue siendo un dato personal en la mayoría de los modelos de amenaza prácticos. Una referencia de acción de un solo uso, limitada a un llamador y a una vida útil breve, tiene un modo de fallo mucho más reducido.

La redacción elimina valores conocidos de un objeto. Ayuda cuando un sistema interno debe mostrar un registro a un operador, pero es frágil como límite principal para los agentes. Los nombres de los campos cambian. El contenido se desplaza a estructuras anidadas. El texto libre contiene información que ninguna lista fija de redacción puede encontrar de forma fiable.

La seudonimización sustituye un identificador por otro. Reduce la exposición cuando el receptor no puede resolver la correspondencia. Falla cuando el mismo agente puede llamar a una herramienta de consulta amplia con ese identificador, cuando el token aparece en herramientas sin relación o cuando el propio valor transmite significado. `acme-health-urgent-001` no es opaco solo porque no tenga el formato de un correo electrónico.

El secreto se consigue manteniendo los datos que permiten resolver la referencia y las credenciales en el lado confiable del límite. El agente envía una instrucción limitada y un servicio resuelve la información protegida solo para esa acción permitida. Esta distinción evita el error habitual de pasar un objeto de cliente «depurado» a un agente y dar por terminado el trabajo.

También debes separar la minimización de datos de la autorización. Un agente correctamente autorizado puede recibir demasiados datos. Por el contrario, un llamador no autorizado puede recibir muy pocos, pero esos pocos aún podrían causar daño. Impón ambos controles y pruébalos por separado.

## Las credenciales amplias hacen menos creíbles los esquemas limitados

Un esquema de solicitud perfecto no puede compensar que un agente tenga una credencial capaz de llamar directamente a la API subyacente. Si el agente puede leer el secreto o utilizarlo desde su entorno de ejecución, puede saltarse la herramienta cuidadosamente diseñada y pedir al proveedor una respuesta más completa.

Coloca las credenciales donde se ejecuta la acción, no donde el modelo razona. El ejecutor inyecta el encabezado de autorización o la identidad SSH después de validar la solicitud limitada. El agente ve el resultado, no el secreto, un marcador ni una copia en una variable de entorno.

OAuth 2.0, descrito en RFC 6749, utiliza ámbitos para limitar el acceso concedido a un cliente. El ámbito es útil, pero muchas implementaciones lo tratan como un pase amplio para todo un departamento. Un ámbito `customers.read` todavía puede permitir consultar el registro completo. Combina el ámbito de la credencial con endpoints específicos para la acción y filtros de respuesta. De lo contrario, el ámbito solo limita qué colección grande puede buscar el agente.

Mantén separada la autoridad según el efecto. Un agente que puede crear un borrador no debería tener también autoridad para enviarlo. Un agente que puede solicitar una revisión de reembolso no debería emitir el reembolso. Esta separación reduce la presión de añadir una credencial administrativa general solo para poner en marcha un flujo.

Para los equipos de macOS que usan Sallyport, la aplicación guarda las credenciales de API y SSH en su bóveda cifrada y ejecuta la acción HTTP o SSH sin revelar la credencial al agente. Esto solo ayuda si la llamada sigue siendo limitada; un token protegido también puede autorizar una solicitud demasiado amplia.

## Un flujo de soporte muestra cómo se propagan los datos

Una filtración habitual empieza con una petición razonable: permitir que un agente de soporte prepare una respuesta sobre una entrega fallida. La primera implementación expone `get_order(order_id)`, que devuelve el pedido, el perfil del cliente, la dirección de entrega, el estado del pago, el historial de soporte y los eventos del transportista. El agente necesita el evento del transportista y permiso para enviar una actualización aprobada.

El agente llama a la consulta y recibe el registro completo. Después pasa algunos detalles a una herramienta para redactar mensajes. El prompt de redacción contiene ahora la dirección y el historial aunque ninguno de los dos influya en el mensaje. Una llamada fallida envía todo el prompt a un registro de errores. Un ingeniero copia ese error en una incidencia para diagnosticarlo. La respuesta amplia original se ha convertido en cuatro problemas de conservación distintos.

Diseña el flujo de otra manera. Dale al agente una acción `assess_delivery_update` que acepte `order_ref`. El servicio de ejecución verifica la autoridad del llamador, obtiene internamente el pedido, lee el estado del transportista, aplica la regla de contacto y devuelve solo esto:

```json
{
  "status": "contact_allowed",
  "event_code": "delivery_delayed",
  "approved_template": "delivery_delay_notice",
  "order_ref": "ord_9VJ3R6M1"
}
```

El agente puede decidir si la situación justifica el mensaje aprobado. Una acción separada `send_approved_delivery_update` acepta `order_ref` y `approved_template`. No acepta una dirección de correo ni un cuerpo de texto libre. El servicio resuelve el destinatario y genera la plantilla después de verificar el consentimiento y el estado del pedido.

Este diseño parece más restrictivo porque lo es. Esa restricción es el objetivo. El agente no puede reutilizar sin más un perfil de cliente para otra tarea, y una herramienta posterior no puede recibir los datos por accidente.

No respondas a esto con el argumento genérico de que «la herramienta necesita flexibilidad». La flexibilidad pertenece al código confiable de la aplicación, donde puedes probarla, revisarla y auditar el tratamiento de datos. Dar flexibilidad a un agente mediante objetos amplios de solicitud y respuesta traslada el coste a cada prompt y a cada sistema posterior.

## Las pantallas de aprobación no pueden inspeccionar todos los campos ocultos

La aprobación protege contra acciones no autorizadas, pero no puede controlar de forma fiable las cargas demasiado grandes. Una persona ve un resumen breve, decide bajo presión y aprueba la acción. Si el servicio oculta una dirección o un historial innecesarios dentro de una solicitud, la aprobación no hizo nada para minimizar la exposición.

La aprobación también crea un incentivo negativo cuando sustituye al diseño de las herramientas. Los desarrolladores siguen añadiendo campos porque «el usuario aprueba cada llamada». Pronto la tarjeta de aprobación contiene demasiados detalles para leer o muy pocos para decidir. La gente aprueba acciones repetidas que parecen inofensivas sin notar que una solicitud incluye un campo nuevo.

Muestra la acción y sus parámetros limitados en la interfaz de aprobación, pero impón la lista de campos permitidos antes de que aparezca esa interfaz. La persona que aprueba debe decidir si autoriza `send approved delivery update for ord_9VJ3R6M1`, no inspeccionar manualmente un registro de cliente serializado.

Usa la aprobación por llamada para los efectos que merecen atención humana en cada ejecución. No la uses como detector de datos personales. La persona que ve la aprobación no tiene ni el tiempo ni el contexto necesarios para decidir si cada campo anidado era imprescindible.

Una buena prueba de revisión es sencilla: elimina al aprobador de la historia. ¿El contrato de la herramienta seguiría impidiendo que salieran datos innecesarios del servicio confiable? Si la respuesta es no, el límite hace demasiado poco.

## Los registros de auditoría deben demostrar las acciones sin convertirse en otro almacén de datos

Necesitas pruebas cuando un agente actúa en nombre de un cliente. No necesitas un archivo permanente, legible por el agente, con todas las cargas completas para obtener esas pruebas.

Registra el nombre de la acción, el proceso o la sesión que la inició, la marca de tiempo, el resultado, la decisión de autorización y una referencia de correlación. Si necesitas pruebas de integridad de la carga, registra un resumen criptográfico de una representación canónica y protegida, no la representación en sí. Guarda los nombres de los campos aceptados, no sus valores sensibles.

Por ejemplo, un evento de auditoría podría conservar esta forma:

```json
{
  "action": "send_approved_delivery_update",
  "session_ref": "sess_4KH8N2",
  "order_ref_digest": "sha256:8e4c...",
  "accepted_fields": ["order_ref", "approved_template"],
  "outcome": "sent",
  "authorized_by": "per_call"
}
```

Un resumen criptográfico no elimina mágicamente el riesgo para la privacidad. Si la entrada procede de un conjunto pequeño y conocido, un atacante puede adivinar valores y comparar hashes. Usa una referencia interna protegida cuando los operadores necesiten recuperar detalles y limita el acceso al sistema que contiene el registro original. Nunca trates un hash simple de una dirección de correo como si fuera anónimo.

Mantén los diagnósticos operativos separados del resultado de la herramienta que recibe el agente. El personal de soporte puede necesitar acceso protegido al cuerpo de un error externo durante un periodo breve. El agente no. Esta separación también facilita la eliminación y la conservación, porque los registros sin procesar no se acumulan en cada diario.

Sallyport proyecta las sesiones de agentes y las llamadas individuales desde un registro de auditoría cifrado, encadenado mediante hashes y protegido contra escritura; su comando `sp audit verify` puede verificar la cadena sin conexión y sin una clave de bóveda. La verificación de integridad responde si se modificó un evento registrado. El esquema del evento sigue determinando si ese registro contiene demasiados datos del cliente desde el principio.

## Prueba el límite compartiendo datos de más de forma deliberada

Una revisión de privacidad que solo prueba llamadas válidas y habituales no detecta el comportamiento que causa la mayoría de las exposiciones accidentales. Comprueba qué ocurre cuando un agente envía un objeto completo, cuando un servicio externo devuelve un campo inesperado y cuando se produce una excepción a mitad de una solicitud.

Usa un accesorio de prueba que contenga valores sensibles falsos fáciles de reconocer y comprueba que no crucen el límite del agente. Debe incluir campos anidados y texto libre, porque los ejemplos planos permiten que el código de redacción supere la prueba con demasiada facilidad.

```json
{
  "case_ref": "case_Ab92Kx71LmQ4Rt8P",
  "reason_code": "duplicate_charge",
  "customer": {
    "email": "test.person@example.invalid",
    "address": "17 Example Lane",
    "payment_note": "card ending 4242"
  },
  "conversation_text": "Customer says their medical appointment depends on delivery."
}
```

El resultado esperado es un fallo de validación que solo mencione la propiedad inesperada. Después inspecciona cuatro lugares: la respuesta visible para el agente, los registros de la aplicación, los registros de seguimiento de errores y los eventos de auditoría. Los desarrolladores suelen validar la solicitud, pero olvidan que su middleware de excepciones registró el cuerpo original.

Añade también pruebas del contrato de salida. Simula una respuesta externa que contenga un objeto completo del cliente y comprueba que la herramienta emita solo los campos documentados. Hazlo cada vez que cambie el cliente del proveedor. Una actualización del SDK puede añadir campos de respuesta sin que nadie toque el prompt del agente.

Por último, ejecuta una prueba de transcripción. Dale al agente una tarea normal, captura todas las entradas y resultados de herramientas que recibe y busca los valores del accesorio de prueba. Esta prueba detecta interpolaciones accidentales en prompts y textos de depuración que las pruebas de esquema pueden pasar por alto.

La primera acción que suele necesitar un rediseño es la herramienta de consulta amplia. Sustitúyela por una acción que produzca un efecto externo real o devuelva una decisión limitada. Si el nuevo contrato parece demasiado estrecho para resultar cómodo, a menudo es una señal de que el sistema dependía del agente para transportar datos que nunca necesitó.
