6. דחיסת הקשר
"ההקשר יתמלא; צריך דרך לפנות מקום"
חדש בנושא?
מה זה חלון הקשר (context window)?
הכמות הכוללת של טקסט (טוקנים) שהמודל יכול 'לראות' בבת אחת — כולל הפרומפט שלך, היסטוריית השיחה, תוצאות כלים, וכל ההודעות הקודמות. חלון ה-context של Claude גדול אבל מוגבל.
למה ה-context מתמלא?
כל קריאת כלי מוסיפה את התוצאה שלה למערך ההודעות. קריאת קובץ של 1000 שורות מוסיפה ~4000 טוקן. אחרי 30 קריאות קבצים ו-20 פקודות bash, אפשר להגיע בקלות ל-100,000+ טוקן ולגשת לגבול.
מה זה micro-compaction?
טכניקה שבה תוצאות כלים ישנות מיותר מ-3 סיבובים מוחלפות בסיכום קצר כמו '[Previous: used read_file]'. זה קוצץ בשקט פרטים ישנים תוך שמירת ה-context האחרון שלם.
הבעיה
חלון ה-context מוגבל. read_file יחיד על קובץ של 1000 שורות עולה ~4000 טוקן. אחרי קריאת 30 קבצים והרצת 20 פקודות bash, מגיעים ל-100,000+ טוקן. הסוכן לא יכול לעבוד על codebases גדולים ללא דחיסה.
הפתרון
שלוש שכבות, בעוצמה עולה:
Every turn:
+------------------+
| Tool call result |
+------------------+
|
v
[Layer 1: micro_compact] (silent, every turn)
Replace tool_result > 3 turns old
with "[Previous: used {tool_name}]"
|
v
[Check: tokens > 50000?]
| |
yes no
| |
v +--- continue normally
[Layer 2: mid_compact]
Summarize assistant messages
Keep only last 5 tool results
|
v
[Check: tokens > 80000?]
| |
yes no
| +--- continue
v
[Layer 3: hard_compact]
Call LLM to write a dense summary
Replace entire history with summary
Inject <identity> reminder
איך זה עובד
- שכבה 1 — דחיסת micro רצה בשקט בכל סיבוב. תוצאות כלים ישנות מיותר מ-3 סיבובים הופכות למציין מקום של שורה אחת.
def micro_compact(messages: list) -> list:
compacted = []
for i, msg in enumerate(messages):
if msg["role"] == "user" and isinstance(msg["content"], list):
age = len(messages) - i
if age > 6: # older than 3 turns (user+assistant pairs)
new_content = []
for block in msg["content"]:
if block.get("type") == "tool_result":
tool_name = block.get("_tool_name", "tool")
new_content.append({
"type": "tool_result",
"tool_use_id": block["tool_use_id"],
"content": f"[Previous: used {tool_name}]",
})
else:
new_content.append(block)
compacted.append({**msg, "content": new_content})
continue
compacted.append(msg)
return compacted
- שכבה 2 — דחיסת mid מופעלת כשספירת הטוקנים עוברת 50,000. היא שומרת את פרומפט המערכת, את 5 תוצאות הכלים האחרונות במלואן, ומסכמת את השאר.
def count_tokens(messages: list) -> int:
text = json.dumps(messages)
return len(text) // 4 # rough estimate: 4 chars ≈ 1 token
def maybe_compact(messages: list) -> list:
tokens = count_tokens(messages)
if tokens > 80000:
return hard_compact(messages)
if tokens > 50000:
return mid_compact(messages)
return micro_compact(messages)
- שכבה 3 — דחיסת hard מבקשת מה-LLM עצמו לכתוב סיכום צפוף של מה שקרה, ואז מחליפה את כל ההיסטוריה בסיכום הזה בתוספת תזכורת זהות.
def hard_compact(messages: list) -> list:
summary_prompt = (
"Summarize the conversation so far. Include: "
"what the user asked, what tools you used, "
"what you found, what's left to do. Be dense."
)
summary_messages = messages + [{"role": "user", "content": summary_prompt}]
response = client.messages.create(
model=MODEL, system=SYSTEM,
messages=summary_messages, max_tokens=2000,
)
summary = response.content[0].text
return [
{"role": "user", "content": f"<context_summary>\n{summary}\n</context_summary>"},
{"role": "assistant", "content": "Understood. Continuing from the summary."},
]
מה השתנה מ-מיומנויות
| רכיב | לפני (מיומנויות) | אחרי (דחיסת הקשר) |
|---|---|---|
| Context | גדל לנצח | דחיסה תלת-שכבתית |
| תוצאות ישנות | תוכן מלא | מציין מקום של שורה אחת |
| גבול טוקנים | פגיעה וקריסה | גבול רך ב-50k, קשה ב-80k |
| היסטוריה | ללא גבולות | נדחסת לפי דרישה |
מסקנה מרכזית
דחיסת ה-context היא מה שהופך סוכנים עם ריצה ארוכה לאפשריים מעשית. האסטרטגיה התלת-שכבתית היא פרוגרסיבית: עשה את הדבר הזול ביותר קודם (micro), הסלם רק כשצריך (mid), ובתור מוצא אחרון בקש מהמודל לסכם את עצמו (hard). קוד הלולאה כמעט לא משתנה — רק עטפו את messages דרך maybe_compact() לפני כל קריאת LLM.
מדריך קוד אינטראקטיבי
1def count_tokens(messages: list) -> int:2 text = json.dumps(messages)3 return len(text) // 4 # rough estimate: 4 chars ≈ 1 token4 5def maybe_compact(messages: list) -> list:6 tokens = count_tokens(messages)7 if tokens > 80000:8 return hard_compact(messages)9 if tokens > 50000:10 return mid_compact(messages)11 return micro_compact(messages)12 13def hard_compact(messages: list) -> list:14 summary_prompt = (15 "Summarize the conversation so far. Include: "16 "what the user asked, what tools you used, "17 "what you found, what's left to do. Be dense."18 )19 summary_messages = messages + [{"role": "user", "content": summary_prompt}]20 response = client.messages.create(21 model=MODEL, system=SYSTEM,22 messages=summary_messages, max_tokens=2000,23 )24 summary = response.content[0].text25 return [26 {"role": "user", "content": f"<context_summary>\n{summary}\n</context_summary>"},27 {"role": "assistant", "content": "Understood. Continuing from the summary."},28 ]29 מלאו את חלון ההקשר על ידי מתן משימות רבות ברצף. צפו בדחיסה נכנסת לפעולה.
רמז
בדקו את ספירת הטוקנים ב-metadata של response