ai-talks-monitor

Monitors YouTube for new long-form original talks and interviews with AI thought leaders. Use this skill whenever the user asks about new AI talks or interviews, says things like "any new Sam Altman talks?", "check YouTube for new AI interviews", "run the talks monitor", "who's on the watchlist?", "add [name] to the watchlist", or wants to manage, schedule, or configure the AI talks monitor. Also use when the user wants to set up automated YouTube monitoring, integrate AI talk discovery with Telegram, Feishu, OpenClaw, or an RSS reader, or enable org-based searches for conference keynotes or unknown speakers. Filters out derivative content (reactions, summaries, explainers) using LLM classification. Writes RSS 2.0 feeds and optionally sends a chat notification.

install
source · Clone the upstream repo
git clone https://github.com/linzzzzzz/ai-talks-monitor-skill
Claude Code · Install into ~/.claude/skills/
git clone --depth=1 https://github.com/linzzzzzz/ai-talks-monitor-skill ~/.claude/skills/linzzzzzz-ai-talks-monitor-skill-ai-talks-monitor
manifest: SKILL.md
source content

AI Talks Monitor

Watches YouTube for new long-form original talks and interviews featuring AI thought leaders. The script handles YouTube search and state; you handle the classification step.

Supports three modes:

  • Person watchlist (default): tracks specific people across any channel they appear on
  • Org search (opt-in): catches lab insiders and researchers not on the person watchlist, using org-confirmation to filter noise
  • Channel watchlist (opt-in): monitors specific high-signal channels for any AI talk

Setup

  1. Set environment variables:

    • YOUTUBE_API_KEY
      — YouTube Data API v3 key (free; get from Google Cloud Console)
    • TELEGRAM_BOT_TOKEN
      — Telegram bot token (optional; only for
      notifications.backend: "native"
      . Also set
      native.target
      in
      config.yaml
      )
    • AI_TALKS_FEEDS_REPO
      — absolute path to a local git repo (optional; if set,
      --commit-file
      copies RSS feeds there and pushes automatically)
  2. Install Python dependencies:

    pip install requests pyyaml yt-dlp
    

Usage

Check for new talks now

This is a four-phase process: the script fetches candidates, you classify them in smaller batches, the script prepares an accepted-items draft, then you enrich only those accepted items before commit.

Phase 1 — fetch candidates from YouTube:

python3 SKILL_DIR/scripts/check_talks.py --fetch-candidates

Do NOT add

--lookback-days
unless the user explicitly asks to backfill a longer period. The default rolling window in
config.yaml
is correct for normal runs.

Phase 2 — classify candidates (subagent parallel):

The

--fetch-candidates
output ends with a CLASSIFICATION PLAN listing candidate files grouped by category. Spawn up to 3 subagents in parallel — one each for people, orgs, and channels.

  • OpenClaw: use
    sessions_spawn
    (with
    runTimeoutSeconds: 480
    )
  • Claude Code: use the
    Agent
    tool (with
    model: "sonnet"
    ,
    run_in_background: true
    )

Do NOT read any

candidates_*.json
files yourself. The subagents will read them. You only need the reference files below.

Step 1 — read reference context. Before spawning, read these files yourself so you can include their content in each subagent task:

  • SKILL_DIR/CLASSIFY.md
    — classification rules
  • SKILL_DIR/output/state.json
    (if it exists) —
    items
    array for cross-run deduplication
  • All ephemeral files (candidates, reviews, enrichment, accepted) live under
    output/scratch/
    , which is wiped at the start of each
    --fetch-candidates
    run.
  • SKILL_DIR/config.yaml
    is NOT needed — the
    org
    field is already baked into each org candidate

Step 2 — spawn one subagent per category. For each category listed in the CLASSIFICATION PLAN, spawn a subagent with this task:

Classify AI talk candidates for the "{category}" category. Read ALL candidate files listed below, apply the classification rules, and write the review file.

CANDIDATE FILES (read every file — each is ≤15 items):
{list all chunk files for this category, e.g.:
  - SKILL_DIR/output/scratch/candidates_orgs_1.json
  - SKILL_DIR/output/scratch/candidates_orgs_2.json
  - SKILL_DIR/output/scratch/candidates_orgs_3.json
  - SKILL_DIR/output/scratch/candidates_orgs_4.json}

REVIEW FILE: SKILL_DIR/output/scratch/review_{category}.json

CLASSIFICATION RULES:
{paste full CLASSIFY.md content here}

STATE ITEMS (for deduplication):
{paste state.json items array here, or "none" if empty}

OUTPUT FORMAT — write the review file as valid JSON:
{
  "source": "scratch/candidates_{category}",
  "candidates_reviewed": <total items across ALL files above>,
  "accepted": [
    {"id": "VIDEO_ID", "reason": "one sentence: why this meets the acceptance criteria (e.g. which person is confirmed as speaker, or which org affiliation was verified)"}
  ],
  "rejected": ["VIDEO_ID_1", "VIDEO_ID_2"],
  "uncertain": ["VIDEO_ID_3"]
}

RULES:
- Read ALL candidate files listed above. Do not skip any file.
- Every candidate must appear in accepted, rejected, or uncertain.
- The "reason" field is required for each accepted item.
- candidates_reviewed must equal the total items across all files.
- Use "uncertain" for candidates with insufficient information (empty description, ambiguous title). These will NOT be written to state.json and will resurface on the next run.

Step 3 — wait and merge. After all subagents complete, read each

output/scratch/review_{category}.json
file and merge into
SKILL_DIR/output/scratch/review.json
:

{
  "accepted": [{"id": "VIDEO_ID_A", "reason": "..."}, {"id": "VIDEO_ID_B", "reason": "..."}],
  "rejected": ["VIDEO_ID_1", "VIDEO_ID_2", "VIDEO_ID_3"],
  "uncertain": ["VIDEO_ID_4"]
}

IDs in

uncertain
are left unmarked in state and will reappear on the next run.

Phase 3 — prepare accepted items for enrichment:

python3 SKILL_DIR/scripts/check_talks.py --prepare-accepted SKILL_DIR/output/scratch/review.json

This writes

SKILL_DIR/output/scratch/accepted.json
with only the accepted candidates plus their original metadata.

Now read

output/scratch/accepted.json
and generate an enrichment file. For each accepted video, write only the generated fields to
SKILL_DIR/output/scratch/enrichment.json
:

[
  {
    "id": "VIDEO_ID_A",
    "description_clean": "...",
    "title_zh": "...",
    "description_zh": "..."
  }
]

Field guidelines:

  • description_clean
    : a cleaned version of the video description in its original language. Base it only on available metadata (title, channel, description). Remove links, sponsor boilerplate, social handles, calls to action. Keep useful structure like chapter/timestamp breakdowns. If the source description is sparse, write a short conservative blurb rather than inventing details.
  • title_zh
    : a concise, natural Chinese translation of the title (not literal word-for-word)
  • description_zh
    : a Chinese translation of
    description_clean
    , written for a Chinese-speaking audience. Do not add information not present in the metadata.

Important: Use Chinese-style quotation marks

「」
instead of ASCII
"
inside Chinese text to avoid breaking JSON.

Then apply the enrichment:

python3 SKILL_DIR/scripts/check_talks.py --apply-enrichment SKILL_DIR/output/scratch/enrichment.json

This merges your generated fields into

accepted.json
. It will reject the enrichment if any accepted items are missing or have empty fields.

Phase 4 — commit accepted videos:

You MUST use

--commit-file
(not
--commit
). Only
--commit-file
publishes the enriched Chinese translations and cleaned descriptions.

python3 SKILL_DIR/scripts/check_talks.py --commit-file SKILL_DIR/output/scratch/accepted.json

This writes both

ai_talks.xml
(English) and
ai_talks_zh.xml
(Chinese titles and translated descriptions), updates
state.json
, sends a notification if configured, and pushes to the feeds repo if
AI_TALKS_FEEDS_REPO
is set.

Note:

--commit-file
will refuse to publish any item missing a
published_at
date (can happen with yt-dlp metadata fallback). If this occurs, re-run
--fetch-candidates
with
YOUTUBE_API_KEY
set and
backends.metadata: youtube_api
, or set
ytdlp_search.cookies_from_browser
in
config.yaml
, then retry.

Use

--dry-run
to preview without writing files or updating state:

python3 SKILL_DIR/scripts/check_talks.py --commit-file SKILL_DIR/output/scratch/accepted.json --dry-run

After committing, report what was accepted to the user.

Add a person to the watchlist

Edit

SKILL_DIR/config.yaml
and add an entry under
thought_leaders
:

- name: "[Full Name]"
  search_query: '[Full Name]'

For bilingual subjects, add a second entry with their native name:

- name: "Fei-Fei Li (Chinese)"
  search_query: '李飞飞'

Confirm the addition to the user.

Remove from watchlist

Edit

SKILL_DIR/config.yaml
and remove the relevant entry from
thought_leaders
.

Enable org-based searches

Edit

SKILL_DIR/config.yaml
, set
orgs.enabled: true
, and add entries under
orgs.searches
:

orgs:
  enabled: true
  searches:
    - name: "Anthropic Talks"
      search_query: 'anthropic researcher talk podcast'
      org: "Anthropic"
      min_duration_minutes: 20

Org searches are noisier than person searches — a higher

min_duration_minutes
helps.

Show current watchlist

Read and display

thought_leaders
,
channels.list
(if enabled), and
orgs.searches
(if enabled) from
SKILL_DIR/config.yaml
.

Adjust settings

  • min_duration_minutes
    — minimum video length to consider (default: 20). Raise to cut more noise.
  • lookback_days
    — rolling search window in days (default: 5). Every run searches this far back.
  • backends.search
    auto
    ,
    youtube_api
    , or
    yt_dlp
    . Use
    yt_dlp
    to save quota on discovery searches.
  • backends.metadata
    auto
    ,
    youtube_api
    , or
    yt_dlp
    . A good hybrid setup is
    search: yt_dlp
    plus
    metadata: youtube_api
    .
  • ytdlp_search.use_this_week_filter
    — when
    backends.search
    uses yt-dlp, apply a YouTube-side "This week" prefilter before local date filtering (default:
    true
    ). Surfaces fresh uploads that yt-dlp's default relevance ranking misses; local date filtering remains the authoritative cutoff either way.
  • ytdlp_search.cookies_from_browser
    — browser to pull cookies from when a yt-dlp backend is used (
    chrome
    ,
    firefox
    , or
    safari
    ; default:
    ""
    ). Set this if yt-dlp hits YouTube bot-checks or if you want yt-dlp metadata fallback to fetch full descriptions.
  • notifications.backend
    telegram
    ,
    openclaw
    , or
    none
  • notifications.openclaw.channel
    /
    notifications.openclaw.target
    /
    notifications.openclaw.account
    — used when routing notifications through OpenClaw, including Feishu support

Automated Daily Check

The fetch step can be scheduled unattended —

--fetch-candidates
only writes candidate files and never modifies state, so it's safe to run on a timer. The review/prepare/commit steps happen via the skill when you review.

macOS (launchd) — runs even after reboots:

# Create ~/Library/LaunchAgents/com.openclaw.ai-talks-monitor.plist
# then: launchctl load ~/Library/LaunchAgents/com.openclaw.ai-talks-monitor.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key><string>com.openclaw.ai-talks-monitor</string>
  <key>ProgramArguments</key>
  <array>
    <string>/usr/bin/python3</string>
    <string>SKILL_DIR/scripts/check_talks.py</string>
    <string>--fetch-candidates</string>
  </array>
  <key>StartCalendarInterval</key>
  <dict><key>Hour</key><integer>9</integer><key>Minute</key><integer>0</integer></dict>
  <key>EnvironmentVariables</key>
  <dict>
    <key>YOUTUBE_API_KEY</key><string>YOUR_KEY_HERE</string>
  </dict>
</dict>
</plist>

Linux/cron — runs daily at 9am:

0 9 * * * YOUTUBE_API_KEY=your_key python3 SKILL_DIR/scripts/check_talks.py --fetch-candidates

How it works

  1. YouTube search (
    --fetch-candidates
    ): Runs three source types — person watchlist (keyword search via API/yt-dlp), channel watchlist (channel feed via API with
    channelId
    or yt-dlp with
    @handle
    ), org searches (keyword search). All use a rolling
    lookback_days
    window.
  2. Heuristic pre-filter (
    --fetch-candidates
    ): Rejects titles containing "reaction", "summary", "explained", "breakdown", "解读", "总结", "面试", etc. Wipes
    output/scratch/
    , then writes survivors to grouped candidate files there
  3. Classification (you): Read
    output/state.json
    + the grouped candidate files in
    output/scratch/
    ; decide which are genuine original talks, deduplicating same-event uploads both within candidates and against already-committed items. Write decisions to
    output/scratch/review.json
  4. Accepted-item enrichment (you): Run
    --prepare-accepted
    , write
    output/scratch/enrichment.json
    with
    description_clean
    ,
    title_zh
    ,
    description_zh
    , then run
    --apply-enrichment
  5. Commit (
    --commit-file accepted.json
    ): Builds feeds with a rolling 30-day RSS window, updates
    state.json
    , sends a notification if configured

TrendRadar integration

Both feeds are written on every

--commit-file
run. Add either or both to TrendRadar's
config/config.yaml
:

rss:
  feeds:
    - id: "ai-talks"
      name: "AI Thought Leader Talks"
      url: "file:///path/to/skills/ai-talks-monitor/ai_talks.xml"
      max_age_days: 30
      enabled: true
    - id: "ai-talks-zh"
      name: "AI大咖讲座精选"
      url: "file:///path/to/skills/ai-talks-monitor/ai_talks_zh.xml"
      max_age_days: 30
      enabled: true

Files

  • SKILL.md
    — this file
  • scripts/check_talks.py
    — main script:
    --fetch-candidates
    ,
    --prepare-accepted
    ,
    --apply-enrichment
    ,
    --commit-file
  • config.yaml
    — watchlist and settings (edit this to customize)
  • output/state.json
    — persistent: seen video IDs, last_checked timestamp, rolling item list
  • output/ai_talks.xml
    — persistent: auto-generated RSS 2.0 feed (English)
  • output/ai_talks_zh.xml
    — persistent: auto-generated RSS 2.0 feed (Chinese titles and translated descriptions)
  • output/scratch/
    — ephemeral per-run directory, wiped at the start of each
    --fetch-candidates
    :
    • candidates.json
      — full candidate dump
    • candidates_people[_N].json
      /
      candidates_orgs[_N].json
      /
      candidates_channels[_N].json
      — chunked candidate files (≤15 items each)
    • review.json
      — merged classification decisions
    • review_{category}.json
      — per-subagent classification output
    • enrichment.json
      — LLM-generated fields (description_clean, title_zh, description_zh)
    • accepted.json
      — built by
      --prepare-accepted
      , enriched by
      --apply-enrichment
      , input to
      --commit-file