Files
minis-analyse-skill/skills/swads-minis-batch-launch/references/tool-sequence.md
T
blueming3andClaude Opus 5.5 9f9e70ea2d feat: minis launch, cold-start watch and delivery watch skills
Three TikTok Minis skills for Codex and Claude Code, with the cold-start
playbook and five benchmark accounts through 88776-1 (2026-09-30).
install.sh links them into ~/.codex/skills and ~/.claude/skills.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 15:10:54 +08:00

228 lines
14 KiB
Markdown

# 工具调用序列(swads MCP)
令牌在管理台 `/operations/mcp-tokens` 按业务包授权:「基础」选「可操作」(上传、审核素材)、
「短剧 Minis 投放」选「可操作」(策源、建计划、推送、开停投、改预算)。要删 TikTok 上的计划,再勾
「允许删除 TikTok 上的计划(不可恢复)」;没勾时工具列表里看不到 `campaign_delete`,硬调返回 `capability_denied`。
`swads_whoami` 能看到自己有什么。
下面 `A` = 账户 `external_account_id`,`T` = `tenant-global-001`(线上会话用清单的 `account.tenant_id`)。
## 0. 账户与上下文
```
intake_list_accounts() # 运营选账户用
intake_upload_context(external_account_id=A, minis_id=<MINIS_ID>)
→ upload{tenant_id, game_id, market, language, ratio}
→ minis[] identities[]
→ product_app_scope{…} # 每个文件上传都带它
```
`minis_id` 从 `minis[]` 里的 `external_id` 取(WISY `mn06s5c3fwlgkdsw`、WISH `mnmq9weqvpsmx81w`、SWAVE `mnkfrtn8rwalt5qy`、
VEYA `mnkttq8epikrbri9`);`asset_inventory_id` 每个户不一样,别从别的户抄。身份从 `identities[]` 取。
## 1. 上传一个文件(begin → PUT → finish → 轮询)
```
creative_upload_from_url(
tenant_id=T, platforms=["tiktok"], platform="tiktok",
media_kind="video"|"image",
file_name=<name>, content_type=<mime>, size_bytes=<n>,
content_sha256=<sha>, # 视频带;轮播不带
game_id=upload.game_id, market=upload.market, language=upload.language,
ratio="9:16", prompt="Material center ingest: <name>",
external_account_id=A, # 不带会落进未归属池
product_app_scope=<scope>,
review_status="approved") # 运营自己剪的素材;策源只挂已审核的
→ {upload_id, object_key, upload_url, method, headers, reused, reused_file?}
```
- `reused: true`:库里已有这个素材,`reused_file.creative_factory_asset_id` 就是 asset id,跳过后两步。
- `upload_url` 为空且 `reused: false`:字节已在存储里(上次没完成的上传,或别的账户传过),**不用 PUT**,直接 finish。
- 否则把结果存成 JSON 文件,`scripts/upload_local_asset.sh <file> <json>` 做 PUT(脚本会自己识别上面两种情况),然后:
```
creative_upload_from_url(…同 begin 的所有字段…, upload_id=…, object_key=…, external_account_id=A, product_app_scope=<scope>, review_status="approved")
creative_import_status(upload_id=…) # 轮询到 status=ready → creative_factory_asset_id
```
入库是异步的:图片几十秒,视频要跑转码和多模态分析,**几分钟是正常的**。每 15 秒问一次,
20 分钟没 ready 才算失败。`finalizing` 期间什么都不用做——不要去读 creative-factory 的代码猜它在干什么。
finish 返回 409 且含 `not in storage`:存储里那个对象是半截的。**不带 `content_sha256` 再 begin 一次**拿全新票据,PUT,再 finish。
上传步骤失败就报原文给运营;不要去读代码、查端口、翻 runbook —— 那些不在这条链上。
## 2. 策源
```
intake_start_run(
external_account_id=A,
prompt=<brief>, # references/brief-templates.md
asset_ids=[…], # 轮播按套顺序
promoted_object={
"object_type": "minis", "external_id": <MINIS_ID>, "name": <MINIS_NAME>,
"subtype": "MINI_SERIES",
"region_codes": ["AU","BR","KR","CA","MX","JP","TH","NZ","ID"],
"landing_page_url": <剧集页>
},
locale="zh")
→ run_id
intake_run_status(run_id, external_account_id=A, wait_seconds=120) # completed / failed / needs_clarification;没结束就再调
# 不带账户回 403 external_account_id_required
intake_answer_clarification(run_id, request_id, answers={key: recommended_value})
```
线上会话里剧集页链接来自清单;本地会话开工前自己按剧名查清单,只问 `missing` 里的剧:
```
minis_episode_link_list(external_account_id=A, minis_id=<MINIS_ID>, titles=[<剧名>, …])
→ {found: [{title, landing_page_url}], missing: [<剧名>]}
```
运营在会话里补的链接先记进小程序的「剧→剧集页」清单再用(下一批自动查到):
```
minis_episode_link_set(external_account_id=A, minis_id=<MINIS_ID>, title=<剧名>, landing_page_url=<剧集页>)
```
线上会话开投放只看清单的 `authorization.enable`(见 `online.md`);`campaign_set_delivery` 在第 5 节。
`completed` 的 `result.campaign_id` / `result.delivery_spec_id` 是后面的入口。
失败原文在 `failure`;`agent_output_invalid` / `repair budget` 这类可以原样再发起一次。
## 2a. 图文配乐搜索(线上会话)
```
launch_carousel_music_search(keyword=<英文关键词>, external_account_id=A, tenant_id=T)
→ { tracks: [ { music_id, material_id, file_name, author, duration, style, sources, cover_url, url, liked, signature }, … ] }
```
只读(`campaign:read`),不消耗策源写权限。一部图文剧最多调 3 次,每次一个新关键词。`file_name` 是曲名
(`name` 恒为空,别读它);`duration` 是秒数;`style` 是粗粒度风格标签,常常是空的;TikTok 没给的项就是空——
别编。挑曲、贴清单、等确认的规则见 `references/online.md`「怎么决定一行做什么」。选中的 id 用下面第 3 节的
`campaign_workspace_patch(…fields={"music_id": …})` 逐条广告写。
## 3. 验形状与补丁
```
campaign_workspace(campaign_id)
→ campaign_name, current_revision.delivery_spec_id, current_revision.campaign_budget_optimization_enabled,
groups[0].group_id, groups[0].ads[].{ad_id, creative_variants[]}
campaign_delete_draft(campaign_id) # 形状不对时(只删没推过的草稿)
campaign_workspace_patch(campaign_id, delivery_spec_id, fields={"campaign_name": "SW-ADS-<产品>-<剧名>-<YYYYMMDD>"})
# 纯图文计划在日期前多插一节 图文:"SW-ADS-<产品>-<剧名>-图文-<YYYYMMDD>"
campaign_workspace_patch(campaign_id, delivery_spec_id, fields={"campaign_budget_optimization_enabled": true})
campaign_workspace_patch(campaign_id, delivery_spec_id, group_id=G, fields={"daily_budget": 0, "group_budget": 0})
campaign_workspace_patch(campaign_id, delivery_spec_id, group_id=G, ad_id=AD, fields={"music_id": "<id>"})
```
每次返回 `delivery_spec_id` 都是新版本,链式传下去。
**删已推送的计划**(只在运营明确要删时;要 `campaign:delete`):
```
campaign_delete(campaign_id, external_account_id=A) # 本地 + TikTok 一起删,广告组和广告一起没,不可恢复
→ {"campaign_id": …, "deleted": true}
campaign_detail(campaign_id, external_account_id=A) # 必须 404 campaign not found,再删下一条
```
一次一条。504 超时不要直接重试:先 `campaign_detail`,还在再删。全删完 `account_sync` 一次。
2026-09-27 在 <external_account_id> 删 5 条暂停计划:每条几秒返回,`campaign_detail` 都 404,
TikTok `/campaign/get/` 读回 5 条 `CAMPAIGN_STATUS_DELETE`,同户在投的 5 条不受影响。
## 4. 推送
```
campaign_push_preview(campaign_id, delivery_spec_id, external_account_id=A) # ready_to_push, gates
campaign_push(campaign_id, delivery_spec_id, external_account_id=A)
campaign_detail(campaign_id, external_account_id=A, wait_seconds=120) # execution_runs 按 created_at 倒序取第一条
→ status ∈ succeeded | failed ;external_reference = TikTok campaign id
```
`external_account_id` 在这三步都不能省:账户只按每次调用的参数定,推送
不带账户会 409 `campaign revision is bound to a different platform account`。
## 5. 同步与启用
```
account_sync(external_account_id=A) → sync_run_id
account_sync_status(sync_run_id, wait_seconds=120) # 到 completed*;推送全部结束后才同步
campaign_set_delivery(campaign_id, status="active", external_account_id=A) # 三层 ENABLE;失败隔 20 秒重试,最多 4 次
```
## 6. 开投以后
```
campaign_set_budget(campaign_id, daily_budget=<USD>, external_account_id=A) # 改日预算,直接写 TikTok,不用再同步
campaign_set_ad_delivery(campaign_id, external_ad_id=<TikTok ad_id>, status="stopped"|"active", external_account_id=A)
metrics_catalog(external_account_id=A) # 挑跑量素材前先看指标名
metrics_semantic_query(query={…}, external_account_id=A) # 按广告取花费/展示/点击/变现收入
```
两个写操作都要求这条计划已经被账户同步读回过,否则 404/409。
## 追加模式(运营明说「追加」时才用,默认是新建)
上传素材与新建相同(第 1、2 步)。之后不策源,直接在已推送计划上加广告:
```
campaign_list(external_account_id=A, search=<剧名>) # 找到 campaign_id,确认 external_campaign_id 非空
campaign_workspace(campaign_id, external_account_id=A) # 取当前 delivery_spec_id、group_id、一条现有 ad_id 作模板
campaign_workspace_add_ad(
campaign_id, delivery_spec_id, group_id,
ad_name="SW-ADS-<产品>-<剧名> Ad <N>",
clone_from_ad_id=<模板广告 ad_id>, # 文案/落地页/CTA/身份照抄
asset_ids=[...], # 新素材,≤15,全视频或全图
external_account_id=A)
→ ad_id, delivery_spec_id(新版本)
campaign_push_preview(campaign_id, delivery_spec_id, external_account_id=A) # 看 counts.add.ads == 1
campaign_push(campaign_id, delivery_spec_id, external_account_id=A, scope="additions")
campaign_detail → execution_runs[0].status = succeeded
account_sync(A) → account_sync_status 到 completed
campaign_set_delivery(campaign_id, status="active", external_account_id=A) # 级联 ENABLE,新广告才开始投
```
**混搭时追加视频广告**(`SKILL.md`「同组混搭」):模板取最后一条图文广告,加完**立刻清掉复制来的配乐**再推:
```
campaign_workspace_add_ad(…, ad_name="SW-ADS-<产品>-<剧名> Video 1", clone_from_ad_id=<最后一条图文广告>, asset_ids=[<视频>])
campaign_workspace_patch(campaign_id, delivery_spec_id, group_id=G, ad_id=<新 ad_id>, fields={"music_id": ""})
campaign_push_preview(…) # counts.add.ads == 1
campaign_push(…, scope="additions")
```
追加**必须** `scope="additions"`:同步过的计划,工作台草稿里已有平台写回的值(状态、预算模式,
Smart+ CBO 计划的日预算甚至会读回成 0),预览会列出 campaign/group/ad 的 update 行,`ready_to_push`
也会因 `campaign_remote_drift_pending` 是 false——这些都不是追加要管的。additions 只推那条 add,
不碰已有实体,也不等差异处理。预览里只核对 `counts.add.ads` 等于你追加的条数。
模板广告按 `campaign_workspace` 里广告的 `platform_status` 选(Smart+ 是 TikTok 的素材状态 `active`、`stopped`、
`reviewing`、`blocked`):有 `active` 的取一条;没有就取已在 TikTok 上、不是 `blocked` 的最后一条(常见于目标
计划本身还是暂停状态),核对表写明模板是回退取的;全是 `blocked` 就不追加——文案照抄过来大概率再被拒。超过 15 个素材就分成两条广告,`Ad <N>` 递增。核对表按广告列:新广告的 TikTok id、
素材数、状态。
**线上会话在追加模式这里(第 5 步为止)不调 `campaign_set_delivery`**(`references/online.md`):加完广告、
推送成功就停。是否开投放看清单的 `authorization.enable`——`false` 时线上会话从头到尾都没有这个工具,开投放是
操作员在「广告活动」页自己做的事;`true` 时白名单才多这一项,而且只在「全部做完」的核对之后、对**这一轮刚建成的**
每条计划(含这里追加的目标计划)各调一次,不是在追加模式这里单独调,也不重开前几轮已经开过的。
## 常见报错 → 含义
| 报错 | 含义 / 处理 |
|---|---|
| `authorization failed`(策源) | 素材没带 `product_app_scope`,重新上传 |
| `Creative asset is not approved`(策源) | 上传时没传 `review_status="approved"`;用 `creative_review_asset(decision="approved")` 逐个补审,或重传 |
| 策源返回带 `content_warnings`(`flagged by the content check` 或 `could not complete`) | 只提示不拦截,策源已发起;处理见 `SKILL.md` 第 3 步 |
| 素材的图被打了码 | 画面里有别人的头像/昵称,系统自动打码后投新版本;设计行为,核对表提一句 |
| `creative_asset_not_approved`(追加) | `asset_ids` 里有没过审的素材;上传时 `review_status="approved"`,或 `creative_review_asset` 补审 |
| `campaign revision is bound to a different platform account` | 推送/详情没带 `external_account_id`;补上重调,不用重建 |
| `remote_identity_conflict`(同步后工作台阻塞) | Smart+ 广告有 `ad_id` 与 `smart_plus_ad_id` 两个 id;新建模式不受影响,写进报告即可。追加模式若因它 409,停下来写进核对表——是产品缺陷,不是重试能解决的 |
| `missing requested region BR` | 平台国家表偶发残缺被缓存;告诉运营重启 sidecar 或稍后重试 |
| `Unsupported image size. Please upload another image.`(推送,视频) | 视频尺寸不是标准 9:16,TikTok 取封面失败;照 `SKILL.md`「视频尺寸」把这几条放大裁成 1080×1920 重新上传。追加模式里失败的那条新增广告要先从工作台删掉再重加 |
| `asset_ids must be unique` | 轮播复用帧按字节复用了;去掉 `content_sha256` 重传那部剧 |
| `campaign_remote_drift_pending`(推送) | 工作台有未处理的平台差异;先 `campaign_workspace` 看,一般是运营在后台改过 |
| `not linked to a TikTok remote campaign`(启用) | 没同步;先 `account_sync` |
| `Allowlist not enabled.`(广告组) | 平台侧瞬时闸门;同一载荷稍后重推 |
| `campaign_already_on_platform`(删除) | `campaign_delete_draft` 只删草稿;运营要删推过的计划就改用 `campaign_delete` |
| `capability_denied` / `required_capability: campaign:delete` | 令牌没勾「计划·删除」;请运营在 `/operations/mcp-tokens` 勾上后重试,别换别的路子删 |
| `Campaign has been deleted`(`campaign_delete`) | 这条早在 TikTok 后台删了,只剩本地记录;`account_sync` 后工作台会出现待处理的平台删除,报给运营在控制台接受 |