HomeBlogTranscribePlansAPI Keys

Transcripciones de YouTube con LlamaIndex: Consultar un Conjunto de Videos

Índice de vectores de LlamaIndex construido a partir de múltiples transcripciones de video de YouTube respondiendo una pregunta

Un pipeline de transcripción de YouTube de LlamaIndex es tres pasos: obtener transcripciones como objetos Document, construir un VectorStoreIndex, hacer preguntas en todos los videos a la vez. Aquí está todo en un script, más el único error de fragmentación que arruina la mayoría de primeros intentos.

Obtener transcripciones como Documentos

Ejecuto youtube2text.org, una API REST que devuelve transcripciones de YouTube como JSON — sin raspado desde tu propia IP, que importa en el momento en que esto se ejecuta en cualquier otro lugar que no sea tu portátil (ver por qué los raspadores locales se bloquean si tienes curiosidad). Un GET por video:

import requests
from llama_index.core import Document, VectorStoreIndex

API_KEY = "yt_your_key_here"

VIDEOS = [
    "https://www.youtube.com/watch?v=VIDEO_ID_1",
    "https://www.youtube.com/watch?v=VIDEO_ID_2",
    "https://www.youtube.com/watch?v=VIDEO_ID_3",
]

def fetch_transcript(url: str) -> dict:
    resp = requests.get(
        "https://youtube2text.org/api/transcribe",
        params={"url": url},
        headers={"x-api-key": API_KEY},
        timeout=90,
    )
    resp.raise_for_status()
    return resp.json()["result"]

docs = []
for url in VIDEOS:
    r = fetch_transcript(url)
    docs.append(Document(
        text=r["content"],
        metadata={"videoId": r["videoId"], "title": r["title"]},
    ))

Para una clave, inicia sesión con Google en youtube2text.org/app/keys, u obtén la clave demo compartida de GET https://youtube2text.org/api/demo-key (5 videos/mes por IP — bien para este ejemplo de tres videos, no para un canal).

El error de fragmentación: los subtítulos automáticos no tienen puntuación

El analizador de nodos predeterminado de LlamaIndex es SentenceSplitter, que encuentra límites de fragmentos al final de las oraciones. Los subtítulos cargados manualmente los tienen. Los subtítulos generados automáticamente frecuentemente no — obtienes cuarenta minutos de texto en minúsculas sin períodos, SentenceSplitter no encuentra nada en lo que dividir, y terminas con fragmentos hinchados e incómodos.

La división basada en tokens evita el problema por completo:

from llama_index.core.node_parser import TokenTextSplitter

splitter = TokenTextSplitter(chunk_size=512, chunk_overlap=64)

512 tokens con 64 de superposición ha sido el punto dulce en mis pruebas: lo suficientemente grande para que un fragmento lleve un pensamiento completo, lo suficientemente pequeño para que la recuperación siga siendo precisa.

Construir el índice y consultarlo

index = VectorStoreIndex.from_documents(docs, transformations=[splitter])
query_engine = index.as_query_engine(similarity_top_k=4)

response = query_engine.query("What did they say about pricing changes?")
print(response)

for node in response.source_nodes:
    meta = node.metadata
    print(f'- {meta["title"]} (https://www.youtube.com/watch?v={meta["videoId"]})')

Porque cada Document lleva videoId y title en metadatos, cada fragmento recuperado sabe de qué video vino — source_nodes te da enlaces funcionales de vuelta a las fuentes. Esto usa cualquier modelo de incrustación y LLM que hayas configurado en Settings; la configuración OpenAI predeterminada funciona de inmediato si OPENAI_API_KEY está establecida.

La limitación que deberías saber

La API devuelve transcripciones como texto plano sin marcas de tiempo, así que tus citas se resuelven a un video, no a un momento dentro de él. "Este reclamo vino del video de desglose de ganancias de Q3" es alcanzable; "a los 14:32" no. Para la mayoría de casos de uso de preguntas y respuestas, la atribución a nivel de video es suficiente, y honestamente, el texto de fragmento más un enlace de video lleva a un humano al lugar correcto en menos de un minuto.

Nota también los dos códigos de error que vale la pena manejar en el bucle de obtención: TRANSCRIPT_UNAVAILABLE (404, video no tiene subtítulos — sáltalo) y RATE_LIMIT_EXCEEDED (429, viene con retryAfterSeconds en el cuerpo del error).

Escalando más allá de un puñado de videos

Tres videos es una demostración. Para un canal completo quieres el feed RSS para descubrimiento de videos, obtención consciente de cuota, y un almacén de vectores persistente — cubro ese pipeline en RAG sobre un canal de YouTube. ¿Usuario de LangChain en su lugar? Mismo patrón de cargador, marco diferente.

El nivel gratuito cubre 5 videos al mes; el plan pro hace 500 por $9.99, que indexa cómodamente la mayoría de canales. Claves en youtube2text.org/app/keys.