לא מזמן יצא לי לכתוב קוד בפייתון. הקוד משתמש ב-API של openAI ולפעמים ה-API הזה קצת מפשל. הוא מחזיר לי שגיאה ואומר שהגזמתי עם הקריאות, או שפתאום לא זמין. זה לא רק openAI אל גם למשל Vertex של גוגל שבו אני קורא לג׳מיני, ה-LLM שלהם. בתיעוד שלהם כתוב למשל, שאם מקבלים 503 או אפילו 500, צריכים לנסות שוב. לפעמים API כושלים בגלל סיבות שונות ואז, אם הפעולה שלכם תלויה ב-API אחר, כדאי לנסות שוב.
כשישבתי לי בנחת ליצור את הקוד שלי שיוצר קשר ל-API של ג׳מיני, חשבתי על זה וביקשתי מקופיילוט לייצר לי קוד שיבצע retry בהתאם לשגיאה. כמובן שהוא יצר לי קוד בקלות ובערימות. אבל אז ב-pull requesy, כשידידי מומחה הפייתון רועי בן יוסף בחן את הקוד הוא אמר לי: ״תגיד, למה גרמת ל-LLM ליצור ערימה של קוד שעושה בדיוק מה ש tenacity decorator עושה?״ והתגובה שלי היתה ????????????.
נכון, קוד ש-LLM מייצר לא עולה כלום, אבל כל קוד מיותר הוא פוטנציאל לבעיות אבטחה, בעיות ביצועים ובעיות באופן כללי. אם יש מודול פופולרי, איכותי ומוצלח שיכול לחסוך לי קוד, אפילו אם מאד זול לייצר את הקוד הזה, אז זה משמח. במקרה הזה שורה אחת שחוסכת ערימות של קוד ��מאפשרת לי לעשות retry לפעולות בקלות ועם הגדרות שונות. תיקנתי את הקוד שלי והבטחתי לרועי לכתוב על זה. בפעם הבאה שאצטרך retry, אנחה את ה-LLM בהתאם.
אז מה זה דקורטור בפייתון? אני מניח שכולכם מכירים אבל מדובר בסינטקס של פונקציה שהקלט שלה הוא פונקציה או מתודה. אנו עושים את זה למגוון מטרות כולל הרחבות. הנה דוגמה מאד פשוטה לדקורטור:
def simple_decorator(func):
def wrapper():
print("Before the function call")
func()
print("After the function call")
return wrapper
@simple_decorator
def say_hello():
print("Hello!")
say_hello()
בדוגמה הזו אני קורא לדקורטור ששמו הוא simple_decorator – כל פונקציה שאני קורא לה לפני תדפיס לפני ואחרי. הפלט יהיה:
Before the function call
Hello!
After the function call
יש לנו הרבה דקורטורים וספריות שונות. אחת מהם היא tenacity שזה באנגלית עקשנות. לא, אין קשר ל Tenacious D שהיא הלהקה של ג׳ק בלאק, מלבד שגם השם שלה מגיע מעקשנות ( ˶ˆᗜˆ˵ ).

עם tenacity שהיא ספריה אולטרא פופולרית אפשר לעשות retry בקלות רבה. הבה ונראה איך זה עובד! אני אשתמש ב-uv. אם אתם לא מכירים, בהחלט כדאי לקחת את מנהל הסביבה הזה לסיבוב מהיר. בפוסט הזה כתבתי על uv – המרענן הרשמי של פייתון הקיץ הזה 🙂 ניצור פרויקט פייתון ראשוני ונוסיף אליו את tanacity:
uv init
uv add tenacity
uv venv
��כשיו אפשר להשתמש ב-tenacity בלי בעיה. הנה הקוד:
import time
from tenacity import retry, stop_after_attempt, wait_fixed, retry_if_exception_type
# Simulate a custom exception
class TemporaryAPIError(Exception):
pass
# Retry decorator
@retry(
stop=stop_after_attempt(5), # Max 5 attempts
wait=wait_fixed(2), # Wait 2 seconds between attempts
retry=retry_if_exception_type(TemporaryAPIError) # Only retry for this exception type
)
def call_api():
print("Calling the API...")
# Always fail
raise TemporaryAPIError("API is always unavailable.")
# Test the API call
try:
result = call_api()
print(result)
except Exception as e:
print(f"Failed after retries: {e}")
מה שמעניין אותנו הוא ה-Retry decorator. תראו כמה פשוט ונחמד הוא. אני קורא ל-API שתמיד נכשל. הוא יחכה שתי שניות וינסה שוב, 5 פעמים. שימו לב שאני יכול להגדיר לו את סוג השגיאה לניסיון מחדש. במידה והוא מקבל את השגיאה הזו מעבר למספר הפעמים שהגדרתי, הוא ייכשל. זה הפלט שאני אקבל:
Calling the API...
Calling the API...
Calling the API...
Calling the API...
Calling the API...
Failed after retries: RetryError[<Future at 0x100ff9f30 state=finished raised TemporaryAPIError>]
ו… זהו! בגדול, בעידן של היום – מה שחשוב הוא להכיר את הדקורטורים השונים ותמיד להטיל ספק בפלט ולשאול את ה-LLM אם יש חבילה טובה שמטפלת בזה. אם הוא ממליץ על משהו, לא להתעצל ולהכנס ל-PyPi ולבחון את הספריה ולראות שהיא תקינה וטובה ומתוחזקת היטב ולא איזו יצירה של סיני אלמוני ומוזר והפעם האחרונה שבה היא התעדכנה היתה כשאני הייתי בתיכון. במקרה הזה tenacity היא חבילה מוצלחת. עכשיו אתם מכירים ואם יהיה לכם את הצורך הזה לבצע קריאה מחדש – תוכלו להנחות את ה-LLM להשתמש בה.
2 תגובות
באמת הקוד יותר יפה וכנראה גם קל יותר לתחזוקה ועובד טוב יותר – אבל לא הוספת שורת קוד אחת – הוספת שורת קוד אחת וספריה שלמה. גם כל הקוד הזה בא עם attack surface גדול, ופתחת את הקוד ל supply chain attack – ראה xz. אז כמו בכל דבר, יש יתרונות, ויש חסרונות.
נקודה מצוינת! נכון, tenacity מוסיפה ספרייה שלמה עם attack surface פוטנציאלי (כמו xz), אבל היתרונות של קוד קריא ותחזוקה קלה לעיתים שווים את הסיכון, במיוחד אם בודקים את הספרייה ומקורותיה. זה תמיד שקלול של trade-offs.