直近のターンを保持するコンパクション
オンデマンドコンパクションで会話の古いターンを要約し、最新のターンを要約の後にそのまま送信します。
「Keep-tail compaction」(末尾保持コンパクション)は、会話の最後の数ターンを要約の後にそのまま保持します。これはコンパクションループの2つの点を変更します。コンパクションリクエストにどのメッセージを含めるか、そしてブロックの後に何を送信するかです。要約から続けるの内容はすべてそのまま適用されます。
保持するターンを選択する
保持するターンを設定するパラメータはありません。履歴内で「cut point」(切り取り位置)を自分で選びます。その位置より前のメッセージはコンパクションリクエストに含まれ、その位置以降のメッセージは保持されます。
保持されたターンは完全な長さのままClaudeに戻されるため、保持する量が多いほど、コンパクションによって空く領域は少なくなります。
ツール呼び出しが未完了のまま残らない位置で切り取り、各ツール呼び出しとその結果が同じ側に来るようにしてください。送信するメッセージが、ツール呼び出しにまだ結果がないassistantターンで終わっている場合、APIはコンパクションリクエストを拒否します。
古いターンをコンパクションし、残りをブロックの後に送信する
直近のターンの末尾部分をそのまま保持するには、それらのターンをコンパクションリクエストから除外します。APIは送信されたすべてのメッセージを要約するため、古いターンのみを送信し、保持したターンの前にブロックを配置します。
保持したターンは、思考ブロックも含め、履歴にあるとおり正確に送信してください。要約をリクエストすると同様に、両方のリクエストにベータヘッダーを付けます。
次の例では、履歴に2つのターンがあり、切り取りによって2番目のターンが保持されます。コンパクションリクエストには最初のターンが含まれます。
{
"model": "claude-opus-5-5",
"max_tokens": 4096,
"messages": [
{
"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."
}
],
"compaction": { "type": "summarize" }
}次のリクエストでは、返されたブロックを最初に送信し、次に保持したターンをそのまま送信し、その後に新しいuserメッセージを送信します。要約から続けるでは、ブロックで始まるリクエストを示しています。
次のプログラムは、ループでコンパクションするのループを、最後の2ターンを保持するように変更したものです。ハイライトされた行は、元のループとの違いを示しています。
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."
KEEP_TURNS = 2
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 KEEP_TURNS < turn < len(QUESTIONS):
# 1ターンは1つのユーザーメッセージと1つのアシスタント応答で構成されるため、
# 保持されるターンはユーザーメッセージから始まります。
split = -2 * KEEP_TURNS
older, recent = history[:split], history[split:]
summary = client.beta.messages.create(
model="claude-opus-5-5",
max_tokens=4096,
system=SYSTEM,
betas=["compact-2026-09-04"],
messages=older,
compaction={"type": "summarize"},
)
if summary.stop_reason == "compaction":
history = [{"role": "assistant", "content": summary.content}, *recent]
print(f"Kept {len(recent) // 2} turns after the block")- 切り取り位置の選択: このプログラムは最後の2ターンを保持します。ここでのターンとは、1つの
userメッセージとそれに対する応答のことです。履歴を末尾から4メッセージの位置で分割するため、保持されるターンはuserメッセージから始まります。 - コンパクションのタイミングの判断: サイズチェックでは、会話のターン数がプログラムが保持する数より多いことも条件としているため、古い部分が空になることはありません。
- コンパクションリクエスト: 元のループでは履歴全体を送信しますが、このバージョンでは古いメッセージのみを送信します。
- 置き換え: 元のループでは履歴全体を返されたメッセージで置き換えますが、このバージョンの新しい履歴は、返されたメッセージの後に保持したターンが続くものになります。
stop_reasonのチェックと、置き換え後のすべてのリクエストは、元のループから変更されていません。
保持したターンの思考を有効に保つ
preserved thinking(思考の保持)に対応したモデルで思考ブロックを送り返す場合、保持したターン内の思考は、残した思考が有効なままとなる条件が満たされている間のみ有効です。そして、その条件の1つが切り取り位置を置ける場所を制限します。
このプログラムの切り取り位置(応答と次のuserメッセージの間)は、その条件を満たしています。すでに行ったリクエストの末尾での切り取りも同様です。その場合は、そのリクエストのmessagesを正確にそのままコンパクションし、それ以降に履歴に追加されたものはすべて保持してください。
Compatibility
| Supported models |
|
|---|---|
| Supported platforms |
|
Was this page helpful?