How Episode and Speaker Profiles Enable Multi-Speaker Podcast Generation
Episode profiles define the structural blueprint and LLM configuration for a podcast, while speaker profiles encapsulate voice models, personalities, and backstories for up to four distinct speakers, enabling the system to composite multiple voice tracks into a cohesive multi-speaker podcast.
The open-notebook repository treats podcast generation as a declarative composition of two distinct configuration objects. By separating the episode structure from speaker identity, the architecture supports complex conversational dynamics where each participant maintains a consistent voice and personality throughout the generated audio.
The Architecture of Multi-Speaker Podcast Generation
The system implements a strict separation of concerns between what gets produced and who produces it. This design allows content creators to remix episode formats with different speaker rosters without changing underlying generation logic.
EpisodeProfile: Configuring the Podcast Structure
The EpisodeProfile class in open_notebook/podcasts/models.py serves as the macro-configuration container. It defines the episode's shape through several critical fields:
speaker_config– Stores the name of theSpeakerProfileto use (lines 34-35), establishing the link between structure and voice roster.outline_llmandtranscript_llm– Reference specific LLM models for generating the episode outline and dialogue transcript (lines 68-74).language,default_briefing, andnum_segments– Control localization, content direction, and episode length.
SpeakerProfile: Defining Voice and Personality
The SpeakerProfile class manages the micro-configuration of individual speakers. At its core lies the speakers list (lines 54-57), where each entry is a dictionary containing:
name– The speaker identifier used in transcript generation.voice_id– The TTS engine reference (e.g., ElevenLabs or OpenAI voice IDs).backstoryandpersonality– Prompt data that guides the LLM's response style and content knowledge.voice_model– Optional per-speaker overrides for the TTS engine (lines 78-80).
The model enforces architectural constraints through validate_speakers, which restricts configurations to between one and four speakers (lines 61-63). The voice_model field at the profile level (lines 50-52) points to a default TTS model, while the _prepare_save_data method normalizes this into a RecordID (lines 71-79).
How the Generation Pipeline Orchestrates Profiles
When a user initiates generation, the PodcastService.submit_generation_job method in api/podcast_service.py executes a validation and orchestration sequence.
First, the service retrieves the episode profile by name using EpisodeProfile.get_by_name and validates its existence (lines 48-50). It then performs the same lookup for the speaker profile (lines 52-55). Both profile names are passed to the background generate_podcast command implemented in commands/podcast_commands.py.
Inside the background command, the episode's speaker_config field is used to fetch the full SpeakerProfile object. The system then calls SpeakerProfile.resolve_tts_config (lines 82-89) to resolve each speaker's voice_model or apply per-speaker overrides. The command iterates over the speakers array, generating audio segments with the appropriate voice for each participant. Because the speaker list supports multiple entries, the final podcast assembles distinct voice tracks into a single conversational experience.
Creating Profiles for Multi-Speaker Content
To create a multi-speaker podcast, you must first define the speaker roster, then reference it in an episode configuration.
Define the Speaker Profile
Create a speaker profile containing up to four distinct voices. Each speaker requires a unique voice ID and descriptive attributes that guide the LLM's generation:
{
"name": "TechPanel",
"description": "Three tech experts debating AI",
"voice_model": "model:openai:gpt-4o-mini",
"speakers": [
{
"name": "Alice",
"voice_id": "elevenlabs:alice",
"backstory": "AI researcher at XYZ",
"personality": "curious, analytical"
},
{
"name": "Bob",
"voice_id": "elevenlabs:bob",
"backstory": "Startup founder",
"personality": "enthusiastic, pragmatic"
},
{
"name": "Carol",
"voice_id": "elevenlabs:carol",
"backstory": "Policy analyst",
"personality": "thoughtful, cautious"
}
]
}
Configure the Episode Profile
Create an episode profile that references the speaker configuration via the speaker_config field, while specifying the LLM models and structural parameters:
{
"name": "AI Debate Episode",
"description": "A 10-minute debate on generative AI risks",
"speaker_config": "TechPanel",
"outline_llm": "model:openai:gpt-4o-mini",
"transcript_llm": "model:openai:gpt-4o-mini",
"language": "en-US",
"default_briefing": "Discuss the advantages and concerns of large language models.",
"num_segments": 5
}
Trigger Generation
Submit the generation job using the service layer, which validates both profiles before queuing the background task:
from open_notebook.api.podcast_service import PodcastService
job_id = await PodcastService.submit_generation_job(
episode_profile_name="AI Debate Episode",
speaker_profile_name="TechPanel",
episode_name="AI_Debate_01",
content="Intro text or notebook ID"
)
print(f"Submitted job {job_id}")
Monitor the job status to track progress:
status = await PodcastService.get_job_status(job_id)
print(status)
Upon completion, the resulting PodcastEpisode record contains a speaker_profile object (lines 12-13 in models.py) that preserves the exact speaker configuration used for that specific episode.
Summary
- Episode profiles control the structural blueprint, LLM selection, and content briefing for podcast generation via the
EpisodeProfileclass inopen_notebook/podcasts/models.py. - Speaker profiles encapsulate the voice models, personalities, and backstories for 1-4 speakers, enforcing constraints through
validate_speakersand supporting per-speaker TTS overrides. - The
PodcastService.submit_generation_jobmethod orchestrates profile validation and hands off togenerate_podcastincommands/podcast_commands.py. - Background generation resolves TTS configurations through
SpeakerProfile.resolve_tts_configand composites multiple voice tracks into the final audio file.
Frequently Asked Questions
How many speakers can a single podcast episode support?
The SpeakerProfile model enforces a limit of four speakers through the validate_speakers method in open_notebook/podcasts/models.py (lines 61-63). This constraint ensures manageable audio compositing while supporting diverse conversational formats like interviews, panels, and debates.
Can individual speakers use different text-to-speech models?
Yes. While the SpeakerProfile defines a default voice_model at the profile level, each speaker entry in the speakers list can optionally specify a voice_model override. The resolve_tts_config method processes these overrides during generation, allowing one speaker to use ElevenLabs while another uses OpenAI's TTS engine within the same episode.
How does the system link an episode to its speaker configuration?
The EpisodeProfile stores the speaker configuration name in its speaker_config field (lines 34-35 of models.py). When PodcastService.submit_generation_job executes, it retrieves both the episode profile and the named speaker profile, passing both to the background generation command where they are resolved into full objects.
Where is the final speaker configuration preserved?
The PodcastEpisode model includes a speaker_profile field (lines 12-13 of models.py) that records the complete speaker configuration used during generation. This ensures that the specific voices, personalities, and TTS settings remain associated with the episode record for future reference or regeneration.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →