# Truncamiento de resultados de API: evita que los agentes hagan cambios incorrectos

Los agentes no necesitan una herramienta maliciosa para causar un cambio dañino. Basta con que una herramienta devuelva en silencio solo una parte de su respuesta. Dale a un agente un resultado de búsqueda que parezca completo y pídele que elimine lo que la búsqueda no encontró. A menudo producirá un error perfectamente lógico basado en premisas falsas.

La solución no es añadir al prompt del sistema una instrucción más larga para que el agente tenga cuidado. La herramienta debe indicar, de forma estable y legible por máquinas, si completó el trabajo solicitado, qué omitió, por qué lo omitió y cómo puede continuar el cliente. Si la herramienta no puede dar esa información, el agente no debe interpretar la ausencia en el resultado como permiso para hacer un cambio amplio.

## Los datos parciales y los datos vacíos son afirmaciones distintas

Un resultado vacío indica que la herramienta no encontró elementos coincidentes dentro del ámbito que examinó. Un resultado vacío completo indica que examinó todo el ámbito solicitado y no encontró nada. Son afirmaciones distintas, pero la mayoría de los contratos de API las reducen al mismo `[]`.

Esa simplificación provoca una inferencia equivocada concreta:

1. El agente solicita todas las cuentas de servicio sin un propietario actual.
2. La API devuelve un array vacío después de revisar su primera página, detenerse al alcanzar un límite de resultados u omitir registros que el token no puede leer.
3. El agente concluye que todas las cuentas tienen propietario.
4. Cambia un control relacionado, un informe o una tarea de limpieza basándose en esa conclusión.

El agente no tuvo que entender mal el inglés. La herramienta le dio una respuesta cuya forma sugería más de lo que el servidor sabía.

Una herramienta debería distinguir al menos cuatro estados. Una consulta completa puede devolver elementos. Una consulta completa puede no devolver ninguno. Una consulta incompleta puede devolver algunos elementos. Una consulta incompleta puede no devolver ninguno. Considerar importante solo el tercer estado hace que se pase por alto el caso más peligroso: una respuesta vacía que convence al agente de que no existe ningún problema.

Los permisos empeoran la situación. Muchos servicios ocultan deliberadamente los objetos inaccesibles devolviendo una colección vacía o filtrada en lugar de un error de permisos. Ese comportamiento puede ser razonable en una interfaz para personas. Es una prueba inaceptable para una tarea de limpieza autónoma, salvo que la API indique exactamente qué visibilidad tuvo el cliente.

No uses una frase como `Some results may be missing` como contrato. No ofrece al agente una rama fiable que seguir ni al ingeniero una condición que pueda probar. Un campo llamado `complete` con un valor booleano parece aburrido. Esa es precisamente la idea.

## Una respuesta HTTP correcta aún puede ser una respuesta incompleta

Los códigos de estado HTTP describen el intercambio entre cliente y servidor. Por sí solos no demuestran que una búsqueda, un inventario o una exportación cubriera el dominio solicitado.

RFC 9110 define la semántica de los códigos de estado HTTP. Un `200 OK` indica que la solicitud se completó correctamente según la semántica del método. No indica que una búsqueda cubriera todas las páginas, todas las particiones, todos los dominios de permisos o todos los registros antes de que terminara el plazo. Los equipos suelen interpretar `200` como una promesa mayor de la que hace el protocolo.

Considera esta respuesta:

```json
HTTP/1.1 200 OK
Content-Type: application/json

{
  "items": [],
  "next_cursor": null
}
```

Parece definitiva. Pero `next_cursor: null` solo indica que este mecanismo concreto de paginación no tiene otra página. No dice nada sobre un límite de resultados del backend, una tarea de búsqueda caducada, una fuente que falló, registros excluidos por política o una API que limita en silencio el periodo consultable.

Una respuesta se convierte en una prueba utilizable cuando el contrato explica qué cubre `complete`. En una búsqueda de cuentas, podría significar todas las cuentas visibles para la identidad que llama dentro de una instantánea indicada. En una búsqueda de código, podría significar todos los archivos indexados en una revisión concreta, excluyendo de forma explícita los archivos ignorados y los archivos generados que no están indexados. El ámbito debe ser lo bastante concreto para que el cliente decida si coincide con la acción propuesta.

No intentes resolverlo devolviendo un `500` por cada respuesta parcial. Los resultados parciales pueden ser útiles. Un panel puede mostrarlos. Un agente puede resumirlos. Una persona puede inspeccionarlos. El error consiste en presentar un resultado parcial como una respuesta autorizada a una pregunta que requiere integridad.

Usa un error cuando la operación solicitada promete una respuesta atómica o completa y no puede cumplir esa promesa. Usa una respuesta correcta con una indicación explícita de que está incompleta cuando los datos parciales tienen un uso legítimo. El cliente necesita una distinción determinista, no una discusión sobre si `200` parecía demasiado optimista.

## Coloca los metadatos de integridad junto a cada resultado

El contrato de un resultado debe exponer la integridad como datos estructurados, tanto si la lista de elementos está completa como si es corta o está vacía. No obligues a los clientes a deducirla a partir del número de elementos, de una cabecera ausente o de una frase en un campo `message`.

Esta estructura funciona para una búsqueda de colecciones:

```json
{
  "items": [
    {"id": "svc-184", "owner": null}
  ],
  "complete": false,
  "truncated": true,
  "incomplete_reasons": [
    {
      "code": "RESULT_LIMIT_REACHED",
      "message": "The query stopped after the configured result limit.",
      "limit": 1000
    }
  ],
  "next_cursor": "eyJvZmZzZXQiOjEwMDB9",
  "scope": {
    "resource": "service_accounts",
    "visibility": "resources readable by this credential",
    "snapshot": "2025-03-08T14:20:11Z"
  },
  "warnings": []
}
```

Los nombres exactos de los campos importan menos que su significado y su coherencia. `complete` es el campo que guía la decisión. `truncated` describe una vía importante hacia la falta de integridad, pero no debe convertirse en un cajón de sastre. Un filtro de permisos no es truncamiento. Una búsqueda federada que agotó el tiempo no es paginación. Si sobrecargas un único indicador, los clientes pierden el motivo que necesitan para recuperarse de forma segura.

Mantén `warnings` separado de `incomplete_reasons`. Una advertencia puede informar al cliente de que apareció un campo obsoleto, de que se normalizó un valor o de que el orden solicitado volvió al predeterminado. Un motivo de falta de integridad indica que la respuesta no puede respaldar afirmaciones sobre la parte no devuelta del ámbito solicitado. Esa diferencia determina si un agente puede continuar.

Evita también un indicador `has_more` aislado como única señal. Normalmente responde a una pregunta estrecha sobre la paginación. Un agente que ve `has_more: false` puede inferir razonablemente que la colección terminó, aunque un límite del servidor o un fragmento inaccesible haya impedido un análisis completo. `has_more` puede mantenerse, pero no debe cargar con toda la responsabilidad de informar sobre la integridad.

En una lectura de un solo recurso, aplica la misma disciplina. Una respuesta con campos omitidos debe indicar si el servidor los omitió porque el cliente no los solicitó, porque no tiene acceso, porque falló la fuente de datos o porque el valor está realmente ausente. Omitir un campo en JSON es compacto, pero ambiguo.

## La paginación necesita un límite estable, no un tamaño de página mayor

La paginación solo es segura para los agentes cuando la API hace fiable la continuación y explica qué cambios pueden invalidarla. Aumentar el límite de página retrasa el problema, pero no lo elimina.

La paginación por desplazamiento es especialmente propensa a conclusiones equivocadas. Un agente lee los registros del 0 al 99, elimina o crea un objeto y después lee del 100 al 199. Si cambia el orden subyacente, puede saltarse un registro o procesar otro dos veces. Para un informe orientativo quizá sea tolerable. Para un plan de cambios puede ser desastroso.

La paginación por cursor suele ser mejor porque el servidor puede codificar una posición dentro de un conjunto de resultados ordenado. Aun así, necesita un contrato. Indica si el cursor congela una instantánea, cuánto tiempo sigue siendo válido y si cambiar los filtros, el orden o la autorización lo invalida. Si un cursor caduca, no reinicies el análisis en silencio para devolver una respuesta combinada. Devuelve un estado incompleto explícito u obliga al cliente a empezar de nuevo.

Una respuesta útil de una colección proporciona al cliente información suficiente para terminar de forma deliberada:

```json
{
  "items": ["item-001", "item-002"],
  "complete": false,
  "next_cursor": "cD0y",
  "page": {
    "returned": 2,
    "requested_size": 2,
    "ordering": "id ascending",
    "snapshot": "search-7f9c"
  },
  "incomplete_reasons": [
    {"code": "MORE_PAGES_AVAILABLE"}
  ]
}
```

El cliente debe continuar hasta recibir `complete: true`, no simplemente hasta recibir una página corta. Las páginas cortas aparecen por muchos motivos. Algunas API las devuelven porque una partición está temporalmente poco poblada, porque un trabajador interno se detuvo antes de tiempo o porque el servicio limita el tamaño de la respuesta en bytes en lugar de hacerlo por número de objetos.

No le pidas al modelo de lenguaje que recuerde este bucle a partir de una explicación en prosa. Incluye el comportamiento de la paginación en la implementación de la herramienta. Una herramienta de alto nivel como `search_all` puede recopilar las páginas, conservar la instantánea, limitar su propio trabajo e informar de si llegó a un estado terminal. Si alcanza su propio límite, debe devolver `complete: false` e indicar que la causa fue el límite del cliente.

Este último caso se olvida a menudo. Los ingenieros añaden correctamente metadatos a la API, pero después crean un envoltorio para el agente con `max_pages=10` y descartan el hecho de que se detuvo en diez páginas. El envoltorio se ha convertido en la fuente de la falta de integridad. El contrato de la herramienta más externa es responsable de comunicarla.

## Los límites de tiempo, los fragmentos fallidos y los permisos necesitan sus propios motivos

Una búsqueda puede terminar su solicitud HTTP aunque parte de su trabajo no haya terminado. Los servicios distribuidos suelen enviar una consulta a varios índices o inquilinos. Si una fuente agota el tiempo y el servicio devuelve las coincidencias de las demás, el resultado puede ser útil, pero está incompleto.

Representa la causa con un código que permita a un programa tomar una decisión. El texto para personas debe acompañarlo, no sustituirlo. Mantén los códigos pocos, estables y documentados. Por ejemplo:

- `MORE_PAGES_AVAILABLE` significa que el cliente puede solicitar la página siguiente.
- `RESULT_LIMIT_REACHED` significa que el servicio aplicó un límite antes de agotar las coincidencias.
- `TIME_BUDGET_EXCEEDED` significa que la búsqueda se detuvo antes de terminar todo el trabajo previsto.
- `SOURCE_UNAVAILABLE` significa que una fuente identificada no respondió.
- `VISIBILITY_RESTRICTED` significa que la autorización del cliente excluyó parte del dominio solicitado.

No ocultes `VISIBILITY_RESTRICTED` detrás de una respuesta genérica correcta. Los equipos de seguridad a veces prefieren respuestas indistinguibles porque no quieren revelar qué objeto existe. Esa preocupación es legítima. La API puede informar de que los límites de visibilidad impiden completar el inventario sin nombrar los objetos ocultos. No debe permitir que el cliente confunda un inventario parcial con uno exhaustivo.

La misma regla se aplica a los límites de velocidad y las cuotas. Si una API lee la primera parte de una solicitud antes de agotar un presupuesto, informa de los datos devueltos y de la condición relativa al presupuesto. Un reintento podría terminar más tarde, pero es un intento nuevo. El agente no debe combinar dos intentos para afirmar que la respuesta está completa, salvo que la API le proporcione una instantánea estable o la tarea admita cambios durante el proceso.

Un plazo debe ser una entrada además de una salida. Cuando un agente solicita un inventario amplio, permite que establezca un presupuesto de tiempo y reciba la cantidad de trabajo completado. Así el intercambio queda visible. Una búsqueda de reconocimiento de diez segundos puede bastar antes de una revisión humana. Es una prueba débil para eliminar todos los recursos que la búsqueda no vio.

## La ausencia es una prueba débil para los cambios destructivos

Un agente puede usar datos parciales de forma segura para preparar un informe, identificar candidatos o pedir a una persona que inspeccione un objetivo pequeño. No debe usar datos parciales para inferir que un recurso no se usa, no tiene propietario, está duplicado o se puede eliminar.

La diferencia está en la dirección de la afirmación. Encontrar un registro con `owner: null` es una prueba positiva sobre ese registro, sujeta a la actualidad del campo. No encontrar registros sin propietario es una afirmación universal sobre el dominio de búsqueda. Las afirmaciones universales requieren una cobertura completa de un ámbito definido.

Este fallo suele disfrazarse de mejora de eficiencia. Un equipo da a un agente una herramienta llamada `list_inactive_projects` y después le permite archivar todos los proyectos devueltos o, peor aún, todos los proyectos ausentes de una segunda lista. La herramienta tiene un número máximo de resultados. Meses después, una organización grande supera ese número. Nadie cambia el prompt del agente, pero el significado pasa de «actuar sobre el inventario» a «actuar sobre un prefijo arbitrario del inventario».

Diseña las herramientas de acción para que exijan pruebas en lugar de aceptar un relato. Una operación de archivado puede requerir los identificadores seleccionados por un inventario completo previo y un token de instantánea que vincule la selección con la lectura. Si el inventario estaba incompleto, la herramienta rechaza la operación. Así la comprobación de seguridad queda en un lugar donde el modelo no puede esquivarla con explicaciones.

Para las acciones que no pueden usar tokens de instantánea, exige un ámbito explícito y vuelve a comprobar cada objetivo en el momento de ejecutar. Esto no demuestra que la búsqueda original fuera exhaustiva, pero evita que una lista obsoleta autorice mutaciones no relacionadas. Mantén la acción lo bastante acotada para que una persona pueda entender el conjunto de objetivos.

La alternativa popular consiste en decirle al agente: «Nunca elimines nada a menos que estés seguro». Parece sensato, pero falla en la práctica. La certeza es una palabra dentro de un prompt. `complete: false` es una condición que una herramienta puede hacer cumplir.

## Los esquemas de las herramientas deben obligar al agente a enfrentarse a la incertidumbre

Una herramienta MCP o cualquier envoltorio orientado a agentes debería devolver un sobre tipado, no un bloque atractivo de prosa. El modelo puede leer la prosa, pero el software que lo rodea necesita campos que pueda validar, registrar, bloquear y probar.

Un tipo de respuesta práctico podría tener este aspecto:

```json
{
  "status": "partial",
  "data": {
    "repositories": [
      {"id": "repo-a", "default_branch": "main"}
    ]
  },
  "completeness": {
    "complete": false,
    "reasons": ["TIME_BUDGET_EXCEEDED"],
    "continuation": {
      "kind": "retry_with_deadline",
      "minimum_seconds": 30
    }
  },
  "warnings": [
    {
      "code": "STALE_INDEX",
      "message": "Search index may lag the source repository."
    }
  ]
}
```

No uses `status: "success"` para esta respuesta. Eso anima a los clientes sencillos a descartar los metadatos. `partial` informa al cliente de que recibió datos utilizables con una limitación. Si tu protocolo debe usar un único estado correcto, haz que `complete` sea obligatorio y exige a los clientes capaces de ejecutar acciones que lo revisen antes de mutar nada.

El campo de continuación debe describir una vía de recuperación real. `next_cursor` sirve para solicitar otra página. `retry_after` encaja con un límite de velocidad. `narrow_query` puede servir para un límite del servidor. No proporciones una continuación que se limite a repetir la misma consulta esperando que el universo se comporte de otra manera.

Las instrucciones para el agente deben establecer un conjunto pequeño y estricto de reglas:

- El agente puede citar una colección vacía como prueba de ausencia solo cuando `complete` sea verdadero.
- El agente puede usar una respuesta parcial para proponer una investigación de solo lectura y con un alcance limitado.
- El agente debe mostrar `incomplete_reasons` antes de solicitar aprobación para cualquier acción que dependa del resultado.
- El agente no debe inventar un token de continuación ni afirmar que un reintento tuvo éxito sin su resultado.

Estas reglas son breves porque los datos contienen los detalles. Un prompt no puede recuperar información que una herramienta decidió no comunicar.

## Las advertencias necesitan un responsable y una vía de caducidad

Las advertencias se convierten en ruido de fondo cuando cada respuesta emite una precaución vaga. Mantenlas específicas, atribuibles y prácticas. Una advertencia que nunca cambia la siguiente decisión del cliente debería convertirse normalmente en documentación o desaparecer.

Por ejemplo, `STALE_INDEX` debería identificar la fuente indexada y, cuando sea posible, su revisión observada o la hora de actualización. El agente puede decidir entonces inspeccionar la fuente de referencia antes de cambiar el código. `PARTIAL_FIELD_SET` debería indicar qué campos omitió el servidor y si el cliente puede solicitarlos. `DEFAULT_SCOPE_APPLIED` debería indicar el ámbito que eligió el servidor, porque los valores predeterminados son una fuente frecuente de acciones amplias accidentales.

No conviertas las advertencias en bloqueos por accidente. El cliente necesita una regla de gravedad clara. Los metadatos de integridad determinan si el resultado respalda una afirmación sobre todo el ámbito. Las advertencias determinan la confianza, la actualidad o la interpretación. Una herramienta puede devolver `complete: true` con una advertencia de antigüedad. Ese resultado puede enumerar todos los elementos de un índice y seguir siendo inadecuado para un cambio que requiera el estado actual.

Asigna códigos estables a las advertencias y prueba los consumidores con ellos. Evita pruebas que solo comprueben un mensaje amable. Los mensajes cambian cuando alguien mejora la redacción; la regla de decisión no debería cambiar.

Decide también quién se hace cargo de una advertencia después de publicarla. Si un equipo de operaciones ve la misma advertencia en cada llamada durante seis meses, dejará de leerla. Repara la condición subyacente, conviértela en un fallo grave cuando corresponda o elimínala si no afecta a ninguna decisión. Las luces amarillas permanentes enseñan a las personas y a los agentes a ignorar las luces amarillas.

## Las pruebas deben ejercitar la respuesta vacía peligrosa

La mayoría de las suites de pruebas cubren una página normal de resultados y un error del servidor. Omiten la respuesta que provoca la inferencia más peligrosa: `items: []` junto con una falta de integridad.

Escribe pruebas de contrato para cada código de motivo. Comprueba que la API devuelva los metadatos con listas llenas y vacías, que los SDK los conserven y que el envoltorio del agente no los convierta en texto plano. Una regresión en cualquiera de las capas puede transformar una respuesta honesta del servidor en un resultado engañoso de la herramienta.

Usa casos como estos en un accesorio de prueba:

```json
{
  "case": "empty first page with more pages",
  "response": {
    "items": [],
    "complete": false,
    "truncated": false,
    "incomplete_reasons": ["MORE_PAGES_AVAILABLE"],
    "next_cursor": "cursor-2"
  },
  "expected_agent_decision": "continue_search"
}
```

Después prueba una solicitud de mutación tras ese caso. La decisión esperada debe ser `refuse_or_request_review`, no `perform_cleanup`. Haz visible la política en el nombre de la prueba. De lo contrario, quienes mantengan el sistema en el futuro podrían considerar el control un caso extremo demasiado cauteloso y eliminarlo para que una demostración de automatización resulte más fluida.

Prueba también la paginación durante una mutación. Inserta, elimina y reordena registros entre páginas. Haz caducar un cursor. Provoca el fallo de un fragmento después de que otro haya devuelto resultados. Retira un permiso a mitad de un recorrido. La herramienta debe conservar una instantánea documentada o informar de que no puede afirmar que la respuesta esté completa. Una prueba que solo use una base de datos falsa y estática no puede detectar las mentiras que aparecen en producción.

Las pruebas de propiedades también ayudan. Genera colecciones mayores que todos los límites configurados, varía los tamaños de página y comprueba un invariante: un cliente solo puede marcar una colección como completa después de haber tenido en cuenta cada elemento de la instantánea declarada. La prueba no necesita un modelo de lenguaje. Es una comprobación normal de la corrección de una interfaz.

## La aprobación humana debe mostrar las pruebas que faltan

El control humano solo funciona cuando la aprobación muestra la decisión que se pide tomar. «Permitir la acción del agente» no es una aprobación. Es una petición para aceptar una cadena opaca de suposiciones.

Cuando una herramienta informa de datos incompletos, muestra la acción propuesta, el ámbito objetivo, el motivo por el que las pruebas están incompletas y la opción de recuperación. Un aviso útil explica que el inventario agotó el tiempo después de devolver 842 recursos y pregunta si se desea reintentar con un plazo mayor, limitar la acción a los identificadores devueltos o abandonar el cambio. Así la persona que revisa puede tomar una decisión real sobre el intercambio.

Sallyport mantiene las credenciales fuera del proceso del agente cuando este realiza acciones HTTP y SSH, y sus registros de actividad pueden mostrar las llamadas resultantes. Ese aislamiento y esa trazabilidad son útiles cuando una persona debe reconstruir una decisión equivocada. No convierten una respuesta ambigua de la API en una prueba, así que la respuesta de la herramienta aún debe incluir su estado de integridad.

Evita la fatiga de aprobación reservándola para las ambigüedades importantes. Una herramienta debe gestionar sin interrumpir repetidamente a una persona la continuación rutinaria, como obtener una página siguiente documentada. Debe detenerse al alcanzar un límite de política: una instantánea caducada, una visibilidad restringida, una acción basada en la ausencia o una mutación propuesta fuera de las pruebas que recopiló.

La primera tarea de ingeniería es pequeña: encuentra cada envoltorio de API que pueda devolver una lista, un agregado o un resultado de búsqueda y añade un estado `complete` explícito a su respuesta más externa. Empieza por los resultados vacíos y las búsquedas con límite. Ahí es donde los agentes seguros de sí mismos fabrican las respuestas equivocadas más convincentes.
