본문으로 건너뛰기

대화 중 도구 변경

고급

Claude가 도구 사용을 지원한 이래로 tools 배열은 대화의 수명 동안 얼어붙어 있었습니다 — 더 정확히는 캐시 엔트리의 수명 동안 얼어붙어 있었습니다. 그것을 바꾸면 프롬프트 캐시가 녹아내립니다.

이는 프롬프트 캐싱이 요청 접두어를 고정된 순서로 해시하기 때문입니다: toolssystemmessages. 도구 목록은 여러분이 보내는 어떤 것보다도 앞에 앉습니다. 도구 하나를 추가하거나 설명 하나의 이름만 바꿔도 그 지점 이후의 모든 캐시된 턴이 미스가 됩니다. 수십만 캐시된 입력 토큰이 있는 긴 에이전트 세션에서 그 "작은 편집"은 실제 돈과 새 다-초 콜드 스타트를 요구할 수 있습니다.

대화 중 도구 변경대화 중 시스템 메시지의 도구 배열 대응물입니다. 여러분은 여전히 도구 우주 전체를 tools에서 한번, 미리 선언합니다. 하지만 이제 어떤 부분집합이 실제로 모델에 제공되는지를 어떤 주어진 턴에 role: "system" 메시지 안에 tool_additiontool_removal 블록을 이어붙여 결정합니다. tools 배열 자체는 절대 바뀌지 않으므로 캐시된 접두어는 바이트-단위 동일하게 유지됩니다.

What you'll learn
  • 왜 tools[]를 편집하면 도구 섹션만이 아니라 전체 캐시가 폭발했는지
  • defer_loading, tool_addition, tool_removal이 어떻게 선언을 가용성과 분리하는지
  • 이 블록들을 나르는 시스템 메시지의 정확한 배치 규칙(대화 중 시스템 메시지의 규칙을 상속)
  • MCP 도구를 개별적으로(mcp_tool_reference) 또는 서버 전체로(mcp_toolset_reference) 참조하는 법
  • 이 베타가 대안들 — 자체 tool_choice를 가진 서브에이전트, 턴별 재전송, 외부 라우터 — 을 언제 이기는지

★ Insight ───────────────────────────────────── 두 가지가 이 기능을 조용히 중요하게 만듭니다. 첫째, Opus 5에서 캐시 가능한 최소 프롬프트가 1,024에서 512 토큰으로 떨어져서 더 작은 세션도 캐시로부터 이득을 봅니다 — 즉 작은 세션도 이제 그것을 무효화하면 마찬가지로 손해를 봅니다. 둘째, 해시에서 toolssystem 앞에 앉는다는 것은 오늘 mid-conversation-system-messages로 새 지시를 몰래 넣을 때 새 도구를 도입해야 하는 날에는 여전히 정가를 낸다는 뜻입니다. 이 베타가 마지막 구멍을 막습니다. ─────────────────────────────────────────────────

캐시 해시 문제, 한 그림으로

요청의 캐시 키는 접두어의 롤링 해시이며, 이 순서입니다:

[ tools ][ system ][ messages…, up to the breakpoint ]

캐시 히트는 브레이크포인트 앞의 모든 바이트가 최근 요청과 매치될 것을 요구합니다. 그래서:

무엇을 바꾸는가여전히 캐시에 히트하는 것다시 지불하는 것
끝에 새 user 턴 이어붙임그 턴까지의 전체 접두어새 턴만
새 대화 중 system 메시지 이어붙임그 앞의 모든 것새 시스템 메시지
최상위 system 필드 편집toolssystem + 모든 메시지
tools에 새 도구 하나 추가아무것도 아님system + 모든 메시지

마지막 행이 대화 중 도구 변경이 다시 쓰는 것입니다.

세 개의 움직이는 부품

1. defer_loading: truetools의 도구 선언에서 이것은 도구를 선언된 상태로 유지하지만 모델로부터 보류합니다. 여전히 캐시 접두어에 해시됩니다(그것이 요지 전체), 하지만 여러분이 드러낼 때까지 Claude는 그것을 호출 가능으로 보지 않습니다.

2. tool_additionrole: "system" 메시지 안의 컨텐츠 블록. defer_loading 도구를 그 턴 이후로 드러냅니다. 또한 이전 tool_removal이 철회한 도구를 재제공합니다.

3. tool_removal — 거울. 현재 제공되고 있는 도구를 그 턴 이후로 철회합니다. 이후 모든 턴이 캐시에 히트하지만 도구는 더 이상 Claude의 선택 집합에 없습니다.

tool_additiontool_removal 모두 tool 필드로 도구를 참조합니다. 세 가지 참조 형태가 합법입니다:

  • {"type": "tool_reference", "name": "get_forecast"}tools에 선언된 일반 도구.
  • {"type": "mcp_tool_reference", "server_name": "linear", "name": "create_issue"} — 단일 MCP 커넥터 도구.
  • {"type": "mcp_toolset_reference", "server_name": "linear"} — MCP 서버가 노출하는 모든 도구, 한 블록으로.

tools에 선언되지 않은 이름을 참조하면 400을 반환합니다.

최소 작동 예제

베타는 mid-conversation-tool-changes-2026-07-01 헤더와 Fable 5, Mythos 5, Opus 4.8, Opus 5 중 하나를 요구합니다. 아래: "read" 도구와 "write" 도구를 미리 선언하고, delete_file을 보류하며, 사용자가 파괴적 의도를 확인한 후에만 그것을 드러냅니다.

import anthropic

client = anthropic.Anthropic()

TOOLS = [
{
"name": "read_file",
"description": "Read a file from disk.",
"input_schema": {
"type": "object",
"properties": {"path": {"type": "string"}},
"required": ["path"],
},
},
{
# Declared but withheld. Hashed into the cache prefix so we can
# surface it later without invalidating anything.
"name": "delete_file",
"description": "Permanently delete a file from disk.",
"defer_loading": True,
"input_schema": {
"type": "object",
"properties": {"path": {"type": "string"}},
"required": ["path"],
},
},
]

messages = [
{"role": "user", "content": "Read notes.md and summarize it."},
# ...several tool_use / tool_result turns...
{"role": "user", "content": "OK, I confirm: delete notes.md."},
# Surface delete_file from this point onward. The cached prefix
# (tools + all earlier turns) still matches byte-for-byte.
{
"role": "system",
"content": [
{
"type": "tool_addition",
"tool": {"type": "tool_reference", "name": "delete_file"},
}
],
},
]

response = client.beta.messages.create(
model="claude-opus-5",
max_tokens=1024,
betas=["mid-conversation-tool-changes-2026-07-01"],
cache_control={"type": "ephemeral"},
tools=TOOLS,
messages=messages,
)

다음 요청은:

  1. tools 해시(변경 없음) → 캐시 히트.
  2. 이전 모든 턴 해시(변경 없음) → 캐시 히트.
  3. 새 user 턴 + 시스템 역할 tool_addition 블록만 새 입력.

이것을 "옛 방식"과 대조해 보세요 — 그 순간에만 delete_filetools에 떨어뜨리는 것. 그 단일 변경이 전체 접두어를 무효화했을 것입니다.

에이전트 루프에서 채택하기

Guided walkthrough1 of 6
  1. 나중에 드러낼 도구를 포함하되 defer_loading: true를 설정하세요. 요지는 캐시 접두어의 도구 섹션을 지금 얼리는 것입니다.

참조 패턴

Withhold destructive tools until the user confirms

system:
<tool_addition tool={type: "tool_reference", name: "delete_project"}>

Only append this after a user turn where the user explicitly confirmed destruction.
Never place before a "clarify what you want to delete?" turn.

Phase toolsets for a plan → execute → review loop

Phase 1 (plan): tools[] visible = { read_repo, search_web } — everything else defer_loading: true.
Phase 2 (execute): append system-role tool_addition for { edit_file, run_tests }.
Phase 3 (review): append system-role tool_removal for { edit_file }, tool_addition for { post_review_comment }.

The tools[] array never changes; only the offered set does. Cache is preserved across all three phases.

Retire an MCP server after a rate limit

On 429 from the Linear MCP connector, append:

system:
<tool_removal tool={type: "mcp_toolset_reference", server_name: "linear"}>

One block retires every tool that server exposed. Re-offer with a matching tool_addition once your backoff window expires.

Sandbox: give a subagent a strict subset

When you dispatch a subagent, do NOT create a new conversation with a smaller tools[]. Instead reuse the same tools[] (cache hit!) and open the subagent turn with a system-role tool_removal for every capability that subagent should not touch. The parent conversation can restore them on return with a matching tool_addition.

배치 규칙 (아주 중요합니다)

tool_addition / tool_removal 블록을 나르는 role: "system" 메시지는 일반 대화 중 시스템 메시지이며 그 배치 규칙을 상속합니다:

  • 첫 번째는 절대 안 됨. system 메시지는 messages의 첫 항목이 될 수 없습니다; 초기 도구 세트는 최상위 system 필드와 tools에 선언하세요.
  • user 턴이나 서버 도구 assistant 턴 뒤에 와야 함. tool_result 블록을 담은 user 메시지도 해당됩니다 — 그것이 바로 방금 도구가 반환한 것에 반응하는 슬롯입니다.
  • assistant 턴에 선행하거나 마지막 항목이어야 함.
  • tool_use와 매칭되는 tool_result 사이에는 절대 안 됨. 그것은 400입니다.

연속된 system 메시지는 합법이며 한 섹션으로 취급됩니다. 같은 content 배열에서 tool_addition, tool_removal, 일반 text 블록을 섞을 수 있습니다.

프롬프트 캐싱과의 상호작용

  • 캐싱을 명시적으로 활성화하세요. 어딘가에 cache_control 필드가 필요합니다; 최상위 자동 캐싱이 가장 간단합니다.
  • 평소처럼 안정 접두어를 캐시하세요 — 요청 간에 바뀌지 않는 마지막 블록까지.
  • 이어붙인 시스템 메시지가 캐시된 접두어 에 오기 때문에 접두어 해시를 바꾸지 않습니다.
  • 시스템 메시지가 대화에 들어가면 안정 히스토리가 되어 다음 턴에 캐시 가능해집니다.
  • tools의 모든 도구는 defer_loading: true 도구를 포함해서 최소 캐시 가능 프롬프트 길이에 카운트됩니다 — Opus 5에서 512 토큰, 대부분 다른 모델에서 1,024.

★ Insight ───────────────────────────────────── 이 설계는 에이전트 저자를 특정 규율로 밀어냅니다: 세션의 야망을 미리 선언하고, 런타임 신호로 접근을 조절하라. 이것은 고전적 함수 호출 API가 형성된 방식보다 OS 프로세스 능력이 모델링되는 방식(당신이 가진 능력 vs. 지금 행사할 수 있는 능력)에 더 가깝습니다. 이것을 중심으로 에이전트를 설계한다면 "이 에이전트가 어떤 도구를 가지고 있는가?"는 두 답 — 선언된 우주와 제공된 부분집합 — 을 가진 질문이 되고, 캐시는 따뜻하게 유지됩니다. ─────────────────────────────────────────────────

이 기능이 하지 않는 것

  • 애초에 tools에 없었던 도구를 도입할 수 있게 하지 않습니다. 모델에 제공될 수 있는 모든 도구는 첫 요청부터 tools에 존재해야 합니다. 그것은 한계가 아니라 기능 — 정확히 해시를 안정적으로 유지하는 것입니다.
  • 대화 중에 도구의 input_schemadescription을 바꿀 수 있게 하지 않습니다. 둘 중 하나라도 tools의 변경이며 캐시 미스를 유발합니다. 도구의 스키마가 진화해야 한다면 서로 다른 이름으로 두 도구를 선언하세요.
  • 오늘 Claude Sonnet 5에는 적용되지 않습니다. Sonnet 5는 대화 중 시스템 메시지를 전혀 지원하지 않으므로 이 베타가 그 위에 올라탈 수 없습니다. 그곳에서 동적 도구 세트가 필요하다면 Sonnet 계층 턴을 외부 라우터로 라우팅하세요.

다른 프로바이더가 같은 문제를 다루는 방식

프로바이더전체 접두어 재처리 없이 동적 도구 세트?
Anthropic Claude Opus/Fable/Mythos예, 이 베타를 통해.
Anthropic Claude Sonnet 5아니오 — tools를 재전송(캐시 미스)하거나 외부 감독자로 라우팅.
OpenAI GPT-5/6실질적으로 아니오. Responses/Chat Completions API에서 tools 배열을 바꾸는 것은 접두어 변경입니다; 자동 캐싱 접두어 매치가 도구 목록에서 깨지길 기대합니다. 흔한 우회: 자식이 범위 지정된 도구 배열을 가지는 부모/자식 에이전트.
Google Gemini 3OpenAI와 비슷. tools 설정이 요청의 일부입니다; 실용적 패턴은 단계별 Function Declaration 세트로, 재선언 비용을 감수.
일반적인 MCP 서버일부 호스트(Claude Code, Cursor)는 호스트 내부에서 "온디맨드 도구 로딩"을 구현하지만, 그것은 전송 수준입니다: 기저 모델은 여전히 재전송된 도구 목록을 받습니다 — 이 베타가 프로바이더 쪽에 도달하기 전까지.

크로스-모델 하니스를 짓고 있다면 "동적 도구" 행동이 어디에서나 있다고 가정하는 것이 아니라 모델별로 기능 감지하는 능력이 되도록 코드를 구성하세요.

흔한 실패 모드

  • 베타 헤더를 잊었습니다. 요청은 수용되고, tool_addition / tool_removal 블록은 시스템 메시지의 알 수 없는 컨텐츠로 취급되며, 행동은 미정의 — 종종 블록이 조용히 무시되고 Claude는 새 도구를 절대 보지 못합니다.
  • tool_usetool_result 사이에 시스템 메시지를 두었습니다. 400 invalid_request_error. tool_result를 담은 이어지는 user 턴 뒤로 옮기세요.
  • tools에 선언되지 않은 도구를 참조했습니다. 400. defer_loading: true로 선언하고 다시 시도하세요.
  • "명확화를 위해" 도구 설명을 편집했습니다. 대화 전체에 대한 캐시 미스. 세션 중 도구를 진화시키려면 새 이름으로 v2 도구를 추가하고 v1에 tool_removal, v2에 tool_addition을 쓰세요.
  • Sonnet 5에서 왜 작동하지 않는지 궁금해합니다. Sonnet 5에서는 작동하지 않습니다. 다른 계층이나 외부 라우터를 사용하세요.
아직 카드가 없습니다 — 추가해서 학습을 시작하세요. 🃏

Check yourself

0/5
  1. 왜 대화 중간에 tools[]에 새 도구 하나를 추가하면 모든 캐시된 턴이 무효화되나요?
  2. defer_loading: true는 실제로 무엇을 하나요?
  3. tool_addition 블록을 나르는 role: system 메시지는 어디에 배치되어야 하나요?
  4. 대화 중에 전체 캐시 미스 없이 도구의 input_schema를 진화시키는 올바른 방법은?
  5. 다음 중 오늘 이 기능을 지원하지 않는 Claude 모델은?

Sources & further reading