用户在 Material Board 上建了一个爬取槽位,只写了一句 brief。20 分钟后,6 条已托管、已看片、已打分的 TikTok 素材出现在预览里。这一页拆开中间发生的全部 6 步 —— 所有数字都来自这个任务落盘的真实产物文件。
整个过程是一个"广撒网 → 层层收窄"的漏斗。前半程靠元数据(标题、热度)快速筛,后半程靠真的看视频(Gemini 逐条分析)严格判。中间还回头补搜了一轮。
"炮轰建筑"四个字太模糊:坦克对轰算吗?只开炮不见楼倒算吗?agent 第一件事就是把 brief 扩成一份检索规格,后面每一步的判断都以它为准。
中文 brief 被翻成英文关键词打 TikHub 的 TikTok 搜索 API(region=US,每次取 20 条)。关键词覆盖不同角度:火炮、攻城、爆破拆除、空袭、坦克。排序在 sort=1(最热)和 sort=0(相关性)之间切换,时间窗在 90 / 180 天之间切换 —— 一个词捞不到的,换个角度再捞。
| # | 轮次 | 关键词 | 排序 | 时间窗 |
|---|---|---|---|---|
| s1 | 第一轮 | cannon destroy building | 最热 | 90 天 |
| s2 | 第一轮 | artillery destroys building | 最热 | 90 天 |
| s3 | 第一轮 | siege cannon wall | 最热 | 90 天 |
| s4 | 第一轮 | 炮轰建筑 | 最热 | 180 天 |
| s5 | 第一轮 | cannon vs building | 相关性 | 90 天 |
| s6 | 第一轮 | tank destroys building | 最热 | 180 天 |
| s7 | 第一轮 | building demolition explosion | 最热 | 180 天 |
| s8 | 第一轮 | artillery strike building | 相关性 | 90 天 |
| s9 | 第一轮 | shelling building war | 相关性 | 90 天 |
| s10 | 第一轮 | cannon blast wall demolish | 最热 | 180 天 |
| s11 | 第一轮 | tank fires at house | 相关性 | 180 天 |
| t1 | 第二轮 | building implosion controlled demolition | 最热 | 180 天 |
| t2 | 第二轮 | tower collapse demolition explosive | 最热 | 180 天 |
| t3 | 第二轮 | cannon breaches castle wall | 相关性 | 180 天 |
| t4 | 第二轮 | airstrike destroys building | 相关性 | 90 天 |
| t5 | 第二轮 | tank shell hits building collapse | 相关性 | 180 天 |
| t6 | 第二轮 | skyscraper demolition blast | 最热 | 180 天 |
搜索结果解析成规整的候选池:第一轮 211 条,第二轮 105 条。这一步不看视频内容 —— 看片太贵,先用便宜的信号(文案是否贴题、点赞播放量)挑出值得看片的入围名单:第一轮 8 条、第二轮 4 条。
{
"id": "tt_7614966496464276758", // 内部 id:tt_ + TikTok aweme_id
"fp": "v24044gl0000d6msn3nog65g...", // 视频内容指纹,S4 去重用
"desc": "When a cannon is fired at a medieval castle wall...",
"digg": 12210, "play": 473721, // 点赞 / 播放
"cmt": 49, "shr": 126, // 评论 / 转发
"dur": 48.7, "age_h": 2748, // 时长(秒) / 发布距今(小时)
"share_url": "https://www.tiktok.com/@ancient.techie/video/...",
"play_url": "https://v16m.tiktokcdn-us.com/...", // CDN 直链 mp4,会过期!
"prevent": false // 是否防下载
}
入围的 id + 内容指纹一起 POST 到 worker 的 known-keys 接口,对 D1 里的历史候选库查重。同一条爆款视频常被搬运多次,指纹(而不是 id)才能认出来。重复的直接跳过,不浪费看片成本。
POST $WORKER_URL/known-keys
{"candidates": [
{"id": "tt_7614966496464276758", "fingerprint": "v24044gl0000d6msn3no..."},
{"id": "tt_7600297867034594591", "fingerprint": "v15044gf0000d5sqs57o..."},
... 共 8 条(第一轮)
]}
← 响应
{"ok": true, "data": {
"duplicates": [], // 本次运气好,零重复
"new": ["tt_7614966496464276758", "tt_7600297867034594591", ... 全部 8 条]
}}
这是全流程最重的一步,也是质量的来源。agent 写了一个编排脚本(.logs/pipeline.wf.js),给每条候选并行起一个子 agent,各自独立跑完 5 站管线:
12 条候选的真实打分结果(.logs/all-pipeline-results.json)—— 打分标准:真实爆破毁楼 0.8–1.0,历史炮轰城墙科普 0.7–0.85,擦边 <0.5:
| 候选 | 内容(据 Gemini 分析) | match | 匹配分 |
|---|---|---|---|
| tt_…879071 | 德州体育场定向爆破,整体坍塌 第二轮 | 真 | |
| tt_…594591 | 大型建筑/桥梁/冷却塔爆破拆除合集 | 真 | |
| tt_…276758 | 3D 科普:加农炮轰中世纪城墙 | 真 | |
| tt_…753951 | 迈阿密水滨高楼爆破内爆 第二轮 | 真 | |
| tt_…590670 | 球形水塔切基坍塌(结构破坏但非炮击)第二轮 | 假 | |
| tt_…770911 | 烟囱破拆("chimney has left the chat") | 假 | |
| tt_…325279 | PCL-181 车载炮实弹射击(只开炮,无建筑) | 假 | |
| tt_…663764 | 弱相关军事画面 | 假 | |
| tt_…556566 | 弱相关军事画面 | 假 | |
| tt_…421982 | 城市火灾(无炮击)第二轮 | 假 | |
| tt_…600223 | 擦边内容 | 假 | |
| tt_…804886 | 基本无关 | 假 |
第一轮 8 条看完只有 2 条强匹配(0.85 / 0.82),不够交付。agent 于是带着教训回到搜索:第一轮输的多是"军事火炮"词,赢的是"爆破拆除"—— 第二轮就全押 demolition 方向,4 条入围又拿下 2 条强匹配(0.90 / 0.82)。
最终组装回调 payload,PATCH 到槽位的 preview 接口,Material Board 确认收货:
PATCH https://sozai.funplus-marketing.ai/api/crawl/slots/96bd3e9f-…/preview
{
"task_id": "bb03a52b-…",
"expanded_brief": "每日抓取「炮轰建筑 / 重型爆破摧毁建筑物」主题的短视频热门素材…",
"summary": "共处理 12 条 TikTok 候选,最终精选 6 条可直接播放的预览样本…",
"samples": [ // 6 条,每条含 video_url / thumbnail_url / caption / source_url ]
}
← Material Board 响应(200)
{"ok": true, "status": "paused", "preview_samples": 6, "hot_materials_inserted": 6}
任务工作区把每一步的中间产物都落了盘。想复核任何一步,用 tb.sh cat bb03a52b-… <文件> 直接看原文: