• בלוג
  • מתכנתים בעידן ה AI לעומת מפעילי AI דרך דוגמת קוד

מתכנתים בעידן ה AI לעומת מפעילי AI דרך דוגמת קוד

01/02/2026

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

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

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

1. צורת החשיבה של מהנדסי AI

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

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

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

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

2. דוגמה: חיבור פוסטים מהבלוג בתור סיכומים לוובינר

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

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

בלי לחשוב יותר מדי כתבתי את הפרומפט הבא ונתתי ל AI לרוץ עם הפיתוח:

Add blog post as summary feature

In talking_ai page we can see previous webinars. Each previous webinar
can have a link to video summary and/or fulltext markdown summary

Modify the app and add a way to connect a previous webinar to a blog
post, so the blog post is the text summary.
A connected webinar will have a the "Text Summary" link lead to that
blog post

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

3. נסיון ראשון

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

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

  2. הסוכן החליט לשנות את הטקסט של התיבה לצירוף קובץ סיכום בפורמט Markdown בלי שביקשתי.

  3. הסוכן מימש "תאימות אחורה" בנתיב הורדת ה Markdown, כלומר אם מישהו לוחץ "הורדת קובץ סיכום ב Markdown" לוובינר שמוגדר לו סיכום בתור פוסט הוא יופנה לפוסט הסיכום ולא יקבל את קובץ ה Markdown.

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

  5. הקוד שנוצר ב view יצא מסורבל וכלל יותר לוגיקה ממה שאני אוהב לכתוב בקבצי view ובאג ביצועים שנקרא N+1 Queries:

<% if session.has_text_summary? %>
  <% text_summary_url = session.blog_post.present? ? blog_path(session.blog_post) : talkingai_summary_path(session) %>
  <a href="<%= text_summary_url %>" class="inline-flex items-center px-3 py-2 bg-blue-600 text-white rounded-lg hover:opacity-90" data-turbo="false" title="<%= t('talking_ai.download_summary') %>"<%= ' target="_blank" rel="noopener noreferrer"' if session.blog_post.present? %>>

בעיית הביצועים כאן נובעת מהפקודה:

blog_path(sesison.blog_post)

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

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

4. נסיון שני

הדבר הראשון שעשיתי היה לכתוב קובץ הוראות לסוכן AGENTS.md התחלתי עם הגרסה הבאה:

  # ToCode Platform - Online Courses and Personal Website of Ynon Perek
  This is a Ruby on Rails project that handles the personal website of Ynon Perek, provides a blog, online courses sold through the platform and weekly webinars.

  ## How to parse instructions
  When reading a feature request:
  1. Read `schema.rb` to understand the different models relevant to the feature
  2. Is this request the best way to approach the issue? 
  3. What are the most probably next changes user is about to make? can you solve a more general problem that will also solve this request and help us in the future?
  4. Push back, challenge the user and ask relevant questions on every feature request

  ## Coding Standards and approaches
  1. Backward compatibility is not that important, simple and beautiful code is. Don't leave behind dead code.

  2. Don't put logic in the views, only in models.

  3. You can use Rails patterns and best practices when relevant, for example service objects, presenter objects.

  4. When selecting data prefer JOIN with relevant columns over include. Avoid N+1 queries.

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

התוצאה:

  1. מסך ה admin עדיין מראה תיבת select. זה הגיוני, לא התיחסנו לנושא הזה בקובץ ההוראות.

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

def text_summary_url
  return blog_path(blog_post) if blog_post.present?
  return talkingai_summary_path(self) if text_summary.present?
  nil
end

זה רעיון יפה! הבעיה שעכשיו ה view יצא יותר גרוע:

<% if session.has_text_summary? %>
  <a href="<%= session.text_summary_url %>" class="inline-flex items-center px-3 py-2 bg-blue-600 text-white rounded-lg hover:opacity-90" data-turbo="false" title="<%= t('talking_ai.text_summary') %>">
    <%= t('talking_ai.text_summary') %>

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

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

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

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

5. נסיון שלישי

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

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

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

def text_summary_button?
  @session.text_summary.present?
end

def text_summary_label
  I18n.t("talking_ai.download_summary")
end

def text_summary_url
  talkingai_summary_path(@session)
end

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

  1. עדכנתי גם את קובץ ההוראות לתוכן הבא:
# ToCode Platform - Online Courses and Personal Website of Ynon Perek
This is a Ruby on Rails project that handles the personal website of Ynon Perek, provides a blog, online courses sold through the platform and weekly webinars.

6. How to parse instructions

When reading a feature request: 1. Read schema.rb to understand the different models relevant to the feature 2. Is this request the best way to approach the issue? 3. What are the most probably next changes user is about to make? can you solve a more general problem that will also solve this request and help us in the future? 4. Push back, challenge the user and ask relevant questions on every feature request 5. When instructions create new database entities or connection explain the entity and connection and list sample data.

7. Coding Standards and approaches

  1. Don't put logic in the views, only in models.

  2. You can use Rails patterns and best practices when relevant, for example service objects, presenter objects.

  3. When selecting data prefer JOIN with relevant columns over include. Avoid N+1 queries.

8. Performance

Performance and page load time is critical. After every change review to check for N+1 query problems and possible JOIN optimisations. Count the number of queries in the diff to verify each query added is justified.

9. Backward Compatibility vs. Simplicity

We prefer simple code over backward compatibility. When adding new features don't hesitate to remove previous implementations and do not implement code to handle "both" new and old behavior. ```

תוצאה:

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

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

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

   def text_summary_url
-    talkingai_summary_path(@session)
+    if @session.blog_post.present?
+      blog_path(@session.blog_post)
+    else
+      talkingai_summary_path(@session)
+    end
   end

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

10. נסיון 4 ואחרון - תיקון שמות ותיקון הפרומפט

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

so the blog post is the text summary.

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

  1. השינוי הראשון הוא להחליף את הפונקציה שמחזירה את סיכום הטקסט. הוצאתי אותה לקובץ נפרד כדי שיהיה ברור שהוא מטפל רק בהורדות. בנוסף הוצאתי מהפונקציה קוד שמטפל ביצירת שם הקובץ וכל לוגיקה אחרת והעברתי אותה למודל כדי לא לבלבל. סך הכל הפונקציה שמטפלת בהורדת סיכום Markdown היא כעת:
module TalkingAi
  class DownloadSummariesController < ApplicationController
    def show
      session = TalkingaiSession.with_downloadable_markdown.find(params[:id])

      markdown, filename = session.prepare_download_markdown
      send_data markdown,
                filename:,
                type: 'text/markdown; charset=utf-8',
                disposition: 'attachment'
    end
  end
end

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

שינוי שני שעשיתי שוב באותו נושא היה לשנות את שמות הפונקציות ב Presenter כך שיהיה ברור שהן מיועדות למידע עבור הורדת קובץ ה Markdown כלומר:

-  def text_summary_button?
+  def download_summary_button?
     @session.text_summary.present?
   end

-  def text_summary_label
+  def download_summary_label
     I18n.t("talking_ai.download_summary")
   end

-  def text_summary_url
+  def download_summary_url
     talkingai_summary_path(@session)
   end

ורק אז הדבקתי את הפרומפט המתוקן:

Add blog post as summary feature

In talking_ai page we can see previous webinars. Each previous webinar
can have a link to video summary and/or fulltext markdown summary

Modify the app and add a way to connect a previous webinar to a blog
post.
A connected webinar will have (in addition to existing capabilities) a new button "קישור לסיכום" that will be a link to the connected post

התוצאה:

  1. מסך הניהול מציג קומפוננטה לבחירת פוסט ספציפית לבחירה מתוך הבלוג ולא Select גנרי.

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

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

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

  5. הקוד שנוצר ב view לא כולל בעיית N+1 Queries וכל המידע נטען מראש בשאילתה אחת.

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

11. נ.ב. ומה אם היינו מפעילים מראש את הפרומפט המתוקן?

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

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

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

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

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

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

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

עדיין אין לכם את הלינק לזום שלנו? לא מסובך רק צריך להירשם כאן ומקבלים אוטומטית למייל: https://www.tocode.co.il/talking_ai