Blog

Mantendo turnos longos do agente dentro da janela de contexto

Um modelo só consegue ler uma certa quantidade de uma vez: a sua janela de contexto. O agente do Mythex faz trabalhos longos: lê muitos arquivos, executa comandos e delega tarefas delimitadas a sub-agentes. Encontramos três formas de o trabalho longo quebrar: sub-agentes cresciam até o modelo recusar todas as requisições, um sub-agente parava cedo porque achava que não tinha mais espaço, e nossa contagem de tokens estava muito errada para textos em russo, chinês e japonês. Aqui está o que mudamos.

Alguns termos antes

  • Tokens são os pedaços que um modelo lê. Limites e custos são contados em tokens, não em caracteres ou palavras.
  • Janela de contexto é o máximo que um modelo consegue ler em uma requisição. Também definimos nosso próprio orçamento menor para cada requisição, para que custos e velocidade continuem razoáveis.
  • Resultados de ferramentas são o que o agente recebe de volta das suas ferramentas: o conteúdo de um arquivo que ele leu, a saída de um comando que executou. Em trabalhos longos, eles são a maior parte do que é enviado.
  • Um sub-agente é um auxiliar que o agente principal inicia para uma tarefa independente, como “confira estes capítulos com as anotações”. Ele recebe uma única instrução e depois trabalha por conta própria por meio de muitas chamadas de ferramentas.

Problema 1: sub-agentes cresciam até o modelo recusá-los

A cada passo, o agente envia ao modelo a conversa até ali. Quando isso ficava grande demais, nosso código cortava o histórico, mas só no início de uma mensagem do usuário, para que uma chamada de ferramenta e o seu resultado nunca fossem separados. Ele mantinha a primeira mensagem, acrescentava uma nota dizendo que a conversa anterior tinha sido cortada e guardava os turnos mais recentes e completos que coubessem.

Um sub-agente tem exatamente uma mensagem do usuário: a sua instrução. Não havia onde cortar. Então ele enviava o histórico inteiro a cada passo, crescendo a cada vez, até a requisição ficar maior do que o modelo aceita, cerca de um milhão de tokens. Em um teste com 8 desses sub-agentes, todos os 8 foram recusados.

A correção: limpar primeiro os resultados antigos de ferramentas

As requisições agora cabem no orçamento em três etapas, cada uma tentada só se a anterior não bastar:

  1. Limpar os resultados de ferramentas mais antigos. O conteúdo deles é substituído por uma nota curta: o resultado foi limpo para manter a conversa dentro do contexto do modelo, e o agente pode executar a ferramenta de novo se precisar. Um arquivo lido há uma hora costuma ser a coisa mais pesada e menos útil na requisição, e o arquivo continua disponível para ser lido de novo. Os resultados mais novos ficam inteiros.
  2. Manter turnos de usuário completos. Como antes: a primeira mensagem, uma nota e os turnos completos mais recentes que coubessem.
  3. Manter rodadas de ferramentas completas. Dentro de um turno longo, que é o único tipo que um sub-agente tem, mantemos a primeira mensagem, uma nota e as rodadas mais recentes e completas de “chamar uma ferramenta, receber o resultado”.

Isso só molda o que é enviado. O histórico salvo do agente guarda tudo. No mesmo teste, 0 de 8 foram recusados e todos os 8 terminaram.

Limpeza em lotes, para o cache continuar funcionando

Os provedores de modelos conseguem reaproveitar trabalho no início de uma requisição se ele for idêntico ao da última: um cache de prefixo. Se limpássemos mais um resultado a cada chamada, o início da requisição mudaria a cada vez e o cache nunca ajudaria.

Por isso, os resultados mais antigos são limpos quatro de cada vez, e não um a um. A parte limpa então permanece igual por várias chamadas. Um resultado que não é muito maior que a nota é deixado em paz: limpá-lo economizaria quase nada e ainda assim mudaria o início da requisição. Quanto fica inteiro é decidido pelo tamanho, não pela quantidade: os resultados mais novos que caibam em um quarto do orçamento ficam, e sempre pelo menos um. Uma versão anterior mantinha seis fixos inteiros, o que era adequado para saídas curtas de comandos, mas, com leituras longas de arquivos, deixava a maior parte do orçamento para resultados antigos.

Recusas agora são explicadas

Uma requisição que o modelo rejeita como inválida não é mais tentada de novo; reenviar a mesma requisição recebe a mesma resposta. Se a conversa for longa demais para o modelo, a pessoa vê: “Esta conversa ficou longa demais para o modelo ler de uma vez. Comece um novo chat para o próximo passo: tudo o que foi construído até agora continua.” Isso é uma decisão para a pessoa tomar, não uma falha do serviço, então não aciona nosso alerta.

Problema 2: o agente parava para “economizar espaço”

Um sub-agente pedido para ler 40 capítulos longos parou depois de 2, dizendo que queria economizar espaço. Ele tinha usado cerca de 100.000 tokens de um orçamento de 400.000.

Ele não tinha como saber que resultados antigos agora seriam limpos para ele. Então, dissemos a ele. As instruções do agente agora dizem que, quando a conversa cresce além do que o modelo aguenta, os resultados de ferramentas mais antigos são limpos e os mais novos ficam inteiros, então não interrompa uma tarefa longa para economizar espaço; continue, e leia um arquivo de novo se precisar do que foi limpo.

Uma correção que o modelo não enxerga não é uma correção do ponto de vista dele. O mecanismo só ajuda se o agente confiar nele.

Problema 3: contar tokens como se tudo fosse inglês

Estimávamos os tokens sem rodar um tokenizador, como caracteres divididos por quatro. É uma regra prática razoável para o inglês. Não é para outros alfabetos: o russo tem cerca de 2,2 caracteres por token, e o chinês ou o japonês, cerca de um.

Assim, um sub-agente lendo capítulos em russo enviava uma requisição de cerca de 640.000 tokens enquanto nossa estimativa dizia que estava abaixo do orçamento de 400.000. A estimativa também poderia facilmente deixar uma requisição passar do limite rígido do modelo.

Agora a estimativa conta por alfabeto: ASCII simples a quatro caracteres por token, chinês, japonês e coreano a um, e todo o resto (cirílico, grego, árabe, letras acentuadas, emojis) a dois. A intenção é errar para cima. Em um capítulo em russo, ela agora estima 43.423 tokens contra cerca de 44.600 medidos; antes, dava 25.023. Como uma mensagem nunca muda depois de escrita, sua estimativa é calculada uma vez e guardada, em vez de ser refeita a cada passo.

O que tiramos disso

  • Corte onde o tamanho realmente está. No trabalho com agentes, isso são os resultados de ferramentas, não as mensagens do usuário.
  • Conte ao agente sobre as próprias redes de segurança. Caso contrário, ele planeja em torno de limites que já não se aplicam.
  • “Caracteres ÷ 4” é uma suposição do inglês. Qualquer coisa que conte tokens para usuários multilíngues precisa contar por alfabeto ou usar um tokenizador de verdade.
  • Cuide do cache. Uma requisição que muda no início a cada chamada custa mais e roda mais devagar.

Se você está incorporando recursos de IA no seu próprio app, nossos guias sobre o que é um LLM e como usar APIs de LLM cobrem janelas de contexto e tokens desde o início. Mais sobre como os sub-agentes funcionam no Mythex está na documentação de sub-agentes.

Mythex · Pricing · Crie qualquer coisa com IA · Templates de sites e apps · Mythex comparado · Glossário de criação de apps com IA · Ferramentas grátis para criar apps com IA · English

English አማርኛ العربية Български বাংলা Bosanski Català Čeština Dansk Deutsch Ελληνικά Español 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 (Portugal) Română Русский Slovenčina Slovenščina Soomaali Shqip Srpski Svenska Kiswahili தமிழ் తెలుగు ไทย Tagalog Türkçe Українська اردو Tiếng Việt 简体中文 繁體中文(香港) 繁體中文(台灣)