בטח, בשמחה. כמי שמתמחה בבניית קורסי AI, אני מבין את החשיבות של תרגול מעשי, במיוחד בנושא כה מרכזי כמו עבודה עם ממשקי API. הכנתי סט תרגילים מקיף שמכסה את כל נושאי השיעור, עם דגש על יישומים רלוונטיים לשוק הישראלי.

התרגילים בנויים במורכבות עולה, החל מקריאות בסיסיות ועד לבניית Agent מורכב, ומסתיימים בפרויקט בית מסכם.

---

<div dir="rtl" lang="he">

# תרגילים - שיעור 5.2: עבודה עם API של Claude, OpenAI ו-Gemini

## מידע כללי
- **שיעור:** 5.2 - עבודה עם API של Claude, OpenAI ו-Gemini
- **סוג השיעור:** מעשי (4 שעות)
- **הערה:** לכל התרגילים נדרשים מפתחות API. יש להתקין את הספריות הרלוונטיות (`openai`, `anthropic`, `google-generativeai`) ולהגדיר קובץ `.env` עם המפתחות הבאים: `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `GEMINI_API_KEY`.

---

## תרגיל 1: השוואת מודלים - קריאת API בסיסית (מעשי - 30 דקות)
### הנחיות:
בתרגיל זה נבצע את אותה קריאה לשלושת המודלים המובילים (GPT-4o, Claude 3.5 Sonnet, Gemini 1.5 Pro) ונשווה את התוצאות, הסגנון והמבנה של התשובות. המטרה היא להבין את ההבדלים הבסיסיים בשימוש בכל API.

**תרחיש:** אתם עובדים בחברת שיווק דיגיטלי ישראלית והתבקשתם לכתוב פוסט קצר לרשתות החברתיות עבור חברת "קפה עלית", המכריז על תערובת קפה חדשה.

### משימות:
1.  צרו פונקציה נפרדת עבור כל ספק (OpenAI, Anthropic, Gemini).
2.  כל פונקציה תקבל `prompt` כארגומנט ותחזיר את תגובת המודל.
3.  הגדירו `prompt` אחיד: "כתוב פוסט קצר וקליט לפייסבוק באורך 3-4 משפטים על תערובת קפה חדשה בשם 'תערובת חיפה'. הדגש את הארומה המיוחדת והטעם העשיר. הפוסט צריך להיות מנוסח בעברית, בסגנון צעיר ומזמין."
4.  הריצו את הקוד וקראו לשלוש הפונקציות עם הפרומפט האחיד.
5.  הדפיסו את התוצאה של כל מודל תחת כותרת ברורה (למשל, "--- OpenAI Response ---").
6.  דונו בכיתה: איזה מודל נתן את התשובה הטובה ביותר לדעתכם? האם היו הבדלים משמעותיים בסגנון?

### קוד התחלתי (Starter Code):
```python
import os
from dotenv import load_dotenv
import openai
import anthropic
import google.generativeai as genai

# Load API keys from .env file
load_dotenv()

# --- 1. OpenAI Client ---
client_openai = openai.OpenAI(api_key=os.environ.get("OPENAI_API_KEY"))

def get_openai_response(prompt):
    response = client_openai.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "user", "content": prompt}]
    )
    return response.choices[0].message.content

# --- 2. Anthropic Client ---
client_anthropic = anthropic.Anthropic(api_key=os.environ.get("ANTHROPIC_API_KEY"))

def get_claude_response(prompt):
    # Your code here: Call Anthropic's API
    # Hint: model="claude-3-5-sonnet-20240620"
    pass

# --- 3. Gemini Client ---
genai.configure(api_key=os.environ.get("GEMINI_API_KEY"))

def get_gemini_response(prompt):
    # Your code here: Call Gemini's API
    # Hint: model="gemini-1.5-pro-latest"
    pass


# --- Main execution ---
main_prompt = "כתוב פוסט קצר וקליט לפייסבוק באורך 3-4 משפטים על תערובת קפה חדשה בשם 'תערובת חיפה'. הדגש את הארומה המיוחדת והטעם העשיר. הפוסט צריך להיות מנוסח בעברית, בסגנון צעיר ומזמין."

print("--- OpenAI Response ---")
# print(get_openai_response(main_prompt))

print("\n--- Claude Response ---")
# print(get_claude_response(main_prompt))

print("\n--- Gemini Response ---")
# print(get_gemini_response(main_prompt))
```

---

## תרגיל 2: חוויה בזמן אמת - Streaming (מעשי - 25 דקות)
### הנחיות:
נבנה צ'אטבוט בסיסי שמדפיס את התשובה למסך מילה במילה (או ליתר דיוק, טוקן אחר טוקן), מה שיוצר חווית משתמש טובה ומהירה יותר. נשתמש ב-Gemini לצורך תרגיל זה, אך העיקרון דומה אצל כל הספקים.

**תרחיש:** עוזר וירטואלי שמסכם כתבות חדשותיות ארוכות בזמן אמת.

### משימות:
1.  כתבו פונקציה המקבלת קלט מהמשתמש בלולאה אינסופית (עד שהמשתמש יקליד "יציאה").
2.  בתוך הלולאה, שלחו את קלט המשתמש למודל Gemini באמצעות קריאת API שתומכת ב-`stream=True`.
3.  עברו בלולאה על התגובה המתקבלת (שהיא `generator`).
4.  בכל איטרציה, הדפיסו את החלק החדש של הטקסט (`chunk.text`) למסך **ללא ירידת שורה** (`end=''`).
5.  השתמשו ב-`flush=True` בפונקציית `print` כדי להבטיח שהטקסט יופיע מיידית.
6.  לאחר שהלולאה הפנימית מסתיימת, הדפיסו ירידת שורה חדשה כדי שהקלט הבא של המשתמש יופיע בשורה נפרדת.

### קוד התחלתי (Starter Code):
```python
import os
import google.generativeai as genai
from dotenv import load_dotenv

load_dotenv()
genai.configure(api_key=os.environ.get("GEMINI_API_KEY"))

def stream_chat():
    model = genai.GenerativeModel('gemini-1.5-pro-latest')
    print("מתחיל צ'אט בסטרימינג. הקלד 'יציאה' כדי לסיים.")
    
    while True:
        user_input = input("אתה: ")
        if user_input.lower() == "יציאה":
            print(" להתראות!")
            break

        # Your code here:
        # 1. Call model.generate_content with the user_input and stream=True
        # 2. Loop through the response chunks
        # 3. Print each chunk's text to the console in real-time
        
        print("\n") # New line for the next user prompt

# Start the chat
stream_chat()
```

---

## תרגיל 3: הרחבת יכולות - Tool Use / Function Calling (מעשי - 40 דקות)
### הנחיות:
נלמד את המודל כיצד להשתמש בכלי חיצוני (פונקציית פייתון) כדי לבצע משימה שאינו יודע לעשות בעצמו, כמו חישובים מדויקים או גישה למידע בזמן אמת.

**תרחיש:** אתם בונים עוזר AI לאתר קניות ישראלי. העוזר צריך להיות מסוגל לחשב מחיר סופי של מוצר כולל מע"מ (17%).

### משימות:
1.  כתבו פונקציית פייתון פשוטה בשם `calculate_final_price` שמקבלת מחיר בסיס (`base_price`) ומחזירה את המחיר כולל 17% מע"מ.
2.  הגדירו את ה-"כלי" (Tool) עבור ה-API של Anthropic (Claude), כך שיכיל את שם הפונקציה, תיאור שלה, והפרמטרים שהיא מקבלת.
3.  כתבו קוד ששולח פרומפט למודל Claude, למשל: "אני רוצה לקנות מוצר שעולה 350 ש"ח. מה יהיה המחיר הסופי שלו כולל מע"מ?".
4.  הקוד צריך לטפל בתגובת המודל:
    *   לבדוק אם המודל ביקש להשתמש בכלי שלכם.
    *   אם כן, לחלץ את שם הפונקציה והארגומנטים מהתגובה.
    *   לקרוא לפונקציית הפייתון `calculate_final_price` עם הארגומנטים שהתקבלו.
    *   לשלוח את תוצאת הפונקציה בחזרה למודל בקריאה נוספת, כדי שהוא יוכל לנסח תשובה סופית למשתמש.
5.  הדפיסו את התשובה הסופית והמנוסחת של המודל.

### קוד התחלתי (Starter Code):
```python
import os
import anthropic
from dotenv import load_dotenv

load_dotenv()
client = anthropic.Anthropic(api_key=os.environ.get("ANTHROPIC_API_KEY"))

# 1. Define the Python function
def calculate_final_price(base_price: float) -> float:
    """Calculates the final price of an item including 17% VAT."""
    vat_rate = 0.17
    final_price = base_price * (1 + vat_rate)
    print(f"[Tool Executed] Calculating VAT for {base_price}. Result: {final_price}")
    return final_price

# 2. Define the tool for the API
tools = [
    {
        "name": "calculate_final_price",
        "description": "חישוב המחיר הסופי של פריט לאחר הוספת מעמ בשיעור 17%. יש להשתמש בכלי זה עבור כל שאלה שקשורה למחיר סופי, מעמ, או עלות כוללת.",
        "input_schema": {
            "type": "object",
            "properties": {
                "base_price": {
                    "type": "number",
                    "description": "המחיר המקורי של הפריט, לפני מעמ. לדוגמה: 500."
                }
            },
            "required": ["base_price"]
        }
    }
]

# 3. Send the initial request
user_prompt = "אני רוצה לקנות אוזניות שעולות 350 שקלים. כמה זה יצא לי סך הכל?"
message = client.messages.create(
    model="claude-3-5-sonnet-20240620",
    max_tokens=1024,
    tools=tools,
    messages=[{"role": "user", "content": user_prompt}]
)

print(f"Initial model response: {message.content}")

# Your code here:
# 4. Check if the model wants to use a tool (message.stop_reason == 'tool_use')
# 5. If so, find the tool call, extract the function name and arguments.
# 6. Call the actual Python function with the extracted arguments.
# 7. Send the result back to the model in a new API call to get a natural language response.
# 8. Print the final answer.
```

---

## תרגיל 4: ניהול שיחה - Multi-Turn Conversation (מעשי - 25 דקות)
### הנחיות:
נשדרג את הצ'אטבוט שלנו כך שיזכור את ההקשר של השיחה. זהו המפתח לבניית אינטראקציות טבעיות ומתמשכות.

**תרחיש:** נמשיך את תרחיש העוזר לאתר הקניות. לאחר שהעוזר חישב את המחיר, המשתמש שואל שאלת המשך.

### משימות:
1.  צרו רשימה (list) בשם `conversation_history`.
2.  הוסיפו לרשימה הודעת `system` ראשונית שתגדיר את תפקיד הבוט (לדוגמה: "אתה עוזר קניות באתר 'מחסני חשמל'").
3.  צרו לולאה שבה המשתמש יכול לשאול שאלות.
4.  בכל פעם שהמשתמש מקליד הודעה:
    *   הוסיפו את הודעת המשתמש ל-`conversation_history` בתור `{"role": "user", "content": ...}`.
    *   שלחו את **כל** `conversation_history` למודל (למשל, OpenAI GPT-4o).
    *   קבלו את תשובת המודל.
    *   הוסיפו גם את תשובת המודל להיסטוריה בתור `{"role": "assistant", "content": ...}`.
    *   הדפיסו את התשובה למשתמש.
5.  בדקו את הבוט עם רצף שיחה:
    *   משתמש: "כמה עולה טלוויזיה סמסונג 55 אינץ'?" (המודל ימציא מחיר)
    *   משתמש: "ומה לגבי דגם 65 אינץ'?" (המודל צריך להבין שמדובר על אותה טלוויזיה)
    *   משתמש: "האם יש לכם אותה במלאי בסניף תל אביב?" (המודל יבין למה "אותה" מתייחס)

### קוד התחלתי (Starter Code):
```python
import os
import openai
from dotenv import load_dotenv

load_dotenv()
client = openai.OpenAI(api_key=os.environ.get("OPENAI_API_KEY"))

conversation_history = [
    {"role": "system", "content": "אתה עוזר וירטואלי באתר 'שופרסל Online'. עליך לענות על שאלות לגבי מוצרים, מחירים ומבצעים. היה אדיב ומקצועי."}
]

def chat_with_history():
    while True:
        user_input = input("אתה: ")
        if user_input.lower() == "יציאה":
            break

        # Your code here:
        # 1. Append the user's message to conversation_history
        
        # 2. Send the entire conversation_history to the OpenAI API
        
        # 3. Get the model's response
        
        # 4. Append the model's response to conversation_history
        
        # 5. Print the model's response content to the user
        
        print(f"שופרסל בוט: {model_response_content}")


chat_with_history()
```

---

## תרגיל 5: עיצוב אישיות - System Prompt Engineering (מעשי - 20 דקות)
### הנחיות:
נראה כיצד הודעת `system` פשוטה יכולה לשנות באופן דרמטי את אישיות, טון וסגנון התגובה של המודל, גם אם שאלת המשתמש זהה.

**תרחיש:** בוט שירות לקוחות של חברת "פלאפון".

### משימות:
1.  הגדירו שני `system_prompts` שונים:
    *   **מקצועי:** "אתה נציג שירות טכני בכיר בחברת פלאפון. עליך לספק תשובות מדויקות, רשמיות ומפורטות. השתמש בטרמינולוגיה טכנית והימנע מסלנג או אימוג'ים."
    *   **חברותי:** "אתה 'פלאפי', העוזר הדיגיטלי והחברותי של פלאפון! דבר עם הלקוחות כמו חבר, בגובה העיניים. השתמש בסגנון קליל, הומור, ואל תהסס להוסיף אימוג'ים! 😎"
2.  הגדירו `user_prompt` זהה עבור שניהם: "האינטרנט הסלולרי שלי ממש איטי היום, מה אני יכול לעשות?"
3.  כתבו פונקציה שמקבלת `system_prompt` ו-`user_prompt` ושולחת אותם למודל (למשל GPT-4o).
4.  קראו לפונקציה פעמיים, פעם אחת עם כל `system_prompt`.
5.  הדפיסו והשוו את שתי התוצאות. שימו לב להבדלים בטון, במבנה המשפט, בבחירת המילים ובשימוש באימוג'ים.

### קוד התחלתי (Starter Code):
```python
import os
import openai
from dotenv import load_dotenv

load_dotenv()
client = openai.OpenAI(api_key=os.environ.get("OPENAI_API_KEY"))

def get_styled_response(system_prompt, user_prompt):
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[
            {"role": "system", "content": system_prompt},
            {"role": "user", "content": user_prompt}
        ],
        temperature=0.7
    )
    return response.choices[0].message.content

# System Prompts
professional_prompt = "אתה נציג שירות טכני בכיר בחברת פלאפון. עליך לספק תשובות מדויקות, רשמיות ומפורטות. השתמש בטרמינולוגיה טכנית והימנע מסלנג או אימוג'ים."
friendly_prompt = "אתה 'פלאפי', העוזר הדיגיטלי והחברותי של פלאפון! דבר עם הלקוחות כמו חבר, בגובה העיניים. השתמש בסגנון קליל, הומור, ואל תהסס להוסיף אימוג'ים! 😎"

# User Prompt
user_query = "האינטרנט הסלולרי שלי ממש איטי היום, מה אני יכול לעשות?"

# Get and print responses
print("--- תגובה מקצועית ---")
# Your code here: call get_styled_response with the professional prompt

print("\n--- תגובה חברותית ---")
# Your code here: call get_styled_response with the friendly prompt
```

---

## תרגיל 6: אתגר כיתתי - בניית Agent למסעדה (תחרותי - 45 דקות)
### הנחיות:
זהו אתגר בזמן מוגבל. עבדו בזוגות או ביחידים. המטרה היא לבנות Agent פונקציונלי שמשתמש במספר כלים כדי לענות על שאלות לקוחות של מסעדה. בסוף הזמן, כל צוות יציג את ה-Agent שלו.

**תרחיש:** אתם בונים בוט הזמנות למסעדה איטלקית בבאר שבע בשם "פסטה בנגב".

### משימות:
1.  השתמשו בקוד ההתחלתי המסופק, הכולל שתי פונקציות: `get_menu()` ו-`check_availability(dish_name, quantity)`.
2.  הגדירו את שני הכלים הללו עבור ה-API של המודל שתבחרו (Claude, Gemini או OpenAI). **שימו לב:** התיאורים של הכלים והפרמטרים קריטיים להצלחה.
3.  בנו לוגיקה שמטפלת בבקשות מהמשתמש, מפעילה את הכלים הנכונים עם הפרמטרים הנכונים, ומחזירה את התוצאה למודל כדי שינסח תשובה.
4.  ה-Agent צריך להיות מסוגל לענות על שאלות כמו:
    *   "מה יש לכם בתפריט?"
    *   "מה המחיר של פיצה מרגריטה?"
    *   "האם יש לכם 3 מנות פפרדלה ראגו במלאי?"
    *   "אני רוצה להזמין 2 מנות לזניה, זה אפשרי?"
5.  **בונוס:** הוסיפו כלי שלישי: `place_order(dishes)` שמקבל רשימה של מנות ומחזיר מספר הזמנה.

### קוד התחלתי (Starter Code):
```python
import random
import json

# --- Mock Restaurant API ---
MENU = {
    "פיצה מרגריטה": 55,
    "פסטה קרבונרה": 68,
    "לזניה בולונז": 72,
    "פפרדלה ראגו": 75,
    "סלט קיסר": 48
}
INVENTORY = {
    "פיצה מרגריטה": 20,
    "פסטה קרבונרה": 15,
    "לזניה בולונז": 0,  # Out of stock!
    "פפרדלה ראגו": 10,
    "סלט קיסר": 30
}

def get_menu():
    """Returns the entire menu with prices as a JSON string."""
    print("[Tool Executed] get_menu()")
    return json.dumps(MENU)

def check_availability(dish_name: str, quantity: int):
    """Checks if a specific quantity of a dish is available in the inventory."""
    print(f"[Tool Executed] check_availability(dish_name='{dish_name}', quantity={quantity})")
    if dish_name not in INVENTORY:
        return json.dumps({"available": False, "reason": "Dish not found"})
    
    if INVENTORY[dish_name] >= quantity:
        return json.dumps({"available": True, "message": f"Yes, we have {quantity} servings."})
    else:
        return json.dumps({"available": False, "reason": f"Sorry, we only have {INVENTORY[dish_name]} left."})

# --- Your Agent Logic Goes Here ---
# Choose your preferred model (OpenAI, Claude, or Gemini)
# 1. Define the tools in the format required by your chosen API.
# 2. Create a loop to get user input.
# 3. In the loop, call the model with the user's prompt and the tool definitions.
# 4. Handle the model's response, execute functions if requested, and send the results back.
# 5. Present the final answer to the user.
```

---

## תרגיל 7: משימת בית - בניית Agent תחבורה ציבורית
### הנחיות:
זוהי משימת סיכום המשלבת את כל מה שלמדנו בשיעור: ניהול שיחה, שימוש בכלים, ועיצוב אישיות. עליכם לבנות בוט שעוזר למשתמשים לתכנן נסיעה בתחבורה ציבורית בישראל.

**הגישו את קובץ ה-`.py` עד תחילת השיעור הבא.**

### משימות:
1.  **אישיות (System Prompt):** הגדירו את הבוט כ"ריילי", עוזר תכנון נסיעות חכם וידידותי של "רב-קו אונליין".
2.  **כלים (Tool Use):** צרו שתי פונקציות פייתון **מדומות** (mock functions):
    *   `get_next_bus(station_id: int, city: str)`: הפונקציה תחזיר זמן הגעה רנדומלי בעוד 5-15 דקות אם העיר היא 'תל אביב', 'ירושלים' או 'חיפה'. עבור כל עיר אחרת, היא תחזיר הודעת שגיאה.
    *   `plan_trip(origin: str, destination: str)`: הפונקציה תחזיר תוכנית נסיעה קבועה מראש אם המוצא הוא 'תל אביב' והיעד הוא 'ירושלים' (למשל, "קו 480 מתחנה מרכזית..."). עבור כל מסלול אחר, היא תחזיר הודעה שהמסלול לא נמצא.
3.  **ניהול שיחה (History):** הבוט חייב לזכור את ההקשר בין השאלות.
4.  **לוגיקה מרכזית:**
    *   צרו לולאת שיחה עם המשתמש.
    *   שלחו את היסטוריית השיחה והכלים למודל שבחרתם (מומלץ Claude 3.5 Sonnet או GPT-4o בשל יכולות ה-Tool Use המתקדמות שלהם).
    *   טפלו בבקשות לשימוש בכלים, הריצו את הפונקציות המדומות, ושלחו את התוצאות בחזרה למודל לקבלת תשובה סופית.
5.  **דוגמת שיחה שהבוט צריך לתמוך בה:**
    *   משתמש: "היי ריילי, מתי האוטובוס הבא מתחנה 12345 בתל אביב?"
    *   בוט: (מפעיל את `get_next_bus`) "היי! האוטובוס הבא צפוי להגיע בעוד כ-8 דקות."
    *   משתמש: "מעולה. ואיך אני מגיע משם לירושלים?"
    *   בוט: (מפעיל `plan_trip` ומבין ש"משם" זה מתל אביב) "כדי להגיע לירושלים, תוכל לקחת את קו 480 מהתחנה המרכזית. הנסיעה אורכת כשעה."
    *   משתמש: "אוקיי. ומה לגבי נסיעה מאילת לחיפה?"
    *   בוט: (מפעיל `plan_trip` ומקבל שגיאה) "מצטער, כרגע אין לי מידע על תכנון מסלול בין אילת לחיפה."

### קריטריונים להערכה
| קריטריון | ציון |
|---|---|
| יישום נכון של System Prompt וניהול היסטוריית שיחה | 25% |
| הגדרה נכונה של הכלים (Tools) עבור ה-API | 25% |
| לוגיקה תקינה לזיהוי קריאה לכלי, הרצת הפונקציה ושליחת התוצאה חזרה | 30% |
| איכות הקוד, תיעוד, וטיפול במקרי קצה (כמו שגיאות מהפונקציות) | 20% |
| **סה"כ** | **100%** |

</div>