1.3 טכניקות מתקדמות הרצאה
בהרצאה הקודמת בנינו פרומפט טוב מהמרכיבים הבסיסיים - context, תפקיד, משימה, אילוצים ופורמט. עכשיו נוסיף שכבה של טכניקות ממוקדות, שכל אחת פותרת בעיה ספציפית: איך נותנים דוגמאות בלי לכתוב פסקה שלמה, איך גורמים למודל "לחשוב לפני שהוא עונה", איך הופכים בקשה מעורפלת למפרט מדויק, ואיך משתמשים במודל כדי לבקר ולשפר את עצמו. אלה לא טריקים - הן דרכים מובנות להוציא תשובה טובה יותר כשהמשימה מורכבת יותר מ"תכתוב לי X".
דוגמאות מכוונות - few-shot prompting¶
הדרך המהירה ביותר להעביר "צורה" רצויה היא לא לתאר אותה במילים, אלא להראות דוגמה או שתיים. הטכניקה הזו נקראת few-shot (בניגוד ל-zero-shot, כשמבקשים בלי שום דוגמה). המודל לומד מהתבנית של הדוגמאות ומחקה אותה על הקלט החדש.
זה שימושי במיוחד כשקשה לתאר את הפורמט הרצוי במילים, אבל קל להראות אותו:
I need to convert variable names from snake_case to camelCase.
Examples:
user_name -> userName
total_price -> totalPrice
is_active_user -> isActiveUser
Now convert the following list:
order_created_at
max_retry_count
db_connection_string
בלי הדוגמאות, המודל כנראה יבין את הבקשה נכון גם ככה (זה מקרה פשוט) - אבל בפורמטים חדשים, לא סטנדרטיים, או ספציפיים לפרויקט שלכם, דוגמה אחת שווה יותר מפסקת הסבר:
Fill in a commit message template based on the examples:
feat(auth): add password reset flow
fix(api): handle null response from payment gateway
refactor(db): extract query builder into separate module
Now write a commit message for this change: I added validation to
the email field in the signup form, and fixed a type error that
caused a crash.
כלל אצבע: 2-3 דוגמאות בדרך כלל מספיקות. יותר מזה בדרך כלל לא משפר משמעותית, ומוסיף אורך מיותר לפרומפט.
וריאציה שימושית: דוגמאות שליליות - הראו גם מה לא רוצים. אם ניסיתם כבר וקיבלתם תוצאה קרובה אבל לא מדויקת, אפשר להראות אותה כדוגמה למה להימנע ממנו:
Write me headlines for a blog post about time management.
Not like this (too generic, clichéd):
"5 Tips for Better Time Management"
Like this (specific, curiosity-inducing):
"Why Your To-Do List Is Failing You - And What to Do Instead"
Now write 5 headlines in the style of the good example.
השילוב של דוגמה חיובית ושלילית יחד מעביר את הכוונה הרבה יותר מדויק מכל אחת לבד.
שרשור מחשבה - chain of thought¶
כשמבקשים תשובה ישירה למשימה שדורשת כמה שלבי היגיון (חישוב, ניתוח לוגי, איתור באג עדין), המודל לפעמים "קופץ" למסקנה בלי לעבור את כל השלבים - בדיוק כמו אדם שעונה מהבטן במקום לחשוב. הבקשה המפורשת "תחשוב שלב אחר שלב" - think step by step - משפרת דיוק במשימות כאלה, כי היא מכריחה את המודל לפרק את הבעיה במפורש לפני שהוא נותן מסקנה.
בלי שרשור מחשבה:
I have a function that's supposed to calculate a tiered discount:
10% on orders over 100, 20% on orders over 500. For an order of
exactly 500, the function returns a 10% discount instead of 20%.
Why?
עם שרשור מחשבה:
I have a function that's supposed to calculate a tiered discount:
10% on orders over 100, 20% on orders over 500. For an order of
exactly 500, the function returns a 10% discount instead of 20%.
Go step by step through the possible logic of the function
(conditions, boundaries of > vs >=), identify where an off-by-one
mistake could be, and only at the end explain the most likely
reason.
הגרסה השנייה מכוונת את המודל לעבור במפורש על השאלה "האם התנאי הוא > או >=", מה שמוביל ישירות לאבחון הנכון (הפונקציה כנראה בדקה total > 500 במקום total >= 500). זו לא קסם - זו פשוט הכוונה של המודל לא לדלג על שלב הביניים שבו נמצאת התשובה.
טכניקה קרובה: לבקש מהמודל לפרט כמה אפשרויות לפני שהוא בוחר אחת - "תציע שתי דרכים אפשריות לפתור את זה, עם יתרונות וחסרונות של כל אחת, ורק אז תמליץ".
פירוק משימה למשימות משנה - task decomposition¶
משימה גדולה ומורכבת ("תבנה לי מערכת ניהול מלאי") היא לעיתים קרובות בעצם כמה משימות קטנות שנארזו יחד. במקום לבקש הכל בפרומפט אחד ולקבל תשובה שמנסה לכסות הכל בשטחיות, אפשר לבקש מהמודל עצמו לפרק את המשימה, ואז לעבוד על כל חלק בנפרד:
I want to build a basic inventory management system for a small
store. Before we start writing code, break this down into
independent sub-tasks, in a logical dependency order (what's needed
before what).
Possible answer from the model:
1. Define the data model (product, quantity, price, supplier)
2. Basic operations - add, update quantity, delete
3. Alert when quantity drops below a threshold
4. Periodic inventory report
5. Interface (CLI or web) for accessing the operations
מכאן אפשר לעבוד משימה-משימה, ולתת לכל אחת פרומפט ממוקד עם הcontext של מה שכבר נבנה. זה גם עוזר לזהות מראש תלויות שלא חשבתם עליהן (למשל שinterface ה-web תלוי בכל שאר החלקים) לפני שמתחילים לכתוב.
מתן מפרט - spec-driven prompting¶
כשהמשימה מורכבת (יותר מפונקציה בודדת - למשל מודול שלם, API, סכמת נתונים), עדיף לא לבקש ישר "תכתוב", אלא לבנות קודם מפרט - spec ורק אחר כך לממש. זה מדמה איך צוותי הנדסה אמיתיים עובדים: קודם מסכימים על "מה", אחר כך על "איך".
Step 1 - the spec:
"I want to build a queueing system for managing orders at a
restaurant. Before you write code, write me a short spec: what
entities are needed, what operations are possible on each entity,
and what edge cases are worth thinking about (a canceled order, a
table booked twice)."
-> The model returns a structured list: entities (order, table,
customer), operations (create, cancel, change time), edge cases.
Step 2 - the implementation:
"Great, the spec looks good. Now implement the order entity as a
Python class with a dataclass, including handling the edge cases
you mentioned."
היתרון: קל בהרבה לתפוס טעות הבנה במפרט קצר (משפט או שניים) מאשר בקוד מלא. אם המודל "פספס" ישות חשובה או מקרה קצה קריטי, זה נראה מיד בשלב המפרט - ולא אחרי שכבר נכתבו 200 שורות קוד.
שכלול איטרטיבי - iterative refinement¶
כמעט אף פעם לא כדאי לצפות לתשובה מושלמת בפרומפט הראשון. עבודה עם צ'אט היא בדרך כלל שיחה, לא בקשה בודדת. הדרך היעילה: להתחיל מבקשה סבירה, ולתקן בהודעות המשך ממוקדות במקום לכתוב פרומפט "מושלם" מראש שמנסה לצפות כל דבר.
Prompt 1: "Write an endpoint in Express that receives a list of
products and returns them sorted by price."
Response: basic working code.
Prompt 2: "Good, now add support for a query parameter 'order'
that can be asc or desc."
Prompt 3: "And return a 400 error if order isn't one of these two
values."
כל סבב בונה על הקודם, בלי לחזור ולהסביר את כל הcontext מחדש - כי הוא כבר בשיחה. זה גם מקטין את הסיכון לפרומפט ראשוני עמוס מדי שמנסה "לנחש" כל דרישה אפשרית מראש.
ביקורת עצמית - self-critique¶
טכניקה חזקה ולא מוכרת מספיק: לבקש מהמודל לבקר את התשובה שלו לפני שמקבלים אותה כסופית. מודל שפה, בדיוק כמו מתכנת אנושי, לרוב מוצא בעיות בקוד שלו כשמבקשים ממנו במפורש לחפש אותן - למרות שהוא לא מצא אותן "מיוזמתו" בתשובה הראשונה.
Prompt 1: "Write a function that validates a credit card number
using the Luhn algorithm."
-> The model returns code.
Prompt 2: "Now review the code you wrote as if you were doing a
code review for someone else: are there unhandled edge cases? Is
there a performance issue? Are the types correct? Write down the
findings, then fix them."
וריאציה שימושית נוספת: לבקש מהמודל "מה היית עושה אחרת אם היית כותב את זה שוב מאפס?" - זו שאלה שמוציאה לפעמים תובנות שלא עלו בביקורת הראשונה, כי היא מזמינה השוואה לגישה חלופית שלמה ולא רק תיקון מקומי.
חשוב לזכור את המגבלה: ביקורת עצמית משפרת את הסיכוי לתפוס טעויות, אבל היא לא ערובה. המודל עדיין יכול לפספס בעיה גם בסבב הביקורת. זה כלי לשיפור הסתברות, לא תחליף לבדיקה אמיתית - הרצת בדיקות, קריאת קוד בעצמכם.
טכניקה שקרובה ברוח לביקורת עצמית: לבקש מהמודל "דרג את הביטחון שלך בתשובה הזו מ-1 עד 10, ותסביר מה היה יכול לגרום לך לטעות". זה לא הופך את המודל ל"יודע באמת" כמה הוא צודק, אבל מכריח אותו לשקול מפורשות איפה ההנחות שלו חלשות - ולפעמים זה חושף בעיה שהייתה נשארת סמויה בתשובה הראשונה הבטוחה-לכאורה.
פלט מובנה - structured output¶
כשהתשובה אמורה להיכנס לתהליך אוטומטי (קוד שמפרסר את הפלט, טבלה שמעתיקים לגיליון) - כדאי לבקש פורמט מדויק במקום טקסט חופשי. JSON, טבלת Markdown, או רשימת מפתח-ערך הם הפורמטים הנפוצים:
Analyze the following five electronics products and return a JSON
table with the fields:
name, category, estimated_price_usd.
Return only valid JSON, with no extra text before or after it.
Products: wireless noise-canceling headphones, 20000mAh portable
charger, mechanical keyboard with backlight, wireless gaming mouse,
portable Bluetooth speaker.
התוצאה - מבנה JSON נקי שאפשר להעתיק ישירות לקוד או לקובץ:
[
{"name": "Wireless noise-canceling headphones", "category": "Audio", "estimated_price_usd": 120},
{"name": "20000mAh portable charger", "category": "Accessories", "estimated_price_usd": 35},
{"name": "Mechanical keyboard with backlight", "category": "Peripherals", "estimated_price_usd": 80},
{"name": "Wireless gaming mouse", "category": "Peripherals", "estimated_price_usd": 55},
{"name": "Portable Bluetooth speaker", "category": "Audio", "estimated_price_usd": 45}
]
הדגשה חשובה: "רק JSON, בלי טקסט נוסף" מונעת מהמודל להוסיף משפט הקדמה ("הנה הטבלה המבוקשת:") שישבור פרסינג אוטומטי. כשהפלט מיועד לעיניים אנושיות, פחות קריטי - אבל כשהוא מיועד לקוד, זו דרישה חשובה.
שילוב טכניקות בפרומפט אחד¶
הטכניקות האלה לא מתחרות זו בזו - השילוב ביניהן הוא לרוב החזק ביותר. דוגמה שממחישה כמה טכניקות יחד:
I need a function that extracts Israeli phone numbers from a free
text string (like an email message or product description), in all
common formats.
Examples of input and desired output:
"Call me at 050-1234567 or the office at 03-6667788"
-> ["050-1234567", "03-6667788"]
"There's no phone number in this text"
-> []
Think step by step about the possible formats (with/without a
hyphen, with a country prefix, numbers with spaces) before you
write the regular expression.
Return only the Python code of the function, with no explanation
around it.
הפרומפט הזה משלב שלוש טכניקות בבת אחת: דוגמאות מכוונות (few-shot) שמראות בדיוק את הפורמט הרצוי, שרשור מחשבה (chain of thought) שמכריח את המודל לחשוב על כל הווריאציות האפשריות לפני כתיבת ה-regex, ופלט מובנה (רק קוד, בלי טקסט) לנוחות שימוש. זה לא מקרה חריג - ברוב הפרומפטים הטובים באמת, אתם תמצאו כמה טכניקות פועלות יחד בלי מאמץ מיוחד, פשוט כי הן פותרות בעיות משלימות.
סיכום¶
- דוגמאות מכוונות - few-shot: 2-3 דוגמאות מעבירות פורמט או סגנון מדויק, טוב יותר מתיאור מילולי ארוך.
- שרשור מחשבה - chain of thought: הבקשה "תחשוב שלב אחר שלב" משפרת דיוק במשימות שדורשות כמה שלבי היגיון, כי היא מונעת קפיצה למסקנה.
- מתן מפרט - spec first: במשימות מורכבות, כדאי לבנות קודם מפרט קצר ולבדוק אותו, ורק אחר כך לבקש מימוש - כך תופסים טעויות הבנה מוקדם וזול.
- שכלול איטרטיבי - iterative refinement: להתחיל מבקשה סבירה ולתקן בהודעות המשך ממוקדות, במקום לנסות לכתוב פרומפט "מושלם" שמנסה לצפות הכל מראש.
- ביקורת עצמית - self-critique: לבקש מהמודל לבחון את התשובה שלו כמו בסקירת קוד - מוצא בעיות שהוא עצמו לא הזכיר בתשובה הראשונה, אבל לא מחליף בדיקה אמיתית.
- פלט מובנה - structured output: לבקש JSON או טבלה מפורמטת כשהתשובה מיועדת לתהליך אוטומטי, ולציין במפורש "בלי טקסט נוסף" כדי לשמור על פלט נקי.
- פירוק משימה - task decomposition: לבקש מהמודל לפרק משימה גדולה למשימות משנה עצמאיות לפי סדר תלות, ואז לעבוד על כל אחת בנפרד במקום לבקש הכל בבת אחת.
- כל הטכניקות האלה משתלבות זו בזו - אפשר, למשל, לבקש מפרט (spec), לתת עליו דוגמה (few-shot) של סגנון רצוי, ואז לבקש ביקורת עצמית על התוצאה הסופית. פרומפטים חזקים בדרך כלל משלבים כמה טכניקות בבת אחת, לא בוחרים אחת בלבד.