Pemadatan & Peringkasan Cabang
LLM memiliki jendela konteks terbatas. Ketika percakapan menjadi terlalu panjang, Pi menggunakan pemadatan untuk meringkas konten lama sambil mempertahankan karya terbaru. Halaman ini mencakup pemadatan otomatis dan branch summarization.
File sumber (pi-mono):
packages/coding-agent/src/core/compaction/compaction.ts- Logika pemadatan otomatispackages/coding-agent/src/core/compaction/branch-summarization.ts- Ringkasan cabangpackages/coding-agent/src/core/compaction/utils.ts- Utilitas bersama (pelacakan file, serialisasi)packages/coding-agent/src/core/session-manager.ts- Jenis entri (CompactionEntry,BranchSummaryEntry)packages/coding-agent/src/core/extensions/types.ts- Jenis acara ekstensi
Untuk definisi TypeScript dalam proyek Anda, periksa node_modules/@earendil-works/pi-coding-agent/dist/.
Ringkasan
Pi memiliki dua mekanisme peringkasan:
| Mekanisme | Pemicu | Tujuan |
|---|---|---|
| Pemadatan | Konteks melebihi ambang batas, atau /compact |
Ringkaslah pesan-pesan lama untuk membebaskan konteks |
| Ringkasan cabang | /tree navigasi |
Pertahankan konteks saat berpindah cabang |
Keduanya menggunakan format ringkasan terstruktur yang sama dan melacak operasi file secara kumulatif. Permintaan pemadatan dan ringkasan cabang menggunakan ID sesi perutean baru dan, jika didukung oleh penyedia, menonaktifkan penulisan prompt-cache karena permintaan satu kali ini kemungkinan tidak akan digunakan kembali.
Pemadatan
Ketika Itu Terpicu
Pemadatan otomatis terpicu ketika:
contextTokens > contextWindow - reserveTokensSecara default, reserveTokens adalah 16384 token (dapat dikonfigurasi dalam ~/.pi/agent/settings.json atau <project-dir>/.pi/settings.json). Hal ini memberikan ruang bagi tanggapan LLM.
Anda juga dapat memicu secara manual dengan /compact [instructions], dengan instruksi opsional yang memfokuskan ringkasan.
Cara Kerjanya
- Temukan titik potong: Berjalan mundur dari pesan terbaru, mengumpulkan perkiraan token hingga
keepRecentTokens(default 20k, dapat dikonfigurasi dalam~/.pi/agent/settings.jsonatau<project-dir>/.pi/settings.json) tercapai - Ekstrak pesan: Kumpulkan pesan dari batas yang disimpan sebelumnya (atau permulaan sesi) hingga titik potong
- Buat ringkasan: Panggil LLM untuk meringkas dengan format terstruktur, meneruskan ringkasan sebelumnya sebagai konteks berulang jika ada
- Tambahkan entri: Simpan
CompactionEntrydengan ringkasan danfirstKeptEntryId - Muat ulang: Sesi dimuat ulang, menggunakan ringkasan + pesan dari
firstKeptEntryIddan seterusnya
Before compaction:
entry: 0 1 2 3 4 5 6 7 8 9
┌─────┬─────┬─────┬─────┬──────┬─────┬──── ─┬──────┬─────┬─────┐
│ hdr │ usr │ ass │ tool │ usr │ ass │ tool │ tool │ ass │ tool│
└─────┴─────┴─────┴──────┴─────┴─────┴──────┴──────┴─────┴─────┘
└────────┬───────┘ └──────────────┬──────────────┘
messagesToSummarize kept messages
↑
firstKeptEntryId (entry 4)
After compaction (new entry appended):
entry: 0 1 2 3 4 5 6 7 8 9 10
┌─────┬─────┬─────┬─────┬──────┬─────┬──── ─┬──────┬─────┬─────┬─────┐
│ hdr │ usr │ ass │ tool │ usr │ ass │ tool │ tool │ ass │ tool│ cmp │
└─────┴─────┴─────┴──────┴─────┴─────┴──────┴──────┴─────┴─────┴─────┘
└──────────┬──────┘ └──────────────────────┬───────────────────┘
not sent to LLM sent to LLM
↑
starts from firstKeptEntryId
What the LLM sees:
┌────────┬─────────┬─────┬─────┬──────┬──────┬─────┬──────┐
│ system │ summary │ usr │ ass │ tool │ tool │ ass │ tool │
└────────┴─────────┴─────┴─────┴──────┴──────┴─────┴──────┘
↑ ↑ └─────────────────┬────────────────┘
prompt from cmp messages from firstKeptEntryIdPada pemadatan berulang, rentang yang diringkas dimulai dari batas pemadatan sebelumnya (firstKeptEntryId), bukan pada entri pemadatan itu sendiri, kembali ke entri setelah pemadatan sebelumnya jika entri yang disimpan tersebut tidak dapat ditemukan di jalurnya. Hal ini mempertahankan pesan-pesan yang bertahan dari pemadatan sebelumnya dengan memasukkannya ke dalam proses rangkuman berikutnya juga. Pi juga menghitung ulang tokensBefore dari konteks sesi yang dibangun kembali sebelum menulis CompactionEntry baru, sehingga jumlah token mencerminkan konteks pra-pemadatan sebenarnya yang diganti.
Putaran Terpisah
Sebuah "giliran" dimulai dengan pesan pengguna dan mencakup semua respons asisten dan panggilan alat hingga pesan pengguna berikutnya. Biasanya, pemadatan dilakukan pada batas belokan.
Ketika satu putaran melebihi keepRecentTokens, titik potong mendarat di tengah putaran pada pesan asisten. Ini adalah "giliran terpisah":
Split turn (one huge turn exceeds budget):
entry: 0 1 2 3 4 5 6 7 8
┌─────┬─────┬─────┬──────┬─────┬──────┬──────┬─────┬──────┐
│ hdr │ usr │ ass │ tool │ ass │ tool │ tool │ ass │ tool │
└─────┴─────┴─────┴──────┴─────┴──────┴──────┴─────┴──────┘
↑ ↑
turnStartIndex = 1 firstKeptEntryId = 7
│ │
└──── turnPrefixMessages (1-6) ───────┘
└── kept (7-8)
isSplitTurn = true
messagesToSummarize = [] (no complete turns before)
turnPrefixMessages = [usr, ass, tool, ass, tool, tool]Untuk putaran terpisah, Pi menghasilkan dua ringkasan dan menggabungkannya:
- Ringkasan riwayat: Konteks sebelumnya (jika ada)
- Ringkasan awalan giliran: Bagian awal giliran split
Aturan Titik Potong
Titik potong yang valid adalah:
- Pesan pengguna
- Pesan asisten
- Pesan BashExecution
- Pesan khusus (pesan_khusus, ringkasan_cabang)
Jangan pernah memotong hasil alat (hasil alat tersebut harus tetap sesuai dengan panggilan alatnya).
Struktur Entri Pemadatan
Didefinisikan dalam session-manager.ts:
interface CompactionEntry<T = unknown> {
type: "compaction";
id: string;
parentId: string;
timestamp: number;
summary: string;
firstKeptEntryId: string;
tokensBefore: number;
usage?: Usage; // LLM usage that generated the summary
fromHook?: boolean; // true if provided by extension (legacy field name)
details?: T; // implementation-specific data
}
// Default compaction uses this for details (from compaction.ts):
interface CompactionDetails {
readFiles: string[];
modifiedFiles: string[];
}Extensions dapat menyimpan data apa pun yang dapat diserialkan JSON di details. Pemadatan default melacak operasi file, namun penerapan ekstensi khusus dapat menggunakan strukturnya sendiri. Ringkasan yang dihasilkan dan disediakan ekstensi menyimpan LLM usage bila tersedia sehingga total sesi mencakup pekerjaan ringkasan.
Lihat prepareCompaction() dan compact() untuk implementasinya. Untuk peringkasan terprogram langsung, generateSummary() mengembalikan teks ringkasan dan generateSummaryWithUsage() mengembalikan { text, usage }.
Ringkasan Cabang
Ketika Itu Terpicu
Saat Anda menggunakan /tree untuk menavigasi ke cabang lain, Pi menawarkan untuk meringkas pekerjaan yang Anda tinggalkan. Ini memasukkan konteks dari cabang kiri ke cabang baru.
Cara Kerjanya
- Temukan nenek moyang yang sama: Node terdalam yang dimiliki bersama oleh posisi lama dan baru
- Kumpulkan entri: Berjalan dari daun tua kembali ke nenek moyang yang sama
- Persiapkan dengan anggaran: Sertakan pesan hingga anggaran token (yang terbaru terlebih dahulu)
- Buat ringkasan: Hubungi LLM dengan format terstruktur
- Tambahkan entri: Simpan
BranchSummaryEntrydi titik navigasi
Tree before navigation:
┌─ B ─ C ─ D (old leaf, being abandoned)
A ───┤
└─ E ─ F (target)
Common ancestor: A
Entries to summarize: B, C, D
After navigation with summary:
┌─ B ─ C ─ D
A ───┤
└─ E ─ F ─ [summary of B,C,D] (new leaf)Pelacakan File Kumulatif
Baik pemadatan maupun branch summarization file track secara kumulatif. Saat membuat ringkasan, pi mengekstrak operasi file dari:
- Alat memanggil pesan yang diringkas
- Ringkasan pemadatan atau cabang sebelumnya
details(jika ada)
Ini berarti pelacakan file terakumulasi di beberapa pemadatan atau ringkasan cabang yang disarangkan, menjaga riwayat lengkap file yang dibaca dan dimodifikasi.
Struktur Entri Ringkasan Cabang
Didefinisikan dalam session-manager.ts:
interface BranchSummaryEntry<T = unknown> {
type: "branch_summary";
id: string;
parentId: string;
timestamp: number;
summary: string;
fromId: string; // Entry we navigated from
usage?: Usage; // LLM usage that generated the summary
fromHook?: boolean; // true if provided by extension (legacy field name)
details?: T; // implementation-specific data
}
// Default branch summarization uses this for details (from branch-summarization.ts):
interface BranchSummaryDetails {
readFiles: string[];
modifiedFiles: string[];
}Sama seperti pemadatan, ekstensi dapat menyimpan data khusus dalam details.
Lihat collectEntriesForBranchSummary(), prepareBranchEntries(), dan generateBranchSummary() untuk penerapannya.
Format Ringkasan
Pemadatan dan branch summarization menggunakan format terstruktur yang sama:
## Goal
[What the user is trying to accomplish]
## Constraints & Preferences
- [Requirements mentioned by user]
## Progress
### Done
- [x] [Completed tasks]
### In Progress
- [ ] [Current work]
### Blocked
- [Issues, if any]
## Key Decisions
- **[Decision]**: [Rationale]
## Next Steps
1. [What should happen next]
## Critical Context
- [Data needed to continue]
<read-files>
path/to/file1.ts
path/to/file2.ts
</read-files>
<modified-files>
path/to/changed.ts
</modified-files>Serialisasi Pesan
Sebelum diringkas, pesan diserialkan menjadi teks melalui serializeConversation():
[User]: What they said
[Assistant thinking]: Internal reasoning
[Assistant]: Response text
[Assistant tool calls]: read(path="foo.ts"); edit(path="bar.ts", ...)
[Tool result]: Output from toolHal ini mencegah model memperlakukannya sebagai percakapan yang berlanjut.
Hasil alat dipotong menjadi 2000 karakter selama serialisasi. Konten yang melebihi batas tersebut diganti dengan penanda yang menunjukkan berapa banyak karakter yang terpotong. Hal ini menjaga permintaan peringkasan tetap dalam anggaran token yang masuk akal, karena hasil alat (terutama dari read dan bash) biasanya merupakan kontributor terbesar terhadap ukuran konteks.
Peringkasan Khusus melalui Extensions
Extensions dapat mencegat dan menyesuaikan pemadatan dan branch summarization. Lihat extensions/types.ts untuk definisi jenis peristiwa.
sesi_sebelum_kompak
Ditembakkan sebelum pemadatan otomatis atau /compact. Dapat membatalkan atau memberikan ringkasan khusus. Lihat SessionBeforeCompactEvent dan CompactionPreparation di file jenis.
pi.on("session_before_compact", async (event, ctx) => {
const { preparation, branchEntries, customInstructions, reason, willRetry, signal } = event;
// preparation.messagesToSummarize - messages to summarize
// preparation.turnPrefixMessages - split turn prefix (if isSplitTurn)
// preparation.previousSummary - previous compaction summary
// preparation.fileOps - extracted file operations
// preparation.tokensBefore - context tokens before compaction
// preparation.firstKeptEntryId - where kept messages start
// preparation.settings - compaction settings
// branchEntries - all entries on current branch (for custom state)
// reason - "manual" (/compact), "threshold", or "overflow"
// willRetry - whether the aborted turn is retried after compaction (overflow recovery)
// signal - AbortSignal (pass to LLM calls)
// Cancel:
return { cancel: true };
// Custom summary:
return {
compaction: {
summary: "Your summary...",
firstKeptEntryId: preparation.firstKeptEntryId,
tokensBefore: preparation.tokensBefore,
// usage: summaryResponse.usage, // Optional; included in session totals
details: { /* custom data */ },
}
};
});Mengubah Pesan menjadi Teks
Untuk membuat ringkasan dengan model Anda sendiri, ubah pesan menjadi teks menggunakan serializeConversation:
import { convertToLlm, serializeConversation } from "@earendil-works/pi-coding-agent";
pi.on("session_before_compact", async (event, ctx) => {
const { preparation } = event;
// Convert AgentMessage[] to Message[], then serialize to text
const conversationText = serializeConversation(
convertToLlm(preparation.messagesToSummarize)
);
// Returns:
// [User]: message text
// [Assistant thinking]: thinking content
// [Assistant]: response text
// [Assistant tool calls]: read(path="..."); bash(command="...")
// [Tool result]: output text
// Now send to your model for summarization
const { summary, usage } = await myModel.summarize(conversationText);
return {
compaction: {
summary,
firstKeptEntryId: preparation.firstKeptEntryId,
tokensBefore: preparation.tokensBefore,
usage,
}
};
});Lihat custom-compaction.ts untuk contoh lengkap menggunakan model yang berbeda.
sesi_sebelum_pohon
Ditembakkan sebelum navigasi /tree. Selalu aktif terlepas dari apakah pengguna memilih untuk meringkas. Dapat membatalkan navigasi atau memberikan ringkasan khusus.
pi.on("session_before_tree", async (event, ctx) => {
const { preparation, signal } = event;
// preparation.targetId - where we're navigating to
// preparation.oldLeafId - current position (being abandoned)
// preparation.commonAncestorId - shared ancestor
// preparation.entriesToSummarize - entries that would be summarized
// preparation.userWantsSummary - whether user chose to summarize
// Cancel navigation entirely:
return { cancel: true };
// Provide custom summary (only used if userWantsSummary is true):
if (preparation.userWantsSummary) {
return {
summary: {
summary: "Your summary...",
// usage: summaryResponse.usage, // Optional; included in session totals
details: { /* custom data */ },
}
};
}
});Lihat SessionBeforeTreeEvent dan TreePreparation di file jenis.
Pengaturan
Konfigurasikan pemadatan di ~/.pi/agent/settings.json atau <project-dir>/.pi/settings.json:
{
"compaction": {
"enabled": true,
"reserveTokens": 16384,
"keepRecentTokens": 20000
}
}| Pengaturan | Bawaan | Keterangan |
|---|---|---|
enabled |
true |
Aktifkan pemadatan otomatis |
reserveTokens |
16384 |
Token untuk dicadangkan untuk respons LLM |
keepRecentTokens |
20000 |
Token terkini yang harus disimpan (tidak diringkas) |
Nonaktifkan pemadatan otomatis dengan "enabled": false. Anda masih dapat memadatkannya secara manual dengan /compact.