fix(skills): skills 随后端打进镜像,根治线上提取「未返回有效 JSON」(系统提示词为空)

真因(查 dev 库 + 重放真实失败请求 + 真调 ARK 定位):线上提取的 system 提示词长度=0。
core-api 镜像由 `./core/backend` 构建,而 skills/ 在仓库根、不在构建上下文 → 镜像里没有
→ _load_skill_system_prompt 返回空串 → 模型收不到「只输出 JSON」铁律 → 吐 markdown/散文
→ 正则抽不到 {} → 「提取结果解析失败(模型未返回有效 JSON)」。本地有 skills 故一直没复现。
(脚本生成同样受影响,只是它有退化兜底提示词,质量打折但不报错。)

验证:用 dev 库里你那条失败请求的真实脚本重放——空 system→无 JSON;补上 skill 提示词
→ 立即出 JSON(角色/场景齐、商品不提)。

改动:
- skills/ → core/backend/skills/(git mv,进构建上下文,Dockerfile `COPY . .` 自动打包)。
- services._skills_root()/script_agent._skill_dir():优先 BASE_DIR/skills,回落仓库根(双兜底)。
- worker 失败时也落 response_payload(含 content / reasoning 预览 / system_chars),
  以后再坏一眼就能定位(本次正是因为失败没存返回,白绕一圈)。
- 回归测试 2 条:skills 必须在 BASE_DIR 内(随镜像走)+ 提取提示词非空且含 JSON 铁律。
- CLAUDE.md 标注 skills 必须放 core/backend 内的原因。

验收:完整 Django 套件 151/151 全绿。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
seaislee1209
2026-06-23 00:12:46 +08:00
co-authored by Claude Opus 4.8
parent c8cb6d3d51
commit 8dab8b4921
12 changed files with 62 additions and 7 deletions
@@ -0,0 +1,86 @@
# 电商带货脚本 · 核心方法论
> 每次生成都读。本文件是脚本的"骨架 + 红线",钩子句式见 `hook-library.md`。
---
## 一、黄金结构:钩子 → 痛点 → 卖点 → CTA
带货短视频的灵魂是**前 3 秒留人 + 结尾促转化**。四个功能镜各司其职:
| role | 功能 | 它要做的事 | 旁白要点 |
| ---- | ---- | ---- | ---- |
| **钩子** | 3 秒留人 | 第一句话/第一帧就让人停下不划走 | 用 `hook-library.md` 的公式,制造痛点共鸣或反差冲击 |
| **痛点** | 共鸣 | 把用户"对,我就这样"的处境讲出来 | 具体场景化,不抽象,让人对号入座 |
| **卖点** | 商品解决 | 商品如何**恰好**解决上面的痛点 | 卖点落到痛点上,给"凭什么信"的理由(成分/效果/对比/口碑) |
| **CTA** | 转化行动 | 临门一脚,告诉用户现在做什么 | 明确动作 + 紧迫感(点下方小黄车 / 主页领券 / 限时) |
**铁原则:**
- **首镜永远是钩子**——3 秒内必须抛出钩子,否则用户划走。
- **末镜永远收 CTA**——可以是独立 CTA 镜,也可以是末镜旁白末尾收一句行动指令。
- **卖点必须挂在痛点上**——不是罗列参数,是"这个痛点 → 商品这样解决"。
---
## 二、档位 × 黄金结构映射(关键)
时长由输入给定(15/30/60/90 四档),每 15 秒一镜,`segment_count = total_duration / 15`
镜数变了,四个功能要**压缩或扩展**,按下表分配 `role`
| 档位 | 镜数 | role 序列(index 0→N | 压缩/扩展说明 |
| ---- | ---- | ---- | ---- |
| **15s** | 1 | `[钩子]` | 一镜到底:开头 3 秒钩子 → 中段一句卖点 → 末尾一句 CTA,全压进这 15 秒旁白里 |
| **30s** | 2 | `[钩子, 卖点]` | 镜0 钩子里带出痛点;镜1 讲卖点并在旁白末尾收一句 CTA |
| **60s** | 4 | `[钩子, 痛点, 卖点, CTA]` | 标准黄金结构,一镜一功能 |
| **90s** | 6 | `[钩子, 痛点, 卖点, 卖点, 卖点, CTA]` | 痛点后给 3 个卖点镜:核心卖点 / 场景化演示 / 信任背书(口碑·数据);末镜 CTA |
说明:
- `role` 字段只能取 `钩子|痛点|卖点|CTA` 四个枚举值。"信任背书""场景演示"等都归入 `卖点`
- 短档位(15/30s)靠**旁白内压缩多功能**达成"留人+转化",不要因为镜少就丢掉 CTA。
- 90s 的 3 个卖点镜要**各有侧重、不重复**(核心功效 / 真实使用场景 / 别人为什么买)。
---
## 三、entity 一致性原则
`entities` 是全脚本共享的角色 / 场景 / 商品池,保证**多镜同一角色是同一张脸、同一商品是同一个包装**。
- **一个角色/场景/商品 = 一个 entity = 一份 `visual_prompt`**。多镜复用同一 id,**绝不为同一对象写两份 visual_prompt**(会导致下游生图人脸/包装漂移)。
- `visual_prompt` 由你**自动生成**(小白不打字):写清外观特征(角色:性别/年龄段/发型/穿着/气质;场景:地点/光线/风格;商品:品类/包装/颜色/摆放),用于直接喂图模型。
- `type` 三选一:`character`(人) / `scene`(环境) / `product`(商品)。一份脚本通常至少 1 个 `product`、**至少 1 个 `scene`**。
- **场景与镜头一一对应(可复用)**:**每个 segment 的 `entity_refs` 必须恰好引用一个 `scene`**——它就是这一镜画面所处的环境。多镜同环境就复用同一个 scene id(如全程宿舍 → 只一个「宿舍书桌」scene,4 镜都引用它);真正换了地点才新建另一个 scene。纯产品特写镜也要绑它所处环境的 scene(无明确环境则复用主场景)。这样下游「场景」基础资产能成图、且同环境背景一致。
- `ref_index` 是该 entity 在图集里的参考序号(从 1 递增,供下游三视图/参考图对齐)。
- `voice_ref`(二期,可选):角色音色参考,用于锁音色;无则 `null`
- **每个声明的 entity 至少被一个 segment 的 `entity_refs` 引用**,不留孤儿。
- `segments[].speaker` 指向某 `character` 的 id 表示该镜由角色开口说;`null` = 画外旁白。
---
## 四、商品露出规范(`product_exposure`
每镜都要规划商品怎么自然出现,避免生硬插入。按 role 给推荐露出方式:
| role | 推荐露出 | 说明 |
| ---- | ---- | ---- |
| 钩子 | 可弱露出或不露 | 钩子先抓人,商品可作悬念,第 1 镜末尾闪一下也行 |
| 痛点 | 不露 / 反面对照 | 展示"没有它"的糟糕状态,商品先按住 |
| 卖点 | **特写 / 使用中** | 商品成为主角:成分特写、质地特写、上脸/上手使用中 |
| CTA | 手持 / 包装正面 | 手持商品对镜头,包装正面清晰,配合行动指令 |
露出方式词汇统一用:`手持` / `特写` / `使用中` / `包装正面` / `场景摆放` / `对比展示`(可组合,如"手持+特写")。
---
## 五、旁白红线(硬规则,违反即不合格)
- **口语化**:像真人对着镜头唠嗑,不准书面腔 / 不准 AI 腔("综上所述""不仅…而且""值得一提的是"全禁)。
- **每镜 `narration` ≤ 55 字**Seedance 在 15 秒内直接发声,55 字是可懂语速**硬上限**。
**写作目标 ≤ 50 字**,留 5 字缓冲——宁可短、不要卡满;短档位(15/30s)一镜要装多功能时尤其要狠删,先保 CTA 不被砍。
- **不浮夸、不空喊**:卖点要给具体理由,不堆形容词。
- **违规词禁令**(电商广告法红线,一律不写):
- **医疗功效类**:治疗 / 根治 / 疗效 / 抗癌 / 消炎 / 杀菌(无证) / 排毒 / 速效 / 抑制 ××病 …
- **绝对化用语**:最 / 第一 / 顶级 / 唯一 / 100% / 国家级 / 世界级 / 永久 / 绝对 / 史上 …
- **虚假承诺**:包治 / 三天见效 / 立刻变白 / 永不反弹 …
- 替换策略:用"帮助""更""不少人反馈""上脸清爽"等柔性、主观化表达替代。
> 自检阶段必须逐镜扫一遍违规词,命中即改写。详见 `checklist.md`。