HomeBlogTranscribePlansAPI Keys

RAG über einen YouTube-Kanal: Von RSS zu Antworten mit Quellenangabe

Pipeline vom RSS-Feed eines YouTube-Kanals über Transkripte und Embeddings zu Antworten mit Quellenangabe

RAG über einen YouTube-Kanal zu bauen heißt, die Frage „Was hat dieser Creator zu X gesagt, und in welchem Video?" zu beantworten — über alles, was er je veröffentlicht hat. Die Pipeline besteht aus vier Schritten: Videos per RSS entdecken, Transkripte holen, chunken und embedden, mit Attribution antworten. Hier ist jeder Schritt als lauffähiges Python, inklusive der Teile, die die meisten Anleitungen auslassen: Kontingent-Handling und wie Zitieren ohne Zeitstempel ehrlicherweise aussieht.

Schritt 1: Video-IDs aus dem RSS-Feed des Kanals

YouTube liefert nach wie vor einen echten RSS-Feed pro Kanal, ganz ohne API-Key:

import requests
import xml.etree.ElementTree as ET

CHANNEL_ID = "UCxxxxxxxxxxxxxxxxxxxxxx"
NS = "{http://www.youtube.com/xml/schemas/2015}"

feed = requests.get(
    f"https://www.youtube.com/feeds/videos.xml?channel_id={CHANNEL_ID}",
    timeout=30,
).text
video_ids = [el.text for el in ET.fromstring(feed).iter(f"{NS}videoId")]

Vorbehalt: Der Feed liefert nur die ~15 neuesten Uploads. Bei einem aktiven Kanal lass das nach Zeitplan laufen und sammle die IDs in einer Datei oder Tabelle; für den Back-Katalog darüber hinaus brauchst du die YouTube Data API oder eine einmalige manuelle Liste. Um einen Index frisch zu halten, ist RSS aber perfekt — es ist auch das Rückgrat der Kanal-zu-Newsletter-Pipeline.

Schritt 2: Transkripte holen, Kontingent respektieren

import time

API_KEY = "yt_your_key_here"

def fetch(video_id: str) -> dict | None:
    resp = requests.get(
        "https://youtube2text.org/api/transcribe",
        params={"url": f"https://www.youtube.com/watch?v={video_id}"},
        headers={"x-api-key": API_KEY},
        timeout=90,
    )
    if resp.status_code == 404:
        return None  # TRANSCRIPT_UNAVAILABLE — no captions, skip
    if resp.status_code == 429:
        time.sleep(resp.json()["error"].get("retryAfterSeconds", 30))
        return fetch(video_id)
    resp.raise_for_status()
    return resp.json()["result"]

transcripts = [t for vid in video_ids if (t := fetch(vid))]

Rechne dein Kontingent durch, bevor du über einen Kanal loopst: Der Free-Tier bietet 5 Videos im Monat, das deckt einen Proof of Concept und sonst nichts. Basic (5,99 $) kauft 50, Pro (9,99 $) kauft 500 — Pro ist die realistische Untergrenze, um einen Kanal samt laufender Uploads zu indexieren.

Schritt 3: Chunken und Embedden

Jeder Vektor-Store funktioniert; Chroma hält das Beispiel kurz. Auto-Untertitel haben oft keine Interpunktion, deshalb chunke ich nach Wortanzahl statt nach Sätzen:

import chromadb

def chunk(text: str, size: int = 300, overlap: int = 50):
    words = text.split()
    for i in range(0, len(words), size - overlap):
        yield " ".join(words[i:i + size])

client = chromadb.PersistentClient(path="./channel_index")
col = client.get_or_create_collection("channel")

for t in transcripts:
    for i, piece in enumerate(chunk(t["content"])):
        col.add(
            ids=[f'{t["videoId"]}-{i}'],
            documents=[piece],
            metadatas=[{"videoId": t["videoId"], "title": t["title"]}],
        )

Schritt 4: Antworten mit Quellenangaben — die ehrliche Version

Die API liefert reinen Text ohne Zeitstempel, deshalb sage ich es dir direkt: „bei 23:41" kannst du aus diesen Daten nicht zitieren. Was du tun kannst — und was meist reicht — ist Video-Attribution auf Chunk-Ebene: Jeder abgerufene Chunk kennt seine videoId und seinen title, Antworten zitieren also, welches Video es gesagt hat, mit funktionierendem Link.

hits = col.query(query_texts=["what's their take on electric trucks?"], n_results=5)

context = "\n\n".join(
    f'[{m["title"]}] (https://www.youtube.com/watch?v={m["videoId"]})\n{doc}'
    for doc, m in zip(hits["documents"][0], hits["metadatas"][0])
)
prompt = (
    "Answer using only the excerpts below. After each claim, "
    f"cite the video title in brackets.\n\n{context}\n\nQuestion: ..."
)

Füttere prompt an ein LLM deiner Wahl — ein lokales Modell funktioniert hier problemlos, gleiches Setup wie die Ollama-Zusammenfassungs-Pipeline.

Notizen zum Zusammenbau

Framework-Fans: Das mappt eins zu eins auf LlamaIndex oder LangChain, wenn du das Chunking lieber nicht selbst bauen willst. So oder so ist die Transkript-Schicht identisch, und sie ist der einzige Teil mit einem Zähler dran — hol dir einen Key unter youtube2text.org/app/keys und starte mit dem Free-Tier, um die Pipeline zu validieren, bevor du sie auf 300 Videos richtest.