Блог

Как уместить длинные ходы агента в контекстное окно

Модель может прочитать за раз лишь ограниченный объём — своё контекстное окно. Агент Mythex выполняет длинную работу: читает много файлов, запускает команды, передаёт ограниченные задачи субагентам. Мы обнаружили три способа, которыми длинная работа ломалась: субагенты росли, пока модель не отказывала во всех запросах; субагент останавливался раньше времени, потому что думал, что места не осталось; а наш подсчёт токенов сильно ошибался для русского, китайского и японского текста. Вот что мы изменили.

Сначала несколько терминов

  • Токены — это фрагменты, которые читает модель. Лимиты и стоимость считаются в них, а не в символах или словах.
  • Контекстное окно — максимум, который модель может прочитать в одном запросе. Мы также задаём собственный, более низкий бюджет для каждого запроса, чтобы стоимость и скорость оставались разумными.
  • Результаты инструментов — то, что агент получает обратно от своих инструментов: содержимое прочитанного файла, вывод выполненной команды. В длинной работе именно они составляют большую часть отправляемого.
  • Субагент — помощник, которого главный агент запускает для самостоятельной задачи, например «сверь эти главы с заметками». Он получает одну инструкцию, а затем сам проходит через множество вызовов инструментов.

Проблема 1: субагенты росли, пока модель их не отклонила

На каждом шаге агент отправляет модели беседу до этого момента. Когда она становилась слишком большой, наш код её обрезал, но только в начале пользовательского сообщения, чтобы вызов инструмента и его результат никогда не разрывались. Он сохранял первое сообщение, добавлял примечание, что более ранний разговор обрезан, и оставлял самые новые целые ходы, которые помещались.

У субагента ровно одно пользовательское сообщение: его инструкция. Резать было негде. Поэтому он отправлял всю свою историю на каждом шаге, и она росла, пока запрос не становился больше того, что модель вообще принимает, — примерно миллиона токенов. В тесте с 8 такими субагентами все 8 были отклонены.

Исправление: сначала очищать старые результаты инструментов

Теперь запросы вписываются в бюджет в три шага, и каждый следующий применяется, только если предыдущий не справился:

  1. Очистить самые старые результаты инструментов. Их содержимое заменяется коротким примечанием: результат очищен, чтобы беседа умещалась в контекст модели, и агент может снова запустить инструмент, если ему нужно. Файл, прочитанный час назад, обычно самая большая и наименее полезная часть запроса, а сам файл всё ещё можно прочитать снова. Самые новые результаты остаются целиком.
  2. Сохранить целые пользовательские ходы. Как и раньше: первое сообщение, примечание и самые новые полные ходы, которые помещаются.
  3. Сохранить целые раунды инструментов. Внутри одного длинного хода — единственного вида, который есть у субагента, — сохранить первое сообщение, примечание и самые новые полные раунды «вызови инструмент, получи результат».

Это меняет только то, что отправляется. Сохранённая история агента содержит всё. В том же тесте не был отклонён ни один из 8 (0 из 8), и все 8 завершились.

Очистка пачками, чтобы кэширование продолжало работать

Провайдеры моделей могут переиспользовать работу над началом запроса, если оно идентично предыдущему — префиксный кэш. Если бы мы очищали по одному результату на каждом вызове, начало запроса менялось бы каждый раз, и кэш никогда бы не помогал.

Поэтому самые старые результаты очищаются по четыре за раз, а не по одному. Очищенная часть тогда остаётся неизменной на протяжении нескольких вызовов. Результат, который не намного больше примечания, не трогаем: его очистка сэкономила бы почти ничего и всё равно изменила бы начало запроса. Сколько остаётся целым, решает размер, а не количество: самые новые результаты, которые помещаются в четверть бюджета, остаются, и всегда хотя бы один. Более ранняя версия сохраняла фиксированные шесть целых результатов, что подходило для коротких выводов команд, но при длинных чтениях файлов оставляло большую часть бюджета старым результатам.

Отказы теперь объясняются

Запрос, который модель отклоняет как некорректный, больше не повторяется: повторная отправка того же запроса даст тот же ответ. Если беседа слишком длинная для модели, человек видит: «Этот разговор стал слишком длинным, чтобы модель могла прочитать его за один раз. Начните новый чат для следующего шага — всё, что уже создано, сохранится». Это решение за человеком, а не сбой, поэтому это не вызывает тревоги у нас.

Проблема 2: агент останавливался, чтобы «сэкономить место»

Субагент, которого попросили прочитать 40 длинных глав, остановился после 2, сказав, что хочет сэкономить место. Он использовал около 100 000 токенов из бюджета в 400 000.

Он не мог знать, что старые результаты теперь будут очищаться за него. Поэтому мы ему сообщили. Инструкции агента теперь говорят: когда беседа вырастает за пределы того, что модель может принять, самые старые результаты инструментов очищаются, а самые новые остаются целыми, — поэтому не останавливайте длинную задачу, чтобы сэкономить место; продолжайте, а если нужно то, что очистили, прочитайте файл снова.

Исправление, которое модель не видит, не является исправлением с точки зрения модели. Механизм помогает, только если агент ему доверяет.

Проблема 3: подсчёт токенов так, будто всё на английском

Мы оценивали токены без токенизатора: символы делились на четыре. Это разумное эмпирическое правило для английского, но не для других письменностей. Русский — примерно 2,2 символа на токен, китайский или японский — около одного.

Поэтому субагент, читающий русские главы, отправлял запрос примерно на 640 000 токенов, в то время как наша оценка говорила, что он в пределах бюджета в 400 000. Оценка с такой же лёгкостью могла отправить запрос за жёсткий лимит модели.

Теперь оценка учитывает письменность: обычный ASCII — по четыре символа на токен, китайский, японский и корейский — по одному, а всё остальное, включая кириллицу, греческий, арабский, буквы с диакритикой и эмодзи, — по два. Оценка намеренно завышена. Для одной русской главы теперь получается 43 423 токена при примерно 44 600 измеренных, тогда как раньше было 25 023. Поскольку сообщение, однажды записанное, не меняется, его оценка вычисляется один раз и запоминается, а не пересчитывается на каждом шаге.

Что мы из этого вынесли

  • Обрезайте там, где на самом деле находится размер. В работе агента это результаты инструментов, а не сообщения пользователя.
  • Расскажите агенту о его страховочных механизмах. Иначе он будет планировать с учётом лимитов, которые уже не действуют.
  • «Символы ÷ 4» — допущение, справедливое для английского. Всё, что считает токены для многоязычных пользователей, должно учитывать письменность или использовать настоящий токенизатор.
  • Берегите кэш. Запрос, который меняется в начале при каждом вызове, стоит дороже и работает медленнее.

Если вы встраиваете ИИ-функции в собственное приложение, наши руководства о том, что такое LLM и как пользоваться API LLM, с самого начала объясняют контекстные окна и токены. Больше о том, как работают субагенты в Mythex, — в документации по субагентам.

Mythex · Pricing · Создавайте что угодно с ИИ · Шаблоны сайтов и приложений · Сравнения Mythex · Глоссарий по созданию приложений с ИИ · Бесплатные инструменты для создания приложений с ИИ · 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 (Brasil) Português (Portugal) Română Slovenčina Slovenščina Soomaali Shqip Srpski Svenska Kiswahili தமிழ் తెలుగు ไทย Tagalog Türkçe Українська اردو Tiếng Việt 简体中文 繁體中文(香港) 繁體中文(台灣)