대화 중간 시스템 메시지 및 도구 변경
이전에 캐시된 프리픽스를 무효화하지 않고 대화 도중에 시스템 지침이나 도구 가용성을 변경합니다.
시스템 지침은 일반적으로 대화의 모든 메시지보다 앞에 있는 최상위 system 필드에 위치합니다. 이 위치는 "prompt caching"(프롬프트 캐싱)에 매우 적합합니다. 시스템 프롬프트가 안정적인 프리픽스의 일부이므로 이후 턴에서 캐시 적중이 발생하기 때문입니다. 하지만 세션 도중에야 필요하다는 것을 알게 되는 지침에는 좋지 않은 위치입니다. 최상위 system 필드를 편집하면 프롬프트의 맨 앞부분이 변경되어 그 뒤에 오는 모든 내용에 대한 캐시가 무효화되기 때문입니다.
"Mid-conversation system message"(대화 중간 시스템 메시지)는 이러한 격차를 해소합니다. 최상위 system 필드를 편집하는 대신, 새 지침이 관련성을 갖게 되는 대화 지점에 {"role": "system"} 메시지를 추가합니다. 캐시된 프리픽스는 그대로 유지되므로 다음 요청도 여전히 캐시에서 이를 읽어오며, 새 지침은 일반 사용자 텍스트가 아닌 시스템 지침으로 적용됩니다.
대화 중간 도구 변경
tools 배열은 해시되는 요청 접두사에서 최상위 system 필드보다도 앞에 위치하므로, 이를 편집하면 대화 전체의 프롬프트 캐시가 무효화됩니다. "Mid-conversation tool changes"(대화 중간 도구 변경)는 대화 중간 시스템 메시지에 대응하는 도구 기능입니다. 대화 전체 기간 동안 도구 목록을 고정하는 대신, 턴 사이에 모델에 제공되는 도구를 변경합니다. 전체 도구 세트를 처음부터 tools에 선언한 다음, tool_addition 및 tool_removal 블록을 사용하여 대화의 특정 지점부터 모델에 도구를 제공하거나 철회합니다. tools 배열 자체는 절대 변경되지 않으므로 캐시된 접두사가 그대로 유지됩니다. 대화 중간 도구 변경은 베타로 제공되며 Claude API에서 inline-tools-2026-09-15 베타 헤더를 사용합니다.
tool_addition과 tool_removal은 role: "system" 메시지의 content 배열에 들어가는 콘텐츠 블록이며, 같은 메시지 안에서 text 블록과 함께 사용할 수 있습니다. 이 메시지는 모든 대화 중간 시스템 메시지의 배치 규칙을 따르며, 일시 중지된 턴 이후에는 추가 제한이 하나 있습니다(제한 사항 참조). 변경 사항은 대화의 해당 지점부터 적용됩니다. 각 블록의 tool 필드는 도구를 정의하는 것이 아니라 참조합니다. {"type": "tool_reference", "name": "..."}는 요청의 tools 배열에 선언된 도구의 이름을 지정하며, MCP 커넥터 도구는 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로 선언되지 않는 한 대화 시작부터 모델에 제공됩니다. defer_loading: true로 선언된 도구는 tool_addition 블록이 이를 노출할 때까지 보류됩니다. tool_addition은 이전 tool_removal이 철회한 도구를 다시 제공하기도 합니다.
다음 요청은 tools에 get_weather를 선언한 다음, 첫 번째 사용자 턴 이후 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_definition 유형의 tool 객체로 감싸세요. definition은 사용자 정의 도구나 Anthropic 정의 클라이언트 또는 서버 도구와 같은 tools 항목이며, cache_control 및 defer_loading을 포함한 일반적인 구성을 갖습니다. 베타 기간 동안 일부 도구 유형(컴퓨터 사용 도구 포함)은 아직 메시지에서 정의할 수 없으며, 그렇다는 내용의 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"
}
}도구의 스키마를 변경하거나 서버 도구를 최신 버전으로 옮기려면 같은 이름으로 다른 정의를 보내세요. 새 정의는 해당 위치부터 이전 정의를 대체합니다. 다른 유형의 도구 이름을 재사용하는 정의는 error.details.error_code가 tool_name_conflict로 설정된 400 오류를 반환합니다. 같은 도구의 최신 버전은 다른 유형으로 간주되지 않습니다. tool_removal은 여전히 참조를 사용하며, 제거된 도구는 나중에 다시 정의하거나 다시 제공할 수 있습니다.
정의가 렌더링되는 위치에 따라 몇 가지 규칙이 적용됩니다:
- 알고 있는 것은 미리 선언하세요. 첫 번째 요청 시점에 알고 있는 도구는
tools에 넣어야 하며, 모델이 아직 보지 않아야 한다면defer_loading: true와 이후의tool_addition참조를 사용하세요. 첫 번째 요청 시점에 알 수 없거나 나중에 변경되는 것만 값으로 정의하세요. tools에 지연되지 않은 도구를 최소 하나 유지하세요.tools배열에 지연되지 않은 도구가 없는 대화도 허용되지만, 값으로 정의하는 첫 번째 도구가 렌더링된 프롬프트의 시작 부분을 변경하므로 해당 요청에서 전체 캐시 미스가 한 번 발생합니다. 도구 검색 도구는 지연되지 않은 도구로 간주됩니다.- 날짜가 지정된 도구 유형은 자체 베타 헤더를 유지합니다. 값으로 정의하는 서버 도구에 자체 베타 헤더가 필요한 경우, 대화의 이후 모든 요청에 해당 헤더를 보내세요.
cache_control은 블록 또는 정의 중 한 곳에만 두고 둘 다에 두지 않으며, 요청의 중단점 제한에 포함됩니다. 지연된 정의에는cache_control을 지정할 수 없습니다.
다음 제한 중 하나라도 초과하면 요청은 error.details.error_code가 available_tools_limit_exceeded로 설정된 400 오류를 반환합니다:
- 어떤 메시지 이후에든 10,000개를 초과하는 지연된 도구가 사용 가능한 경우.
- 첫 번째 사용자 메시지 이후에 정의된 도구 중 10,000개를 초과하는 도구가 어떤 메시지 이후에든 사용 가능한 경우.
- 첫 번째 사용자 메시지 이후에 전송되어 어떤 메시지 이후에든 여전히 사용 가능한 도구 정의의 합계가 4 MB(4,194,304바이트)를 초과하는 경우.
- 렌더링된 도구 텍스트가 4 MB(4,194,304바이트)보다 큰 경우.
대화 중간에 MCP 서버 추가하기 (베타)
대화 도중에 MCP 커넥터 서버를 추가하려면 inline-tools-2026-09-15와 함께 mcp-client-2026-09-15 베타 헤더를 보내세요. 그러면 tool_addition 블록의 definition이 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 객체는 default_config 및 configs를 포함하여 tools에 넣는 것과 동일한 객체입니다. 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]을 읽는 대신 각 블록의 type을 확인하세요.
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를 계속 보내세요. 그러면 이후 요청은 서버에 다시 요청하는 대신 기록된 목록을 사용합니다. 도구 세트를 직접 고정하려면 MCP 서버의 도구 목록 고정하기에 설명된 대로 해당 목록을 mcp_toolset의 tools 필드에 복사하세요.
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에서는 대신 같은
user메시지의 마지막tool_result뒤에 사용자의 말을text블록으로 추가하세요. 도구 결과 이후 배치를 참조하세요. - 상시 권한을 부여하는 모드 전환. 세션 수준 모드는 대화 중간 시스템 메시지를 사용하여 멀티에이전트 워크플로 자동 실행과 같은 비용이 큰 기능에 대한 상시 동의를 부여할 수 있으며, 몇 턴마다 짧은 리프레셔를 보내고 모드가 꺼질 때 종료 알림을 보낼 수 있습니다. 실제 예시는 오케스트레이션 모드 구축을 참조하세요.
이 모든 경우에 지침을 일반 user 메시지에 넣을 수도 있으며, Claude는 사용자 턴에 도착하는 지침도 따릅니다. 차이점은 우선순위입니다. user 메시지는 최종 사용자로부터 온 것으로 취급되는 반면, system 메시지는 애플리케이션 운영자인 여러분으로부터 온 것으로 취급됩니다. 둘이 충돌할 때 시스템 지침이 우선하므로, 최종 사용자가 다른 것을 요청하더라도 유지되어야 하는 운영자 수준의 사실과 제약에는 system 역할을 사용하세요. 대화 중간 시스템 메시지는 최상위 system 필드를 편집하는 캐시 미스 비용을 지불하지 않으면서 운영자 수준의 우선순위를 유지합니다.
작동 방식
messages 배열에 "role": "system"이 있는 메시지를 추가하세요. content에는 user 또는 assistant 턴과 마찬가지로 일반 문자열이나 콘텐츠 블록을 사용합니다. 지침은 대화의 해당 지점부터 적용됩니다. 지침이 충돌하는 경우 나중의 시스템 메시지가 이전 시스템 메시지보다 우선하며, 대화 중간 시스템 메시지는 그 뒤에 오는 턴에 대해 최상위 system 필드보다 우선합니다.
전체 대화에 적용되어야 하는 지침에는 여전히 최상위 system 필드를 설정할 수 있습니다. 대화 중간 시스템 메시지는 나중에야 관련성을 갖게 되는 지침이나 캐시된 프리픽스를 무효화하지 않고 추가하려는 지침에 사용하세요.
role: "system" 메시지는 대화 도중에 effort 수준을 변경하기 위해 output_config.effort를 담을 수도 있습니다. 이는 Claude API 및 Google Cloud의 Claude Fable 5.1, Claude Mythos 5.1, Claude Opus 5.5, Claude Opus 5, Claude Sonnet 5.5, Claude Haiku 5.5에서 베타로 제공되며, 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에서 이를 지원하지 않습니다. 메시지별 effort를 참조하세요.
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 턴이 와야 합니다. tool_result 블록을 포함하는 user 메시지도 해당됩니다. 에이전트 루프에서는 Claude의 다음 턴 이전, 도구 결과 바로 뒤에 시스템 메시지를 배치할 수 있습니다. assistant의 tool_use 블록과 이에 응답하는 tool_result 사이를 포함하여, 콘텐츠를 포함하는 메시지를 그 외의 위치에 두면 400 오류가 반환됩니다. output_config.effort만 설정하고 content가 비어 있는 메시지는 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는 사용자에게 불리하게 작용하는 것처럼 보이는 지침에 저항하도록 훈련되어 있으며, 이 보호는 시스템 역할에도 여전히 적용됩니다. 따라서 "사용자가 말한 것을 무시하세요"와 같은 표현은 무엇이 변경되었는지 진술하는 것보다 효과가 떨어집니다.
이 패턴은 대화 자체의 최종 사용자로부터 온 입력을 전달하기 위한 것입니다. 도구 출력, 검색된 문서 또는 기타 제3자 콘텐츠를 전달하는 데 사용하지 마세요. 그러한 콘텐츠는 tool_result 블록에 유지하세요(제한 사항 참조).
Claude Sonnet 5.5 및 Claude Haiku 5.5에서는 대신 사용자 턴에서 사용자의 말을 전달하세요. 이 모델들은 도구 결과를 통한 "prompt injection"(프롬프트 인젝션)에 저항하도록 학습되었으므로, 도구 결과 바로 뒤에 배치된 시스템 메시지를 신뢰할 수 없는 텍스트로 취급하여 무시할 수 있습니다. 같은 user 메시지의 마지막 tool_result 뒤에 사용자의 말을 text 블록으로 추가하세요. 리마인더와 같은 하네스 알림은 해당 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에서는 이를 통해 이후의 thinking 블록도 유효하게 유지됩니다. 이전 리마인더를 삭제하면 해당 블록 이전의 대화가 변경되어 대화 검사에 실패하지만, 지워진 메시지는 배열에 남아 있어 해당 대화를 변경하지 않습니다.
다음 요청은 에이전트 루프의 이후 단계입니다. 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에서는 그 이후에 생성된 모든 thinking 블록이 대화 검사에 실패합니다. - 텍스트만 사용할 수 있습니다.
content는 하나 이상의text블록(또는 문자열)입니다. 턴 범위 메시지에서tool_addition및tool_removal블록은 400 오류를 반환하며,output_config도 마찬가지입니다. 이러한 항목에는clear_at이 없는 별도의role: "system"메시지를 사용하세요. - 블록에
cache_control을 넣을 수 없습니다. 지워진 메시지는 캐시 키의 일부가 되지 않으므로, 그 위에 둔 중단점은 절대 일치할 수 없습니다. 예시처럼 대신 앞선 사용자 턴의 마지막 블록에 중단점을 두세요. 최상위 자동 캐싱 필드는 중단점을 선택할 때 턴 범위 메시지를 건너뜁니다. 메시지를 지우는 요청에서 재사용 가능한 캐시된 접두사는 그 앞의 사용자 턴에서 끝나므로, 해당 메시지와 새 사용자 메시지 사이의 어시스턴트 턴 하나만 다시 처리됩니다. - 지워졌는지 여부와 관계없이 배치 규칙은 여전히 적용됩니다. 턴 범위 메시지는 다른 대화 중간 시스템 메시지와 마찬가지로
user턴(또는 서버 도구 결과로 끝나는assistant턴) 뒤에 와야 하며,assistant턴 앞에 오거나 배열의 끝이어야 합니다. 배열의 끝에 있는 메시지는 항상 렌더링됩니다. 바로 뒤에 다른user메시지가 오는 메시지는 지워진 메시지가 아니라 400 오류가 됩니다. 한 도구 라운드의 모든 결과를 하나의 사용자 메시지에 넣고 리마인더는 그 뒤에 두세요. - 어시스턴트 턴은 메시지를 지우지 않습니다. 메시지 뒤에 오는 미리 채워진(prefilled) 또는 일시 중지된 어시스턴트 턴이나 서버 측 도구 루프는 사용자 메시지를 추가하지 않으므로, 해당 연속 요청에서도 메시지가 계속 렌더링됩니다. 클라이언트 측 도구 루프 전반에 걸쳐 리마인더를 계속 보이게 하려면 각
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 in Amazon Bedrock에서는 Claude API와 마찬가지로 anthropic-beta 헤더에 베타 값을 보내세요(베타 헤더 참조). Amazon Bedrock InvokeModel API에서는 대신 요청 본문의 anthropic_beta 배열에 넣으세요.
SDK를 통해서는 messages의 role: "system" 항목에 clear_at을 설정하고 베타 헤더를 보내세요. 다음 예시는 사용자 턴 뒤에 턴 범위 리마인더를 추가합니다. 다음 요청에서 이후 사용자 메시지가 존재하게 되면 리마인더는 배열에 남아 있지만 더 이상 렌더링되지 않습니다:
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)프롬프트 캐싱과 함께 사용하기
대화 중간 시스템 메시지와 "prompt caching"(프롬프트 캐싱)은 함께 사용하도록 설계되었습니다:
- 캐싱을 명시적으로 활성화하세요. 캐싱은 요청에
cache_control이 포함된 경우에만 발생합니다. 이는 최상위 자동 캐싱 필드일 수도 있고, 콘텐츠 블록에 설정한 명시적 중단점일 수도 있습니다. 대화 중간 시스템 메시지는 그 자체로 캐시 항목을 생성하지 않습니다. 또한 캐싱이 활성화되지 않은 경우에는 보존할 절감 효과도 없습니다. - 평소처럼 안정적인 프리픽스를 캐시하세요. 요청 간에 동일하게 유지되는 마지막 블록에
cache_control을 배치하세요. 이 위치는 최상위system필드의 끝, 도구 정의의 끝, 또는 메시지 기록의 안정적인 지점일 수 있습니다. - 중단점 뒤에 시스템 메시지를 추가하세요. 시스템 메시지는 캐시된 프리픽스 뒤에 오므로 프리픽스 해시를 변경하지 않으며, 캐시도 계속 적중합니다.
- 대화 중간 시스템 메시지 자체도 캐시할 수 있습니다. 시스템 메시지가 대화에 포함되면 안정적인 기록의 일부가 됩니다. 다음 턴에서는 캐시 중단점을 그 뒤로 옮길 수 있습니다(또는 자동 캐싱이 이를 처리하도록 맡길 수 있습니다). 그러면 시스템 메시지도 다른 턴과 마찬가지로 캐시에서 읽힙니다.
이미 보낸 대화 중간 시스템 메시지를 편집하거나 제거하지 마세요. 이전 메시지에 대한 다른 변경과 마찬가지로, 그 지점부터 캐시가 무효화됩니다. Claude Fable 5.1, Claude Opus 5.5, Claude Sonnet 5.5, Claude Haiku 5.5에서는 이후 모든 어시스턴트 턴의 thinking 블록도 무효화됩니다. 한 턴에만 적용되어야 하는 안내에는 턴 범위 시스템 메시지를 사용하고 그대로 두세요. 지시를 발전시켜야 하는 경우 이전 메시지를 다시 작성하는 대신 새 시스템 메시지를 추가하세요. 연속된 시스템 메시지는 허용되며 하나의 시스템 섹션으로 취급되고, 전체적으로 동일한 배치 규칙을 따릅니다.
제한 사항
- 첫 번째 메시지로 사용할 수 없습니다. 콘텐츠가 있는
system메시지는messages의 첫 번째 항목이 될 수 없습니다. 처음부터 적용할 지침에는 최상위system필드를 사용하세요. - 배치 위치가 제한됩니다. 콘텐츠(
text,tool_addition또는tool_removal블록)가 있는system메시지는user턴(tool_result블록을 담은user턴 포함) 또는 서버 도구 결과로 끝나는assistant턴 바로 뒤에 와야 합니다. 또한assistant턴 앞에 오거나 배열의 마지막 항목이어야 합니다.tool_use블록과 그에 대한tool_result사이에는 둘 수 없습니다. 다른 위치에 두면 400 오류가 반환됩니다. 단, 한 가지 예외가 있습니다. 일시 중지된assistant턴(서버 도구 결과로 끝나는 턴) 바로 뒤에는text블록은 둘 수 있지만tool_addition및tool_removal블록은 둘 수 없습니다. 이 경우 일시 중지된 턴을 먼저 재개한 다음, 다음system메시지에서 도구 변경을 보내세요.content가 비어 있고output_config.effort만 설정하는 메시지는 해당 위치에 아무것도 렌더링하지 않으므로, 첫 번째 위치나assistant턴과user턴 사이를 포함해messages의 어디에나 둘 수 있습니다. 연속된system메시지는 하나의 그룹으로 판단됩니다. 따라서 effort만 설정하는 메시지 옆에 텍스트가 있는 메시지를 추가하면 그룹 전체에 콘텐츠 규칙이 적용됩니다. - 턴 범위 메시지는 텍스트만 담을 수 있으며 그대로 다시 보내야 합니다.
clear_at: "next_user_message"메시지에는tool_addition,tool_removal,output_config,cache_control을 넣을 수 없습니다. 또한 지워진 후에도 이후 요청의messages에 바이트 단위로 동일하게 남아 있어야 합니다. 턴 범위 시스템 메시지를 참조하세요. - 신뢰할 수 없는 콘텐츠를 넣으면 안 됩니다. Claude는 시스템 콘텐츠를 운영자 지침으로 취급하고 따릅니다. 원시 도구 출력, 검색된 문서, 웹 콘텐츠처럼 대화 외부에서 온 텍스트를 시스템 메시지에 직접 넣지 마세요. 그렇게 하면 해당 텍스트에 운영자 수준의 권한이 부여됩니다. 이러한 데이터는
tool_result블록에 두고, 탈옥 및 프롬프트 인젝션 완화 지침을 계속 따르세요.
관련 항목
캐싱 작동 방식, 중단점 배치 위치, 캐시 사용량 필드를 읽는 방법.
예상한 캐시 적중이 발생하지 않을 때 두 요청이 정확히 어디서 갈라졌는지 알아보세요.
메시지 구조, 멀티턴 대화, system 필드.
효과적인 프롬프트와 시스템 지침 작성하기.
messages 배열에서 tool_use 및 tool_result 블록이 구조화되는 방식.
Was this page helpful?