온디맨드 압축
애플리케이션이 선택한 시점에 Claude에게 대화 요약을 요청한 다음, 그 요약에서 대화를 이어갑니다.
"On-demand compaction"(온디맨드 압축)을 사용하면 대화를 요약할 시점을 애플리케이션이 결정합니다. compaction 매개변수를 포함한 요청 하나를 보내면, Claude는 응답 대신 요약을 반환합니다.
온디맨드 압축의 작동 방식
"Compaction"(압축) 요청은 대화 턴과 별개입니다. 현재 상태의 대화를 compaction 매개변수와 함께 보내면, 응답에는 compaction 블록 하나만 포함됩니다. 이 블록에는 읽을 수 있는 텍스트 형태의 요약과 "signature"(서명)가 담겨 있습니다. 이후 요청에서는 이 블록을 받은 그대로 보내세요.
그때부터 블록은 자신이 요약한 메시지를 대신합니다. 블록은 messages의 맨 앞에 오고, 요약된 메시지는 제거되며, 다음 턴이 그 뒤에 이어집니다. Claude는 해당 메시지가 있던 자리에서 요약을 보게 됩니다.
요약 요청하기
요약을 요청하는 요청과, 서명된 블록을 담은 이후의 모든 요청에 compact-2026-09-04 "beta header"(베타 헤더)를 보내세요. 모델이 온디맨드 압축을 지원하는지 확인하려면 베타 헤더와 함께 Models API를 호출하고 각 모델의 capabilities.compaction을 확인하세요. 하나의 요청에서 compaction과 context_management를 함께 사용할 수 없습니다.
현재 상태의 대화를 "compaction": {"type": "summarize"}와 함께 보내세요. API는 요청에 포함된 모든 메시지를 한 번 요약하고, 그 뒤에 응답을 생성하지 않으며, stop_reason "compaction"과 함께 블록만 반환합니다. 대화의 나머지 부분에서 사용하는 것과 동일한 system 프롬프트와 tools를 보내세요. "Summarizer"(요약기)가 이를 읽으며, "preserved thinking"(보존된 사고)를 사용하는 모델에서 블록 뒤의 턴을 유지하는 경우 해당 턴의 사고는 system과 tools가 일치할 때만 유효하게 유지됩니다. 이 예제의 대화에는 system 프롬프트나 도구가 없으므로 요청에서 둘 다 보내지 않습니다:
from anthropic.types.beta import BetaMessageParam
client = anthropic.Anthropic()
history: list[BetaMessageParam] = [
{
"role": "user",
"content": "I am building a recipe app. Help me name the main entities in the data model.",
},
{
"role": "assistant",
"content": "Start with Recipe, Ingredient, and Step. Add a RecipeIngredient entry that holds the quantity and unit for each ingredient in a recipe.",
},
{"role": "user", "content": "Good. Now suggest field names for Recipe."},
]
response = client.beta.messages.create(
model="claude-opus-5-5",
# max_tokens는 사고 과정을 포함한 호출 전체의 상한이므로 수천 토큰 정도의 여유를 두세요.
max_tokens=4096,
betas=["compact-2026-09-04"],
messages=history,
compaction={"type": "summarize"},
)
print(f"Stop reason: {response.stop_reason}"){
"id": "msg_013Zva2CMHLNnXjNJJKqJ2EF",
"type": "message",
"role": "assistant",
"model": "claude-opus-5-5",
"content": [
{
"type": "compaction",
"content": "Summary of the conversation: the user is designing the data model for a recipe app. The entities agreed so far are Recipe, Ingredient, Step, and RecipeIngredient, which holds the quantity and unit. The user then asked for field names for Recipe.",
"signature": "EuYBCkQY..."
}
],
"stop_reason": "compaction",
"usage": {
"input_tokens": 0,
"output_tokens": 0,
"iterations": [{ "type": "compaction", "input_tokens": 144, "output_tokens": 276 }]
}
}요약 호출은 요청의 모델, system, tools, 사고 설정, max_tokens를 사용합니다. 요약기는 도구 정의를 읽지만 도구를 실행하지는 않으며, 응답에는 사고가 포함되지 않습니다. max_tokens는 모델이 요약을 작성하기 전에 수행하는 사고를 포함하여 호출 전체의 상한이 되므로, 수천 개의 토큰을 허용하세요. 호출이 청구되는 방식은 압축 사용량 계산하기에서 확인할 수 있습니다.
마지막 assistant 턴이 아직 결과가 없는 도구 호출로 끝나면 API는 요청을 거부합니다. 해당 턴의 도구 결과를 먼저 보내세요. 또한 stop_sequences, 구조화된 출력의 output_config.format, 그리고 유형이 any 또는 tool인 tool_choice는 제외하세요. 이들은 요약 호출에서 아무런 효과가 없으며, API가 거부합니다. 대화는 여전히 모델의 컨텍스트 윈도우에 들어가야 하므로, 한도를 넘은 후가 아니라 넘기 전에 압축하세요.
응답을 스트리밍하면 블록은 한 번에 전체가 도착합니다. 완전한 블록을 담은 content_block_start 이벤트 하나와 그 뒤의 content_block_stop을 받으며, content_block_delta 이벤트는 없습니다. ping 이벤트는 이들 앞이나 사이에 도착할 수 있습니다.
요약에서 이어서 진행하기
기록에서 보낸 메시지를 반환된 어시스턴트 메시지로 교체하세요. compaction 블록은 signature를 포함하여 API가 반환한 그대로 유지하세요. 압축 요청을 보낸 후에 진행된 턴은 변경 없이 블록 뒤에 이어지며, 백그라운드 압축은 바로 이 점을 기반으로 합니다. 이후의 모든 요청에서 베타 헤더와 함께 블록을 맨 앞에 보내세요:
{
"model": "claude-opus-5-5",
"max_tokens": 2048,
"messages": [
{
"role": "assistant",
"content": [
{
"type": "compaction",
"content": "Summary of the conversation: the user is designing the data model for a recipe app. The entities agreed so far are Recipe, Ingredient, Step, and RecipeIngredient, which holds the quantity and unit. The user then asked for field names for Recipe.",
"signature": "EuYBCkQY..."
}
]
},
{
"role": "assistant",
"content": "For Recipe, use title, description, servings, prep_minutes, and cook_minutes. Add created_at and updated_at timestamps."
},
{ "role": "user", "content": "Now do the same for Ingredient." }
]
}이 예제는 user 턴으로 끝난 요청 샘플에서 이어집니다. 다이어그램은 요약이 작성되는 동안 진행된 턴이 없는 더 단순한 경우를 보여줍니다. 여기서 두 번째 assistant 메시지는 마지막으로 요약된 user 턴에 대한 응답입니다. 이 메시지는 요약이 작성되는 동안 도착했으므로 요약된 메시지에 포함되지 않았습니다. 블록이 여전히 맨 앞에 오기 때문에 여기서는 assistant 메시지가 두 개 연속으로 와도 괜찮습니다.
API는 블록이 있는 자리에 요약을 넣고, 이후의 모든 메시지를 변경 없이 Claude에게 전달합니다. 다음 규칙을 따르세요:
- 블록을
messages의 맨 앞에 두세요. 독립된assistant메시지로 두거나, 첫 번째 메시지의 첫 번째 콘텐츠 블록으로 둘 수 있으며, 이때 첫 번째 메시지는user메시지든assistant메시지든 상관없습니다. - 요약된 메시지를 제거하세요. 블록 앞에 남아 있는 메시지가 있으면 요청은 400 오류(
compaction_block_misplaced)를 반환합니다. - 이후의 모든 요청에서 요청당 정확히 하나의
compaction블록을 보내세요.
"Threshold compaction"(임계값 압축)은 반대 방식으로 작동합니다. 임계값 압축의 블록은 요약한 메시지 뒤에 오며, API가 해당 메시지를 대신 제거합니다. 압축 블록 다시 전달하기를 참조하세요.
Python에서는 이 페이지의 샘플처럼 client.beta.messages를 사용하세요. client.messages를 호출하고 블록을 직접 직렬화하는 경우 to_dict() 또는 model_dump(exclude_none=True)를 사용하세요. 일반 model_dump()는 블록에 citations: null과 text: null을 추가하며, API는 이를 거부합니다.
블록 뒤의 턴을 유지하고 해당 턴의 사고 블록을 다시 보내는 경우, 그 사고를 유효하게 유지하는 조건은 압축과 보존된 사고에 설명되어 있습니다.
다시 압축하기
이미 블록으로 시작하는 대화를 압축하려면 compaction을 다시 보내세요. 새 블록은 이전 요약과 그 이후의 모든 내용을 요약합니다. 그때부터는 가장 최신 블록만 보내세요.
루프에서 압축하기
다음 요청에는 응답도 함께 포함되므로, 루프는 각 턴이 끝난 후 마지막 응답의 입력 토큰과 출력 토큰을 더합니다. 그 합계가 한도를 넘고 아직 진행할 턴이 남아 있으면, 루프는 동일한 모델과 system 프롬프트로 압축 요청을 보내고, stop_reason을 확인하고, 기록을 반환된 메시지로 교체한 다음, 압축 직전의 턴을 출력합니다. 샘플의 한도인 2,500 토큰은 짧은 대화도 압축되도록 의도적으로 낮게 설정한 값입니다. 실제 입력 예산에 가깝게 설정하세요.
from anthropic.types.beta import BetaMessageParam
client = anthropic.Anthropic()
# 실제 입력 예산에 가깝게 설정하세요. 여기서는 짧은 대화도 압축되도록 낮게 설정했습니다.
COMPACT_AT_TOKENS = 2500
SYSTEM = "You help design a recipe app's data model. Keep answers short."
QUESTIONS = [
"What are the main entities in the data model?",
"Which fields should Recipe have?",
"Which fields should Ingredient have?",
"Which fields should RecipeIngredient have?",
"Which fields should Step have?",
"Which indexes should these tables have?",
"Which fields should be required?",
"Which fields should have default values?",
]
history: list[BetaMessageParam] = []
for turn, question in enumerate(QUESTIONS, start=1):
history.append({"role": "user", "content": question})
response = client.beta.messages.create(
model="claude-opus-5-5",
max_tokens=8192,
system=SYSTEM,
betas=["compact-2026-09-04"],
messages=history,
)
history.append({"role": "assistant", "content": response.content})
# 다음 요청에서 이 응답도 함께 전송되므로 이를 포함해 계산하세요.
conversation_tokens = response.usage.input_tokens + response.usage.output_tokens
if conversation_tokens > COMPACT_AT_TOKENS and turn < len(QUESTIONS):
summary = client.beta.messages.create(
model="claude-opus-5-5",
max_tokens=4096,
system=SYSTEM,
betas=["compact-2026-09-04"],
messages=history,
compaction={"type": "summarize"},
)
if summary.stop_reason == "compaction":
history = [{"role": "assistant", "content": summary.content}]
print(f"Compacted before turn {turn + 1}")코드는 블록을 찾기 전에 stop_reason을 먼저 확인합니다. 그 이유는 요약 누락 또는 오류 처리하기에서 설명합니다. 기록은 추가되는 것이 아니라 교체됩니다. 반환된 메시지는 요약에서 이어서 진행하기의 규칙에 따라 요청에 포함된 모든 메시지를 대체합니다. 요약이 반환되지 않으면 루프는 기록을 그대로 유지하고 다음 턴 이후에 다시 요청합니다.
Python, TypeScript, C#, Go, Java의 SDK "tool runner"(도구 러너)는 압축 요청을 대신 보낼 수 있습니다. 압축하기로 결정하면 러너에서 compact_before_next_turn()을 호출하세요(TypeScript와 Java에서는 compactBeforeNextTurn(), C#과 Go에서는 CompactBeforeNextTurn()). 현재 턴과 해당 도구 호출이 완료되면 러너가 압축 요청을 보내고 기록을 반환된 메시지로 교체합니다. 러너는 compact-2026-09-04 베타를 자동으로 추가하지 않으므로, 이 베타를 지정하여 러너를 생성하세요. 러너는 자체 매개변수로 요청을 구성하며 context_management는 제외합니다. 해당 매개변수에 stop_sequences, 유형이 any 또는 tool인 tool_choice, 또는 구조화된 출력의 output_config.format이 포함되어 있으면 API는 400 오류와 함께 요청을 거부합니다. 그 이유는 요약 요청하기에서 설명합니다. 러너의 context_management에 압축 편집이 있으면 러너는 압축을 거부하므로, 하나의 러너에서는 한 종류의 압축만 사용하세요.
압축 시점
완료된 턴 이후라면 언제든 압축 요청을 보낼 수 있으므로, 시점은 코드에서 결정합니다.
다음 요청의 크기를 추정하려면 루프에서처럼 마지막 응답의 usage에서 input_tokens와 output_tokens를 더하세요. 프롬프트 캐싱을 사용하는 경우 input_tokens는 마지막 캐시 중단점 이후의 토큰만 계산하므로 cache_read_input_tokens와 cache_creation_input_tokens도 더하세요. 동일한 메시지를 토큰 카운팅 엔드포인트로 보낼 수도 있습니다.
그 숫자를 모델의 컨텍스트 윈도우보다 낮게 직접 정한 한도와 비교하세요.
자체 요약 프롬프트 작성하기
instructions가 없으면 API는 자체 요약 프롬프트를 사용합니다. 비어 있지 않은 instructions 문자열(최대 16,384자)은 해당 프롬프트를 완전히 대체합니다. 예를 들면 다음과 같습니다:
{
"compaction": {
"type": "summarize",
"instructions": "Summarize this recipe app design conversation. Preserve every entity and field name agreed so far, and the user's latest open request. Do not call tools; respond with the summary text only."
}
}요약기는 instructions 유무와 관계없이 이전 사고를 포함한 전체 대화를 읽습니다. instructions에는 요약에 반드시 남겨야 할 내용을 명시하고, 모델에게 도구를 호출하지 말라고 지시하세요. 요약 호출은 다른 모든 요청과 동일한 안전장치 하에서 실행됩니다.
요약 누락 또는 오류 처리하기
요약은 요약 호출이 도구 호출 없이 텍스트와 함께 정상적으로 종료될 때만 생성됩니다. 그렇지 않은 경우에도 응답은 빈 content와 함께 200으로 반환되므로, 블록을 찾기 전에 stop_reason을 확인하세요. 이 호출도 청구되며 usage.iterations에 보고되고, 호출 자체를 수행할 수 없었던 경우에는 사용량이 0으로 보고됩니다. stop_reason은 요약 호출이 종료된 사유입니다. 어떤 경우든 요약 없이 계속 진행하고 나중에 압축할 수 있습니다.
stop_reason | 원인 | 조치 방법 |
|---|---|---|
"max_tokens" | 요약이 중간에 잘렸습니다. | 더 큰 max_tokens로 다시 보내세요. |
"model_context_window_exceeded" | 요약 프롬프트를 넣을 공간이 없었습니다. | 더 짧은 instructions 또는 더 적은 메시지로 다시 보내세요. |
"tool_use" | 모델이 요약을 작성하는 대신 도구를 호출했습니다. | 모델에게 도구를 호출하지 말라고 지시하는 instructions와 함께 다시 보내세요. |
"refusal" | 요청이 거절되었습니다. | 요약 없이 계속 진행하세요. |
"end_turn" | 호출이 텍스트를 반환하지 않았습니다. | 요약 없이 계속 진행하세요. |
요약 호출에는 다른 요청과 동일한 안전장치가 적용됩니다. "refusal" 이후에는 stop_details에서 거절의 원인이 된 정책 범주를 확인할 수 있습니다.
오류
압축 요청이나 블록을 담은 요청이 아예 실패할 수도 있습니다. 대부분의 400 오류에는 무엇을 제거하거나 다시 보내야 하는지 알려주는 메시지가 포함됩니다. 일부 오류에는 compaction_으로 시작하는 error.details.error_code도 포함됩니다. compaction과 함께 사용할 수 없는 필드와 같은 매개변수 오류에는 메시지만 포함됩니다.
| 오류 | 원인 | 조치 방법 |
|---|---|---|
529 overloaded_error, error.details.error_code compaction_unavailable | 블록을 생성하는 중이거나 다시 보낸 블록을 읽는 중에 일시적인 서버 문제가 발생했습니다. | 요청을 재시도하세요. |
400 compaction_block_misplaced | 요약된 메시지가 블록 앞에 남아 있습니다. | 해당 메시지를 제거하여 블록이 messages의 맨 앞에 오도록 하세요. |
400 compaction_signature_invalid 또는 compaction_content_mismatch | API가 반환한 후 블록의 signature 또는 content가 변경되었습니다. | signature를 포함하여 블록을 반환된 그대로 보내세요. |
| 400 | 요청에 compaction 블록이 두 개 이상 포함되어 있습니다. | 가장 최신 블록 하나만 보내세요. |
| 400 | 마지막 assistant 턴이 아직 결과가 없는 도구 호출로 끝납니다. | 해당 턴의 도구 결과를 보낸 다음 압축하세요. |
400 compaction_nothing_to_summarize | messages에 user 또는 assistant 콘텐츠가 없습니다(예: 빈 목록). | user 또는 assistant 메시지를 하나 이상 보내세요. |
압축 요청에서 발생하는 400으로, compaction 매개변수가 requires anthropic-beta: compact-2026-09-04라는 메시지가 포함됨 | 압축 요청에 베타 헤더가 누락되었습니다. | 베타 헤더를 추가하세요. 요약 요청하기를 참조하세요. |
블록을 담은 이후 요청에서 발생하는 400: compaction이 예상되는 콘텐츠 블록 유형이 아니라는 유효성 검사 오류. 메시지에 헤더에 대한 언급은 없음 | 해당 요청에 베타 헤더가 누락되었습니다. | 블록을 담은 모든 요청에 베타 헤더를 추가하세요. 요약 요청하기를 참조하세요. |
messages.0.content.0.compaction.citations: Extra inputs are not permitted와 같은 400 유효성 검사 오류 | API가 반환하지 않은 필드(예: citations: null)가 포함된 채로 블록이 다시 전송되었습니다. | 블록을 반환된 그대로 보내세요. 요약에서 이어서 진행하기를 참조하세요. |
압축 사용량 계산하기
요약 호출은 다른 요청과 마찬가지로 청구되고 속도 제한이 적용되며, usage.iterations에서 compaction 항목으로 보고됩니다. 응답이 생성되지 않았으므로 최상위 input_tokens와 output_tokens는 0입니다. 대화에서 소비한 양을 계산하려면 최상위 필드가 아니라 usage.iterations 전체를 합산하세요. 이후 요청에서 블록을 다시 보내는 데에는 추가 압축 비용이 발생하지 않습니다.
이제 대화를 압축하고 요약 누락을 처리하는 작동하는 루프가 완성되었습니다. 다음 두 페이지는 루프의 실행 방식을 바꾸며, 둘을 함께 사용할 수도 있습니다. 최근 턴을 유지하는 압축은 마지막 턴들을 원문 그대로 유지하고, 백그라운드 압축은 요약이 작성되는 동안에도 대화를 계속 진행할 수 있게 합니다. 사고 블록을 다시 보내면서 이 둘 중 하나를 사용하는 경우에는 압축과 보존된 사고가 적용됩니다.
제한 사항 및 다른 기능과의 상호작용
- 임계값 압축 및 컨텍스트 편집. 동일한 요청에서
compaction과context_management를 함께 보낼 수 없습니다. 임계값 압축(compact_20260112)은 서명된 블록을 담은 요청에서는 실행할 수 없습니다. - 프롬프트 캐싱. 블록에
cache_control을 지정하면 요약 뒤에 중단점이 설정됩니다. - 대화 중간의 시스템 메시지 및 도구 변경. 요약 범위 안에 있는
role: "system"메시지도 함께 요약되므로, 블록이 이를 대체하면 해당 메시지의 텍스트 지시 사항은 더 이상 적용되지 않습니다. 여전히 중요한 지시 사항이 있다면role: "system"메시지로 다시 명시하세요. 이 메시지는 다음 새user턴 바로 뒤에 보내고, 그 이후로는 기록에 계속 남겨 두세요. 도구 변경에 대한 내용과, 블록 뒤의 턴을 유지할 때 해당 메시지를 어디에 두어야 하는지는 시스템 프롬프트 또는 도구 변경를 참조하세요. - 작업 예산. "task budget"(작업 예산)의
remaining값(output_config.task_budget.remaining)을compaction과 함께 보내거나 블록을 담은 요청에서 보내지 마세요. 그렇게 하면 400 오류가 반환됩니다. - 토큰 카운팅. 토큰 카운팅 엔드포인트는
compaction매개변수를 무시합니다. - 요약에 담을 수 없는 콘텐츠. 요약된 메시지 안의 이미지, 문서,
container_upload블록, 가져온 URL은 블록이 해당 메시지를 대체하면 사라집니다. 이후 턴에서 여전히 필요한 내용은 다시 명시하거나 다시 업로드하세요.
Compatibility
| Supported models |
|
|---|---|
| Supported platforms |
|
Was this page helpful?