חזרה לבלוג

יצירת פודקאסט עם שני מנחים מתוך פלט של אפליקציה, עם Gemini

מנוע ליצירת פודקאסטים מעל אפליקציה, לצד הסוכן הקולי החי. prompt אחד של ~30 שורות הוא כל התרומה האנושית; שני מודלים של Gemini משתפים פעולה דרך שדה JSON יחיד. הנה איך זה עובד, מה למדתי, וה־template repo וה־playground שהוצאתי מזה.

gemini tts podcast multi-speaker llm audio

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

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

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

הדבר היחיד שבן אדם כתב

כל התרומה האנושית היא prompt יחיד. הוא עושה ארבעה דברים, ושלושה מהם הם בעצם לומר לא.

הוא מלהק שתי דמויות. “You are writing a two-host podcast script.” מנחה A מוביל את השיחה ושואל את שאלת ההמשך החדה; מנחה B הוא האנליטי שמביא מספרים, תאריכים ושמות ספציפיים, ודוחף בחזרה כשטענה מרגישה רדודה. אז ה־דינמיקה — מראיין מול ספקן — מעוצבת בידי אדם. המילים שהם בפועל אומרים, לא.

הוא מציב גדרות נגד AI slop. זה רוב ה־prompt, וזה כולו מרחב שלילי: אל תפתח ב־“welcome back”, אל תשתמש במילות מילוי כמו “great point” או “what’s fascinating”, בלי סיום, בלי רמזי מוזיקה, בלי הוראות במה. כל מי ששמע פודקאסט שנוצר ב־AI מכיר את המחלה — ה־“וואו, זה ממש מרתק!” הנושם בין כל חילופי דברים. ה־prompt הוא רשימת חיסונים נגד זה.

הוא נועל הכול לעובדות. “Do not invent facts. If the source omits something, omit it from the script.” אותה משמעת כמו של המסמך שהוא מסכם. למודל מותר לביים מחדש ידע כדיאלוג, לא להוסיף שום דבר.

הוא כופה צורה קריאה־למכונה. הפלט הוא לא פרוזה אלא JSON קשיח — [{"speaker": "Alex", "text": "..."}]. האילוץ האחרון נראה כמו פינוק של פורמט, אבל הוא נושא משקל, ואני אחזור לזה.

מי כותב את התוכן

Gemini Pro כותב אותו. המודל מקבל את ה־prompt הזה ועוד את גוף הפלט של האפליקציה, ומחזיר את הדיאלוג. Temperature 0.4 — נמוך בכוונה, כי המשימה היא “לביים את העובדות האלה כשיחה”, לא “להיות יצירתי”. הפלט הוא בערך 1,100–1,500 מילים, מה שמתורגם לבערך שמונה עד עשר דקות אודיו.

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

כמה דברים שלמדתי

gemini-2.5-flash-preview-tts הוא מודל דיבור גנרטיבי, לא הסוג השטוח והרובוטי הישן. הוא לא מחבר הברות מוקלטות מראש. הוא מייצר את גל האודיו מאפס, וכדי לעשות את זה טוב הוא צריך לקרוא את המשפט ולהחליט איך בן אדם היה אומר אותו — איפה לשים את ההדגשה, מתי לעצור, האם שורה היא שאלה אמיתית או עקיצה. “You said what?” יוצא אחרת מ־“you said what”. הוא מפרש את אופן המסירה. מה שהוא לא עושה זה להסיק על התוכן: הוא לא יכול להוסיף עובדה או לסדר מחדש את השיחה. מודל אחד חושב על מה להגיד, מודל שני חושב רק על איך להגיד את זה. שתי משימות צרות, לא מודל אחד שעושה את שתיהן.

החוזה בין שני המודלים הוא שדה JSON יחיד. מודל התסריט מתייג כל שורה ב־"speaker". שלב ה־TTS מוגדר עם בדיוק שני voice mappings — "Alex" ← קול אחד, "Maya" ← קול אחר — וזה הדבר היחיד שהוא יודע. זו גם מגבלה קשיחה: multi-speaker TTS תומך במקסימום שני דוברים.

כאן האילוץ של “JSON קשיח” משתלם, וזה ה־failure mode הכי מעניין בכל ה־pipeline. שלב הסינתזה לא עושה שום ולידציה משלו — הוא מרנדר בעיוורון כל מחרוזת "speaker" שכל שורה נושאת. אם מודל התסריט היה אי פעם ממציא מנחה שלישי, או מאיית שגוי "Alexx", לא היה קול לשייך לשורה הזאת. שלב הסינתזה היה מעביר זבל.

הדבר היחיד שעומד בין שם שהוזה לבין קריאת אודיו שבורה הוא בדיקת allow-list בגבול הפענוח: לפני שמייצרים אודיו, כל תווית דובר נבדקת מול שני השמות המותרים, וכל דבר אחר זורק שגיאה ומנסה שוב. עמדת התכנון היא למנוע, לא לשקם — לוודא חזק פעם אחת, בתפר, ואז לתת לכל שלב במורד הזרם לסמוך על הנתונים. שני המודלים לעולם לא מדברים זה עם זה; הם משתפים פעולה דרך שדה אחד מאומת.

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

ה־template repo

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

github.com/Nitzan94/gemini-podcast

מה שזה נותן הוא פחות “מחולל פודקאסטים” ויותר תבנית: structured multi-speaker orchestration, חוזה מאומת בין שני מודלים, שכבת TTS routing, ו־speaker abstraction שהופך את ה־rendering pipeline לניתן־להחלפת־קולות.

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

playground

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

gemini-podcast.vercel.app

אם תבנו משהו מעל זה, אשמח לשמוע למה השתמשתם בו ואיפה זה נשבר.