бесплатно без регистрации урок 3 из 16

Инструменты: чем агент действует

Проектировать инструменты как границу доверия: проверять имя, параметры и права, держать набор коротким и менять его по ходу, писать возврат для читателя, который видит только его, и различать три вида отказов.

18 мин чтения · 4 главы ·4 задания ·домашняя работа

Что такое инструмент

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

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

Модель: «позови запись файла, путь и текст такие-то»Обвязка: такой инструмент есть? параметры годные? права есть?да → зовём настоящую функциюнет → отказ понятным текстомРезультат — в историю сообщением от инструментаотказ тоже идёт в историю: модель обязана узнать, что вызов не прошёл
Между просьбой модели и настоящей функцией стоят три проверки обвязки. Убрать их — значит отдать модели право вызывать что угодно с чем угодно.

Между просьбой и настоящей функцией стоит обвязка, и она делает три вещи, ни одну из которых пропускать нельзя.

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

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

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

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

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

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

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

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

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

МалоВ самый разМного2–35–1230+агент обходитсятем, что есть,и делает лишниешагикаждый нужен,различимыпо названиюи назначениювыбирает не то,путает похожие,описания съедаютокносужать набор под задачу дешевле, чем учить модель выбиратьсписок приходит с каждым вызовом, поэтому его можно менять по ходу
Набор — не витрина возможностей, а меню выбора. Чем он длиннее, тем чаще выбирается не то и тем дороже каждый шаг.

Почему длинный набор вредит, причин три, и все практические.

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

Похожие инструменты путаются. Если в наборе есть найти_файл, поиск_по_содержимому и искать, модель будет выбирать между ними, и иногда не то. Это не её слабость: вы бы тоже выбирали, глядя на три названия без контекста.

Лишние возможности зовут. Инструмент, лежащий в наборе, рано или поздно будет вызван, даже если задача его не требует. Если в наборе есть «удалить файл», однажды он будет вызван при задаче «почини расчёт».

И вот здесь пригождается деталь из прошлого урока: список приходит с каждым вызовом, а не объявляется один раз. Значит, его можно менять по ходу работы, и это самый дешёвый способ улучшить выбор.

Приёмы, которые из этого следуют:

Сужать под задачу. Задача на разбор кода не требует инструментов записи. Убрать их — значит убрать и соблазн, и вероятность ошибки, и несколько сотен знаков из каждого вызова.

Открывать по мере надобности. Пока агент не прочитал ни одного файла, инструмент «записать файл» ему не нужен; появится после первого чтения. Такой приём заметно снижает число правок вслепую.

Группировать вместо размножения. Вместо пяти инструментов поиска — один с параметром «где искать». Модели проще выбрать параметр внутри понятного инструмента, чем инструмент среди похожих.

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

Что инструмент возвращает

Половина качества инструмента — в том, что он возвращает. И здесь самая частая ошибка выглядит безобидно: вернуть «то, что напечатала команда».

Код завершенияполучилось или нетГлавное из выводатри значимые строкиРазмер и отброшенное«ещё 397 строк скрыто»Что делать дальшеподсказка при отказемолчаливое обрезание хуже длинного вывода: модель не знает, что чего-то не видит
Возврат — это не «вывод команды». Это сообщение, написанное для читателя, который не видел ничего, кроме него.

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

Хороший возврат содержит четыре вещи.

Получилось или нет — явно. Не «вывод пуст», а «команда завершилась успешно, вывода нет». Пустой ответ модель истолкует как угодно, и чаще всего неправильно.

Главное из вывода. Из четырёхсот строк сборки значимы обычно три: что упало и где. Остальное — шум, за который вы платите на каждом следующем шаге.

Размер и факт обрезания. Это тонкое место и источник самых обидных ошибок. Если обвязка молча обрезала вывод до первых ста строк, модель считает, что видела всё, и делает выводы по неполной картине. Написать «показаны первые 100 строк из 412» стоит ничего и меняет поведение целиком: модель попросит остальное, если оно нужно.

Что делать дальше — при отказе. Об этом отдельно в следующем уроке, но правило простое: сообщение об ошибке пишется для агента и содержит путь к исправлению.

Отдельно стоит сказать про формат возврата. Соблазн вернуть строгую структуру — скажем, JSON — велик, и иногда он оправдан. Но модель одинаково хорошо читает и текст, а текст занимает меньше места и не ломается от лишней запятой. Практическое правило: структура нужна там, где её будет разбирать код, а не модель. Если возврат читает только модель — пишите его словами.

И про размер. Разумный потолок на один возврат — порядка двух-трёх тысяч знаков. Больше почти всегда означает, что инструмент возвращает сырьё вместо ответа. Исключение — чтение файла, который агент собирается править: тут нужен весь текст, иначе он будет править вслепую. Но и здесь стоит иметь в инструменте параметр «строки с такой-то по такую-то»: файл на две тысячи строк целиком в окне не нужен почти никогда.

Последнее про возвраты — про единообразие. Если два инструмента сообщают об успехе по-разному («OK», «done», пустая строка), модель тратит внимание на расшифровку вместо работы. Договоритесь об одном виде возврата на все инструменты: первая строка — исход, дальше подробности, в конце — размер и отброшенное. Это не эстетика: одинаковая форма означает, что модель узнаёт неудачу с первой строки и не пытается вычитать её из содержания.

Когда инструмент отказал

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

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

Второе правило: различать, чей это отказ. Три вида, и вести себя с ними надо по-разному.

Чей отказПримерЧто делает обвязка
Модель ошибласьпуть с опечаткой, нет обязательного параметравернуть отказ модели, она исправится
Среда подвеласеть отвалилась, сервис ответил 503повторить самой, модель не трогать
Так устроеноправ нет, файл вне рабочей папкивернуть отказ и объяснить границу

Разница между первым и вторым видом важнее, чем кажется. Ошибку модели чинит модель — она видит отказ и на следующем шаге пробует иначе. Сбой среды модель починить не может: сколько ни пробуй, сеть от этого не появится. Если отдать ей сбой среды, она потратит три шага на попытки и, скорее всего, сделает вывод «этот способ не работает» — то есть научится неверному из случайного события.

Третье правило — про идемпотентность, и это слово стоит того, чтобы его знать. Инструмент идемпотентен, если повторный вызов с теми же параметрами не делает ничего нового. Записать файл с тем же содержимым — идемпотентно. Дописать строку в конец файла — нет: два вызова дадут две строки.

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

И последнее. Отказ инструмента — это сигнал не только модели, но и вам. Один и тот же отказ, повторяющийся в трассах разных задач, почти всегда означает, что инструмент спроектирован неудачно: модель систематически зовёт его не так, как вы ожидали. Чинить это правкой инструкции («не забывай указывать путь от корня») — та самая подмена, о которой шла речь в первом уроке. Чинить надо инструмент: сделать параметр необязательным, принять оба формата пути, переименовать так, чтобы назначение было очевидно.

Задания урока

Каждое проверяется сразу: ответ сверяется с эталоном, и на неверный вариант приходит разбор.

  1. Что обязана проверить обвязка между просьбой модели и вызовом настоящей функции?средне
  2. Чем больше инструментов в наборе, тем лучше работает агент.средне
  3. Если вывод команды слишком длинный, лучше молча обрезать его до первых ста строк.сложно
  4. Разложите отказы по тому, кто их чинит.средне
Пройти урок с проверкой заданий Курс «ИИ-Инжиниринг» открыт и бесплатен, прогресс сохраняется.
Открыть курс