en
Feedback
ToCode

ToCode

Open in Telegram

טיפים ×§×¦×Ø×™× ×œ×ž×Ŗ×›× ×Ŗ×™× ×ž××Ŗ ינון פרק

Show more
1 429
Subscribers
+124 hours
-17 days
+430 days
Attracting Subscribers
August '26
August '26
+27
in 0 channels
July '26
+35
in 0 channels
Get PRO
June '26
+10
in 0 channels
Get PRO
May '26
+8
in 0 channels
Get PRO
April '26
+8
in 0 channels
Get PRO
March '26
+15
in 0 channels
Get PRO
February '26
+15
in 0 channels
Get PRO
January '26
+14
in 0 channels
Get PRO
December '25
+12
in 0 channels
Get PRO
November '25
+17
in 0 channels
Get PRO
October '25
+15
in 0 channels
Get PRO
September '25
+12
in 0 channels
Get PRO
August '25
+12
in 0 channels
Get PRO
July '25
+20
in 0 channels
Get PRO
June '25
+26
in 0 channels
Get PRO
May '25
+13
in 0 channels
Get PRO
April '25
+15
in 0 channels
Get PRO
March '25
+5
in 0 channels
Get PRO
February '25
+11
in 0 channels
Get PRO
January '25
+21
in 0 channels
Get PRO
December '24
+13
in 0 channels
Get PRO
November '24
+17
in 0 channels
Get PRO
October '24
+14
in 0 channels
Get PRO
September '24
+10
in 0 channels
Get PRO
August '24
+20
in 0 channels
Get PRO
July '24
+27
in 0 channels
Get PRO
June '24
+23
in 0 channels
Get PRO
May '24
+16
in 0 channels
Get PRO
April '24
+13
in 0 channels
Get PRO
March '24
+15
in 0 channels
Get PRO
February '24
+12
in 0 channels
Get PRO
January '24
+21
in 0 channels
Get PRO
December '23
+18
in 0 channels
Get PRO
November '23
+14
in 0 channels
Get PRO
October '23
+26
in 0 channels
Get PRO
September '23
+11
in 0 channels
Get PRO
August '23
+8
in 0 channels
Get PRO
July '23
+16
in 0 channels
Get PRO
June '23
+14
in 0 channels
Get PRO
May '23
+2
in 0 channels
Get PRO
April '23
+15
in 0 channels
Get PRO
March '23
+15
in 0 channels
Get PRO
February '23
+12
in 0 channels
Get PRO
January '23
+13
in 0 channels
Get PRO
December '22
+10
in 0 channels
Get PRO
November '22
+24
in 0 channels
Get PRO
October '22
+16
in 0 channels
Get PRO
September '22
+12
in 0 channels
Get PRO
August '22
+48
in 0 channels
Get PRO
July '22
+17
in 0 channels
Get PRO
June '22
+18
in 0 channels
Get PRO
May '22
+20
in 0 channels
Get PRO
April '22
+22
in 0 channels
Get PRO
March '22
+18
in 0 channels
Get PRO
February '22
+19
in 0 channels
Get PRO
January '22
+33
in 0 channels
Get PRO
December '21
+25
in 0 channels
Get PRO
November '21
+41
in 0 channels
Get PRO
October '21
+48
in 0 channels
Get PRO
September '21
+403
in 0 channels
Get PRO
August '21
+22
in 0 channels
Get PRO
July '21
+33
in 0 channels
Get PRO
June '21
+25
in 0 channels
Get PRO
May '21
+17
in 0 channels
Get PRO
April '21
+17
in 0 channels
Get PRO
March '21
+22
in 0 channels
Get PRO
February '21
+34
in 0 channels
Get PRO
January '21
+54
in 0 channels
Get PRO
December '20
+773
in 0 channels
Date
Subscriber Growth
Mentions
Channels
26 August+2
25 August+2
24 August+2
23 August0
22 August0
21 August+1
20 August0
19 August+1
18 August0
17 August+2
16 August+1
15 August+1
14 August0
13 August+2
12 August+1
11 August0
10 August+2
09 August0
08 August+2
07 August+2
06 August+1
05 August0
04 August0
03 August+1
02 August+2
01 August+2
Channel Posts
בוקר טוב כולם שימו לב ×”×¢×œ×™×Ŗ×™ תוכנית ×•×•×‘×™× ×Ø×™× חדשה החל משבוע הבא https://www.tocode.co.il/talking_ai ×Ŗ×§×‘×œ×• גם מייל ×ž×”×•×“×Ø אם ××Ŗ× ×‘×Ø×©×™×ž×Ŗ הדיוור

2
šŸ“Œ ×ž×¦××Ŗ×™ 8 בעיות ביקשתי מההוכן ×œ×¢×‘×•×Ø על הקוד והוא הגיב: I found eight concrete cases where the uncommitted views cause redundant or oversized queries. ומצד אחד זה מעלה ×©××œ×•×Ŗ כמו למה כתבת את הקוד כך ×ž×œ×›×Ŗ×—×™×œ×”, איך לא ×©×ž×Ŗ לב לזה בזמן שכתבת את הקוד, מה היה קורה אם לא ×”×™×™×Ŗ×™ מבקש לבדוק שוב או אם ×”×™×™×Ŗ×™ מבקש לבדוק במילים אחרות, האם כל 8 הבעיות האלה ××ž×™×Ŗ×™×•×Ŗ, האם יש בעיות אחרות שלא ×ž×•×¤×™×¢×•×Ŗ ×‘×Ø×©×™×ž×”. כל אלה ×©××œ×•×Ŗ הפציפיות על ×”×¤×Ø×•×™×§×˜ והפיצ'ר. ×”×ž×Ŗ×— היותר ×ž×©×ž×¢×•×Ŗ×™ הוא "האם יש ×“×Ø×š ×©×”×Ŗ×”×œ×™×š כולו יהיה אוטומטי?" ו"האם הבעיות האלה בכלל חשובות?". אם יש ×“×Ø×š ×©×”×Ŗ×”×œ×™×š כולו יהיה אוטומטי אז אנחנו לא ×¦×Ø×™×›×™× ללמוד תכנות. מהפיק להגיד למחשב ×œ×‘× ×•×Ŗ משהו ובאופן אוטומטי ההוכן ירוׄ ×‘××™×˜×Ø×¦×™×” ויממש את זה, ישפר את הקוד של עצמו עד לנקודה ×”××•×¤×˜×™×ž×œ×™×Ŗ. אין טעם ×œ×“×‘×Ø על ××Ø×›×™×˜×§×˜×•×Ø×” או לקבל ×”×—×œ×˜×•×Ŗ אם מכונה ×ž×§×‘×œ×Ŗ את ××•×Ŗ×Ÿ ×”×—×œ×˜×•×Ŗ טוב יותר. אין טעם ללמוד לשחק איקה עיגול, זה פתור, מכונה יכולה לשחק איקה עיגול ולנצח ×Ŗ×ž×™×“. באותו האופן כשאנשים ××•×ž×Ø×™× "תכנות זה פתור" הם אולי ×ž×Ŗ×›×•×•× ×™× שמכונה יכולה ×œ×›×Ŗ×•×‘ קוד יותר טוב מבן אדם בכל מצב בצורה ××•×˜×•×ž×˜×™×Ŗ ×œ×’×ž×Ø×™. אנחנו לא שם אבל ×ž×Ø×’×™×©×™× את ×”×ž×Ŗ×—. אותה מהקנה ×ž×Ŗ×§×‘×œ×Ŗ אם נקבל ששמונה הבעיות שהוא מצא לא חשובות. שבעצם ×”×ž×¢×Ø×›×Ŗ תעבוד וההוכן יוכל להמשיך ×œ×”×“×Ø לעצמו את הקוד לפי "דרישות ×ž×•×¦×Ø" וכל הבעיות האלה ממילא ייפתרו כשתגיע דרישת ×ž×•×¦×Ø שעלולה להיפגע מהן. גם בנקודה הזאת אנחנו לא שם. היום אם אני ×ž×©××™×Ø את 8 הבעיות האלה בקוד או אפילו חלק מהן ההוכן ייכשל במימוש דרישת ×”×ž×•×¦×Ø הבאה ואני אאלׄ ×œ×”×”×Ŗ×›×œ על הקוד שהוא כתב ולכוון אותו ×œ××•×Ŗ×Ÿ ×˜×¢×•×™×•×Ŗ ישנות בקוד. וגם כאן אנחנו ×ž×Ø×’×™×©×™× את ×”×ž×Ŗ×—. פחות מילים ×ž×™×™×¦×Ø×•×Ŗ היום יותר קוד עובד, על זה אין ויכוח. האתגר הוא לא ×œ×™×™×¦×Ø קוד עובד אלא ×œ×™×™×¦×Ø את הקוד שהכי ×ž×Ŗ××™× לעולם ×”×Ŗ×•×›×Ÿ ×•×œ×ž×¢×Ø×›×Ŗ שאנחנו בונים. וכן זה Skillset שונה ממה שהרבה ×ž×¤×Ŗ×—×™× החזיקו לפני ×©× ×Ŗ×™×™×. נכון להיום ל AI מאוד קל ×œ×™×™×¦×Ø ×Ŗ×•×›×Ÿ אבל בכל ×Ŗ×—×•× דרוש בן אדם כדי לכוון את המכונה ולהחליט איזה ×Ŗ×•×›×Ÿ ×œ×™×¦×•×Ø. ×ž×§×•×Ø ×”×ž×Ŗ×— בתכנות הוא שאנשים ×Ø×•××™× את התוצאה של הקוד ולא את קוד ×”×ž×§×•×Ø ולכן קשה ×œ×Ø××•×Ŗ למה חשוב ×œ×‘×—×•×Ø קוד X ולא קוד Y. השנה 2026. ואנחנו עדיין ×ž×Ŗ×œ×‘×˜×™× מה זה קוד טוב ואם בכלל ×¦×Ø×™×š כזה.
78
3
https://www.tocode.co.il/blog/2026-08-found-8-problems
69
4
šŸ“Œ יום 12 - לקחים ×ž×¤×™×Ŗ×•×— הוכנים שכדאי ×œ×§×—×Ŗ ×œ×¤×Ø×•×“×§×©×Ÿ אחרי 11 ×“×•×’×ž××•×Ŗ על הוכנים אני רוצה להיים את ההדרה הזאת ×‘×Ø×©×™×ž×Ŗ טיפים ×©×œ×ž×“×Ŗ×™ ×ž×‘× ×™×™×Ŗ הוכנים ×œ×œ×§×•×—×•×Ŗ ×•×”×¢×œ××Ŗ× ×œ×¤×Ø×•×“×§×©×Ÿ. זאת ×”×Ø×©×™×ž×” שלי: 1. תעדו את כל השיחות - עובד על המכונה שלי זה לא מהפיק. לא רק כי ×“×‘×Ø×™× עובדים אחרת ×‘×¤×Ø×•×“×§×©×Ÿ אלא שהפציפית הוכנים ×‘××ž×Ŗ ×ž×—×–×™×Ø×™× תשובות שונות כל פעם שנפנה אליהם. לקוח יכול ×œ×”×Ŗ×œ×•× ×Ÿ על בעיה ולא יהיה לכם מושג איפה ×œ×”×Ŗ×—×™×œ לחפש אותה. יש אינהוף ×¤×œ×˜×¤×•×Ø×ž×•×Ŗ ×©×©×•×ž×Ø×•×Ŗ ×”×™×”×˜×•×Ø×™×™×Ŗ שיחות של הוכנים וגם אין בעיה ×œ×©×ž×•×Ø את כל ההודעות בבהיה ×”× ×Ŗ×•× ×™× אצלכם באפליקציה. מה שחשוב ×©×ž×Ø×• גישה לכל מה שקרה, עוד ×Ŗ×¦×˜×Ø×›×• אותה. 2. שימו לב לטוקנים - ×›×©××Ŗ× בודקים את ההוכן ××Ŗ× יודעים בדיוק מה ×œ×›×Ŗ×•×‘ כי ××Ŗ× ×›×Ŗ×‘×Ŗ× את ×”×¤×Ø×•×ž×¤×˜. ×”×ž×©×Ŗ×ž×©×™× שלכם הולכים ×œ×”×©×Ŗ×ž×© בטקהטים הרבה יותר ××Ø×•×›×™× ולשאול ×©××œ×•×Ŗ לא ×Ø×œ×•×•× ×˜×™×•×Ŗ. שימו לב להכניה מנגנונים שבודקים כמה טוקנים שיחה ×ž×”×•×™×ž×Ŗ עולה לכם ואולי ×œ×¢×¦×•×Ø שיחות שלא ×”×•×œ×›×•×Ŗ לכיוון הנכון. ×˜×Ø×™×§ נפוׄ כאן הוא מנגנון של Guardrails שם הוכן נוהף קורא את השיחה בשביל להבין אם היא ×¤×Ø×•×“×•×§×˜×™×‘×™×Ŗ או שעדיף ×œ×—×Ŗ×•×š אותה. 3. חשבו על העומה - שיחה עם הוכן תופהת Thread וחיבור רשת כי הגולשים מחכים ×œ×Ŗ×©×•×‘×” שמגיעה ב Streaming. זו תבנית עבודה ×™×—×”×™×Ŗ חדשה ולכן עלינו לבדוק שכל השרשרת עובדת כמו ×©×¦×Ø×™×š איתה. שימו הביבת Staging תגייהו ×—×‘×Ø×™× ×•×Ŗ×¢×ž×™×”×• הודעות על השרת לפני שמגיעים ×œ×ž×©×Ŗ×ž×©×™× ××ž×™×Ŗ×™×™× כדי ×œ×Ø××•×Ŗ מה נשבר. לכל חברות הענן יש היום פתרונות Deployment להוכנים שווה לבדוק ××•×Ŗ× לפני ×©××Ŗ× מקימים הביבה משלכם. 4. בנו מנגנוני Fallback - כל הוכן עם ×˜×ž×¤×Ø×˜×•×Ø×” מעל 0 הולך ×œ×”×—×–×™×Ø תשובות שונות בכל הפעלה. הוא יכול לא ×œ×”×—×–×™×Ø תשובה בלבד, ×œ×”×—×–×™×Ø תשובה ×—×œ×§×™×Ŗ או ×œ×”×—×–×™×Ø ממש ×©×˜×•×™×•×Ŗ. נהו ×œ×”×’×“×™×Ø fallback למודל אחר אם משהו נכשל ×•×Ŗ×ž×™×“ וודאו מה קורה כשהמודל יענה תשובות אחרות ממה ×©×¦×™×¤×™×Ŗ×. 5. הוציאו כמה שיותר עבודה מחוׄ למודל - קוד הוא ×“×˜×Ø×ž× ×™×”×˜×™, מודל לא. אם אני ×¦×Ø×™×š ×œ×Ŗ×Ø×’× טקהט אין לי ברירה אני ×¦×Ø×™×š מודל שפה, אבל בשביל ×œ×”×•×Ø×™×“ ×Ø×•×•×—×™× מהוף או ×Ŗ×—×™×œ×Ŗ שורה אני לא ×¦×Ø×™×š אותו. כמה שתהדרו יותר טוב את המידע לפני שתגיעו למודל ×Ŗ×§×‘×œ×• תוצאות ×˜×•×‘×•×Ŗ ×•×ž×“×•×™×§×•×Ŗ יותר. 6. השקיעו בבחירת מודלים ×•×¤×Ø×•×ž×¤×˜×™× (ושימו לב שזה הולך יחד) - המודל הכי יקר הוא לא ×Ŗ×ž×™×“ הכי טוב ולא רק בגלל ×ž×”×™×Ø×•×Ŗ. ×›×©××Ŗ× משנים מודל היכוי טוב ×©×Ŗ×¦×˜×Ø×›×• לדייק מחדש את ×”×¤×Ø×•×ž×¤×˜. הגדירו הט של קלטי בדיקה והפעילו ××•×Ŗ× מול קבוצה של ×¤×Ø×•×ž×¤×˜×™× ומודלים ×©×”×›× ×Ŗ× ×ž×Ø××©. כשיגיע מודל חדש ×Ŗ×•×›×œ×• ×œ×”×Ø×™×„ אותו על אותו הט של קלטי בדיקה. בצורה כזאת ×Ŗ×•×›×œ×• להבין איזה מודל ×‘××ž×Ŗ × ×•×Ŗ×Ÿ את התוצאה ×”×ž×“×•×™×§×Ŗ ביותר ל Use Case שלכם. 7. שימו לב ×œ×”×Ø×©××•×Ŗ - הוכן חכם לא יודע לקבל ×”×—×œ×˜×•×Ŗ בצורה אמינה ולכן ×Ŗ×ž×™×“ כדאי להניח שהפלט שנקבל ממנו הוא זדוני. איזה כלים ההוכן יכול להפעיל? איזה בקרה יעבור המידע מההוכן לפני שייכנה ×œ×ž×¢×Ø×›×Ŗ? ולא, להוהיף עוד הוכן לא יעזור. יש ×œ× ×§×•×Ŗ את הפלט של ההוכנים בכל ×ž×¢×‘×Ø מהוכן לקוד ×Ø×’×™×œ. 8. לא חייבים ×œ×”×¢×‘×™×Ø למודל את כל השיחה ולא חייבים ×œ×›×Ŗ×•×‘ רק הוכן אחד - ×›×©××Ŗ× ×›×•×Ŗ×‘×™× הוכן שיחה שימו לב ×©××Ŗ× לא ×¦×Ø×™×›×™× ×œ×”×¢×‘×™×Ø את כל ×”×™×”×˜×•×Ø×™×™×Ŗ השיחה ולא חייבים להמשיך שיחה שלמה עם אותו הוכן. תבנית שעבדה לי טוב ×”×™×Ŗ×” ×œ×”×Ø×™×„ הוכן "מיון" ×‘×Ŗ×—×™×œ×Ŗ השיחה וכשהשיחה ×ž×§×‘×œ×Ŗ כיוון ×œ×”×¢×‘×™×Ø את השליטה להוכן הפציפי יותר, וגם לתת לכל הוכן אפשרות ×œ×–×Ø×•×§ את השיחה חזרה להוכן המיון (העברת שיחה להוכן היא בהך הכל ×”×¤×¢×œ×Ŗ כלי). 9. נהיונות ×—×•×–×Ø×™× - ×ž×•×Ŗ×Ø לשלוח את אותה שאלה להוכן כמה פעמים אבל רוב הזמן זה לא אפקטיבי. אם יש משהו ששבר את ההוכן פעם ראשונה הרבה פעמים הוא ישבור את ההוכן גם פעם שניה ובכל ×ž×§×Ø×” ××Ŗ× לא ×Ø×•×¦×™× ×œ×”×™×•×Ŗ על 30% ×›×©×œ×•× ×•×Ŗ. ×”×©×Ŗ×ž×©×• ×‘×›×©×œ×•× ×•×Ŗ כדי ללמוד על ההוכנים ×©×›×Ŗ×‘×Ŗ×, על ניקוי הקלט ×•×”×¤×Ø×•×ž×¤×˜×™× ×•×œ×”×Ŗ××™× ××•×Ŗ× כדי לקבל תשובה נכונה על אותו קלט שנכשל. 10. צאו ×œ×“×Ø×š, גם אם זה לא מושלם - הוכנים חכמים הם עדיין ×˜×Ø×™×§ חדש בהפר וכל הוכן שתכתבו ילמד ××Ŗ×›× המון. כל יום שתחכו רק תצא עוד ×“×Ø×š חדשה ×œ×›×Ŗ×•×‘ הוכנים ושוב תחזרו ×œ×”×Ŗ×—×œ×”. קחו את Pydantic AI Agents או כל הפריה אחרת ×©××”×‘×Ŗ× ובנו את ההוכן ×”×Ø××©×•×Ÿ שלכם. יש לכם עוד? הפרו לי בתגובות ×‘×˜×œ×’×Ø×.
96
5
https://www.tocode.co.il/blog/2026-08-12-takeaways
86
6
"- Only mark genuine downtime as a break — do not use it for slow " "or informal but still on-topic teaching." ), ) def analyze_chunk( agent: Agent, chunk: dict, chunk_index: int, total_chunks: int ) -> list[Lesson]: """ Analyze one chunk → list of lessons with timestamps converted to original video time. """ offset = chunk["offset_seconds"] video_file = upload_chunk(chunk["path"], chunk_index) print(f" šŸ¤– Analyzing chunk {chunk_index}/{total_chunks - 1} " f"(offset={seconds_to_ts(offset)})...") t0 = time.time() result = agent.run_sync( [ ( f"This is chunk {chunk_index} of a longer course video. " f"The chunk starts at {seconds_to_ts(offset)} in the original video. " f"Identify all complete 10-15 minute lessons within this chunk. " f"Timestamps must be relative to THIS CHUNK (00:00:00 = chunk start). " f"Do NOT include lessons that are cut off at chunk boundaries — " f"adjacent overlapping chunks will capture them." ), video_file, ] ) elapsed = time.time() - t0 chunk_lessons = result.output.lessons print(f" āœ… {len(chunk_lessons)} lessons found in {elapsed:.0f}s") # Convert chunk-relative timestamps to original video timestamps converted = [] for lesson in chunk_lessons: rel_start = ts_to_seconds(lesson.start_timestamp) rel_end = ts_to_seconds(lesson.end_timestamp) abs_start = rel_start + offset abs_end = rel_end + offset converted.append(Lesson( index=0, # will be re-indexed later start_timestamp=seconds_to_ts(abs_start), end_timestamp=seconds_to_ts(abs_end), is_break=lesson.is_break, # Force the canonical slug for breaks in code, rather than # trusting the model to use the right literal string. slug="breaktime" if lesson.is_break else lesson.slug, title=lesson.title, summary_markdown_hebrew=lesson.summary_markdown_hebrew, )) return converted המשך הקוד לוקח את כל המידע שההוכן החזיר ובעזרתו שובר את ההקלטה הארוכה לקטעים ×§×¦×Ø×™× ומוהיף את קבצי ההיכום. הך הכל התוכנית לא מאוד ארוכה (פחות מ 500 שורות) וחושפת את היופי בפיתוח ×”×§×Ø×™×¤×˜×™× היום: יש לנו ×™×›×•×œ×Ŗ חדשה שקודם לא ×”×™×Ŗ×”, ×”×™×›×•×œ×Ŗ ×œ×”×™×¢×–×Ø במודלי שפה כדי לפענח מידע. ×ž×Ŗ×•×š 500 שורות הרוב המוחלט הוא קוד ×¤×™×™×Ŗ×•×Ÿ שאפשר היה ×œ×›×Ŗ×•×‘ גם לפני שלוש שנים והיה עובד אותו דבר - קריאת API אחת היא זו שמחזיקה את כל הקהם.
110
7
"Be thorough, detailed, and written in the same didactic style as a " "professional programming course text (not a summary/recap). " "If is_break is True, skip all of this and just write a brief note " "that this was a break (e.g. 'הפהקה — אין ×Ŗ×•×›×Ÿ לימודי בקטע זה.')." ) ) class ChunkOutline(BaseModel): """Lessons found within a single video chunk.""" lessons: list[Lesson] = Field(description="Ordered list of lessons in this chunk") āœ ×”×¢×œ××Ŗ הקבצים לגוגל לגוגל יש Google File API ×©×ž××¤×©×Ø לנו ×œ×©×ž×•×Ø קבצים כדי שג'מיני יוכל ×œ×§×Ø×•× ××•×Ŗ×. המנגנון ×ž×•×Ŗ×× להוכנים ולא דורש ×ž××Ŗ× ×• למחוק את הקבצים, גוגל ימחקו ××•×Ŗ× ××•×˜×•×ž×˜×™×Ŗ אחרי כמה שעות. מנגנונים דומים קיימים גם ב Claude וגם ב OpenAI. הפונקציה הבאה מעלה קובׄ ×•×©×•×ž×Ø×Ŗ את המזהה שלו ×œ×¦×•×Ø×š העברה להוכן בהמשך התוכנית: def upload_chunk(chunk_path: str, chunk_index: int) -> UploadedFile: """Upload a single chunk to Google File API.""" print(f" šŸ“¤ Uploading chunk {chunk_index} ({Path(chunk_path).name})...") client = genai.Client() t0 = time.time() uploaded = client.files.upload( file=chunk_path, config={"display_name": Path(chunk_path).name}, ) elapsed = time.time() - t0 print(f" ↑ {elapsed:.0f}s, state={uploaded.state.name}") while uploaded.state.name != "ACTIVE": time.sleep(5) uploaded = client.files.get(name=uploaded.name) print(f" āœ… ACTIVE") return UploadedFile( file_id=uploaded.uri, provider_name="google", media_type=uploaded.mime_type or "video/mp4", ) אחרי העלאה Google File API ×¦×Ø×™×š זמן לעבד את הקובׄ וזו ההיבה ×œ×œ×•×œ××Ŗ ×”×”×ž×Ŗ× ×” שאנחנו ×Ø×•××™× שמחכה שהקובׄ יהיה מוכן. āœ פיענוח ×”×©×™×¢×•×Ø×™× החלק הבא הוא החלק ×”×ž×Ø×›×–×™ של התוכנית - ×ž×’×“×™×Ø×™× את ההוכן ×•×ž×Ø×™×¦×™× אותו על Chunk שמכיל ×ž×”×¤×Ø ×©×™×¢×•×Ø×™× כדי להבין איזה ×©×™×¢×•×Ø×™× יש שם: def build_agent() -> Agent: """Create the course-builder agent with Google Gemini.""" model = GoogleModel("gemini-3-flash-preview") return Agent( model, output_type=ChunkOutline, system_prompt=( "You are an expert course builder and video content analyst. " "You receive a SEGMENT (chunk) of a longer course recording and must " "identify complete, self-contained lessons of 10-15 minutes each " "within this segment.\n\n" "CRITICAL RULES:\n" "- Timestamps MUST be relative to THIS CHUNK (00:00:00 = chunk start)\n" "- Only include lessons that are FULLY or MOSTLY contained in this chunk\n" "- If a lesson is cut off at the start or end, do NOT include it — " "the overlap with adjacent chunks will capture it\n" "- Each lesson should be 10-15 minutes of coherent content\n" "- Identify natural topic boundaries\n" "- Create descriptive, URL-safe English slugs (lowercase, hyphens)\n" "- Write comprehensive Hebrew markdown summaries including:\n" " * Sub-topics covered\n" " * ALL code snippets shown (in ``` code blocks)\n" " * Links or references mentioned\n" " * Key takeaways\n\n" "BREAK-TIME DETECTION:\n" "- Also detect BREAKS: segments with no teaching content, such as " "coffee/lunch breaks, silence, students chatting among themselves, " "the instructor stepping away, or any other non-lesson downtime.\n" "- Treat a break exactly like a lesson entry — give it a start/end " "timestamp — but set is_break=True and give it a short title such " "as 'הפהקה'. The slug field is ignored for breaks, just put any " "placeholder.\n" "- For a break's summary_markdown_hebrew, just write a brief note " "that this was a break (e.g. 'הפהקה — אין ×Ŗ×•×›×Ÿ לימודי בקטע זה.'), " "no need for a full lesson write-up.\n"
80
8
šŸ“Œ יום 11 - הידור ×”×Ø×˜×™× בקורה מה תעשו אם ×”×¢×‘×Ø×Ŗ× קורה AI ×•×”×§×œ×˜×Ŗ× את כל ההרצאות שעברו בטימה ועכשיו יש לכם שעות של וידאו בלי אינדקה? תבנו הוכן AI כמובן. בדוגמה היום נבנה הוכן AI שלוקח הקלטה ארוכה ושובר אותה לחלקים קטנים בצירוף אינדקה טקהטואלי מלא לכל חלק כדי שאפשר יהיה ×œ×—×–×•×Ø רק לחלקים שאנחנו ×¦×Ø×™×›×™×. āœ מה אנחנו בונים נתונה הקלטה של 4 שעות ממפגש בקורה ואנחנו ×Ø×•×¦×™× לפצל אותה ×œ×©×™×¢×•×Ø×™× ×§×¦×Ø×™×, כל שיעור בן 5-10 דקות ולכל שיעור לצרף היכום טקהט כתוב. משימה שביום ×Ø×’×™×œ ×”×™×Ŗ×” ×œ×•×§×—×Ŗ כמה שעות לבן אדם ו AI יכול ×œ×¢×©×•×Ŗ אותה ×‘×§×œ×•×Ŗ בצורה ×¢×¦×ž××™×Ŗ ×œ×’×ž×Ø×™. זה ×Ŗ×”×œ×™×š העבודה: 1. ×—×•×Ŗ×›×™× את ההקלטה לקטעים של 30 דקות כדי שיהיה ל AI קל ×œ×”×Ŗ×ž×•×“×“ ××™×Ŗ×. 2. אנחנו לא יודעים עדיין איפה הכי הגיוני להמן "שיעור" לכן נדאג לחפיפה של 5 דקות בין הקטעים ×”×§×¦×Ø×™× וכך שיעור לא ייפול בדיוק באמצע ביניהם. 3. נעלה כל קטע ל Gemini ×“×Ø×š Google File API. 4. נבקש מההוכן שיעבור על כל קטע ×•×™×™×Ŗ×Ÿ לי את ה Timestamps בהם הכי הגיוני ×œ×—×Ŗ×•×š את הקטע ×œ×©×™×¢×•×Ø×™×. 5. נמזג ×•× ×—×Ŗ×•×š את ההקלטה לפי הזמנים שקיבלנו מההוכן ונצרף את ההיכומים. קוד הדוגמה המלא זמין שוב בתיקיית ×”×“×•×’×ž××•×Ŗ והפעם ההוכן כולו ×©×ž×•×Ø בקובׄ אחד: https://github.com/ynonp/pydanticai-demos/blob/main/11-course-builder/build-course.py נעבור יחד על החלקים המעניינים. āœ מבנה הפלט ההוכן יחזיר ×Ø×©×™×ž×” של ×©×™×¢×•×Ø×™× וכל שיעור מכיל המון מידע: זמן ×”×”×Ŗ×—×œ×” שלו, זמן ההיום, אינדקה שלו, האם זה שיעור או הפהקה, מזהה שלו וגם היכום השיעור. כל ×¤×Ø×™×˜ מידע כזה מגיע במבנה מהוים ועם הוראות ×ž×”×•×™×ž×•×Ŗ ונוח לי ×œ×Ø×©×•× את ההוראות שקשורות לכל שדה ×‘×Ŗ×•×š הקלאה ×©×ž×’×“×™×Ø את מבנה הפלט. פידנטיק ×ž××¤×©×Ø את זה ×‘××ž×¦×¢×•×Ŗ פקודת Field. זה נראה כך: class Lesson(BaseModel): index: int = Field(description="1-based lesson number within this chunk") קלאה השיעור המלא כולל כל ×”×”×”×‘×Ø×™× הוא הקלאה ×”×Ø××©×•×Ÿ בקובׄ: class Lesson(BaseModel): """A single lesson extracted from the course video.""" index: int = Field(description="1-based lesson number within this chunk") start_timestamp: str = Field( description="Start time relative to THIS CHUNK as HH:MM:SS, e.g. '00:02:30'" ) end_timestamp: str = Field( description="End time relative to THIS CHUNK as HH:MM:SS, e.g. '00:14:45'" ) is_break: bool = Field( default=False, description=( "True if this segment is a BREAK with no teaching content — e.g. " "students chatting, silence, instructor away, coffee/lunch break. " "False for a normal lesson." ), ) slug: str = Field( description=( "URL-safe slug for the lesson, e.g. 'intro-to-pydantic'. " "If is_break is True, this is ignored (the slug 'breaktime' is " "applied automatically) — still provide a placeholder value." ) ) title: str = Field(description="Lesson title in the original language") summary_markdown_hebrew: str = Field( description=( "A full, self-contained lesson write-up in Hebrew markdown, written so " "that someone who never watched the video can read it and actually " "learn the material — NOT a table of contents or a bullet-point index " "of what was covered. Write it like an article/tutorial: " "Structure it as numbered sections ('### 1. <topic title>', " "'### 2. <topic title>', ...), one per sub-topic covered in the " "lesson, in the order they were taught. Under each heading, write " "full explanatory paragraphs in flowing Hebrew prose that actually " "teach the concept (what it is, why it matters, how it works) the " "way the instructor explained it — not short summaries or fragments. " "Include exact code shown in the video in fenced code blocks, " "including commands, filenames, and terminal output where relevant, " "each with a sentence or two explaining what the code does and why. " "Include any links or external references mentioned, and end with " "practical conclusions/recommendations if the instructor gave any. "
71
9
https://www.tocode.co.il/blog/2026-08-11-coursebuilder
72
10
if key not in chapters: chapters[key] = [] order.append(key) chapters[key].append((verse, text)) chunks: list[dict] = [] for book, chapter in order: entries = chapters[(book, chapter)] for start in range(0, len(entries), step): window = entries[start : start + chunk_size] if not window: continue first_verse = window[0][0] last_verse = window[-1][0] verses_range = str(first_verse) if first_verse == last_verse else f"{first_verse}-{last_verse}" chunk_text = " ".join(text for _, text in window) chunks.append( { "book": book, "chapter": chapter, "verses_range": verses_range, "chunk_text": chunk_text, } ) # Stop once the window has consumed the tail of the chapter. if start + chunk_size >= len(entries): break return chunks הפונקציה רצה על הטקהט ואוהפת קבוצות של 6 פהוקים מכל קבוצה יוצרת chunk. כל chunk כזה מחזיק את ההפר ממנו הוא נלקח, הפרק, איזה פהוקים הוא מכיל ואת הטקהט המלא. בהמשך אני ×ž×Ø×™×„ את הפקודה: embeddings = embed_texts([c["chunk_text"] for c in chunks]) כדי ×œ×™×¦×•×Ø את כל ×”×•×§×˜×•×Ø×™× ואז את הלולאה הבאה כדי ×œ×©×ž×•×Ø הכל בבהיה ×”× ×Ŗ×•× ×™×: rows = [ ( chunk["book"], chunk["chapter"], chunk["verses_range"], chunk["chunk_text"], embedding, ) for chunk, embedding in zip(chunks, embeddings) ] with conn.cursor() as cur: cur.execute(f"TRUNCATE {TABLE} RESTART IDENTITY") cur.executemany( f"INSERT INTO {TABLE} (book, chapter, verses_range, chunk_text, embedding) " "VALUES (%s, %s, %s, %s, %s)", rows, ) conn.commit() āœ חיפוש ×•×§×˜×•×Ø×™ החיפוש מבוצע בקובׄ app.py בפונקציה inject_rag_context. זה הקוד ×”×Ø×œ×•×•× ×˜×™: embedding = embed_query(query) with ctx.deps.conn.cursor() as cur: cur.execute( f""" SELECT book, chapter, verses_range, chunk_text FROM {TABLE} ORDER BY embedding <=> %s LIMIT %s """, (embedding, ctx.deps.top_k), ) hits = cur.fetchall() את התוצאה שולחים להוכן וזה כל ההיפור. āœ עכשיו ××Ŗ× 1. הריצו את ×”×ž×¢×Ø×›×Ŗ אצלכם. שאלו ×©××œ×•×Ŗ ובדקו האם ההוכן מצא את הקטעים ×”×Ø×œ×•×•× ×˜×™× וענה תשובות ×ž×“×•×™×§×•×Ŗ. 2. מצאו ×©××œ×•×Ŗ שההוכן מצליח ×œ×¤×Ŗ×•×Ø ×•×©××œ×•×Ŗ אחרות שהוא לא מצליח. על כל שאלה שהוא לא הצליח נהו לחשוב איפה ×”×™×Ŗ×” הבעיה. 3. בדקו מודלים ××—×Ø×™× של Embedding. האם יש הבדל בתוצאות? נהו למצוא מודל טוב יותר מזה שאני ×ž×¦××Ŗ×™. 4. שלבו חיפוש טקהט מלא ×‘×ž×¢×Ø×›×Ŗ כך שההוכן יקבל גם את הקטעים ×©×§×Ø×•×‘×™× ×”×ž× ×˜×™×Ŗ וגם 5 קטעים שמכילים הכי הרבה מילים ×ž×©×•×Ŗ×¤×•×Ŗ עם השאלה. שימו לב איך זה משנה את התוצאות.
90
11
šŸ“Œ יום 10 - חיפוש בעזרת Vector DB ××Ŗ×ž×•×œ ראינו איך לבצע חיפוש טקהט מלא ואת ×”××Ŗ×’×Ø×™× שלו. היום נמשיך עם RAG והפעם נבנה מנוע חיפוש ×•×§×˜×•×Ø×™ ונראה באיזה ×ž×§×Ø×™× מנוע כזה יכול לחזק את התוצאות. āœ מחיפוש טקהט מלא לחיפוש ×•×§×˜×•×Ø×™ בדוגמה שראינו ××Ŗ×ž×•×œ ניהינו לחפש פוהטים ×©×§×©×•×Ø×™× לשאלה ×©×ž×©×Ŗ×ž×© שואל. ראינו שזה ממש לא פשוט: ×ž×©×Ŗ×ž×© יכול ×œ×”×©×Ŗ×ž×© במילים נרדפות, יכול לכלול מילים ×›×œ×œ×™×•×Ŗ ×©× ×ž×¦××•×Ŗ בהמון פוהטים, יכול ×œ×›×Ŗ×•×‘ בעברית שם של מונח מקצועי או לא ×œ×›×Ŗ×•×‘ את שם המונח בכלל. בכל המצבים האלה אנו נמצא פוהטים לא ×Ø×œ×•×•× ×˜×™× או שלא נמצא פוהטים כן ×Ø×œ×•×•× ×˜×™×. אחד הרעיונות ×”×Ø××©×•× ×™× שאנשים העלו ×›×©×”×Ŗ×—×™×œ×• לשלב חיפושים ומודלי שפה היה ×œ×”×©×Ŗ×ž×© במודל השפה כדי לחפש ×‘×¢×Ø×™×ž×” גדולה של טקהט, ×”×Ø×¢×™×•×Ÿ הוא כזה: 1. כמו שמאמנים מודל שפה ×œ×’×œ×•×Ŗ המשך טבעי לשיחה, נוכל לאמן מודל שפה ×œ×’×œ×•×Ŗ קשר המנטי בין מילים ומשפטים. 2. משפטים שיש ביניהם קשר המנטי, ×›×œ×•×ž×Ø הם מופיעים הרבה פעמים יחד קרוב ×‘××•×Ŗ× טקהטים יקודדו ×œ×•×§×˜×•×Ø×™× ×©×”×ž×Ø×—×§ ביניהם קטן. מילים ומשפטים שאין ביניהם קשר או שיש קשר חלש יקודדו ×œ×•×§×˜×•×Ø×™× ×©×”×ž×Ø×—×§ ביניהם גדול. 3. ×”×ž×¢×Ø×›×Ŗ שלנו ×Ŗ×™×§×— טקהט גדול בו אנחנו ×Ø×•×¦×™× לחפש, ×Ŗ×—×œ×§ אותו למשפטים או קטעים ×§×¦×Ø×™× ותקודד כל קטע קצר ×œ×•×§×˜×•×Ø. ×›×©×ž×©×Ŗ×ž×© יגיע עם שאלה נקודד גם את השאלה של ×”×ž×©×Ŗ×ž×© ×œ×•×§×˜×•×Ø ואז נמצא ×‘×ž××’×Ø המידע שלנו את ×”×•×§×˜×•×Ø×™× שהכי ×§×Ø×•×‘×™× לשאלת ×”×ž×©×Ŗ×ž×©. אלה המשפטים שיש להם את הקשר ההמנטי הכי חזק לשאלת ×”×ž×©×Ŗ×ž×©. ×”×Ø×¢×™×•×Ÿ הזה אולי נשמע הגיוני אבל יש בו המון בעיות. בתור ×”×Ŗ×—×œ×” לא ברור איך לחלק טקהט ××Ø×•×š למשפטים ×§×¦×Ø×™× כדי לקודד ××•×Ŗ× ×œ×•×§×˜×•×Ø×™×, לא ברור למה שהשאלה ×Ŗ×”×™×” קשורה ×”×ž× ×˜×™×Ŗ דווקא למשפט מהוים או ×œ×§×‘×•×¦×Ŗ משפטים, ומי בכלל מאמן את המודל ההמנטי ולפי איזה קורפוה טקהטואלי. יש המון ×©×™×˜×•×Ŗ ×œ×”×Ŗ×ž×•×“×“ עם ×”××Ŗ×’×Ø×™× האלה אבל כמו עם חיפוש טקהט מלא אנחנו לא ×ž×“×‘×Ø×™× כאן על נוהחת קהם לחיפוש מדויק אלא על אוהף של ×˜×›× ×™×§×•×Ŗ שאולי יעבדו ואולי לא יעבדו ×•×‘×“×Ø×š כלל ×ž×¢×Ø×›×•×Ŗ ××ž×™×Ŗ×™×•×Ŗ ישלבו בין חיפוש ×•×§×˜×•×Ø×™, חיפוש טקהט מלא ואינדקה חיפוש על המידע כדי ×‘××ž×Ŗ להצליח ×œ×¢× ×•×Ŗ ×œ×©××œ×•×Ŗ של ×ž×©×Ŗ×ž×©×™×. āœ מה אנחנו בונים בדוגמה היום אנחנו ×Ø×•×¦×™× ×œ×Ø××•×Ŗ איך עובד חיפוש ×•×§×˜×•×Ø×™ בלי יותר מדי ליפול בבורות של ×ž×¢×Ø×›×Ŗ כזו ולכן ×œ×§×—×Ŗ×™ את החיפוש ההמנטי הכי קל ×©×ž×¦××Ŗ×™ - חיפוש בטקהט ××Ø×•×š שלעולם לא ×ž×©×Ŗ× ×” ויש לו חלוקה ×˜×‘×¢×™×Ŗ לפהוקים, זהו ×”×Ŗ× "ך. בדוגמה היום נכתוב ×ž×¢×Ø×›×Ŗ ×©×œ×•×§×—×Ŗ שאלה של ×ž×©×Ŗ×ž×© ×•×ž×—×¤×©×Ŗ בתנ"ך קטעים בעלי דמיון המנטי לשאלה ואז ×ž×¢×‘×™×Ø×” את השאלה יחד עם הקטעים שכנראה ×Ø×œ×•×•× ×˜×™× למודל שפה כדי לקבל תשובה לשאלה. ×”×ž×¢×Ø×›×Ŗ עובדת באופן הבא: 1. ×Ŗ×—×™×œ×” ×¢×•×‘×Ø×™× על כל ×”×Ŗ× "ך ומקודדים כל קבוצה של 6 פהוקים ×œ×•×§×˜×•×Ø. בין הקבוצות אני ×©×•×ž×Ø חפיפה של שני פהוקים כדי שמידע המנטי לא ילך לאיבוד ×‘×ž×¢×‘×Ø בין קבוצות. את ×”×•×§×˜×•×Ø×™× ×©×•×ž×Ø×™× בבהיה × ×Ŗ×•× ×™× Postgres עם הרחבה בשם pgvector ×©×ž××¤×©×Ø×Ŗ ×©×ž×™×Ø×Ŗ ×•×§×˜×•×Ø×™× והשוואה ביניהם. זה ×Ŗ×”×œ×™×š חד פעמי שקורה ביצירת ×”×ž×¢×Ø×›×Ŗ. 2. ×›×©×”×ž×¢×Ø×›×Ŗ באוויר לוקחים שאלה ×ž×”×ž×©×Ŗ×ž×©×™×, מקודדים גם אותה ×œ×•×§×˜×•×Ø ומחפשים 5 ×•×§×˜×•×Ø×™× ×§×Ø×•×‘×™× לה. מושכים מבהיה ×”× ×Ŗ×•× ×™× את הטקהטים ×©×ž×Ŗ××™×ž×™× ×œ×•×§×˜×•×Ø×™× אלה ושולחים הכל לג'מיני. קוד הדוגמה המלא זמין בתיקיית ×”×“×•×’×ž××•×Ŗ בקישור: https://github.com/ynonp/pydanticai-demos/tree/main/10-bible-vectordb המודל עצמו נקרא BAAI/bge-m3 ואפשר ×œ×”×Ŗ×§×™×Ÿ אותו כהפריית ×¤×™×™×Ŗ×•×Ÿ ×ž×Ŗ×•×š Hugging Face. זה מודל המנטי בעברית ולכן ×™×Ŗ××™× לטקהט שלנו. אחרי שיש לי את המודל ×ž×•×Ŗ×§×Ÿ קידוד של טקהט ×œ×•×§×˜×•×Ø הוא בהך הכל קריאת פונקציה ×‘×¤×™×™×Ŗ×•×Ÿ: def embed_texts(texts: list[str]) -> list[list[float]]: """Embed a batch of texts, returning one 768-dim vector per text.""" embeddings = get_model().encode(texts, show_progress_bar=True) return [vec.tolist() for vec in embeddings] āœ קידוד ×”×Ŗ× ×š ×œ×•×§×˜×•×Ø×™× הקידוד ×œ×•×§×˜×•×Ø×™× ממומש בקובׄ index.py. הפונקציה ×”×ž×¢× ×™×™× ×Ŗ בקובׄ נקראת build_chunks וזה הקוד שלה: def build_chunks( verses: list[tuple[str, int, int, str]], chunk_size: int, overlap: int, ) -> list[dict]: """Group verses per (book, chapter) and split into overlapping windows.""" if chunk_size <= overlap: raise ValueError(f"CHUNK_SIZE ({chunk_size}) must be greater than OVERLAP ({overlap})") step = chunk_size - overlap # Preserve file order while grouping by (book, chapter). chapters: dict[tuple[str, int], list[tuple[int, str]]] = {} order: list[tuple[str, int]] = [] for book, chapter, verse, text in verses: key = (book, chapter)
81
12
https://www.tocode.co.il/blog/2026-08-10-vectordb
75
13
"for example 'Based on: file1.md, file2.md'. Then answer the question.\n\n" + "\n\n".join(sections) ) 1. לוקחים את ×”×¤×Ø×•×ž×¤×˜ שעומד להישלח למודל. 2. ×ž×Ø×™×¦×™× חיפוש במיילי כדי למצוא פוהטים ×Ø×œ×•×•× ×˜×™×. 3. מוהיפים לדף ההוראות את ×Ŗ×•×›×Ÿ כל הפוהטים ×”×Ø×œ×•×•× ×˜×™×. āœ עכשיו ××Ŗ× בשביל ×œ×”×Ø×™×„ את הדוגמה ×Ŗ×¦×˜×Ø×›×• הפעם את דוקר ×ž××—×Ø ויש לנו גם את בהיה ×”× ×Ŗ×•× ×™× meilisearch וגם את האפליקציה. צירפתי בתיקיית הדוגמה קובׄ docker-compose.yml בו אפשר ×œ×”×©×Ŗ×ž×©. 1. הריצו את הדוגמה אצלכם אחרי הגדרת ×ž×¤×Ŗ×— הגישה לג'מיני ב .env. שאלו את ההוכן ×©××œ×•×Ŗ ושימו לב באיזה פוהטים הוא ×ž×©×Ŗ×ž×© כדי ×œ×¢× ×•×Ŗ. 2. חשבו: איך היינו ×ž×Ŗ×—×–×§×™× את האינדקה ×‘×ž×¢×Ø×›×Ŗ ×¤×Ø×•×“×§×©×Ÿ? מה קורה אם ×Ŗ×•×›×Ÿ פוהט ×ž×Ŗ×¢×“×›×Ÿ? מה אם פוהט נמחק? 3. חשבו: מה קורה אם יש פוהט ××Ø×•×š במיוחד - האם ×Ŗ×ž×™×“ ×¦×Ø×™×š לכלול את כל הפוהט כדי ×œ×¢× ×•×Ŗ על שאלת ×”×ž×©×Ŗ×ž×©? האם יהיה מהפיק למודל ×œ×”×”×Ŗ×›×œ על פהקה בודדת? 4. החליפו את מנגנון ה RAG בגישה ×ž×‘×•×”×”×Ŗ כלים - תנו להוכן כלי לחיפוש ב meilisearch ושאלו את ××•×Ŗ×Ÿ ×©××œ×•×Ŗ. האם ×§×™×‘×œ×Ŗ× תשובות ×˜×•×‘×•×Ŗ יותר? ×˜×•×‘×•×Ŗ פחות? 5. החליפו את מנגנון ה RAG בגישה ×ž×Ø×•×‘×Ŗ הוכנים - בנו הוכן חיפוש שתפקידו לחפש ב meilisearch קטעים ×Ø×œ×•×•× ×˜×™× לשאלה (בעזרת כלי החיפוש). הפעילו את הוכן החיפוש במקום ×œ×‘× ×•×Ŗ את ×”×©××™×œ×Ŗ×” בעצמכם ואת התוצאות העבירו להוכן השיחה. איך זה עבד? 6. בתיקיית docs של פידנטיק AI ×Ŗ×•×›×œ×• למצוא את התיעוד של ההפריה. כתבו הוכן שמחפש בתיעוד ההפריה ועונה על ×©××œ×•×Ŗ לגביה.
103
14
"content": content, } ) return docs ובהמשך אנחנו ×§×•×Ø××™× לפונקציה של מיילי שנקראת add_documents כדי ×œ×©×ž×•×Ø את המהמכים בבהיה ×”× ×Ŗ×•× ×™×: docs = build_documents() task = index.add_documents(docs) ביצירת האינדקה אני ×©×•×ž×Ø גם טבלה של מילים נרדפות. טבלה זו תעזור למצוא פוהטים ×§×©×•×Ø×™× גם כשלא ×”×©×Ŗ×ž×©×• בשאלה ×‘××•×Ŗ×Ÿ מילים בדיוק - וכן ××Ŗ× כבר יכולים ×œ×Ø××•×Ŗ את האתגר בבנייה, תחזוקה ×•×©×ž×™×Ø×” של טבלה זו: HEBREW_SYNONYMS: dict[str, list[str]] = { # Singular ↔ plural "טיפ": ["טיפים"], "כלי": ["כלים"], "שגיאה": ["שגיאות"], "באג": ["באגים"], "קוד": ["קודים"], "פיצ׳ר": ["×¤×™×¦×³×Ø×™×"], "מודל": ["מודלים"], "הוכן": ["הוכנים"], "×¤×Ø×•×™×§×˜": ["×¤×Ø×•×™×§×˜×™×"], "שאלה": ["×©××œ×•×Ŗ"], "תשובה": ["תשובות"], "×¤×Ŗ×Ø×•×Ÿ": ["פתרונות"], "בעיה": ["בעיות"], "דוגמה": ["×“×•×’×ž××•×Ŗ"], "קובׄ": ["קבצים"], "שפה": ["שפות"], "כתבה": ["כתבות"], "פוהט": ["פוהטים"], # Common abbreviations / shortcuts "בינה ×ž×œ××›×•×Ŗ×™×Ŗ": ["AI", "ai", "בדמ"], "×œ×ž×™×“×Ŗ מכונה": ["ML", "ml", "machine learning"], "עיבוד שפה ×˜×‘×¢×™×Ŗ": ["NLP", "nlp"], "ביג דאטה": ["big data"], # English terms that appear in Hebrew text "prompt": ["×¤×Ø×•×ž×¤×˜", "×¤×Ø×•×ž×¤×˜×™×", "הנדהת ×¤×Ø×•×ž×¤×˜×™×"], "agent": ["אייג׳נט", "אייג׳נטים"], "token": ["טוקן", "טוקנים"], "API": ["api", "ממשק"], "framework": ["×¤×Ø×™×™×ž×•×•×Ø×§", "×¤×Ø×™×™×ž×•×•×Ø×§×™×"], "library": ["הפרייה", "הפריות"], "debugging": ["דיבוג", "ניפוי שגיאות"], "refactoring": ["×Ø×™×¤×§×˜×•×Ø", "×Ø×™×¤×§×˜×•×Ø×™× ×’", "שכתוב קוד"], "testing": ["בדיקות", "טהטים", "טהט"], "code review": ["קוד ריוויו", "הקירת קוד"], "open source": ["קוד פתוח", "open-source"], "CLI": ["cli", "שורת פקודה"], "LLM": ["llm", "מודל שפה גדול", "מודלי שפה"], "RAG": ["rag"], "MCP": ["mcp"], "VSCode": ["vs code", "ויזואל הטודיו קוד", "vs-code"], "Git": ["git", "גיט"], "GitHub": ["github", "גיטהאב"], "Copilot": ["copilot", "קופיילוט"], "Claude": ["claude", "קלוד"], "Docker": ["docker", "דוקר"], "Python": ["python", "×¤×™×™×Ŗ×•×Ÿ"], "JavaScript": ["javascript", "js", "ג׳אווה ×”×§×Ø×™×¤×˜"], "TypeScript": ["typescript", "ts"], "Ruby": ["ruby", "רובי"], "Rails": ["rails", "×Ø×™×™×œ×”"], "React": ["react", "×Ø×™××§×˜"], "Vue": ["vue", "ויו"], } āœ חיפוש ושילוב התוצאות ראינו ×‘×“×•×’×ž××•×Ŗ ×§×•×“×ž×•×Ŗ איך ×œ×”×’×“×™×Ø להוכן דף הוראות הטטי - ×›×œ×•×ž×Ø טקהט של instructions שיהביר להוכן מה ×¦×Ø×™×š ×œ×¢×©×•×Ŗ. בעבודה RAG דף ההוראות נבנה בצורה ×“×™× ×ž×™×Ŗ לפי ×”×©××™×œ×Ŗ×” ולכן אנחנו ×ž×©×Ŗ×ž×©×™× במנגנון נוהף של Pydantic AI Agents שנקרא Dynamic Instructions. מנגנון זה ×ž××¤×©×Ø לנו ×œ×©×ž×•×Ø פונקציה בתור תוהפת ×œ×”×•×Ø××•×Ŗ, פידנטיק יפעיל את הפונקציה ויוהיף את ערך ההחזר שלה לדף ההוראות. הפונקציה רצה בדיוק לפני ×©×œ×™×—×Ŗ ההודעה למודל ויש לה גישה ×œ×¤×Ø×•×ž×¤×˜ ×•×œ×Ŗ×œ×•×™×•×Ŗ של אותה בקשה. נקרא את הקוד הבא ×ž×Ŗ×•×š הקובׄ app.py: @agent.instructions async def inject_rag_context(ctx: RunContext[Deps]) -> str: """Search Meilisearch and attach the top matching posts to the prompt. The filenames are also stored in ``ctx.metadata['rag_sources']`` so the output validator can prepend them to the final answer. """ query = _extract_query(ctx.prompt) if not query: return "" if ctx.metadata is None: ctx.metadata = {} results = ctx.deps.meili.index(INDEX_NAME).search(query, {"limit": ctx.deps.top_k}) hits = results.get("hits", []) filenames = [hit["filename"] for hit in hits] ctx.metadata["rag_sources"] = filenames if not hits: return ( "No relevant blog posts were found for this question. " "Answer based on your general knowledge, but mention that no posts matched." ) sections = [f"--- {hit['filename']} ---\n{hit['content']}" for hit in hits] return ( f"Relevant blog posts found by Meilisearch: {', '.join(filenames)}. " "Use the content below to answer the user's question. " "Start your response by listing the filenames of the posts you used, "
69
15
šŸ“Œ יום 9 - חיפוש בבלוג ×‘××ž×¦×¢×•×Ŗ RAG וחיפוש טקהט מלא הרבה אנשים שומעים RAG ומיד חושבים על חיפוש ×•×§×˜×•×Ø×™, אבל ×”××ž×Ŗ ש RAG הוא הרבה יותר מזה. היום נדבר על RAG ×“×Ø×š משימה של חיפוש פוהטים בבלוג ×•×ž×—×Ø נעבור לדוגמה עם חיפוש ×•×§×˜×•×Ø×™. שתי ×”×“×•×’×ž××•×Ŗ יחד יחשפו חלק ×ž×”×ž×•×Ø×›×‘×•×Ŗ בכתיבת הוכנים ×©×¦×Ø×™×›×™× ×œ×”×—×–×™×Ø תשובות ×ž×Ŗ×•×š ×ž××’×Ø ידע. āœ אתגר הקונטקהט ×”×Ŗ×—×œ× ×• את ההדרה עם ההוכן הבוחן, הוכן שמקבל ×ž××ž×Ø ×•×ž×™×™×¦×Ø ממנו 10 ×©××œ×•×Ŗ. בהמשך ראינו את ההוכן ×©×ž×™×™×¦×Ø × ×™×•×–×œ×˜×Ø בעזרת כלים, אותו הוכן חיפש בעצמו ברשת ×ž××ž×Ø×™× מעניינים וחיבר ××•×Ŗ× ×œ× ×™×•×–×œ×˜×Ø. שני ×”×ž×§×Ø×™× היו ×“×•×’×ž××•×Ŗ ×¤×©×•×˜×•×Ŗ להוכנים ×©×ž×—×–×™×Ø×™× תשובה על בהיה ×ž××’×Ø ידע ובשניהם העבודה ×”×™×Ŗ×” דומה: ההוכן קיבל את המידע יחד עם הבקשה ובהוף החזיר תשובה כשבחלון הקונטקהט שלו היו המידע הדרוש להחלטה והשאלה של ×”×ž×©×Ŗ×ž×©. למנגנון הזה בדיוק אנחנו ×§×•×Ø××™× RAG שזה ראשי תיבות של Retrieval-augmented generation. ×ž×™×œ×Ŗ ×”×ž×¤×Ŗ×— של RAG היא Retrieval, ×”×ž×¢×Ø×›×Ŗ ×ž×•×©×›×Ŗ את המידע ×”×Ø×œ×•×•× ×˜×™, מוהיפה אותו לחלון הקונטקהט (זה ה Augmented) ומפעילה את המודל כדי ×œ×™×™×¦×Ø תשובה. בשתי ×”×“×•×’×ž××•×Ŗ שהצגתי לא היינו ×¦×Ø×™×›×™× ×œ×”×Ŗ×œ×‘×˜ איזה מידע להכניה לחלון הקונטקהט. ההוכן הבוחן היה ×¦×Ø×™×š את ×”×ž××ž×Ø וזה מה שהוא קיבל. הוכן ×”× ×™×•×–×œ×˜×Ø היה ×¦×Ø×™×š איזשהם ×ž××ž×Ø×™× ומילא לעצמו את החלון. ×ž×¢×Ø×›×•×Ŗ RAG יותר ×ž×¢× ×™×™× ×•×Ŗ הן ×ž×¢×Ø×›×•×Ŗ שצריכות להחליט בצורה ×“×™× ×ž×™×Ŗ ולפי בקשת ×”×ž×©×Ŗ×ž×© איזה מידע להכניה לחלון הקונטקהט לפני שפונים למודל. ×ž×¢×Ø×›×Ŗ RAG ×ž×•×Ø×›×‘×Ŗ לכן ×Ŗ×ž×™×“ ×ž×ž×”×¤×Ø חלקים - החלק של ההוכן והחלק שמחפש ×œ×¢×‘×•×Ŗ את בקשת ×”×ž×©×Ŗ×ž×© עם מידע ×ž×ž××’×Ø הידע. שימו לב שכל חלק הוא עצמאי, ההוכן הכי טוב בעולם לא יצליח ×œ×¢× ×•×Ŗ נכון אם לא תתנו לו את המידע, והמידע הכי ×Ø×œ×•×•× ×˜×™ לא יעזור אם ההוכן לא מצליח להבין אותו. הרבה פעמים נרצה לשלב גם אופציה לחיפוש המשך, ×›×œ×•×ž×Ø × ×™×Ŗ×Ÿ להוכן מידע ראשוני וכלים כדי למשוך מידע נוהף. בדוגמה היום נבנה הוכן שיודע ×œ×¢× ×•×Ŗ על ×©××œ×•×Ŗ לפי פוהטים מהבלוג הזה. אנחנו נבנה: 1. ×”×§×Ø×™×¤×˜ ×©×ž×•×Ø×™×“ את הפוהטים מהבלוג ומאנדקה ××•×Ŗ× לבהיה × ×Ŗ×•× ×™× המיועד לחיפוש בשם Meilisearch. 2. קוד ×¤×™×™×Ŗ×•×Ÿ שמקבל שאלת ×ž×©×Ŗ×ž×© ומחפש ב Meilisearch איזה פוהטים עשויים ×œ×”×™×•×Ŗ ×Ø×œ×•×•× ×˜×™× לשאלה זו (איזה פוהטים מכילים מילים מהשאלה). 3. קוד הוכן שמקבל פוהטים מהבלוג ×•×©××œ×Ŗ ×ž×©×Ŗ×ž×© ועונה על השאלה לפי מה שכתוב בפוהטים. ×©×œ×•×©×Ŗ החלקים מהווים ×ž×¢×Ø×›×Ŗ RAG וילמדו אותנו על ×”××Ŗ×’×Ø×™× בבניית ×ž×¢×Ø×›×Ŗ כזו. āœ יצירת המידע שלב ×Ø××©×•×Ÿ ×‘×ž×¢×Ø×›×Ŗ הוא יצירת ×ž××’×Ø המידע. בדוגמה שלנו × ×Ŗ×—×™×œ עם ×”×”×§×Ø×™×¤×˜ download-data.py ×©×ž×•×Ø×™×“ את כל הפוהטים ×ž×”××Ŗ×Ø לקבצי markdown מקומיים. זה קל כי אני ממילא ×ž×¤×Ø×”× את הפוהטים גם ב HTML וגם ב Markdown ולכן הקוד ×¦×Ø×™×š ×œ×Ø×•×„ על דף או דפי אינדקה מהבלוג, לאהוף את ×”×§×™×©×•×Ø×™× לפוהטים ואז ×œ×”×•×Ø×™×“ ××•×Ŗ× בלי להעמיה על השרת. זאת הפונקציה ×”×ž×Ø×›×–×™×Ŗ: async def run(count: int) -> None: DATA_DIR.mkdir(parents=True, exist_ok=True) semaphore = asyncio.Semaphore(MAX_CONCURRENCY) async with httpx.AsyncClient( headers={"User-Agent": USER_AGENT}, follow_redirects=True, timeout=30.0, ) as client: slugs = await collect_slugs(client, count, semaphore) if len(slugs) < count: print(f"warning: only {len(slugs)} posts found (requested {count})") # The slug list is the download queue; the semaphore enforces at most # MAX_CONCURRENCY concurrent requests. await asyncio.gather( *[download_post(client, slug, semaphore) for slug in slugs] ) print(f"done: downloaded {len(slugs)} posts to {DATA_DIR}/") ×”×”×§×Ø×™×¤×˜ לוקח ×ž×”×ž×©×Ŗ×ž×© ×ž×”×¤×Ø פוהטים ×œ×”×•×Ø×™×“ ×•×©×•×ž×Ø את כולם בתיקיית data. āœ ×©×ž×™×Ø×Ŗ הפוהטים בבהיה × ×Ŗ×•× ×™× לחיפוש אחרי שיש לנו ×ž××’×Ø מידע אנחנו ×Ø×•×¦×™× לאנדקה אותו ×›×œ×•×ž×Ø ×œ×©×ž×•×Ø את המידע בצורה שיהיה קל ×œ××—×–×Ø את הפוהטים ×”×Ø×œ×•×•× ×˜×™× ×œ×©××œ×•×Ŗ ×ž×©×Ŗ×ž×©×™×. זה תפקידו של ×”×”×§×Ø×™×¤×˜ בקובׄ index.py מנוע החיפוש מיילי ×©×•×ž×Ø אוביקטים ויודע לחפש בטקהט שלהם. הפונקציה הראשונה קוראת את הקבצים מהדיהק ×œ×Ø×©×™×ž×” ×‘×–×›×Ø×•×Ÿ: def build_documents() -> list[dict[str, str]]: """Read every ``.md`` file in ``data`` and build Meilisearch documents.""" docs: list[dict[str, str]] = [] for path in sorted(DATA_DIR.glob("*.md")): content = path.read_text(encoding="utf-8") docs.append( { "id": path.stem, "filename": path.name, "title": _extract_title(content),
95
16
https://www.tocode.co.il/blog/2026-08-09-blogsearch
77
17
āœ קוד ההוכן ההוכן נמצא בקובׄ quiz_agent.py וכולל את המודל שבחרנו (ג'מיני), ההוראות למודל ומבנה הפלט. ההוכן הבוחן ×Ŗ×ž×™×“ ×ž×—×–×™×Ø פלט באותו מבנה שכולל גם את הציון על השאלה ×”×§×•×“×ž×Ŗ וגם את השאלה הבאה, כאשר כל אחד ×ž×”×©×“×•×Ŗ עשוי ×œ×”×™×•×Ŗ מאופה: class QuizTurn(BaseModel): """One step of the conversational quiz.""" feedback: str = Field( description="Assessment of the user's previous answer; empty for the " "first question.", ) score: int = Field( ge=0, le=10, description="Score for the previous answer, 0 for the first question.", ) next_question: str | None = Field( description="The next question to ask; null when the quiz is over.", ) āœ ×˜×œ×’×Ø× בשביל החיבור ×œ×˜×œ×’×Ø× ×ž×¤×™×™×Ŗ×•×Ÿ ×”×©×Ŗ×ž×©×Ŗ×™ בהפריית python-telegram-bot. הקוד ×©×ž×Ŗ×—×‘×Ø ×œ×˜×’×Ø× ×©×ž×•×Ø בקובׄ main.py. העבודה עם ×˜×œ×’×Ø× ×ž×‘×•×”×”×Ŗ על Handlers, ×›×œ×•×ž×Ø אנחנו ×Ø×•×©×ž×™× פונקציות שלנו שייקראו ×›×©××™×Ø×•×¢×™× מהוימים יקרו ×‘×˜×œ×’×Ø× לדוגמה: app.add_handler(CommandHandler("start", on_start)) app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, on_text)) כששולחים פקודת /start נפעיל את הפונקציה on_start. כששולחים טקהט כלשהו שאינו פקודה נפעיל את הפונקציה on_text. פונקציית ×”×”×Ŗ×—×œ×” ×©×•×œ×—×Ŗ הודעת פתיחה ופונקציית קריאת הטקהט בודקת אם הטקהט נראה כמו URL היא ×Ŗ×Ŗ×—×™×œ שיחה חדשה עם ההוכן על ×”×ž××ž×Ø שנשלח, וכל טקהט אחר נחשב בתור תשובה לשאלה שההוכן אולי שלח. āœ עכשיו ××Ŗ× 1. דברו עם botfather, צרו בוט חדש ×œ×˜×œ×’×Ø× והפעילו את ההוכן הבוחן על המכונה שלכם כדי ×œ×“×‘×Ø עם הבוט שלכם. ×ž×“×Ø×™×š ×ž×¤×•×Ø×˜ כאן: https://paths.grasp.study/public-modules/2989b86f-e56c-4677-b75d-5042f0666827/lessons/13e343e2-1bb8-43f5-abc0-08695aaff682 2. הבוט ×ž×™×™×¦×Ø שאלה חדשה אחרי כל הודעה. עדכנו את הקוד כך שהבוט יבנה את עשר ×”×©××œ×•×Ŗ מיד עם ×§×‘×œ×Ŗ ×”×ž××ž×Ø. חשבו: מה היתרונות והחהרונות של כל גישה? 3. האם ×ž×©×Ŗ×ž×©×™× יכולים ×œ×©× ×•×Ŗ את התנהגות הבוט, למשל ×œ×™×™×¦×Ø ×ž×”×¤×Ø שונה של ×©××œ×•×Ŗ או ×œ×’×Ø×•× לבוט לקבל תשובות לא נכונות, או אפילו לחשוף מידע שלא ××ž×•×Ø ×œ×”×™×•×Ŗ נגיש להם? שחקו עם הבוט, נהו לבעוט בו ולמצוא נקודות תורפה. 4. מה יקרה אם תנהו לדחוף את הבוט כמו שהוא כתוב כרגע לשרת בענן של Vercel? על איזה ×©×Ø×Ŗ×™× אפשר ×œ×”×Ø×™×„ את הבוט הזה? מה ×¦×Ø×™×š לעדכן כדי ×œ×”×Ø×™×„ אותו בתור AWS Lambda Function או Vercel Function?
110
18
šŸ“Œ יום 8: בוחן ××™× ×˜×Ø×§×˜×™×‘×™ ×ž×”×˜×œ×’×Ø× ×–×•×›×Ø×™× את ההוכן הבוחן ×©×”×Ŗ×—×œ× ×• איתו את ההדרה? ההוכן לקח ×ž××ž×Ø ×ž×”×Ø×©×Ŗ ויצר ממנו ×©××œ×•×Ŗ הבנה כדי שנוכל ללמוד ×—×•×ž×Ø בצורה יעילה יותר. ההוכן של היום הוא גרהה ×ž×•×Ø×—×‘×Ŗ של אותו ההוכן, הפעם לא רק שולח ×©××œ×•×Ŗ ובורח אלא גם נשאר ××Ŗ×›× ובודק את התשובות והכל ×ž×Ŗ×•×š ×”×˜×œ×’×Ø×. קוד הדוגמה בקישור: https://github.com/ynonp/pydanticai-demos/tree/main/08-interactice-telegram-quiz-agent āœ מה אנחנו בונים הבוט זמין לשיחה עם ×ž×©×Ŗ×ž×©×™× ×Ø×‘×™×, כל ×ž×©×Ŗ×ž×© יכול לשלוח לינק ×œ×ž××ž×Ø, הבוט ימשוך את ×”×ž××ž×Ø ויכין 10 ×©××œ×•×Ŗ הבנה על אותו ×ž××ž×Ø. אחר כך הבוט ישלח ×œ×ž×©×Ŗ×ž×© שאלה ראשונה, יחכה ×œ×Ŗ×©×•×‘×” ואז ישלח את השאלה הבאה וכך עד להיום הבוחן. āœ אחהון השיחות נקודת ×”×”×Ŗ×—×œ×” ×œ×”×§×™×Ø×” היום היא הקובׄ store.py שמנהל את השיחות. הקובׄ בהך הכל ×©×•×ž×Ø Dictionary ×‘×–×›×Ø×•×Ÿ עם כל השיחות של כל ×”×ž×©×Ŗ×ž×©×™×. בעתיד אפשר יהיה ×œ×”×¢×‘×™×Ø את זה לבהיה × ×Ŗ×•× ×™× ×ž×”×•×“×Ø או ל redis. זה המימוש: class InMemoryKVStore: """A ``KVStore`` backed by a plain dict. State is lost when the process exits.""" def __init__(self) -> None: self._data: dict[str, Any] = {} def get(self, key: str, default: Any = None) -> Any: return self._data.get(key, default) def set(self, key: str, value: Any) -> None: self._data[key] = value def has(self, key: str) -> bool: return key in self._data def delete(self, key: str) -> None: self._data.pop(key, None) āœ המח של הבוט - הקובׄ quiz בקובׄ quiz.py נמצא המוח של הבוט. הקובׄ ×ž×’×“×™×Ø בהך הכל שתי פונקציות: 1. ×”×Ŗ×—×œ×Ŗ בוחן 2. טיפול בתשובה פונקציית ×”×Ŗ×—×œ×Ŗ הבוחן ×ž×•×©×›×Ŗ את ×”×ž××ž×Ø ובונה ממנו את השאלה הראשונה: async def start_quiz( store: KVStore, agent: Agent[None, QuizTurn], chat_id: int | str, url: str ) -> list[str]: """Fetch the article and ask the first question.""" try: title, text = fetch_article(url) except ValueError as exc: return [f"āš ļø {exc}"] result = await agent.run(SEED_PROMPT.format(article=text)) turn = result.output store.set( _key(chat_id), { "title": title, "q_number": 1, "total_score": 0, "status": "active", "pai_history": _dump_history(result), }, ) return [ f"šŸ“– Quizzing you on: *{title}*\nSend /start anytime to begin a new one.", f"Question 1/{TOTAL_QUESTIONS}:\n{turn.next_question}", ] פונקציית הטיפול בתשובה ×ž×—×¤×©×Ŗ את השיחה הנוכחית, מזהה מה ×ž×”×¤×Ø השאלה ×•×©×•×œ×—×Ŗ להוכן את התשובה ×©×”×ž×©×Ŗ×ž×© שלח. אחר כך היא ×©×•×œ×—×Ŗ פידבק ואת השאלה הבאה: async def handle_answer( store: KVStore, agent: Agent[None, QuizTurn], chat_id: int | str, text: str ) -> list[str]: """Evaluate the user's answer and ask the next question (or wrap up).""" session = store.get(_key(chat_id)) if not session or session.get("status") != "active": return [ "Send me an article link (starting with http) and I'll quiz you " "on it." ] q_number = session["q_number"] final = q_number >= TOTAL_QUESTIONS prompt = text if final: prompt += FINAL_CONTROL.format(n=q_number, total=TOTAL_QUESTIONS) history = _load_history(session) result = await agent.run(prompt, message_history=history) turn = result.output total_score = session["total_score"] + turn.score session["total_score"] = total_score session["pai_history"] = _dump_history(result) feedback_line = f"šŸ“ {turn.feedback} (Score: {turn.score}/10)" if final or turn.next_question is None: session["status"] = "finished" store.set(_key(chat_id), session) max_score = TOTAL_QUESTIONS * 10 scorecard = ( f"šŸ Quiz complete!\n\n" f"Final score: *{total_score}/{max_score}*\n\n" f"Send another article link to play again." ) return [feedback_line, scorecard] session["q_number"] = q_number + 1 store.set(_key(chat_id), session) return [ feedback_line, f"Question {q_number + 1}/{TOTAL_QUESTIONS}:\n{turn.next_question}", ]
76
19
https://www.tocode.co.il/blog/2026-08-08-quiz-telegram-agent
89
20
ctx: RunContext[Deps], file: str, existing_text: str, replacement_text: str ) -> str: """Replace the single, exact occurrence of ``existing_text`` in ``file``.""" path = _resolve(ctx.deps.work_dir, file) if not path.is_file(): return f"Error: no such file: {file}" text = path.read_text(encoding="utf-8") count = text.count(existing_text) if count == 0: return f"Error: existing_text not found in {file}" if count > 1: return ( f"Error: existing_text appears {count} times in {file}; " "add more surrounding context to make it unique" ) path.write_text(text.replace(existing_text, replacement_text), encoding="utf-8") return f"Edited {file}" āœ ×”×¤×¢×œ×Ŗ פקודת ×ž×¢×Ø×›×Ŗ הכלי ×”××—×Ø×•×Ÿ, bash, מפעיל פקודת ×ž×¢×Ø×›×Ŗ. פה יש לנו שני ××Ŗ×’×Ø×™×: 1. עלינו לוודא שהפקודה לא ×Ŗ×™×Ŗ×§×¢, ולכן אנחנו ×ž×’×“×™×Ø×™× timeout. 2. עלינו לוודא שהפקודה לא תחזיר פלט ××Ø×•×š מדי. בניגוד לקובׄ שנשאר על הדיהק, פלט של פקודה נעלם אחרי שהפעלנו אותה, ולכן הכלי כותב את הפלט לקובׄ זמני ×•×ž×¢×‘×™×Ø את שם הקובׄ להוכן. כך אם הפלט לא ××Ø×•×š מדי ההוכן יוכל ×œ×§×Ø×•× אותו ×‘×¢×Ø×š שחוזר מהכלי. אם הפלט כן ××Ø×•×š ההוכן ×™×¦×˜×Ø×š להפעיל את כלי read_file אחרי ×”×¤×¢×œ×Ŗ פקודת ×”×ž×¢×Ø×›×Ŗ ×•×œ×§×Ø×•× את ההמשך. זה קוד הכלי: def bash(ctx: RunContext[Deps], command: str) -> str: """Run a shell command in the project directory and return its output. Returns the first 10,000 chars of combined stdout+stderr. The full output is saved to a temp file whose path is reported when truncated, so it can be read in full later with read_file. """ try: proc = subprocess.run( command, shell=True, cwd=ctx.deps.work_dir, capture_output=True, text=True, timeout=BASH_TIMEOUT, ) except subprocess.TimeoutExpired: return f"Error: command timed out after {BASH_TIMEOUT}s" output = proc.stdout + proc.stderr header = f"(exit code {proc.returncode})\n" if len(output) <= MAX_OUTPUT: return header + output with tempfile.NamedTemporaryFile( mode="w", delete=False, suffix=".txt", prefix="minicoder-", encoding="utf-8" ) as fh: fh.write(output) tmp_path = fh.name return ( header + output[:MAX_OUTPUT] + f"\n\n[output truncated; full output saved to {tmp_path} — " "read it with read_file]" ) āœ עכשיו ××Ŗ× 1. הפעילו את ההוכן על המכונה שלכם ובנו בעזרתו משחק. האם הוא הצליח? נהו להחליף מודל ובדקו כיצד מודלים שונים ×ž×Ŗ×ž×•×“×“×™× עם המשימה. 2. הוהיפו להוכן "×–×›×Ø×•×Ÿ" כך שההוכן יכתוב ×¤×Ø×˜×™× חשובים שהוא מגלה על הקוד לקובׄ טקהט. הוהיפו את קובׄ הטקהט הזה ×œ×¤×Ø×•×ž×¤×˜ באופן אוטומטי. האם ×”×–×›×Ø×•×Ÿ עוזר להוכן ×œ×›×Ŗ×•×‘ קוד טוב יותר? 3. הוהיפו ×Ŗ×ž×™×›×” בניהול ×ž×”×¤×Ø שיחות במקביל. פקודת /new פותחת שיחה חדשה, פקודת /list ×ž×Ø××” את כל השיחות ופקודת /resume חוזרת לשיחה אחרת.
98