T9 — Tool output pruning với protected tools
— nhưng bảo vệ skill tool và 2 user turn cuối để giữ nguyên runtime instructions.Tổng quan Context
Điểm tinh tế là protected tools: tool skill
không được prune vì output của nó là runtime instructions (SKILL.md content)
mà LLM cần đọc để biết cách hành xử. Prune skill output đồng nghĩa với
xoá hướng dẫn của agent — không thể phục hồi từ summary.
Ngoài ra, T9 tôn trọng skip boundary: 2 user turn cuối không được prune bất kể tool nào. Đây là vùng "hot context" chứa lệnh gần nhất và kết quả trực tiếp của nó.
time.compacted: Date.now(). Điều này cho phép debug và audit:
biết chính xác message nào đã bị prune và khi nào.
Phân tích code chi tiết Anatomy
Bước 1 — Xác định protected tools và skip boundary
Protected set được định nghĩa cứng là new Set(["skill"]).
Skip boundary = index của user turn thứ 2 tính từ cuối.
session/compaction.ts — setup
{`
const PROTECTED_TOOLS = new Set(["skill"])
// "skill" tool trả về nội dung SKILL.md — runtime instructions
// Prune nó sẽ khiến agent mất hướng dẫn hoạt động
function findSkipBoundary(messages: Message[]): number {
// Tìm index của user turn thứ 2 từ cuối
let userTurnCount = 0
for (let i = messages.length - 1; i >= 0; i--) {
if (messages[i].role === 'user') {
userTurnCount++
if (userTurnCount >= 2) return i // skip messages từ i trở đi
}
}
return 0 // Ít hơn 2 user turns → không skip gì cả (prune tất cả)
}
`}Bước 2 — Selective pruning với 2 điều kiện
Một message bị prune khi đồng thời thỏa: (1) role = "tool", (2) tool name không trong protected set, (3) index < skipBoundary.
session/compaction.ts — pruning loop
{`
export function pruneToolOutputs(messages: Message[]): Message[] {
const skipBoundary = findSkipBoundary(messages)
return messages.map((msg, idx) => {
// Không prune: sau skip boundary (gần đây)
if (idx >= skipBoundary) return msg
// Không prune: không phải tool message
if (msg.role !== 'tool') return msg
// Không prune: protected tool
const toolName = msg.metadata?.toolName ?? ''
if (PROTECTED_TOOLS.has(toolName)) return msg
// Prune: thay content bằng placeholder
return {
...msg,
content: '<tool-output-compacted />',
metadata: {
...msg.metadata,
time: {
...msg.metadata?.time,
compacted: Date.now(), // audit trail
},
},
}
})
}
`}Ví dụ thực tế
Với 10 messages, 2 user turns ở vị trí 3 và 7 (0-indexed): skipBoundary = 3. Messages 0, 1, 2 có thể bị prune. Messages 3–9 được bảo vệ.
Ví dụ — skip boundary visualization
{`
Index Role Tool Action
0 user — (trước boundary)
1 asst read_file (trước boundary)
2 tool read_file ← PRUNE (không protected, trước boundary)
3 user — ← skipBoundary (user turn thứ 2)
4 asst skill (sau boundary → bảo vệ)
5 tool skill ← BẢO VỆ (protected tool)
6 asst edit_file (sau boundary)
7 tool edit_file ← BẢO VỆ (sau boundary)
8 user — (sau boundary)
9 asst — (sau boundary)
`}Tương tác với các kỹ thuật khác Interaction
T9 là bước pre-processing bắt buộc trong T8. Thứ tự đúng: T9 trước, rồi mới split head/tail (T8), rồi LLM compact (T12). Nếu đảo ngược, tail có thể chứa tool messages đã bị prune — gây ra context không nhất quán.
memory_store
trả về long-term memory content → thêm vào protected list để không bị prune.
Failure modes Risk
FM-1: Prune tool output gần nhất
Nếu không có skip boundary, model compact không thấy kết quả tool call vừa thực hiện:
Anti-pattern — prune tất cả tool outputs
{`
// ❌ Prune tất cả — kể cả turn gần nhất
function pruneAll(messages) {
return messages.map(msg =>
msg.role === 'tool'
? { ...msg, content: '<compacted />' }
: msg
)
// Model nhận lệnh "đọc file X" (user turn) nhưng
// không thấy kết quả đọc → hỏi lại hoặc làm sai
}
`}FM-2: Prune skill output
skill tool trả về nội dung SKILL.md — hướng dẫn chi tiết về
cách thực hiện một tác vụ. Prune nó khiến agent không còn runtime instructions:
Anti-pattern — prune skill output
{`
// ❌ Không có protected set — prune cả skill
// Kết quả: model không biết cách chạy skill build-report
// → action sai, không theo template
const PROTECTED_TOOLS = new Set() // empty — nguy hiểm
`}FM-3: Skip boundary không đủ
Nếu chỉ skip 1 user turn (thay vì 2), một số tool results liên quan đến lệnh gần nhất vẫn có thể bị prune — đặc biệt khi 1 user turn trigger nhiều tool calls liên tiếp.
Ưu điểm
- Giảm đáng kể số token trong head trước khi compact
- Protected tools — bảo vệ runtime instructions
- Skip boundary = 2 turns — giữ đủ hot context
time.compactedmetadata — audit trail
Nhược điểm
- Protected list cứng — plugin phải tự extend
- Placeholder đơn giản — không biết output đã prune là gì
- Không prune partially — prune toàn bộ content hoặc không
So sánh với các harness khác Compare
| Harness | Tool output pruning? | Protected tools? | Skip boundary? |
|---|---|---|---|
| opencode | Có — selective với placeholder | Có (skill + extensible) | 2 user turns cuối |
| Claude Code | Có | Không rõ chi tiết | Có (không public) |
| LangChain | Không — prune đều tất cả hoặc không | Không có khái niệm | Không |
| Cursor | Không public chi tiết | Không rõ | Không rõ |
Implementation recipe Recipe
compaction.ts — selective tool pruner recipe
{`
// tool-pruner.ts — selective pruning với protected set + boundary
const PROTECTED_TOOLS = new Set<string>(["skill"])
const SKIP_BOUNDARY_TURNS = 2
const COMPACTED_PLACEHOLDER = '<tool-output-compacted />'
export function pruneToolOutputs(
messages: Message[],
protectedTools: Set<string> = PROTECTED_TOOLS
): Message[] {
// Tìm skipBoundary: user turn thứ N từ cuối
let userTurnCount = 0
let skipBoundary = 0
for (let i = messages.length - 1; i >= 0; i--) {
if (messages[i].role === 'user') {
userTurnCount++
if (userTurnCount >= SKIP_BOUNDARY_TURNS) {
skipBoundary = i
break
}
}
}
return messages.map((msg, idx) => {
// Giữ nguyên: sau boundary, không phải tool, hoặc protected
if (idx >= skipBoundary) return msg
if (msg.role !== 'tool') return msg
if (protectedTools.has(msg.metadata?.toolName ?? '')) return msg
// Prune với audit metadata
return {
...msg,
content: COMPACTED_PLACEHOLDER,
metadata: {
...msg.metadata,
time: { ...msg.metadata?.time, compacted: Date.now() },
},
}
})
}
`}Tham khảo Refs
- Medium — Context management in LLM agents: pruning strategies
- JetBrains Research — AI context compression
- Google ADK — Context compression techniques
- opencode source — session/compaction.ts (lines 171–219)
- Anthropic — Tool use in context-heavy sessions
- arXiv 2404.11843 — Compressing tool-augmented agent histories