Системные сообщения и изменения инструментов в середине разговора
Изменяйте системные инструкции или доступность инструментов в ходе разговора, не делая недействительным кэшированный префикс, который им предшествовал.
Системные инструкции обычно находятся в поле верхнего уровня system, перед всеми сообщениями разговора. Это положение отлично подходит для «prompt caching» (кэширования подсказок): системная подсказка является частью стабильного префикса, поэтому последующие ходы попадают в кэш. Это плохое положение для инструкций, необходимость в которых вы обнаруживаете только в середине сессии, потому что редактирование поля верхнего уровня system изменяет самое начало подсказки и делает кэш недействительным для всего, что следует за ним.
Системные сообщения в середине разговора закрывают этот пробел. Вы добавляете сообщение {"role": "system"} в той точке разговора, где новая инструкция становится актуальной, вместо того чтобы редактировать поле верхнего уровня system. Кэшированный префикс остаётся прежним, поэтому следующий запрос по-прежнему читает его из кэша, а новая инструкция по-прежнему применяется как системная инструкция, а не как обычный пользовательский текст.
Изменения инструментов в середине разговора
Массив tools находится в хэшируемом префиксе запроса даже раньше, чем поле верхнего уровня system, поэтому его редактирование делает недействительным кэш подсказок для всего диалога. Изменения инструментов в середине диалога — это аналог системных сообщений в середине диалога для инструментов. Вместо того чтобы фиксировать список инструментов на всё время существования диалога, вы меняете набор инструментов, предлагаемых модели, между ходами: заранее объявите полный набор инструментов в tools, а затем используйте блоки tool_addition и tool_removal, чтобы предложить инструмент модели или отозвать его начиная с определённой точки диалога. Сам массив tools никогда не меняется, поэтому кэшированный префикс остаётся нетронутым. Изменения инструментов в середине диалога находятся в бета-версии и используют бета-заголовок inline-tools-2026-09-15 в Claude API.
tool_addition и tool_removal — это блоки содержимого в массиве content сообщения role: "system", и их можно сочетать с блоками text в одном сообщении. Сообщение подчиняется правилам размещения для любого системного сообщения в середине диалога, с одним дополнительным ограничением после приостановленного хода (см. Ограничения), а изменение применяется начиная с этой точки диалога. Поле tool каждого блока ссылается на инструмент, а не определяет его: {"type": "tool_reference", "name": "..."} указывает инструмент, объявленный в массиве tools запроса, а на инструменты MCP connector можно ссылаться по отдельности с помощью mcp_tool_reference (server_name и name) или как на целый набор инструментов с помощью mcp_toolset_reference (server_name). Ссылка на имя, не объявленное в tools, возвращает ошибку 400 (в Claude API — с error.details.error_code, равным tool_reference_unresolved). Вместо этого блок tool_addition может содержать полное определение инструмента, что не поддерживается заголовком mid-conversation-tool-changes-2026-07-01.
Каждый инструмент, объявленный в tools, предлагается модели с начала разговора. Исключение — инструменты, объявленные с defer_loading: true: они скрыты, пока блок tool_addition не сделает их доступными. tool_addition также снова предлагает инструмент, отозванный ранее блоком tool_removal.
Следующий запрос объявляет get_weather в tools, а затем отзывает его после первого хода пользователя с помощью блока tool_removal. Запрос отправляет бета-заголовок inline-tools-2026-09-15.
client = anthropic.Anthropic()
response = client.beta.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
betas=["inline-tools-2026-09-15"],
# Полный набор инструментов объявляется заранее и не меняется, поэтому
# кэшированный префикс остаётся нетронутым.
tools=[
{
"name": "get_weather",
"description": "Get the current weather for a location.",
"input_schema": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "City name"},
},
"required": ["location"],
},
},
],
messages=[
{
"role": "user",
"content": "Say OK.",
},
# Отзываем get_weather начиная с этого места. Блок ссылается
# на инструмент по имени, а не изменяет `tools`, поэтому прежние ходы остаются
# побайтово идентичными и кэш по-прежнему срабатывает.
{
"role": "system",
"content": [
{
"type": "tool_removal",
"tool": {"type": "tool_reference", "name": "get_weather"},
},
],
},
],
)
for block in response.content:
if block.type == "text":
print(block.text)Определение инструментов в сообщении (бета)
С бета-заголовком inline-tools-2026-09-15 блок tool_addition может определять инструмент по значению, то есть содержать его полное определение вместо ссылки. Так можно ввести инструмент, неизвестный в начале разговора или со схемой, которая меняется позже: достаточно добавить сообщение role: "system". Массив tools и все предыдущие сообщения остаются точно такими, какими были отправлены. Поэтому кэш подсказок по-прежнему срабатывает, и как новые входные данные обрабатывается только добавленное сообщение. Единственное исключение — массив tools без неотложенных инструментов — описано в правилах ниже. Этот заголовок также охватывает добавление и удаление инструментов по ссылке, поэтому отправлять ещё и mid-conversation-tool-changes-2026-07-01 не нужно.
Оберните определение в объект tool типа tool_definition. definition — это элемент tools с его обычной конфигурацией, включая cache_control и defer_loading. Это может быть, например, пользовательский инструмент или определённый Anthropic клиентский или серверный инструмент. Во время бета-периода некоторые типы инструментов (в том числе инструмент computer use) пока нельзя определить в сообщении: для них возвращается ошибка 400 с соответствующим пояснением. Объявляйте такие инструменты в tools и добавляйте по ссылке. Например, чтобы определить пользовательский инструмент в середине разговора:
{
"role": "system",
"content": [
{
"type": "tool_addition",
"tool": {
"type": "tool_definition",
"definition": {
"name": "db_query",
"description": "Run a read-only SQL query against the analytics database.",
"input_schema": {
"type": "object",
"properties": { "sql": { "type": "string" } },
"required": ["sql"]
}
}
}
}
]
}Начиная с этой позиции модель может вызывать инструмент так же, как инструмент, объявленный в tools. Повторная отправка идентичного определения ничего не меняет, поэтому клиент может безопасно отправить его снова, например при повторной попытке.
Следующий запрос сохраняет get_weather в tools и определяет db_query после первого хода пользователя:
client = anthropic.Anthropic()
response = client.beta.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
betas=["inline-tools-2026-09-15"],
# Оставляйте в `tools` хотя бы один неотложенный инструмент, чтобы инструмент, определённый
# позже, не менял начало сформированной подсказки.
tools=[
{
"name": "get_weather",
"description": "Get the current weather for a location.",
"input_schema": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "City name"},
},
"required": ["location"],
},
},
],
messages=[
{"role": "user", "content": "How many orders shipped yesterday?"},
# С этого момента определяйте db_query по значению. `tools` и
# предыдущие сообщения остаются точно такими, как были отправлены, поэтому кэш по-прежнему срабатывает.
{
"role": "system",
"content": [
{
"type": "tool_addition",
"tool": {
"type": "tool_definition",
"definition": {
"name": "db_query",
"description": "Run a read-only SQL query against the analytics database.",
"input_schema": {
"type": "object",
"properties": {"sql": {"type": "string"}},
"required": ["sql"],
},
},
},
},
],
},
],
)
for block in response.content:
if block.type == "tool_use":
print(block.name, block.input)content ответа включает блок tool_use для нового инструмента, например:
{
"type": "tool_use",
"id": "toolu_01A09q90qw90lq917835lq9",
"name": "db_query",
"input": {
"sql": "SELECT COUNT(*) FROM orders WHERE shipped_at::date = CURRENT_DATE - 1"
}
}Чтобы изменить схему инструмента или перевести серверный инструмент на более новую версию, отправьте другое определение под тем же именем. Новое определение заменяет предыдущее начиная с этой позиции. Если определение повторно использует имя инструмента другого типа, возвращается ошибка 400 с error.details.error_code, равным tool_name_conflict. Более новая версия того же инструмента не считается другим типом. tool_removal по-прежнему принимает ссылку, а удалённый инструмент позже можно снова определить или предложить.
Из того, где отображается определение, вытекает несколько правил:
- Объявляйте заранее то, что знаете. Инструмент, известный на момент первого запроса, должен находиться в
tools. Если модель пока не должна его видеть, объявите его сdefer_loading: trueи добавьте позже ссылкой в блокеtool_addition. Определяйте по значению только то, что неизвестно на момент первого запроса или меняется позже. - Оставляйте в
toolsхотя бы один неотложенный инструмент. Разговор, в массивеtoolsкоторого нет неотложенных инструментов, принимается. Однако первый инструмент, определённый в нём по значению, изменяет начало отображаемой подсказки. Из-за этого запрос, в котором он определён, полностью промахивается мимо кэша. Инструмент поиска инструментов считается неотложенным. - Датированные типы инструментов сохраняют собственные бета-заголовки. Если серверный инструмент, который вы определяете по значению, требует собственного бета-заголовка, отправляйте этот заголовок в каждом последующем запросе разговора.
cache_controlуказывается либо в блоке, либо в определении, но не в обоих местах. Он учитывается в лимите «breakpoint» (точек останова) запроса. Отложенное определение не может содержатьcache_control.
Запрос возвращает ошибку 400 с error.details.error_code, равным available_tools_limit_exceeded, если превышен любой из этих лимитов:
- После любого сообщения доступно более 10 000 отложенных инструментов.
- После любого сообщения доступно более 10 000 инструментов, определённых после первого сообщения пользователя.
- Определения инструментов, отправленные после первого сообщения пользователя и всё ещё доступные после любого сообщения, в сумме превышают 4 МБ (4 194 304 байта).
- Отображаемый текст инструментов превышает 4 МБ (4 194 304 байта).
Добавление сервера MCP в середине разговора (бета)
Чтобы добавить сервер коннектора MCP по ходу разговора, отправьте бета-заголовок mcp-client-2026-09-15 вместе с inline-tools-2026-09-15. Тогда definition в блоке tool_addition может быть объектом mcp_toolset, и инструменты сервера становятся доступными без редактирования tools. Как обычно, укажите параметры подключения к серверу в mcp_servers. Затем добавьте набор инструментов в том месте, где сервер стал доступен:
{
"role": "system",
"content": [
{
"type": "tool_addition",
"tool": {
"type": "tool_definition",
"definition": { "type": "mcp_toolset", "mcp_server_name": "calendar" }
}
}
]
}Объект mcp_toolset — тот же, который вы поместили бы в tools, включая default_config и configs. Блок tool_addition никогда не содержит URL-адрес или токен сервера: они остаются в mcp_servers.
Следующий запрос сохраняет get_weather в tools, указывает сервер календаря в mcp_servers и добавляет набор инструментов сервера после первого хода пользователя:
client = anthropic.Anthropic()
response = client.beta.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
betas=["inline-tools-2026-09-15", "mcp-client-2026-09-15"],
mcp_servers=[
{
"type": "url",
"url": "https://mcp.example.com/calendar",
"name": "calendar",
"authorization_token": "YOUR_TOKEN",
},
],
tools=[
{
"name": "get_weather",
"description": "Get the current weather for a location.",
"input_schema": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "City name"},
},
"required": ["location"],
},
},
],
messages=[
{"role": "user", "content": "What's on my calendar tomorrow?"},
# Сделать инструменты сервера календаря доступными начиная с этого момента.
# Блок указывает имя сервера; он никогда не содержит URL или токен.
{
"role": "system",
"content": [
{
"type": "tool_addition",
"tool": {
"type": "tool_definition",
"definition": {
"type": "mcp_toolset",
"mcp_server_name": "calendar",
},
},
},
],
},
],
)
# Ответ начинается с блока mcp_tool_listing для сервера календаря,
# поэтому проверяйте тип каждого блока, а не читайте content[0].
for block in response.content:
match block.type:
case "mcp_tool_listing":
print(block.mcp_server_name, [tool.name for tool in block.tools])
case "text":
print(block.text)С mcp-client-2026-09-15 ответ, для которого API получил список инструментов сервера, начинается с блоков mcp_tool_listing — по одному на каждый такой сервер. Если ваш код читает content[0], пропускайте эти блоки. Отправляйте сообщение ассистента обратно без изменений, включая этот блок. Продолжайте отправлять mcp-client-2026-09-15 в каждом запросе, который содержит такой блок. Тогда последующие запросы используют записанный список и не обращаются к серверу повторно. Чтобы закрепить набор инструментов самостоятельно, скопируйте этот список в поле tools объекта mcp_toolset, как описано в разделе Закрепление списка инструментов сервера MCP.
mcp-client-2026-09-15 включает всё, что включает mcp-client-2025-11-20, поэтому отправлять оба заголовка не нужно. Эти функции доступны в Claude API. На запросы, использующие коннектор MCP, распространяются его условия хранения данных.
Когда использовать системное сообщение в середине разговора
Кэширование подсказок хэширует префикс запроса по порядку: tools, затем system, затем messages. Для попадания в кэш требуется, чтобы префикс точно, байт в байт, совпадал с недавним запросом вплоть до точки разрыва кэша.
Такой порядок означает, что поле верхнего уровня system находится почти в самом начале хэшируемого префикса. Любое его изменение, даже добавление одного предложения, даёт другой хэш, и запрос промахивается мимо кэша для системной подсказки и каждого кэшированного сообщения после неё.
Системные сообщения в середине разговора позволяют вместо этого добавить инструкцию в конец истории сообщений. Всё, что предшествует новой инструкции, остаётся неизменным, поэтому существующая запись кэша по-прежнему совпадает, и только новое сообщение обрабатывается как свежий ввод.
Несколько ситуаций, в которых это важно:
- Изменения политики или персоны в середине сессии. Длинной агентной сессии требуется новое ограничение («с этого момента пиши весь SQL в виде параметризованных запросов») после десятков кэшированных ходов. Добавление его в поле верхнего уровня
systemпривело бы к повторной обработке всей истории. - Контекст для каждого хода, который должен быть авторитетным. Вы хотите внедрить заметку об актуальности, крайний срок сессии или изменение доступности инструментов с весом системного уровня, и это меняется слишком часто, чтобы находиться в кэшированном префиксе.
- Напоминания для каждого хода, которые не должны накапливаться. Обвязка подталкивает модель после каждой порции результатов инструментов («запрашивай независимые чтения вместе», «пользователь давно не получал от тебя ответа») и хочет, чтобы модель видела только самую новую копию. Системное сообщение, ограниченное ходом, отображается в течение одного хода, а затем ничего не стоит, без удаления чего-либо из истории.
- Изменения состояния, которые наблюдает ваше приложение. Ваше приложение замечает что-то, что Claude должен воспринимать как факт уровня оператора: файлы изменились на диске, пользователь переключил настройку автоматического одобрения, изменились доступные инструменты или оставшийся бюджет токенов упал ниже порога.
- Ввод пользователя, который не должен прерывать агентный цикл. Пользователь вводит уточнение, пока Claude ещё выполняет инструменты для предыдущего запроса. Передача его в виде системного сообщения после следующего результата инструмента позволяет Claude включить новый ввод в уже выполняемую работу, а не воспринимать его как новый запрос, на который нужно переключиться. В Claude Sonnet 5.5 и Claude Haiku 5.5 вместо этого добавляйте слова пользователя в виде блока
textпосле последнегоtool_resultв том же сообщенииuser. См. Размещение после результатов инструментов. - Переключения режимов, предоставляющие постоянные разрешения. Режим уровня сессии может использовать системное сообщение в середине разговора, чтобы предоставить постоянное согласие на дорогостоящую возможность, такую как автоматический запуск мультиагентных рабочих процессов, с коротким напоминанием каждые несколько ходов и уведомлением о выходе, когда режим выключается. Разобранный пример см. в разделе Создание режима оркестрации.
Во всех этих случаях вы могли бы поместить инструкцию в обычное сообщение user, и Claude действительно следует инструкциям, поступающим в пользовательских ходах. Разница в приоритете: сообщение user рассматривается как исходящее от конечного пользователя, тогда как сообщение system рассматривается как исходящее от вас, оператора приложения. Когда они конфликтуют, системные инструкции имеют приоритет, поэтому используйте роль system для фактов и ограничений уровня оператора, которые должны соблюдаться, даже если конечный пользователь просит о чём-то другом. Системное сообщение в середине разговора сохраняет этот приоритет уровня оператора, не платя цену промаха кэша за редактирование поля верхнего уровня system.
Как это работает
Добавьте сообщение с "role": "system" в массив messages. Для content используйте простую строку или блоки содержимого — так же, как для хода user или assistant. Инструкция применяется начиная с этой точки разговора. При конфликте инструкций более поздние системные сообщения имеют приоритет над более ранними. Системные сообщения в середине разговора также имеют приоритет над полем верхнего уровня system для следующих за ними ходов.
Поле верхнего уровня system по-прежнему подходит для инструкций, которые должны действовать на протяжении всего разговора. Системные сообщения в середине разговора используйте для инструкций, которые становятся актуальными только позже или которые нужно добавить, не делая недействительным кэшированный префикс.
Сообщение role: "system" также может содержать output_config.effort, чтобы изменить уровень effort посреди разговора. Эта возможность находится в бета-версии в Claude Fable 5.1, Claude Mythos 5.1, Claude Opus 5.5, Claude Opus 5, Claude Sonnet 5.5 и Claude Haiku 5.5 в Claude API и Google Cloud и требует бета-заголовка mid-conversation-output-config-2026-07-01. В Amazon Bedrock она находится в бета-версии в Claude Fable 5.1, Claude Mythos 5.1 и Claude Opus 5.5 с тем же бета-значением. Claude Opus 5 не поддерживает её в Amazon Bedrock. См. Уровень усилий для отдельных сообщений.
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
# Автоматическое "prompt caching" (кэширование подсказок): каждый запрос кэширует текущий диалог,
# а следующий запрос читает неизменённый префикс из кэша.
cache_control={"type": "ephemeral"},
system="You are a code review assistant. Be concise.",
messages=[
{
"role": "user",
"content": "Review process() in utils.py for performance issues.",
},
{
"role": "assistant",
"content": "The list comprehension is fine for small inputs. For large inputs, consider a generator to avoid materializing the full list.",
},
{
"role": "user",
"content": "Now review the calling code that invokes process().",
},
# Посреди сеанса рецензент понимает, что все предложения должны
# также соответствовать строгой политике типизации команды. Добавление
# инструкции здесь сохраняет прежние ходы побайтово идентичными, поэтому
# префикс, закэшированный предыдущим запросом, по-прежнему читается из кэша.
{
"role": "system",
"content": "From now on, every suggestion must include explicit type annotations.",
},
],
)
for block in response.content:
if block.type == "text":
print(block.text)Этот пример включает автоматическое кэширование с помощью поля верхнего уровня cache_control. Кэширование подсказок нужно включать явно. Если в запросе нет поля cache_control (ни автоматического, ни явной точки останова), ничего не кэшируется. Тогда каждый запрос оплачивается по обычной цене входных токенов за весь разговор. При включённом кэшировании добавление системного сообщения не меняет уже кэшированные ходы. Поэтому запрос с новой инструкцией по-прежнему считывает их из кэша, а не обрабатывает заново. Кроме того, разговор должен достигать минимальной длины кэшируемой подсказки. Такой короткий пример, как этот, её не достигает, поэтому cache_creation_input_tokens и cache_read_input_tokens остаются равными 0, пока разговор не станет длиннее.
Системное сообщение в середине разговора, имеющее содержимое (блоки text, tool_addition или tool_removal), должно непосредственно следовать за ходом user (или ходом assistant, заканчивающимся результатом серверного инструмента) и должно либо быть последней записью в messages, либо непосредственно предшествовать ходу assistant. Сообщение user, содержащее блоки tool_result, тоже подходит: в агентном цикле вы можете разместить системное сообщение сразу после результатов инструментов, перед следующим ходом Claude. Любая другая позиция для сообщения с содержимым, в том числе между блоком tool_use в ходе assistant и отвечающим на него tool_result, возвращает ошибку 400. Сообщение с пустым content, которое только задаёт output_config.effort, принимается в любом месте messages. Последовательные сообщения system оцениваются вместе, поэтому сообщение, задающее только effort, рядом с сообщением с содержимым подчиняется правилу для сообщений с содержимым.
Размещение после результатов инструментов
В агентном цикле системное сообщение размещается после сообщения user, доставляющего результаты инструментов. На большинстве моделей именно здесь ваше приложение также может передать ввод, который пользователь набрал, пока Claude работал, чтобы новый контекст был учтён без перезапуска хода (для Claude Sonnet 5.5 и Claude Haiku 5.5 см. конец этого раздела):
[
{ "role": "user", "content": "Run the test suite and fix any failures." },
{
"role": "assistant",
"content": [{ "type": "tool_use", "id": "toolu_01", "name": "run_tests", "input": {} }]
},
{
"role": "user",
"content": [
{ "type": "tool_result", "tool_use_id": "toolu_01", "content": "12 passed, 0 failed" }
]
},
{
"role": "system",
"content": "The user sent the following message while you were working: also update the changelog before you finish."
}
]Формулируйте системное содержимое как контекст, а не как команду, отменяющую указания пользователя. Изложите факт («от пользователя поступил новый ввод: X», «оставшийся бюджет токенов теперь Y») и позвольте Claude действовать на его основе. Claude обучен сопротивляться инструкциям, которые выглядят направленными против пользователя, и эта защита по-прежнему распространяется на системную роль, поэтому формулировки вроде «игнорируй то, что сказал пользователь» менее эффективны, чем изложение того, что изменилось.
Этот шаблон предназначен для передачи ввода от собственного конечного пользователя разговора. Не используйте его для передачи вывода инструментов, извлечённых документов или другого стороннего содержимого; храните это содержимое в блоках tool_result (см. Ограничения).
В Claude Sonnet 5.5 и Claude Haiku 5.5 вместо этого передавайте слова пользователя в пользовательском ходе. Эти модели обучены сопротивляться внедрению подсказок через результаты инструментов, поэтому они могут воспринять системное сообщение, размещённое сразу после результата инструмента, как недоверенный текст и проигнорировать его. Добавляйте слова пользователя в виде блока text после последнего tool_result в том же сообщении user. Уведомления обвязки, например напоминания, размещайте в отдельном системном сообщении в середине разговора после этого сообщения user и никогда не помещайте уведомление и слова пользователя в один блок:
[
{ "role": "user", "content": "Run the test suite and fix any failures." },
{
"role": "assistant",
"content": [{ "type": "tool_use", "id": "toolu_01", "name": "run_tests", "input": {} }]
},
{
"role": "user",
"content": [
{ "type": "tool_result", "tool_use_id": "toolu_01", "content": "12 passed, 0 failed" },
{ "type": "text", "text": "Also update the changelog before you finish." }
]
}
]Подробнее об этом поведении см. в разделах Подсказки для Claude Sonnet 5.5 и Составление подсказок для Claude Haiku 5.5.
Системные сообщения, ограниченные ходом
Чтобы ограничить сообщение role: "system" текущим ходом, задайте его поле clear_at. Оно принимает одно из двух значений:
"never"(по умолчанию): сообщение отображается на своей позиции в каждом запросе, который его включает. Отсутствие поля равнозначно этому значению."next_user_message": сообщение ограничено ходом. Его текст отображается, только пока после него вmessagesнет сообщенияrole: "user". Сообщение пользователя, содержащее только блокиtool_result, здесь тоже считается сообщением пользователя. Как только появляется более позднее сообщение пользователя, сообщение очищается. Оно остаётся в массиве, но ничего не отображает и не расходует входные токены — ни в этом запросе, ни во всех последующих.
Системные сообщения, ограниченные ходом, находятся в бета-версии. Включите бета-заголовок mid-conversation-system-clear-at-2026-08-21. Без него clear_at отклоняется как неизвестное поле.
{
"role": "system",
"clear_at": "next_user_message",
"content": "First privately list what you need next; then request every item that doesn't depend on another's result in this one response."
}Основное применение — напоминание для каждого хода в цикле инструментов. Добавляйте напоминание после сообщения tool_result каждый раз, когда хотите, чтобы модель его увидела, и оставляйте все более ранние копии на своих местах. Модель видит только копии, идущие после последнего сообщения пользователя, поэтому напоминание никогда не накапливается. Ничто ранее в messages не меняется, поэтому кэш подсказок продолжает совпадать. В Claude Fable 5.1, Claude Opus 5.5, Claude Sonnet 5.5 и Claude Haiku 5.5 это также сохраняет действительность последующих блоков размышлений: удаление более раннего напоминания изменило бы разговор перед этими блоками и привело бы к провалу проверки разговора, тогда как очищенное сообщение остаётся в массиве и оставляет этот разговор неизменным.
Следующий запрос — более поздний шаг агентного цикла. messages[3] отображалось в предыдущем запросе, когда было последним сообщением в массиве. Как только появляется messages[5] (более позднее сообщение пользователя), messages[3] очищается. Очищенное сообщение остаётся в массиве, поэтому разговор перед блоком размышлений в messages[4] не меняется, но модель больше не видит его текст. messages[6] и messages[7] отображаются оба, по порядку.
{
"model": "claude-fable-5-1",
"max_tokens": 16000,
"messages": [
{ "role": "user", "content": "Fix the failing test." },
{
"role": "assistant",
"content": [
{ "type": "thinking", "thinking": "", "signature": "..." },
{
"type": "tool_use",
"id": "toolu_01",
"name": "read_file",
"input": { "path": "test_auth.py" }
}
]
},
{
"role": "user",
"content": [{ "type": "tool_result", "tool_use_id": "toolu_01", "content": "..." }]
},
{
"role": "system",
"clear_at": "next_user_message",
"content": "Request independent reads in one turn."
},
{
"role": "assistant",
"content": [
{ "type": "thinking", "thinking": "", "signature": "..." },
{
"type": "tool_use",
"id": "toolu_02",
"name": "read_file",
"input": { "path": "auth.py" }
},
{
"type": "tool_use",
"id": "toolu_03",
"name": "read_file",
"input": { "path": "tokens.py" }
}
]
},
{
"role": "user",
"content": [
{ "type": "tool_result", "tool_use_id": "toolu_02", "content": "..." },
{
"type": "tool_result",
"tool_use_id": "toolu_03",
"content": "...",
"cache_control": { "type": "ephemeral" }
}
]
},
{
"role": "system",
"clear_at": "next_user_message",
"content": "Request independent reads in one turn."
},
{
"role": "system",
"clear_at": "next_user_message",
"content": "The shell exited with status 137."
}
]
}Правила для сообщений, ограниченных ходом:
- Отправляйте очищенные сообщения повторно дословно. Очищенное сообщение по-прежнему является частью истории разговора. Его пересоздание из текущего состояния (свежий подсчёт токенов, временная метка), удаление как избыточного или изменение его значения
clear_atявляется редактированием более раннего сообщения. Кэш подсказок промахивается с этой точки, а в Claude Fable 5.1, Claude Opus 5.5, Claude Sonnet 5.5 и Claude Haiku 5.5 каждый блок размышлений, созданный после него, не проходит проверку разговора. - Только текст.
content— это один или несколько блоковtext(или строка). Блокиtool_additionиtool_removalв сообщении в пределах хода возвращают ошибку 400, как иoutput_config. Для них используйте отдельное сообщениеrole: "system"безclear_at. - Никакого
cache_controlв его блоках. Очищенное сообщение никогда не входит в ключ кэша, поэтому точка останова на нём никогда не совпала бы. Вместо этого поместите точку останова на последний блок предшествующего хода пользователя, как в примере. Поле верхнего уровня автоматического кэширования пропускает сообщения в пределах хода при выборе точки останова. В запросе, который очищает сообщение, повторно используемый кэшированный префикс заканчивается на ходе пользователя перед ним, поэтому повторно обрабатывается только один ход ассистента между этим сообщением и новым сообщением пользователя. - Правила размещения по-прежнему действуют, независимо от того, очищено сообщение или нет. Сообщение в пределах хода должно следовать за ходом
user(или ходомassistant, заканчивающимся результатом серверного инструмента) и предшествовать ходуassistantили завершать массив, как любое системное сообщение в середине диалога. Сообщение, завершающее массив, отображается всегда. Сообщение, за которым непосредственно следует другое сообщениеuser, вызывает ошибку 400, а не очищается: помещайте все результаты одного раунда инструментов в одно сообщение пользователя, а напоминания — после него. - Ходы ассистента не очищают его. Предзаполненный или приостановленный ход ассистента после сообщения, а также серверный цикл инструментов не добавляют сообщения пользователя, поэтому сообщение по-прежнему отображается при таком продолжении. Чтобы напоминание оставалось в поле зрения на протяжении клиентского цикла инструментов, добавляйте его снова после каждого сообщения
tool_result. - Подсчёт токенов соответствует тому, что отображается. Очищенное сообщение ничего не добавляет к
usage.input_tokensили к подсчёту токенов. - Импортированная история. В транскрипте, который вы создаёте за один шаг (примеры few-shot, перенесённый диалог), сообщение в пределах хода, после которого уже есть ход ассистента и сообщение пользователя, очищено с первого запроса и никогда не отображается. Это правильное состояние для переносимого напоминания для каждого хода. Оставляйте
clear_atнезаданным только для сообщения, которое модель должна видеть в каждом запросе.
Ошибки валидации:
messages.3.clear_at: Extra inputs are not permitted
messages.3.clear_at: clear_at is only permitted on role 'system' messages
messages.3.clear_at: Input should be 'next_user_message' or 'never'
messages.3: a turn-scoped system message supports text blocks only (clear_at: 'next_user_message')
messages.3: output_config is not permitted on a turn-scoped system message (clear_at: 'next_user_message')
messages.3.content.0: cache_control is not permitted on a turn-scoped system message (clear_at: 'next_user_message')Первая — это ошибка, возвращаемая без бета-заголовка. В Google Cloud и Claude в Amazon Bedrock отправляйте бета-значение в заголовке anthropic-beta, как и в Claude API (см. Бета-заголовки). При использовании InvokeModel API Amazon Bedrock вместо этого поместите его в массив anthropic_beta тела запроса.
Через SDK задайте clear_at в элементе role: "system" в messages и отправьте бета-заголовок. Следующий пример добавляет напоминание в пределах хода после хода пользователя; в следующем запросе, как только появляется более позднее сообщение пользователя, напоминание остаётся в массиве, но больше не отображается:
client = anthropic.Anthropic()
response = client.beta.messages.create(
model="claude-fable-5-1",
max_tokens=4096,
messages=[
{
"role": "user",
"content": "Draft a short status update on the database migration for the team channel.",
},
# Напоминание в рамках хода: отображается в текущем ходе и удаляется, когда появляется следующее сообщение пользователя.
{
"role": "system",
"clear_at": "next_user_message",
"content": "The reader is on call: keep this reply under 50 words.",
},
],
betas=["mid-conversation-system-clear-at-2026-08-21"],
)
for block in response.content:
if block.type == "text":
print(block.text)Сочетание с кэшированием подсказок
Системные сообщения в середине разговора и кэширование подсказок спроектированы для совместного использования:
- Включайте кэширование явно. Кэширование происходит только тогда, когда запрос включает
cache_control— либо поле верхнего уровня автоматического кэширования, либо явную точку разрыва на блоке содержимого. Системное сообщение в середине разговора само по себе не создаёт запись кэша, а без включённого кэширования нет экономии, которую можно было бы сохранить. - Кэшируйте стабильный префикс как обычно. Поместите
cache_controlна последний блок, который остаётся неизменным между запросами, будь то конец поля верхнего уровняsystem, конец ваших определений инструментов или стабильная точка в истории сообщений. - Добавляйте системное сообщение после точки разрыва. Поскольку оно идёт после кэшированного префикса, оно не меняет хэш префикса, и кэш по-прежнему срабатывает.
- Системное сообщение в середине разговора само по себе кэшируемо. Как только оно оказывается в разговоре, оно становится частью стабильной истории. На следующем ходе вы можете переместить точку разрыва кэша за него (или положиться на автоматическое кэширование, чтобы оно сделало это), и системное сообщение читается из кэша, как любой другой ход.
Избегайте редактирования или удаления уже отправленного системного сообщения в середине разговора. Как и любое другое изменение более ранних сообщений, это делает кэш недействительным с этой точки и далее. В Claude Fable 5.1, Claude Opus 5.5, Claude Sonnet 5.5 и Claude Haiku 5.5 это также делает недействительными блоки размышлений во всех последующих ходах ассистента. Для указаний, которые должны применяться только к одному ходу, используйте системное сообщение в пределах хода и оставьте его на месте. Если инструкция должна меняться, добавьте новое системное сообщение, а не переписывайте старое. Последовательные системные сообщения принимаются и рассматриваются как единый системный раздел, который в целом подчиняется тому же правилу размещения.
Ограничения
- Не для первого сообщения. Сообщение
systemс содержимым не может быть первой записью вmessages. Для инструкций, действующих с самого начала, используйте поле верхнего уровняsystem. - Размещение ограничено. Сообщение
systemс содержимым (блокамиtext,tool_additionилиtool_removal) должно следовать непосредственно за ходомuser(в том числе за ходомuserс блокамиtool_result) или за ходомassistant, заканчивающимся результатом серверного инструмента. Кроме того, оно должно предшествовать ходуassistantили завершать массив. Оно не может находиться между блокомtool_useи егоtool_result. Размещение в другом месте возвращает ошибку 400.- Исключение: блоки
tool_additionиtool_removalне принимаются сразу после приостановленного ходаassistant(заканчивающегося результатом серверного инструмента), хотя блокиtextтам допустимы. Сначала возобновите приостановленный ход, а затем отправьте изменение инструментов в следующем сообщенииsystem. - Сообщение с пустым
content, которое только задаётoutput_config.effort, ничего не отображает на своей позиции и допускается в любом местеmessages, в том числе первым или между ходомassistantи ходомuser. - Несколько сообщений
systemподряд оцениваются вместе. Если рядом с сообщением, задающим только effort, добавить сообщение с текстом, на всю группу распространяется правило для сообщений с содержимым.
- Исключение: блоки
- Сообщения, ограниченные ходом, содержат только текст и отправляются повторно без изменений. Сообщение
clear_at: "next_user_message"не может содержатьtool_addition,tool_removal,output_configилиcache_control. После очистки оно должно оставаться вmessagesбайт в байт во всех последующих запросах. См. Системные сообщения, ограниченные ходом. - Не место для недоверенного содержимого. Claude рассматривает системное содержимое как инструкции оператора и следует им. Не помещайте в системное сообщение текст извне разговора, например необработанный вывод инструментов, извлечённые документы или веб-содержимое: это наделит такой текст полномочиями уровня оператора. Храните такие данные в блоках
tool_resultи продолжайте следовать рекомендациям из раздела Защита от джейлбрейков и внедрения подсказок.
Связанные материалы
Как работает кэширование, где размещать точки разрыва и как читать поля использования кэша.
Выясните, где именно разошлись два запроса, когда ожидаемое попадание в кэш не происходит.
Структура сообщений, многоходовые разговоры и поле system.
Написание эффективных подсказок и системных инструкций.
Как блоки tool_use и tool_result структурированы в массиве messages.
Was this page helpful?