Tous les articles
13 min de lecture

Transformer vos épisodes de podcast en documents pour le RAG

Les notes d’épisode décrivent un podcast ; elles n’exposent pas ce qui s’y dit. Voici le chemin d’ingestion complet, du flux RSS aux chunks horodatés et indexables — synchronisation du flux, règles de découpage, schéma PostgreSQL et formatage des citations.

Les podcasts recèlent des connaissances de fond, des entretiens et des discussions d’experts, que l’on cherche très mal.

Un titre et des notes d’épisode décrivent le contenu, mais n’exposent pas tout ce qui s’y dit. Un système RAG a besoin de l’intégralité de la parole, sous forme de texte structuré.

Techtuel convertit les URL audio de podcast, les pages d’épisode et les sources RSS prises en charge en transcriptions horodatées, via une seule API de transcription de podcast.

Vous pouvez ensuite :

  • découper chaque épisode en chunks indexables ;
  • calculer les embeddings ;
  • les stocker dans une base vectorielle ;
  • chercher dans tout un catalogue de podcasts ;
  • répondre aux questions avec un lien vers le moment concerné ;
  • résumer ou comparer plusieurs épisodes.
URL RSS ou d’épisode de podcast

Techtuel

Transcription horodatée

Chunks et métadonnées

Embeddings et index de recherche

Réponses RAG avec citation de l’épisode

Pourquoi les podcasts sont d’excellentes sources RAG

Un catalogue de podcasts peut faire office de base de connaissances spécialisée.

Il peut contenir :

  • des entretiens avec des experts du domaine ;
  • des discussions internes à l’entreprise ;
  • des explications techniques ;
  • des archives historiques ;
  • des analyses sectorielles ;
  • des conversations de recherche ;
  • des appels clients ;
  • des conférences enregistrées.

La difficulté n’est pas de calculer un embedding. La difficulté est de transformer chaque épisode en documents propres et structurés de façon homogène.

Un bon processus d’ingestion de podcasts doit conserver :

  • le nom du podcast ;
  • le titre de l’épisode ;
  • l’URL de l’épisode ;
  • la date de publication ;
  • l’URL audio ;
  • la langue ;
  • la durée ;
  • la transcription ;
  • les instants de départ des segments ;
  • les identifiants externes, comme le GUID RSS.

Transcrire un épisode de podcast

Soumettez l’URL directe de l’épisode ou du fichier audio à POST /v1/transcribe :

curl https://api.techtuel.com/v1/transcribe \
  -H "Authorization: Bearer $TECHTUEL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "source_url": "https://cdn.example.com/podcast/episode-42.mp3"
  }'

La source est résolue, transcrite, et le texte revient dans la réponse — un seul appel, sans identifiant de job à conserver.

Exemple de résultat :

{
  "id": "job_9f2c",
  "status": "completed",
  "title": "Quand les systèmes de recherche déraillent en production",
  "language": "fr",
  "minutes": 54.7,
  "transcript": "Bienvenue dans l’émission...",
  "segments": [
    {
      "start_seconds": 0.0,
      "text": "Bienvenue dans l’émission. Aujourd’hui, nous parlons de systèmes de recherche."
    },
    {
      "start_seconds": 5.8,
      "text": "Notre invité a passé cinq ans à construire des infrastructures de recherche."
    }
  ]
}

Chaque segment porte un instant de départ et son texte — et rien d’autre. Un segment se termine implicitement là où commence le suivant, ce qui suffit à votre moteur de recherche pour citer le passage exact de l’épisode.

Un épisode de podcast dure rarement trois minutes, et une transcription ne peut pas dépendre indéfiniment de la durée d’une requête HTTP : POST /v1/transcribe attend jusqu’à trente secondes (ajustable via ?wait=, jusqu’à 120), puis renvoie 202 avec {"id": "job_9f2c", "status": "processing"}. Ce n’est pas une erreur, mais le signal de récupérer la transcription plus tard via GET /v1/transcriptions/{id}. Sur un catalogue d’épisodes longs, c’est le cas nominal — d’où le modèle par jobs décrit ci-dessous.

Ingérer les épisodes depuis un flux RSS

Un flux RSS de podcast expose normalement chaque épisode dans un élément <item>. Le fichier audio est le plus souvent accessible via son <enclosure>. Techtuel expose aussi une API de transcription RSS pour ce type de source.

Un worker d’ingestion basique peut :

  1. récupérer le flux RSS ;
  2. repérer les épisodes pas encore indexés ;
  3. extraire l’URL de l’enclosure ;
  4. soumettre cette URL à Techtuel — POST /v1/transcriptions est ici le bon outil : un rattrapage de flux est de l’ingestion en lot, sur des épisodes longs, et rien n’attend la réponse ;
  5. relire le job jusqu’à un statut terminal ;
  6. stocker la transcription et les métadonnées ;
  7. créer les chunks indexables.

L’étape 5 est bien du polling : il n’existe pas de webhook, l’intégration prévue est un worker planifié qui relit ses jobs en cours. Quatre statuts existent — processing, claimed, completed, failed — mais seuls completed et failed sont terminaux. claimed veut simplement dire qu’un worker a pris le job en charge : traitez-le comme un processing.

const TERMINAL = new Set(['completed', 'failed'])
 
export function isFinished(status: string): boolean {
  // `claimed` n'est PAS terminal : le job vient d'être pris en charge.
  return TERMINAL.has(status)
}

Pour un épisode isolé — un test, un réabonnement à la demande, un ajout manuel — POST /v1/transcribe reste plus court : la transcription arrive dans la réponse, et le 202 éventuel se récupère sur le même GET /v1/transcriptions/{id}.

Utilisez le GUID RSS comme identifiant externe lorsqu’il est stable.

Exemple de métadonnées :

{
  "podcast": "The Search Engineering Podcast",
  "episode": "La recherche hybride en production",
  "guid": "episode-2026-042",
  "published_at": "2026-07-20T08:00:00Z",
  "episode_url": "https://example.com/episodes/hybrid-retrieval",
  "audio_url": "https://cdn.example.com/episodes/hybrid-retrieval.mp3"
}

Exemple Node.js : détecter les nouveaux épisodes RSS

import Parser from 'rss-parser'
 
const parser = new Parser()
 
type StoredEpisode = {
  guid: string
}
 
export async function findNewEpisodes(
  feedUrl: string,
  storedEpisodes: StoredEpisode[],
) {
  const feed = await parser.parseURL(feedUrl)
  const knownGuids = new Set(storedEpisodes.map((episode) => episode.guid))
 
  return feed.items
    .filter((item) => item.guid && !knownGuids.has(item.guid))
    .map((item) => ({
      guid: item.guid!,
      title: item.title ?? 'Épisode sans titre',
      publishedAt: item.isoDate ?? null,
      episodeUrl: item.link ?? null,
      audioUrl: item.enclosure?.url ?? null,
    }))
    .filter((episode) => episode.audioUrl)
}

En production, prévoyez aussi :

  • les GUID manquants ou réutilisés ;
  • les migrations de flux ;
  • les URL d’enclosure modifiées ;
  • les flux privés ;
  • les reprises ;
  • les jobs en double ;
  • les épisodes supprimés ;
  • les limites de débit.

Créer des chunks de podcast pour la recherche

Les transcriptions de podcast sont conversationnelles. Un découpage aveugle à intervalle fixe de caractères tranche une explication en plein milieu.

Une première version pragmatique fusionne les segments adjacents jusqu’à atteindre une taille cible.

Chaque chunk devrait conserver :

{
  "content": "L’invité explique que la recherche hybride combine...",
  "metadata": {
    "podcast": "The Search Engineering Podcast",
    "episode": "La recherche hybride en production",
    "episode_guid": "episode-2026-042",
    "episode_url": "https://example.com/episodes/hybrid-retrieval",
    "start": 1184.2,
    "end": 1268.7,
    "published_at": "2026-07-20T08:00:00Z"
  }
}

start provient du start_seconds de l’API. end, non : l’API n’en renvoie jamais. Vous le calculez au moment du découpage, à partir de l’instant de départ du segment qui suit le chunk — et le dernier chunk d’un épisode n’a pas de successeur : laissez-le à null, ou refermez-le avec les minutes du job converties en secondes.

Quelques règles utiles :

  • préserver l’ordre chronologique ;
  • ne fusionner que des segments adjacents ;
  • viser une plage de tokens stable ;
  • ajouter un léger recouvrement ;
  • conserver l’instant de départ de chaque chunk, et en déduire la fin ;
  • inclure le titre de l’épisode et celui du podcast ;
  • garder la transcription brute ;
  • éviter autant que possible de vectoriser les génériques et les publicités récurrentes.

Construire des citations de podcast horodatées

Votre application peut créer un lien source horodaté quand le lecteur de podcast le permet.

Quand les liens horodatés ne sont pas disponibles, affichez l’instant de départ :

{
  "title": "La recherche hybride en production",
  "episode_url": "https://example.com/episodes/hybrid-retrieval",
  "timecode": "19:44",
  "start_seconds": 1184
}

Un petit formateur suffit :

export function formatTimecode(seconds: number): string {
  const value = Math.max(0, Math.floor(seconds))
  const hours = Math.floor(value / 3600)
  const minutes = Math.floor((value % 3600) / 60)
  const remainingSeconds = value % 60
 
  const mm = String(minutes).padStart(2, '0')
  const ss = String(remainingSeconds).padStart(2, '0')
 
  return hours > 0 ? `${hours}:${mm}:${ss}` : `${minutes}:${ss}`
}

Chercher dans tout un catalogue de podcasts

Une fois les transcriptions indexées, vos utilisateurs posent des questions auxquelles les métadonnées seules ne répondent pas :

  • Quand l’invité a-t-il abordé le coût des bases vectorielles ?
  • Quels épisodes mentionnent telle entreprise ?
  • Quels arguments ont été avancés pour et contre le fine-tuning ?
  • Où sont passées toutes les discussions sur la recherche hybride ?
  • Qu’ont dit trois invités différents du même sujet ?
  • Quelle est la citation exacte sur cette décision technique ?

Votre couche de recherche peut combiner :

  • la recherche vectorielle sémantique ;
  • la recherche plein texte ;
  • des filtres sur la date de publication ;
  • des filtres par podcast et par épisode ;
  • des filtres de langue ;
  • du reranking ;
  • des règles de diversité.

La recherche hybride est souvent précieuse pour les podcasts : les requêtes contiennent des noms propres, des termes techniques ou des noms de produits qui ne doivent pas dépendre uniquement de la similarité sémantique.

Exemple de schéma PostgreSQL

Notez le end_seconds : il est nullable, car c’est une valeur que votre pipeline déduit du segment suivant, pas une valeur renvoyée par l’API. Le dernier chunk d’un épisode n’a rien après lui pour refermer l’intervalle.

CREATE TABLE podcast_episodes (
  id UUID PRIMARY KEY,
  feed_url TEXT,
  guid TEXT NOT NULL,
  podcast_title TEXT,
  episode_title TEXT NOT NULL,
  episode_url TEXT,
  audio_url TEXT NOT NULL,
  published_at TIMESTAMPTZ,
  language TEXT,
  transcript TEXT NOT NULL,
  UNIQUE (feed_url, guid)
);
 
CREATE TABLE podcast_chunks (
  id UUID PRIMARY KEY,
  episode_id UUID NOT NULL REFERENCES podcast_episodes(id) ON DELETE CASCADE,
  content TEXT NOT NULL,
  -- Vient directement du segment renvoyé par l’API.
  start_seconds DOUBLE PRECISION NOT NULL,
  -- DÉDUIT du début du segment suivant ; NULL sur le dernier chunk.
  end_seconds DOUBLE PRECISION,
  embedding VECTOR(1536)
);

Gardez l’état d’ingestion à part si le pipeline est asynchrone :

CREATE TABLE podcast_ingestion_jobs (
  id UUID PRIMARY KEY,
  episode_id UUID NOT NULL REFERENCES podcast_episodes(id),
  transcription_id TEXT UNIQUE,
  -- Reflète le statut du job Techtuel : processing, claimed, completed, failed.
  status TEXT NOT NULL,
  attempts INTEGER NOT NULL DEFAULT 0,
  last_error TEXT,
  updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

Cas d’usage RAG des transcriptions de podcast

Moteur de recherche de podcasts

Laissez vos auditeurs chercher à l’intérieur de chaque épisode et sauter au bon moment.

Base documentaire de recherche

Indexez entretiens et conversations de fond pour les journalistes, chercheurs et analystes.

Base de connaissances interne

Rendez interrogeables vos archives audio privées, vos réunions enregistrées et vos podcasts d’entreprise.

Assistant expert

Créez un assistant ancré dans une émission, une publication, un sujet ou un panel d’experts.

Réutilisation de contenus

Retrouvez les passages pertinents avant de rédiger des notes d’épisode, des articles, des newsletters ou des extraits pour les réseaux.

Analyse transversale

Comparez positions, thèmes et concepts récurrents sur tout un catalogue.

Traiter un catalogue rétrospectif

Pour un gros catalogue :

  • énumérez tous les items du flux RSS ;
  • dédupliquez par GUID et par URL audio ;
  • soumettez les jobs avec une concurrence maîtrisée ;
  • persistez l’identifiant de transcription Techtuel ;
  • rejouez les échecs transitoires avec POST /v1/transcriptions/{id}/retry, décrit sur la page API de transcription asynchrone ;
  • indexez les transcriptions terminées indépendamment ;
  • notez les épisodes définitivement indisponibles.

Les sources publiques déjà traitées peuvent bénéficier du cache intégré de Techtuel, qui évite de retraiter inutilement la même source.

Surveillez les réponses 402 pendant un rattrapage : le champ reason du corps indique si vous avez atteint quota_exceeded (mettez la file en pause) ou payment_failed (un problème de facturation qu’aucune reprise ne résoudra).

Séparer l’ingestion de l’indexation

Ne faites pas de la transcription, du découpage et du calcul d’embeddings une seule opération irréversible.

Stockez :

  1. les métadonnées de la source ;
  2. la transcription complète ;
  3. les segments d’origine ;
  4. vos chunks actuels ;
  5. vos embeddings actuels.

Vous pourrez alors :

  • changer de modèle d’embedding ;
  • ajuster la taille des chunks ;
  • ajouter un index par mots-clés ;
  • retirer les publicités récurrentes ;
  • améliorer les métadonnées ;
  • reconstruire l’index RAG ;

sans retranscrire le moindre épisode.

Hébergement et maîtrise des données

Techtuel est conçu et hébergé en France.

Les fichiers médias sont traités puis supprimés. La transcription est renvoyée par l’API et reste sous votre contrôle jusqu’à l’appel de DELETE /v1/transcriptions/{id}. Ce que cela implique concrètement pour un produit européen est détaillé dans l’article sur la transcription hébergée en Europe.

Pour des contenus privés ou réglementés, il vous revient encore de définir :

  • qui peut soumettre des médias ;
  • quelles sources les utilisateurs ont le droit de traiter ;
  • combien de temps les transcriptions sont conservées ;
  • qui peut interroger l’index ;
  • comment la suppression se propage aux chunks et aux embeddings ;
  • si des données personnelles doivent être caviardées.

Questions fréquentes

Peut-on utiliser un flux RSS comme source ?

Techtuel prend en charge l’ingestion de médias orientée podcast et RSS. Pour un contrôle fin de la synchronisation du catalogue, vous pouvez aussi analyser le flux vous-même et soumettre l’URL audio de chaque épisode.

Peut-on indexer d’anciens épisodes ?

Oui. Tant que l’URL audio reste accessible, un épisode peut être soumis quelle que soit sa date de publication.

La transcription contient-elle des horodatages ?

Oui — des instants de départ. Chaque segment JSON porte start_seconds et text. Il n’y a pas d’instant de fin : un segment court jusqu’au début du suivant, à vous de reconstituer les intervalles au découpage. Les sorties SRT et VTT sont également disponibles.

Peut-on chercher dans plusieurs podcasts à la fois ?

Oui. Stockez le nom du podcast, les métadonnées de l’épisode et les chunks de transcription dans le même moteur de recherche, puis filtrez par collection au besoin.

Comment gérer un nouvel épisode ?

Planifiez une synchronisation du flux, détectez un nouveau GUID, soumettez l’URL audio, puis relisez le job jusqu’à un statut terminal — completed ou failed, claimed signifiant seulement qu’un worker l’a pris en charge — avant d’indexer la transcription. Pour un épisode court soumis à la main, POST /v1/transcribe renvoie directement le texte et vous évite tout ce cycle.

Peut-on exploiter le résultat avec un LLM ?

Oui. La transcription est du texte structuré ordinaire. Elle se découpe, se vectorise et alimente le contexte de n’importe quel modèle de langage compatible.

Commencez à indexer vos podcasts

Transformez vos épisodes en documents horodatés, prêts pour la recherche, les embeddings et le RAG.

Lire la documentation de l’API

Essayer sans compte