Blog

Mantener los turnos largos del agente dentro de la ventana de contexto

Un modelo solo puede leer cierta cantidad a la vez: su ventana de contexto. El agente de Mythex hace trabajo largo: lee muchos archivos, ejecuta comandos, delega tareas acotadas en subagentes. Encontramos tres formas en que el trabajo largo se rompía: los subagentes crecían hasta que el modelo rechazaba todas sus peticiones, un subagente se detenía pronto porque creía que se había quedado sin espacio, y nuestro recuento de tokens era muy incorrecto para el texto en ruso, chino y japonés. Esto es lo que cambiamos.

Algunos términos primero

  • Tokens son los fragmentos que lee un modelo. Los límites y los costes se cuentan en ellos, no en caracteres ni palabras.
  • Ventana de contexto es lo máximo que un modelo puede leer en una petición. Nosotros además fijamos un presupuesto propio, más bajo, para cada petición, de modo que los costes y la velocidad sigan siendo razonables.
  • Resultados de herramientas son lo que el agente recibe de sus herramientas: el contenido de un archivo que leyó, la salida de un comando que ejecutó. En el trabajo largo, son la mayor parte de lo que se envía.
  • Un subagente es un ayudante que el agente principal inicia para una tarea autocontenida, como «revisa estos capítulos contra las notas». Recibe una sola instrucción y después trabaja por su cuenta a través de muchas llamadas a herramientas.

Problema 1: los subagentes crecían hasta que el modelo los rechazaba

En cada paso, el agente envía al modelo la conversación hasta ese momento. Cuando eso se hacía demasiado grande, nuestro código la recortaba, pero solo al comienzo de un mensaje de usuario, para que una llamada a herramienta y su resultado nunca se separaran. Conservaba el primer mensaje, añadía una nota indicando que la conversación anterior se había recortado, y mantenía los turnos completos más recientes que cupieran.

Un subagente tiene exactamente un mensaje de usuario: su instrucción. No había dónde cortar. Así que enviaba todo su historial en cada paso, creciendo cada vez, hasta que la petición era más grande de lo que el modelo acepta en absoluto: unos 1.000.000 de tokens. En una prueba con 8 subagentes así, los 8 fueron rechazados.

La solución: borrar primero los resultados de herramientas antiguos

Las peticiones ahora encajan en el presupuesto en tres pasos, probando cada uno solo si el anterior no bastó:

  1. Borrar los resultados de herramientas más antiguos. Su contenido se sustituye por una nota corta: el resultado se borró para mantener la conversación dentro del contexto del modelo, y el agente puede volver a ejecutar la herramienta si lo necesita. Un archivo leído hace una hora suele ser lo más grande y menos útil de la petición, y el archivo sigue ahí para volver a leerlo. Los resultados más recientes se mantienen íntegros.
  2. Mantener turnos de usuario completos. Igual que antes: el primer mensaje, una nota y los turnos completos más recientes que quepan.
  3. Mantener rondas de herramientas completas. Dentro de un turno largo (el único tipo que tiene un subagente), conservar el primer mensaje, una nota y las rondas más recientes completas de «llama a una herramienta, obtén su resultado».

Esto solo da forma a lo que se envía. El historial guardado del agente conserva todo. En la misma prueba, 0 de 8 fueron rechazados y los 8 terminaron.

Borrar en lotes, para que la caché siga funcionando

Los proveedores de modelos pueden reutilizar trabajo del inicio de una petición si es idéntica a la anterior: una caché de prefijo. Si borráramos un resultado más en cada llamada, el inicio de la petición cambiaría en cada llamada y la caché nunca ayudaría.

Así que los resultados más antiguos se borran de cuatro en cuatro, no de uno en uno. La parte borrada se mantiene igual durante varias llamadas. Un resultado que no es mucho más grande que la nota se deja en paz: borrarlo ahorraría casi nada y aun así cambiaría el inicio de la petición. Cuánto se mantiene íntegro se decide por tamaño, no por número: los resultados más recientes que quepan en un cuarto del presupuesto se quedan, y siempre al menos uno. Una versión anterior mantenía fijos seis íntegros, lo que era correcto para salidas cortas de comandos pero, con lecturas largas de archivos, dejaba la mayor parte del presupuesto en resultados antiguos.

Los rechazos ahora se explican

Una petición que el modelo rechaza como inválida ya no se reintenta: volver a enviar la misma petición da la misma respuesta. Si la conversación es demasiado larga para el modelo, la persona ve: «Esta conversación se ha vuelto demasiado larga para que el modelo la lea de una vez. Empieza un chat nuevo para el siguiente paso; todo lo construido hasta ahora se conserva.» Eso es algo que la persona debe resolver, no una caída del servicio, así que no nos avisa.

Problema 2: el agente se detenía para «ahorrar espacio»

Un subagente al que se le pidió leer 40 capítulos largos se detuvo tras 2, diciendo que quería ahorrar espacio. Había usado unos 100.000 tokens de un presupuesto de 400.000.

No tenía forma de saber que los resultados antiguos ahora se borrarían para él. Así que se lo dijimos. Las instrucciones del agente ahora indican que, cuando una conversación crece más allá de lo que el modelo puede aceptar, los resultados de herramientas más antiguos se borran y los más recientes se mantienen íntegros; por tanto, no detengas una tarea larga para ahorrar espacio: sigue, y vuelve a leer un archivo si necesitas lo que se borró.

Un arreglo que el modelo no puede ver no es un arreglo desde su punto de vista. El mecanismo solo ayuda si el agente confía en él.

Problema 3: contar tokens como si todo fuera inglés

Estimábamos los tokens sin ejecutar un tokenizador, como caracteres divididos entre cuatro. Es una regla práctica razonable para el inglés. No lo es para otras escrituras: el ruso tiene unos 2,2 caracteres por token, y el chino o el japonés, alrededor de uno.

Así que un subagente que leía capítulos en ruso enviaba una petición de unos 640.000 tokens, mientras nuestra estimación decía que estaba por debajo del presupuesto de 400.000. La estimación podría igualmente haber puesto una petición por encima del límite duro del modelo.

Ahora la estimación cuenta por escritura: ASCII normal a cuatro caracteres por token; chino, japonés y coreano a uno; y todo lo demás (cirílico, griego, árabe, letras acentuadas, emoji) a dos. Está pensada para errar por exceso. En un capítulo en ruso, ahora estima 43.423 tokens frente a unos 44.600 medidos; antes decía 25.023. Como un mensaje no cambia una vez escrito, su estimación se calcula una sola vez y se recuerda, en lugar de hacerse en cada paso.

Qué aprendimos

  • Recorta donde está realmente el tamaño. En el trabajo de agentes, eso son los resultados de herramientas, no los mensajes de usuario.
  • Dile al agente cuáles son sus propias redes de seguridad. Si no, planifica alrededor de límites que ya no se aplican.
  • «Caracteres ÷ 4» es una suposición del inglés. Cualquier cosa que cuente tokens para usuarios multilingües tiene que contar por escritura, o usar un tokenizador real.
  • Cuida la caché. Una petición que cambia al inicio en cada llamada cuesta más y va más lenta.

Si estás añadiendo funciones de IA a tu propia app, nuestras guías sobre qué es un LLM y cómo usar APIs de LLM cubren desde el principio las ventanas de contexto y los tokens. Más información sobre cómo funcionan los subagentes en Mythex está en la documentación de subagentes.

Mythex · Pricing · Crea lo que quieras con IA · Plantillas de sitios web y apps · Mythex comparado · Glosario para crear apps con IA · Herramientas gratis para crear apps con IA · English

English አማርኛ العربية Български বাংলা Bosanski Català Čeština Dansk Deutsch Ελληνικά Español (España) Eesti فارسی Suomi Français Français (Canada) ગુજરાતી हिन्दी Hrvatski Magyar Հայերեն Bahasa Indonesia Íslenska Italiano 日本語 ქართული Қазақ тілі ಕನ್ನಡ 한국어 Lietuvių Latviešu Македонски മലയാളം Монгол मराठी Bahasa Melayu မြန်မာ Norsk bokmål Nederlands ਪੰਜਾਬੀ Polski Português (Brasil) Português (Portugal) Română Русский Slovenčina Slovenščina Soomaali Shqip Srpski Svenska Kiswahili தமிழ் తెలుగు ไทย Tagalog Türkçe Українська اردو Tiếng Việt 简体中文 繁體中文(香港) 繁體中文(台灣)