הבלוג של ינון פרק

טיפים קצרים וחדשות למתכנתים

יום 4 - בואו נכתוב סוכן שיחה

03/10/2025

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

דוגמאות למערכות כאלה יכולות להיות:

  1. ממשק צ'ט עם סוכן חכם כמו באתר ChatGPT.

  2. סוכן תמיכה חכם שעונה לפניות של משתמשים במקום נציג אנושי.

  3. סוכן מכירות חכם שמתקשר למשתמש ומנהל שיחה כדי למכור מוצר.

  4. מורה לאנגלית שמנהל עם משתמש שיעור אנגלית מובנה תוך כדי שיחה.

המשך קריאה

יום 3 - דוגמה לתהליך עבודה של שני סוכנים

02/10/2025

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

המשך קריאה

יום 1 - פיתוח מערכות אג'נטיות

30/09/2025

הי חברים,

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

המשך קריאה

כשהבדיקות נכשלות

29/09/2025

כשבדיקה אחת נכשלת אפשר להסתכל ולתקן.

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

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

כש 50 בדיקות נכשלות הדרך היחידה להתקדם היא למחוק את כל הבדיקות ולהתחיל מחדש.

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

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

תרגיל פייתון: הרחבת pairwise עם גודל וגודל צעד

28/09/2025

הפונקציה itertools.pairwise בפייתון לוקחת רשימה ומחזירה רשימה של זוגות חופפים מתוך הקלט המקורי, לדוגמה:

>>> import itertools
>>> list(itertools.pairwise(range(100)))
[(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (7, 8), (8, 9), (9, 10), (10, 11), (11, 12), (12, 13), (13, 14), (14, 15), (15, 16), (16, 17), (17, 18), (18, 19), (19, 20), (20, 21), (21, 22), (22, 23), (23, 24), (24, 25), (25, 26), (26, 27), (27, 28), (28, 29), (29, 30), (30, 31), (31, 32), (32, 33), (33, 34), (34, 35), (35, 36), (36, 37), (37, 38), (38, 39), (39, 40), (40, 41), (41, 42), (42, 43), (43, 44), (44, 45), (45, 46), (46, 47), (47, 48), (48, 49), (49, 50), (50, 51), (51, 52), (52, 53), (53, 54), (54, 55), (55, 56), (56, 57), (57, 58), (58, 59), (59, 60), (60, 61), (61, 62), (62, 63), (63, 64), (64, 65), (65, 66), (66, 67), (67, 68), (68, 69), (69, 70), (70, 71), (71, 72), (72, 73), (73, 74), (74, 75), (75, 76), (76, 77), (77, 78), (78, 79), (79, 80), (80, 81), (81, 82), (82, 83), (83, 84), (84, 85), (85, 86), (86, 87), (87, 88), (88, 89), (89, 90), (90, 91), (91, 92), (92, 93), (93, 94), (94, 95), (95, 96), (96, 97), (97, 98), (98, 99)]

בואו נרחיב אותה באמצעות שני פרמטרים:

  1. פרמטר size קובע את גודל ה״זוג״ (וכן כל מספר גדול מ-2 כבר לא יהיה זוג).

  2. פרמטר stride קובע את גודל הצעד כלומר החפיפה בין הזוגות.

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

המשך קריאה

שינוי קומפוננטות מבחוץ

27/09/2025

רוב הספריות הפופולריות לפיתוח צד לקוח לא תומכות בהרחבה של קומפוננטות מבחוץ וזה חבל. נדמיין שקיבלתי קומפוננטת ריאקט של מונה לחיצות:

export default function Counter() {
    const [count, setCount] = useState(0);

    return (
        <div>
            <p>Count = {count}</p>
            <button onClick={() => setCount(c => c + 1)}>+1</button>
        </div>
    );
}

אני רוצה להוסיף לו כפתור איפוס, אבל בלי לשנות את הקובץ המקורי של Counter. אפילו אם אני מוכן לשנות את הקריאות ל Counter מבחוץ כלומר לעטוף אותו באיזה CounterWithReset אין לי איך לעטוף את הקומפוננטה ולשנות את ה Virtual DOM שהיא מחזירה. זה פשוט לא בכללים.

בואו נראה שתי דוגמאות לספריות שכן מאפשרות מנגנון כזה ומה הופך אותו לאפשרי.

המשך קריאה

השאלה הלא נכונה של Vibe Coding

26/09/2025

"זה עובד?"

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

וזה בכלל לא משנה.

הנה כמה שאלות שתמיד היו חשובות וקשה לראות למה עכשיו פתאום לא אכפת לנו מהן יותר:

  1. כמה קל יהיה לשנות את הפרויקט כשהדרישות ישתנו?

  2. כמה משאבים זה צריך?

  3. כשזה יישבר, האם אדע לתקן?

  4. כשימציאו חומרה חדשה, מערכת הפעלה חדשה או גירסאות חדשות לספריות, כמה קל יהיה להתאים את המערכת לתשתית החדשה?

  5. אפשר לשבור את זה בטעות?

  6. אפשר לשבור את זה בכוונה?

  7. מה עדיין לא עובד?

  8. איזה דרישות חדשות עשויות להיות לי שלא יתאימו לארכיטקטורה שנבחרה ויכריחו אותי לבנות מערכת חדשה?

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

אז כבר לא צריך CSRF Token?

25/09/2025

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

<input name="__RequestVerificationToken" type="hidden" value="CfDJ8NrAkS ... s2-m9Yw">

טופס שיוצר בריילס הכיל את השדה:

<input type="hidden" name="authenticity_token" value="Y1OeREmhWaEzt4AVJJFzquSrJjHvZ-3rkxMde4IOzLznve33Dx3lYkXpkVkChj-VLIKUQK-ff_6hG9jPs9DTFA" autocomplete="off" />

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

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

החל מכרום גירסה 80 ופיירפוקס גירסה 79 (שינוי שקרה כבר ב 2020) ברירת המחדל של דפדפנים השתנתה. היום ברירת המחדל היא לא לשלוח Cookies בבקשות POST ולכן כל עוד לא ביטלתם את מנגנון ההגנה הזה עם הגדרת SameSite שונה לעוגיות שלכם אתם לכאורה מוגנים ממתקפות CSRF כבר ברמת הדפדפן.

למה לכאורה? יש שניים וחצי מצבים עם פוטנציאל בעייתי:

  1. אם הטופס שלכם משתמש בבקשה מסוג GET בטעות, העוגיות עדיין יישלחו גם מאתרים אחרים שמגישים את הטופס.

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

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

לכן ההמלצה היום היא להישאר עם שני המנגנונים מופעלים. לא לוותר על הגנת ה CSRF המובנית בפריימוורק ובמקביל לא לבטל את הגנת ה SameSite המובנית בדפדפן.

למה קשה ללמוד תכנות מ AI

24/09/2025

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

יש עם זה עדיין כמה בעיות:

  1. טמפלייט ההתחלה של לאבבל (או כל AI Builder) עבר אופטימיזציה להיות בסיס טוב לסוכן קידוד מבוסס AI. זה לא תמיד בסיס טוב ללמידה. הטמפלייט מכיל המון קונספטים שכל אחד מהם דורש עבודת למידה.

  2. ללמוד מ AI זה ללמוד את מה שעובד לא את מה שנכון. אם AI כתב באותה תוכנית את אותו מנגנון 3 פעמים ורק אחת מהפעמים עובדת (שני המימושים האחרים לא עובדים אבל במקרה גם לא שוברים כלום) אז מי שינסה ללמוד מה AI עלול ללמוד שיטה שבורה.

  3. ללמוד מ AI זה ללמוד משהו שמהר מאוד לא יהיה העבודה שלנו. אם AI יודע לכתוב את זה לבן אדם יש דברים יותר טובים לעשות.

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

לדוגמה נניח ש AI כתב לי פונקציה כזאת בתוך קומפוננטת ריאקט:

const formatTime = (seconds: number) => {
  const mins = Math.floor(seconds / 60);
  const secs = Math.floor(seconds % 60);
  return `${mins}:${secs.toString().padStart(2, '0')}`;
};

זאת יכולה להיות הזדמנות למידה מצוינת. אני רואה את הקוד, עוצר ומחפש ללמוד:

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

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

  3. מה זה Math. ? יש עוד דברים שאפשר לכתוב אחריו חוץ מ floor?

  4. מה משמעות השורה האחרונה בפונקציה? האם יש דרכים נוספות לכתוב אותה? מה זה אומר שכותבים נקודה ו toString אחרי משתנה? יש דברים נוספים שאפשר לכתוב במקום toString באותו הקשר?

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