Claude Platform Docs
MessagesУправление контекстом

Системные сообщения и изменения инструментов в середине разговора

Изменяйте системные инструкции или доступность инструментов в ходе разговора, не делая недействительным кэшированный префикс, который им предшествовал.

Системные инструкции обычно находятся в поле верхнего уровня system, перед всеми сообщениями разговора. Это положение отлично подходит для «prompt caching» (кэширования подсказок): системная подсказка является частью стабильного префикса, поэтому последующие ходы попадают в кэш. Это плохое положение для инструкций, необходимость в которых вы обнаруживаете только в середине сессии, потому что редактирование поля верхнего уровня system изменяет самое начало подсказки и делает кэш недействительным для всего, что следует за ним.

Системные сообщения в середине разговора закрывают этот пробел. Вы добавляете сообщение {"role": "system"} в той точке разговора, где новая инструкция становится актуальной, вместо того чтобы редактировать поле верхнего уровня system. Кэшированный префикс остаётся прежним, поэтому следующий запрос по-прежнему читает его из кэша, а новая инструкция по-прежнему применяется как системная инструкция, а не как обычный пользовательский текст.

Изменения инструментов в середине разговора

Массив tools располагается в хэшируемом префиксе запроса ещё раньше, чем поле верхнего уровня system, поэтому его редактирование делает недействительным кэш подсказок для всего разговора. Изменения инструментов в середине разговора — это аналог системных сообщений в середине разговора для инструментов. Вместо того чтобы фиксировать список инструментов на всё время жизни разговора, вы меняете, какие инструменты предлагаются модели между ходами: объявите полный набор инструментов в tools заранее, а затем используйте блоки tool_addition и tool_removal, чтобы предложить инструмент модели или отозвать его, начиная с определённой точки разговора. Сам массив tools никогда не меняется, поэтому кэшированный префикс остаётся нетронутым.

tool_addition и tool_removal — это блоки содержимого в массиве content сообщения с role: "system", и их можно смешивать с блоками text в одном сообщении. Сообщение подчиняется тем же правилам размещения, что и любое системное сообщение в середине разговора (см. Ограничения), и изменение применяется с этой точки разговора и далее. Поле tool каждого блока ссылается на инструмент, а не определяет его: {"type": "tool_reference", "name": "..."} указывает имя инструмента, объявленного в массиве tools запроса, а на инструменты коннектора MCP можно ссылаться по отдельности с помощью mcp_tool_reference (server_name и name) или на весь набор инструментов целиком с помощью mcp_toolset_reference (server_name). Ссылка на имя, не объявленное в tools, возвращает ошибку 400.

Каждый инструмент, объявленный в tools, предлагается модели с начала разговора, если только он не объявлен с defer_loading: true, что удерживает его скрытым до тех пор, пока блок tool_addition не сделает его доступным. tool_addition также повторно предлагает инструмент, который был отозван более ранним tool_removal.

client = anthropic.Anthropic()

response = client.beta.messages.create(
    model="claude-opus-5",
    max_tokens=1024,
    betas=["mid-conversation-tool-changes-2026-07-01"],
    # Полный набор инструментов объявляется заранее и никогда не меняется,
    # поэтому кэшированный префикс остаётся нетронутым.
    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)

Изменения инструментов в середине разговора находятся в бета-версии. Чтобы использовать их, включите бета-заголовок mid-conversation-tool-changes-2026-07-01 в ваши запросы.

Когда использовать системное сообщение в середине разговора

Кэширование подсказок хэширует префикс запроса по порядку: tools, затем system, затем messages. Для попадания в кэш требуется, чтобы префикс точно, байт в байт, совпадал с недавним запросом вплоть до точки разрыва кэша.

Такой порядок означает, что поле верхнего уровня system находится почти в самом начале хэшируемого префикса. Любое его изменение, даже добавление одного предложения, даёт другой хэш, и запрос промахивается мимо кэша для системной подсказки и каждого кэшированного сообщения после неё.

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

Несколько ситуаций, в которых это важно:

  • Изменения политики или персоны в середине сессии. Длинной агентной сессии требуется новое ограничение («с этого момента пиши весь SQL в виде параметризованных запросов») после десятков кэшированных ходов. Добавление его в поле верхнего уровня system привело бы к повторной обработке всей истории.
  • Контекст для каждого хода, который должен быть авторитетным. Вы хотите внедрить заметку об актуальности, крайний срок сессии или изменение доступности инструментов с весом системного уровня, и это меняется слишком часто, чтобы находиться в кэшированном префиксе.
  • Напоминания для каждого хода, которые не должны накапливаться. Обвязка подталкивает модель после каждой порции результатов инструментов («запрашивай независимые чтения вместе», «пользователь давно не получал от тебя ответа») и хочет, чтобы модель видела только самую новую копию. Системное сообщение, ограниченное ходом, отображается в течение одного хода, а затем ничего не стоит, без удаления чего-либо из истории.
  • Изменения состояния, которые наблюдает ваше приложение. Ваше приложение замечает что-то, что Claude должен воспринимать как факт уровня оператора: файлы изменились на диске, пользователь переключил настройку автоматического одобрения, изменились доступные инструменты или оставшийся бюджет токенов упал ниже порога.
  • Пользовательский ввод, который не должен прерывать агентный цикл. Пользователь вводит уточнение, пока Claude всё ещё выполняет инструменты для предыдущего запроса. Передача его в виде системного сообщения после следующего результата инструмента позволяет Claude встроить новый ввод в работу, которую он уже выполняет, вместо того чтобы воспринимать его как новый запрос, на который нужно переключиться. См. Размещение после результатов инструментов.
  • Переключения режимов, предоставляющие постоянные разрешения. Режим уровня сессии может использовать системное сообщение в середине разговора, чтобы предоставить постоянное согласие на дорогостоящую возможность, такую как автоматический запуск мультиагентных рабочих процессов, с коротким напоминанием каждые несколько ходов и уведомлением о выходе, когда режим выключается. Разобранный пример см. в разделе Создание режима оркестрации.

Во всех этих случаях вы могли бы поместить инструкцию в обычное сообщение user, и Claude действительно следует инструкциям, поступающим в пользовательских ходах. Разница в приоритете: сообщение user рассматривается как исходящее от конечного пользователя, тогда как сообщение system рассматривается как исходящее от вас, оператора приложения. Когда они конфликтуют, системные инструкции имеют приоритет, поэтому используйте роль system для фактов и ограничений уровня оператора, которые должны соблюдаться, даже если конечный пользователь просит о чём-то другом. Системное сообщение в середине разговора сохраняет этот приоритет уровня оператора, не платя цену промаха кэша за редактирование поля верхнего уровня system.

Как это работает

Добавьте сообщение с "role": "system" в массив messages. Используйте для content простую строку или блоки содержимого, так же как для хода user или assistant. Инструкция применяется с этой точки разговора и далее. Когда инструкции конфликтуют, более поздние системные сообщения имеют приоритет над более ранними, а системные сообщения в середине разговора имеют приоритет над полем верхнего уровня system для ходов, которые следуют за ними.

Вы по-прежнему можете задавать поле верхнего уровня system для инструкций, которые должны применяться ко всему разговору. Оставьте системные сообщения в середине разговора для инструкций, которые становятся актуальными только позже или которые вы хотите добавить, не делая недействительным кэшированный префикс.

Сообщение с role: "system" также может нести output_config.effort, чтобы изменить уровень усилия начиная со следующего хода user. Это находится в бета-версии на Claude Fable 5.1, Claude Mythos 5.1 и Claude Opus 5 в Claude API и требует бета-заголовка mid-conversation-output-config-2026-07-01. См. Усилие для отдельного сообщения.

client = anthropic.Anthropic()

response = client.messages.create(
    model="claude-opus-5",
    max_tokens=1024,
    # Автоматическое кэширование подсказок: каждый запрос кэширует текущий диалог,
    # а следующий запрос читает неизменённый префикс из кэша.
    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, пока разговор не вырастет.

Системное сообщение в середине разговора должно следовать непосредственно за ходом user (или ходом assistant, заканчивающимся результатом серверного инструмента) и должно либо быть последней записью в messages, либо непосредственно предшествовать ходу assistant. Сообщение user, несущее блоки tool_result, считается: в агентном цикле вы можете разместить системное сообщение сразу после результатов инструментов, перед следующим ходом Claude. Любое другое положение, включая положение между блоком tool_use хода assistant и отвечающим на него tool_result, возвращает ошибку 400.

Размещение после результатов инструментов

В агентном цикле системное сообщение идёт после сообщения user, которое доставляет результаты инструментов. Здесь же ваше приложение может передать ввод, который пользователь набрал, пока Claude работал, чтобы новый контекст был усвоен без перезапуска хода:

[
  { "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 (см. Ограничения).

Системные сообщения, ограниченные ходом

Чтобы ограничить сообщение с 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 это также сохраняет действительность последующих блоков мышления: удаление более раннего напоминания изменило бы разговор перед этими блоками и провалило бы проверку разговора, тогда как очищенное сообщение остаётся в массиве и оставляет этот разговор неизменным.

Следующий запрос — это более поздний шаг агентного цикла. 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 каждый блок мышления, созданный после него, проваливает проверку разговора.
  • Только текст. 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')

Первая — это ошибка, возвращаемая без бета-заголовка. В Amazon Bedrock и Google Cloud передавайте бета-значение, как описано в разделе Бета-заголовки.

Через 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 это также делает недействительными блоки мышления в каждом последующем ходе ассистента. Для указаний, которые должны применяться только к одному ходу, используйте системное сообщение, ограниченное ходом, и оставьте его на месте. Если инструкция должна развиваться, добавьте новое системное сообщение, а не переписывайте старое. Последовательные системные сообщения принимаются и рассматриваются как единый системный раздел, который как целое подчиняется тому же правилу размещения.

Ограничения

  • Не для первого сообщения. Сообщение system, несущее содержимое, не может быть первой записью в messages. Используйте поле верхнего уровня system для инструкций, которые применяются с самого начала.
  • Размещение ограничено. Сообщение system, несущее содержимое (блоки text, tool_addition или tool_removal), должно следовать непосредственно за ходом user (включая ход user, несущий блоки tool_result) или ходом assistant, заканчивающимся результатом серверного инструмента, и должно предшествовать ходу assistant или завершать массив. Оно не может находиться между блоком tool_use и его tool_result. Размещение его в другом месте возвращает ошибку 400. Сообщение с пустым content, которое только задаёт output_config.effort, ничего не отображает на своей позиции и принимается в любом месте messages, в том числе первым или между ходом assistant и ходом user. Последовательные сообщения system оцениваются вместе, поэтому добавление сообщения с текстом рядом с сообщением, содержащим только усилие, заставляет всю группу подчиняться правилу для содержимого.
  • Сообщения, ограниченные ходом, содержат только текст и повторно отправляются дословно. Сообщение с 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?