פורסם ב כלים לחיים קלים, לא רק למתכנתות

כתיבת קוד תחרותית – ChatGPT, Deepseek & Claude

קוד: wegovy-tracker

אם נכנסתן לאינטרנט בשבוע שעבר, סביר להניח ששמעתן על חברת Deepseek ועל מודל ה- GenAI החדש שלה.
המודל הזה טלטל את התעשייה, הכה חזק את שוק המניות האמריקאי, ושינה את התפיסה שלנו לגבי כמה AI צריך לעלות ומה הוא יכול לעשות. המודל של Deepseek יעיל בהרבה מהמודלים של OpenAI, מטא וגוגל, הוא רץ על צ'יפים יותר פשוטים וזולים, ועל פי הדיווחים עלה הרבה (הרבה) פחות לפתח אותו.

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

(השוואת ביצועים מ-https://www.nbcnews.com/data-graphics/deepseek-ai-comparison-openai-chatgpt-google-gemini-meta-llama-rcna189568)

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


לשם הניסוי היום החלטתי להשתמש בשלושה מודלים – 

  1. מחברת OpenAI השתמשתי ב-GPT-4. 
  2. מחברת Deepseek השתמשתי ב-V3.
  3. מחברת Anthropic השתמשתי ב-Claude 3.5 Sonnet.

אגב, כששאלתי את כל אחד מהמודלים עם מי אני מדברת כדי לוודא, קיבלתי תשובות מאוד נרגשות מ-ChatGPT ו-DeepSeek, ותשובה די לקונית מ-Claude, אבל אני מעריכה את זה שהוא לא מנסה להתחנף.

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

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

לפניכן התוצאות:

  1. Deepseek
  1. ChatGPT
  1. Claude

סיכום מחשבות ראשוניות – 
אף אחד מהציורים הוא לא שיא היופי, אבל אין ספק שבפרס הכיעור זוכה ChatGPT. בעיני Deepseek יצא הכי חמוד ו-Claude נראה הכי כמו מזרק.
ראויה לציון העובדה ש-Claude רינדר לי את ה-Html ישר בתוך הדפדפן שלו, וזה היה ממש נוח. בשניים האחרים הייתי צריכה להעתיק את הקוד מקומית ולהריץ את הקובץ כדי לראות את התוצאה.

שני דברים קטנים שהפריעו לי בציור של Deepseek –

  1. הנוזל הכחול לא מעוגל למטה ולכן דורך על המזרק וזה לא נראה טוב.
  2. מבחינת כמויות – נראה שבמזרק שלו יש בערך 150 מ"ל ובשניים האחרים יש 10 מ"ל. כיוון שהזנתי להם שכל מנה היא 0.75 מ"ל הניחוש שלהם יותר הגיוני.

נמשיך לשלב השני, אני רוצה כפתור שמעדכן שלקחתי מנה, ומוריד את הכמות שנותרה במזרק. כמו כן, ביקשתי שהכפתור ישלח בקשת API עם הכמות המעודכנת במזרק, כי אני יודעת שאני ארצה לעקוב אחרי הסטטוס של המזרק ב-DB מקומי.

והרי התוצאות:

הערה – שיניתי את הצבע לסגול כי הכחול היה הרבה יותר מדי רפואי בשבילי, אבל זה לא קשור לקוד

  1. Deepseek
  1. ChatGPT
  1. Claude

מסקנות מהסיבוב השני – הפעם המודלים עשו דברים קצת שונה, אז בואו נדבר על זה.
כל המודלים מימשו כפתור כמו שביקשתי שמעדכן את המסך ושולח בקשת API. כמו כן, כל המודלים הכניסו איזשהו טיפול בשגיאת API. אבל, בעוד ש-ChatGPT ו-Deepseek רק כתבו שגיאה ללוג והמשיכו כרגיל, Claude הרים שגיאה למשתמשת ולא עדכן את המסך.

מצד אחד, יש פה ניצחון ברור של Claude שכתב קוד יותר חסין לשגיאות – במקרה של האחרים יכולתי בקלות לפספס את הלוג של בשגיאה ולא לדעת שהבקשה שלי לא יצאה לפועל.
מצד שני, ChatGPT ו-Deepseek כן איפשרו לי לראות שהפונקציונליות של הכפתור עובדת כמו שביקשתי וזה היה נוח שאני יכולה לסמן וי על החלק הזה.


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

אני אשתמש בשתי טבלאות – 

  1. טבלת איוונטים, שתתעד את כל המנות שנלקחו, תאריכים, כמויות וכו'.
  2. טבלת סטטוס, שתחזיק את הסטטוס הנוכחי של המזרק – כמה מ"ל ומנות נשארו.

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

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

  1. למשתמשים יכולים להיות מזרקים שונים שמייצגים תרופות שונות (ניתן גם כמובן להמיר לבקבוקים, חפיסות וכו'). 
  2. אנחנו נרצה לתעד מידע על מזרקים ישנים שכבר סיימנו.
  3. אנחנו נרצה לשנות בקלות את המבנה והשימוש של האיוונטים שלנו בלי להשפיע על התצוגה של המזרקים.
  4. איוונטים יכולים ללכת לאיבוד – אנחנו נרצה שה-source of truth שלנו ייצג את המצב הנוכחי של המזרק.

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

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

יצרתי את שתי הטבלאות שלי – 

עכשיו אני ארצה לבקש מהמודולים שלי לכתוב פונקציות עוטפות ל-API של Notion כדי שאני אוכל לקרוא להן מהשרת –

  1. פונקציה שמעדכנת את שתי הטבלאות כשמנה נלקחת – איוונט חדש צריך להיווצר והסטטוס של המזרק צריך להתעדכן.
  2. פונקציה שמחזירה את הסטטוס של המזרק הנוכחי ותשמש להצגה ראשונית של המסך.

את קבצי הקוד שכל מודול כתב אתן יכולות לראות בקומיט הזה.

ההבדל העיקרי בין הגרסאות השונות הוא שהקוד של Claude הוא הרבה יותר "פייתוני". בקוד של Claude יש שימוש ב-Class, בניגוד לשני המודולים האחרים שכתבו פונקציות עצמאיות. כמו כן, שמות הפונקציות הן בקונבנציה הפייתונית של snake case, למרות שאני נתתי בפרומפט שלי שמות מפורשים להשתמש בהם לפונקציות ב- camel case.
הבדל נוסף שחשוב לציין הוא ש-Claude ו-Deepseek הוסיפו טסטים בסיסיים לקוד שלהם, ו-ChatGPT סיפק את הפונקציות בלבד.


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

כתבתי שרת בסיסי מאוד שקורא לכל אחד מהטמפלייטים שלי עם אותם משתנים התחלתיים רק כדי לראות שהכל רץ ועובד כמו שצריך. 

import os
from flask import Flask, redirect, render_template, request
app = Flask(__name__)
@app.route("/deepseek")
def deepseek():
    remaining = 3.8
    total=6.8
    # Render the HTML page current syringe status
    return render_template("tracker-deepseek.html", remaining=remaining, total=total)
@app.route("/chatgpt")
def chatgpt():
    remaining = 3.8
    total=6.8
    # Render the HTML page current syringe status
    return render_template("tracker-chatgpt.html", remaining=remaining, total=total)
@app.route("/claude")
def claude():
    remaining = 3.8
    total=6.8
    # Render the HTML page current syringe status
    return render_template("tracker-claude.html", remaining=remaining, total=total)
if __name__ == "__main__":
    port = int(os.environ.get("PORT", 4000))
    app.run(host="0.0.0.0", port=port)

אחרי העלאה לשרת הקוד של Claude ו-Deepseek התנהגו כמו קודם, והפעם דווקא ChatGPT הציג באג מוזר. הכפתור של ChatGPT עבד – אבל רק בלחיצה הראשונה. בלחיצות הבאות הכפתור נכשל והמסך לא התעדכן. לדעתי זו הוכחה נוספת שהגישה של Claude שהכשיל את הפונקציונליות של הכפתור כשה-API לא עבד היא הגישה הנכונה יותר, כי אני יודעת איפה לחפש את הבעיה. לדבג את הקוד של ChatGPT שנכשל חלקית יהיה מסובך יותר.


עכשיו אני רוצה לבדוק את הפונקציונליות של הבקאנד שלי. בתור התחלה רציתי להשתמש בפונקציה GetSyringeStatus עם ה-DB האמתי שלי, כדי לראות שהקריאה אליו עובדת כמו שצריך. לשם כך, שמרתי כמשתנה סביבה את ה-id של הדאטבייס שלי, וחיווטתי אליו את הקוד. עכשיו יש לי שני משתני סביבה שאני משתמשת בהם, אחד למפתח הסודי שלי ל-Notion, ואחד לדאטבייס.

אחרי דיפלוי מהיר גיליתי לצערי באג בחיבור לדאטבייס. ChatGPT ו-Deepseek הראו מזרקים ריקים, ו-Claude הראה שגיאה.

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

query = {
        "filter": {
            "property": "Status",
            "select": {
                "equals": "IN_USE"
            }
        }
    }

הבקשה הזו בנויה לשדה פשוט מסוג מחרוזת, אבל השדה שאני מפלטרת לפיו הוא בפועל מסוג status וזקוק לפילטר ספציפי

השגיאה שקיבלתי מ-Notion הייתה מצוינת ואינפורמטיבית – 

{"object":"error","status":400,"code":"validation_error","message":"The property type in the database does not match the property type of the filter provided: database property status does not match filter select"}

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

בשלב הזה למרבה הצער Claude גירש אותי כי נגמרו לי הבקשות החינמיות ו-Deepseek היה "עסוק מדי" כל הזמן. החלטתי שמפה והלאה יש לי בסיס מספיק יציב כדי להמשיך לבדי.


מסקנות לסיום – 

אם הייתי צריכה לבחור רק חבר אחד לתכנת איתו הייתי הולכת על Claude. כל הקוד שהוא כתב היה יותר טוב והתשובות שלו היו יותר מהירות. כמו כן, התצוגת קוד + התצוגה הוויזואלית שלו ממש נוחה. אני ממש שמחה שיצא לי לעשות את ההשוואה הזו, כי עד עכשיו לא יצא לי לעבוד הרבה עם Claude, ואני מרגישה ש"גיליתי" אותו סוף סוף. עם זאת, מבחינת חווית משתמש Deepseek היה קצת יותר נחמד מאשר Claude, אני חושבת שלשימושים אחרים אני כנראה אמשיך איתו לבינתיים. 


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

תכנות נעים!

תגובה אחת בנושא “כתיבת קוד תחרותית – ChatGPT, Deepseek & Claude

השאר תגובה