16. שילוח לפרודקשן

"סוכן דמו זה קל; סוכן אמין זו הנדסה"

30 דקות קריאה
💡חדש בנושא?

מה זה streaming?

במקום לחכות לתשובת ה-LLM השלמה בבת אחת, streaming מעביר טוקנים בזמן שהם נוצרים — כמו לצפות במישהו מקליד. זה גורם לסוכן להרגיש רספונסיבי גם בהפקות ארוכות.

מה זה model routing?

שימוש במודלים שונים למשימות שונות. מודלים מהירים וזולים (Haiku) להחלטות פשוטות. מודלים חזקים ויקרים (Opus) לחשיבה מורכבת. זה חותך עלויות 60-80% בלי לפגוע באיכות.

מה זה exponential backoff?

אסטרטגיית retry שבה ממתינים יותר זמן בין כל ניסיון: 1 שנייה, 2 שניות, 4 שניות, 8 שניות. זה מונע הפצצה של API כושל ונותן לו זמן להתאושש.

הבעיה

בניתם סוכן. הוא עובד על הלפטופ שלכם. אתם עושים דמו וזה נראה מדהים. אז אתם שולחים אותו.

המשתמש הראשון פוגע ב-rate limit ומקבל stack trace. המשתמש השני ממתין 45 שניות בוהה במסך ריק. המשתמש השלישי מריץ לולאה ששורפת $200 בטוקנים לפני שמישהו שם לב. הפער בין “עובד על הלפטופ שלי” ל”אמין בפרודקשן” הוא עצום.

הפתרון

שכבו דאגות פרודקשן על הרתמה הקיימת. אתם לא משכתבים את לולאת הסוכן — אתם עוטפים אותה עם התשתית שהיא צריכה כדי לשרוד תעבורה אמיתית:

Streaming      →  UX רספונסיבי, בלי מסכים ריקים
Retries        →  לשרוד rate limits וכשלים חולפים
ניתוב מודלים   →  לחתוך עלויות 60-80% בלי לאבד איכות
מעקב עלויות   →  לדעת מה מוציאים, לעצור לפני חריגה
ניטור בריאות   →  לראות בעיות לפני שמשתמשים מדווחים

Streaming תשובות

def stream_response(messages: list) -> anthropic.types.Message:
    with client.messages.stream(
        model="claude-sonnet-4-6-20250610",
        messages=messages,
        tools=TOOLS,
        max_tokens=8000,
    ) as stream:
        for text in stream.text_stream:
            print(text, end="", flush=True)
        print()
    return stream.get_final_message()

Retry עם Exponential Backoff

def call_with_retry(messages: list, max_retries: int = 4):
    for attempt in range(max_retries):
        try:
            return client.messages.create(
                model=select_model(messages),
                messages=messages,
                tools=TOOLS,
                max_tokens=8000,
            )
        except anthropic.RateLimitError:
            wait = 2 ** attempt
            print(f"Rate limited. Retrying in {wait}s...")
            time.sleep(wait)
        except anthropic.APITimeoutError:
            if attempt == max_retries - 1:
                raise
            time.sleep(1)
    raise RuntimeError("API unavailable after retries")

דפוס exponential backoff — המתנה של 1s, 2s, 4s, 8s — נותן ל-API זמן להתאושש.

ניתוב מודלים

def select_model(messages: list) -> str:
    token_count = count_tokens(messages)
    if token_count < 2000:
        return "claude-haiku-4-5-20251001"
    return "claude-sonnet-4-6-20250610"

חישוב עלות קונקרטי: ל-100 משימות סוכן, כל אחת 10k input + 2k output:

  • הכל-Sonnet: $6.00
  • 70% Haiku + 30% Sonnet: $2.92

חיסכון של 51% עם היוריסטיקת ניתוב נאיבית.

מה השתנה מתצפית

רכיבתצפיתפרודקשן
מיקודלראות מה הסוכן עושהלהפוך את הסוכן לאמין
כשליםלתעד שגיאות ל-post-mortemלנסות מחדש שגיאות חולפות אוטומטית
לאטנסילמדוד זמני תגובהלהפחית לאטנסי נתפס עם streaming
עלותלעקוב אחר הוצאה ב-tracesלאכוף תקציבים, לנתב למודלים זולים

נקודה מרכזית

סוכן פרודקשן הוא לא סוכן שונה — הוא אותה לולאת הסוכן עטופה בהנדסה שהיא צריכה כדי לשרוד את העולם האמיתי. Streaming לרספונסיביות. Retries לעמידות. ניתוב מודלים לעלות. מעקב תקציב לבטיחות. ניטור בריאות לנראות. כל אחד שכבה קטנה ועצמאית. ביחד הם ההבדל בין דמו לשירות.

מדריך קוד אינטראקטיבי

סוכן פרודקשן עם Streaming ו-Retries
1def agent_loop_production(messages: list, tracer: AgentTracer):
2 while True:
3 response = call_with_retry(messages)
4 tracer.record("llm_call", {
5 "tokens": response.usage.input_tokens + response.usage.output_tokens,
6 "model": response.model,
7 })
8 messages.append({"role": "assistant", "content": response.content})
9 if response.stop_reason != "tool_use":
10 return
11 
12 for block in response.content:
13 if block.type == "tool_use":
14 verdict = guardrail.check(block.name, block.input)
15 if verdict == "DENIED":
16 result = "Error: Tool call denied by guardrail"
17 elif verdict == "COST_CAP_EXCEEDED":
18 result = "Error: Cost cap exceeded"
19 elif verdict == "NEEDS_APPROVAL":
20 result = human_approve(block)
21 else:
22 result = execute_tool(block)
23 tracer.record("tool_exec", {"tool": block.name})
24 messages.append(tool_result(block.id, result))
25 
26def call_with_retry(messages, max_retries=4):
27 for attempt in range(max_retries):
28 try:
29 return client.messages.create(
30 model=select_model(messages),
31 messages=messages, tools=TOOLS, max_tokens=8000,
32 )
33 except anthropic.RateLimitError:
34 wait = 2 ** attempt
35 time.sleep(wait)
36 raise RuntimeError("API unavailable after retries")
37 
38def select_model(messages) -> str:
39 token_count = count_tokens(messages)
40 if token_count < 2000:
41 return "claude-haiku-4-5-20251001"
42 return "claude-sonnet-4-6-20250610"
43 
לולאת הפרודקשן משלבת הכל: ה-tracer מתעד כל קריאה, ה-guardrail בודק כל כלי, retries מטפלים בכשלים. זו לולאת הסוכן אחרי שגדלה.
שלב 1 מתוך 4
🧪 נסו בעצמכם
🔥 חימום ~5 min

חשבו את הפרש העלויות: הרצת 100 משימות סוכן שכל אחת משתמשת ב-10k input + 2k output טוקנים. השוו הכל-Sonnet מול ניתוב 70% ל-Haiku.

רמז

בדקו תמחור נוכחי ב-docs.anthropic.com. Haiku זול בערך פי 10-20 לטוקן.

🔨 בנייה ~20 min

הוסיפו streaming ללולאת הסוכן: השתמשו ב-client.messages.stream() והדפיסו טוקנים כשהם מגיעים. הציגו spinner בזמן ביצוע כלים.

רמז

השתמשו ב-with client.messages.stream(...) as stream: for text in stream.text_stream: print(text, end='', flush=True)

🚀 אתגר ~45 min

בנו דשבורד בריאות: endpoint HTTP פשוט שמדווח על סוכנים פעילים, סך טוקנים שנצרכו היום, שיעור שגיאות, וזמן תגובה ממוצע.

רמז

השתמשו ב-http.server או Flask. קראו מתיקיית .traces/ כדי לחשב מטריקות.

מצאתם טעות? דווחו ←