Dengan "on-demand compaction" (pemadatan sesuai permintaan), aplikasi Anda yang menentukan kapan percakapan diringkas: Anda mengirim satu permintaan dengan parameter compaction, dan Haijun mengembalikan ringkasan sebagai pengganti balasan.
Cara kerja compaction sesuai permintaan
Permintaan compaction terpisah dari giliran percakapan Anda. Anda mengirim percakapan apa adanya dengan parameter compaction, dan respons berisi satu blok compaction. Blok tersebut memuat ringkasan sebagai teks yang dapat Anda baca, serta sebuah tanda tangan (signature). Kirimkan blok itu dalam permintaan berikutnya persis seperti saat diterima.
Sejak saat itu, blok tersebut menggantikan pesan-pesan yang diringkasnya. Blok ditempatkan pertama di messages, pesan-pesan yang diringkas dihapus, dan giliran Anda berikutnya menyusul setelahnya. Haijun melihat ringkasan di tempat pesan-pesan tersebut sebelumnya berada.
Meminta ringkasan
Kirim header beta compact-2026-09-04 pada permintaan yang meminta ringkasan dan pada setiap permintaan berikutnya yang membawa blok bertanda tangan. Untuk memeriksa apakah suatu model mendukung compaction sesuai permintaan, panggil Models API dengan header beta dan baca capabilities.compaction dari setiap model. Anda tidak dapat menggabungkan compaction dengan context_management dalam satu permintaan.
Kirim percakapan apa adanya dengan "compaction": {"type": "summarize"}. API meringkas setiap pesan dalam permintaan satu kali, tidak menghasilkan balasan setelahnya, dan hanya mengembalikan blok tersebut dengan stop_reason "compaction". Kirim prompt system dan tools yang sama dengan yang Anda gunakan untuk sisa percakapan. Peringkas membaca keduanya, dan jika Anda mempertahankan giliran setelah blok pada model dengan "preserved thinking" (pemikiran yang dipertahankan), thinking dalam giliran tersebut tetap valid hanya jika system dan tools cocok. Percakapan dalam contoh ini tidak memiliki prompt system maupun alat, sehingga permintaan tidak mengirim keduanya:
# max_tokens membatasi seluruh panggilan, termasuk proses thinking, jadi sediakan beberapa ribu token.
curl https://haijun.my.id/v1/messages \
-H "x-api-key: $JUGLOW_API_KEY" \
-H "juglow-version: 2023-06-01" \
-H "juglow-beta: compact-2026-09-04" \
-H "content-type: application/json" \
-d '{
"model": "haijun-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."},
{"role": "user", "content": "Good. Now suggest field names for Recipe."}
],
"compaction": {"type": "summarize"}
}' ant beta:messages create --beta compact-2026-09-04 <<'YAML'
model: haijun-opus-5-5
# max_tokens membatasi seluruh panggilan, termasuk proses thinking, jadi sediakan beberapa ribu token.
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.
- role: user
content: Good. Now suggest field names for Recipe.
compaction:
type: summarize
YAML from juglow.types.beta import BetaMessageParam
client = juglow.Juglow()
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="haijun-opus-5-5",
# max_tokens membatasi seluruh panggilan, termasuk proses thinking, jadi sediakan beberapa ribu token.
max_tokens=4096,
betas=["compact-2026-09-04"],
messages=history,
compaction={"type": "summarize"},
)
print(f"Stop reason: {response.stop_reason}") const client = new Juglow();
const history: Juglow.Beta.Messages.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." }
];
const response = await client.beta.messages.create({
model: "haijun-opus-5-5",
// max_tokens membatasi seluruh panggilan, termasuk proses thinking, jadi sediakan beberapa ribu token.
max_tokens: 4096,
betas: ["compact-2026-09-04"],
messages: history,
compaction: { type: "summarize" }
});
console.log(`Stop reason: ${response.stop_reason}`); using Juglow.Models.Beta;
using Juglow.Models.Beta.Messages;
using Model = Juglow.Models.Messages.Model;
JuglowClient client = new();
List<BetaMessageParam> history =
[
new()
{
Role = Role.User,
Content = "I am building a recipe app. Help me name the main entities in the data model.",
},
new()
{
Role = 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.",
},
new() { Role = Role.User, Content = "Good. Now suggest field names for Recipe." },
];
var response = await client.Beta.Messages.Create(new MessageCreateParams
{
Model = Model.HaijunOpus5_5,
// max_tokens membatasi seluruh panggilan, termasuk proses thinking, jadi sediakan beberapa ribu token.
MaxTokens = 4096,
Betas = [JuglowBeta.Compact2026_09_04],
Messages = history,
Compaction = new BetaCompactionConfig(), // type defaults to "summarize"
});
Console.WriteLine($"Stop reason: {response.StopReason?.Raw()}"); client := juglow.NewClient()
history := []juglow.BetaMessageParam{
juglow.NewBetaUserMessage(juglow.NewBetaTextBlock("I am building a recipe app. Help me name the main entities in the data model.")),
{
Role: juglow.BetaMessageParamRoleAssistant,
Content: []juglow.BetaContentBlockParamUnion{juglow.NewBetaTextBlock("Start with Recipe, Ingredient, and Step. Add a RecipeIngredient entry that holds the quantity and unit for each ingredient in a recipe.")},
},
juglow.NewBetaUserMessage(juglow.NewBetaTextBlock("Good. Now suggest field names for Recipe.")),
}
response, err := client.Beta.Messages.New(context.TODO(), juglow.BetaMessageNewParams{
Model: juglow.ModelHaijunOpus5_5,
// max_tokens membatasi seluruh panggilan, termasuk proses thinking, jadi sediakan beberapa ribu token.
MaxTokens: 4096,
Betas: []juglow.JuglowBeta{juglow.JuglowBetaCompact2026_09_04},
Messages: history,
Compaction: juglow.BetaCompactionConfigUnionParam{
OfSummarize: &juglow.BetaSummarizeCompactionParam{},
},
})
if err != nil {
log.Fatal(err)
}
fmt.Println("Stop reason:", response.StopReason) import com.juglow.models.beta.JuglowBeta;
import com.juglow.models.beta.messages.BetaCompactionConfig;
import com.juglow.models.beta.messages.MessageCreateParams;
void main() {
var client = JuglowOkHttpClient.fromEnv();
var params = MessageCreateParams.builder()
.model(Model.HAIJUN_OPUS_5_5)
// max_tokens membatasi seluruh panggilan, termasuk proses thinking, jadi sediakan beberapa ribu token.
.maxTokens(4096)
.addBeta(JuglowBeta.COMPACT_2026_09_04)
.addUserMessage("I am building a recipe app. Help me name the main entities in the data model.")
.addAssistantMessage("Start with Recipe, Ingredient, and Step. Add a RecipeIngredient entry that holds the quantity and unit for each ingredient in a recipe.")
.addUserMessage("Good. Now suggest field names for Recipe.")
.compaction(BetaCompactionConfig.builder().build()) // type defaults to "summarize"
.build();
var response = client.beta().messages().create(params);
response.stopReason().ifPresent(reason -> IO.println("Stop reason: " + reason));
} use Juglow\Beta\JuglowBeta;
use Juglow\Beta\Messages\BetaCompactionConfig;
use Juglow\Beta\Messages\BetaMessageParam;
use Juglow\Beta\Messages\BetaMessageParam\Role;
$client = new Client();
$history = [
BetaMessageParam::with(
role: Role::USER,
content: 'I am building a recipe app. Help me name the main entities in the data model.',
),
BetaMessageParam::with(
role: 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.',
),
BetaMessageParam::with(role: Role::USER, content: 'Good. Now suggest field names for Recipe.'),
];
$response = $client->beta->messages->create(
model: Model::HAIJUN_OPUS_5_5,
// max_tokens membatasi seluruh panggilan, termasuk proses thinking, jadi sediakan beberapa ribu token.
maxTokens: 4096,
betas: [JuglowBeta::COMPACT_2026_09_04],
messages: $history,
compaction: BetaCompactionConfig::with(), // type defaults to 'summarize'
);
echo "Stop reason: {$response->stopReason}", PHP_EOL; client = Juglow::Client.new
history = [
{
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: Juglow::Model::HAIJUN_OPUS_5_5,
# max_tokens membatasi seluruh panggilan, termasuk proses thinking, jadi sediakan beberapa ribu token.
max_tokens: 4096,
betas: [Juglow::JuglowBeta::COMPACT_2026_09_04],
messages: history,
compaction: { type: "summarize" }
)
puts "Stop reason: #{response.stop_reason}"{
"id": "msg_013Zva2CMHLNnXjNJJKqJ2EF",
"type": "message",
"role": "assistant",
"model": "haijun-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 }]
}
}Panggilan peringkasan menggunakan model, system, tools, pengaturan thinking, dan max_tokens dari permintaan. Peringkas membaca definisi alat tetapi tidak pernah menjalankan alat, dan respons tidak membawa thinking. max_tokens membatasi seluruh panggilan, termasuk thinking apa pun yang dilakukan model sebelum menulis ringkasan, jadi sediakan beberapa ribu token. Menghitung penggunaan compaction menunjukkan bagaimana panggilan tersebut ditagih.
Jika giliran assistant terakhir diakhiri dengan panggilan alat yang belum memiliki hasil, API menolak permintaan tersebut. Kirim hasil alat dari giliran itu terlebih dahulu. Selain itu, jangan sertakan stop_sequences, output_config.format untuk structured output, maupun tool_choice bertipe any atau tool. Parameter-parameter tersebut tidak berpengaruh apa pun pada panggilan peringkasan, dan API menolaknya. Percakapan tetap harus muat dalam "context window" (jendela konteks) model, jadi lakukan compaction sebelum percakapan melampauinya, bukan sesudahnya.
Saat Anda melakukan streaming respons, blok tiba secara utuh. Anda mendapatkan satu event content_block_start yang membawa blok lengkap, lalu content_block_stop, tanpa event content_block_delta. Event ping dapat tiba sebelum atau di antara keduanya.
Melanjutkan dari ringkasan
Dalam riwayat Anda, ganti pesan-pesan yang Anda kirim dengan pesan assistant yang dikembalikan. Pertahankan blok compaction persis seperti yang dikembalikan API, termasuk signature-nya. Giliran apa pun yang diambil setelah Anda mengirim permintaan compaction menyusul blok tersebut tanpa perubahan, dan hal inilah yang menjadi dasar Compaction di latar belakang. Kirim blok tersebut di urutan pertama pada setiap permintaan berikutnya, dengan header beta:
{
"model": "haijun-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." }
]
}Contoh ini melanjutkan sampel permintaan, yang berakhir pada giliran user; diagram menunjukkan kasus yang lebih sederhana, di mana tidak ada giliran yang diambil selama ringkasan ditulis. Di sini pesan assistant kedua adalah balasan untuk giliran user terakhir yang diringkas. Pesan itu tiba saat ringkasan sedang ditulis, sehingga tidak termasuk dalam pesan-pesan yang diringkas. Dua pesan assistant berturut-turut tidak menjadi masalah di sini, karena blok tetap berada di urutan pertama.
API menempatkan ringkasan di posisi blok dan meneruskan setiap pesan berikutnya ke Haijun tanpa perubahan. Ikuti aturan berikut:
- Tempatkan blok di urutan pertama dalam
messages, baik sebagai pesanassistanttersendiri maupun sebagai blok konten pertama dari pesan pertama, entah itu pesanuseratauassistant.
- Hapus pesan-pesan yang diringkas. Jika ada yang masih tersisa di depan blok, permintaan mengembalikan error 400 (
compaction_block_misplaced).
- Kirim tepat satu blok
compactionper permintaan, pada setiap permintaan berikutnya.
Warning: Dua kesalahan dalam penukaran ini tidak memunculkan error. Jika pesan yang diringkas masih tersisa setelah blok, API mengirimkannya lagi ke Haijun. Jika permintaan berikutnya tidak menyertakan blok, Haijun tidak mendapatkan ringkasan.
Compaction ambang batas bekerja sebaliknya: bloknya mengikuti pesan-pesan yang diringkasnya, dan API menghapus pesan-pesan tersebut untuk Anda. Lihat Mengirimkan kembali blok compaction.
Di Python, gunakan client.beta.messages, seperti yang dilakukan sampel di halaman ini. Jika Anda memanggil client.messages dan menyerialisasi blok sendiri, gunakan to_dict() atau model_dump(exclude_none=True): model_dump() biasa menambahkan citations: null dan text: null ke blok, dan API menolaknya.
Jika Anda mempertahankan giliran setelah blok dan mengirim kembali blok thinking-nya, syarat-syarat yang menjaga thinking tersebut tetap valid dijelaskan di Compaction dan pemikiran yang dipertahankan.
Melakukan compaction lagi
Untuk melakukan compaction pada percakapan yang sudah diawali dengan sebuah blok, kirim compaction lagi. Blok baru meringkas ringkasan lama beserta semua yang ada setelahnya. Sejak saat itu, kirim hanya blok terbaru.
Compaction dalam loop
Setelah setiap giliran, loop menjumlahkan token input dan output dari respons terakhir, karena permintaan berikutnya juga mengirim balasan tersebut. Ketika total itu melewati batas dan masih ada giliran lain yang akan datang, loop mengirim permintaan compaction dengan model dan prompt system yang sama, memeriksa stop_reason, mengganti riwayatnya dengan pesan yang dikembalikan, dan mencetak giliran yang didahului oleh compaction tersebut. Batas sampel sebesar 2.500 token sengaja dibuat rendah, agar percakapan yang singkat pun mengalami compaction. Atur batas Anda mendekati anggaran input Anda yang sebenarnya.
from juglow.types.beta import BetaMessageParam
client = juglow.Juglow()
# Atur nilai ini mendekati anggaran input Anda yang sebenarnya. Di sini sengaja rendah agar percakapan singkat pun dipadatkan.
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="haijun-opus-5-5",
max_tokens=8192,
system=SYSTEM,
betas=["compact-2026-09-04"],
messages=history,
)
history.append({"role": "assistant", "content": response.content})
# Permintaan berikutnya juga mengirim balasan ini, jadi ikut hitung.
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="haijun-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}") const client = new Juglow();
// Atur nilai ini mendekati anggaran input Anda yang sebenarnya. Di sini nilainya rendah agar percakapan singkat pun dipadatkan.
const compactAtTokens = 2500;
const systemPrompt = "You help design a recipe app's data model. Keep answers short.";
const 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?"
];
let history: Juglow.Beta.Messages.BetaMessageParam[] = [];
for (const [index, question] of questions.entries()) {
const turn = index + 1;
history.push({ role: "user", content: question });
const response = await client.beta.messages.create({
model: "haijun-opus-5-5",
max_tokens: 8192,
system: systemPrompt,
betas: ["compact-2026-09-04"],
messages: history
});
history.push({ role: "assistant", content: response.content });
// Permintaan berikutnya juga mengirim balasan ini, jadi ikut hitung.
const conversationTokens = response.usage.input_tokens + response.usage.output_tokens;
if (conversationTokens > compactAtTokens && turn < questions.length) {
const summary = await client.beta.messages.create({
model: "haijun-opus-5-5",
max_tokens: 4096,
system: systemPrompt,
betas: ["compact-2026-09-04"],
messages: history,
compaction: { type: "summarize" }
});
if (summary.stop_reason === "compaction") {
history = [{ role: "assistant", content: summary.content }];
console.log(`Compacted before turn ${turn + 1}`);
}
}
} using Juglow.Models.Beta;
using Juglow.Models.Beta.Messages;
using Model = Juglow.Models.Messages.Model;
JuglowClient client = new();
// Atur nilai ini mendekati anggaran input Anda yang sebenarnya. Di sini nilainya rendah agar percakapan singkat pun dipadatkan.
const int CompactAtTokens = 2500;
const string SystemPrompt = "You help design a recipe app's data model. Keep answers short.";
string[] 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?",
];
List<BetaMessageParam> history = [];
foreach (var (index, question) in questions.Index())
{
var turn = index + 1;
history.Add(new() { Role = Role.User, Content = question });
var response = await client.Beta.Messages.Create(new MessageCreateParams
{
Model = Model.HaijunOpus5_5,
MaxTokens = 8192,
System = SystemPrompt,
Betas = [JuglowBeta.Compact2026_09_04],
Messages = history,
});
history.Add(new()
{
Role = Role.Assistant,
Content = response.Content.Select(block => new BetaContentBlockParam(block.Json)).ToList(),
});
// Permintaan berikutnya juga mengirim balasan ini, jadi ikut hitung balasan tersebut.
var conversationTokens = response.Usage.InputTokens + response.Usage.OutputTokens;
if (conversationTokens > CompactAtTokens && turn < questions.Length)
{
var summary = await client.Beta.Messages.Create(new MessageCreateParams
{
Model = Model.HaijunOpus5_5,
MaxTokens = 4096,
System = SystemPrompt,
Betas = [JuglowBeta.Compact2026_09_04],
Messages = history,
Compaction = new BetaCompactionConfig(), // type defaults to "summarize"
});
if (summary.StopReason == BetaStopReason.Compaction)
{
history =
[
new()
{
Role = Role.Assistant,
Content = summary.Content.Select(block => new BetaContentBlockParam(block.Json)).ToList(),
},
];
Console.WriteLine($"Compacted before turn {turn + 1}");
}
}
} ctx := context.Background()
client := juglow.NewClient()
// Atur nilai ini mendekati anggaran input Anda yang sebenarnya. Di sini nilainya rendah agar percakapan singkat pun dipadatkan.
const compactAtTokens = 2500
system := []juglow.BetaTextBlockParam{{Text: "You help design a recipe app's data model. Keep answers short."}}
questions := []string{
"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?",
}
var history []juglow.BetaMessageParam
for i, question := range questions {
turn := i + 1
history = append(history, juglow.NewBetaUserMessage(juglow.NewBetaTextBlock(question)))
response, err := client.Beta.Messages.New(ctx, juglow.BetaMessageNewParams{
Model: juglow.ModelHaijunOpus5_5,
MaxTokens: 8192,
System: system,
Betas: []juglow.JuglowBeta{juglow.JuglowBetaCompact2026_09_04},
Messages: history,
})
if err != nil {
log.Fatal(err)
}
history = append(history, response.ToParam())
// Permintaan berikutnya juga mengirim balasan ini, jadi ikut hitung.
conversationTokens := response.Usage.InputTokens + response.Usage.OutputTokens
if conversationTokens > compactAtTokens && turn < len(questions) {
summary, err := client.Beta.Messages.New(ctx, juglow.BetaMessageNewParams{
Model: juglow.ModelHaijunOpus5_5,
MaxTokens: 4096,
System: system,
Betas: []juglow.JuglowBeta{juglow.JuglowBetaCompact2026_09_04},
Messages: history,
Compaction: juglow.BetaCompactionConfigUnionParam{
OfSummarize: &juglow.BetaSummarizeCompactionParam{},
},
})
if err != nil {
log.Fatal(err)
}
if summary.StopReason == juglow.BetaStopReasonCompaction {
history = []juglow.BetaMessageParam{summary.ToParam()}
fmt.Printf("Compacted before turn %d\n", turn+1)
}
}
} import com.juglow.models.beta.JuglowBeta;
import com.juglow.models.beta.messages.BetaCompactionConfig;
import com.juglow.models.beta.messages.BetaMessageParam;
import com.juglow.models.beta.messages.BetaStopReason;
import com.juglow.models.beta.messages.MessageCreateParams;
// Atur nilai ini mendekati anggaran input Anda yang sebenarnya. Di sini nilainya rendah agar percakapan singkat pun dipadatkan.
static final long COMPACT_AT_TOKENS = 2500;
static final String SYSTEM = "You help design a recipe app's data model. Keep answers short.";
void main() {
var client = JuglowOkHttpClient.fromEnv();
var questions = List.of(
"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?"
);
var history = new ArrayList<BetaMessageParam>();
for (int turn = 1; turn <= questions.size(); turn++) {
history.add(BetaMessageParam.builder()
.role(BetaMessageParam.Role.USER)
.content(questions.get(turn - 1))
.build());
var params = MessageCreateParams.builder()
.model(Model.HAIJUN_OPUS_5_5)
.maxTokens(8192)
.system(SYSTEM)
.addBeta(JuglowBeta.COMPACT_2026_09_04)
.messages(history)
.build();
var response = client.beta().messages().create(params);
history.add(response.toParam());
// Permintaan berikutnya juga mengirim balasan ini, jadi ikut hitung balasan ini.
long conversationTokens = response.usage().inputTokens() + response.usage().outputTokens();
if (conversationTokens > COMPACT_AT_TOKENS && turn < questions.size()) {
var summaryParams = MessageCreateParams.builder()
.model(Model.HAIJUN_OPUS_5_5)
.maxTokens(4096)
.system(SYSTEM)
.addBeta(JuglowBeta.COMPACT_2026_09_04)
.messages(history)
.compaction(BetaCompactionConfig.builder().build()) // type defaults to "summarize"
.build();
var summary = client.beta().messages().create(summaryParams);
if (summary.stopReason().map(BetaStopReason.COMPACTION::equals).orElse(false)) {
history.clear();
history.add(summary.toParam());
IO.println("Compacted before turn " + (turn + 1));
}
}
}
} use Juglow\Beta\JuglowBeta;
use Juglow\Beta\Messages\BetaCompactionConfig;
use Juglow\Beta\Messages\BetaMessageParam;
use Juglow\Beta\Messages\BetaMessageParam\Role;
use Juglow\Beta\Messages\BetaStopReason;
$client = new Client();
// Atur nilai ini mendekati anggaran input Anda yang sebenarnya. Di sini dibuat rendah agar percakapan singkat pun dipadatkan.
const COMPACT_AT_TOKENS = 2500;
const 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 = [];
foreach ($questions as $index => $question) {
$turn = $index + 1;
$history[] = BetaMessageParam::with(role: Role::USER, content: $question);
$response = $client->beta->messages->create(
model: Model::HAIJUN_OPUS_5_5,
maxTokens: 8192,
system: SYSTEM,
betas: [JuglowBeta::COMPACT_2026_09_04],
messages: $history,
);
$history[] = BetaMessageParam::with(role: Role::ASSISTANT, content: $response->content);
// Permintaan berikutnya juga mengirim balasan ini, jadi ikut hitung.
$conversationTokens = $response->usage->inputTokens + $response->usage->outputTokens;
if ($conversationTokens > COMPACT_AT_TOKENS && $turn < count($questions)) {
$summary = $client->beta->messages->create(
model: Model::HAIJUN_OPUS_5_5,
maxTokens: 4096,
system: SYSTEM,
betas: [JuglowBeta::COMPACT_2026_09_04],
messages: $history,
compaction: new BetaCompactionConfig(), // type defaults to 'summarize'
);
if ($summary->stopReason === BetaStopReason::COMPACTION->value) {
$history = [BetaMessageParam::with(role: Role::ASSISTANT, content: $summary->content)];
printf("Compacted before turn %d\n", $turn + 1);
}
}
} client = Juglow::Client.new
# Atur nilai ini mendekati anggaran input Anda yang sebenarnya. Di sini sengaja rendah agar percakapan singkat pun dipadatkan.
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 = []
questions.each.with_index(1) do |question, turn|
history << { role: "user", content: question }
response = client.beta.messages.create(
model: Juglow::Model::HAIJUN_OPUS_5_5,
max_tokens: 8192,
system_: SYSTEM,
betas: [Juglow::JuglowBeta::COMPACT_2026_09_04],
messages: history
)
history << { role: "assistant", content: response.content }
# Permintaan berikutnya juga mengirim balasan ini, jadi ikut hitung.
conversation_tokens = response.usage.input_tokens + response.usage.output_tokens
if conversation_tokens > COMPACT_AT_TOKENS && turn < questions.length
summary = client.beta.messages.create(
model: Juglow::Model::HAIJUN_OPUS_5_5,
max_tokens: 4096,
system_: SYSTEM,
betas: [Juglow::JuglowBeta::COMPACT_2026_09_04],
messages: history,
compaction: { type: "summarize" }
)
if summary.stop_reason == :compaction
history = [{ role: "assistant", content: summary.content }]
puts "Compacted before turn #{turn + 1}"
end
end
endPemeriksaan stop_reason dilakukan sebelum kode mencari blok; Menangani ringkasan yang tidak ada atau error menjelaskan alasannya. Riwayat diganti, bukan ditambahkan: pesan yang dikembalikan menggantikan setiap pesan yang dibawa permintaan, sesuai aturan di Melanjutkan dari ringkasan. Ketika tidak ada ringkasan yang dikembalikan, loop mempertahankan riwayatnya dan meminta lagi setelah giliran berikutnya.
Tool runner SDK di Python, TypeScript, C#, Go, Java, PHP, dan Ruby dapat mengirim permintaan compaction untuk Anda. Saat Anda memutuskan untuk melakukan compaction, panggil compact_before_next_turn() (typescript, java, php: compactBeforeNextTurn(); csharp, go: CompactBeforeNextTurn()) pada runner. Setelah giliran saat ini dan panggilan alatnya selesai, runner mengirim permintaan compaction dan mengganti riwayatnya dengan pesan yang dikembalikan. Buat runner dengan beta compact-2026-09-04, karena runner tidak menambahkannya.
Runner menyusun permintaan compaction dari parameternya sendiri dan tidak menyertakan context_management. Runner juga tidak menyertakan stop_sequences, tool_choice bertipe any atau tool, dan output_config.format untuk structured output, yang ditolak API pada permintaan compaction. Meminta ringkasan menjelaskan alasannya. Runner mengirimkannya lagi pada permintaan-permintaan berikutnya. Versi SDK sebelum Python 1.8.0, TypeScript 0.128.0, C# 12.50.0, Go 1.75.0, dan Java 2.65.0 juga mengirimkannya pada permintaan compaction. Pada versi-versi tersebut, runner yang menetapkan salah satu parameter ini akan mendapatkan error 400. Runner mengirim task budget (anggaran tugas) tanpa perubahan. Jika output_config.task_budget menetapkan remaining, permintaan compaction mengembalikan error 400, jadi biarkan remaining tidak ditetapkan, seperti yang dijelaskan di Batasan dan interaksi dengan fitur lain. Runner menolak melakukan compaction selama context_management-nya memiliki edit compaction, jadi gunakan satu jenis compaction saja pada sebuah runner.
Kapan melakukan compaction
Anda dapat mengirim permintaan compaction setelah giliran mana pun yang telah selesai, sehingga kode Anda yang menentukan kapan.
Untuk memperkirakan seberapa besar permintaan berikutnya, jumlahkan input_tokens dan output_tokens dari usage respons terakhir, seperti yang dilakukan loop. Dengan "prompt caching" (caching prompt), input_tokens hanya menghitung token setelah cache breakpoint terakhir, jadi tambahkan juga cache_read_input_tokens dan cache_creation_input_tokens. Anda juga dapat mengirim pesan yang sama ke endpoint penghitungan token.
Bandingkan angka tersebut dengan batas yang Anda pilih, di bawah jendela konteks model.
Menulis prompt peringkasan Anda sendiri
Tanpa instructions, API menggunakan prompt peringkasannya sendiri. String instructions yang tidak kosong (hingga 16.384 karakter) menggantikan prompt tersebut sepenuhnya. Sebagai contoh:
{
"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."
}
}Peringkas membaca seluruh percakapan, termasuk thinking sebelumnya, baik dengan maupun tanpa instructions. Dalam instructions Anda, sebutkan apa yang harus dipertahankan oleh ringkasan dan minta model untuk tidak memanggil alat. Panggilan peringkasan berjalan di bawah perlindungan yang sama seperti permintaan lainnya.
Menangani ringkasan yang tidak ada atau error
Ringkasan hanya dihasilkan ketika panggilan peringkasan berakhir secara normal dengan teks dan tanpa panggilan alat. Jika tidak, respons tetap berstatus 200 dengan content kosong, jadi periksa stop_reason sebelum Anda mencari blok. Panggilan tersebut tetap ditagih dan dilaporkan di usage.iterations, dengan penggunaan nol jika tidak ada panggilan yang dapat dilakukan. stop_reason adalah alasan berhentinya panggilan peringkasan. Dalam setiap kasus, Anda dapat melanjutkan tanpa ringkasan dan melakukan compaction nanti.
stop_reason | Penyebab | Yang harus dilakukan |
|---|---|---|
"max_tokens" | Ringkasan terpotong. | Kirim ulang dengan max_tokens yang lebih besar. |
"model_context_window_exceeded" | Tidak ada ruang untuk prompt peringkasan. | Kirim ulang dengan instructions yang lebih pendek atau pesan yang lebih sedikit. |
"tool_use" | Model memanggil alat alih-alih menulis ringkasan. | Kirim ulang dengan instructions yang meminta model untuk tidak memanggil alat. |
"refusal" | Permintaan ditolak. | Lanjutkan tanpa ringkasan. |
"end_turn" | Panggilan tidak mengembalikan teks. | Lanjutkan tanpa ringkasan. |
Panggilan peringkasan tunduk pada perlindungan yang sama seperti permintaan Anda yang lain. Setelah "refusal", stop_details mengidentifikasi kategori kebijakan yang mendasarinya.
Error
Permintaan compaction, atau permintaan yang membawa blok, juga dapat gagal sepenuhnya. Sebagian besar error 400 memiliki pesan yang menyebutkan apa yang harus dihapus atau dikirim ulang. Beberapa juga membawa error.details.error_code yang diawali dengan compaction_. Error parameter, seperti field yang tidak dapat digabungkan dengan compaction, hanya membawa pesan.
| Error | Penyebab | Yang harus dilakukan |
|---|---|---|
529 overloaded_error, error.details.error_code compaction_unavailable | Masalah server sementara saat menghasilkan blok, atau saat membaca blok yang Anda kirim kembali. | Coba ulang permintaan. |
400 compaction_block_misplaced | Pesan yang diringkas masih tersisa di depan blok. | Hapus pesan-pesan tersebut, sehingga blok berada di urutan pertama dalam messages. |
400 compaction_signature_invalid atau compaction_content_mismatch | signature atau content blok diubah setelah API mengembalikannya. | Kirim blok persis seperti yang dikembalikan, termasuk signature-nya. |
| 400 | Permintaan membawa lebih dari satu blok compaction. | Kirim tepat satu, yaitu yang terbaru. |
| 400 | Giliran assistant terakhir diakhiri dengan panggilan alat yang belum memiliki hasil. | Kirim hasil alat dari giliran tersebut, lalu lakukan compaction. |
400 compaction_nothing_to_summarize | messages tidak memiliki konten user atau assistant, misalnya daftar kosong. | Kirim setidaknya satu pesan user atau assistant. |
400 pada permintaan compaction, dengan pesan yang menyatakan bahwa parameter compaction requires juglow-beta: compact-2026-09-04 | Permintaan compaction tidak menyertakan header beta. | Tambahkan header beta; lihat Meminta ringkasan. |
400 pada permintaan berikutnya yang membawa blok: error validasi yang menyatakan bahwa compaction bukan salah satu tipe blok konten yang diharapkan. Pesan tersebut tidak menyebutkan header | Permintaan tersebut tidak menyertakan header beta. | Tambahkan header beta ke setiap permintaan yang membawa blok; lihat Meminta ringkasan. |
Error validasi 400, seperti messages.0.content.0.compaction.citations: Extra inputs are not permitted | Blok dikirim kembali dengan field yang tidak dikembalikan API, seperti citations: null. | Kirim blok persis seperti yang dikembalikan; lihat Melanjutkan dari ringkasan. |
Menghitung penggunaan compaction
Panggilan peringkasan ditagih dan dikenai "rate limit" (batas laju) seperti permintaan lainnya, dan usage.iterations melaporkannya sebagai entri compaction. input_tokens dan output_tokens tingkat atas bernilai nol karena tidak ada balasan yang dihasilkan. Untuk menghitung apa yang dikonsumsi sebuah percakapan, jumlahkan seluruh usage.iterations, bukan field tingkat atas. Mengirim kembali blok pada permintaan berikutnya tidak menambah biaya compaction.
Anda kini memiliki loop yang berfungsi untuk melakukan compaction pada percakapan dan menangani ringkasan yang tidak ada. Dua halaman mengubah cara loop tersebut berjalan, dan Anda dapat menggabungkan keduanya: Compaction yang mempertahankan giliran terbaru mempertahankan giliran terakhir kata demi kata, dan Compaction di latar belakang memungkinkan percakapan berlanjut selama ringkasan ditulis. Compaction dan pemikiran yang dipertahankan berlaku jika Anda mengirim kembali blok thinking dan melakukan salah satu dari keduanya.
Batasan dan interaksi dengan fitur lain
- Compaction ambang batas dan pengeditan konteks. Anda tidak dapat mengirim
compactiondancontext_managementdalam permintaan yang sama. Compaction ambang batas (compact_20260112) tidak dapat berjalan pada permintaan yang membawa blok bertanda tangan.
- Caching prompt.
cache_controlpada blok menempatkan breakpoint setelah ringkasan.
- Pesan sistem dan perubahan alat di tengah percakapan. Pesan
role: "system"di dalam rentang yang diringkas juga ikut diringkas, sehingga instruksi teksnya tidak lagi berlaku setelah blok menggantikannya. Jika suatu instruksi masih penting, nyatakan kembali dalam pesanrole: "system". Kirim pesan tersebut tepat setelah giliranuserbaru Anda berikutnya, dan biarkan pesan itu tetap ada di riwayat Anda sejak saat itu. Untuk perubahan alat, dan untuk posisi pesan tersebut ketika Anda mempertahankan giliran setelah blok, lihat Mengubah prompt sistem atau alat.
- Anggaran tugas. Jangan kirim nilai
remainingdari anggaran tugas (output_config.task_budget.remaining) bersamacompactionatau pada permintaan yang membawa blok. Melakukannya akan mengembalikan error 400.
- Penghitungan token. Endpoint penghitungan token mengabaikan parameter
compaction.
- Konten yang tidak dapat dibawa oleh ringkasan. Gambar, dokumen, blok
container_upload, dan URL yang diambil di dalam pesan yang diringkas akan hilang setelah blok menggantikannya. Nyatakan ulang atau unggah ulang apa pun yang masih dibutuhkan oleh giliran berikutnya.