用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/pty819/music-record --skill music-daily-recs命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
基于 SOC 职业分类
正在显示 SKILL.md
| name | music-daily-recs |
| description | 每日巡检 75 个音乐评论站,三层抓取 (RSS → HTML → Camoufox) + Kanban Swarm 评分推送 GitHub + Telegram |
| category | music |
| cron_job | ec5ea562d589(每天 04:00 北京时间) |
| author | hermes-agent |
| version | 7.8 |
| license | MIT |
| created | 2026-05-07T00:00:00.000Z |
| updated | 2026-06-14T00:00:00.000Z |
| trigger_condition | cron 每天 04:00 自动,或手动 `hermes cron run ec5ea562d589` |
| metadata | {"hermes":{"tags":["music-reviews","kanban","fan-out","scraper","aggregator"],"related_skills":["kanban-worker","hermes-agent-skill-authoring"]}} |
cron 04:00 → Step 0–5 (cron session)
│
├─ Step 2: RSS (50 站, 8 线程并发, 60-120s)
├─ Step 3: HTML (18 站子进程并发, ~180s, → html_reviews.json)
├─ Step 4: merge (rss + html + camoufox → scraped_raw.json)
├─ Step 5: kanban-swarm.py --confirm
│
↓ cron session 退出,kanban 调度器接管
┌─────────────────────────────────────────────┐
│ Root (done, shared blackboard) │
│ ├─ 6 Camoufox workers (boomkat, PoD, │
│ │ progressor, wild_city, jazztokyo, │
│ │ musicircus) │
│ ├─ Verifier (todo, parent=workers+root) │
│ │ merge_scraped.py + 质量门 │
│ └─ Synthesizer (todo, parent=verifier) │
│ 评分 → 报告 → git push → Telegram │
│ → 归档 │
└─────────────────────────────────────────────┘
站点分发(75 站 = 50 RSS + 18 HTML + 6 Camoufox + 1 skip)
优先级:RSS > HTML > Camoufox > skip。每个站只走一层。
| 层 | 数量 | 实现 |
|---|---|---|
| RSS | 50 | fast-rss-scrape.py — has_rss=True AND rss_url 非空的站 |
| HTML | 18 | scrape_html_parallel.py — HTML_SCRIPT_IDS 集合里的站 |
| Camoufox | 6 | kanban-swarm.py — 剩余站(无 RSS、非 HTML、非 skip) |
| skip | 2 | syrphe, textura(fluid_radio 归 RSS 因为 has_rss=True) |
代码位置:
bin/fast-rss-scrape.py:load_sites()bin/kanban-swarm.py:HTML_SCRIPT_IDS + get_sites()the_wire, the_quietus, a_closer_listen, avant_music_news, bandcamp_daily, igloo_magazine, fluid_radio, icareifyoulisten, jazztimes, sequenza21, van_magazine, rhythm_passport, progarchives, rest_is_noise_ph, attn_magazine, chain_dlk, hhv_mag, new_music_buff, jazz_journal, five_against_four, modern_classical_music, the_classic_review, froots, prog_mistress, side_line, post_punk_com, i_die_you_die, peek_a_boo_magazine
注:fluid_radio 的 feed 灌的是 2013-2022 历史存档,预期 0 条新内容。
| 站 | 脚本 | 备注 |
|---|---|---|
| all_about_jazz | scrape_all_about_jazz.py | Cloudflare 列表页 |
| bandwagon_asia | scrape_bandwagon_asia.py | 新加坡/东南亚 |
| dark_entries_be | scrape_dark_entries.py | 暗潮/哥特/工业 |
| downbeat | scrape_downbeat.py | 爵士 |
| free_jazz_blog | scrape_free_jazz_blog.py | 自由爵士/先锋 |
| hear65 | scrape_hear65.py | 新加坡本地 |
| jazz_trail | scrape_jazz_trail.py | --max-pages 3 控速 |
| mixmag_asia | scrape_mixmag_asia.py | 列表页 + excerpt |
| musique_machine | scrape_musique_machine.py | 电影/音乐混合 |
| resident_advisor | scrape_resident_advisor.py | Cloudflare |
| roots_world | scrape_roots_world.py | curl 直连 |
| sea_of_tranquility | scrape_sea_of_tranquility.py | urllib 直连,早停 5 条 |
| songlines | scrape_songlines.py | 180s timeout |
| squids_ear | scrape_squids_ear.py | 极高产 100+ 条 |
| strangely_isolated_place | scrape_strangely_isolated_place.py | urllib 优先 |
| truth_and_lies_music | scrape_truth_and_lies_music.py | 小而精 |
| world_music_central | scrape_world_music_central.py | 全球 roots/融合 |
| 站 | 备注 |
|---|---|
| boomkat | Cloudflare ASN 封锁,脚本调 Camoufox REST API |
| point_of_departure | JS 渲染站,脚本调 Camoufox REST API |
| progressor | 冷门 prog/fusion,无独立 scrape 脚本 |
| wild_city | 印度/南亚电子,脚本调 Camoufox REST API |
| jazztokyo | 日本爵士,需 JS 渲染 |
| musicircus | 日本先锋/即兴音乐 |
注:サナコレ (sanacol) 因 DNS 解析到 DoD 保留地址 (28.0.0.199) 已移除。水牛 Suigyu、岡島豊樹 jazzbrat 因 RSS 已死/长期停更已移除(2026-06-13)。
syrphe
所有抓取源统一 cutoff:--days 1.5。
fast-rss-scrape.py 默认 --days 1.5--days 1.5--days 1.5禁止:自行计算 cutoff 日期、RSS 走通后开浏览器"交叉验证"、翻超过前 2 页列表页。
# DB 完整性
python3 -c "import sqlite3; c=sqlite3.connect('/home/liyifan/.hermes/kanban.db'); print('DB:' + c.execute('PRAGMA integrity_check').fetchone()[0]); c.close()"
# Gateway
systemctl --user is-active hermes-gateway
# 网络
curl -s -o /dev/null -w "%{http_code}" --max-time 10 https://daily.bandcamp.com/feed
# 期望 200。000 = VPN 断连
cd /home/liyifan/music-record && git pull origin main
# scripts/ 已是 symlink → ~/music-record/bin/,无需复制
# 只需同步 SKILL.md
cp skills/music/music-daily-recs/SKILL.md ~/.hermes/skills/music/music-daily-recs/
8 线程并发,每线程 30s socket timeout。实测 60-120s 完成。
DATE_DIR="/home/liyifan/music-record/2026/$(date +%m)/$(date +%Y-%m-%d)"
mkdir -p "$DATE_DIR"
python3 bin/fast-rss-scrape.py --days 1.5 -o "$DATE_DIR/rss_merged.json"
输出:rss_merged.json — {meta: {total, scraped_at, cutoff_date}, items: [...]}
python3 bin/scrape_html_parallel.py \
--out-dir "$DATE_DIR" --days 1.5 --timeout 180
scrape_html_parallel.py 并行起 18 个子进程。一个失败不影响其他。
sea_of_tranquility 走 --out-dir 模式(自写文件),其余走 stdout 重定向。
输出:17 个 {site_id}_reviews.json
cd /home/liyifan/music-record
python3 bin/merge_scraped.py \
--date-dir "$DATE_DIR" -o scraped_raw.json
输出:scraped_raw.json — {meta: {total, merged_from, scraped_at}, items: [...]}
python3 /home/liyifan/music-record/bin/kanban-swarm.py --confirm
创建:
Root (done, idempotency_key=music-recs-swarm-YYYY-MM-DD)
├─ Camoufox workers (6 个: boomkat, PoD, progressor, wild_city, jazztokyo, musicircus)
├─ Verifier (todo, parent=workers+root)
└─ Synthesizer (todo, parent=verifier)
cron session 到此结束。 kanban scheduler 接管后续。
由 kanban-swarm.py 自动创建。每个 worker 的 body 包含站点 URL、--days 1.5 约束、180 iteration budget。worker 完成后输出 _reviews.json。
body 步骤(VERIFIER_BODY in kanban-swarm.py):
merge_scraped.py 合并 RSS + HTML + Camoufox 数据 → scraped_raw.jsonitems > 0 且含 site_id 字段kanban_complete(metadata={gate: pass, total_items: N})kanban_block(reason=...)body 步骤(SYNTHESIZER_BODY in kanban-swarm.py):
process_reviews.py --max-workers 3 → processed.json(评分 + 中文总结)generate_report.py → recommend/{DATE}.mdgit push origin mainhermes kanban list --tenant music --status done | awk '{print $2}' | xargs hermes kanban archive 归档本轮已完成任务(仅 music tenant)kanban_completeprocess_reviews.py 用 MiniMax-M3 (MiniMax 国内版)(Anthropic SDK,api.minimaxi.com/anthropic)。评分+中文总结同一次 API 调用,线程池 3 并发。
迁移记录:
references/2026-06-15-api-provider-migration.md(从 Tencent Cloud hy3 迁移到 MiniMax M3)。
6 维评分:口味匹配度(最高权重)、创新性、跨领域融合、地区特色、主流降权、评论质量。
输出 schema:{"total_score": 1-10, "cn_summary": "150-300 字"}
| 触发 | 命令 |
|---|---|
| 自动 | cron ec5ea562d589 每天 04:00 |
| 手动 | hermes cron run ec5ea562d589 |
hermes kanban list | grep "$(date +%Y-%m-%d)"
# 期望:Swarm done + Verifier done + Synthesizer done
当用户问"今天是不是正确执行了",按以下步骤逐层验证,每一步都要跑命令拿真实数据:
latest=$(ls -t ~/.hermes/cron/output/ec5ea562d589/ | head -1)
grep -m1 "version:" ~/.hermes/cron/output/ec5ea562d589/$latest
如果 version 不是最新,说明 cron 用了旧版。
DATE_DIR="/home/liyifan/music-record/2026/$(date +%m)/$(date +%Y-%m-%d)"
# RSS
python3 -c "import json; d=json.load(open('$DATE_DIR/rss_merged.json')); print(f'RSS: {d[\"meta\"][\"total\"]} 条')"
# HTML: 每个文件
for f in $DATE_DIR/*_reviews.json; do
site=$(basename "$f" _reviews.json)
size=$(stat -c%s "$f")
count=$(python3 -c "import json; print(len(json.load(open('$f')).get('items',[])))" 2>/dev/null || echo "ERR")
echo " $site: ${size}B, items=$count"
done
# Merge
python3 -c "import json; d=json.load(open('$DATE_DIR/scraped_raw.json')); print(f'Merge: {d[\"meta\"][\"total\"]} 条 from {list(d[\"meta\"].get(\"merged_from\",{}).keys())}')"
# Processed
python3 -c "import json; d=json.load(open('$DATE_DIR/processed.json')); print(f'Processed: {len(d) if isinstance(d,list) else len(d.get(\"items\",[]))} 条')"
cd /home/liyifan/music-record
git log --oneline --since="$(date +%Y-%m-%d)" | grep "recommend\|daily"
ls -la recommend/$(date +%Y-%m-%d).md
hermes kanban list | grep "$(date +%Y-%m-%d)"
| 症状 | 处理 |
|---|---|
| cron 未触发 | hermes cron status;journalctl --user -u hermes-gateway |
| gateway stopped | hermes gateway run --replace |
| worker 卡 ready | hermes kanban dispatch |
| worker running 但无进程 | hermes kanban complete <id> |
| verifier gate=block | hermes kanban show <verifier_id> 看日志;手动重跑 merge 后 hermes kanban edit <verifier_id> --add-metadata '{"gate":"pass"}' |
| MiniMax 429 | process_reviews.py --max-workers 3 → 降 2 |
| 跨日重复 swarm | 自动 idempotency:music-recs-swarm-YYYY-MM-DD |
| 全天推荐丢失 | 一个 worker blocked → verifier 永远 todo → synthesizer 永远 todo。`hermes kanban list |
| 用途 | 路径 |
|---|---|
| RSS 抓取 | bin/fast-rss-scrape.py |
| HTML 16 脚本 | bin/scrape_*.py(不含 scrape_wild_city.py) |
| HTML 并行 wrapper | bin/scrape_html_parallel.py |
| 合并 | bin/merge_scraped.py |
| 评分 | bin/process_reviews.py |
| 报告 | bin/generate_report.py |
| Swarm 创建 | bin/kanban-swarm.py |
| 站点配置 | data/sites.json(唯一源,git 管理) |
| Skill | ~/.hermes/skills/music/music-daily-recs/SKILL.md |
| Camoufox 服务 | systemctl --user hermes-camoufox.service |
| Cron job ID | ec5ea562d589 |
| GitHub | https://github.com/pty819/music-record |
| 症状 | 原因 | 处理 |
|---|---|---|
| scrape 脚本不认 --days | parallel scraper 统一传 --days 1.5,但有些脚本只认 --hours 或 --ref-date → rc=2 | 脚本必须加 --days 支持。新脚本上线前 --help 验证 |
| stdout 模式脚本不输出到 stdout | 脚本只 json.dump(f) 写文件不 print() → parallel scraper 捕获 0B | stdout 模式必须有 print(json.dumps(result)) |
| wild_city 归类错误 | 脚本调 CAMOFOX_BASE REST API 但被放进 HTML_SCRIPT_IDS | 新站上线前 grep CAMOFOX_BASE 确认依赖 |
| Cron 用旧版 skill | commit 在 cron 之后 → git pull 拉不到新代码 | 改完立即 git push;重大改动手动 hermes cron run 验证 |
| RSS 全部 0 条 | VPN TLS 握手超时(15s 太紧) | socket.setdefaulttimeout(30) + terminal timeout=900 |
| HTML 全部 0B + Camoufox 500 | Meta VPN 断连 | curl -s -o /dev/null -w "%{http_code}" https://daily.bandcamp.com/feed 检查;RSS 可能仍可用 |
| Verifier 卡 todo | 等待所有 worker done | 正常;若都 done 仍 todo → hermes kanban dispatch |
| Synthesizer 卡 todo | verifier gate ≠ pass | hermes kanban edit <verifier_id> --add-metadata '{"gate":"pass"}' |
| Camoufox 静默退出 | 进程消失 task 留 running | hermes kanban complete <id> |
| Songlines 180s timeout | 40+ 条需 120-180s | 如仍超时升 240s |
| Songlines 详情页 paywall | /review/xxx 需免费注册才能看全文,curl 返回 "Register now to continue reading" → LLM 评分看到注册引导文字 → 全部 1/10。免费账号每月只能看 2 篇,对 scraper 无用(每次要抓 10-20 篇)。paywall 是 JS 渲染的,curl 永远被拦截 | 列表页已有摘要(~150字),可直接用于评分;如需全文需付费订阅 |
| MiniMax 429 | 并发过高 | --max-workers 3(已默认) |
| bandwagon_asia/strangely_isolated_place 间歇 rc=2 | 17 脚本并行时资源竞争(单独跑正常) | merger 容忍单脚本失败 |