Код вместо набора инструментов
Этот блок сравнивает два способа работы агента с инструментами: по одному вызову или скриптом, который пишет модель. Он показывает, где что стоит токенов, где код проигрывает и какие проверки надо перенести, если модель пишет код. У вендоров это называется code mode или programmatic tool calling.
Northwind нужны все заказы прошлого месяца с задержкой больше пяти дней из 10 000 строк и по задаче в CRM на каждый такой заказ перевозчика NorthPost.
Прочитать всю книгу или выполнить список
По телефону коллега говорит «прочитай мне книгу заказов», и вы читаете вслух 10 000 строк. Он всё это слушает, выбирает 312 и просит 312 раз завести задачу, по одному звонку, и каждый раз весь разговор повторяется с самого начала.
Или коллега диктует короткий список: «пройди по книге, оставь такие строки, на каждую заведи задачу, скажи, сколько вышло». Вы делаете это в запертой подсобке и перезваниваете с двумя числами.
Что происходит, шаг за шагом
В API Anthropic это называется programmatic tool calling. Вы отмечаете, какие инструменты может вызывать скрипт:
{
"name": "list_orders",
"description": "Lists orders for a month with status and carrier.",
"input_schema": {
"type": "object",
"properties": {
"month": {
"type": "string"
}
},
"required": [
"month"
]
},
"allowed_callers": [
"code_execution_20260120"
]
}- Модель пишет код на Python, где
list_orders— это асинхронная функция. Она принимает один словарь и возвращает строку. - Скрипт запускается в изолированном контейнере.
- Когда скрипт вызывает инструмент, он останавливается, и API возвращает вашему коду блок
tool_use. Полеcallerназывает запуск кода. - Ваш код выполняет инструмент и отправляет
tool_result. Скрипт продолжается, а результат не попадает в контекст модели. - Когда скрипт заканчивается, модель получает его вывод и работает дальше.
Сам скрипт может выглядеть так:
rows = json.loads(await list_orders({"month": "2026-10"}))
late = [r for r in rows
if r["carrier"] == "NorthPost" and r["days_late"] > 5]
for r in late:
await create_task({"order_id": r["order_id"], "kind": "follow_up"})
print(len(rows), len(late))Модель прочитает два числа. Code Mode от Cloudflare устроен по той же идее, только со сгенерированным JavaScript в изоляте V8. Детали у каждого продукта свои: например, в API Anthropic так нельзя вызывать инструменты из MCP-коннектора, и инструменты со strict: true не поддерживаются.
Что это экономит и когда не экономит
Результат, отправленный модели, читается заново на каждом следующем шаге, поэтому последовательные вызовы быстро накапливаются. На графике начало контекста 12 000 токенов, кэша нет, арифметика наша.
Параллельные вызовы закрывают заметную часть разрыва, и песочница им не нужна. При двух маленьких зависимых вызовах модель должна увидеть первый результат, прежде чем написать второй вызов, поэтому скрипт не экономит ни шага.
Опубликованные числа зависят от задачи, так что смотрите, кто её выбрал:
Источник | Задача | Результат |
|---|---|---|
Пост Anthropic | Гипотетический поток из Drive в Salesforce | С 150 000 до 2000 токенов |
Cloudflare | Весь API Cloudflare в двух инструментах | С 1,17 млн примерно до 1000 токенов на определения инструментов |
Документация Anthropic | Бенчмарк на 75 инструментов | Примерно на 38% меньше оплачиваемых входных токенов при той же точности |
Документация Anthropic | τ²-bench, один-два последовательных вызова за шаг | Те же результаты, примерно на 8% дороже |
Примеры вендоров подобраны в их пользу. Качество зависит от модели и длины цепочки. В одном препринте августа 2026 года, на подмножестве из 309 задач BFCL v4, код совпал с вызовами в JSON или превзошёл их у 11 моделей из 14, а три старые модели GPT отстали. В тесте на цепочки из 52 задач преимущество доходило до 18,8 пункта на цепочках из 12 шагов и длиннее, а на коротких цепочках его не было.
Кто теперь следит
При tool call модель видит каждый результат и выбирает следующий шаг. Скрипт она пишет один раз, и цикл может оформить 500 возвратов за один шаг, а модель не прочитает ни одного результата. Сколько видит ваш код, зависит от продукта. В API Anthropic каждый вызов по-прежнему останавливается и приходит к вашему коду. codeMcpServer от Cloudflare вызывает обработчики изнутри одного внешнего инструмента code, и его документация требует проверять авторизацию и одобрение в каждом обработчике.
Поэтому правила из блока про возвраты остаются в инструменте, и к ним добавляются два.
- Лимиты на прогон стоят в инструменте: сколько возвратов, какая общая сумма и один возврат на заказ за прогон.
- Ключи остаются вне песочницы. В схеме search-and-execute у Cloudflare сгенерированный код получает функцию запроса и никогда не видит сам ключ.
Считайте код недоверенным. Инъекция, которую модель прочитала раньше, может повлиять на скрипт, и Anthropic пишет, что сгенерированному коду нужны песочница, лимиты ресурсов и мониторинг. Hugging Face пишет, что локальная песочница Python не может быть полностью безопасной, так что интерпретатор внутри вашего процесса границей не считается. allowed_callers тоже не граница: документация Anthropic предупреждает, что полагаться на него как на границу безопасности нельзя.
Как выбирать
| Tool call | Код |
|---|---|---|
Подходит | Мало инструментов, одиночные шаги | Много инструментов, большие результаты, длинные цепочки, циклы |
Инфраструктура | Ничего сверх обычного | Песочница, лимиты, контроль сети |
Надзор | Модель читает каждый результат | Проверка стоит в инструменте |
Выбирать можно для каждого инструмента отдельно. list_orders в Northwind идёт через код, а issue_refund остаётся обычным инструментом со своими проверками, потому что каждый возврат кто-то должен видеть.
Вывод
- Код держит результаты вне контекста, поэтому окупается на больших данных и длинных цепочках, а на коротких может стоить дороже.
- Экономия у вендоров показана на примерах в их пользу. Меряйте на своих задачах и моделях.
- Код от модели недоверенный. Ему нужны настоящая песочница, ключи снаружи и лимиты внутри каждого инструмента.