El registro de 300 000 caracteres
Tu agente llama a read_logs y la herramienta, obediente, devuelve el fichero entero. Son 80 000 tokens. El modelo los lee, pierde el hilo de la tarea y empieza a citar líneas que no vienen al caso. La factura sube, la latencia también, y la respuesta es peor que si hubiera visto diez líneas bien elegidas.
Es un fallo muy común y casi siempre invisible: nadie lo ve en las pruebas porque los datos de prueba son pequeños. En producción, la herramienta decide cuánto contexto consume el agente, no el modelo. Conviene que esa decisión sea tuya y no un accidente.
Tres estrategias, tres costes
No hay una respuesta única. Hay tres, y cada una falla de una manera distinta.
| Estrategia | Qué hace | Cuándo encaja | Cómo falla |
|---|---|---|---|
| Truncar | Corta en un límite fijo | Salidas con la información al principio o al final | Pierde justo la parte que importaba |
| Paginar | Devuelve un trozo y un cursor | Listas, búsquedas, ficheros que se recorren | El agente pide páginas sin criterio |
| Resumir | Condensa antes de devolver | Textos largos donde basta la esencia | El resumen omite el detalle exacto |
Truncar es lo más barato y lo más traicionero. Si cortas a ciegas, el agente trabaja con una vista parcial sin saberlo, y lo que no sabe que le falta no lo va a pedir.
Truncar sin engañar al modelo
La regla de oro: si recortas, dilo. El resultado debe indicar que está incompleto, cuánto se ha omitido y cómo obtener el resto. Así el modelo puede decidir si le basta o necesita más.
MAX_CHARS = 8_000
def limit_output(text: str, *, keep_head: int = 3_000, keep_tail: int = 3_000) -> dict:
"""Recorta una salida larga conservando principio y final."""
if len(text) <= MAX_CHARS:
return {"content": text, "truncated": False}
omitted = len(text) - keep_head - keep_tail
return {
# Principio y final: en logs y trazas, lo útil suele estar en los extremos
"content": text[:keep_head]
+ f"\n\n[... {omitted} caracteres omitidos ...]\n\n"
+ text[-keep_tail:],
"truncated": True,
"total_chars": len(text),
# Pista accionable: cómo pedir el resto
"hint": "Usa read_logs(offset=..., limit=...) o filtra con 'pattern'.",
}
Conservar cabecera y cola funciona bien con logs, trazas de error y salidas de comandos: el comienzo trae el contexto y el final trae el fallo. Con otros formatos no sirve. Un JSON cortado por la mitad es un JSON roto, así que en datos estructurados recorta por elementos, no por caracteres.
Paginar para que el agente decida
Paginar traslada el control al modelo, y eso es una virtud siempre que le des lo necesario para usarlo bien. Una página útil devuelve tres cosas: los elementos, el total y el cursor de la siguiente.
{
"items": ["... 20 resultados ..."],
"total": 342,
"next_cursor": "c_9f2a",
"note": "Mostrando 20 de 342. Afina la consulta antes de pedir más páginas."
}
La nota final no es decoración. Un agente que ve «342 resultados» y solo tiene un botón de «siguiente» tiende a recorrerlos todos. Mejor empujarlo hacia la herramienta correcta: más filtros, no más páginas. Si además fijas un tope de páginas por tarea, evitas el bucle de paginación infinita.
Resumir: útil, pero con red
Resumir tiene un atractivo evidente: el agente recibe 400 tokens en vez de 40 000. El riesgo es el opuesto al del truncado: el resumen parece completo, y un dato que el resumidor descartó ya no existe para el agente.
Dos precauciones sencillas:
- Guarda el original fuera del contexto, con un identificador, y deja que el agente lo recupere por tramos si necesita el detalle.
- Resume con la tarea a la vista. «Resume este documento» produce algo genérico; «resume lo que afecta a la renovación del contrato» produce algo útil.
Si el resumen lo hace otro modelo, trátalo como una herramienta más: mídelo. Un resumidor barato que pierde cifras es un fallo silencioso. En compactación de contexto en agentes se discute la misma tensión aplicada al historial.
Dónde poner el límite
Un tope único para todas las herramientas es cómodo y casi siempre erróneo. Una búsqueda devuelve fragmentos pequeños y numerosos; una lectura de fichero devuelve un bloque grande. Lo razonable es un presupuesto por herramienta, definido en su especificación y no en el bucle del agente.
Conviene también registrar lo que pasa: cuántas salidas se recortan, de qué herramientas y cuánto. Si una herramienta se trunca en el 60 % de las llamadas, el problema no es el límite: es que la herramienta devuelve demasiado o que el agente la usa mal. Entra en la observabilidad como una métrica más.
Por dónde empezar
- Mide el tamaño real de las salidas de tus herramientas en producción, no en pruebas. Mira el percentil 95, no la media.
- Pon un presupuesto por herramienta y haz que cualquier recorte se declare en el resultado, con una pista para obtener el resto.
- Para listas y búsquedas, pagina y empuja hacia el filtrado antes que hacia más páginas.
- Si resumes, conserva el original recuperable y mide qué se pierde.
El contexto del modelo es un recurso caro y finito. Quien decide qué entra en él es la herramienta, así que diseña sus salidas con el mismo cuidado que sus entradas.