HomeBlogTranscribePlansAPI Keys

RAG untuk Kanal YouTube: Dari RSS Menjadi Jawaban Bersitasi

Pipeline dari feed RSS kanal YouTube melalui transkrip dan embedding menjadi jawaban bersitasi

Membangun RAG untuk sebuah kanal YouTube berarti menjawab "apa yang pernah dikatakan kreator ini tentang X, dan di video yang mana?" — di seluruh konten yang pernah mereka terbitkan. Pipeline-nya terdiri dari empat langkah: temukan video lewat RSS, ambil transkrip, potong dan embed, jawab dengan atribusi. Berikut setiap langkahnya dalam Python yang bisa langsung dijalankan, termasuk bagian yang dilewatkan kebanyakan tulisan: penanganan kuota dan seperti apa sitasi yang jujur tanpa timestamp.

Langkah 1: video ID dari feed RSS kanal

YouTube masih menyediakan feed RSS sungguhan per kanal, tanpa perlu 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")]

Peringatan: feed ini hanya mengembalikan sekitar 15 unggahan terbaru. Untuk kanal yang aktif, jalankan ini secara terjadwal dan akumulasikan ID di sebuah file atau tabel; katalog lama di luar itu memerlukan YouTube Data API atau daftar manual sekali jalan. Namun untuk menjaga indeks tetap segar, RSS sempurna — ia juga tulang punggung pipeline kanal-ke-newsletter.

Langkah 2: ambil transkrip, hormati kuota

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))]

Hitung dulu paket Anda sebelum melakukan loop atas sebuah kanal: paket gratis 5 video per bulan, yang cukup untuk proof of concept dan tidak lebih. Basic ($5,99) memberi 50, pro ($9,99) memberi 500 — pro adalah batas bawah yang realistis untuk mengindeks sebuah kanal plus unggahan barunya.

Langkah 3: potong dan embed

Vector store apa pun bisa; Chroma membuat contohnya tetap ringkas. Auto-caption sering tidak punya tanda baca, jadi saya memotong berdasarkan jumlah kata alih-alih kalimat:

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"]}],
        )

Langkah 4: jawab dengan sitasi — versi jujur

API ini mengembalikan teks polos tanpa timestamp, jadi saya akan jujur kepada Anda: Anda tidak bisa menyitasi "pada 23:41" dari data ini. Yang bisa Anda lakukan — dan yang biasanya sudah cukup — adalah atribusi video di tingkat chunk: setiap chunk yang diambil tahu videoId dan title-nya, sehingga jawaban menyitasi video mana yang mengatakannya, dengan tautan yang berfungsi.

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: ..."
)

Suplai prompt ke LLM mana pun yang Anda suka — model lokal berfungsi dengan baik di sini, setup yang sama dengan pipa ringkasan Ollama.

Catatan perakitan

Bagi penggemar framework: ini terpetakan satu-satu ke LlamaIndex atau LangChain jika Anda tidak ingin membuat chunking sendiri. Apa pun pilihannya, lapisan transkripnya identik, dan itulah satu-satunya bagian yang bermeteran — ambil key di youtube2text.org/app/keys dan mulailah dengan paket gratis untuk memvalidasi pipeline sebelum mengarahkannya ke 300 video.