Из чего состоит окно
Окно — это всё, что модель видит в момент принятия решения. Ни больше ни меньше: чего в окне нет, того для неё не существует, даже если оно лежит в соседнем файле и очевидно любому человеку.
На первом шаге в окне лежит немного: системное сообщение, описания инструментов и поручение. К восьмому шагу картина другая — большую часть места занимает история работы, и среди неё бывают очень длинные куски.
Разберём части по очереди.
Системное сообщение — то, что верно для всех задач этого агента: кто он, с каким проектом работает, какие есть соглашения, чего делать нельзя. Оно пишется один раз и едет в каждый вызов. Отсюда его свойство: каждое лишнее предложение здесь оплачивается на каждом шаге каждой задачи. Системное сообщение на три тысячи знаков при десятишаговых задачах — это тридцать тысяч знаков в день на ровном месте.
Описания инструментов мы разбирали в прошлом уроке. Здесь важно только напомнить, что они тоже лежат в окне и тоже едут каждый раз.
Поручение — то, ради чего всё затевалось. Показательно, что это обычно самая маленькая часть окна: сто-двести знаков против десятков тысяч. И при этом именно оно определяет, будет ли работа сделана. Это не парадокс, а обычное дело: инструкция к станку длиннее, чем чертёж детали.
История — просьбы модели и результаты инструментов. Она растёт сама и требует управления; ей посвящена последняя глава этого урока.
И сразу — про самое частое заблуждение. Кажется, что положить в окно побольше безопаснее: пусть будет, вдруг пригодится. Это неверно сразу по двум причинам. Во-первых, за объём платят — каждый шаг, а не один раз. Во-вторых, и это важнее: лишнее в окне не нейтрально. Оно конкурирует за внимание с нужным. Модель, которой дали пять файлов вместо одного нужного, будет искать ответ во всех пяти — и иногда найдёт его не там.
Есть и третья причина, менее очевидная и самая обидная. Лишнее в окне делает поведение агента менее предсказуемым. Пока в окне только нужное, на одну и ту же задачу вы получаете примерно один и тот же ход работы. Стоит положить туда пять посторонних файлов — и агент начинает то заглядывать в них, то нет, в зависимости от мелочей формулировки. Система, которая ведёт себя по-разному на похожих задачах, неремонтопригодна: непонятно, что менять, потому что непонятно, от чего зависит результат.
Отсюда правило, которое стоит сформулировать явно, потому что оно противоречит первому побуждению: окно собирают по принципу «что нужно», а не «что может пригодиться». Второй принцип кажется осторожным, а на деле он означает отказ от решения: вместо того чтобы разобраться, что нужно задаче, в окно кладётся всё подряд и ответственность перекладывается на модель.
Где в окне читается лучше
Теперь про свойство, которое стоит знать всем, кто строит обвязку: внимание распределено по окну неравномерно. Начало и конец работают надёжнее середины.
Это не поломка конкретной модели и не временное явление. Оно проявляется у всех и в разной степени, и относиться к нему стоит как к свойству материала — как столяр относится к тому, что дерево колется вдоль волокон. С этим не борются, на это опираются.
Практические следствия простые.
Правила и запреты — в начало. Системное сообщение стоит первым не случайно: это самое надёжное место окна.
Текущая задача — в конец. Если после сорока тысяч знаков истории вы хотите, чтобы модель помнила, что именно сейчас требуется, — напомните об этом в последнем сообщении. Это называют «повторить задачу в конце», и приём стоит одной строки.
Длинные выводы — в середину. Не потому, что они неважны, а потому, что середина всё равно самое слабое место, и класть туда стоит то, что модель будет искать целенаправленно, а не то, что она должна учесть непременно.
Второе свойство того же рода: модель лучше работает с тем, что структурировано. Не в смысле формата данных, а в смысле разметки текста: заголовки, списки, явные разделители между кусками. Окно, в котором содержимое трёх файлов слито в одну простыню без границ, — это окно, в котором модель будет путать, что откуда.
Отсюда привычка: всё, что кладётся в окно, подписывается. Не просто содержимое файла, а «файл src/cart.js, строки 1–80». Не просто вывод команды, а «вывод npm test, код завершения 1». Подпись стоит двадцать знаков и снимает целый класс путаницы — особенно когда файлов несколько.
И третье, менее очевидное: порядок внутри окна должен быть осмысленным. Если вы кладёте три файла, положите сначала тот, который правится, а потом те, которые нужны для справки. Модель читает подряд, и первый файл получает больше внимания. Порядок «по алфавиту» здесь — это порядок «случайно», и он работает хуже.
Положить заранее или дать искать
Следующий вопрос — что класть в окно заранее, а что оставить агенту искать самому. Это выбор между двумя способами, у каждого своя цена.
| Положить заранее | Дать искать | |
|---|---|---|
| стоимость | платим всегда, даже если не пригодилось | платим шагами, только когда нужно |
| скорость | мгновенно | два-три оборота круга |
| риск | лишнее отвлекает | может не найти или найти не то |
| когда лучше | нужно почти всегда и невелико | нужно изредка или объёмно |
Правило, которое из этого выводится: кладите заранее то, что нужно почти всегда и невелико; остальное дайте искать.
Что обычно попадает в первую категорию: структура проекта (список папок, а не файлов), соглашения команды, сегодняшняя дата, имя и роль пользователя, схема базы, если задача про данные. Всё это короткое, нужно почти в каждой задаче, и поиск его стоил бы шага.
Что во вторую: содержимое конкретных файлов, история изменений, документация, данные из внешних систем. Это объёмно, нужно выборочно, и заранее вы не знаете, что именно понадобится.
Симметричный признак: вы положили слишком много, если в окне регулярно лежит то, к чему агент ни разу не обратился. Понять это сложнее, потому что напрямую не видно, но косвенный признак есть: окно большое, а шагов мало. Значит, работа простая, а платите вы за объём.
Теперь про поиск. Если агент ищет сам, качество поиска становится частью качества системы — и это часто недооценивают. Плохой поиск даёт три беды сразу: агент не находит нужного и работает по неполным данным; находит слишком много и забивает окно; тратит лишние шаги на уточнение запроса.
Практические требования к поиску в обвязке: возвращать фрагменты, а не файлы целиком (строка совпадения и несколько строк вокруг); сообщать число совпадений, даже если показаны не все; уметь искать и по имени, и по содержимому, и явно говорить в описании, какой инструмент для чего.
И приём, который часто оказывается лучше и того и другого: двухступенчатая подача. Сначала в окно кладётся короткая сводка — список файлов с одной строкой описания каждого, оглавление документа, перечень таблиц базы. Агент по ней выбирает нужное и запрашивает подробности. Это дёшево (сводка короткая), точно (выбирает тот, кто знает задачу) и не требует угадывать заранее.
И последнее про подачу — про то, что кладётся не в окно, а рядом. Есть материалы, которые нужны редко, но когда нужны, нужны целиком: подробная документация, справочник кодов ошибок, длинный регламент. Класть их заранее нельзя (объём), давать искать по ним сложно (поиск по тексту регламента работает плохо). Разумный приём — дать оглавление в окно, а содержимое по разделам через инструмент. Оглавление короткое, агент видит, что такой раздел существует, и запрашивает его целиком, когда доходит до дела.
Это тот же двухступенчатый приём, применённый к материалу, а не к файлам, и он показывает главное: вопрос «что положить в окно» почти всегда решается не выбором из двух вариантов, а придумыванием третьего — более дешёвого представления того же самого.
Проверить, удалось ли это, можно одним вопросом к самому себе: сколько знаков окна уходит на то, что задаче не понадобилось. Если ответ «не знаю» — значит, окно собирается наугад, и первый шаг к его удешевлению не в том, чтобы что-то выбросить, а в том, чтобы начать это мерить.
Когда история не помещается
Рано или поздно история перестаёт помещаться в окно. Это не исключительная ситуация: на длинной задаче она наступает обязательно, и решение должно быть принято заранее.
Выбросить старое. Самый простой способ: держим системное сообщение, поручение и последние N сообщений, всё, что между, выбрасываем. Дёшево и работает, пока задача не требует помнить начало. Беда в том, что именно в начале обычно лежит то, что было решено и почему.
Сжать в итог. Когда история подбирается к пределу, обвязка делает лишний вызов модели: «перескажи коротко, что уже сделано, что выяснилось и что осталось». Итог заменяет собой выброшенные сообщения. Стоит одного вызова и заметно лучше держит длинную работу.
Вынести в файл. Агент сам ведёт рабочие заметки: что решено, что проверено, что не сработало. Файл лежит на диске, в окно кладётся только его короткая часть, остальное агент перечитывает по надобности. Способ самый мощный и самый капризный: он требует, чтобы агент заметки вёл и перечитывал, а это не всегда происходит само.
Практический совет по сжатию, который стоит знать: в итог обязательно идёт то, что не сработало. Обычная просьба «перескажи, что сделано» даёт пересказ успехов, и агент после сжатия заново пробует тот способ, который уже отверг три шага назад. Формулировка должна быть явной: «что сделано, что выяснилось, что пробовали и почему отказались, что осталось».
И более общее правило, которое сильно упрощает жизнь: лучший способ справиться с переполнением — не доводить до него. Задача, которая укладывается в десять шагов, не переполнит окно почти никогда. Переполнение — это, как правило, симптом того, что задача слишком крупная или что в окно кладётся слишком много. Прежде чем строить сжатие, стоит посмотреть, нельзя ли разделить работу.
Наконец, полезно мерить заполнение окна и записывать его в трассу. Одна цифра на шаг — сколько процентов окна занято. Она сразу показывает и задачи, которые подходят к пределу, и инструменты, которые возвращают слишком много, и системные сообщения, которые давно пора укоротить. Без этой цифры переполнение всегда оказывается неожиданностью.