Tobatsu · Material Board · Preview Research

「帮我找一批炮轰建筑的热门的视频」
这句话是怎么变成 6 条素材的

用户在 Material Board 上建了一个爬取槽位,只写了一句 brief。20 分钟后,6 条已托管、已看片、已打分的 TikTok 素材出现在预览里。这一页拆开中间发生的全部 6 步 —— 所有数字都来自这个任务落盘的真实产物文件。

taskbb03a52b-2b7d typematerial_board_preview_research 模板v15 · prod 耗时20.6 分钟 结果done · 入库 6 条
00 · 总览

先看漏斗:从 316 条候选到 6 条交付

整个过程是一个"广撒网 → 层层收窄"的漏斗。前半程靠元数据(标题、热度)快速筛,后半程靠真的看视频(Gemini 逐条分析)严格判。中间还回头补搜了一轮。

S1
Brief 扩写
10 个字的需求 → 一份带纳入/排除标准的详细检索规格
1句 → 1 份规格
S2
TikHub 关键词搜索
两轮共 17 个查询,多角度换词、换排序、换时间窗
17次搜索
S3
解析 + 元数据初筛
211 + 105 条候选池,只看标题和热度挑出入围名单
316 → 12
S4
指纹去重
拿视频指纹去 D1 候选库查重,避免重复入库
12条全新
S5
并行媒体管线(真看片)
每条:转存 R2 → Gemini 分析 → 抽帧 → 打 0–1 分 → 入库
12 → 4强匹配
S6
精选 + 回传
4 条强匹配 + 2 条补充维度,PATCH 回 Material Board
6条交付
* 第一轮只捞到 2 条强匹配,agent 自己决定回到 S2 补搜 —— 见第 06 节
01 · Brief 扩写

先把 10 个字翻译成"什么算、什么不算"

"炮轰建筑"四个字太模糊:坦克对轰算吗?只开炮不见楼倒算吗?agent 第一件事就是把 brief 扩成一份检索规格,后面每一步的判断都以它为准。

帮我找一批炮轰建筑的热门的视频
payload.brief · 用户原话,全部输入就这一句
✅ 算数的画面(纳入)
  • +冲击力直接命中并摧毁建筑/城墙/高塔/体育场,有坍塌和粉尘
  • +真实定向爆破拆除(controlled demolition / implosion)
  • +火炮轰城墙的历史科普和 3D 演示
  • +军事炮击 / 空袭命中建筑的纪实片段
❌ 不算的画面(排除)
  • 只开炮、看不到建筑被击中的靶场/军演展示
  • 武器静态陈列、博物馆展品
  • 打的是车辆或人物,不是建筑
  • 标题写"炮轰"但画面无关的标题党
这份规格随最终结果一起回传
扩写后的 expanded_brief 会写进回调 payload。用户在 Material Board 上能看到 agent 是按什么标准找的 —— 不满意可以改 brief 重跑。
02 · TikHub 搜索

两轮 17 个查询,故意互相不重样

中文 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 天
注意第二轮的词变了
第一轮偏"火炮/军事"(cannon、artillery、tank),实际捞到大量"坦克对轰、只开炮不见楼"的弱匹配。第二轮明显转向"爆破拆除"(implosion、demolition、collapse)—— 这是 agent 看完第一轮打分结果后主动修正搜索方向,不是预设好的。
* 每次搜索的原始响应约 1.5MB,全部落盘在 .logs/s1–s11.json、t1–t6.json
03 · 元数据初筛

316 条候选,只看"标题 + 热度"先砍到 12

搜索结果解析成规整的候选池:第一轮 211 条,第二轮 105 条。这一步不看视频内容 —— 看片太贵,先用便宜的信号(文案是否贴题、点赞播放量)挑出值得看片的入围名单:第一轮 8 条、第二轮 4 条。

每条候选提取的字段(.logs/candidates.json,真实样例)
{
  "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                          // 是否防下载
}
为什么 play_url 很关键
这是 TikTok CDN 的临时直链,几小时就过期。所以入围的视频必须马上转存到自己的 R2(S5 第一步),否则回传给 Material Board 的链接是死的。
04 · 指纹去重

看片之前,先问 D1:"这几条我们收过吗?"

入围的 id + 内容指纹一起 POST 到 worker 的 known-keys 接口,对 D1 里的历史候选库查重。同一条爆款视频常被搬运多次,指纹(而不是 id)才能认出来。重复的直接跳过,不浪费看片成本。

.logs/known-keys-req.json → resp.json(本次实录)
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 条]
}}
05 · 并行媒体管线

真正的把关:每条视频都被"看"了一遍

这是全流程最重的一步,也是质量的来源。agent 写了一个编排脚本(.logs/pipeline.wf.js),给每条候选并行起一个子 agent,各自独立跑完 5 站管线:

STEP A
转存 R2
把会过期的 TikTok 直链 mp4 流式转存到自家 CDN,拿到永久 URL
media-uploader → cdn-asia.funplus-marketing.ai
STEP B
Gemini 看片
VAW 按固定 prompt 出完整分析 markdown + 关键时间戳
VIDEO_ANALYSIS_WORKER (Gemini)
STEP C
抽 5 帧
按关键时间戳 ffmpeg 抽帧传 R2;选最能体现"轰击/坍塌"的一帧当封面
ffmpeg + media-uploader
STEP D
判定打分
基于分析文本(不是标题!)判 match 真假 + 0–1 匹配分,写中文 caption
子 agent 按 brief 规格裁决
STEP E
入库留痕
分析全文 + 帧 + 评审结论写进 D1 候选库,供以后去重和复用
worker /ingest + /update-review

12 条候选的真实打分结果(.logs/all-pipeline-results.json)—— 打分标准:真实爆破毁楼 0.8–1.0,历史炮轰城墙科普 0.7–0.85,擦边 <0.5:

候选内容(据 Gemini 分析)match匹配分
tt_…879071德州体育场定向爆破,整体坍塌 第二轮
0.90
tt_…594591大型建筑/桥梁/冷却塔爆破拆除合集
0.85
tt_…2767583D 科普:加农炮轰中世纪城墙
0.82
tt_…753951迈阿密水滨高楼爆破内爆 第二轮
0.82
tt_…590670球形水塔切基坍塌(结构破坏但非炮击)第二轮
0.45
tt_…770911烟囱破拆("chimney has left the chat")
0.45
tt_…325279PCL-181 车载炮实弹射击(只开炮,无建筑)
0.35
tt_…663764弱相关军事画面
0.35
tt_…556566弱相关军事画面
0.35
tt_…421982城市火灾(无炮击)第二轮
0.30
tt_…600223擦边内容
0.20
tt_…804886基本无关
0.10
为什么标题筛过了还会被打低分?
因为标题会骗人。tt_…325279 的文案满是火炮词,元数据筛稳过 —— 但 Gemini 看完发现全程只开炮、一栋楼都没炸,正好踩中 brief 的排除项,0.35 淘汰。这就是"必须真看片"的价值。
06 · 补搜与交付

第一轮不够,就换方向再来一轮,最后凑 6 条回传

第一轮 8 条看完只有 2 条强匹配(0.85 / 0.82),不够交付。agent 于是带着教训回到搜索:第一轮输的多是"军事火炮"词,赢的是"爆破拆除"—— 第二轮就全押 demolition 方向,4 条入围又拿下 2 条强匹配(0.90 / 0.82)。

tt_…879071 · @stadiumdb0.90 强匹配
德克萨斯球场被定时炸药实弹爆破:炸药沿球场圆周连续炸裂,白色屋顶失去支撑整体塌陷,建筑瞬间被粉尘云吞没。
cdn-asia.funplus-marketing.ai/ultra/tt_7641715811173879071.mp4
tt_…594591 · @varde.cb0.85 强匹配
大型建筑物拆除爆破集锦:筒仓倾倒、烟囱倒地、大桥爆破坠谷、冷却塔连环爆破、摩天大楼整体坍塌。
cdn-asia.funplus-marketing.ai/ultra/tt_7600297867034594591.mp4
tt_…753951 · @gisellefiumara0.82 强匹配
迈阿密水滨高楼真实爆破拆除:底部炸药连续引爆,整座建筑倾斜后垂直坍塌,激起巨大粉尘云。
cdn-asia.funplus-marketing.ai/ultra/tt_7627863016624753951.mp4
tt_…276758 · @ancient.techie0.82 强匹配
3D 动画科普:加农炮轰击中世纪城堡石墙炸开缺口,再演示复合墙与星形要塞如何弹开炮弹。
cdn-asia.funplus-marketing.ai/ultra/tt_7614966496464276758.mp4
tt_…590670 · @wattsupjustine0.45 补充
巨型球形水塔切基坍塌,储水罐砸地激起尘土 —— 补"结构坍塌"这一维度。
cdn-asia.funplus-marketing.ai/ultra/tt_7619820899503590670.mp4
tt_…325279 · @vexrmedia0.35 补充
PCL-181 车载加农炮 + PHL-11 火箭炮实弹射击 —— 补"火炮开火"这一维度。
cdn-asia.funplus-marketing.ai/ultra/tt_7647035789322325279.mp4

最终组装回调 payload,PATCH 到槽位的 preview 接口,Material Board 确认收货:

material-board/preview-payload.json → preview-response.json(实录)
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}
status: "paused" 是什么意思
这是预览研究任务:槽位建好后先试爬一批给用户过目,槽位停在 paused 等用户确认。用户点了确认,这个槽位才会进每日 cron(也就是 material_board_daily_crawl)按同样的方法天天跑。
附录 · 证据文件

以上每个数字都能在任务产物里对上

任务工作区把每一步的中间产物都落了盘。想复核任何一步,用 tb.sh cat bb03a52b-… <文件> 直接看原文:

.logs/s1–s11.json / t1–t6.json两轮 17 次 TikHub 搜索的原始响应(每个约 1.5MB) .logs/candidates.json / candidates2.json解析后的候选池:211 条 + 105 条 .logs/shortlist.json / shortlist2.json元数据初筛入围:8 条 + 4 条 .logs/known-keys-req/resp.jsonD1 指纹去重的请求与响应 .logs/pipeline.wf.js并行媒体管线的编排脚本(每候选一个子 agent) .logs/all-pipeline-results.json12 条候选的完整打分结果 .logs/final-selection.json最终 6 条的 caption、匹配分与 R2 链接 material-board/preview-payload.json回传给 Material Board 的完整 payload material-board/preview-response.jsonMaterial Board 的 200 收货回执