Context window adalah "working memory" LLM — dan seperti working memory manusia, kapasitasnya terbatas. Yang membedakan developer biasa dan yang mahir adalah kemampuan memilih apa yang masuk dan apa yang dibuang dari window tersebut.
Context Window — Bukan Sekedar "Panjang Chat"
| Model | Context Window | Biaya per 1M token (input) | Sweet Spot |
|---|---|---|---|
| Claude 3 Haiku | 200K token | $0.25 | High-volume triage, classification |
| Claude 3.5 Sonnet | 200K token | $3.00 | Complex reasoning, agent tasks |
| GPT-4o mini | 128K token | $0.15 | Budget-conscious production |
| GPT-4o | 128K token | $2.50 | Frontier reasoning |
| Gemini 1.5 Flash | 1M token | $0.075 | Very long document processing |
| Gemini 1.5 Pro | 2M token | $1.25 | Extreme long-context tasks |
Walaupun Gemini bisa handle 2M token, memasukkan semua dokumen ke context bukan solusi yang baik: (1) biaya token sangat mahal, (2) LLM mengalami "lost in the middle" — attention berkurang untuk konten di tengah context window yang sangat panjang. RAG masih lebih efisien untuk sebagian besar use case.
Token Counting — Perlu Tahu Ini
Satu token ≈ 0.75 kata dalam Bahasa Inggris. Untuk Bahasa Indonesia, ratio-nya sedikit berbeda karena tokenizer dilatih lebih banyak dari data Inggris:
import tiktoken # pip install tiktoken
def count_tokens(text: str, model: str = "gpt-4o") -> int:
enc = tiktoken.encoding_for_model(model)
return len(enc.encode(text))
# Estimasi biaya sebelum run
def estimate_cost(messages: list, model: str = "claude-3-5-sonnet") -> float:
PRICE_PER_M = {
"claude-3-5-sonnet": 3.0,
"claude-3-haiku": 0.25,
"gpt-4o": 2.5,
"gpt-4o-mini": 0.15,
}
total_tokens = sum(count_tokens(m["content"]) for m in messages)
price = PRICE_PER_M.get(model, 1.0)
return (total_tokens / 1_000_000) * price
# Rule of thumb untuk Bahasa Indonesia:
# 1000 kata Indonesia ≈ 1500-2000 token (lebih dari Inggris)
# PDF 10 halaman teks ≈ 5000-8000 token
Apa yang Masuk, Apa yang Dibuang
Ini keputusan terpenting dalam context engineering — setiap token yang masuk adalah biaya dan "perhatian" LLM yang terbagi:
Teknik Context Compression
class ContextManager:
def __init__(self, llm, max_tokens: int = 4000):
self.llm = llm
self.max_tokens = max_tokens
self.summary = "" # ringkasan sesi
self.recent_messages = [] # 5-10 pesan terakhir
def add_message(self, role: str, content: str):
self.recent_messages.append({"role": role, "content": content})
# Kalau terlalu panjang, compress history lama ke summary
if self._total_tokens() > self.max_tokens:
old_messages = self.recent_messages[:-5] # semua kecuali 5 terakhir
self.recent_messages = self.recent_messages[-5:]
self.summary = self._summarize(old_messages)
def get_context(self) -> list:
messages = []
if self.summary:
messages.append({
"role": "system",
"content": f"Ringkasan percakapan sebelumnya:\n{self.summary}"
})
return messages + self.recent_messages
def _summarize(self, messages: list) -> str:
return self.llm.invoke(
f"Buat ringkasan singkat percakapan ini dalam 3-5 kalimat:\n{messages}"
)
Semantic Caching — Hemat Biaya untuk Query Serupa
Kalau 40% pertanyaan user adalah variasi dari 10 pertanyaan yang sama, Anda bisa cache LLM response dan serve dari cache untuk query yang semantically similar:
import numpy as np
class SemanticCache:
def __init__(self, embedding_model, redis_client, threshold=0.92):
self.embed = embedding_model
self.redis = redis_client
self.threshold = threshold # similarity threshold untuk cache hit
def get(self, query: str):
query_vec = self.embed.encode(query)
cached = self.redis.hgetall("semantic_cache")
for cached_query, cached_data in cached.items():
cached_vec = np.frombuffer(cached_data["embedding"])
similarity = np.dot(query_vec, cached_vec) # cosine similarity
if similarity >= self.threshold:
return cached_data["response"] # cache hit!
return None # cache miss — perlu call LLM
def set(self, query: str, response: str):
query_vec = self.embed.encode(query)
self.redis.hset("semantic_cache", query, {
"embedding": query_vec.tobytes(),
"response": response
})
Context engineering adalah tentang signal-to-noise ratio. Setiap token yang masuk ke context adalah "perhatian" LLM yang harus dibagi. Lebih sedikit noise = output yang lebih fokus dan lebih murah.
Kesimpulan
Context window bukan hanya soal "berapa panjang chat yang bisa ditampung" — tapi soal keputusan aktif apa yang masuk dan apa yang tidak. Sliding window untuk history, relevance threshold untuk RAG, truncation untuk tool results, dan semantic caching untuk efisiensi biaya adalah empat teknik yang langsung bisa diterapkan.