How to Configure Episode and Speaker Profiles for Multi-Speaker Podcast Generation in Open Notebook

Open Notebook uses EpisodeProfile and SpeakerProfile models to declaratively configure reusable podcast templates and voice definitions, enabling fully automated multi-speaker audio generation through REST API endpoints.

Open Notebook provides a structured approach to multi-speaker podcast generation through two complementary data models that separate episode configuration from voice characteristics. By defining reusable profiles in open_notebook/podcasts/models.py, you can create consistent podcast templates without hard-coding speaker details for each generation request. This architecture supports complex multi-speaker scenarios while maintaining clean separation between content structure and voice synthesis configuration.

Understanding the Profile Architecture

EpisodeProfile

The EpisodeProfile model acts as a reusable podcast template stored in open_notebook/podcasts/models.py. It contains the episode structure, language settings, and references to both the speaker configuration and the LLM models used for outline and transcript generation. According to the source code, validation ensures that num_segments falls between 3 and 20 (lines 82-86).

SpeakerProfile

The SpeakerProfile model defines the voice cast for your episodes, supporting 1-4 speakers per profile. Each speaker requires a dictionary with mandatory keys: name, voice_id, backstory, and personality. The validator enforces both the speaker count limit and required field presence (lines 60-68). Profiles may also specify a shared TTS model via voice_model or legacy fields for backward compatibility.

Creating Speaker Profiles via REST API

To define a multi-speaker setup, POST to the speaker profiles endpoint defined in api/routers/speaker_profiles.py. Each speaker needs distinct voice characteristics and backstory to drive personality-driven dialogue generation.

import requests

profile = {
    "name": "TechTalk",
    "description": "Four-person tech round-table",
    "voice_model": "model://tts/openai/whisper-v2",
    "speakers": [
        {
            "name": "Host",
            "voice_id": "voice_01",
            "backstory": "Seasoned tech journalist",
            "personality": "Curious, friendly"
        },
        {
            "name": "Engineer",
            "voice_id": "voice_02",
            "backstory": "Senior software engineer",
            "personality": "Analytical, dry"
        },
        {
            "name": "Designer",
            "voice_id": "voice_03",
            "backstory": "UX designer with a flair for storytelling",
            "personality": "Creative, enthusiastic"
        },
        {
            "name": "Investor",
            "voice_id": "voice_04",
            "backstory": "Venture capitalist",
            "personality": "Strategic, confident"
        }
    ]
}

resp = requests.post("http://localhost:5055/speaker-profiles", json=profile)
print(resp.json())

The validator rejects any profile with fewer than 1 or more than 4 speakers, ensuring the generation system can manage dialogue complexity effectively.

Defining Episode Profiles

Episode profiles link to speaker configurations and specify generation parameters. The speaker_config field references your speaker profile by name, while outline_llm and transcript_llm point to model record IDs that get resolved via _resolve_model_config during generation.

episode = {
    "name": "AI Futures",
    "description": "Exploring upcoming AI trends",
    "speaker_config": "TechTalk",
    "outline_llm": "model://llm/openai/gpt-4o",
    "transcript_llm": "model://llm/openai/gpt-4o-mini",
    "language": "en-US",
    "default_briefing": "Discuss the impact of AI on society in 5 minutes.",
    "num_segments": 5
}

resp = requests.post("http://localhost:5055/episode-profiles", json=episode)
print(resp.json())

Episode profile endpoints live in api/routers/episode_profiles.py. The num_segments value must be between 3 and 20, as enforced by the Pydantic validator in the model definition.

Triggering Multi-Speaker Podcast Generation

When you trigger generation via api/routers/podcasts.py, the backend orchestrates the resolution process before handing configurations to the podcast-creator library.

payload = {
    "episode_name": "AI Futures – Episode 1",
    "episode_profile": "AI Futures"
}

resp = requests.post("http://localhost:5055/podcasts/generate", json=payload)
print(resp.json())

The generation pipeline executes four critical steps:

  1. Profile Retrieval: Calls EpisodeProfile.get_by_name to load the template and SpeakerProfile.get_by_name to resolve the linked voice configuration.
  2. Model Resolution: Invokes resolve_outline_config and resolve_transcript_config to convert stored model IDs into concrete provider-model-config tuples using _resolve_model_config.
  3. TTS Configuration: For each speaker, calls SpeakerProfile.resolve_tts_config to obtain the TTS provider, model, and credentials.
  4. Orchestration: Passes all resolved configurations to the podcast-creator library for multi-speaker synthesis.

The main orchestration logic also resides in commands/podcast_commands.py, which builds safe output directories and creates PodcastEpisode records during processing.

Migration Considerations

If upgrading from legacy versions, the migration script in open_notebook/podcasts/migration.py (line 66) converts string-based model references to the new record-ID schema. This ensures existing profiles remain compatible with the current resolution system without manual intervention.

Summary

  • Open Notebook separates podcast configuration into EpisodeProfile (templates) and SpeakerProfile (voices) models defined in open_notebook/podcasts/models.py.
  • Speaker profiles support 1-4 speakers with mandatory fields: name, voice_id, backstory, and personality.
  • Episode profiles validate num_segments between 3-20 and link to speaker configs via the speaker_config field.
  • The generation pipeline in api/routers/podcasts.py resolves model IDs and TTS configurations before passing data to the podcast-creator library.
  • Legacy migrations are handled automatically via open_notebook/podcasts/migration.py.

Frequently Asked Questions

How many speakers can I configure in a single SpeakerProfile?

The SpeakerProfile model in open_notebook/podcasts/models.py enforces a limit of 1-4 speakers per profile (lines 60-68). Each speaker must include the mandatory fields name, voice_id, backstory, and personality to ensure the generation system can create distinct voice characteristics and dialogue patterns.

What validation rules apply to episode segments?

According to the source code at lines 82-86 of open_notebook/podcasts/models.py, the num_segments field in EpisodeProfile must be between 3 and 20. This validation ensures that generated episodes maintain a reasonable narrative structure without becoming too fragmented or excessively long.

How does Open Notebook resolve model references during generation?

When processing a request in api/routers/podcasts.py, the system calls resolve_outline_config and resolve_transcript_config on the episode profile, which internally use _resolve_model_config to convert stored model IDs into concrete provider-model-config tuples. For text-to-speech, it calls SpeakerProfile.resolve_tts_config to obtain provider credentials and voice settings for each speaker.

Can I reuse the same speaker profile across multiple episode templates?

Yes, the architecture is designed for reusability. You create a SpeakerProfile once via api/routers/speaker_profiles.py, then reference it by name in multiple EpisodeProfile configurations using the speaker_config field. This allows you to maintain consistent voice casting across different podcast series or episode formats while varying the LLM models or segment counts.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →