פורסם ב כלים לחיים קלים

[פוסט אורחת] Lingohelp

קוד: lingohelp

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

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

ההשראה

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

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

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

התכנון

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

אחרי שסיימתי לאפיין את הפונקציונליות של השפה, ניגשתי לשלב הבא – דיזיין טכני. בחרתי להגדיר cron job שבחצות הליל ישלח את הפרומפט המיוחל שמכיל את השפה הנלמדת, רשימת המילים והבקשה לכתיבת הסיפור ל-Gemini – כלי ה-AI של גוגל. התשובה שתתקבל תשלח למשתמשת במייל. כותרת המייל תהיה ״תרגול ה[הכניסי-שפה] היומי שלך״, והוא יכיל סיפור קצר בשפה הנלמדת שמכיל בתוכו את המילים ופירוש כל מילה בתחתית הסיפור.

תוך כדי עבודה חשבתי על פיצ׳רים נוספים שמשפרים את הליך הלמידה, כמו נניח:

  • ניתוח של מילים שהכנסתי למערכת מספר פעמים. אם התקשיתי לזכור אותן, אולי אפשר לתרגל אותן מעט יותר.
  • מסך נוסף של quiz שיבקש מהמשתמשת להתאים בין המילה לפירוש שלה. 
  • הוספת פונקציית שמע, על מנת לדעת להגות את המילה כמו שצריך. בטח תופתעו לגלות שיש הבדל בהגייה של dessus (מעל) ו-dessous (מתחת). נו, מה אפשר לצפות משפה שבה 90 זה 4 פעמים עשרים ועוד עשר.

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

הביצוע

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

  1. טבלת שפות שנתמכות במערכת. 
  2. טבלה שמאגדת את כלל המילים שלמדתי אי פעם, בכל השפות.

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

בטבלת המילים אני שומרת את המילה, את הקונטקסט, ואת השפה שמהווה foreign key, או בעברית: מפתח זר – כזה שהערכים שלו נשאבים משדה מקביל בטבלה אחרת אשר בה משמש השדה כ-primary key. במקרה הזה, החלטתי להגדיר את השפה כ-foreign key מפני שהעדפתי שבשליפה אחת של המילים מה-DB, יופיע לי גם השם של השפה, ולצורך שימוש עתידי, החלטתי להמנע בשימוש במחרוזת כערכים לשפה, כדי לשמור על אחידות מיטבית.

בשלב הבא רציתי לבדוק אם הקוד עובד. התחלתי לייצר קריאות מהשרת שלי ל-Notion, ולאט לאט הוספתי מילים. כשראיתי שהמילים מתווספות לטבלאות בהצלחה, עברתי ליצירת ה-cron job, שהוא למעשה משימה מתוזמנת שחוזרת על עצמה לפי הגדרת המשתמש, נניח גיבוי שנעשה מדי חמש דקות, או polling – תשאול מתוזמן שמאפשר לנו למשוך מידע בזמן אמת. השתמשתי בחבילה שנקראת node-schedule. זה כלי נוח מאד שניתן לתזמן באמצעותו בקלות לתוך פורמט מובנה את הזמן בו נרצה שהג׳וב יתבצע. בחרתי להגדיר את הזמן לחצות, כי אני יוצאת מנקודת הנחה שאם נתקלתי במילה שלא הכרתי בספר או בסדרה כלשהי, ייתכן שאפגוש אותה שוב מהר מן המצופה, ועל כן יש חשיבות לתרגל אותה בסמיכות לזמן בו קראתי או שמעתי אותה. כלל המילים שנלמדו ביום הקודם יימשכו מ-Notion, ובעבור כל שפה, אשלח פרומפט בנפרד. 

בשלב הזה חשבתי שאוכל לעשות זאת באמצעות ChatGPT אבל לצערי, השימוש ב-API שלו אינו חינמי, ולבסוף בחרתי ב-Gemini. לאחר שהתייעצתי איתו, גיליתי שקיים לו SDK. השימוש שלי בו היה די בסיסי והוא ענה לי על הצורך בתוך פרומפט אחד בלבד. התקנתי את ה-Client של Google Generative AI, קינפגתי ויצרתי את הפונקציה שמקבלת את רשימת המילים ואת השפה, מייצרת את הפרומפט ושולחת אותו לשרת.


על מנת לשלוח את המייל למשתמשת, בחרתי להשתמש ב-Nodemailer שגם היא חבילה של NPM. למעשה, המשתמשת שולחת את המייל לעצמה באמצעות ה-credentials שלה וישנו צורך בחשבון Gmail לשם כך. ישנה קונפיגורציה פשוטה שצריך לעשות ב-Gmail ושלאחריה ניתן לייצר את החיבור. לטובת יצירת התבנית למייל, בחרתי להשתמש ב-Handlebars שהיא שפת תבניות פשוטה. באמצעות תבנית ואובייקט קלט, היא מסוגלת לייצר קובץ HTML או פורמטים אחרים של טקסט. אז יצרתי לעצמי קובץ Handlebars בסיסי למדי, שלתוכו אני מזריקה את ה-variables שלי שהם כאמור השפה, רשימת המילים והסיפור המג׳ונרט ומקבלת בחזרה את קובץ ה-HTML שנשלח למשתמשת.



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

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

מסקנות

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

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

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

3 תגובות בנושא “[פוסט אורחת] Lingohelp

  1. מהמם! האם הפרויקט זמין כקוד פתוח? הייתי שמחה לנסןת

    1. היי רעות! תודה רבה.
      בשלב הזה לא ניתן לתרום אליו אבל מוודאה שאכין ראית את הקישור ל-repo שנמצא במעלה הדף.

השאר תגובה