docs/devtools/youtube-researcher

YouTube Researcher

An agent that treats YouTube as a knowledge base. It decides on its own to search via a {SearchYT(...)} tool call, downloads the audio, transcribes it with Whisper, and answers you from what it just “watched.” Most real know-how lives in videos — so the model goes there too.

Install

setup.sh
pip install -q google-api-python-client yt-dlp openai-whisper
apt-get install -y ffmpeg   # pre-installed on Kaggle

You'll need a YouTube Data API key from Google Cloud. The notebook runs a small local model (Qwen 2.5 3B) but any tool-capable model works.

youtube_tools.py
from googleapiclient.discovery import build
import yt_dlp, whisper, torch

def search_youtube(query, max_results=5):
    youtube = build("youtube", "v3", developerKey=API_KEY)
    res = youtube.search().list(q=query, type="video",
                                part="id,snippet", maxResults=max_results).execute()
    return [{"title": v["snippet"]["title"],
             "url": f"https://www.youtube.com/watch?v={v['id']['videoId']}"}
            for v in res.get("items", [])]

def download_audio_wav(url, out="target_audio"):
    opts = {"format": "bestaudio/best", "outtmpl": out, "quiet": True,
            "postprocessors": [{"key": "FFmpegExtractAudio",
                                "preferredcodec": "wav", "preferredquality": "192"}]}
    with yt_dlp.YoutubeDL(opts) as ydl:
        ydl.download([url])
    return f"{out}.wav"

def transcribe_audio(path):
    device = "cuda" if torch.cuda.is_available() else "cpu"
    model = whisper.load_model("medium", device=device)
    return model.transcribe(path)["text"]

The agent loop

The model decides for itself when to research. If its reply contains a {SearchYT(query)} call, the system runs the search, transcribes the top hit, then answers with (user message + transcript) as context:

agent_loop.py
import re
searchyt_pattern = re.compile(r"\{SearchYT\((.*?)\)\}", re.DOTALL)

SYSTEM_PROMPT = """You are a research agent designed to search YouTube at your own will.
Search by emitting:  {SearchYT(insert-query-here)}
This returns a list of videos; the top hit is transcribed and read with the
user input to produce your answer."""

raw_reply = generate_response(user_msg, transcript=None)   # first pass: decide
match = searchyt_pattern.search(raw_reply)
if match:
    videos = search_youtube(match.group(1).strip(), max_results=10)
    wav = download_audio_wav(videos[0]["url"])
    transcript = transcribe_audio(wav)
    final = generate_response(user_msg, transcript=transcript)  # second pass: answer