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
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.
Search & transcribe
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:
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