Series 3 - Bài 4: Token Budget Management
Token budget không phải afterthought — là resource management bắt buộc trong production agent. Bài này xây dựng hệ thống đếm, phân bổ và cắt token chủ động.
Token management trong agent system giống memory management trong low-level programming — bạn có thể ignore nó ở đầu, nhưng production sẽ buộc bạn phải học.
Không có token budget strategy → agent sẽ crash với context overflow, hoặc tốn gấp 10x chi phí không cần thiết.
Token là gì và đếm như thế nào
Token counting:
"Hello world" = 2 tokens
"processLoan" = 2 tokens (process + Loan)
"TypeScript" = 2 tokens (Type + Script)
Tiếng Anh: 1 word ≈ 0.75 tokens
Tiếng Việt: 1 word ≈ 1.5-2 tokens (unicode heavy)
Estimation shortcuts:
1,000 tokens ≈ 750 words (English)
1 page A4 text ≈ 500-700 tokens
1 file TypeScript 100 lines ≈ 1,500-3,000 tokens
1 JSON config 50 lines ≈ 500-1,000 tokens
1 tool_result (command output) ≈ 200-5,000 tokens
Đối với production, dùng tiktoken (OpenAI) hoặc claude-tokenizer để count chính xác. Estimation đủ cho planning, nhưng không đủ cho hard limits.
Budget Framework
Budget Model:
Total budget = Model context limit - Safety buffer
Với Claude Sonnet (200,000 tokens):
Total budget = 200,000 - 10,000 = 190,000 tokens
(Safety buffer 5% để tránh API error)
Budget Allocation:
┌─────────────────────────────────────────┐
│ Component Budget % of Total │
│ ────────────────────────────────────── │
│ System prompt 5,000 2.6% │
│ Project context 10,000 5.3% │
│ Episodic memory 5,000 2.6% │
│ File context 50,000 26.3% │
│ History 80,000 42.1% │
│ Tool results 30,000 15.8% │
│ Current message 5,000 2.6% │
│ Reserved output 10,000 5.3% │
│ ────────────────────────────────────── │
│ Total 195,000 100% │
└─────────────────────────────────────────┘
Budget này không phải fixed — nó là starting point. Từng session có thể allocate khác nhau dựa trên task.
Trimming Strategy: Ai bị cắt trước
Khi budget bị vượt, phải trim. Thứ tự trim quan trọng:
Trim Priority (cắt trước đến cắt sau):
Priority 1 - KHÔNG BAO GIỜ TRIM:
System prompt
Current user message
→ Đây là context cốt lõi, mất đi = agent mất direction
Priority 2 - TRIM SAU CÙNG:
Recent conversation (5 turns gần nhất)
Last 3 tool results
→ Cần để maintain coherence trong task
Priority 3 - TRIM KHI CẦN:
Old conversation history
Old tool results đã được processed
→ Thông tin đã được LLM dùng, có thể compress
Priority 4 - TRIM TRƯỚC:
Episodic memory ít liên quan
File content của files không được đề cập gần đây
→ Lowest relevance cho current task
Trim Techniques
1. Sliding Window: Giữ N turns gần nhất
Sliding Window:
Full history: [Turn 1][Turn 2]...[Turn 18][Turn 19][Turn 20]
Window size = 10 turns:
Keep: [Turn 11][Turn 12]...[Turn 18][Turn 19][Turn 20]
Drop: [Turn 1] through [Turn 10]
Simple, predictable, nhưng mất context cũ hoàn toàn.
2. Summary + Recent: Compress cũ, giữ mới
Summary + Recent:
Old history (turns 1-10):
→ Compress thành summary:
"Agent đã đọc loan.service.ts, phát hiện bug trong
processLoan() tại line 45. Đã refactor và run tests.
Tests pass. Đang chuyển sang fix payment.service.ts."
Recent history (turns 11-20):
→ Giữ nguyên
Result: 10 turns old → ~200 tokens summary
Saving: ~8,000 tokens
Kỹ thuật này tốt hơn sliding window nhưng cần LLM call để generate summary — thêm latency và cost.
3. Selective Tool Result Trimming
Tool results thường là source of token waste lớn nhất:
Before trimming:
Tool result: read_file("src/loan.service.ts")
Content: [full 300-line file, ~4,500 tokens]
After trimming (nếu file đã được processed):
Tool result: read_file("src/loan.service.ts")
Content: "[File read — 300 lines. Relevant section
extracted and applied in turn 5.]"
Size: ~20 tokens
Saving: 4,480 tokens per old tool result
Khi nào trim tool result? Khi có evidence trong conversation rằng LLM đã sử dụng thông tin đó (ví dụ: đã viết code từ file đó).
4. File Context Rotation
Thay vì inject tất cả files liên quan một lúc, rotate theo task progress:
Turn 1-3: Task là "fix loan.service.ts"
Inject: loan.service.ts, loan.entity.ts
Drop: payment.service.ts (chưa cần)
Turn 10: Agent chuyển sang payment
Inject: payment.service.ts
Drop: loan.service.ts content (đã xong)
Keep: loan.service.ts path reference
Proactive vs Reactive Trimming
Reactive (xấu):
Chờ đến khi hit limit → trim → có thể đã mất
important context trong panic trim
Proactive (tốt):
Theo dõi budget liên tục
Khi đạt 70%: start trim low-priority content
Khi đạt 85%: aggressive trim history
Khi đạt 95%: emergency — summarize aggressively
Never reach 100%
Token Counter Component
Token Counter trong Context Layer:
count(messages[]) → number
Estimate token count của messages array
Dùng character-based heuristic:
English: len / 4 tokens
Vietnamese: len / 2.5 tokens (unicode adjustment)
budgetRemaining() → { total, used, remaining, pct }
Snapshot của budget state hiện tại
shouldTrim() → boolean
true nếu used > 70% của total
trimHistory(messages[], targetTokens) → messages[]
Trim oldest non-system messages đến targetTokens
Giữ nguyên system prompt và current message
Trade-off: Aggressive vs Conservative Trimming
Aggressive trimming:
+ Token cost thấp
+ Context clean, ít noise
- Rủi ro mất context quan trọng
- Agent có thể "quên" decisions từ early turns
Conservative trimming:
+ Giữ nhiều context hơn
+ Agent ít bị mất direction hơn
- Token cost cao hơn
- Performance degradation với very long context
Thực tế: start conservative, measure cost, adjust aggressiveness dựa trên budget và task complexity.
Kết luận
- Token management là resource management — không phải optimization, là bắt buộc.
- Budget allocation trước khi start: system, file, history, output đều cần limits.
- Trim priority: cắt old history và processed tool results trước, không bao giờ cắt system prompt hay current message.
- Sliding window đơn giản nhưng mất context. Summary + Recent tốt hơn nhưng tốn thêm LLM call.
- Proactive trimming tốt hơn reactive — không chờ hit limit mới xử lý.
- Tiếng Việt tốn token gấp 1.5-2x tiếng Anh — factor này vào budget calculation.