1.2 הנדסת פרומפט הרצאה
"תכתוב לי פונקציה שממיינת רשימה" ו-"תכתוב לי פונקציה בפייתון שממיינת רשימה של מילונים לפי מפתח 'תאריך', מהחדש לישן, ומחזירה רשימה חדשה בלי לשנות את המקורית" - שני פרומפטים על אותו נושא, אבל הפער באיכות התשובה ביניהם עצום. הנדסת פרומפט - prompt engineering - היא לא קסם או "שפה סודית". זו פשוט המיומנות לנסח בקשה כך שהמודל יבין בדיוק מה רוצים ממנו, באותה מידה שהייתם מסבירים למתכנת חדש שהצטרף לצוות. בהרצאה הזו נפרק פרומפט טוב לחלקים, נראה דוגמאות לפני ואחרי, ונבנה היוריסטיקה פשוטה שאפשר להשתמש בה בכל בקשה.
למה זה בכלל משנה¶
מודל שפה לא קורא מחשבות. הוא מייצר את התשובה הכי סבירה בהינתן הטקסט שקיבל - ואם הטקסט הזה עמום, חסר context או פתוח לפרשנות, המודל ינחש. לפעמים הניחוש נכון, לרוב הוא מפספס פרטים שהיו חשובים לכם אבל לא נכתבו.
הבעיה לא ב"טיפשות" של המודל - היא בכך שהמידע פשוט לא היה זמין לו. פרומפט טוב לא "משכנע" את המודל לחשוב טוב יותר; הוא נותן לו את המידע שהוא צריך כדי לפתור בדיוק את הבעיה שלכם, ולא בעיה דומה אבל שונה.
אנטומיה של פרומפט טוב¶
פרומפט חזק בדרך כלל מכיל חלק מהמרכיבים הבאים, בסדר הגיוני:
| מרכיב | תפקיד | דוגמה |
|---|---|---|
| הקשר - context | מה הרקע? באיזה פרויקט/תחום מדובר | "אני בונה API ב-FastAPI שמנהל הזמנות למסעדה" |
| תפקיד - role | איזו נקודת מבט צריך המודל לאמץ | "תתנהג כמו מהנדס backend בכיר שסוקר קוד" |
| המשימה עצמה - task | מה בדיוק מבקשים, בפועל | "תכתוב endpoint שמקבל הזמנה חדשה ושומר אותה בבסיס הנתונים" |
| אילוצים - constraints | מגבלות, כללים, מה אסור | "בלי תלות חיצונית מעבר ל-SQLAlchemy, עם טיפול בשגיאות ולידציה" |
| פורמט פלט - output format | איך התשובה צריכה להיראות | "רק קוד, בלי הסבר מסביב, עם הערות קצרות בעברית" |
| דוגמאות - examples | הדגמה של הצורה הרצויה | "בדיוק כמו ה-endpoint הזה שכבר יש לי: [קוד לדוגמה]" |
לא כל פרומפט צריך את כל השישה. שאלת הבהרה קצרה ("מה ההבדל בין let ל-const?") לא צריכה context או פורמט. אבל ככל שהמשימה מורכבת יותר, ככל שיש יותר "דרך נכונה אחת" לבצע אותה בפרויקט הספציפי שלכם - כך שווה יותר להשקיע בכל מרכיב.
למה כל מרכיב חשוב בפני עצמו¶
הקשר - context הוא הבסיס שעליו כל השאר נבנה. בלי context, המודל פותר את הבעיה הכללית ביותר האפשרית - "פונקציה שממיינת רשימה" יכולה להתאים לאלף תרחישים שונים. הcontext מצמצם את המרחב הזה לתרחיש הספציפי שלכם.
תפקיד - role משפיע על נקודת המבט ורמת הפירוט של התשובה. "תסביר לי מה זו רקורסיה" מול "תסביר לי מה זו רקורסיה כאילו אני מרצה שמכין שיעור למתחילים לגמרי" - שתי הבקשות מייצרות תוכן שונה מאוד, אף שהשאלה הבסיסית זהה. תפקיד שימושי במיוחד כשרוצים טון או עומק מסוימים: "תתנהג כמו בודק QS קפדני" ייצר רשימת מקרי קצה ארוכה יותר מ"תבדוק את הקוד".
המשימה עצמה - task צריכה להיות פועל ברור ויחיד ברוב המקרים: "תכתוב", "תסביר", "תתקן", "תשווה". פרומפט שמערבב כמה משימות בלי סדר ("תכתוב ותסביר ותבדוק ותשווה לגרסה הקודמת") לרוב מייצר תשובה שמנסה לעשות הכל בחצי מהעומק. אם יש כמה משימות, עדיף לפרק אותן לסבבים נפרדים (נדבר על זה בהרצאה על טכניקות מתקדמות).
אילוצים - constraints הם המקום שבו הכי משתלם להשקיע, כי הם המידע שהכי סביר שהמודל ינחש לא נכון בלעדיו: גרסת שפה, ספריות מותרות, מגבלות ביצועים, דרישות תאימות לאחור. כל אילוץ שלא נכתב הוא הימור על כך שהמודל ינחש בדיוק את מה שהתכוונתם.
פורמט פלט - output format קובע איך קל יהיה להשתמש בתשובה. בקשה ל"קוד בלבד, בלי הסברים" חוסכת זמן גלילה כשרוצים להעתיק ישירות לעורך; בקשה ל"טבלה משווה" הופכת השוואה מילולית ארוכה לקריאה נוחה בשנייה.
דוגמאות - examples הן הדרך המהירה ביותר להעביר "צורה" שקשה לתאר במילים - נראה את זה בהרחבה בהרצאה הבאה על טכניקות מתקדמות, כשנדבר על למידה מדוגמאות - few-shot.
ההיוריסטיקה: "מה הייתי נותן לעובד חדש"¶
הדרך הפשוטה ביותר לבדוק אם פרומפט מספיק טוב: תדמיינו שאתם מעבירים את אותה בקשה למתכנת חדש שהצטרף לצוות היום. הוא חכם ומיומן, אבל לא מכיר את הפרויקט שלכם, לא יודע מה ההעדפות שלכם, ולא יכול לשאול אתכם שאלות (אלא אם אתם מבקשים ממנו במפורש). מה הוא היה צריך לדעת כדי לבצע את המשימה נכון בפעם הראשונה?
- הוא לא יודע איזו שפה/פריימוורק - צריך לכתוב.
- הוא לא יודע אם יש קונבנציות קוד קיימות - צריך להראות דוגמה או לתאר אותן.
- הוא לא יודע כמה "עמוק" לפתור - האם זה פרוטוטייפ מהיר או קוד production עם טיפול בכל מקרה קצה.
- הוא לא יודע איך אתם רוצים לקבל את התוצאה - קובץ שלם, diff, רק ההסבר.
זו בדיוק הבדיקה שהופכת פרומפט עמום לפרומפט מדויק. בואו נראה אותה בפעולה.
דוגמאות לפני ואחרי¶
דוגמה 1: כתיבת פונקציה¶
לפני:
אחרי:
I'm writing a Python script that processes a list of orders from an
API. Each order is a dict with keys: id, customer, total, created_at
(a string in ISO 8601 format).
Write a function sort_orders(orders: list[dict]) -> list[dict]
that sorts by created_at from newest to oldest, and does not modify
the original list.
Requirements:
- Full type hints
- If created_at is missing or invalid in a given item, it should go
to the end of the list without raising an error
- Only the function code, no usage example
הפרומפט השני נותן למודל את כל מה שמתכנת חדש היה צריך: מבנה הנתונים המדויק, סוג המיון, טיפול במקרה קצה (תאריך חסר), וסגנון הפלט הרצוי. אין ניחושים.
דוגמה 2: דיבוג¶
לפני:
אחרי:
I'm running a Node.js server with Express and getting the following
error when I send a POST request to /users:
TypeError: Cannot read properties of undefined (reading 'email')
at createUser (/app/routes/users.js:14:22)
The relevant code:
[paste the createUser function here, 10-15 lines]
The request is sent with Postman, the request body is valid JSON
with an email field.
Explain what's causing the error and suggest a fix.
בלי traceback, בלי קוד ובלי תיאור איך הבקשה נשלחת, המודל יכול רק לנחש קטגוריות כלליות של שגיאות. עם המידע הזה הוא יכול לאבחן במדויק (לרוב: חוסר express.json() middleware, ולכן req.body מגיע undefined).
דוגמה 3: כתיבת טקסט¶
לפני:
אחרי:
Write a product description for an online store, for wireless
noise-canceling headphones.
Target audience: tech people who work from home and want quiet
during calls.
Tone: direct and professional, no exaggerated superlatives ("the
best in the world").
Length: 3-4 sentences.
Must mention: 30-hour battery, Bluetooth 5.3 connection, 250 gram
weight.
שימו לב שגם במשימה "יצירתית" כמו כתיבת שיווקי, אותם מרכיבים חוזרים: קהל יעד (context), טון (constraint), אורך ופרטים חובה (output format + task).
דוגמה 4: סקירת קוד - code review¶
לפני:
אחרי:
Do a code review of the following function, as if you were a
senior backend engineer reviewing a Pull Request before merging to
main. The code is meant to run in a production environment with
high load (thousands of requests per second).
Focus on three things only: concurrency issues, unhandled edge
cases, and missing error handling. Don't comment on code style or
naming - we have a separate linter for that.
Return the findings as a list, for each finding: severity
(high/medium/low), a short description, and a suggested fix.
[paste the function here]
מה מקבלים: סקירה ממוקדת בדיוק בשלושת התחומים שביקשתם, בפורמט קל לסריקה, בלי "רעש" של הערות סגנון שלא מעניינות אתכם כרגע.
הבדל מרכזי מהדוגמאות הקודמות: כאן התפקיד (role) והאילוץ (מה לא לבדוק) הם המרכיבים שעושים הכי הרבה עבודה. בלעדיהם, סקירת קוד "כללית" נוטה להתפזר על הכל - סגנון, ביצועים, קריאות - ולתת תשובה שטחית בכל תחום במקום עומק באחד.
הפער בין "עובד" ל"מדויק"¶
חשוב להבהיר: הפרומפט הקצר ב"לפני" בדרך כלל כן מייצר תשובה. הבעיה היא לא שהמודל נכשל - הבעיה שהוא פותר בעיה כללית מדי, ואז אתם צריכים לחזור ולתקן ("לא, התכוונתי שזה יהיה טיפוס X", "לא, בפרויקט שלי זה עובד אחרת"). כל סבב תיקון כזה עולה זמן. ההשקעה בפרומפט טוב מראש בדרך כלל מהירה יותר מכמה סבבי תיקון.
זה לא אומר שצריך תמיד לכתוב פסקאות ארוכות. שאלה פשוטה כמו "מה ההבדל בין let ל-const ב-JavaScript?" לא צריכה שדרוג - היא כבר ממוקדת וברורה. הנדסת פרומפט היא לא "כתבו יותר" אלא "כתבו את מה שחסר לתשובה נכונה".
כיול הפירוט לפי מורכבות המשימה¶
שאלה נפוצה: "כמה זה מפורט מדי?" אין נוסחה מדויקת, אבל יש כלל אצבע שימושי - כמות הפירוט צריכה לגדול עם מספר ההחלטות שהמודל צריך לקבל בלעדיכם.
| סוג משימה | רמת פירוט נדרשת | דוגמה |
|---|---|---|
| שאלת ידע כללי | מינימלית | "מה ההבדל בין TCP ל-UDP?" |
| טרנספורמציה חד-פעמית | בינונית - פורמט קלט/פלט | "תמיר את ה-JSON הזה לטבלת Markdown" |
| כתיבת קוד בפרויקט קיים | גבוהה - context, קונבנציות, אילוצים | הדוגמאות שראינו למעלה |
| החלטה ארכיטקטונית | גבוהה מאוד - אילוצים, חלופות שנשקלו, קריטריונים להחלטה | "תשווה בין Redis ל-in-memory cache למקרה השימוש הזה, בהתחשב בכך שיש לנו כבר Redis לסשנים" |
הסימן שהפרומפט קצר מדי: קיבלתם תשובה טכנית תקינה אבל היא פותרת בעיה קצת שונה ממה שהתכוונתם, וצריך סבב תיקון. הסימן שהוא ארוך מדי: אתם כותבים פרטים שלא ישפיעו על התשובה בכלל (תיאור ארוך של למה אתם צריכים את זה, כשהמודל לא צריך לדעת את הרקע ההיסטורי, רק את הדרישה הנוכחית). עם הזמן, כיול הפירוט הזה הופך לאינטואיטיבי.
מלכודות נפוצות¶
- עמימות בכינויי גוף. "תתקן את זה" בלי לציין מה "זה" כשיש כמה קבצים או פונקציות בcontext - המודל ינחש לפי context קרוב, ולפעמים יטעה.
- הנחת ידע לא כתוב. "תכתוב את זה כמו שאנחנו עושים בפרויקט" - אם המודל לא ראה את הקונבנציה, הוא לא יודע מה "אנחנו עושים". צריך להראות דוגמה או לתאר אותה.
- בקשות מרובות בלי סדר עדיפויות. "תכתוב פונקציה מהירה, קריאה, קצרה וגנרית" - לפעמים הדרישות סותרות זו את זו, ואז שווה לציין מה חשוב יותר.
- שכחת פורמט הפלט. אם לא מציינים, המודל יכול להחזיר הסבר ארוך לפני הקוד, כשרציתם רק את הקוד. משפט אחד ("רק קוד, בלי הסברים") חוסך את זה.
סיכום¶
- פרומפט טוב מורכב ממרכיבים: הcontext, תפקיד, המשימה עצמה, אילוצים, פורמט פלט ודוגמאות - לא כולם נדרשים בכל פעם.
- ההיוריסטיקה המרכזית: תנו למודל בדיוק את מה שהייתם נותנים לעובד חדש שלא מכיר את הפרויקט ולא יכול לשאול אתכם שאלות.
- הפער בין פרומפט עמום לפרומפט מדויק הוא לא "עובד מול לא עובד" - הוא כמה סבבי תיקון תצטרכו כדי להגיע לתשובה הנכונה.
- דוגמאות (few-shot) וcontext קונקרטי (מבנה נתונים, שגיאה מדויקת, קהל יעד) משפרים תשובות הרבה יותר מניסוח "יצירתי" יותר של הבקשה.
- מלכודות נפוצות: עמימות בכינויי גוף, הנחת ידע שלא נכתב, דרישות סותרות בלי סדר עדיפויות, ושכחה לציין פורמט פלט רצוי.
- לא כל פרומפט צריך להיות ארוך - המטרה היא שלמות המידע, לא אורך.
- כיול הפירוט: ככל שיש יותר החלטות שהמודל צריך לקבל בלעדיכם (קונבנציות פרויקט, אילוצי ביצועים, בחירה בין חלופות), כך שווה להשקיע יותר בפרומפט - שאלת ידע כללית לא צריכה את אותו טיפול כמו החלטה ארכיטקטונית.
המיומנות הזו משתפרת עם תרגול, ולא דורשת זיכרון מושלם של רשימת המרכיבים. מספיק לשאול את עצמכם, לפני שלוחצים Enter: "אם הייתי נותן את הבקשה הזו למתכנת חדש בצוות, האם הוא היה יודע בדיוק מה לעשות?" אם התשובה "לא בטוח" - זה הרגע להוסיף עוד משפט או שניים.