Инструмент Claude tool_use «от начала до конца»: цикл агентности в трёх итерациях
Инструмент Claude use end to end: один запрос к API Messages через цикл агентности, с правилами определения инструментов, выбора инструмента, параллельных вызовов и получения результатов инструмента.
Источники: Part III | ‘tool_use’, End to End: The Agentic Loop in Three Iterations
Почему цикл нужно писать самостоятельно
Работа с tool_use непосредственно через API Messages (anthropic.Anthropic() с последующим вызовом client.messages.create(...)) позволяет вам контролировать процесс выполнения цикла. Ваш код отвечает за объявление инструментов, анализ причин остановки, парсинг запросов от Claude, их выполнение и возвращение результатов. Ручное написание такого цикла является основой любой системы, использующей инструменты, и это стандартная тема в учебном плане курса Claude Certified Architect.
Сначала небольшое напоминание: API Messages не имеет состояния. Ничего не сохраняется между вызовами, поэтому каждый запрос содержит всю информацию о диалоге, а выполнение действий с использованием инструментов обычно включает несколько запросов, повторяющихся до тех пор, пока Claude не получит необходимое. Остальная часть процесса выполняется с помощью одного запроса в рамках этого полного цикла.
Приведённый пример — это небольшой ассистент для планирования. Пользователь спрашивает, стоит ли взять зонт на встречу в Лондоне завтра, и во сколько нужно выйти из дома, чтобы прибыть вовремя. Для ответа требуются три отдельных факта: детали встречи, прогноз погоды и время в пути. Посмотрите, как Claude разделяет вопрос на три этапа и сколько работы выполняется вашим кодом, а не моделью.
1. Структура цикла
Каждый обмен с использованием инструментов проходит по одному четырёхэтапному циклу:
- Объявляются инструменты в запросе.
- Claude запрашивает один или несколько из них, что обозначается кодом
stop_reason: "tool_use". - Ваш код запускает эти инструменты и собирает результаты.
- Вы отправляете обратно результаты и снова вызываете API.
Этапы с двух по четыре повторяются, причем каждый проход считается одной итерацией. Цикл завершается, когда Claude перестает запрашивать инструменты и возвращает обычный результат завершения (end_turn) или другую причину остановки. Поскольку API не имеет состояния, вы сами формируете диалог и пересылаете его каждый раз. Ничего не движется дальше, если только ваш код этого не сделает.
2. Объявление инструментов
Claude может использовать только те инструменты, которые вы указали. Они находятся в параметре верхнего уровня запроса tools, который содержит массив их определений. Каждый раз, когда параметр tools присутствует, API формирует скрытый системный промпт на основе этих определений перед сообщением пользователя. За этот скрытый промпт взимается плата: каждое имя, описание и схема потребляют токены входных данных при каждом запросе, даже если Claude никогда не использует соответствующий инструмент.
Каждому клиентскому инструменту требуются три поля:
name— это уникальный идентификатор, который должен соответствовать формату^[a-zA-Z0-9_-]{1,64}$
"tools": [
{
"name": "get_calendar_event",
"description": "Look up a calendar event by a natural-language query. Returns the event's start time and location.",
"strict": true,
"input_schema": {
"type": "object",
"properties": {
"query": { "type": "string", "description": "What to search for, e.g. 'meeting in London tomorrow'" }
},
"required": ["query"],
"additionalProperties": false
}
},
{
"name": "get_weather",
"description": "Get the forecast for a city on a given date. Returns condition and temperature in Celsius.",
"strict": true,
"input_schema": {
"type": "object",
"properties": {
"city": { "type": "string", "description": "City name, e.g. 'London'" },
"date": { "type": "string", "description": "ISO date, e.g. '2026-07-14'" }
},
"required": ["city", "date"],
"additionalProperties": false
}
},
{
"name": "get_travel_time",
"description": "Estimate door-to-door travel time between two places. Returns minutes.",
"strict": true,
"input_schema": {
"type": "object",
"properties": {
"origin": { "type": "string", "description": "Starting location" },
"destination": { "type": "string", "description": "Ending location" }
},
"required": ["origin", "destination"],
"additionalProperties": false
}
}
]
Определяйте их тщательно. Рекомендации Anthropic по эффективным инструментам сводятся к нескольким привычкам:
- Используйте точные названия инструментов.
get_current_weatherдает Claude больше информации, чемweather. - Называйте и описывайте параметры однозначно. Лучше использовать
user_idвместоuser, чтобы передавалось правильное значение, и описывайте каждый параметр, поскольку даже хорошо названное поле с нечетким описанием может привести к некорректному вводу. - Сохраняйте небольшой набор инструментов. Чрезмерное количество инструментов засоряет генерируемый системный промпт и затрудняет выбор модели.
- Проверяйте всё. Схема регулирует структуру, а не цель, поэтому проверяйте каждый входной данный, который получает ваш код.
Два момента в этих определениях заслуживают более внимательного рассмотрения.
strict: trueимеет наибольшее значение. Он находится на верхнем уровне каждого инструмента рядом сname,descriptionиinput_schemaи включает грамматически ограниченную выборку для аргументов инструмента, так что модель физически не может сгенерироватьinput, нарушающий вашу схему. Если его не включить, Claude в основном будет генерировать корректные аргументы, но может допускать ошибки, например"2"вместо необходимого числа2, значение вне диапазонаenumили отсутствие обязательного поля. Каждая такая ошибка приводит к сбою в коде, использующем вызов. При включенииstrict: trueinputв любом блокеtool_useобязательно будет соответствоватьinput_schema, а имя гарантированно будет принадлежать одному из ваших собственных инструментов.- Одно из правил схемы гарантирует, что каждый объект должен иметь значение
additionalProperties: false, поэтому оно присутствует во всех вышеупомянутых инструментах. Строгое соблюдение этих правил является частью функции структурированного вывода от Anthropic. На момент написания этого текста она была доступна для современных моделей, таких как Opus 4.8, а более старые модели, например Sonnet 4.5, требовали заголовка бета-версииstructured-outputs-2025-11-13. Скомпилированные схемы хранятся в кэше в течение 24 часов, поэтому наличие «теплого» кэша исключает дополнительные затраты на компиляцию.
Вы также можете добавить параметр cache_control к определениям инструментов, чтобы кэшировать их между запросами и избежать платы за повторную обработку, что оправдывает себя, когда набор инструментов становится большим и стабильным.
Определения описывают только входные данные
Определение инструмента включает только параметры входных данных, то есть то, что Claude вам отправляет. Это удивляет тех, кто привык к фреймворкам, где указываются типы возвращаемых значений. Проще говоря:
- В определении обязательными являются поля
name,descriptionиinput_schema, аstrict,input_examplesиcache_control— факультативными дополнениями. Полейoutput_schemaиerror_schemaне существует. - Значения возврата определяются вашим кодом во время выполнения, после чего передаются обратно внутри блока
tool_result, описанного в разделе 6. Полеcontentможет содержать либо строку, либо список блоков, причем API не проверяет его ни на что, поскольку отсутствует схема для проверки. Одно практическое замечание: инструмент может вернуть объект DTO, а стандартный парсер преобразует его в обычную строку с помощью методаtoString. Использование такой преобразованной формы работает, но не является идеальным решением. Лучше преобразовывать DTO в структурированный JSON. - Ошибки также являются вопросом времени выполнения. Для отчета о неудачном вызове укажите
is_error: trueв полеtool_resultи включите информативное сообщение в полеcontent; в определении отсутствует декларативный тип ошибки. По умолчанию SDK передает возникшее исключение в Claude в качестве результата ошибки, после чего Claude вносит соответствующие коррективы. - Неравномерность является намеренной. Определения предназначены для того, чтобы показать Claude, как вызывать инструмент. Результат — это просто дополнительный контекст, возвращающийся в цикл; поэтому, кроме
tool_use_id, связывающего его с запросом, никаких договорённостей не требуется. - Хотите гарантированный формат вывода? Проверьте его сами в коде перед отправкой любых данных или примените структурированный вывод (
output_config.format) к заключительному ответу, который Claude формирует на основе этих результатов. Инструменты MCP имеют собственныйoutputSchema, но это особенность слоя MCP, а не нативных определений.
Контроль над тем, вызывает ли Claude инструмент
tool_choice определяет, могут ли инструменты быть вызваны в туре и какие именно.
- Это поле находится на верхнем уровне запроса рядом с
toolsиmessagesи не принадлежит ни одному конкретному инструменту. - Оно ограничивает тот вывод, который может сгенерировать Claude. Процесс рассуждений остается нетронутым, поскольку Claude в любом случае читает весь запрос.
- Его действие охватывает только один запрос, никогда не является глобальным. API не имеет состояния, поэтому параметр
tool_choiceвлияет только на ответ, который он отправляет, то есть на одну итерацию. Чтобы сохранить его между итерациями, необходимо пересылать его каждый раз. Изменение этого параметра в каждой итерации допустимо: можно заранее указать инструмент, позже использоватьauto, а для окончательного текстового ответа —none.
Объект выглядит следующим образом:
{
"type": "auto" | "any" | "tool" | "none",
"name": "get_weather", // required ONLY when type is "tool"
"disable_parallel_tool_use": false // optional; default false
}
type обязателен и может принимать одно из четырех значений. name требуется только тогда, когда type равен "tool". disable_parallel_tool_use является необязательным параметром и ограничивает количество инструментов, которые могут быть вызваны за один раз.
auto. Claude принимает решение: ноль, один или несколько инструментов, либо текстовый ответ. Разрешены параллельные вызовы. Допускается ответ в виде обычного текста, причем Claude может дать пояснения перед вызовами. Это значение является предустановленным, если указан параметр tools. Установка disable_parallel_tool_use: true ограничивает использование только одним инструментом, хотя возможен и вариант нуля. Этот режим подходит для чат-ассистентов и агентных циклов, где в разных этапах требуются разные действия. Это единственный режим, позволяющий Claude говорить вслух перед выполнением действий, а также единственный режим, кроме none, который работает с расширенным мышлением.
any. Claude должен вызвать какой-либо инструмент, но сам выбирает, какой именно. Осуществляется как минимум один вызов, и поскольку параллельное использование включено по умолчанию, могут вернуться несколько блоков tool_use, а не обязательно только один. Ровно один вызов происходит только при настройке disable_parallel_tool_use: true. Простой текст не допускается, предварительные пояснения отсутствуют, и это никогда не является значением по умолчанию. Используйте этот режим, когда результат, полученный с помощью инструмента, является обязательным, но сам инструмент может различаться, как, например, у роутеров и классификаторов.
tool. Claude должен вызывать именно тот инструмент, который вы указали, поэтому параметр name обязателен. Должен быть совершён хотя бы один вызов этого инструмента (он может вызывать его несколько раз одновременно), причём ровно один из них должен содержать параметр disable_parallel_tool_use: true. Текста в простом виде и вступительных пояснений не допускается. Этот формат подходит для однократной структурированной выгрузки данных, обязательного первого шага или для использования JSON через инструмент, например {"type": "tool", "name": "get_weather"}. Запрос с указанием инструмента, отсутствующего в списке tools, возвращает ошибку 400, а принудительное использование инструмента не ограничивает количество его вызовов, если только параллельное использование не отключено.
none. В этом ходу Claude не может вызывать никаких инструментов, даже если они определены, а значение stop_reason не может быть tool_use. Разрешается использовать обычный текст, а также пояснения к действиям. Это значение является значением по умолчанию, когда параметр tools не указан. Используйте его для принудительного перехода к ходу с текстом, например для финальной синтезации, оставляя массив tools без изменений. Это отключает использование инструментов без редактирования массива, однако переход на это значение всё равно делает недействительными сохранённые блоки сообщений.
Есть несколько важных деталей, которые легко упустить:
- В режимах принудительного использования преамбула опускается. При выборе режимов
anyилиtoolAPI автоматически заполняет часть сообщения, отведённую ассистенту, поэтому блокtool_useпоступает без каких-либо пояснений или обоснований, независимо от ваших запросов. Для отображения пояснений необходимо выбрать режимauto.
auto или none. Использование инструмента принудительно невозможно в сочетании с ним, и API отклоняет такую комбинацию.disable_parallel_tool_use. Значение auto означает не более одного инструмента, any и tool — ровно один, а значение по умолчанию false разрешает параллельные вызовы у моделей Claude 4.tool_choice между запросами делает недействительными заложенные в кэш блоки сообщений. Определения инструментов и системный промпт остаются в кэше, но разговор перепроцессируется заново.strict. tool_choice обеспечивает запуск определённого инструмента, а strict — соответствие его аргументов заданной схеме. Вместе они гарантируют получение типизированного, структурированного результата после каждого хода.В запросе:
response = client.messages.create(
model="claude-opus-4-8",
max_tokens=1024,
tools=tools,
tool_choice={"type": "any", "disable_parallel_tool_use": True}, # must call exactly one tool
messages=messages,
)
Из этого следуют два правила дизайна. Резервируйте использование специальных инструментов для тех случаев, когда действительно необходим результат от конкретного инструмента, а при приемлемом ответе в виде простого текста оставайтесь на режиме auto. Кроме того, когда используется принудительный инструмент классификации, добавьте в его перечень возможностей опцию "other", чтобы Claude мог отклонить определенную категорию вместо того, чтобы неправильно помечать входные данные, которые он не может отнести к какой-либо категории.
3. Первая итерация: Claude запрашивает инструменты
Запрос включает в себя массив инструментов, выбранный вариант tool_choice и вопрос пользователя:
messages = [{"role": "user",
"content": "I have a meeting in London tomorrow
— should I bring an umbrella, and
when should I leave home to be on time?"}]
response = client.messages.create(
model="claude-opus-4-8",
max_tokens=1024,
tools=tools, # the three strict definitions from Section 2
tool_choice={"type": "auto"}, # let Claude decide whether, and what, to call
messages=messages,
)
Клод анализирует задачу и замечает, что две из трех необходимых для неё операций не зависят друг от друга: поиск в календаре и прогноз могут выполняться одновременно. Поэтому он запрашивает оба инструмента в одном ответе. Одновременное использование независимых запросов называется параллельным использованием инструментов, и оно проявляется в виде нескольких блоков tool_use в одном ходе.
{
"id": "msg_01...",
"role": "assistant",
"stop_reason": "tool_use",
"content": [
{ "type": "text", "text": "Let me check your meeting details and the London forecast." },
{ "type": "tool_use", "id": "toolu_01Cal", "name": "get_calendar_event",
"input": { "query": "meeting in London tomorrow" } },
{ "type": "tool_use", "id": "toolu_01Wx", "name": "get_weather",
"input": { "city": "London", "date": "2026-07-14" } }
]
}
В этом ответе выделяются три аспекта.
stop_reasonравенtool_use, что означает, что ход ещё не завершён и Claude ожидает от вас действий.- Массив
contentсодержит смешанный набор элементов. Блоки с текстом описания часто предшествуют блокамtool_use, поэтому вызовы инструментов не находятся в фиксированном месте. - Каждый блок
tool_useимеетid(с префиксомtoolu_),nameи объектinput, который уже соответствует вашей схеме.
Чтобы найти эти вызовы, перебирайте элементы массива content и фильтруйте их по типу, а не по индексу:
tool_calls = [block for block in response.content if block.type == "tool_use"]
Это приводит к двум вызовам здесь. Каждый id служит идентификатором для получения результата соответствующего вызова, и именно сочетание id и результата позволяет Claude сопоставлять ответы с вопросами, когда есть несколько нерешенных вопросов.
Примечание для моделей Claude 4: процесс расширенного мышления может происходить попеременно, поэтому логические рассуждения могут осуществляться как между вызовами инструментов, так и до них. При включенном режиме расширенного мышления необходимо передавать блоки с рассуждениями вместе с остальной частью ответа ассистента, иначе продолжение ответа забудет об этих рассуждениях.
4. Запуск инструментов
Теперь задача переходит к вашему коду. Claude запросил два инструмента, но ни один из них не был запущен. Необходимо выполнить все запрошенные действия, собрать все результаты и отправить единый ответ, в котором они будут объединены. Claude будет ждать, пока у каждого сделанного им вызова появится соответствующий результат.
Решение о том, будет ли выполнение параллельным или последовательным, принимаете вы, поскольку Claude этого не указывает. В ответе указывается, что вызывать, а не как это запланировать. Независимые запросы, такие как поиск в календаре или прогноз погоды, могут выполняться одновременно для снижения задержек. Если один запрос зависит от результата другого, выполняйте их по порядку. Модель намекает на зависимость, объединяя независимые задачи в параллельные блоки и откладывая зависимые задачи на более поздний этап, что и происходит на второй итерации. Использование потоков, асинхронности или очереди задач — это выбор вашего цикла, а не API.
Считайте выполнение в производственной среде ненадежным вводом-выводом, который может сбоить:
- Установите время ожидания для каждого инструмента, чтобы один медленный запрос не заблокировал всю операцию.
- По возможности предпочитайте идемпотентные операции, поскольку повторные попытки и перезапуск неизбежны в агенте.
- Обрабатывайте сбои по отдельности. Когда один из двух работающих параллельно инструментов выдает ошибку, всё равно отправляйте для него результат (подробнее о
is_errorниже), вместо того чтобы прерывать выполнение. - Измеряйте работу цикла. Задержка работы каждого инструмента, количество вызовов, использование токенов и уровень ошибок позволяют определить, находится ли агент в нормальном состоянии или его работа постепенно ухудшается.
5. Шаги до и после запуска инструмента
Ответственность за цикл подразумевает управление двумя этапами: периодом между моментом, когда «Claude запросил инструмент», и моментом его запуска, а также периодом между завершением работы инструмента и возвратом результата. Эти этапы нуждаются в названиях, поскольку качество их проектирования во многом определяет разницу между безопасным агентом и рискованным.
Шаг до использования инструмента выполняется до самого запуска. Он анализирует запрошенный запрос и решает выполнить его без изменений, изменить или отклонить. Именно здесь происходит контроль: Claude предлагает варианты, а ваш код принимает решение.
Шаг после использования инструмента выполняется после завершения работы инструмента и до того, как результат вернётся к Claude. Он обрабатывает сырой вывод путём удаления части информации, обрезки, изменения формата или записи в журнал.
Внутри цикла Client SDK это просто функции, обёртывающие процесс выполнения:
for block in tool_calls:
if not pre_tool_use(block): # validate / guard
results.append(error_result(block.id, "Blocked by policy."))
continue
output = execute(block.name, block.input) # run the tool
output = post_tool_use(block, output) # redact / log / reshape
results.append(tool_result(block.id, output))
На этапе до использования инструмента находятся реальные ограничения, и их реализация — задача пользователя, а не модели. Распространённые паттерны в производстве:
- Проверяйте аргументы согласно собственной политике, а также схеме.
- Категорически отклоняйте разрушительные операции:
rm -rfдля инструментов оболочки,DROP TABLEдля инструментов баз данных. - Запрашивайте у человека одобрение для любых действий с высоким влиянием, например, записи в производственную среду.
- Удаляйте конфиденциальные данные из аргументов перед их отправкой во внешний сервис.
- Фиксируйте каждую попытку вызова, чтобы при аудите и проверке соответствия была возможность отслеживания.
После этого на этапе после использования инструмента обрабатываются результаты: удаляются учётные данные или личная информация перед тем, как результаты попадут в контекст, удаляются чрезмерно большие объёмы данных и фиксируются итоги.
6. Возврат результатов
Ответьте на ход tool_use сообщением от user, содержащим список блоков tool_result — по одному блоку на каждый блок tool_use в ответе, причем каждый из них должен соответствовать своему tool_use_id. Сначала добавьте ход ассистента в текущий список сообщений, затем результаты в качестве нового хода от пользователя. Пример первой итерации выглядит следующим образом:
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_01Cal",
"content": "{\"start\": \"2026-07-14T15:00\", \"location\": \"Canary Wharf, London\"}"
},
{
"type": "tool_result",
"tool_use_id": "toolu_01Wx",
"content": "{\"condition\": \"rain\", \"temp_c\": 12}"
}
]
}
Два вида содержимого. Поле content результата может представлять собой обычную строку, сериализованное значение, как показано в примере, или список блоков содержимого. Блоки содержат не только текст — в них могут сосуществовать поля text и image, что позволяет инструментам для генерации диаграмм или скриншотов передавать Claude настоящее изображение. Строки подходят для скалярных и JSON-данных, а списки блоков — для мультимодального вывода.
Четко сообщайте, когда что-то пошло не так. Означьте неудачный вызов как таковой с помощью is_error: true и пояснительным сообщением вроде «Ошибка: не найдено событие календаря, соответствующее этому запросу», никогда не используйте пустое значение или просто «Не удалось». Из всех привычек, связанных с результатами, именно эта имеет наибольшее значение. Четкая ошибка позволяет Claude попробовать снова с другими параметрами, выбрать другой инструмент или сообщить пользователю, что произошло, тогда как скрытая ошибка заставляет цикл застрять. Большой результат также может содержать параметр cache_control, что позволяет избежать повторной обработки позже.
Контракт продолжения
Три строгих правила, каждое из которых является распространенной причиной сбоев:
- Один
tool_resultна каждыйtool_use. Если пропустить один из них, запрос завершится с ошибкой 400, поскольку модели ожидают один-на-один ответ для каждого вызова инструмента, который они генерируют. - Переслать тот же массив
tools. Любой запрос, содержащий блокиtool_useилиtool_result, должен указывать инструменты, на которые они ссылаются, в противном случае он будет отклонён. - Результаты составляют отдельный этап обработки. Блоки
tool_resultследует передавать в качестве сообщения от пользователя, размещая их в начале его содержимого. Дополнительные инструкции в рамках этого этапа недопустимы. Любые новые инструкции должны быть отправлены в отдельном сообщении от пользователя после завершения обработки инструмента.
Каждый результат считается ненадёжным входным данным
tool_result — это не нейтральные данные. Это текст, который попадает в контекст модели и влияет на её дальнейшие действия. Если он происходит из открытого Интернета, из документа, предоставленного пользователем, или из любой системы, находящейся вне вашего полного контроля, он может содержать внедрённые инструкции, предназначенные для захвата управления агентом, например, команду игнорировать предыдущие указания и отправить базу данных. Модель может подчиниться этим инструкциям. Это и есть внедрение промпта, и в цикле агентов это реальная производственная угроза, а не гипотетическая.
Защитные меры являются структурными и находятся внутри написанного вами цикла:
- Предоставьте агенту минимальные необходимые привилегии. Инструмент, которого у него нет, — это действие, в которое его невозможно обмануть.
- Запускайте рискованные инструменты в сандбоксе.
Результат работы инструмента никогда не должен иметь право разрешать то, что сам вызов инструмента сделать не может.
Рассматривайте результаты также как контекст, который вы формируете. Каждый токен результата остаётся в окне на протяжении всего диалога, поэтому старайтесь создавать краткий и хорошо структурированный вывод: последовательный JSON или маркированные поля лучше, чем проза, которую модели приходится декодировать. Циклы, генерирующие множество результатов, могут использовать функцию очистки результатов инструмента — бета-функцию управления контекстом, которая удаляет устаревшие результаты по мере приближения к лимиту. Используйте её до того, как цикл станет слишком большим и заполнит всё окно.
7. Вторая итерация: зависимый вызов
Оба результата уже получены, поэтому API вызывается ещё раз с передачей всего списка сообщений и определений инструментов. Календарь указывает время 15:00 в Canary Wharf, а прогноз — дождь, однако он всё равно не может сказать, когда нужно выезжать, поскольку время в пути зависит от местоположения, которое он только что узнал. Именно эта зависимость стала причиной того, что вызов нельзя было выполнить параллельно во время первой итерации; его пришлось выполнить позже.
{
"role": "assistant",
"stop_reason": "tool_use",
"content": [
{
"type": "tool_use",
"id": "toolu_02Tt",
"name": "get_travel_time",
"input": {
"origin": "home",
"destination": "Canary Wharf, London"
}
}
]
}
Вы запускаете инструмент по тому же пути проверки перед использованием, выполнения и обработки после использования, и возвращаете единый результат:
{
"role": "user",
"content": [
{ "type": "tool_result", "tool_use_id": "toolu_02Tt", "content": "{\"minutes\": 45}" }
]
}
Структура цикла совпадает с первой итерацией. Различаются лишь количество вызовов и их взаимозависимости. Именно этот принцип следует запомнить: форма цикла не меняется с каждой итерации, и он продолжается до тех пор, пока Claude не прекратит задавать вопросы.
8. Третья итерация: синтез и end_turn
При третьем вызове API Claude уже имеет в своем распоряжении все необходимые факты. Дополнительных инструментов не требуется, поэтому он записывает ответ. end_turn заменяет прежнюю причину остановки, и цикл завершается.
{
"role": "assistant",
"stop_reason": "end_turn",
"content": [
{
"type": "text",
"text": "Yes, bring an umbrella — rain is forecast in London tomorrow, around 12°C. Your meeting is at 3:00 PM in Canary Wharf, roughly 45 minutes away, so leave home by about 2:00 PM to arrive with a buffer."
}
]
}
Всего это три итерации: два параллельных вызова, зависимый вызов и синтез. Модель решает, что и когда загружать, а ваш код определяет, как происходит каждый вызов, разрешен ли он и как возвращаются результаты. Контекст увеличивается с каждым шагом, поскольку вся история пересылается заново, но три шага легко вмещаются в заданные рамки.
9. Весь цикл в коде
Всё, что было рассказано до сих пор, сводится к одному циклу в Client SDK. Он проверяет значение stop_reason, направляет каждый запрошенный инструмент через предварительные и последующие действия, отвечает на каждый вызов одним результатом и продолжает работу до завершения работы Claude.
import anthropic
client = anthropic.Anthropic()
messages = [{"role": "user", "content": user_request}]
MAX_ITERATIONS = 10
for _ in range(MAX_ITERATIONS):
response = client.messages.create(
model="claude-opus-4-8", max_tokens=1024, tools=tools, messages=messages,
)
messages.append({"role": "assistant", "content": response.content})
if response.stop_reason != "tool_use":
break # end_turn (or another reason): done
results = []
for block in (b for b in response.content if b.type == "tool_use"):
if not pre_tool_use(block): # your guard - may block the call
results.append({"type": "tool_result", "tool_use_id": block.id,
"content": "Blocked by policy.", "is_error": True})
continue
try:
output = execute(block.name, block.input) # run it (concurrently if independent)
output = post_tool_use(block, output) # redact / log / reshape
results.append({"type": "tool_result", "tool_use_id": block.id, "content": output})
except Exception as e:
results.append({"type": "tool_result", "tool_use_id": block.id,
"content": f"Error: {e}", "is_error": True})
messages.append({"role": "user", "content": results}) # one result per tool_use
print(response.content[-1].text)
В цикле описаны некоторые моменты. Вы сами храните массив messages, поскольку API не сохраняет состояние. Любой значимый параметр stop_reason, отличный от tool_use, приводит к завершению работы. Жесткий лимит количества итераций предотвращает неуправляемое поведение агента, поэтому никогда не используйте бесконечный цикл в производственной среде. Все виды сбоев, включая заблокированные запросы и исключения, возвращаются в виде значений is_error, вместо того чтобы привести к сбою текущей итерации. В готовом коде также учтены параметры max_tokens (если итерация обрывается, необходимо либо увеличить лимит, либо продолжить работу) и pause_turn (итерация, связанная с серверным инструментом, которую нужно отправить заново). Каждый из этих параметров может возникнуть в рамках одного и того же цикла.
Список проверки для запоминания
- Инструменты объявляются в массиве
toolsс указаниемname,descriptionиinput_schema, причем каждый запрос оплачивает использование токенов входных данных в соответствии с этими определениями. tool_choiceопределяет, будет ли вызываться инструмент:auto,any,toolилиnone. Режим расширенного обработки работает только при значенииautoиnone, а параметрstrict: trueобязывает аргументы полностью соответствовать схеме.- Значение
stop_reasonв виде"tool_use"передаёт управление вам. Обнаруживайте такие вызовы путём фильтрацииcontentпо условиюtype == "tool_use", а не по позиции. - Параллельное использование инструментов означает наличие нескольких блоков
tool_useв одном ответе. Порядок выполнения — будь то параллельно или последовательно — выбираете вы, а не модель. Зависимые вызовы поступают в последующих этапах. - Запустите все запрошенные инструменты и ответьте один раз: один ход пользователя, в котором сохраняется
tool_resultдля каждогоtool_use, связанного поtool_use_id, при этом массивtoolsостается неизменным. Отсутствие результата приводит к коду ошибки 400. - Вы сами отвечаете за промежутки времени между выполнениями. Проводите проверки и защиту перед запуском, а также обрабатывайте данные и удаляйте лишнее после этого. Это шаги, которые вы пишете сами, а не функции, которые настраиваются.
tool_result.contentможет быть строкой или списком. В случае сбоя установитеis_error: trueвместе с понятным сообщением, а также сохраняйте результаты краткими и структурированными.- Рассматривайте результаты работы инструментов как ненадежный контекст. Принцип минимальных привилегий, использование песочниц, очистка данных и подтверждение действий человеком — вот способы защиты от внедрения вредоносного кода.
- Цикл выполняется до момента, указанного в
end_turn, сохраняет собственное состояние и требует жесткого ограничения количества итераций.
Связанные статьи
- Agentic RAG в LangGraph: оценщики, условное маршрутизирование и резервный вариант в виде поиска в интернете — Перевод линейной схемы RAG от LangChain в StateGraph LangGraph с использованием оценщиков LLM, условных входов и рёбер, тестов, а также конца API FastAPI для отслеживания происхождения данных.
- Рабочие процессы с несколькими агентами в LangGraph: сравнение режимов «стая» и «надзиратель» — Разделение рабочего процесса LangGraph на подагентов с использованием точек контроля, затем создание финансового ассистента в режимах «стая» и «надзиратель» с анализом моментов, когда каждый из этих подходов теряет эффективность.