From b4db3d8d6c46dfdf8107374c1e4df35c5049afd5 Mon Sep 17 00:00:00 2001 From: codes-factory-of-bg Date: Sun, 30 Aug 2026 19:57:16 +0800 Subject: [PATCH 01/34] =?UTF-8?q?fix(docker):=20daemon.env=20PATH=20?= =?UTF-8?q?=E8=A1=A5=20wrapper=20bin=EF=BC=8C=E8=A6=86=E7=9B=96=E6=A8=A1?= =?UTF-8?q?=E6=9D=BF=E4=B8=8E=E8=BF=90=E8=A1=8C=E6=97=B6=E6=8C=81=E4=B9=85?= =?UTF-8?q?=E5=8D=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Docker 模板原把 PATH 行注释掉,且 setup-crew.sh 的 PATH 注入在 'no PATH line' 时会直接 skip,导致容器 gateway 继承镜像默认 PATH, 缺 ~/.openclaw/bin,agent exec 调裸技能名找不到 wrapper 软链。 - config/daemon.env.template: 启用 PATH 行并前置 /home/wukong/.openclaw/bin - docker-entrypoint.sh: source 完 daemon.env 后幂等保证 wrapper bin 在 PATH 最前,兼容已有持久化 volume 的 daemon.env --- config/daemon.env.template | 7 ++++--- docker/docker-entrypoint.sh | 10 ++++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/config/daemon.env.template b/config/daemon.env.template index 34744563..fb192c1c 100644 --- a/config/daemon.env.template +++ b/config/daemon.env.template @@ -15,6 +15,7 @@ OPENCLAW_DISABLE_BONJOUR=true # camoufox 有头登录走这个 display。用户不用改。 # DISPLAY=:99 -# ── PATH 注入(裸机由 install.sh 写,Docker 由 base image 已有)───────────── -# Docker 不需要此行(node:24-bookworm 自带 PATH);裸机 install.sh 会追加 node 路径 -# PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin +# ── PATH 注入(Docker entrypoint 保证 wrapper bin 在 PATH 最前)─────────── +# gateway 不 source shell rc,agent exec 调裸技能名(如 aigc-video-gen)靠此 PATH +# 解析 ~/.openclaw/bin 下的 wrapper 软链。此文件被 entrypoint source,$HOME 会展开。 +PATH=$HOME/.openclaw/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin diff --git a/docker/docker-entrypoint.sh b/docker/docker-entrypoint.sh index 9795461c..8f4069fa 100755 --- a/docker/docker-entrypoint.sh +++ b/docker/docker-entrypoint.sh @@ -73,6 +73,16 @@ load_runtime_environment() { # DISPLAY=:0 被抄进来),会覆盖入口脚本上方 export 的 Xvfb 显示号(:99), # 有头浏览器开去不存在的显示、noVNC 里什么都看不到。这里强制恢复入口设定。 export DISPLAY=":${DISPLAY_NUM}" + + # 保证 skill wrapper 软链目录在 PATH 最前。gateway 不 source shell rc,agent exec + # 调裸技能名(如 aigc-video-gen)靠 PATH 解析 ~/.openclaw/bin 下的 wrapper 软链。 + # 幂等:已在 PATH 中则跳过;缺失则前置,避免 daemon.env 模板或持久卷里漏写。 + if [ -d "$OPENCLAW_HOME/bin" ]; then + case ":${PATH:-}:" in + *":$OPENCLAW_HOME/bin:"*) ;; + *) export PATH="$OPENCLAW_HOME/bin:$PATH" ;; + esac + fi } # ─── 3. 首启生成 gateway token ───────────────────────────────────── From f9aeec6194d0c35ff33624b466e6869e49c4145e Mon Sep 17 00:00:00 2001 From: codes-factory-of-bg Date: Tue, 1 Sep 2026 18:15:07 +0800 Subject: [PATCH 02/34] =?UTF-8?q?fix(expert-wx-mp):=20=E4=B8=BB=E9=A2=98?= =?UTF-8?q?=E6=B3=A8=E5=86=8C=E8=A1=A8=E8=B7=AF=E5=BE=84=E7=BB=9F=E4=B8=80?= =?UTF-8?q?=E5=88=B0=20wx=5Fmp/wenyan-theme/?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 发布端从 wx_mp/wenyan-theme/index.json 读自定义主题,但生成端 SKILL 注册规则 与 editing/content-production workflow 仍指向旧 wenyan-theme/,软链部署下写读 错位会重复触发发布端读不到登记主题的故障。补齐 generate-wenyan-theme SKILL、 两个 workflow、collect-theme-sources usage 文案到新路径。 - collect-theme-sources.js: wx-mp-hunter 解析改 PATH 优先 + 软链探测兜底 - publish_wx_mp.py: _detect_crew_workspace 兼容直接/软链部署,旧 css 写法兼容 --- .../tools/generate-wenyan-theme/SKILL.md | 4 +- .../scripts/collect-theme-sources.js | 72 ++++++++++++++--- .../tools/wx-mp-publisher/SKILL.md | 6 +- .../wx-mp-publisher/scripts/publish_wx_mp.py | 77 +++++++++++++++---- .../workflows/content-production.md | 6 +- .../skills/expert-wx-mp/workflows/editing.md | 6 +- 6 files changed, 134 insertions(+), 37 deletions(-) diff --git a/crews/main/skills/expert-wx-mp/tools/generate-wenyan-theme/SKILL.md b/crews/main/skills/expert-wx-mp/tools/generate-wenyan-theme/SKILL.md index 561fae63..6bd0cc10 100644 --- a/crews/main/skills/expert-wx-mp/tools/generate-wenyan-theme/SKILL.md +++ b/crews/main/skills/expert-wx-mp/tools/generate-wenyan-theme/SKILL.md @@ -18,7 +18,7 @@ description: 从自然语言描述或对标公众号文章生成自定义排版 - **自然语言转 CSS**:理解视觉需求(如"赛博朋克风"、"深色代码块"、"带装饰的引用块"),转换为精确的 CSS 代码 - **微信文章仿样式生成**:当用户提供 `https://mp.weixin.qq.com` 文章链接时,调用 `wx-mp-hunter fetch --html` 获取正文 HTML,分析原文排版特征后生成 wenyan CSS - **公众号近期文章归纳生成**:当用户提供公众号账号时,调用 `wx-mp-hunter search` + `account-posts` + `fetch --html` 采集近期文章 HTML,抽取共性后生成模板 -- **主题注册到 index.json**:生成自定义 CSS 后,把主题信息写入工作区 `wenyan-theme/index.json`,`wx-mp-publisher` 会从这里读自定义主题 +- **主题注册到 index.json**:生成自定义 CSS 后,把主题信息写入工作区 `wx_mp/wenyan-theme/index.json`,`wx-mp-publisher` 会从这里读自定义主题 - **微信排版规范适配**:严格遵循 `#wenyan` 命名空间约束,确保样式能完美注入微信公众号 DOM 结构 - **高级排版特效**:支持伪元素 (`::before`/`::after`)、渐变背景 (`linear-gradient`)、内联 SVG/Base64 图片等高级 CSS 特性 @@ -312,7 +312,7 @@ generate-wenyan-theme --account <公众号名> --keywords "关键词1,关键词2 每次成功生成自定义 CSS 后,必须更新工作区主题注册表: ```text -wenyan-theme/index.json +wx_mp/wenyan-theme/index.json ``` 结构固定为: diff --git a/crews/main/skills/expert-wx-mp/tools/generate-wenyan-theme/scripts/collect-theme-sources.js b/crews/main/skills/expert-wx-mp/tools/generate-wenyan-theme/scripts/collect-theme-sources.js index b8887e2f..6387755b 100644 --- a/crews/main/skills/expert-wx-mp/tools/generate-wenyan-theme/scripts/collect-theme-sources.js +++ b/crews/main/skills/expert-wx-mp/tools/generate-wenyan-theme/scripts/collect-theme-sources.js @@ -4,9 +4,10 @@ */ import { spawn } from "node:child_process"; -import { constants } from "node:fs"; +import { accessSync, constants, statSync } from "node:fs"; import { access, mkdir, open, stat } from "node:fs/promises"; import { basename, dirname, join, resolve, sep } from "node:path"; +import { homedir } from "node:os"; import { fileURLToPath } from "node:url"; const DEFAULT_OUTPUT = "wenyan-theme-sources.json"; @@ -35,11 +36,27 @@ function usage() { " --max-scan N 关键词筛选最多扫描文章数,默认 100", "", "Output:", - " --output 工作区内相对 .json 路径(可含子目录,如 wenyan-theme/sources.json);禁止绝对路径 / .. 上跳。", + " --output 工作区内相对 .json 路径(可含子目录,如 wx_mp/wenyan-theme/sources.json);禁止绝对路径 / .. 上跳。", ].join("\n") + "\n" ); } +function findExecutableOnPathSync(name) { + for (const dir of (process.env.PATH ?? "").split(":")) { + if (!dir) continue; + const candidate = join(dir, name); + try { + const info = statSync(candidate); + if (!info.isFile()) continue; + accessSync(candidate, constants.X_OK); + return candidate; + } catch { + // Not here; keep scanning. + } + } + return null; +} + function readFlag(args, flag) { const idx = args.indexOf(flag); if (idx < 0 || idx + 1 >= args.length) return null; @@ -62,13 +79,42 @@ function parseKeywords(raw) { .filter(Boolean); } -function defaultWxHunterPath() { - const currentFile = fileURLToPath(import.meta.url); - // Script lives at: crews/main/skills/generate-wenyan-theme/scripts/ - // wx-mp-hunter is a sibling skill under crews/main/skills/, so go up 2 - // levels to reach crews/main/skills/, then into the wx-mp-hunter tree. - const skillsRoot = resolve(dirname(currentFile), "../.."); - return join(skillsRoot, "wx-mp-hunter", "scripts", "wx-mp-hunter.sh"); +// wx-mp-hunter wrapper resolution (D21 convention): +// 1. Prefer the PATH-exposed wrapper (~/.openclaw/bin/wx-mp-hunter, equivalent to +// `command -v wx-mp-hunter`), so callers never depend on where this tool lives. +// 2. Fall back to probing sibling skills: walk up from this script's directory +// looking for a wx-mp-hunter skill dir (top-level .sh wrapper per D21, +// legacy scripts/wx-mp-hunter.sh as a compat candidate). +function candidateWxHunterPaths() { + const candidates = []; + let dir = dirname(fileURLToPath(import.meta.url)); + while (true) { + // D21 layout: /wx-mp-hunter/wx-mp-hunter.sh + candidates.push(join(dir, "wx-mp-hunter", "wx-mp-hunter.sh")); + // Legacy pre-D21 layout: /wx-mp-hunter/scripts/wx-mp-hunter.sh + candidates.push(join(dir, "wx-mp-hunter", "scripts", "wx-mp-hunter.sh")); + const parent = dirname(dir); + if (parent === dir) break; + dir = parent; + } + // Belt-and-braces: D21 exposes wrappers at ~/.openclaw/bin even if PATH lacks it. + candidates.push(join(homedir(), ".openclaw", "bin", "wx-mp-hunter")); + return candidates; +} + +async function resolveWxHunterPath() { + const onPath = findExecutableOnPathSync("wx-mp-hunter"); + if (onPath) return { path: onPath, via: "PATH" }; + for (const candidate of candidateWxHunterPaths()) { + const info = await stat(candidate).catch(() => null); + if (!info?.isFile()) continue; + const executable = await access(candidate, constants.X_OK).then( + () => true, + () => false + ); + if (executable) return { path: candidate, via: "probe" }; + } + return null; } function isWechatArticleUrl(value) { @@ -102,7 +148,6 @@ function parseArgs() { scanBatch: Math.min(readNumberFlag(args, "--scan-batch", DEFAULT_SCAN_BATCH), DEFAULT_SCAN_BATCH), maxScan: readNumberFlag(args, "--max-scan", DEFAULT_MAX_SCAN), output: readFlag(args, "--output") ?? DEFAULT_OUTPUT, - wxHunter: defaultWxHunterPath(), }; } @@ -309,6 +354,13 @@ async function collectByAccount(options) { async function main() { const options = parseArgs(); + const wxHunter = await resolveWxHunterPath(); + if (!wxHunter) { + fail( + `找不到 wx-mp-hunter wrapper(PATH 及候选路径均未命中;已探测:\n${candidateWxHunterPaths().map((item) => ` - ${item}`).join("\n")}` + ); + } + options.wxHunter = wxHunter.path; await assertExecutableFile(options.wxHunter, "wx-mp-hunter wrapper"); if (options.mode === "url") { diff --git a/crews/main/skills/expert-wx-mp/tools/wx-mp-publisher/SKILL.md b/crews/main/skills/expert-wx-mp/tools/wx-mp-publisher/SKILL.md index 4d2bf14f..159ee56e 100644 --- a/crews/main/skills/expert-wx-mp/tools/wx-mp-publisher/SKILL.md +++ b/crews/main/skills/expert-wx-mp/tools/wx-mp-publisher/SKILL.md @@ -39,14 +39,14 @@ wx-mp-publisher [theme] [--account ALIAS] - `theme`:渲染主题,三种形态: 1. **内置 id**(`pie` / `lapis` / `default` / …)——原样作为 `theme` 传给 relay 2. **本地 `.css` 文件路径**——脚本读出文件内容,作为 `custom_theme` 字段随 multipart 上传 relay - 3. **wenyan-theme/index.json 登记的自定义 id**--从工作区主题注册表解析出对应 CSS 路径,同 (2) + 3. **wx_mp/wenyan-theme/index.json 登记的自定义 id**--从工作区主题注册表解析出对应 CSS 路径,同 (2) 可选,缺省由 relay 默认渲染。 - `--account ALIAS`:多账号时指定目标公众号;缺省用 `accounts.json` 的 `default` > **自定义主题不持久化到 relay**:relay 是无状态多租户中转,**不存任何用户主题**。CSS 随请求上传,relay 写到 per-request 临时目录、用后即清理,天然按用户隔离。 > -> 主题注册表在 client 侧:**工作区 `wenyan-theme/index.json`**。generate-wenyan-theme 生成新主题后写入该文件,发布时从该文件读取。结构固定为 `version: 1` + `themes` 数组;每条记录包含 `id`、`name`、`css`、`source`、`createdAt`。 +> 主题注册表在 client 侧:**工作区 `wx_mp/wenyan-theme/index.json`**。generate-wenyan-theme 生成新主题后写入该文件,发布时从该文件读取。结构固定为 `version: 1` + `themes` 数组;每条记录包含 `id`、`name`、`css`、`source`、`createdAt`,其中 `css` 为相对工作区根的路径(如 `wx_mp/wenyan-theme/.css`)。 脚本自动: - 从 `accounts.json` 取目标账号凭据 @@ -57,7 +57,7 @@ wx-mp-publisher [theme] [--account ALIAS] ### 主题选择(未指定时) -> 自定义主题说明:`generate-wenyan-theme` 生成的用户自定义 CSS 登记在 `wenyan-theme/index.json`。若用户明确指定某个自定义主题,必须优先采用;未指定时才按内容在内置主题和已登记自定义主题中匹配。 +> 自定义主题说明:`generate-wenyan-theme` 生成的用户自定义 CSS 登记在 `wx_mp/wenyan-theme/index.json`。若用户明确指定某个自定义主题,必须优先采用;未指定时才按内容在内置主题和已登记自定义主题中匹配。 | 主题 ID | 风格描述 | 适用场景 | |---------|---------|---------| diff --git a/crews/main/skills/expert-wx-mp/tools/wx-mp-publisher/scripts/publish_wx_mp.py b/crews/main/skills/expert-wx-mp/tools/wx-mp-publisher/scripts/publish_wx_mp.py index 3d58b940..f928408e 100644 --- a/crews/main/skills/expert-wx-mp/tools/wx-mp-publisher/scripts/publish_wx_mp.py +++ b/crews/main/skills/expert-wx-mp/tools/wx-mp-publisher/scripts/publish_wx_mp.py @@ -30,8 +30,42 @@ TOOLS_DIR = PUBLISHER_DIR.parent EXPERT_PACK_DIR = TOOLS_DIR.parent SKILLS_DIR = EXPERT_PACK_DIR.parent -CREW_WORKSPACE = SKILLS_DIR.parent -THEME_ROOT = CREW_WORKSPACE / "wenyan-theme" + + +def _detect_crew_workspace() -> Path: + """定位真实运行工作区(wx_mp/ 等运行时数据的根目录)。 + + 技能有两种部署形态: + A. 直接部署:技能本体就在 ~/.openclaw/workspace-/skills/ 下, + SKILLS_DIR.parent 即工作区。 + B. 软链部署(D21):~/.openclaw/workspace-/skills/expert-wx-mp → + ~/wiseflow/crews//skills/expert-wx-mp。__file__ resolve() 后落在 + 代码仓,但主题注册表等运行时数据只在 ~/.openclaw/workspace-/wx_mp/ + 下,因此按 crews/ 映射回 ~/.openclaw/workspace-/。 + 兜底:向上探测含 wx_mp/wenyan-theme/ 或 db/published_track.db 的目录; + 都找不到时退回旧推导(SKILLS_DIR.parent),注册表缺失时仍走内置主题兜底。 + """ + if ( + SKILLS_DIR.parent.name.startswith("workspace-") + and SKILLS_DIR.parent.parent.name == ".openclaw" + and SKILLS_DIR.parent.is_dir() + ): + return SKILLS_DIR.parent + parts = SKILLS_DIR.parts + if "crews" in parts: + i = len(parts) - 1 - parts[::-1].index("crews") + if i + 1 < len(parts): + mapped = Path.home() / ".openclaw" / f"workspace-{parts[i + 1]}" + if mapped.is_dir(): + return mapped + for cand in (SKILLS_DIR, *SKILLS_DIR.parents): + if (cand / "wx_mp" / "wenyan-theme").is_dir() or (cand / "db" / "published_track.db").is_file(): + return cand + return SKILLS_DIR.parent + + +CREW_WORKSPACE = _detect_crew_workspace() +THEME_ROOT = CREW_WORKSPACE / "wx_mp" / "wenyan-theme" THEME_INDEX = THEME_ROOT / "index.json" DEFAULT_RELAY_BASE_URL = "https://relay.openclaw-for-business.com" ENDPOINT = "/api/v1/wx-mp/publish" @@ -105,10 +139,11 @@ def relay_env() -> tuple[str, str]: # ── 主题解析 ────────────────────────────────────────────────────────────────── def _resolve_registered_theme_path(theme_id: str) -> Path | None: - """从 wenyan-theme/index.json 查登记的主题 id,返回受控 CSS 路径或 None。 + """从 /wx_mp/wenyan-theme/index.json 查登记的主题 id,返回受控 CSS 路径或 None。 注册表结构: - {"version": 1, "themes": [{"id": "...", "css": "wenyan-theme/....css", ...}]} + {"version": 1, "themes": [{"id": "...", "css": "wx_mp/wenyan-theme/....css", ...}]} + css 为相对工作区根的路径;兼容旧的 "wenyan-theme/.css" 写法(落到主题目录下同名文件)。 """ if not THEME_INDEX.exists(): return None @@ -130,16 +165,26 @@ def _resolve_registered_theme_path(theme_id: str) -> Path | None: die(f"主题 {theme_id!r} 缺少 css 路径: {THEME_INDEX}") css_path = Path(css) if css_path.is_absolute() or css_path.suffix != ".css": - die(f"主题 {theme_id!r} 的 css 必须是 wenyan-theme/ 下的相对 .css 路径") - resolved_root = THEME_ROOT.resolve() - resolved_path = (CREW_WORKSPACE / css_path).resolve() - try: - resolved_path.relative_to(resolved_root) - except ValueError: - die(f"主题 {theme_id!r} 的 css 路径越界: {css}") - if not resolved_path.is_file(): - die(f"主题 {theme_id!r} 的 CSS 文件不存在: {resolved_path}") - return resolved_path + die( + f"主题 {theme_id!r} 的 css 必须是相对工作区的 .css 路径" + f"(如 wx_mp/wenyan-theme/{theme_id}.css): {THEME_INDEX}" + ) + root = THEME_ROOT.resolve() + # 候选解析(每个候选都先做 THEME_ROOT 包含校验,防止越界): + # 新约定:相对工作区根,如 "wx_mp/wenyan-theme/.css" + # 旧约定:"wenyan-theme/.css" 或裸 ".css" → 落到主题目录下同名文件 + for cand in (CREW_WORKSPACE / css_path, THEME_ROOT / css_path.name): + resolved = cand.resolve() + try: + resolved.relative_to(root) + except ValueError: + continue + if resolved.is_file(): + return resolved + die( + f"主题 {theme_id!r} 的 CSS 文件不存在或路径越界: {css}" + f"(注册表: {THEME_INDEX},主题目录: {THEME_ROOT})" + ) return None @@ -149,7 +194,7 @@ def resolve_theme(theme_arg: str | None) -> tuple[str, str] | None: 解析顺序: 1. theme_arg 为空 → None 2. 以 .css 结尾且是本地文件 → custom_theme(CSS 文本) - 3. wenyan-theme/index.json 登记的自定义 id → 解析 CSS 路径 → custom_theme + 3. wx_mp/wenyan-theme/index.json 登记的自定义 id → 解析 CSS 路径 → custom_theme 4. 其它 → 内置主题 id,原样作为 theme """ if not theme_arg: @@ -336,7 +381,7 @@ def main() -> None: parser.add_argument("markdown_file", help="Markdown 文件路径") parser.add_argument( "theme", nargs="?", default=None, - help="主题:内置 id(pie/lapis/default/…)/ 本地 .css 路径 / wenyan-theme/index.json 登记的自定义 id", + help="主题:内置 id(pie/lapis/default/…)/ 本地 .css 路径 / wx_mp/wenyan-theme/index.json 登记的自定义 id", ) parser.add_argument("--account", default=None, help="指定公众号 alias(缺省用 accounts.json 的 default)") args = parser.parse_args() diff --git a/crews/main/skills/expert-wx-mp/workflows/content-production.md b/crews/main/skills/expert-wx-mp/workflows/content-production.md index ccba14c0..d227f958 100644 --- a/crews/main/skills/expert-wx-mp/workflows/content-production.md +++ b/crews/main/skills/expert-wx-mp/workflows/content-production.md @@ -267,9 +267,9 @@ image_list: 排版独立于 DNA,不作为风格符合证据;排版在这一步选定: 1. 用户指定主题 -> 使用指定主题。 -2. 用户未指定 -> Agent 根据文章内容自主选择:先看 `wenyan-theme/index.json` 有无可复用自定义主题,再按 `wx-mp-publisher` 的决策树从内置主题中挑选,不丢给 relay 默认。 -3. 用户要求对标排版(“照着这篇的排版来”)-> 调 `generate-wenyan-theme` 提取参考文章排版风格生成主题,登记到 `wenyan-theme/index.json`。 -4. 自定义主题把登记的 `theme-id` 直接传给 `wx-mp-publisher`(脚本经 `wenyan-theme/index.json` 解析出 CSS 并随请求上传 relay);直接传 `.css` 文件路径也可以。 +2. 用户未指定 -> Agent 根据文章内容自主选择:先看 `wx_mp/wenyan-theme/index.json` 有无可复用自定义主题,再按 `wx-mp-publisher` 的决策树从内置主题中挑选,不丢给 relay 默认。 +3. 用户要求对标排版(“照着这篇的排版来”)-> 调 `generate-wenyan-theme` 提取参考文章排版风格生成主题,登记到 `wx_mp/wenyan-theme/index.json`。 +4. 自定义主题把登记的 `theme-id` 直接传给 `wx-mp-publisher`(脚本经 `wx_mp/wenyan-theme/index.json` 解析出 CSS 并随请求上传 relay);直接传 `.css` 文件路径也可以。 ### 9-2. 发布 diff --git a/crews/main/skills/expert-wx-mp/workflows/editing.md b/crews/main/skills/expert-wx-mp/workflows/editing.md index edf487a1..e885b36b 100644 --- a/crews/main/skills/expert-wx-mp/workflows/editing.md +++ b/crews/main/skills/expert-wx-mp/workflows/editing.md @@ -41,11 +41,11 @@ 用户说"换个排版"、"换个主题"、"照着这篇的排版来": -1. **用户直接指定了主题名** -> 内置 id 直接用;自定义主题先从 `wenyan-theme/index.json` 解析 `theme-id` +1. **用户直接指定了主题名** -> 内置 id 直接用;自定义主题先从 `wx_mp/wenyan-theme/index.json` 解析 `theme-id` 2. **用户给了微信文章链接**(`mp.weixin.qq.com` 开头)-> 调 `generate-wenyan-theme` 提取参考文章的排版风格,生成自定义主题 - - 生成后保存到 `wenyan-theme/.css`,并登记 `wenyan-theme/index.json` + - 生成后保存到 `wx_mp/wenyan-theme/.css`,并登记 `wx_mp/wenyan-theme/index.json` 3. **用户说"换个好看的"** -> agent 根据内容类型和阅读场景推荐 2-3 个主题 -4. **对标某个账号的排版** -> 先抓几篇那个号的文章 -> 调 `generate-wenyan-theme` 提取,并登记到 `wenyan-theme/index.json` +4. **对标某个账号的排版** -> 先抓几篇那个号的文章 -> 调 `generate-wenyan-theme` 提取,并登记到 `wx_mp/wenyan-theme/index.json` 换完排版重新生成预览,让用户看效果再确认;发布时把自定义 `theme-id` 传给 `wx-mp-publisher`。 From 08580236544765c921361fc651225303a40dbb2e Mon Sep 17 00:00:00 2001 From: codes-factory-of-bg Date: Tue, 1 Sep 2026 18:24:00 +0800 Subject: [PATCH 03/34] =?UTF-8?q?docs:=20=E4=B8=BB=E9=A2=98=E8=B7=AF?= =?UTF-8?q?=E5=BE=84=E5=BC=95=E7=94=A8=E7=BB=9F=E4=B8=80=E5=88=B0=20wx=5Fm?= =?UTF-8?q?p/wenyan-theme/?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DNA 架构文档与 wx_mp README 中残留的 wenyan-theme/ 平台主题目录引用, 对齐实际运行路径(本地实例与 expert-wx-mp 均已迁移到 wx_mp/wenyan-theme/)。 --- crews/main/wx_mp/README.md | 2 +- docs/expert-pack-dna-architecture.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crews/main/wx_mp/README.md b/crews/main/wx_mp/README.md index fee4109d..027ce90a 100644 --- a/crews/main/wx_mp/README.md +++ b/crews/main/wx_mp/README.md @@ -8,7 +8,7 @@ | `outputs/` | 文章产出(每篇一个 `/`:article.md、配图、封面、dna-meta.json) | | `dna/` | DNA 运行资产(每 `/`:reports / dna 文档 / template / evals) | | `calibration/` | 校准与复盘数据(baseline、受众画像、对标记录、平台状态) | -| `wenyan-theme/` | 排版主题模板存放 | +| `wenyan-theme/` | 排版主题模板存放(`wx_mp/wenyan-theme/`) | `calibration/` 默认含 `audience.md`(受众画像)与 `platform-state.json`(平台状态);其余文件(对标记录等)由相应 workflow 按需生成。 diff --git a/docs/expert-pack-dna-architecture.md b/docs/expert-pack-dna-architecture.md index 862bfbf8..5e9d4f93 100644 --- a/docs/expert-pack-dna-architecture.md +++ b/docs/expert-pack-dna-architecture.md @@ -188,7 +188,7 @@ Workspace/ | 数据 | 归属 | |------|------| | DNA report / DNA 文档 / DNA template | `/dna//` | -| 排版主题 | 平台主题目录,例如 `wenyan-theme/` | +| 排版主题 | 平台主题目录,例如 `wx_mp/wenyan-theme/` | | 校准、复盘、抓取结果 | `/calibration/` | 硬性边界: @@ -422,7 +422,7 @@ crews//skills/expert-/tools/-style-profiler/ - 只有工具清单中明确列出的 wrapper 名称可以直接作为 shell 命令调用;未暴露 wrapper 的 Tool 名称仅用于定位工具说明,不得拼成脚本路径。 2. **运行期数据只能写 Workspace**。 - - DNA report、DNA 文档、DNA template、主题 CSS、抓取结果、校准数据等生成物,都必须保存到 Workspace 下的平台数据目录(例如 `wx_mp/dna/`、`wenyan-theme/`、`wx_mp/calibration/`)。 + - DNA report、DNA 文档、DNA template、主题 CSS、抓取结果、校准数据等生成物,都必须保存到 Workspace 下的平台数据目录(例如 `wx_mp/dna/`、`wx_mp/wenyan-theme/`、`wx_mp/calibration/`)。 - 不得写入专家包的 `dna/`、`references/`、`tools/` 或其他包内目录。专家包是可替换、可重建、可软链的代码与规则资产;运行期写入会造成实例状态和源仓状态耦合, reinstall / 重建 / 升级时也容易丢失。 - 专家包只发布方法论、框架和工具;DNA 生成结果保存在 Workspace `/dna//`,不通过索引登记。 From a5193032e123b44dd369b82597f8c09a0f94f446 Mon Sep 17 00:00:00 2001 From: codes-factory-of-bg Date: Wed, 2 Sep 2026 10:25:32 +0800 Subject: [PATCH 04/34] =?UTF-8?q?fix(published-track):=20douyin=20?= =?UTF-8?q?=E6=8E=A2=E6=B4=BB=20status=5Fcode=20=E9=9D=9E=200/8=20?= =?UTF-8?q?=E4=B8=8D=E5=86=8D=E5=88=A4=E6=AD=BB=EF=BC=8CUNKNOWN=20?= =?UTF-8?q?=E6=94=BE=E8=A1=8C=E7=94=B1=E7=9C=9F=E5=AE=9E=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=E8=A3=81=E5=86=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 9-1/9-2 连续两晚凌晨复盘 douyin 误报 SESSION_EXPIRED:pong 端点 history/read 间歇返回 status_code=4(status_msg「服务器打瞌睡了」, 风控/限流特征),但同 cookie 真实取数(aweme detail)成功—— 原实现非 0 一律判死,把端点异常误报成登录失效触发无谓重登。 - pongDouyin 三态化:0=ok、8=明确未登录(唯一判死分支)、 其余/HTTP 错误/网络异常=UNKNOWN 放行,Step 3 真实请求最终裁决 - UNKNOWN 时响应体前 120 字节保留进 reason,缓存条目带 unknown 标记,checkSession/verifyCookies 以 ping:"unknown" + detail 透出, 供观察期确认 0↔4 翻转模式 - fetch-and-update-metrics.sh gate hint 不再硬编码「Cookie 关键字段 缺失/过期」(Tier2 pong 失败时误导值班 agent),改为指向 reason 已实测:status_code=4 → gate 放行 exit 0,真实取数 exit 0。 排查文档:workspace-main/douyin/20260902-抖音探活误报-SESSION_EXPIRED-研发排查文档.md --- crews/main/skills/_shared/check-session.ts | 57 ++++++++++++++----- .../scripts/fetch-and-update-metrics.sh | 2 +- 2 files changed, 45 insertions(+), 14 deletions(-) diff --git a/crews/main/skills/_shared/check-session.ts b/crews/main/skills/_shared/check-session.ts index 04c4d366..e16efd53 100644 --- a/crews/main/skills/_shared/check-session.ts +++ b/crews/main/skills/_shared/check-session.ts @@ -15,6 +15,12 @@ * douyin GET /aweme/v1/web/history/read/(a_bogus 签名)→ status_code==0 * wx_mp 不在本模块——走 wx-mp-hunter check(cgi-bin/home

「新的创作」)。 * + * pong 三态:ok / fail(明确未登录:douyin status_code=8、xhs guest)/ UNKNOWN + * (端点异常但无法证明未登录:douyin 除 0/8 外的 status_code、HTTP 层错误、网络异常)。 + * UNKNOWN 不判死——gate 放行,由下游真实请求最终裁决。2026-09-01/02 连续两晚凌晨 + * douyin pong 回 status_code=4 被判 SESSION_EXPIRED 触发误报重登,但同 cookie 真实 + * 取数成功(探活端点被风控/限流间歇拦截),见 workspace-main/douyin/20260902 排查文档。 + * * pong 结果落 ~/.cache/wiseflow-check-login/.json,TTL 600s。 * 批量调用复用同一缓存,把 N 次 pong 压成 1 次,避免批量签名触风控。 * @@ -81,7 +87,7 @@ export interface CheckResult { error?: "SESSION_EXPIRED" | "SIGN_UNAVAILABLE"; reason?: string; detail?: string; - ping?: "skipped" | "cached" | "ok" | "fail"; + ping?: "skipped" | "cached" | "ok" | "fail" | "unknown"; } /** 平台 key → 中央存储 session 文件名(xhs/xhs-browse 共用 xhs-browse.json) */ @@ -235,7 +241,7 @@ function genFakeMsToken(): string { return t + "=="; } -async function pongDouyin(map: CookieMap): Promise<{ ok: boolean; reason?: string }> { +async function pongDouyin(map: CookieMap): Promise<{ ok: boolean; reason?: string; unknown?: boolean }> { const { douyinSign } = await import("./relay-sign.ts"); const ua = loadUa("douyin"); const params = new URLSearchParams({ max_cursor: "0", count: "20", msToken: genFakeMsToken() }).toString(); @@ -246,17 +252,28 @@ async function pongDouyin(map: CookieMap): Promise<{ ok: boolean; reason?: strin headers: { "User-Agent": ua, Cookie: cookieHeader(map), Referer: "https://www.douyin.com/", Accept: "application/json" }, signal: AbortSignal.timeout(15_000), }); - if (!resp.ok) return { ok: false, reason: `history/read HTTP ${resp.status}` }; - const data = (await resp.json()) as { status_code?: number }; - // status_code==0 已登录;==8 未登录 - return data.status_code === 0 ? { ok: true } : { ok: false, reason: `status_code=${data.status_code}` }; + // HTTP 层异常不能证明未登录(风控/限流同样落这里)——UNKNOWN 放行,真实请求裁决 + if (!resp.ok) return { ok: true, unknown: true, reason: `history/read HTTP ${resp.status}` }; + const text = await resp.text(); + let data: { status_code?: number } = {}; + try { + data = JSON.parse(text) as { status_code?: number }; + } catch { + /* 非 JSON 响应(如风控验证页)——落 UNKNOWN 分支 */ + } + // status_code==0 已登录;==8 明确未登录。其余值语义未知(2026-09 误报教训,见模块头注释): + // 除 8 外一律 UNKNOWN 放行,由下游真实请求最终裁决;响应体片段保留在 reason 供观察。 + if (data.status_code === 0) return { ok: true }; + if (data.status_code === 8) return { ok: false, reason: "status_code=8(未登录)" }; + return { ok: true, unknown: true, reason: `status_code=${data.status_code}(语义未知) body=${text.slice(0, 120)}` }; } catch (e) { const msg = e instanceof Error ? e.message : String(e); - return { ok: false, reason: `history/read error: ${msg.slice(0, 120)}` }; + // 网络异常同理不能证明未登录——UNKNOWN 放行(真实请求若同样失败会以自身错误上报) + return { ok: true, unknown: true, reason: `history/read error: ${msg.slice(0, 120)}` }; } } -async function pong(platform: string, map: CookieMap): Promise<{ ok: boolean; reason?: string }> { +async function pong(platform: string, map: CookieMap): Promise<{ ok: boolean; reason?: string; unknown?: boolean }> { const p = pongPlatform(platform); switch (p) { case "bilibili": return pongBilibili(map); @@ -272,6 +289,7 @@ async function pong(platform: string, map: CookieMap): Promise<{ ok: boolean; re interface CacheEntry { ok: boolean; reason?: string; + unknown?: boolean; at: number; } @@ -324,8 +342,12 @@ export async function verifyCookies(platform: string, map: CookieMap, opts: { no } const r = await pong(platform, map); - writeCache(platform, { ok: r.ok, reason: r.reason, at: Date.now() }); - if (r.ok) return { ok: true, detail: pres.detail, ping: "ok" }; + writeCache(platform, { ok: r.ok, reason: r.reason, unknown: r.unknown, at: Date.now() }); + if (r.ok) { + // UNKNOWN(pong 端点异常但无法证明未登录)——放行,detail 带原因供日志观察 + if (r.unknown) return { ok: true, ping: "unknown", detail: `pong UNKNOWN: ${r.reason}(放行,由真实请求最终裁决)` }; + return { ok: true, detail: pres.detail, ping: "ok" }; + } return { ok: false, error: "SESSION_EXPIRED", reason: r.reason, ping: "fail" }; } @@ -365,12 +387,21 @@ export async function checkSession(platform: string, opts: { noPing?: boolean } // Tier 2: pong(带缓存) const cached = readCache(platform); if (cached) { - if (cached.ok) return { ok: true, detail: pres.detail, ping: "cached" }; + if (cached.ok) { + if (cached.unknown) { + return { ok: true, ping: "unknown", detail: `pong UNKNOWN(缓存): ${cached.reason}(放行,由真实请求最终裁决)` }; + } + return { ok: true, detail: pres.detail, ping: "cached" }; + } return { ok: false, error: "SESSION_EXPIRED", reason: cached.reason, ping: "cached" }; } const r = await pong(platform, loaded.map); - writeCache(platform, { ok: r.ok, reason: r.reason, at: Date.now() }); - if (r.ok) return { ok: true, detail: pres.detail, ping: "ok" }; + writeCache(platform, { ok: r.ok, reason: r.reason, unknown: r.unknown, at: Date.now() }); + if (r.ok) { + // UNKNOWN(pong 端点异常但无法证明未登录)——放行,detail 带原因供日志观察 + if (r.unknown) return { ok: true, ping: "unknown", detail: `pong UNKNOWN: ${r.reason}(放行,由真实请求最终裁决)` }; + return { ok: true, detail: pres.detail, ping: "ok" }; + } return { ok: false, error: "SESSION_EXPIRED", reason: r.reason, ping: "fail" }; } diff --git a/crews/main/skills/published-track/scripts/fetch-and-update-metrics.sh b/crews/main/skills/published-track/scripts/fetch-and-update-metrics.sh index 8ea2649d..e3819b64 100755 --- a/crews/main/skills/published-track/scripts/fetch-and-update-metrics.sh +++ b/crews/main/skills/published-track/scripts/fetch-and-update-metrics.sh @@ -185,7 +185,7 @@ if [ "$NEEDS_COOKIE" = true ]; then CHECK_EXIT=${CHECK_EXIT:-0} if [ "$CHECK_EXIT" -eq 2 ]; then CHECK_REASON=$(printf '%s' "$CHECK_OUT" | node -e 'let d="";process.stdin.on("data",c=>d+=c);process.stdin.on("end",()=>{try{console.log(JSON.parse(d).reason||"")}catch{}})' 2>/dev/null) - echo "{\"ok\":false,\"error\":\"SESSION_EXPIRED\",\"platform\":\"$PLATFORM\",\"login_platform\":\"$LM_PLATFORM\",\"method\":\"script\",\"reason\":\"$CHECK_REASON\",\"hint\":\"Cookie 关键字段缺失/过期,请使用 login-manager 技能引导用户重新登录 $LM_PLATFORM(camoufox-cli --session $LM_PLATFORM --persistent --headed open $PLATFORM_HOME → 用户手动登录 → cookies export + identity export 落中央存储)\"}" + echo "{\"ok\":false,\"error\":\"SESSION_EXPIRED\",\"platform\":\"$PLATFORM\",\"login_platform\":\"$LM_PLATFORM\",\"method\":\"script\",\"reason\":\"$CHECK_REASON\",\"hint\":\"登录态探活未通过(具体原因见 reason):cookie 关键字段缺失/过期,或 pong 明确返回未登录。请使用 login-manager 技能引导用户重新登录 $LM_PLATFORM(camoufox-cli --session $LM_PLATFORM --persistent --headed open $PLATFORM_HOME → 用户手动登录 → cookies export + identity export 落中央存储)\"}" exit 2 fi if [ "$CHECK_EXIT" -ne 0 ]; then From 55706addfe490775c8377829a2403ba566c35270 Mon Sep 17 00:00:00 2001 From: codes-factory-of-bg Date: Fri, 4 Sep 2026 09:11:57 +0800 Subject: [PATCH 05/34] update: media send hint --- AGENTS.md | 17 ++++++++++------- scripts/lib/agent-skills.sh | 6 +++++- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index a64e956c..71777e93 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,9 +1,3 @@ -# AGENTS.md - -This file provides guidance to Codex (Codex.ai/code) when working with code in this repository. - -Codex 被授权在本仓库中执行任何 git 命令(包括 push、branch、tag 等),无需逐次确认。 - ## Docker 部署规范 - 用户态镜像、Compose service 和持久卷统一使用 **xiaobei** 命名;不得新增 `wiseflow-*` 镜像或卷名。 @@ -39,7 +33,7 @@ openclaw 实际识别的 frontmatter 字段(参见 `openclaw/src/agents/skills - 顶层:`name`、`description`(**必需**)、`user-invocable`(默认 true)、`disable-model-invocation`(默认 false) - `metadata.openclaw.*`:`emoji`、`homepage`、`skillKey`、`primaryEnv`、`os`、`requires`、`install`、`always` -其他字段(如 Codex 的 `argument-hint`、`allowed-tools`、`license`)会被静默忽略。 +其他字段(如 claude code 的 `argument-hint`、`allowed-tools`、`license`)会被静默忽略。 **写法用 YAML block style**,不要用 flow style(嵌套花括号 + 引号)。openclaw bundled 技能和官方文档均采用 block style: @@ -59,6 +53,14 @@ metadata: - `always: true` 的真实语义是"跳过 `requires` 二进制/env 检查直接判定 eligible"(见 `config-eval.ts:124`),**不是**"强制注入整个 SKILL.md"。如果 skill 没声明 `requires`,加 `always: true` 等于无意义,应删除。 - 加载阶段 openclaw 只把 `name` + `description` + SKILL.md 绝对路径塞进 system prompt 的 `` 块;agent 用到时才主动 read 全文。所以 frontmatter 写得再多也不会污染 system prompt,但反过来也意味着——除上述识别字段外,多余字段不会带来任何运行时收益。 +## SKILL.md 内容书写规范 + +SKILL.md 是写给**执行时的智能体**看的操作手册,不是开发日志。内容只写指导性指令、正例、反例: + +- ✅ 直接写"应该怎么做""不要怎么做",配 ✅ 正例 / ❌ 反例 +- ❌ 不写排查故事、历史往来、开发经历、思路背景、踩坑全过程 +- 经验教训要提炼成一条可执行的规则,而不是一段叙事 + ## skill 依赖打包规则 产品拆分后(D8)addons/ 结构已销毁,skill 只有两层: @@ -84,5 +86,6 @@ metadata: |-------|--------------|----------------| | `crews/main/skills/wx-mp-hunter` | `cheerio` | ✅ | | `crews/main/skills/rss-reader` | `rss-parser` | ✅ | +| `crews/main/skills/ui-demo` | `camoufox-js`、`playwright-core` | ✅ | 其余 skill 的脚本只用 Node 内置模块或相对 import,不需要 `package.json`。 diff --git a/scripts/lib/agent-skills.sh b/scripts/lib/agent-skills.sh index 971f17d4..4c56c7a5 100644 --- a/scripts/lib/agent-skills.sh +++ b/scripts/lib/agent-skills.sh @@ -475,7 +475,11 @@ inject_media_send_guide() { - 在本地打开媒体文件(如调用图片查看器、浏览器打开 file://)——用户不一定方便操作这台电脑,本地打开对用户毫无意义。 - 把 base64 或文件原始字节当作文本贴进回复——刷屏且用户无法使用。 -正确做法:用当前渠道的媒体发送能力把文件本体直接投递到聊天中(需提供文件绝对路径)。具体调用哪个工具/action 以本机当前可用渠道为准,不要假定渠道名或写死某个 action。 +正确做法:用当前渠道的媒体发送能力把文件本体直接投递到聊天中(需提供文件绝对路径)。 + +- 飞书:`message(action="send", media="<绝对路径>")`(对于 HTML 类型文件,飞书要求先复制到 `/tmp/openclaw/`,再执行发送)。 +- `openclaw-weixin`:`message(action="send", media="<本地绝对路径或 HTTPS URL>")`,当前会话可不传 target。 +- `awada`: 不支持本地路径;当前回复用 `MEDIA:`,预置云文件用 `message(action="sendAttachment", file_name="<文件名>")`。 GUIDE } From 721bdea5ec47dd2474eba957fc92729695f05077 Mon Sep 17 00:00:00 2001 From: codes-factory-of-bg Date: Tue, 8 Sep 2026 21:41:36 +0800 Subject: [PATCH 06/34] dna update for douyin/wx_channel/xhs --- crews/content-producer/AGENTS.md | 12 +- .../skills/video-producer/SKILL.md | 40 ++- .../pipelines/dna-ad-video-pipeline.md | 139 +++++++++ .../video-producer/scripts/make-cover.py | 26 +- .../skills/video-producer/video-producer.sh | 4 + crews/main/AGENTS.md | 2 +- crews/main/skills/expert-douyin/SKILL.md | 16 +- .../tools/douyin-style-profiler/SKILL.md | 170 +++++----- .../references/account-dna-framework.md | 83 +++++ .../references/style-17d-framework.md | 295 ------------------ .../scripts/build_style_profile.py | 233 ++++++-------- .../workflows/account-benchmark.md | 34 +- .../expert-douyin/workflows/account-setup.md | 12 +- .../workflows/content-production.md | 28 +- .../skills/expert-douyin/workflows/editing.md | 10 +- .../skills/expert-douyin/workflows/review.md | 24 +- .../expert-douyin/workflows/style-dna.md | 171 ++++------ crews/main/skills/expert-wx-channel/SKILL.md | 18 +- .../tools/wechat-channels-publish/SKILL.md | 38 +-- .../tools/wx-channel-engagement/SKILL.md | 10 +- .../scripts/fetch_engagement.py | 6 +- .../tools/wx-channel-style-profiler/SKILL.md | 163 ++++------ .../references/account-dna-framework.md | 84 +++++ .../references/video-dna-dimensions.md | 80 ----- .../scripts/build_style_profile.py | 235 ++++++-------- .../workflows/account-benchmark.md | 44 ++- .../workflows/account-setup.md | 18 +- .../workflows/content-production.md | 106 +++---- .../expert-wx-channel/workflows/editing.md | 20 +- .../expert-wx-channel/workflows/review.md | 26 +- .../expert-wx-channel/workflows/style-dna.md | 180 ++++------- crews/main/skills/expert-xhs/SKILL.md | 14 +- .../tools/xhs-style-profiler/SKILL.md | 184 +++++------ .../references/account-dna-framework.md | 86 +++++ .../references/xhs-note-dna-dimensions.md | 68 ---- .../scripts/build_style_profile.py | 247 ++++++--------- .../expert-xhs/workflows/account-benchmark.md | 33 +- .../expert-xhs/workflows/account-setup.md | 14 +- .../workflows/content-production.md | 276 ++++++++-------- .../skills/expert-xhs/workflows/editing.md | 22 +- .../skills/expert-xhs/workflows/review.md | 24 +- .../skills/expert-xhs/workflows/style-dna.md | 181 ++++------- crews/main/skills/published-track/SKILL.md | 2 +- .../scripts/validate_content.py | 2 +- docs/expert-pack-dna-architecture.md | 14 +- 45 files changed, 1580 insertions(+), 1914 deletions(-) create mode 100644 crews/content-producer/skills/video-producer/pipelines/dna-ad-video-pipeline.md create mode 100644 crews/main/skills/expert-douyin/tools/douyin-style-profiler/references/account-dna-framework.md delete mode 100644 crews/main/skills/expert-douyin/tools/douyin-style-profiler/references/style-17d-framework.md create mode 100644 crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/references/account-dna-framework.md delete mode 100644 crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/references/video-dna-dimensions.md create mode 100644 crews/main/skills/expert-xhs/tools/xhs-style-profiler/references/account-dna-framework.md delete mode 100644 crews/main/skills/expert-xhs/tools/xhs-style-profiler/references/xhs-note-dna-dimensions.md diff --git a/crews/content-producer/AGENTS.md b/crews/content-producer/AGENTS.md index 26e63f42..d856b684 100644 --- a/crews/content-producer/AGENTS.md +++ b/crews/content-producer/AGENTS.md @@ -6,6 +6,7 @@ | 入口信号 | 走哪条路 | 入口技能 | |---------|---------|---------| +| Brief 中指定 `pipeline`(如 `dna-ad-video-pipeline`) | 指定 Pipeline 制作 | `video-producer` + 对应 `pipelines/` 文档 | | 用户要"从零做视频""出一支完整视频""按这个脚本/主题拍片子" | 端到端视频制作 | `video-producer` Stage 0→14 全流程 | | 用户要对已有素材进行剪辑、修整、拼接等 | 已有素材剪辑 | `video-producer` Stage 12 工具箱 | | 用户要"把这句话/这句口播做成拼贴 B-roll""纸拼贴动画""半调拼贴" | 纸拼贴组装动画 | `collage-broll` | @@ -15,14 +16,17 @@ ## 通用约定 - **每接到一个活儿先建工作区**:视频类走 `output_videos//`(由 `video-producer` 内脚本建);平台专家包委托并传入现成项目目录(`/outputs//`)时直接沿用,不另建;平面设计类走 `design_assets/YYYY-MM-DD-<任务名>/`(由 `design-full init` 建) -- **Brief 确认前不得干活**:任何方向都先把需求整理成 brief,发用户确认后再进后续 +- **Brief 确认前不得干活**:用户直接发起时,先整理 brief 发用户确认;main agent 委托时,只接受带确认状态与闸门批准人的 brief。Brief 已确认且 GATE A 已由 main 代理批准时,不重开需求讨论;缺关键字段先向 Brief owner 澄清 - **成片/成稿交付前必跑自检**:视频走公共 `video-review`,平面设计走视觉 review(对照 brief + DESIGN.md) -- **封面**:交付成片视频必须配含标题文字的封面图,走公共 `siliconflow-img-gen` +- **封面**:默认交付含封面主文案的封面图(平台有标题时可用标题;视频号无标题时用核心传达),走公共 `siliconflow-img-gen`;Brief 明确要求无字高情绪帧时按 Brief 执行并记录例外 - **不许声称没做过的事**:没有 tool result 或产物文件证明,不许声称已生成/已渲染/已改动 - **平台运营不在 CP**:发布到抖音/B站/小红书等归 main agent 的各 publish 技能,CP 不碰 ## 衔接关系 -- **viral-chaser → CP**:main agent 的 viral-chaser 只出追爆报告,制作委托 CP。接手时把报告当 brief 的一部分,走 `video-producer` 的 reference-driven 阶段——**CP 只吃报告出 2–3 个差异化概念 + 成本**,不做视频下载/转写/抽帧(那是 viral-chaser 的活,CP 不重复造);无报告则跳过该阶段直入出脚本 -- **main 的 video-edit → CP**:用户要从零做视频 → 转 CP 的 `video-producer`;用户给已有素材要轻剪辑/拼接/烧字幕 → 仍归 main 的 `video-edit` +- **viral-chaser → CP**:main agent 的 viral-chaser 只出追爆报告,制作委托 CP。接手时把报告当 brief 的一部分;Brief 已指定 Pipeline 或已锁定概念时直接按 Brief 执行,不重开概念阶段。否则走 `video-producer` 的 reference-driven 阶段——**CP 只吃报告出 2–3 个差异化概念 + 成本**,不做视频下载/转写/抽帧(那是 viral-chaser 的活,CP 不重复造);无报告则跳过该阶段直入出脚本 +- **main 的 video-edit → CP**:用户要从零做视频 → main 只出 Brief,CP 按 `video-producer` 或指定 Pipeline 制作;用户给已有素材要轻剪辑/拼接/烧字幕 → 仍归 main 的 `video-edit` +- **main / CP 分界点**:Brief。main 负责已有视频素材的简单加工、长文/图文内容和视频全案 Brief;CP 负责 Brief 之外的成片制作。Brief 指定 Pipeline 时必须采用;未指定时 CP 自由发挥。口播文案 DNA 已启用时,main 交付口播终稿,CP 不重写策略文案 +- **机器资源约束**:线程数、分辨率上限、低载编码等部署差异读本 workspace `MEMORY.md`;代码仓 Pipeline 不写死本机参数 - **main 的 talking-head-cut**:口播类去口气词/高光剪辑归 main,CP 不做 +- **字幕原子例外**:CP 不承接 main 的语义级 `video-edit` 素材深剪;但 Pipeline 需要烧字幕且无 CP 等价子命令时,可调用已暴露的 `video-edit subtitles` 原子,并在交付说明记录 diff --git a/crews/content-producer/skills/video-producer/SKILL.md b/crews/content-producer/skills/video-producer/SKILL.md index a0b8aa22..ac5feac8 100644 --- a/crews/content-producer/skills/video-producer/SKILL.md +++ b/crews/content-producer/skills/video-producer/SKILL.md @@ -1,6 +1,6 @@ --- name: video-producer -description: 视频制作全能工具,两种用法——(A) 端到端生产:从零做一支完整视频,出脚本、分镜、机位一致性、素材匹配、闸门、渲染、自检、交付;(B) 给定素材剪辑:已有几个片段要拼一下、给一个片段配音合成、剪辑烧字幕等,直接用 Stage 12 工具箱(assemble/clip-trim/audio-mix/timeline-compose/scene-compose/add-silent-audio/make-outro)。 +description: 视频制作全能工具,两种用法——(A) 端到端生产:按 Brief 或指定 Pipeline 从零做完整视频,覆盖脚本、分镜、素材、闸门、渲染、自检、交付;(B) 给定素材剪辑:已有几个片段要拼一下、配音合成、剪辑烧字幕等,直接用 Stage 12 工具箱。 metadata: openclaw: emoji: 🎬 @@ -20,7 +20,7 @@ metadata: 本技能有两种用法,agent 据用户请求判断走哪条: -**模式 A:端到端生产**——用户给主题/关键词/已有脚本/已有素材中的任一组合,要求从零做一支完整视频。走 Stage 0→14 全流程:意图路由 → 故事 → 剧本 → 分镜 → 机位 → 素材 → 闸门 → 渲染 → 自检 → 交付。另可接收 **main agent 喂入的 viral-chaser 追爆报告**(作为 brief 的一部分,本技能不做视频下载/转写/抽帧——那是 viral-chaser 的活)。 +**模式 A:端到端生产**——用户给主题/关键词/已有脚本/已有素材中的任一组合,或 main agent 交付已确认 Brief,要求从零做完整视频。默认走 Stage 0→14 全流程;Brief 指定 Pipeline 时先读取对应 Pipeline 文档并按其编排。另可接收 **main agent 喂入的 viral-chaser 追爆报告**(作为 brief 的一部分,本技能不做视频下载/转写/抽帧——那是 viral-chaser 的活)。 **模式 B:给定素材剪辑**——不涉及从零开剧本,编辑已有素材,直接用 Stage 12 工具箱(见下方"Stage 12 工具箱"段): @@ -42,13 +42,32 @@ metadata: --- +## Pipeline 机制 + +Brief 的 `pipeline` 字段是制作契约: + +| Pipeline | 何时使用 | 文档 | +| --- | --- | --- | +| `video-producer:default` | Brief 未指定 Pipeline,或用户直接发起从零制作 | 本 SKILL.md 的 Stage 0→14 | +| `dna-ad-video-pipeline` | Brief 明确指定,用于影视解说 + 反转植入 | `pipelines/dna-ad-video-pipeline.md` | + +规则: + +- Brief 未指定 Pipeline 时,CP 按默认全流程自由选择实现。 +- Brief 指定 Pipeline 时,必须先读对应 Pipeline 文档并直接采用,不得替换为自创流程。 +- Pipeline 只负责编排与内容套路;原子能力仍使用本技能子命令与公共技能,不新增脚本。 +- main / CP 的分界点是 Brief。main 交付的 Brief 已含确认与代理闸门批准时,CP 不重开需求讨论;缺关键字段时向 Brief owner 澄清。 +- 机器资源限制、线程数、分辨率上限等部署环境差异,从 CP workspace `MEMORY.md` 或 Brief 的环境约束读取,不写入 Pipeline。 + +--- + ## 工作区目录约定 调用方传入了现成项目目录时(如平台专家包委托制作,传入 `/outputs//`,brief 已在其中)直接沿用;否则在 `output_videos/` 下建项目文件夹 `/`: ``` / # 即 /outputs// 或 output_videos// -├── brief.md # Stage 0/1 产出:意图路由 + 概念选项 + 用户选定 +├── brief.md # 用户/main 交付或 Stage 0/1 产出的制作契约(可含 pipeline) ├── reference-driven/ # Stage 1(可选,仅当 main 喂了 viral-chaser 报告) │ ├── viral-chaser-report.md # main 喂入的追爆报告原档(本技能不自己跑 viral-chaser) │ ├── concepts.md # 据报告出的 2–3 差异化概念 + 成本 + 备选路径 @@ -98,6 +117,8 @@ metadata: └── final-deliver.md # Stage 14 交付清单 ``` +Pipeline 文档位于技能包内 `pipelines/`,不是项目目录;项目目录只保存 Brief、素材、脚本、渲染与交付产物。 + --- ## 阶段链(15 段,两闸门) @@ -127,11 +148,11 @@ Stage 11 mix-audio 配音配乐四场景分流(A 人物对话声画 Stage 12 assemble 按序拼接成片(原子工具箱:clip-trim 切段 / audio-mix 混音 / timeline-compose 时间轴合成 / assemble 拼接,agent 按 §Stage 12 工具箱场景化组合,不写死 Workflow) Stage 13a video-review 公共 video-review 技术自检(强制闸门) Stage 13b motion-audit CP 侧 motion_led 抽查(兑付 delivery-promise) -Stage 14a make-cover 封面(siliconflow-img-gen,必含标题文字) +Stage 14a make-cover 封面(siliconflow-img-gen,必含封面主文案) Stage 14b 交付 向用户呈交成片+封面+关键参数 ``` -> Stage 0–6 全是**文本产物**,付费生成前必停——GATE A 落在这条边界上。GATE B 落在素材就绪、pre-compose 闸门通过后,确认渲染前最终计划。 +> Stage 0–6 全是**文本产物**,付费生成前必停——GATE A 落在这条边界上。GATE B 落在素材就绪、pre-compose 闸门通过后,确认渲染前最终计划。指定 Pipeline 时,以 Pipeline 文档的阶段映射为准;若 main 已在 Brief 中代理批准 GATE A,记录批准范围后继续。 --- @@ -160,7 +181,7 @@ Stage 14b 交付 向用户呈交成片+封面+关键参数 | `audio-mix` | 多条音轨 + 各自延时/音量 | 混合音频 | Stage 12 原子工具:多轨混音 | | `timeline-compose` | timeline.json(每段素材/入点/出点/倍速/音轨及延时) | 合成片段 | Stage 12 原子工具:按时间轴调 clip-trim + audio-mix 合成 | | `motion-audit` | video.mp4 + delivery-promise.json | `review/motion-audit.json`(motion_led 抽查) | Stage 13b(补公共 video-review) | -| `make-cover` | brief.md(标题)+ storyboard 关键帧 | `cover.jpg` | Stage 14a(调 siliconflow-img-gen) | +| `make-cover` | brief.md(封面主文案)+ storyboard 关键帧 | `cover.jpg` | Stage 14a(调 siliconflow-img-gen) | > wrapper `video-producer.sh` 内部 `exec python3 "$SCRIPT_DIR/scripts/<子命令>.py" "$@"`——子命令名即脚本名,零路径拼接。 @@ -174,6 +195,7 @@ Stage 14b 交付 向用户呈交成片+封面+关键参数 - 呈交摘要:档位、场次数、镜数、角色数、关键决策(路径/模型/风格选择的备选+置信度+理由) - **结束本轮回复**,不许在同条回复里进 Stage 7 +- 批准人是 Brief owner(用户或 main agent);main 已代理批准时,必须把批准范围落 `gates/gate-a.md` - 批准是**逐闸门的**——早先的一句"你继续"不覆盖本闸门 - 用户要改哪段就重跑对应子命令(产物文件存在性即 checkpoint,不会重生成未改的) @@ -182,6 +204,7 @@ Stage 14b 交付 向用户呈交成片+封面+关键参数 素材齐 + 计划过 slideshow_risk + delivery_promise 锁,**停下发用户看 contact sheet**: - 呈交:slot 总数、素材就绪率、slideshow_risk 六维分与 verdict、delivery_promise 八类与 motion_ratio 预估、素材 contact sheet +- 批准人是 Brief owner;授权与来源记录必须一并呈交 - 同 GATE A 收尾纪律 ### 返工与耗时上限 @@ -196,7 +219,7 @@ Stage 14b 交付 向用户呈交成片+封面+关键参数 ### 模糊意图不算确认 -- 用户说"做个短片""帮我策划"**不算确认**,必须先问清楚走哪条 workflow(故事讲述型/纯画面动效型/蒙太奇剪接型)、时长、受众 +- 用户说"做个短片""帮我策划"**不算确认**,必须先问清楚走哪条 workflow(故事讲述型/纯画面动效型/蒙太奇剪接型)、时长、受众;main 交付的 Brief 需带确认状态与闸门批准人 - 起草/讨论脚本属对话协助,**不许调 render 工具** - 默认**小规模**:1 场 3–5 镜,不许把模糊想法擅自扩成多场多镜;用户要扩才扩 @@ -218,6 +241,7 @@ Stage 14b 交付 向用户呈交成片+封面+关键参数 | 公共 `pexels-footage` / `pixabay-footage` | skills/ | Stage 8 Stock Footage 素材补充 / Stage 11C BGM 搜 | | 公共 `bgm-library` | skills/ | Stage 11C BGM 搜(ccMixter 免版税 + 自动 TASL 署名,免 key,商用安全;与 pexels/pixabay 并列,优先用) | | 公共 `video-review` | skills/ | Stage 13a 成片技术自检闸门 | +| 已暴露 `video-edit subtitles` | main crew 的 wrapper 原子 | 指定 Pipeline 需要烧字幕时使用;不可用时向 Brief owner 报工具缺口 | | `requests` | 仓根 requirements.txt | 各脚本 HTTP 调用 | --- @@ -249,7 +273,7 @@ Stage 14b 交付 向用户呈交成片+封面+关键参数 | `video-producer audio-mix` | 多轨混音 | 0 成功 / 1 参数错 | | `video-producer timeline-compose` | 时间轴合成 | 0 成功 / 1 参数错 | | `video-producer motion-audit` | motion_led 抽查 | 0 成功 / 1 参数错 | -| `video-producer make-cover` | 封面(必含标题) | 0 成功 / 1 参数错 / 2 env 未配 | +| `video-producer make-cover` | 封面(必含封面主文案) | 0 成功 / 1 参数错 / 2 env 未配 | --- diff --git a/crews/content-producer/skills/video-producer/pipelines/dna-ad-video-pipeline.md b/crews/content-producer/skills/video-producer/pipelines/dna-ad-video-pipeline.md new file mode 100644 index 00000000..db893f9e --- /dev/null +++ b/crews/content-producer/skills/video-producer/pipelines/dna-ad-video-pipeline.md @@ -0,0 +1,139 @@ +# Pipeline:dna-ad-video-pipeline(影视解说 + 反转植入) + +## 定位 + +用于生产「叙事短片前段 + 后段集中产品/服务反转」的短视频。Brief 中写: + +```yaml +pipeline: dna-ad-video-pipeline +``` + +Brief 未写 `pipeline` 时不强制使用本 Pipeline,Content Producer 可按 `video-producer:default` 自由选择;一旦指定本 Pipeline,必须直接采用,不得替换为自创流程。 + +本 Pipeline 只写编排与内容套路;原子能力全部来自 `video-producer` 与公共技能,不新增脚本。 + +## Brief 输入契约 + +MainAgent 交付的 Brief 至少包含: + +| 字段 | 要求 | +| --- | --- | +| platform | douyin / wx_channel / xhs 等,用于发布说明与合规边界 | +| dna_id | 使用的账号级 DNA | +| core_message | 本条必须传达的核心信息 | +| packaging_copy | 选题、观看理由、平台包装文案方向;wx_channel 为视频简介 | +| product_points | 产品/服务事实、允许讲的能力、禁用承诺;只以 Brief 为准,不内置品牌事实 | +| voiceover | 若口播文案 DNA 已启用,main 交付口播终稿 `voiceover.md`;未启用时 CP 可在 Pipeline 内写脚本 | +| source_mode | open_license_footage / user_provided / aigc / mixed | +| assets | 素材路径、来源 URL、许可证、授权确认记录 | +| form | 横竖屏、时长带、画面风格、配音/BGM 倾向;只写账号级边界,不写逐镜细节 | +| subtitle_style | 是否烧字幕、样式与安全区要求;未指定时按平台常规可读性处理 | +| environment_constraints | 可选:质量、时长、资源或交付约束;本机资源限制仍以 CP workspace `MEMORY.md` 为准 | +| variant | two_stage / concentrated_reversal,未指定时由 CP 根据素材与 Brief 选择 | +| gates | GATE A/B 批准人(用户或 main agent);若 main 已代理批准,需写明批准范围 | +| acceptance | 交付物、验收标准、遗留问题记录要求 | + +缺失关键字段时先向 Brief owner 澄清,不得自行补品牌事实、授权或许可证。 + +## 素材 sourcing + +来源模式只允许以下四类(含混合): + +1. **open_license_footage**:使用 Brief 指定的开源/免版权片源。main 需提供本地素材或明确授权直链;CP 只做素材入库与技术处理,不做对标拆解、转写或爆款分析。必须记录原片 URL、许可证、署名要求;发布说明中如实署名。不得把“网上能下载”等同于可商用或可改编。 +2. **user_provided**:用户提供现成片段。入库前记录文件参数、来源说明、授权背景;版权风险由用户确认承担,CP 只做技术处理,不做授权背书。 +3. **aigc**:按 Brief 与 DNA 风格边界生成。调用公共 `aigc-video-gen`,记录 prompt、模型、生成时间与产物 metadata;发布说明按平台要求标注 AI 生成。 +4. **mixed**:以上模式混合。每一段素材都必须能追溯到来源模式与授权记录。 + +通用入库要求: + +- 每个素材通过工具校验可解码,并记录分辨率、帧率、时长、音轨。 +- 建立素材清单与候选区索引;候选区文件名必须能反查源素材时间点。 +- 检查素材中是否有旧品牌名、水印、URL、域名、平台标识或不可授权元素。 +- 素材支撑度不足时,回到 Brief owner 处理;不得为了凑成片编造叙事。 + +## 叙事与植入套路 + +### 通用规则 + +- **单线目标**:主角动机一句话说清,并在片内闭环;素材支撑不了就改写,不留断头线。 +- **节拍 ≤ 6**:剧情段每节拍一句,句间因果可追;段尾悬停在求助、任务、待发或冲突节点。 +- **钩连句**:转生、化身、置换等转折必须用口播明写因果,并用同色、同物或同动作的画面衔接。 +- **意象桥**:产品/服务段第一镜必须复用剧情核心意象;禁止用无关风景空镜硬切。 +- **产品段**:3-4 句单点深打,至少覆盖 Brief 允许的 3 个功能/价值点,每点有对应画面;产品段占比 20%-26%。 +- **反转点**:two_stage 变体 63%-76%;concentrated_reversal 变体 55%-65%。比例是创作约束,不是硬编码参数。 +- **收束**:产品/服务段结束后立即收尾,不加无信息量的仪式句或氛围空镜。 +- **合规**:正片零 URL、零域名、零联系方式;官网、仓库、价格、活动入口只进入发布说明。产品能力与承诺只讲 Brief 允许的范围,不承诺收益数字。 +- **旁白语气**:除非 Brief 另有要求,使用第三人称解说体;避免问句、感叹号、第二人称和促销信号词。若口播文案 DNA 已启用,以 main 交付的 `voiceover.md` 为准。 + +### concentrated_reversal 变体 + +- 反转点之前**零产品提及**:不插 punch 句、不散落卖点、不出现品牌暗示。 +- 产品/服务介绍集中在反转后的一段完成,介绍完即收尾。 +- 开源免费、安装方式、活动入口等只在 Brief 要求时进入产品段口播或贴图;域名只进入发布说明。 +- 可在反转瞬间使用 whoosh 等音效标记剧情→产品切换,但不得用音效替代因果衔接。 + +## 制作护栏 + +1. **规格一次锁定**:横竖屏、目标时长、分辨率与帧率由 Brief / DNA 决定。不得为了“看起来高级”擅自放大素材;需要统一规格时,先确认素材源质量和部署环境约束,再写入 timeline 计划与决策日志。 +2. **帧率一致性**:混排来源素材时,先确认各段帧率;不一致必须通过 `assemble` 的归一化参数或分段预处理统一,不得把不同帧率直接 concat 后交付。输出后核对时长、音画同步与段边界。 +3. **旁白排布**:使用 `mix-audio` + `narration-align` 的实测时长 / 字级时间戳排布,不用文本长度估算。每句旁白不得越过对应镜头边界;连续旁白保留呼吸间隔,累计漂移要能追溯到每段实际时长。 +4. **字幕安全区**:`subtitles.srt` 必须来自对齐后的口播时间轴;烧录前检查目标画幅安全区,避免字幕落到画面中部或关键主体上。字幕样式由 Brief / DNA 的视觉边界决定,不硬编码项目个案参数。 +5. **素材四查**:候选帧与成片抽帧检查 URL / 域名、旧品牌名或水印、残缺元素、字幕遮挡。发现风险优先换干净素材窗口;裁切只能作为确认安全后的次选。 +6. **AIGC 原声**:AIGC 声画同出素材如含有效环境音,可作为低音量音床并在口播下 ducking;无有效音轨时补静音,不用噪声填充。 +7. **决策审计**:素材取舍、规格、音色、字幕样式、fallback 与弃用中间产物都写入 `script/decisions.json` 或 `final-deliver.md`,不留在口头说明。 + +## 原子能力映射 + +| Pipeline 环节 | 使用能力 | +| --- | --- | +| Brief 解析与决策审计 | Agent 读取 Brief;决策写 `script/decisions.json` | +| 口播/脚本 | main 交付 `voiceover.md` 时,原样落为 `script/script.md` 并锁定,不重写策略文案,仍跑 `script-self-eval` 做检查;未交付时用 `story-develop` → `script-write` → `script-self-eval` | +| 分镜与素材 slot | `storyboard-build` → `shot-decompose` → `slot-plan` | +| 素材获取 | `asset-resolve`(stock/AIGC slot)或按 Brief 素材清单入库;AIGC 段用 `render-shot` | +| 口播与字幕 | `mix-audio`;需要逐句对齐时 `narration-align` | +| 字幕烧录 | 使用已暴露的字幕原子命令(当前为 `video-edit subtitles`);不可用时向 Brief owner 报工具缺口,不手写 ffmpeg | +| 闸门前评估 | `slideshow-risk` → `delivery-promise-lock` | +| 切段与时间轴 | `clip-trim` / `timeline-compose` | +| 混音 | `audio-mix`;环境音、口播、BGM、音效按 Brief 与素材音轨处理 | +| 合成 | `assemble`;需要分段预合时 `scene-compose` | +| 动效审计 | `motion-audit` | +| 技术自检 | 公共 `video-review` | +| 封面 | `make-cover`,封面主文案与视觉承诺来自 Brief/DNA;wx_channel 无平台标题,封面主文案用核心传达 | +| 交付 | `final-deliver.md` | + +禁止直接手写 ffmpeg 命令;所有 ffmpeg 操作必须通过上述子命令或公共工具完成。机器资源限制、编码线程数、分辨率上限、低载参数等属于部署环境差异,读取 Content Producer workspace 的 `MEMORY.md` 或 Brief 中的环境约束,不写入本 Pipeline。 + +## 闸门 + +- **GATE A(文本闸门)**:口播/脚本、叙事节拍、意象桥、产品段与素材支撑计划齐备后,向 Brief owner 呈交摘要。若 main 已在 Brief 中代理批准 GATE A,记录批准范围后继续;否则停下等待批准。 +- **GATE B(素材闸门)**:素材清单、授权记录、候选区 contact sheet、风险检查与交付承诺齐备后,向 Brief owner 呈交。批准人是用户或 main,按 Brief `gates` 字段执行。 +- Brief 变更时升版本;已开工中间产物按新版取舍,弃用部分记入 `final-deliver.md`。 + +## 交付物 + +```text +/ + brief.md + voiceover.md # main 交付口播时 + script/ # 脚本、分镜、决策与自评 + materials/ # 原始素材与授权记录 + render/ # AIGC / 处理后素材 + audio/ # 口播、字幕、混音 + video.mp4 + cover.jpg + publish-desc.md # 平台包装文案或视频简介/署名/AI 标注/允许出现的链接 + final-deliver.md # 素材来源、授权、段时长、自检、遗留问题 +``` + +`publish-desc.md` 是发布说明,不是成片内容。Content Producer 不发布、不私信用户;预览由 Brief owner 或用户侧渠道发送。 + +## 验收检查 + +1. 正片零 URL、零域名、零联系方式。 +2. 品牌名、产品名、能力表述与 Brief 一致;无收益承诺。 +3. 叙事单线闭环,节拍 ≤ 6,转折因果可追。 +4. 产品段占比、反转点位置与选定变体一致。 +5. 每段素材来源与授权可追溯;AIGC 标注完整。 +6. 口播、字幕、画面、音效与 Brief / `voiceover.md` 一致。 +7. `video-review` verdict 为 pass;未通过不得交付。 +8. 交付说明包含弃用中间产物、fallback 决策和遗留问题。 diff --git a/crews/content-producer/skills/video-producer/scripts/make-cover.py b/crews/content-producer/skills/video-producer/scripts/make-cover.py index 67e0f98d..df813a09 100644 --- a/crews/content-producer/skills/video-producer/scripts/make-cover.py +++ b/crews/content-producer/skills/video-producer/scripts/make-cover.py @@ -1,14 +1,14 @@ #!/usr/bin/env python3 -"""Stage 14a — make-cover:封面(siliconflow-img-gen,必含标题文字)。 +"""Stage 14a — make-cover:封面(siliconflow-img-gen,必含封面主文案)。 Usage: python3 scripts/make-cover.py --title "..." -入:project_dir/script/brief.md(标题)+ storyboard 关键帧 -出:project_dir/cover.jpg(含标题文字的封面图) +入:project_dir/brief.md(封面主文案)+ storyboard 关键帧 +出:project_dir/cover.jpg(含封面主文案的封面图) -封面硬约束:必含标题文字。siliconflow-img-gen 不一定能把中文标题烤进图, -agent 生成后用 image 工具看,确认标题可见——不可见就用 ImageMagick/Pillow 烧字上去。 +封面硬约束:必含封面主文案。平台有标题时可用标题;视频号无标题时用核心传达。siliconflow-img-gen 不一定能把中文封面主文案烤进图, +agent 生成后用 image 工具看,确认封面主文案可见——不可见就用 ImageMagick/Pillow 烧字上去。 """ import argparse @@ -25,7 +25,7 @@ def die(msg: str) -> None: def main() -> None: parser = argparse.ArgumentParser(description="Stage 14a make-cover") parser.add_argument("project_dir", help="项目目录(output_videos// 或平台运营目录 /outputs//)") - parser.add_argument("--title", default=None, help="封面标题文字,不传则从 brief.md 抽") + parser.add_argument("--title", default=None, help="封面主文案,不传则从 brief.md 抽") args = parser.parse_args() project = Path(args.project_dir).resolve() @@ -39,7 +39,7 @@ def main() -> None: brief = project / "brief.md" if brief.is_file(): content = brief.read_text(encoding="utf-8") - # 抽第一个 # 标题或前 50 字作 title + # 抽第一个 # 标题作为封面主文案,或取前 50 字兜底 for line in content.splitlines(): if line.startswith("# "): title = line[2:].strip() @@ -47,19 +47,19 @@ def main() -> None: if not title: title = content[:50].strip() if not title: - die("需 --title 或 brief.md 含 # 标题") + die("需 --title 或 brief.md 含 # 封面主文案标题行") stub = { - "title": title, + "cover_copy": title, "instruction": ( - "agent 调公共 siliconflow-img-gen 生成封面图,prompt 必含标题文字指令。" - "生成后用 image 工具看,确认标题可见——不可见就用 ImageMagick/Pillow 烧字上去。" + "agent 调公共 siliconflow-img-gen 生成封面图,prompt 必含封面主文案指令。" + "生成后用 image 工具看,确认封面主文案可见——不可见就用 ImageMagick/Pillow 烧字上去。" "落 cover.jpg 到项目根。" ), "cover_path": str(cover), } - print(f"[plan] cover.jpg 标题:{title}") - print(f"[next] agent 调 siliconflow-img-gen 生成 → 确认标题可见 → 落 {cover}") + print(f"[plan] cover.jpg 封面主文案:{title}") + print(f"[next] agent 调 siliconflow-img-gen 生成 → 确认封面主文案可见 → 落 {cover}") if __name__ == "__main__": diff --git a/crews/content-producer/skills/video-producer/video-producer.sh b/crews/content-producer/skills/video-producer/video-producer.sh index 8293626f..82ee9863 100755 --- a/crews/content-producer/skills/video-producer/video-producer.sh +++ b/crews/content-producer/skills/video-producer/video-producer.sh @@ -21,6 +21,10 @@ video-producer — 端到端视频制作(wrapper) video-producer <子命令> [参数...] 跑对应阶段的原子脚本 video-producer help 列可用子命令 +Pipeline: + Brief 指定 pipeline 时,先读技能包内 pipelines/.md,再按其映射调用下列子命令。 + 未指定 pipeline 时走 SKILL.md 的 Stage 0→14 默认流程。 + 子命令(按工作流阶段序): intent-router Stage 0 意图路由 → 三档脚本模板(故事讲述型/纯画面动效型/蒙太奇剪接型) reference-concepts Stage 1 吃 viral-chaser 报告出 2–3 差异化概念(可选,无报告跳过) diff --git a/crews/main/AGENTS.md b/crews/main/AGENTS.md index bceffd0c..542fe62a 100644 --- a/crews/main/AGENTS.md +++ b/crews/main/AGENTS.md @@ -23,7 +23,7 @@ - 专家包按任务路由,互不越界:推特/小红书评论区获客 / 截流等 BD 场景走 `expert-bd`,不走平台运营包。 - 商业模式打磨(接触投资人前的前置环节)不属于任何专家包:结合 `business_knowledge.md` 直接与用户对话完成(多路径权衡用 `council`),结论落 `MEMORY.md`。 -- **零星工作兜底**:未被专家包覆盖的任务,可直接调用手头的工具完成(skill 清单会话时会自动加载,此处不列出);更适合其他 crew 承担的工作,以 spawn subagent 的方式委托(如从零生产完整视频交 content-producer,技术问题、系统排障与环境配置交 IT engineer)。找不到匹配的专家包或工具时,先询问用户或保守处理,不猜测平台规则与 DNA。 +- **零星工作兜底**:未被专家包覆盖的任务,可直接调用手头的工具完成(skill 清单会话时会自动加载,此处不列出);更适合其他 crew 承担的工作,以 spawn subagent 的方式委托(如视频全案由 main 出 Brief 后交 content-producer,技术问题、系统排障与环境配置交 IT engineer)。找不到匹配的专家包或工具时,先询问用户或保守处理,不猜测平台规则与 DNA。 - crew 生命周期管理(启用/停用/调整其他 crew)是你的固有职责,不经专家包路由,见下文「crew 管理」段。 ## 数据存储 diff --git a/crews/main/skills/expert-douyin/SKILL.md b/crews/main/skills/expert-douyin/SKILL.md index c6318ff3..71065614 100644 --- a/crews/main/skills/expert-douyin/SKILL.md +++ b/crews/main/skills/expert-douyin/SKILL.md @@ -1,12 +1,12 @@ --- name: expert-douyin -description: 抖音短视频运营专家。承接从定位起号、选题脚本、内容制作、发布到数据复盘的完整运营工作。零散的发布、拆解参考视频、取数等操作也可以直接做。 +description: 抖音账号运营专家。承接定位起号、账号级 DNA、选题与包装、长文图文、已有素材轻加工、视频全案 Brief、发布与数据复盘;全片制作委托 content-producer。 metadata: openclaw: emoji: 🎵 --- -# 抖音短视频运营专家 +# 抖音账号运营专家 ## 预设 Workflow @@ -15,7 +15,7 @@ metadata: | 场景 | Workflow | 什么时候触发 | |------|----------|-------------| | 内容 DNA 管理 | Style DNA | 建 / 更新内容 DNA(样本、偏好、局部借鉴、对标融合),决定样本落到哪个 DNA | -| 内容生产 | Content Production | 做一条 / 做几条抖音视频;输入可以是粗略想法、参考视频(仿照 / 同主题改写)、已有素材或已有脚本 | +| 内容生产 | Content Production | 做一条 / 做几条抖音内容;main 直接做已有素材轻加工,视频全案只产出并委托 Brief | | 起号与定位 | Account Setup | 新号起号、定位梳理、内容支柱搭建、老号接手与诊断 | | 账号对标 | Account Benchmark | 对标账号 / 对标视频分析,并与默认或指定 DNA 逐项比较 | | 改片与调整 | Editing | 改文案、重剪、换封面、调结构、换风格 | @@ -37,17 +37,21 @@ metadata: | 工具 | 用途 | 命令 | |------|------|------| -| `douyin-style-profiler` | 生成单条视频 17 维 DNA report,并聚合 DNA 文档与 DNA template | `douyin-style-profiler` | +| `douyin-style-profiler` | 生成单条视频的账号级 DNA report,并聚合 DNA 文档与 Brief template | `douyin-style-profiler` | | `douyin-comments` | 抓取抖音视频评论(对标分析 / 标签反推用,纯 HTTP 不起浏览器) | `douyin-comments` | | `douyin-publish` | 成片 → 抖音创作者中心发布(浏览器自动化) | `douyin-publish` | 跨领域通用技能:`viral-chaser`(抖音 / B站 / 小红书视频下载拆解,DNA 采样与仿写参考的取数主力)、`smart-search`(跨平台搜索,选题调研优先走社交平台,不用通用搜索引擎)、`content-calibrator`(DNA 表现评估)、`published-track`(发布记录与指标库)、`login-manager`(抖音登录态维护)。 -制作链相关技能(边界见 Content Production Workflow):`video-edit`(素材加工拼接)、`talking-head-cut`(口播轻剪辑)、`ui-demo`(产品操作录屏)、`video-review`(成片质检闸门)、`aigc-video-gen`(AIGC 片段)、`siliconflow-img-gen`(封面图)、`pexels-footage` / `pixabay-footage`(免版权素材)。从零出脚本、端到端制作一律委托 `content-producer`,main 不代写完整脚本。 +制作链相关技能(边界见 Content Production Workflow):`video-edit`(素材加工拼接)、`talking-head-cut`(口播轻剪辑)、`ui-demo`(产品操作录屏)、`video-review`(成片质检闸门)、`siliconflow-img-gen`(封面图)、`pexels-footage` / `pixabay-footage`(免版权素材)。 + +**分工硬边界**:main 只做已有视频素材的简单加工和长文 / 图文内容;视频全案只产出 Brief 并委托 `content-producer`。口播类视频若 DNA 启用口播文案 DNA,口播文案由 main 写好并随 Brief 交付;Content Producer 只负责声画制作。Brief 指定 Pipeline 时 CP 必须采用,未指定时 CP 自由发挥。 ## 风格与 DNA -账号内容风格 DNA 存储目录是 `douyin/dna/`。未指定 DNA 时默认使用并更新 `dna-0`。生产前同时读取 DNA 文档与 DNA template;对标分析先建立独立对标 DNA,不默认写入 `dna-0`。DNA 维度框架(17 维,初始版本已确认)位于 `douyin-style-profiler` 的 `references/style-17d-framework.md`。 +账号级 DNA 存储目录是 `douyin/dna/`。未指定 DNA 时默认使用并更新 `dna-0`。生产前同时读取 DNA 文档与 DNA template;对标分析先建立独立对标 DNA,不默认写入 `dna-0`。 + +DNA 是账号级框架:定位与核心传达、选题组合、标题包装、账号简介、内容形式比例、发布习惯、高数据创意、视觉/声音倾向、口播文案 DNA、互动系列与制作管线。它指导 main agent 出内容或 Brief,不规定成片制作细节。维度框架 v1 位于 `douyin-style-profiler` 的 `references/account-dna-framework.md`。 ## 数据与记录 diff --git a/crews/main/skills/expert-douyin/tools/douyin-style-profiler/SKILL.md b/crews/main/skills/expert-douyin/tools/douyin-style-profiler/SKILL.md index 418ecaf5..c3ebf8d1 100644 --- a/crews/main/skills/expert-douyin/tools/douyin-style-profiler/SKILL.md +++ b/crews/main/skills/expert-douyin/tools/douyin-style-profiler/SKILL.md @@ -1,28 +1,31 @@ --- name: douyin-style-profiler -description: 为单条抖音视频提取 17 维 DNA report,按 DNA ID 聚合历史 report 生成 DNA 文档,并推导完整的 DNA template。 +description: 提取抖音账号级 DNA:单条视频生成 report,按 DNA ID 聚合定位、选题、标题、内容形式、发布节奏、高数据创意与制作管线,推导 main agent 的 Brief template。 +metadata: + openclaw: + emoji: 🧬 --- # douyin-style-profiler -抖音短视频的内容风格提取与 DNA 生产工具。输入是**视频的转录文本**(口播转录全文 + 标题/描述/时长/互动线索,由 Agent 先行整理成 `.md` / `.txt`)与封面 / 首帧图;不接受视频文件或视频链接作为输入。 +抖音账号级 DNA 提取与聚合工具。输入是**视频转录文本**(口播全文 + 标题/描述/时长/互动线索,由 Agent 先整理成 `.md` / `.txt`)与可选封面 / 首帧图;不接受视频文件或链接作为直接输入。 + +DNA 的用途是指导 main agent 选题、包装、账号表达和视频制作 Brief;不指导 main agent 直接产出全片。 ## 产物模型 ```text 单条视频 -> DNA report -同一个 DNA 目录下的全部 report + 权重/focus -> DNA 文档 -DNA 文档 -> DNA template +同一 DNA 下的全部 report + 权重/focus + 用户输入 -> DNA 文档 +DNA 文档 -> main agent Brief template ``` -- **DNA report**:单条视频的 17 维提取结果。 -- **DNA 文档**:聚合历史 report 后得到的内容与风格规则。 -- **DNA template**:由 DNA 文档推导出的生产模板,供生产时直接执行。 +- **DNA report**:单条样本的账号级观测与 13 维提取结果。 +- **DNA 文档**:聚合后的账号级规则与样本覆盖度说明。 +- **DNA template**:main agent 生成内容或委托 Content Producer 时使用的 Brief / 生产输入模板。 ## 存储结构 -DNA 以 DNA ID 为主体存储,一个 DNA 可以持续放入任意数量视频,样本可以来自一个或多个参考账号,甚至来自用户直接的想法。 - ```text douyin/dna/{dna-id}/ reports/ @@ -33,15 +36,15 @@ douyin/dna/{dna-id}/ {dna-id}.template.md ``` -原始转录文本可以临时来自任何位置(建议 `douyin/ref/{dna-id}/transcripts/`);生成后的 DNA report 必须进入对应 DNA 的 `reports/` 目录。 +原始转录文本可临时放在 `douyin/ref/{dna-id}/transcripts/`;生成后的 report 必须进入目标 DNA 的 `reports/` 目录。 ## 职责边界 -- 输入转录文本支持 `.md` / `.txt`;视频文件、链接不直接作为输入。 -- 统计只作为聚合证据底座,不评分、不替代定性判断。 -- 17 维语义判断由 Agent 回读转录原文(必要时回看视频关键帧)完成;封面 / 首帧维度必须由视觉模型读取本地图片完成。 -- 不输出选题价值判断、合规结论、账号权重或风格评分。 -- 不要求用户确认或登记 INDEX。 +- 单条样本只提供候选信号;账号比例、发布节奏、高数据共性必须由多样本或账号级数据聚合。 +- 统计只做证据底座,不评分、不替代定性判断。 +- 视觉语言有图片/关键帧证据时才由视觉模型分析;缺失写「未观测」。 +- 口播文案 DNA 独立聚合;样本不足时保持未启用。 +- 不输出合规结论、账号权重或风格评分。 ## Report - 单条提取 @@ -52,36 +55,22 @@ douyin-style-profiler report \ --sample-id {sample-id} \ --cover-image path/to/cover.jpg \ --source-url "https://www.douyin.com/video/..." \ - --duration 89 -``` - -默认输出: - -```text -douyin/dna/{dna-id}/reports/{sample-id}.report.md + --duration 89 \ + --output-dir douyin/dna/{dna-id}/reports ``` -参数说明: +- `--cover-image`:封面或首帧本地图片,用于视觉证据。 +- `--source-url`:原视频链接;本地素材无链接时省略。 +- `--duration`:视频时长(秒),用于口播密度统计。 +- `--weight`:样本权重,默认 1。 +- `--focus`:限制该样本只影响指定维度,可重复传入。 -- `--cover-image`:封面或首帧本地图片(抽帧产物或封面下载图),进入视觉模型分析。 -- `--source-url`:原视频链接,作为报告证据保留;本地素材无链接时省略。 -- `--duration`:视频时长(秒),用于口播密度统计;未知时省略。 +Agent 生成 scaffold 后必须: -可配置权重: - -```bash ---weight 3 -``` - -可限制该条只在某些维度参与借鉴: - -```bash ---focus hook ---focus speech-rhythm -``` - -Agent 生成 scaffold 后必须回读转录原文,补齐每个维度的单条结论、原文证据和可复用创作信号;钩子维度必须逐字摘录前 3 秒口播 / 首帧字幕。 -封面 / 首帧维度必须读取 `--cover-image` 指向的本地图片,并通过视觉模型补齐主体、构图、色彩、光线、质感、风格、文字视觉、品牌元素、避免项和 AIGC 复现提示词要素。没有图片时记录"未提供",不得编造。 +1. 补齐「样本与账号观测」:样本类型、账号与简介、发布时间、内容形式、数据线索、视频形态与授权信息。 +2. 回读转录原文,补齐 13 维的单条结论、原文证据和可复用信号。 +3. 对账号简介、内容比例、发布节奏等账号级字段,单条样本无法观测时写「未观测」。 +4. 高数据样本必须回读创意与内容形式,不得只凭播放量下结论。 ## Build - 聚合 DNA 文档与模板 @@ -89,20 +78,14 @@ Agent 生成 scaffold 后必须回读转录原文,补齐每个维度的单条 douyin-style-profiler build --dna-id {dna-id} ``` -默认读取: - -```text -douyin/dna/{dna-id}/reports/ -``` - -默认输出: +默认读取 `douyin/dna/{dna-id}/reports/`,输出: ```text douyin/dna/{dna-id}/{dna-id}.dna.md douyin/dna/{dna-id}/{dna-id}.template.md ``` -也可显式传入一个或多个 report 文件/目录: +也可显式传入 report 文件/目录: ```bash douyin-style-profiler build \ @@ -113,31 +96,25 @@ douyin-style-profiler build \ Agent 聚合时必须: 1. 读取全部 DNA report,不能只看统计表。 -2. 按每个 report 的 `weight` 和 `focus` 判断影响范围。 +2. 按 `weight` 与 `focus` 判断影响范围。 3. 区分高频共性、高权重偏好、局部借鉴、孤例和例外。 -4. 为每个维度写聚合结论、报告依据和创作规则。 -5. 确保 DNA 文档能够完整推导 template。 +4. 标注样本覆盖度;单条/少量样本不得称为稳定账号 DNA。 +5. 为每个维度写聚合结论、报告依据和可执行规则。 +6. 确保 DNA 文档能完整推导 template。 ## DNA Template -Template 是生产模板,不是概念解释。必须从 DNA 文档的 17 个维度推导,至少包含: - -- 选题角度、受众关系 -- 标题类型、参考标题、话题标签策略、封面 / 首帧风格和封面 AIGC 提示词要素 -- 起、承、转、合、CTA 五个固定语义部分(对应短视频的黄金开场、主体推进、高潮转折、收尾、互动引导) -- 每个部分的本段任务、执行方式和必须做 / 避免项 +Template 是 main agent 的 Brief / 内容生产输入模板,不是成片制作模板。固定语义段: -模板整体固定为七个部分:选题、标题(含封面)、起、承、转、合、CTA。固定的是语义结构,不是物理时长占比;任一部分可以对应视频中的一个或多个段落,也可以在特定内容形态下弱化。 +1. 定位与核心传达 +2. 选题与标题包装 +3. 内容形式与发布节奏 +4. 高数据创意模式 +5. 互动与系列 +6. 制作交接与 Pipeline +7. 口播文案 DNA(可选) -五个视频部分必须充分吸收 DNA 文档中的维度结论: - -| 部分 | 主要推导来源 | -| --- | --- | -| 起 | 3秒钩子、选题角度、口播节奏、语气与人设感、镜头语言 | -| 承 | 视频结构模式、叙事节奏、口播节奏、用词习惯、专业度体现、镜头语言 | -| 转 | 冲突与张力、叙事节奏、情绪表达(语气与人设感)、口播节奏 | -| 合 | 视频结构模式、签名式标记、语气与人设感、系列化与合集 | -| CTA | 互动设计、选题与受众关联、语气与人设感 | +制作交接段必须写清 MainAgent 与 Content Producer 的交付物、Pipeline、素材授权和风格边界。Brief 未指定 Pipeline 时,Content Producer 可自由选择;指定 Pipeline 时必须直接采用。 ## Update - 增量聚合 @@ -148,61 +125,54 @@ douyin-style-profiler update \ --template douyin/dna/{dna-id}/{dna-id}.template.md ``` -脚本会合并 DNA 文档记录的历史 report 与新 report,重新计算加权统计,并保留 Agent 已完成内容。Agent 仍需重新审视聚合结论,再同步修订 DNA 文档和 template。 +脚本合并历史 report 与新 report,重新计算统计并保留 Agent 已完成内容。Agent 仍需重新审视聚合结论,并同步修订 DNA 文档和 template。 -`--input` 可省略。省略时表示没有新增样本,只基于历史 report、既有 DNA 文档和用户输入做融合;适用于采纳另一个 DNA 文档或 template 中的局部规则。此时仍必须通过 `--user-input` 传入要融合的要求,并由 Agent 转译到具体维度。 +`--input` 可省略,用于只融合用户输入或另一个 DNA 的局部规则;此时必须传 `--user-input`。 ## 用户输入转译 用户输入是参考信息,不是可直接入库的 DNA 规则。 ```bash ---user-input "开头冲突再前置一点" +--user-input "账号更偏实拍拼接,少用纯动画" ``` -Agent 必须把输入转译到具体维度,例如: +Agent 必须转译到具体维度,例如: ```text -hook:钩子改为矛盾前置,第一句直接给反差结果 -video-structure:开场压缩到 2 秒内进入冲突画面 -speech-rhythm:开场两句均为短句,不做背景铺垫 +content-form-mix:主形态为实拍拼接,纯动画仅作补充 +production-pipeline:Brief 默认使用 video-producer:default(montage) +visual-language:画面保持实拍质感,不采用插画化风格 ``` 处理要求: -1. 在 DNA 文档的"用户输入转译区"记录 affected dimensions、DNA 修改和 template 修改。 -2. 把原话转译成可执行的聚合结论与创作规则。 -3. Template 只写转译后的执行规则,不直接抄用户原话。 -4. 与样本证据冲突时保留冲突说明,由用户选择优先级。 +1. 在 DNA 文档的「用户输入转译区」记录 affected dimensions、DNA 修改和 template 修改。 +2. 原话必须转译为可执行规则,不得直接抄进 template。 +3. 与样本证据冲突时保留冲突说明,由用户选择优先级。 ## Focus ID | ID | 维度 | |---|---| -| `topic-angle` | 选题角度 | -| `title-style` | 标题与文案 | -| `cover-frame` | 封面与首帧 | -| `hook` | 3秒钩子 | -| `word-habit` | 用词习惯 | -| `speech-rhythm` | 口播节奏 | -| `tone` | 语气与人设感 | -| `shot-language` | 镜头语言 | -| `visual-style` | 画面风格 | -| `sound-design` | 声音与BGM | -| `video-structure` | 视频结构模式 | -| `narrative-rhythm` | 叙事节奏 | -| `conflict-tension` | 冲突与张力 | -| `professionalism` | 专业度体现 | -| `interaction-design` | 互动设计 | -| `signature` | 签名式标记 | -| `series-design` | 系列化与合集 | +| `positioning-core` | 定位与核心传达 | +| `topic-portfolio` | 选题组合 | +| `title-packaging` | 标题与包装 | +| `bio-profile` | 账号简介与主页表达 | +| `content-form-mix` | 内容形式与比例 | +| `publish-cadence` | 发布习惯 | +| `high-performer-patterns` | 高数据创意模式 | +| `visual-language` | 视觉语言 | +| `audio-language` | 声音语言 | +| `narration-dna` | 口播文案 DNA | +| `engagement-conversion` | 互动与转化 | +| `series-signature` | 系列与签名 | +| `production-pipeline` | 制作管线倾向 | ## 统计与分词 -脚本统计转录文本的句段、标点、人称、口播密度(需 `--duration`)等指标;中文高频信号使用相邻二字组合,仅作为候选线索。Agent 必须回读原文判断它是否真是口头禅或签名式表达。 - -当前不引入 jieba。若后续候选噪声明显,可把 jieba 作为候选词挖掘器加入仓库级依赖,但分词结果不能直接作为 DNA 结论。 +脚本统计转录文本的句段、标点、人称、口播密度等指标;中文高频信号使用相邻二字组合,仅作为候选线索。Agent 必须回读原文确认口头禅或签名式表达。分词结果不能直接作为 DNA 结论。 ## 参考资料 -- `references/style-17d-framework.md`(抖音 17 维 DNA 分析框架,初始版本已确认) +- `references/account-dna-framework.md`(抖音账号级 DNA 框架 v1、Pipeline 映射与聚合边界) diff --git a/crews/main/skills/expert-douyin/tools/douyin-style-profiler/references/account-dna-framework.md b/crews/main/skills/expert-douyin/tools/douyin-style-profiler/references/account-dna-framework.md new file mode 100644 index 00000000..9a95a304 --- /dev/null +++ b/crews/main/skills/expert-douyin/tools/douyin-style-profiler/references/account-dna-framework.md @@ -0,0 +1,83 @@ +# 抖音账号级 DNA 框架(v1) + +> 状态:v1。Focus ID、维度命名与 template 语义以本文件为准。调整维度必须升版本,并同步 `scripts/build_style_profile.py` 与工具 `SKILL.md`。 + +## 定位与边界 + +抖音 DNA 是**账号级运营框架**,不是单条视频的制作细节清单。它回答这个账号长期如何被识别、如何选题、如何包装、如何安排内容形式与发布节奏,并指导 main agent 产出制作 Brief。 + +- **必须进入 DNA**:定位与核心传达、选题组合、标题与包装、账号简介、图文/视频等内容形式及比例、发布时间段与混合节奏、高数据内容的创意共性、稳定视觉/声音倾向、口播文案 DNA、互动与系列标记、制作管线倾向。 +- **不进入主 DNA**:单镜运镜、逐句文案、转场参数、字幕样式、成片剪辑微操。口播文案 DNA 是独立可选块;没有证据时写「未观测」,不强行规定。 +- **MainAgent / Content Producer 分界**:main agent 负责已有视频素材的简单加工、长文/图文内容,以及视频全案 Brief。除 Brief 之外的成片制作由 Content Producer 执行。口播类视频的口播文案若由 DNA 明确启用,则由 main agent 写好并随 Brief 交付;Content Producer 只做声画制作。 +- **样本可以是账号或单条**:账号样本可提取账号简介与发布习惯;用户提供单条或多条单篇时,仍按同维度统计聚合,但账号简介、比例、发布节奏等字段必须标「样本不足 / 未观测」。 + +## 维度(13 维) + +### 一、定位与选题 + +| # | ID | 维度 | 观测内容 | +|---|----|------|----------| +| 1 | `positioning-core` | 定位与核心传达 | 一句话定位、目标人群、账号长期让用户记住的核心承诺、每条内容的主张边界 | +| 2 | `topic-portfolio` | 选题组合 | 常见选题类型、内容支柱、选题入口、选题之间的系列关系;不判断单条选题好坏 | +| 3 | `title-packaging` | 标题与包装 | 标题/描述模式、封面或首帧包装承诺、话题标签策略;不规定具体字句 | +| 4 | `bio-profile` | 账号简介与主页表达 | 昵称、简介、置顶内容、主页视觉与承诺;仅账号样本可得 | + +### 二、账号节奏 + +| # | ID | 维度 | 观测内容 | +|---|----|------|----------| +| 5 | `content-form-mix` | 内容形式与比例 | 图文、视频、口播、实拍拼接、创意转场、纯 AIGC 动画等形式;如混合,记录比例或倾向 | +| 6 | `publish-cadence` | 发布习惯 | 发布时间段、频率、内容形式混合节奏(如三图文对一视频)、栏目化节奏 | +| 7 | `high-performer-patterns` | 高数据创意模式 | 高互动/高播放样本的选题、包装、形式与创意共性;数据只是证据,不自动等于风格 | + +### 三、表达与风格 + +| # | ID | 维度 | 观测内容 | +|---|----|------|----------| +| 8 | `visual-language` | 视觉语言 | 稳定画面风格、横竖屏、视觉记忆点;不规定镜头参数与逐镜设计 | +| 9 | `audio-language` | 声音语言 | 配音类型/音色倾向、口播密度、BGM/音效倾向;不规定 TTS 参数 | +| 10 | `narration-dna` | 口播文案 DNA | 独立可选块:开头钩子、起承转合、收束、人称与语气、签名表达;与主 DNA 分开判断稳定性 | + +### 四、互动与制作 + +| # | ID | 维度 | 观测内容 | +|---|----|------|----------| +| 11 | `engagement-conversion` | 互动与转化 | 评论/关注/私信等平台内行动、承接路径、引导强度 | +| 12 | `series-signature` | 系列与签名 | 栏目名、固定开场/收尾、系列承诺、识别符号 | +| 13 | `production-pipeline` | 制作管线倾向 | 视频内容形态到 Content Producer Pipeline 的映射;未指定时交给 CP 自由发挥 | + +## Pipeline 映射 + +`production-pipeline` 只能写 Content Producer 已支持的管线,不得发明不存在的管线名。 + +| DNA 观测形态 | Brief 中的 Pipeline | +|--------------|---------------------| +| 影视解说 + 反转植入 | `dna-ad-video-pipeline` | +| 口播 / 真人讲述 | `video-producer:default`(narrative) | +| 实拍拼接 / 蒙太奇 | `video-producer:default`(montage) | +| 创意转场 / 动效 | `video-producer:default`(motion) | +| 纯 AIGC 动画 | `video-producer:default`(AIGC render) | + +Brief 未写 `pipeline` 时,Content Producer 按 `video-producer:default` 自由选择实现;Brief 写了 Pipeline 时必须直接采用,不得替换成自创流程。 + +## Report 与聚合规则 + +1. 单条 report 先记录「样本与账号观测」:样本类型、账号与简介、发布时间、内容形式、数据线索、视频形态、授权信息。缺失写「未观测」。 +2. 单条样本不能推导账号比例、发布节奏或高数据共性;聚合时至少要有多个样本或账号级数据,并在 DNA 文档标注覆盖度。 +3. 高数据样本必须回读创意与内容形式,不能只凭播放量归因。 +4. 口播文案 DNA 独立聚合;没有足够样本时保持未启用。 +5. 脚本统计只提供句长、人称、口播密度等证据;语义判断由 Agent 回读原文完成。 + +## Template 语义 + +DNA template 是 main agent 的 **Brief / 内容生产输入模板**,不是成片制作模板。固定语义段: + +1. 定位与核心传达 +2. 选题与标题包装 +3. 内容形式与发布节奏 +4. 高数据创意模式 +5. 互动与系列 +6. 制作交接与 Pipeline +7. 口播文案 DNA(可选) + +模板不得规定镜头表、逐句台词、转场参数或编码细节。视频全案的制作选择由 Content Producer 在 Pipeline 内完成。 diff --git a/crews/main/skills/expert-douyin/tools/douyin-style-profiler/references/style-17d-framework.md b/crews/main/skills/expert-douyin/tools/douyin-style-profiler/references/style-17d-framework.md deleted file mode 100644 index d90c9592..00000000 --- a/crews/main/skills/expert-douyin/tools/douyin-style-profiler/references/style-17d-framework.md +++ /dev/null @@ -1,295 +0,0 @@ -# 抖音 17 维 DNA 分析框架 - -> **状态:初始版本(2026-08-27 经用户确认)**。后续如需增删合并维度,必须同步更新 `scripts/build_style_profile.py` 的 `DIMENSION_GROUPS` 与 Focus ID 表,并对已有 DNA 重新聚合。 - -本框架用于分析抖音短视频作品的内容与风格,逐步累积形成抖音内容生产 DNA。与微信公众号 17 维不同,短视频的观测物是**口播转录文本 + 画面 + 声音 + 结构节奏**的组合,因此维度按短视频语境重新组织。分析产物必须是三层: - -1. **DNA report**:单条视频的 17 维提取结果。 -2. **DNA 文档**:聚合同一个 DNA ID 下全部 report 后形成的规则。 -3. **DNA template**:从 DNA 文档推导出的生产模板。 - -```text -单条视频 -> DNA report -多个 DNA report + 权重/focus + 用户输入转译 -> DNA 文档 -DNA 文档 -> DNA template -``` - -样本观测来源: - -- `viral-chaser` 拆解产物:ASR 转录全文与分句时间戳、视频时长、标题/描述/作者、互动计数、抽帧关键帧。 -- 用户提供的文字稿、脚本或口述要点(转成 `.md` / `.txt` 后输入)。 -- 画面维度(镜头语言、画面风格、封面 / 首帧)必须由视觉模型读取视频关键帧或封面图完成,不得凭转录文本想象补齐。 - -## 一、选题与包装(3) - -### 1. 选题角度 - -- 选题类型:痛点教程、失败复盘、公开挑战、案例拆解、反常识观点、清单避坑、答疑、场景实验、系列预告等 -- 选题入口:现象、问题、冲突、数据、热点、挑战还是个人经历 -- 观看理由:陌生人为什么要看完这条;看完能记住账号的哪个身份、冲突、热情或承诺 - -分析时必须回答: - -- 这条内容解决了什么具体问题,或提供了什么具体情绪价值。 -- 选题与目标人群的关联是什么:理解、判断、行动、避坑、身份认同还是情绪共鸣。 - -### 2. 标题与文案 - -- 标题类型:痛点型、数字型、反差型、悬念型、搜索长尾型、身份点名型 -- 标题原文与描述文案原文 -- 话题标签策略:主标签、场景词、痛点词的使用方式与数量 -- 搜索词预埋:标题与描述中的长尾搜索句 - -### 3. 封面与首帧 - -封面 / 首帧来源: - -- `viral-chaser` 抽帧关键帧(首选 `frame_00`)或封面图下载。 -- 用户手动提供本地图片文件。 -- 无法获取时记录"未提供",不得根据转录文本或标题想象补齐。 - -Agent 必须通过视觉模型读取图片,至少提取: - -| 特征 | 分析要求 | -| --- | --- | -| 画面主体与场景 | 主体是什么、在做什么、处于什么场景,主体占画面比例 | -| 构图与画幅 | 竖屏构图、主体位置、视觉动线、留白、前景背景层次 | -| 色彩体系 | 主色、辅色、强调色、饱和度、明度、对比关系 | -| 光线与质感 | 自然光 / 人造光、方向、阴影、颗粒、锐度、材质 | -| 风格与媒介 | 实拍 / 插画 / 录屏 / 图文卡片 / 混剪,具体流派或时代感 | -| 文字视觉与图文关系 | 封面字内容、字体气质、字号层级、贴纸、标题框、字图遮挡关系 | -| 品牌识别元素 | 固定角标、边框、水印、系列符号、版式惯性 | -| 避免项 | 与该封面风格相反、容易破坏识别度的元素 | - -视觉模型分析必须能反推为一套可执行 AIGC 提示词,包含主体、场景、构图、画幅、色彩、光线、质感、风格、文字视觉、品牌元素和负向约束。 - -## 二、钩子与表达(4) - -### 4. 3秒钩子 - -- 钩子类型:矛盾前置、数据冲击、场景代入、悬念留白、结果反差、身份点名、利益承诺 -- 钩子原文:前 3 秒口播逐字摘录 + 首帧字幕 -- 钩子与定位的对应关系;钩子承诺在正文中是否兑现 -- 画面、字幕、口播是否在同一秒传递同一个重点 - -### 5. 用词习惯 - -- 高频词 Top 10-15(脚本统计仅为候选线索) -- 口头禅与高频连接词 -- 情绪词偏好 - -### 6. 口播节奏 - -- 句长分布(短/中/长)与短句连发模式 -- 停顿与留白位置(结合时间戳判断) -- 口播密度(字/分钟,脚本统计需 `--duration`) -- 设问自答频率、排比/对比使用频率 - -### 7. 语气与人设感 - -- 说话方式:朋友式 / 专家式 / 陪伴成长式比例 -- 亲密度与距离感;人称使用偏好(我/你/我们) -- 人设张力:真实短板、热情、判断标准如何成为记忆点 -- 人设边界:不编造悲惨经历、虚假失败、身份标签(编造即失格,不进 DNA) - -## 三、视觉与制作(3) - -### 8. 镜头语言 - -- 出镜形态:真人口播、旁白 + 空镜、产品实拍、屏幕录制、情景演绎 -- 场景选择与记忆点(场景放大) -- 景别与机位切换频率、运镜方式 -- 动作元素:边做什么边讲(实测、挑战、拆箱、复刻、限时完成) - -### 9. 画面风格 - -- 色调与滤镜倾向(暖/冷、高饱和/低饱和) -- 字幕样式:字体粗细、位置、背景框、重点字变色放大 -- 贴纸、特效、转场的使用密度与克制程度 -- 信息密度:画面元素多寡与留白 - -### 10. 声音与BGM - -- 声音形态:原声口播、TTS 配音、旁白、纯画面 + 字幕 -- BGM 类型与节奏(卡点 / 铺底 / 无 BGM) -- 音效使用(强调音、转场音、环境声) -- 音量层次:人声与 BGM 的主从关系 - -## 四、结构与节奏(4) - -### 11. 视频结构模式 - -- 总时长与时长带(<15s / 15-60s / 1-3min / >3min) -- 开场(钩子)—主体—高潮转折—收尾的时长占比 -- 单条完整叙事还是系列切片 -- 分段方式:一气呵成还是明显小节(字幕分段 / 场景切换) - -### 12. 叙事节奏 - -- 信息密度波动:密集输出段与喘息段交替 -- 节奏切换点位置(几秒处换场景 / 换论点 / 上钩子回收) -- 高光位置:最强信息或最强情绪放在哪里 -- 完播设计:中段是否有持续悬念或递进承诺 - -### 13. 冲突与张力 - -- 放大方式:场景放大、动作放大、冲突放大、系列放大、人格放大 -- 反差与矛盾的呈现位置(前置还是后置揭晓) -- 限制条件与失败现场的使用 -- 检查项:放大层是否服务内容本身,是否只剩噱头 - -### 14. 专业度体现 - -- 证据与演示方式:实物、实操、数据、对比、案例 -- 拆解深度:结论复述还是过程与判断展示 -- 术语密度与解释方式 -- 边界标注:适用条件、样本限制、不确定性的说明习惯 - -## 五、互动与标记(3) - -### 15. 互动设计 - -- 评论引导方式:具体问题、选择题(A 还是 B)、征集案例、关键词评论 -- 引导位置与密度;是否合规(不诱导、不以利益换互动) -- 评论区经营痕迹:置顶、作者回复风格 -- 关注引导:系列承诺、主页价值、下集预告 - -### 16. 签名式标记 - -- 固定开场白 / 结束语 -- 口头禅与标志性表达片段 -- 标志性动作、道具、场景或剪辑习惯 -- 系列符号:固定角标、固定 BGM、固定字幕样式 - -### 17. 系列化与合集 - -- 系列设定:单条是否属于持续任务("7 天改造""从不会到能做") -- 集与集之间的钩子:本集结尾如何预告下一集 -- 合集归属与命名习惯 -- 内容支柱意识:系列是否服务于同一人群 / 同一能力线 - -## 分析要求 - -### DNA report 要求 - -除封面 / 首帧外,每个维度至少包含: - -1. **单条结论**:这条视频在该维度上的具体做法。 -2. **原文证据**:口播逐字引用(注明时间区间)或画面描述。 -3. **可复用创作信号**:聚合时值得进入 DNA 文档的信号。 - -单条 report 不需要判断跨条稳定性;这个判断属于 DNA 文档聚合阶段。 - -钩子维度必须逐字摘录前 3 秒口播 / 首帧字幕,不概括、不改写。 - -封面 / 首帧的 DNA report 必须记录图片路径、视觉模型分析结果、AIGC 复现提示词要素和无法判断的信息。没有图片时不得编造视觉证据。 - -### DNA 文档要求 - -除封面 / 首帧外,每个维度至少包含: - -1. **聚合结论**:当前账号采用的规则。 -2. **报告依据**:来自哪些 DNA report、各自权重和 focus。 -3. **创作规则**:生产时必须怎么做,避免只写"有网感""节奏快"这类空泛形容词。 - -聚合时要区分高频共性、高权重样本偏好和用户的明确指示。用户输入必须先映射到具体维度,再转译为聚合结论和创作规则。 - -封面 / 首帧聚合时必须输出可复用的视觉生成规则和 AIGC 提示词模板,并说明覆盖多少张封面 / 首帧、哪些元素是共性、哪些是孤例。 - -样本规则: - -- 默认所有 DNA report 权重为 1。 -- 用户可指定某条 report 权重更高,或通过 focus 限制它只影响某些维度。 -- 聚合结论必须说明依据,不把统计表直接当结论。 -- Agent 必须回读 report 与必要转录原文,避免只看数值。 -- 互动数据(播放/点赞/评论/分享/收藏)只能来自用户提供的数据线索或平台记录,不得编造;数据好坏不直接等于内容风格好坏。 - -## DNA 文档到模板的推导 - -DNA 文档完成后,必须把可复用结论转成生产模板: - -- 选题角度转成选题入口和受众关联要求。 -- 标题与文案转成标题类型偏好、话题标签策略,同时选择代表性标题作为示例直接记录到模板中。 -- 封面 / 首帧特征转成封面风格规则和 AIGC 提示词要素。 -- 其余部分按固定语义结构组织为"起、承、转、合、CTA"五个部分(对应短视频的黄金开场、主体推进、高潮转折、收尾、互动引导)。 -- 每个部分写明本段任务、执行方式、必须做和避免项。 -- 固定的是七个模板部分,不是物理时长占比;任一部分可以对应视频中的一个或多个段落,也可以在特定内容形态下弱化。 -- 每个部分的规则必须能从 DNA 文档的 17 维聚合结论推导,不得引入样本和用户输入之外的新规则。 -- 用户输入必须先映射到具体维度,再转译为模板中的执行规则。 - -七个部分与 17 维的推导关系: - -| 模板部分 | 必须吸收的 DNA 维度 | -| --- | --- | -| 选题 | 选题角度、选题与受众关联(观看理由) | -| 标题(含封面) | 标题类型、参考标题、话题标签策略、封面 / 首帧风格与 AIGC 提示词要素 | -| 起 | 3秒钩子、选题角度、口播节奏、语气与人设感、镜头语言 | -| 承 | 视频结构模式、叙事节奏、口播节奏、用词习惯、专业度体现、镜头语言、画面风格 | -| 转 | 冲突与张力、叙事节奏、语气与人设感、口播节奏、声音与BGM | -| 合 | 视频结构模式、签名式标记、语气与人设感、系列化与合集 | -| CTA | 互动设计、选题与受众关联、语气与人设感 | - -推荐形态: - -```markdown -(选题角度推荐:xxx、xx、xxx) -(选题需考虑的受众关联角度:xxx) - -[标题](类型为主:xxx、xx、xx) -(参考:xxx、xxx、xxx) -(话题标签策略:xxx) -(封面 / 首帧风格:xxx) -(封面 AIGC 提示词要素:xxx) - -[起部分] -(本段任务:在 3 秒内给出观看理由) -(钩子类型:xxx) -(开场口播:xxx) -(画面呈现:xxx) -(句式节奏:xxx) -(必须做:xxx) -(避免:xxx) - -[承部分] -(本段任务:xxx) -(推进逻辑:xxx) -(信息密度:xxx) -(镜头与画面:xxx) -(口播表达:xxx) -(证据与素材:xxx) -(必须做:xxx) -(避免:xxx) - -[转部分] -(本段任务:xxx) -(转折触发:xxx) -(冲突与反差:xxx) -(情绪表达:xxx) -(句式节奏:xxx) -(必须做:xxx) -(避免:xxx) - -[合部分] -(本段任务:xxx) -(收束方式:xxx) -(情绪落点:xxx) -(签名式标记:xxx) -(必须做:xxx) -(避免:xxx) - -[CTA部分] -(行动目标:xxx) -(表达方式:xxx) -(时机与位置:xxx) -(受众关联:xxx) -(必须做:xxx) -(避免:xxx) -``` - -## 更新规则 - -- 新视频先形成单条 DNA report,再与历史 report 合并重新聚合。 -- Agent 需要比较新旧结论:保留共性、记录变化、移动孤例。 -- 用户明确指定只参考某一块时,只更新对应维度,其他维度不得静默改写。 -- 用户输入必须转译为 affected dimensions、DNA 修改、template 修改和执行规则;原话只保留在转译来源中。 -- 更新后 DNA 文档与模板必须同步,避免两者表达两套规则。 diff --git a/crews/main/skills/expert-douyin/tools/douyin-style-profiler/scripts/build_style_profile.py b/crews/main/skills/expert-douyin/tools/douyin-style-profiler/scripts/build_style_profile.py index a6919ea9..966d6d1d 100644 --- a/crews/main/skills/expert-douyin/tools/douyin-style-profiler/scripts/build_style_profile.py +++ b/crews/main/skills/expert-douyin/tools/douyin-style-profiler/scripts/build_style_profile.py @@ -33,32 +33,26 @@ } DIMENSION_GROUPS = { - "选题与包装": [ - ("topic-angle", "选题角度"), - ("title-style", "标题与文案"), - ("cover-frame", "封面与首帧"), + "定位与选题": [ + ("positioning-core", "定位与核心传达"), + ("topic-portfolio", "选题组合"), + ("title-packaging", "标题与包装"), + ("bio-profile", "账号简介与主页表达"), ], - "钩子与表达": [ - ("hook", "3秒钩子"), - ("word-habit", "用词习惯"), - ("speech-rhythm", "口播节奏"), - ("tone", "语气与人设感"), + "账号节奏": [ + ("content-form-mix", "内容形式与比例"), + ("publish-cadence", "发布习惯"), + ("high-performer-patterns", "高数据创意模式"), ], - "视觉与制作": [ - ("shot-language", "镜头语言"), - ("visual-style", "画面风格"), - ("sound-design", "声音与BGM"), + "表达与风格": [ + ("visual-language", "视觉语言"), + ("audio-language", "声音语言"), + ("narration-dna", "口播文案 DNA"), ], - "结构与节奏": [ - ("video-structure", "视频结构模式"), - ("narrative-rhythm", "叙事节奏"), - ("conflict-tension", "冲突与张力"), - ("professionalism", "专业度体现"), - ], - "互动与标记": [ - ("interaction-design", "互动设计"), - ("signature", "签名式标记"), - ("series-design", "系列化与合集"), + "互动与制作": [ + ("engagement-conversion", "互动与转化"), + ("series-signature", "系列与签名"), + ("production-pipeline", "制作管线倾向"), ], } DIMENSIONS = [] @@ -70,7 +64,6 @@ ) number += 1 - def split_sentences(text: str) -> list[str]: return [item.strip() for item in SENTENCE_SPLIT.split(text) if item.strip()] @@ -296,6 +289,7 @@ def statistics_markdown(statistics: dict) -> str: lines.extend( [ "", + f"样本覆盖度:`{statistics['report_count']}` 条 report;单条或少量样本不能推导账号比例、发布节奏或高数据共性。", f"权重模式:`{statistics['weighting']}`;总权重:`{statistics['total_weight']}`。", "口播密度(字/分钟)仅在 report 提供 `duration` 时有意义;未提供时该行只是 0 值占位。", "统计只用于辅助聚合,不生成评分;定性判断必须回到各篇 DNA report。", @@ -375,36 +369,31 @@ def load_reports(paths: list[Path]) -> list[dict]: return reports +REPORT_DIMENSION_PROMPTS = { + 'positioning-core': '- 单条观测:本条暴露出的定位、目标人群与核心传达。\n- 账号级判断:单条样本只给候选,不直接判定账号稳定性。', + 'topic-portfolio': '- 单条观测:本条选题类型、入口和系列关系。', + 'title-packaging': '- 单条观测:标题 / 描述 / 封面包装模式;只记模式,不抄成固定字句。', + 'bio-profile': '- 账号级观测:账号简介、主页与置顶表达;单条样本无法观测时写未观测。', + 'content-form-mix': '- 单条观测:本条内容形式。\n- 聚合边界:图文/视频比例与混合节奏必须由多样本或账号级数据推导。', + 'publish-cadence': '- 单条观测:本条发布时间。\n- 聚合边界:时间段、频率和三图文对一视频等节奏必须由多样本或账号级数据推导。', + 'high-performer-patterns': '- 数据线索:记录本条互动/播放/阅读线索。\n- 创意判断:回读选题、包装、形式与创意,不得把高数据直接等同于风格好。', + 'visual-language': '- 视觉证据:有图片或关键帧时由视觉模型读取;无证据写未观测。\n- 边界:只记录账号级稳定视觉语言,不规定逐镜设计。', + 'audio-language': '- 声音证据:来自口播稿、原视频信息或用户说明;无证据写未观测。\n- 边界:只记录音色/语速/声音气质倾向,不规定 TTS 参数。', + 'narration-dna': '- 独立块:记录开头、起承转合、收束、人称与签名表达。\n- 聚合边界:样本不足时保持未启用,不把单条句式上升为账号 DNA。', + 'engagement-conversion': '- 单条观测:平台内行动引导与承接路径。', + 'series-signature': '- 单条观测:栏目、固定表达或识别符号;高频词必须回读原文确认。', + 'production-pipeline': '- 管线映射:只写 Content Producer 已支持的 Pipeline;未确定时写待定。\n- 分界:DNA 指导 main agent 出 Brief,不规定成片制作细节。', +} + def report_dimension_markdown(dimension: dict) -> str: heading = f"### {dimension['number']}. {dimension['name']}" - if dimension["id"] == "cover-frame": - return ( - f"{heading}\n\n" - "**视觉模型分析:**待 Agent 基于封面 / 首帧图补齐。\n\n" - "- 画面主体与场景:待 Agent 补齐。\n" - "- 构图与画幅:待 Agent 补齐。\n" - "- 色彩体系:待 Agent 补齐。\n" - "- 光线与质感:待 Agent 补齐。\n" - "- 风格与媒介:待 Agent 补齐。\n" - "- 文字视觉与图文关系(封面字 / 贴纸 / 标题框):待 Agent 补齐。\n" - "- 品牌识别元素:待 Agent 补齐。\n" - "- 避免项:待 Agent 补齐。\n\n" - "**AIGC 复现提示词要素:**待 Agent 补齐;要求能据此生成风格高度一致的封面图。\n\n" - "**可复用创作信号:**待 Agent 补齐。" - ) - if dimension["id"] == "hook": - return ( - f"{heading}\n\n" - "**钩子原文(前 3 秒口播 / 首帧字幕):**待 Agent 逐字摘录。\n\n" - "**单条结论:**待 Agent 补齐(钩子类型、是否对应定位、正文是否兑现)。\n\n" - "**原文证据:**待 Agent 补齐。\n\n" - "**可复用创作信号:**待 Agent 补齐。" - ) + prompt = REPORT_DIMENSION_PROMPTS.get(dimension["id"], "- 单条观测:待 Agent 补齐。") return ( f"{heading}\n\n" + f"{prompt}\n\n" "**单条结论:**待 Agent 补齐。\n\n" - "**原文证据:**待 Agent 补齐(口播逐字引用或画面描述,注明时间区间)。\n\n" - "**可复用创作信号:**待 Agent 补齐。" + "**原文证据:**待 Agent 补齐(逐字引用、账号信息、发布信息、画面/声音描述或数据线索;注明来源)。\n\n" + "**可复用信号:**待 Agent 补齐;样本不足时写未观测,不推导账号级稳定性。" ) @@ -441,6 +430,8 @@ def report_markdown( "本文件只描述这一条视频。它不是聚合后的 DNA 文档,也不直接作为生产模板。", "## 单条统计", f"- 转录字符:{document['characters']}\n- 口播句子:{document['sentences']}\n- 视频时长:{document['duration'] or '未提供'}\n- 标题候选:{' / '.join(document['title_candidates'])}\n- 来源链接:{source_url or '未提供'}\n- 封面 / 首帧图:{cover_image or '未提供'}", + "## 样本与账号观测", + '- 样本类型:待 Agent 补齐(账号作品 / 用户提供单条)。\n- 账号与简介:待 Agent 补齐;单条样本无法观测时写未观测。\n- 发布时间与时间段:待 Agent 补齐;单条样本只记录本条时间,不推导账号节奏。\n- 内容形式:待 Agent 补齐(口播 / 实拍拼接 / 创意转场 / 纯 AIGC 动画 / 图文 / 混合)。\n- 数据表现线索:待 Agent 补齐;只作证据,不直接判风格好坏。\n- 视频形态与授权信息:待 Agent 补齐(横竖屏、时长、素材来源、授权边界)。', f"## {len(DIMENSIONS)} 维单条分析", "\n\n".join(dimensions), "## 单条边界", @@ -484,19 +475,11 @@ def dna_document_markdown( if body: dimensions.append(f"{heading}\n\n{body}") else: - if dimension["id"] == "cover-frame": - dimensions.append( - f"{heading}\n\n**聚合结论:**待 Agent 补齐。\n\n" - "**报告依据:**待 Agent 列出使用的封面 / 首帧图和 DNA report。\n\n" - "**视觉生成规则:**待 Agent 转成可执行的 AIGC 提示词要素。\n\n" - "**例外与约束:**待 Agent 补齐。" - ) - else: - dimensions.append( - f"{heading}\n\n**聚合结论:**待 Agent 补齐。\n\n" - "**报告依据:**待 Agent 列出使用的 DNA report、权重和 focus。\n\n" - "**创作规则:**待 Agent 补齐。" - ) + dimensions.append( + f"{heading}\n\n**聚合结论:**待 Agent 补齐。\n\n" + "**报告依据:**待 Agent 列出使用的 DNA report、权重和 focus。\n\n" + "**创作规则:**待 Agent 补齐。" + ) report_paths = "\n".join(report["report_path"] for report in reports) existing_user_inputs = ( extract_named_section(previous_dna, "## 用户输入转译区") if previous_dna else None @@ -512,7 +495,7 @@ def dna_document_markdown( f"generated_at: {yaml_value(generated_at())}\n" "---", f"# {dna_id} DNA 文档", - "本文件聚合历史 DNA report。它是账号当前采用的短视频内容与风格规则,也必须能推导出生产模板。", + "本文件聚合历史 DNA report。它是账号当前采用的账号级运营规则,也必须能推导出 main agent 的内容生产 / Brief 模板。", "## 报告与权重", "\n".join( f"- `{report['report_path']}`:weight `{report['weight']}`,focus `{', '.join(report['focus']) or 'all'}`" @@ -574,56 +557,57 @@ def extract_named_section(markdown: str, heading: str) -> str: return "\n".join(body).strip() -TEMPLATE_STAGES = ("起", "承", "转", "合", "CTA") +TEMPLATE_STAGES = ("定位与核心传达", "选题与标题包装", "内容形式与发布节奏", "高数据创意模式", "互动与系列", "制作交接与Pipeline", "口播文案DNA") TEMPLATE_STAGE_FIELDS = { - "起": ( - "本段任务", - "钩子类型", - "开场口播", - "画面呈现", - "句式节奏", - "必须做", - "避免", + "定位与核心传达": ( + "一句话定位", + "目标人群", + "核心传达", + "账号简介与主页表达", + "不变承诺", ), - "承": ( - "本段任务", - "推进逻辑", - "信息密度", - "镜头与画面", - "口播表达", - "证据与素材", - "必须做", - "避免", + "选题与标题包装": ( + "选题组合", + "标题模式", + "封面或首帧包装", + "禁用方向", ), - "转": ( - "本段任务", - "转折触发", - "冲突与反差", - "情绪表达", - "句式节奏", - "必须做", - "避免", + "内容形式与发布节奏": ( + "图文或视频比例", + "发布时间带", + "内容形式混合节奏", + "系列栏目", + ), + "高数据创意模式": ( + "高表现样本共性", + "可复用创意原型", + "触发条件", + "例外", ), - "合": ( - "本段任务", - "收束方式", - "情绪落点", - "签名式标记", + "互动与系列": ( + "互动目标", + "引导方式", + "系列与签名标记", "必须做", "避免", ), - "CTA": ( - "行动目标", - "表达方式", - "时机与位置", - "受众关联", + "制作交接与Pipeline": ( + "MainAgent交付物", + "ContentProducer交付物", + "Pipeline", + "素材与授权", + "风格边界", + ), + "口播文案DNA": ( + "是否启用", + "起承转合结构", + "语言与人称", + "声音倾向", "必须做", "避免", ), } - - def parse_template_fields(body: str) -> dict[str, str]: fields = {} for line in body.splitlines(): @@ -649,25 +633,6 @@ def template_segment(stage: str, values: dict[str, str] | None = None) -> str: return "\n".join(lines) -def extract_topic_title_body(previous_template: str | None) -> str: - if not previous_template: - return "" - for heading in ("## 生产模板", "## 选题与标题"): - body = extract_named_section(previous_template, heading) - if not body: - continue - topic_title_lines = [] - for line in body.splitlines(): - # 只在真正的模板分段([起部分] 等)处截断;[标题] 属于选题与标题区,必须保留。 - if re.match(r"^\[[^\]]+部分\]$", line.strip()): - break - topic_title_lines.append(line) - rendered = "\n".join(topic_title_lines).strip() - if rendered: - return rendered - return "" - - def stage_values_from_template(old_sections: dict[str, str]) -> dict[str, dict[str, str]]: values = {stage: {} for stage in TEMPLATE_STAGES} for heading in sorted(old_sections, key=template_order): @@ -687,36 +652,24 @@ def template_markdown( old_sections = extract_template_sections(previous_template) stage_values = stage_values_from_template(old_sections) segments = [template_segment(stage, stage_values[stage]) for stage in TEMPLATE_STAGES] - topic_title = extract_topic_title_body(previous_template) or ( - "(选题角度推荐:待 Agent 补齐。)\n" - "(选题需考虑的受众关联角度:待 Agent 补齐。)\n" - "\n" - "[标题](类型为主:待 Agent 补齐。)\n" - "(参考:待 Agent 补齐。)\n" - "(话题标签策略:待 Agent 补齐。)\n" - "(封面 / 首帧风格:待 Agent 补齐。)\n" - "(封面 AIGC 提示词要素:待 Agent 补齐。)" - ) - if previous_template: - for field in ("(封面 / 首帧风格:", "(封面 AIGC 提示词要素:"): - if field not in topic_title: - topic_title += f"\n{field}待 Agent 补齐。)" section_defaults = [ ( "## 生产模板", - topic_title + "\n\n" + "\n\n".join(segments), + "\n\n".join(segments), ), ( "## 用户输入转译后的执行规则", "- (来自用户输入:待 Agent 补齐来源。)\n" f"- (影响维度:待 Agent 映射到 {len(DIMENSIONS)} 维 ID。)\n" - "- (执行规则:待 Agent 写成生产时可直接执行的要求。)", + "- (执行规则:待 Agent 写成 Brief 或图文生产时可直接执行的要求。)", ), ( "## 使用检查", - "- 选题与标题是否符合 DNA 文档的选题角度、受众关联和标题类型。\n" - "- 起、承、转、合、CTA 五个部分是否完成各自任务。\n" - "- 每个部分是否反映 DNA 文档中对应的钩子、口播、镜头、节奏、冲突、互动与签名标记。\n" + "- 是否只用一个 DNA,且与本次账号 / 内容任务匹配。\n" + "- 定位、选题、标题包装、核心传达是否来自 DNA 文档。\n" + "- 图文/视频比例、发布节奏和高数据创意是否尊重样本覆盖度;样本不足时是否标注未观测。\n" + "- 视频全案是否只输出 Brief,且 Brief 明确 Pipeline、素材授权、验收标准和交付边界。\n" + "- 口播文案 DNA 是否独立启用;未启用时是否避免规定逐句口播。\n" "- 用户输入是否已转译为具体执行规则。", ), ] @@ -740,7 +693,7 @@ def template_markdown( f"generated_at: {yaml_value(generated_at())}\n" "---", f"# {dna_id} DNA Template", - "本模板是视频生产时直接执行的 production template,必须由 DNA 文档推导;不得引入 DNA 文档未确认的规则。", + "本模板是 main agent 的账号级内容生产 / Brief 输入模板,必须由 DNA 文档推导;不得引入 DNA 文档未确认的规则,也不规定成片制作细节。", *rendered_sections, ] ) + "\n" diff --git a/crews/main/skills/expert-douyin/workflows/account-benchmark.md b/crews/main/skills/expert-douyin/workflows/account-benchmark.md index 310d5849..d8369648 100644 --- a/crews/main/skills/expert-douyin/workflows/account-benchmark.md +++ b/crews/main/skills/expert-douyin/workflows/account-benchmark.md @@ -33,7 +33,7 @@ | 视频链接(对标账号代表作) | self-spawn subagent 走 `viral-chaser`(转录 + 时长 + 标题/描述 + 互动线索 + 关键帧) | | 对标视频评论 | `douyin-comments fetch --url <视频链接>` 直接抓取(默认前 40 条热度评论;摘要落 `douyin/ref/{benchmark-dna-id}/comments/{sample-id}.comments.md`) | | 本地文字稿 / 脚本 | 整理为 `.md` 直接输入 profiler | -| 用户提供的截图 / 数据 | 作为互动信号线索保留,不编造 | +| 用户提供的截图 / 数据 / 账号信息 | 作为互动信号、账号简介、发布时间与内容形式线索保留,不编造 | 选择建议: @@ -41,7 +41,8 @@ 2. 抖音没有账号作品列表抓取工具;请用户提供对标账号的代表性视频链接,或由 `smart-search` 辅助发现候选账号后请用户确认。 3. 账号级对标至少收集 10 条代表性视频;账号可获取视频不足 10 条时,提供全部并说明数量限制。 4. 单条视频可以形成单条观察,但不得当成账号级稳定 DNA;多个样本才分析覆盖率和共性。 -5. 互动数据线索只说明"用户怎么投票",不直接等于内容质量;归因前先按 `review.md` 的混杂因素清单排除账号成熟度、投流、选题热度等干扰。 +5. 每条样本尽量记录账号简介、发布时间、内容形式、素材来源与授权;缺失写「未观测」,不虚构。 +6. 互动数据线索只说明"用户怎么投票",不直接等于内容质量;归因前先按 `review.md` 的混杂因素清单排除账号成熟度、投流、选题热度等干扰。 ### Step 2 - 建立对标 DNA @@ -54,6 +55,7 @@ douyin-style-profiler report \ --sample-id {sample-id} \ --cover-image path/to/frame.jpg \ --source-url "https://www.douyin.com/video/..." \ + --output-dir douyin/dna/{benchmark-dna-id}/reports \ --duration 89 ``` @@ -70,13 +72,13 @@ douyin/dna/{benchmark-dna-id}/{benchmark-dna-id}.dna.md douyin/dna/{benchmark-dna-id}/{benchmark-dna-id}.template.md ``` -对标 DNA template 也必须固定为七个部分:选题、标题(含封面)、起、承、转、合、CTA。后五个部分是语义结构,不限制实际时长占比;每一部分都要能从对标 DNA 文档推导。 +对标 DNA template 使用 v1 账号级语义段:定位与核心传达、选题与标题包装、内容形式与发布节奏、高数据创意模式、互动与系列、制作交接与 Pipeline、口播文案 DNA。每一部分都要能从对标 DNA 文档推导。 ### Step 3 - 模式分析与差异化(agent 推理) 基于对标 DNA 与样本数据,回答三个问题(不下没有证据的结论): -1. **它为什么有效**:对标账号的高表现内容在钩子、结构、人设、互动设计上有什么共性?哪些信号在多条视频中稳定出现? +1. **它为什么有效**:对标账号的高表现内容在选题、标题包装、内容形式、发布节奏、高数据创意与互动设计上有什么共性?哪些信号在多条视频中稳定出现? 2. **相对表现**:同一账号内部,哪类内容明显高于其他条(用用户提供的互动数据判断;无数据时只做内容面分析,不编数据)。 3. **差异化切入点**:我们的账号比对标强在哪、弱在哪?有哪些内容空白或人群空白可以切入?每个切入点说明依据和建议的验证方式(一条视频验证一个变量)。 @@ -97,16 +99,16 @@ douyin/dna/{base-dna-id}/{base-dna-id}.template.md 比较必须覆盖两层: -1. **17 维 DNA 文档**:逐个维度比较规则、证据和适用条件;封面 / 首帧需比较视觉特征与 AIGC 复现要素。 -2. **七部分 template**:逐项比较选题、标题(含封面)、起、承、转、合、CTA 的执行方式。 +1. **13 维 DNA 文档**:逐个比较定位、选题、标题包装、账号简介、内容形式、发布节奏、高数据创意、视觉/声音、口播文案 DNA、互动系列与制作管线。 +2. **template 语义段**:逐项比较定位与核心传达、选题与标题包装、内容形式与发布节奏、高数据创意、互动与系列、制作交接、口播文案 DNA。 -每个维度和模板部分都输出四类结论: +每个维度和模板语义段都输出四类结论: | 类别 | 判断标准 | | --- | --- | | 保持 | 基线已有优势,与目标观众和商业定位一致 | | 引入 | 对标更有效,且不冲突业务事实、合规边界和用户偏好 | -| 局部借鉴 | 只适合钩子、开头、结构、互动等局部场景 | +| 局部借鉴 | 只适合选题、包装、内容形式、发布节奏、Pipeline 等局部场景 | | 不采纳 | 仅依赖孤例、冲突商业定位、风险高或难稳定执行 | 每项至少说明: @@ -120,26 +122,26 @@ douyin/dna/{base-dna-id}/{base-dna-id}.template.md 对比结果不自动更新基线 DNA。用户明确采纳后,选择一种方式: -1. **局部 DNA 融合**:用户决定采纳对标 DNA 的某个维度或模板部分时,直接读取对标 DNA 文档 / template 中对应内容,把它当作用户提供的一条融合要求,更新基线 DNA;不需要重新抓取参考视频,也不需要重新生成对标视频的基线 report。 +1. **局部 DNA 融合**:读取对标 DNA 文档 / template 中对应内容,把它当作用户提供的融合要求,更新基线 DNA。 2. **局部样本借鉴**:仅在用户明确希望引入原视频证据、权重或 focus 时,才将对标视频重新生成属于基线 DNA 的 report,并用 `--focus` 限定采纳维度。 -3. **偏好转译**:用户表达"钩子再冲突一点""开头节奏更快"等要求时,按 `style-dna.md` 的用户输入转译更新,并落到七部分 template 的具体执行字段。 +3. **偏好转译**:用户表达“选题更像对标”“实拍比例提高”等要求时,按 `style-dna.md` 的用户输入转译更新。 4. **明确不改 DNA**:仅作为本次选题或制作参考,不落盘到 DNA。 局部 DNA 融合流程: -1. 明确采纳范围:17 维中的维度、七部分中的模板部分,或两者组合。 +1. 明确采纳范围:13 维中的维度、template 语义段,或两者组合。 2. 读取对标 DNA 文档 / template 的对应规则、适用条件和例外。 -3. 将其整理为一条可转译的输入,必须包含来源 `dna-id`、采纳范围和具体规则。 +3. 整理为一条可转译输入,包含来源 `dna-id`、采纳范围和具体规则。 4. 在基线 DNA 上执行无新增样本的 update: ```bash douyin-style-profiler update \ --dna douyin/dna/{base-dna-id}/{base-dna-id}.dna.md \ --template douyin/dna/{base-dna-id}/{base-dna-id}.template.md \ - --user-input "采纳 {benchmark-dna-id} 的钩子部分:xxx" + --user-input "采纳 {benchmark-dna-id} 的内容形式与发布节奏:xxx" ``` -5. Agent 把该输入视作用户提供的参考要求,转译为基线 DNA 的 affected dimensions、聚合结论、创作规则和七部分 template 执行字段。 -6. 融合时必须检查与基线证据、业务事实、合规边界和用户偏好的冲突;冲突保留说明,不静默覆盖。 +5. Agent 把该输入转译为基线 DNA 的 affected dimensions、聚合结论、创作规则和 template 执行字段。 +6. 融合时检查与基线证据、业务事实、合规边界和用户偏好的冲突;冲突保留说明,不静默覆盖。 -更新后必须同步修订基线 DNA 文档与 template,并保留来源说明。 +更新后同步修订基线 DNA 文档与 template,并保留来源说明。 diff --git a/crews/main/skills/expert-douyin/workflows/account-setup.md b/crews/main/skills/expert-douyin/workflows/account-setup.md index eca8e1b7..3f015177 100644 --- a/crews/main/skills/expert-douyin/workflows/account-setup.md +++ b/crews/main/skills/expert-douyin/workflows/account-setup.md @@ -93,7 +93,7 @@ 2. 目标 DNA 固定为 `dna-0`。 3. 每条生成 report。 4. 运行 `douyin-style-profiler build --dna-id dna-0`,生成 DNA 文档与 template。 -5. 把选题、钩子、结构、口播、可复用规则和例外发送用户确认。 +5. 把定位、选题、标题包装、账号简介、内容形式与比例、发布习惯、高数据创意、口播 DNA 与制作管线发送用户确认。 6. 用户调整意见按 `style-dna.md` 的"用户输入转译"更新到 `dna-0`。 用户没有明确权重时,所有 report 默认权重为 1。 @@ -114,7 +114,7 @@ Agent 基于 `business_knowledge.md` 和关键词地图提炼搜索关键词, #### 路径 C:用户拒绝外部参考或已有明确想法 1. 结合 `business_knowledge.md`、用户定位、目标观众、变现方式和用户思路,提炼 `dna-0`。 -2. Agent 直接按 17 维框架编写: +2. Agent 直接按 13 维账号级框架编写: ```text douyin/dna/dna-0/dna-0.dna.md @@ -123,7 +123,7 @@ douyin/dna/dna-0/dna-0.template.md 3. DNA 文档必须标明来源是"业务信息 + 用户指定方向",没有视频样本统计。 4. 能确定的规则写为执行要求;未确定维度写"待校准",不得虚构单条证据。 -5. Template 仍必须给出可执行的七部分生产结构;不能只有抽象定位词。 +5. Template 仍必须给出可执行的 Brief / 内容生产语义段;不能只有抽象定位词。 6. 将 DNA 摘要和待校准点发给用户确认,再按用户意见转译修订。 后续获得真实视频样本时,按 `style-dna.md` 生成 report 并更新 `dna-0`。 @@ -142,7 +142,7 @@ douyin/dna/dna-0/dna-0.template.md 检查要求: - DNA 文档说明样本来源、覆盖数量、权重、例外和待校准点。 -- Template 能由 DNA 文档推导,直接指导选题、标题、钩子、结构、口播、画面和互动。 +- Template 能由 DNA 文档推导,直接指导定位、选题、标题包装、内容形式、发布节奏、高数据创意、互动与制作交接。 - 用户输入已转译为维度级规则,不是原话堆积。 - 对标样本 report 不混入 `dna-0`;用户采纳后,只有转译后的规则通过局部 DNA 融合进入 `dna-0`。 @@ -171,7 +171,7 @@ douyin/dna/dna-0/dna-0.template.md ### Step 1 - 获取老号样本 1. 抖音没有账号作品列表抓取工具;请用户提供老号最近的代表性视频链接(原则上至少 10 条,账号作品不足 10 条时提供全部并说明)。 -2. 拿到链接后逐条 self-spawn subagent 走 `viral-chaser` 拆解,剔除重复、删除和无效样本;保留标题、描述、时长、互动线索和关键帧。 +2. 拿到链接后逐条 self-spawn subagent 走 `viral-chaser` 拆解,剔除重复、删除和无效样本;保留标题、描述、发布时间、账号简介、内容形式、时长、互动线索和关键帧。 3. 用户也可补充后台截图(播放、完播、粉丝画像),作为诊断线索,不编造。 ### Step 2A - 账号基本可用,用户让 Agent 接手 @@ -186,7 +186,7 @@ douyin/dna/dna-0/dna-0.template.md ### Step 2B - 用户认为账号不太可用,需要推倒重来 -1. 基于 Step 1 拿到的样本提炼出常用署名、核心卖点、选题方向、账号名称、核心 CTA 等基础信息,将这些信息与用户确认。 +1. 基于 Step 1 拿到的样本提炼定位、账号简介、选题组合、标题包装、内容形式、发布节奏、核心卖点与 CTA 等基础信息,将这些信息与用户确认。 2. 结合上述信息,走「从零开始打造新号」流程。 3. 如果 `business_knowledge.md` 不全,可以先从 Step 1 的样本中提取,不足的信息与用户讨论。 diff --git a/crews/main/skills/expert-douyin/workflows/content-production.md b/crews/main/skills/expert-douyin/workflows/content-production.md index 9866558e..b9e41288 100644 --- a/crews/main/skills/expert-douyin/workflows/content-production.md +++ b/crews/main/skills/expert-douyin/workflows/content-production.md @@ -2,7 +2,7 @@ 从选题到发布的完整内容生产。用户说"帮我做条抖音视频""发个抖音""这条照着做一条""这个选题我们也做一条"走这个。 -视频制作分工是硬边界(用户钦定):main agent 只做**基于已有素材的组装与轻剪辑**;出脚本、从零端到端制作一律委托 `content-producer`。本 workflow 的价值在于:用 DNA template 锁定内容与风格要求、编排输入分支与确认节点、衔接制作(自做或委托)、把关质量与发布记录。 +视频制作分工是硬边界:main agent 只做**已有视频素材的简单加工**,并负责长文 / 图文内容;除此之外的视频全案,main agent 只产出 **Brief**,成片制作委托 `content-producer`。本 workflow 的价值在于:用账号级 DNA 锁定定位、选题、包装、内容形式与制作边界,编排输入分支与确认节点,衔接制作并把关发布记录。 ## Step 0 - 入口判断 @@ -54,7 +54,7 @@ douyin/dna/{dna-id}/{dna-id}.dna.md douyin/dna/{dna-id}/{dna-id}.template.md ``` -DNA template 是选题、标题、钩子、结构、口播、画面和互动路线的直接执行依据;DNA 文档用于理解稳定性、例外和适用条件。委托 `content-producer` 时,把 template 的执行要求写进制作简报,让制作方按 DNA 生产。 +DNA template 是 main agent 的内容生产 / Brief 输入模板,覆盖定位与核心传达、选题与标题包装、内容形式与发布节奏、高数据创意、互动系列、制作交接与可选口播文案 DNA。它不规定镜头表、逐句台词、转场或编码细节。委托 `content-producer` 时,把 template 的账号级约束转成 Brief;具体制作方案由 CP 在指定 Pipeline 内完成。 ### 2. 读取业务知识 @@ -91,7 +91,7 @@ DNA template 是选题、标题、钩子、结构、口播、画面和互动路 > Agent 的一般内容判断 ``` -DNA 约束的是选题、钩子、结构、口播、画面和互动方式;不能覆盖用户指定素材、事实、合规边界和转化要求。 +DNA 约束的是账号定位、选题组合、标题包装、核心传达、内容形式、发布节奏、互动与制作边界;口播文案 DNA 只在明确启用时约束口播。DNA 不能覆盖用户指定素材、事实、合规边界和转化要求。 ## Step 2 - 素材获取与整理 @@ -172,20 +172,28 @@ DNA 约束的是选题、钩子、结构、口播、画面和互动方式;不 ### 路线 A:素材组装 / 轻剪辑(main 直接做) -按 DNA template 的起承转合 CTA 结构编排素材: +只处理已有素材的简单加工,不升格为全案制作: -1. 按 template 各部分的画面与口播要求,把素材整理成剪辑顺序清单(哪段素材对应哪个部分、配什么口播/字幕)。 +1. 按 DNA 的核心传达、内容形式与互动目标整理剪辑顺序清单;若口播文案 DNA 已启用且需要新口播,由 main 先写口播稿。 2. 口播类素材去口气词、剪高光 -> `talking-head-cut`。 3. 抽段拼接、加旁白 / BGM、烧字幕、编号合成 -> `video-edit`。 4. 素材缺口经 AIGC 片段(`aigc-video-gen`)或免费素材库(`pexels-footage` / `pixabay-footage`)补充,补充素材在清单中标注来源。 5. 产品操作演示 -> `ui-demo` 录制。 +6. 若需求超出“简单加工”(需要重写叙事、全新分镜、全案生成),停止自做,改走 Brief 委托路线。 ### 路线 B / C:委托 content-producer 制作 -1. 产出**制作简报** `douyin/outputs//brief.md`,至少包含:选题与观看理由、标题与文案、DNA template 各部分执行要求(钩子类型与开场口播、结构与节奏、画面与镜头、声音、互动引导)、素材清单(如有)、时长带、验收标准(钩子兑现、结构完整、DNA 检查项)。 +1. 产出**制作简报** `douyin/outputs//brief.md`。Brief 是 main / CP 的唯一分界点,至少包含: + - 选题、观看理由、标题与文案、核心传达 + - 账号级 DNA 约束:内容形式、视觉/声音倾向、互动目标、禁用方向 + - `pipeline`:已确定则写 Content Producer 支持的 Pipeline(如 `dna-ad-video-pipeline`);未确定则省略,由 CP 自由发挥 + - 素材清单、来源与授权 + - 时长带、横竖屏、交付物与验收标准 + - 若口播文案 DNA 已启用:main 写好的口播终稿,CP 不得重写策略文案 2. 参考模式附上 `viral-chaser` 拆解报告路径,作为 brief 的一部分。 -3. spawn `content-producer` 作为 subagent(或提示用户直接委托 content-producer),由其出脚本并完成端到端制作;脚本讨论也直接找 content-producer,main 不代写完整脚本。 -4. 跟进制作进展,避免长时间卡住;成品视频回传 `douyin/outputs//`。 +3. spawn `content-producer`,由其按 Brief 完成制作;Brief 之外的制作方案、分镜与实现细节归 CP。 +4. Brief 变更时更新版本并推送变更要点;已开工中间产物按新版取舍,弃用部分记入交付说明。 +5. 成品视频回传 `douyin/outputs//`。 ## Step 6 - 成片自检 @@ -197,8 +205,8 @@ DNA 约束的是选题、钩子、结构、口播、画面和互动方式;不 ## Step 7 - 封面 -1. 读取 DNA template 的封面 / 首帧风格和封面 AIGC 提示词要素。 -2. 结合最终标题、目标观众和本条核心收益,补齐本次封面的主体、场景、构图、色彩、文字视觉和负向约束。 +1. 读取 DNA template 的标题包装与视觉语言,只取账号级风格边界,不搬逐镜制作细节。 +2. 结合最终标题、目标观众和本条核心收益,确定本次封面的主体、色彩、文字视觉和负向约束。 3. 优先从成片中选帧作为封面(与内容一致);需要更强视觉冲击时用 `siliconflow-img-gen` 生成。 4. 用户已提供封面时直接使用;用户提供素材不足时作为生成参考。 5. 封面图发用户确认,确认后保存为 `douyin/outputs//cover.jpg`。 diff --git a/crews/main/skills/expert-douyin/workflows/editing.md b/crews/main/skills/expert-douyin/workflows/editing.md index 48a73852..92ecb994 100644 --- a/crews/main/skills/expert-douyin/workflows/editing.md +++ b/crews/main/skills/expert-douyin/workflows/editing.md @@ -10,16 +10,16 @@ | 用户的说法 | 改的层级 | 怎么走 | |-----------|---------|--------| -| "改下标题 / 简介 / 话题" | 文案层 | 按 DNA template 标题与文案维度重写,直接给候选 | +| "改下标题 / 简介 / 话题" | 文案层 | 按 DNA `title-packaging` 重写,直接给候选 | | "剪紧一点 / 去口气词 / 把高光剪出来" | 轻剪层 | `talking-head-cut`(去口气词、结巴、静音,按发言内容剪高光) | | "加 BGM / 加字幕 / 补片头片尾 / 插一段素材" | 加工层 | `video-edit`(audio-mix / subtitles / extract / assemble) | | "结构调一下 / 把第二段提前 / 换个讲法" | 结构层 | 先出调整方案(新的段落顺序与时点)-> 确认 -> `video-edit` 重剪 | -| "换个封面" | 封面层 | 成片抽帧或 `siliconflow-img-gen` 按 DNA template 封面规则重做 | +| "换个封面" | 封面层 | 成片抽帧或 `siliconflow-img-gen` 按 DNA `visual-language` 重做 | | "换个风格 / 方向不对" | 方向层 | 回到 `content-production.md` 从选题重新走 | ## 文案层 -标题、简介、话题标签按目标 DNA template 的标题与文案维度重写: +标题、简介、话题标签按目标 DNA `title-packaging` 重写: 1. 目标 DNA 不确定时先确认(用户指定或默认 `dna-0`)。 2. 标题 ≤ 30 字;简介与话题按 template 策略;禁止引流信息。 @@ -38,13 +38,13 @@ ## 结构层 先出结构调整方案(新的段落顺序、每段时点、删留内容),用户确认后再动剪。 -结构动了通常钩子和节奏也要跟着调——按目标 DNA template 的起、承、转、合重新核对各段任务,别只动骨架不换节奏。 +结构调整先核对核心传达与内容形式;若口播文案 DNA 已启用,再按 `narration-dna` 核对口播结构。不要把账号级 DNA 扩写成逐镜规则。 ## 换封面 1. 用户直接指定封面图 -> 直接使用。 2. 用户说"从片子里选一帧" -> 抽帧候选给用户挑。 -3. 用户说"重做一张" -> 按 DNA template 封面 / 首帧风格与 AIGC 提示词要素,用 `siliconflow-img-gen` 生成,用户确认后替换 `cover.jpg`。 +3. 用户说"重做一张" -> 按 DNA `visual-language` 与本次标题/核心收益,用 `siliconflow-img-gen` 生成,用户确认后替换 `cover.jpg`。 ## 改完必做 diff --git a/crews/main/skills/expert-douyin/workflows/review.md b/crews/main/skills/expert-douyin/workflows/review.md index 1b3da437..697b76ab 100644 --- a/crews/main/skills/expert-douyin/workflows/review.md +++ b/crews/main/skills/expert-douyin/workflows/review.md @@ -34,17 +34,17 @@ - 用户级留存数据不可得。 - 用户可提供创作者中心后台截图(完播、粉丝画像、流量来源),作为更高置信度的证据;没有就用库内指标。 -### 互动漏斗 → template 七部分 → 17 维映射 - -| 漏斗卡点 | 先怀疑的 template 部分 | 可回溯的 DNA 维度 | -|---------|----------------------|------------------| -| 播放低(推荐/点击瓶颈) | 选题、标题(含封面) | topic-angle、title-style、cover-frame | -| 点击后快速划走(完播估算低) | 起(钩子)、承 | hook、video-structure、narrative-rhythm、speech-rhythm | -| 点赞低 | 承、合(价值感与情绪落点) | professionalism、conflict-tension、tone | -| 评论低 | CTA、互动设计 | interaction-design、conflict-tension | -| 分享低 | 选题、合(社交货币) | topic-angle、conflict-tension | -| 收藏低 | 承(实用价值密度) | professionalism、video-structure | -| 关注转化低 | 合、CTA | signature、series-design、tone | +### 互动漏斗 → template 语义段 → 13 维映射 + +| 漏斗卡点 | 先怀疑的 template 语义段 | 可回溯的 DNA 维度 | +|---------|--------------------------|------------------| +| 播放低(推荐/点击瓶颈) | 定位与核心传达、选题与标题包装 | positioning-core、topic-portfolio、title-packaging | +| 点击后快速划走(完播估算低) | 内容形式与发布节奏、口播文案 DNA | content-form-mix、narration-dna、high-performer-patterns | +| 点赞低 | 高数据创意模式、互动与系列 | positioning-core、high-performer-patterns、engagement-conversion | +| 评论低 | 互动与系列 | engagement-conversion、series-signature | +| 分享低 | 选题与标题包装、高数据创意模式 | topic-portfolio、high-performer-patterns | +| 收藏低 | 高数据创意模式 | high-performer-patterns、positioning-core | +| 关注转化低 | 定位与核心传达、互动与系列 | bio-profile、series-signature、engagement-conversion | ### 平台混杂因素(归因前必排) @@ -84,7 +84,7 @@ content-calibrator eval --platform douyin --dna-id # 指定 DNA 1. 判定只看比值与走向,绝对值只作上下文。 2. 逐条排除平台混杂因素,输出替代假设检验结果。 -3. 回读 `douyin/dna//.dna.md` / `.template.md` 与待评估作品原文(`source_folder` 下的转录 / 简报),把趋势变化落到 template 七部分与 17 维。 +3. 回读 `douyin/dna//.dna.md` / `.template.md` 与待评估作品原文(`source_folder` 下的转录 / Brief / 口播终稿),把趋势变化落到 template 语义段与 13 维。 ### Step 3 - 报告与标记 diff --git a/crews/main/skills/expert-douyin/workflows/style-dna.md b/crews/main/skills/expert-douyin/workflows/style-dna.md index b7a37b14..feffe0a8 100644 --- a/crews/main/skills/expert-douyin/workflows/style-dna.md +++ b/crews/main/skills/expert-douyin/workflows/style-dna.md @@ -1,43 +1,36 @@ -# 抖音内容 DNA 创建与更新 Workflow +# 抖音账号级 DNA 创建与更新 Workflow -本 Workflow 只负责内容 DNA 三层产物的创建与更新:判断样本来源、选择目标 DNA、获取视频样本材料(`viral-chaser` 拆解)、调用 `douyin-style-profiler` 生成或更新 report / DNA 文档 / template。17 维提取与聚合方法以 `douyin-style-profiler` 为准(维度框架见其 `references/style-17d-framework.md`,初始版本已确认)。 +本 Workflow 负责 DNA report、DNA 文档与 DNA template 的创建与更新。维度框架以 `douyin-style-profiler` 的 `references/account-dna-framework.md` 为准(账号级 DNA v1)。 -边界说明: +## 边界 -- 账号初始化、定位梳理与默认 `dna-0` 初始化走 `account-setup.md`(该 workflow 会调用本 workflow 的样本获取与更新机制)。 -- 账号对标分析走 `account-benchmark.md`;本 workflow 只承担对标 DNA 的三层产物生成与更新,以及采纳后并入基线 DNA 的更新。 -- DNA 如何被用于内容生产(含仿照创作、素材组装、委托制作)由 `content-production.md` 规定,改片与文案调整由 `editing.md` 规定;本 workflow 不描述生产过程。 - -DNA 描述选题、钩子、口播表达、视觉制作、结构节奏与互动标记等内容规则;不描述发布操作与数据复盘。 +- DNA 是账号级运营框架,不是单条视频制作细节。 +- 账号初始化与默认 `dna-0` 建立走 `account-setup.md`;对标样本先走 `account-benchmark.md`。 +- DNA 如何用于内容生产走 `content-production.md`;改片走 `editing.md`;数据复盘走 `review.md`。 +- DNA 指导 main agent 出内容或视频 Brief;全片制作委托 `content-producer`。 ## 入口判断 走本 Workflow: -- "建 DNA / 更新 DNA / 提炼这条视频的风格" -- "把这条视频落到某个 DNA 上" -- "提取下这个账号的内容 DNA" +- “建 DNA / 更新 DNA / 提炼这个账号的风格” +- “把这条视频落到某个 DNA 上” +- “用户提供单条或多条视频,帮我提炼可复用模式” 不走本 Workflow: -- "帮我做条视频 / 照着这条仿一条 / 这个选题我们也做一条"(使用 DNA 生产)-> 走 `content-production.md` -- "改改这条的文案 / 重新剪一下" -> 走 `editing.md` -- "看数据 / 复盘 / 评估这个 DNA" -> 走 `review.md` -- "起号 / 梳理账号定位" -> 先走 `account-setup.md` -- "分析对标账号 / 对比风格差异" -> 走 `account-benchmark.md` +- 做内容 / 仿内容 → `content-production.md` +- 改文案 / 重剪 → `editing.md` +- 看数据 / 评估 DNA → `review.md` +- 起号 / 定位 → `account-setup.md` +- 对标比较 → `account-benchmark.md` ## 目标 DNA 选择 -1. 用户明确指定 `dna-id` 时,使用该 DNA。 -2. 用户明确说"落到默认 DNA"或未指定目标时,使用 `dna-0`。 -3. 用户意图是账号对标、模式对比时,走 `account-benchmark.md`,对标样本必须进入独立 `dna-id`,不得直接写入 `dna-0`。 -4. 找不到目标 DNA 文档时,先走 `account-setup.md` 建立 `dna-0`,或按用户明确指定的新 `dna-id` 初始化。 - -默认规则: - -- 除 `account-benchmark.md` 分流的对标样本外,未特别分流的新参考视频累积到 `dna-0`。 -- `dna-0` 不代表某个参考账号,而是当前工作区的默认内容生产规则集。 -- 用户可以随时把样本、偏好或局部借鉴明确落到任意已有 DNA。 +1. 用户指定 `dna-id` 时使用该 DNA。 +2. 未指定或说“默认 DNA”时使用 `dna-0`。 +3. 对标样本必须进入独立 DNA,不直接写入 `dna-0`;采纳后再通过局部融合更新。 +4. 目标 DNA 不存在时,先走 `account-setup.md` 或按用户明确指定的新 `dna-id` 初始化。 ## 存储结构 @@ -51,42 +44,37 @@ douyin/dna/{dna-id}/ {dna-id}.template.md ``` -- 原始转录文本可以临时保存在 `douyin/ref/{dna-id}/transcripts/`,也可以来自用户提供的路径。 -- 生成后的 DNA report 必须进入目标 DNA 的 `reports/` 目录。 -- `sample-id` 必须可读且稳定;覆盖同名 report 前,先向用户说明该文件会被重算。 +原始转录可临时放在 `douyin/ref/{dna-id}/transcripts/`。生成后的 report 必须进入目标 DNA 的 `reports/` 目录;覆盖同名 report 前先向用户说明。 ## 样本获取 -抖音样本的观测物是视频:口播转录、画面、封面、时长与互动线索。获取方式: - | 来源 | 处理 | | --- | --- | -| 抖音视频链接(`v.douyin.com` / `www.douyin.com/video/...`) | self-spawn subagent 走 `viral-chaser` 拆解,产出转录全文、分句时间戳、时长、标题/描述/作者、互动计数与关键帧 | -| 用户提供的文字稿 / 脚本 / 口述要点 | Agent 整理为 `.md` / `.txt` 后直接作为 profiler 输入 | -| 本地视频文件 | 没有转录时请用户提供文字稿,或确认是否先委托转写;不得凭空编造转录 | -| 用户直接的想法 / 偏好 | 不生成 report,按"用户偏好"转译进入目标 DNA | +| 抖音视频链接 | self-spawn subagent 走 `viral-chaser`,取得转录、时长、标题/描述、互动线索与关键帧 | +| 用户提供的文字稿 / 脚本 | 整理为 `.md` / `.txt`,保留用户提供的数据与账号线索 | +| 本地视频文件 | 需用户提供文字稿或确认转写;不得凭空编造转录 | +| 用户想法 / 偏好 | 不生成 report,按用户输入转译进入 DNA | -处理样本时: +每条样本尽量收集: -1. `viral-chaser` 产物转成样本材料:转录全文(含标题、描述、作者、时长、互动数据线索)整理为一个转录 `.md`,标题写在一级标题;关键帧中的封面 / 首帧图(或封面下载图)作为 `--cover-image`。 -2. 剔除重复、已删除、正文缺失或纯广告样本。 -3. 保留来源 URL、作者、发布时间和获取时间作为报告线索。 -4. Profiler 本身不限制样本量;一个样本生成单条 report,多个样本聚合统计。账号级初始化、老号诊断和对标比较可在 `account-setup.md` / `account-benchmark.md` 设置最低样本要求。 -5. 互动数据(播放/点赞/评论/分享/收藏)只来自 `viral-chaser` 返回或用户提供的数据线索,不得编造;选样本时可参考互动信号,但数据好坏不直接等于风格好坏。 -6. 封面 / 首帧缺失时保留缺失状态,不得用正文帧或想象补齐视觉证据。 +- 样本类型:账号作品 / 用户提供单条 +- 账号名、简介、主页承诺 +- 发布时间、内容形式、素材来源与授权 +- 播放、点赞、评论、分享、收藏等数据线索 +- 横竖屏、时长、口播/实拍/AIGC 形态 -## 建立或重建 DNA +缺失字段写「未观测」。数据只作证据,不自动判断风格好坏。 -适用场景:目标 DNA 下还没有 report,或用户要求基于当前输入整体重建。 +## 建立或重建 DNA ### Step 1 - 准备样本 -把可用视频整理为转录 `.md`(含封面 / 首帧图路径与时长),并确定目标 `dna-id`。未指定时使用 `dna-0`。 +1. 把视频整理为转录 `.md`,首个一级标题写标题/描述。 +2. 确定目标 `dna-id`、`sample-id`、样本权重和 focus。 +3. 整理账号观测信息,供 Agent 补进 report。 ### Step 2 - 生成单条 report -每条视频执行一次: - ```bash douyin-style-profiler report \ --input path/to/transcript.md \ @@ -94,15 +82,16 @@ douyin-style-profiler report \ --sample-id {sample-id} \ --cover-image path/to/cover.jpg \ --source-url "https://www.douyin.com/video/..." \ - --duration 89 + --duration 89 \ + --output-dir douyin/dna/{dna-id}/reports ``` -可选参数: - -- `--weight N`:用户明确说某条参考价值非常高时使用。 -- `--focus DIMENSION`:用户明确说只借鉴钩子、开头、结构等局部时使用,可重复传入。 +生成 scaffold 后必须: -Agent 生成 scaffold 后必须回读转录原文(必要时回看关键帧),补齐 17 维单条结论、原文证据和可复用信号;钩子维度必须逐字摘录前 3 秒口播 / 首帧字幕;封面 / 首帧维度必须由视觉模型读取本地图片,输出可复现的 AIGC 提示词要素。 +1. 补齐「样本与账号观测」。 +2. 回读转录原文,补齐 13 维的单条结论、证据和可复用信号。 +3. 单条样本不得推导账号比例、发布节奏或高数据共性;相关字段写「未观测」。 +4. 视觉语言必须有图片/关键帧证据;口播文案 DNA 样本不足时保持未启用。 ### Step 3 - 聚合 DNA @@ -110,89 +99,55 @@ Agent 生成 scaffold 后必须回读转录原文(必要时回看关键帧) douyin-style-profiler build --dna-id {dna-id} ``` -Agent 聚合时必须读取全部 report,结合权重、focus、高频共性、高权重偏好、孤例和例外,修订: - -```text -douyin/dna/{dna-id}/{dna-id}.dna.md -douyin/dna/{dna-id}/{dna-id}.template.md -``` - -Template 聚合要求: +Agent 必须读取全部 report,按权重/focus 聚合: -1. 固定输出七个部分:选题、标题(含封面)、起、承、转、合、CTA。 -2. 选题和标题保持 profiler 规定的形态(选题角度推荐、受众关联角度;标题类型、参考标题、话题标签策略、封面 / 首帧风格与 AIGC 提示词要素)。 -3. 起、承、转、合、CTA 是固定语义部分(对应黄金开场、主体推进、高潮转折、收尾、互动引导),不是固定时长占比;每个部分可对应视频中的一个或多个段落。 -4. 每个部分必须写入 DNA 文档中对应维度的执行要求,包括本段任务、执行方式、必须做和避免项。 -5. 所有模板规则必须能从 DNA 文档推导;Agent 不得为了填满七部分而编造样本没有的规则。 - -多个样本时,定性结论必须说明覆盖多少条;单条特征只能写成单条观察,不得伪装成稳定共性。 +- 高频共性、高权重偏好、局部借鉴、孤例、例外分开写。 +- 标注样本覆盖度;单条/少量样本不得称为稳定账号 DNA。 +- 高数据内容要回读创意与内容形式,不能只归因播放量。 +- 为每个维度写聚合结论、报告依据和可执行规则。 +- 确保 DNA 文档能推导 template。 ## 更新已有 DNA -适用场景:目标 DNA 已存在,新增样本、偏好、局部借鉴或表现反馈。 - ### 新增样本 -1. 先为新视频生成属于目标 DNA 的 report。 -2. 再执行: +生成新 report 后运行: ```bash douyin-style-profiler update \ - --input douyin/dna/{dna-id}/reports/{sample-id}.report.md \ + --input douyin/dna/{dna-id}/reports/{new-sample}.report.md \ --dna douyin/dna/{dna-id}/{dna-id}.dna.md \ --template douyin/dna/{dna-id}/{dna-id}.template.md ``` -3. Agent 根据新的加权统计和 17 维证据,同步修订 DNA 文档与 template。 +再由 Agent 重新审视聚合结论并同步修订 template。 ### 用户偏好 -用户意见不是直接入库的规则。Agent 必须先理解其指向,再转译到具体维度和执行要求,例如"开头冲突再前置一点"应落到 3秒钩子、视频结构模式、口播节奏等维度。 - -来自另一个 DNA 文档或 template 的局部结论,也按用户提供的参考要求处理:必须记录来源 `dna-id`、采纳范围、具体规则和冲突说明,再转译到当前 DNA 的对应维度与七部分 template。它不要求重新抓取或重新提取原视频。 - -可传入: - -```bash ---user-input "开头冲突再前置一点" -``` - -转译结果写入 DNA 文档的"用户输入转译区",并以可执行规则进入 template 的对应部分。原话不能成为 template 里的抽象口号。 +用户偏好不直接入库。通过 `--user-input` 传入后,Agent 映射到具体维度,并把原话转译为账号级规则与 Brief 规则。 ### 局部借鉴 -用户只希望借鉴某条视频的钩子、开头或结构时: - -1. 为该视频生成目标 DNA 的 report。 -2. 用 `--focus` 限定参与影响的维度。 -3. 更新 DNA 文档与 template,并在报告中保留来源和 focus 说明。 +对标 DNA 的局部规则必须先说明来源、适用条件和影响维度,再融合进目标 DNA;不得整包照搬。 ### 表现反馈 -来源:`content-calibrator` 的 DNA 表现评估报告(`douyin/dna/{dna-id}/evals/*.eval.md`)。评估回答"这个 DNA 好不好、哪些部分好/不好",本 workflow 负责把**用户确认采纳**的评估结论转译进 DNA。 - -1. 前提:评估报告已存在,且用户逐条确认了要采纳的建议(未确认的建议不动 DNA)。 -2. 每条采纳建议按参考输入处理:转译到具体维度和执行要求(如"播放稳定但评论持续走低,结尾提问太泛"→ 互动设计维度 + CTA 部分表达方式),经 `douyin-style-profiler update --user-input` 传入。 -3. 转译结果写入 DNA 文档的「表现反馈区」(与「用户输入转译区」并列),每条记录:来源 eval 文件、affected dimensions、DNA 文档修改、template 修改。 -4. 同步修订 template 对应部分;表现反馈只改规则表达,不引入样本未覆盖的新风格。 -5. 趋势类证据(比值走向)只支持方向性调整(加强/弱化既有规则),不支持凭空新增维度规则——新增规则仍需样本或用户输入支撑。 +复盘产生的建议先列证据与影响维度,经用户确认后写入 DNA;不得把一次数据波动直接升格为账号规则。 ## DNA 使用接口 -本 Workflow 不描述如何用 DNA 生产:生产 workflow 自行读取 `douyin/dna/{dna-id}/{dna-id}.dna.md` 与 `{dna-id}.template.md`,按 template 七部分执行,见 `content-production.md` / `editing.md`。 - -反馈回流判定:来自生产、改片或复盘的成品修改意见,先判断是否可复用偏好——只有可复用偏好才经本 workflow「更新已有 DNA」进入 DNA;单次修改留在稿件审阅记录,不动 DNA。参考视频的风格吸收("把这条的风格融入到我们的 DNA")属于本 workflow 的新增样本 / 局部借鉴场景,不是生产流程的一部分。发布数据驱动的风格优化走「表现反馈」:`content-calibrator` 评估产出建议 → 用户逐条确认 → 本 workflow 转译进 DNA;评估本身不改 DNA。 +- **内容生产**:读取 DNA 文档与 template,确定定位、选题、标题包装、内容形式、发布节奏和高数据创意。 +- **视频全案**:main agent 产出 Brief;Brief 写明 Pipeline、素材授权、验收标准、交付边界。未指定 Pipeline 时 CP 自由发挥,指定时必须采用。 +- **口播类视频**:若口播文案 DNA 已启用,main agent 写口播文案并随 Brief 交付;CP 不重写策略文案。 +- **图文/长文**:main agent 直接生产。 ## 对标接口 -对标账号或一组对标视频不得默认落入现有 DNA;分析流程、逐项对比与采纳判断以 `account-benchmark.md` 为准。本 Workflow 只承担两件事: - -1. 对标 DNA 的三层产物生成与更新:使用独立 `dna-id`(如 `dna-benchmark-{slug}`,同组后续更新复用),样本获取、report、build/update 同本 workflow 的创建/更新流程。 -2. 用户采纳后,按「更新已有 DNA」执行并入基线 DNA 的更新:局部 DNA 融合、局部样本借鉴或偏好转译;默认优先局部 DNA 融合,不要求重新提取原视频。 +对标样本进入独立 `dna-id`;比较时输出定位、选题、标题包装、内容形式、发布节奏、高数据创意与制作管线的差异。用户明确采纳后才融合进 `dna-0`。 ## 编排原则 -- Workflow 负责选择路径和衔接工具,不复制 profiler 的 17 维定义。 -- Agent 判断必须回读转录原文和 report,不能只依赖统计表。 -- 样本统计描述内容模式,不替代事实核查、合规审核和商业判断。 -- 用户确认只用于初始化方向或采纳建议,不是登记资产的前置门槛。 +- 一个生产任务只使用一个 DNA;需要融合时先更新 DNA。 +- 样本、用户输入、数据反馈必须可追溯。 +- 账号级结论必须有覆盖度;不足就写未观测。 +- Template 只写 main agent 可执行的输入/Brief 规则,不写成片制作细节。 diff --git a/crews/main/skills/expert-wx-channel/SKILL.md b/crews/main/skills/expert-wx-channel/SKILL.md index 2dc7c9ac..7a49beab 100644 --- a/crews/main/skills/expert-wx-channel/SKILL.md +++ b/crews/main/skills/expert-wx-channel/SKILL.md @@ -1,12 +1,12 @@ --- name: expert-wx-channel -description: 微信视频号运营专家。承接从定位起号、视频选题脚本、制作发布到数据复盘的完整运营工作。零散的发布、取数等操作也可以直接做。 +description: 微信视频号账号运营专家。承接定位起号、账号级 DNA、选题包装、已有素材轻加工、视频全案 Brief、发布与数据复盘;全片制作委托 content-producer。 metadata: openclaw: emoji: 📺 --- -# 微信视频号运营专家 +# 微信视频号账号运营专家 ## 预设 Workflow @@ -15,7 +15,7 @@ metadata: | 场景 | Workflow | 什么时候触发 | |------|----------|-------------| | 内容 DNA 管理 | Style DNA | 建 / 更新内容 DNA(样本、偏好、局部借鉴、对标融合),决定样本落到哪个 DNA | -| 内容生产 | Content Production | 做一条 / 做几条视频号视频;输入可以是粗略想法、参考视频(仿照 / 同主题改写)或已有脚本草稿 | +| 内容生产 | Content Production | 做一条 / 做几条视频号内容;main 直接做已有素材轻加工,视频全案只产出并委托 Brief | | 起号与定位 | Account Setup | 新号起号、定位梳理、内容支柱搭建、冷启动方案、老号接手与诊断 | | 账号对标 | Account Benchmark | 对标账号 / 对标视频分析,并与默认或指定 DNA 逐项比较 | | 改稿与调整 | Editing | 改脚本、润色口播、换钩子、换风格、换封面、压缩时长 | @@ -37,18 +37,20 @@ metadata: | 工具 | 用途 | 命令 | |------|------|------| -| `wx-channel-style-profiler` | 生成单条视频 16 维 DNA report(维度 v0),并聚合 DNA 文档与 DNA template | `wx-channel-style-profiler` | +| `wx-channel-style-profiler` | 生成单条视频的账号级 DNA report,并聚合 DNA 文档与 Brief template | `wx-channel-style-profiler` | | `wechat-channels-publish` | 发布视频到视频号创作者中心(camoufox-cli 持久化 session `wechat-channel`) | 无 wrapper,按工具说明驱动 `camoufox-cli` | | `wx-channel-engagement` | 视频号助手后台作品数据抓取,写入 published-track 的 `pub_wx_channel` 表 | `wx-channel-engagement` | 跨领域通用技能:`published-track`(发布记录与指标库)、`content-calibrator`(DNA 表现评估)、`smart-search`(跨平台搜索,选题调研优先社交平台)、`council`(定位决策辅助)、`siliconflow-img-gen`(封面图生成)。 -视频制作链路(内容生产时按需编排,不属于本专家包):`content-producer` subagent(从脚本端到端制作成片)、`video-edit`(已有素材加工拼接)、`talking-head-cut`(口播轻剪辑)、`viral-chaser`(抖音/B站/小红书视频追爆拆解)。 +视频制作链路(内容生产时按需编排,不属于本专家包):`content-producer` subagent(吃 Brief 制作成片)、`video-edit`(已有素材加工拼接)、`talking-head-cut`(口播轻剪辑)、`viral-chaser`(抖音/B站/小红书视频追爆拆解)。 + +**分工硬边界**:main 只做已有视频素材的简单加工和长文 / 图文内容;视频全案只产出 Brief 并委托 `content-producer`。口播类视频若 DNA 启用口播文案 DNA,口播文案由 main 写好并随 Brief 交付;Content Producer 只负责声画制作。Brief 指定 Pipeline 时 CP 必须采用,未指定时 CP 自由发挥。 ## 平台速查 - 视频号核心引擎是**社交推荐 > 算法推荐**:分享(转发朋友圈/群聊)权重高于点赞;判断内容健康度交叉看「完播 × 分享」。 -- 视频号作品**没有标题概念**:描述文案(≤300 字,含 hashtag)就是作品展示文本;`published-track record --platform wx_channel --title` 必须传完整描述文案,不要传短标题。 +- 视频号作品**没有标题概念**:视频简介(≤300 字,含 hashtag)就是作品展示文本;`published-track record --platform wx_channel --title` 必须传完整视频简介;`--title` 只是数据库字段名,不代表平台标题。 - 发布与取数共用持久化 session `wechat-channel`(fail-first 队列):读到「session 正忙」就等当前操作完成再重试,不自动 close。 - 前 3 秒决定去留:封面三要素(身份 + 痛点 + 解决方案),前 2 秒抛冲突,第 3 秒预告价值。 - 真人出镜占比建议 ≥ 60%;起号期前 5 条必须垂直打透一个定位,周更 3-5 条。 @@ -58,7 +60,9 @@ metadata: ## 风格与 DNA -账号内容风格 DNA 存储目录是 `wx_channel/dna/`。未指定 DNA 时默认使用并更新 `dna-0`。生产前同时读取 DNA 文档与 DNA template;对标分析先建立独立对标 DNA,不默认写入 `dna-0`。 +账号级 DNA 存储目录是 `wx_channel/dna/`。未指定 DNA 时默认使用并更新 `dna-0`。生产前同时读取 DNA 文档与 DNA template;对标分析先建立独立对标 DNA,不默认写入 `dna-0`。 + +DNA 是账号级框架:定位与核心传达、选题组合、视频简介包装、账号简介、内容形式比例、发布习惯、高数据创意、视觉/声音倾向、口播文案 DNA、社交分享闭环、互动系列与制作管线。它指导 main agent 出内容或 Brief,不规定成片制作细节。维度框架 v1 位于 `wx-channel-style-profiler` 的 `references/account-dna-framework.md`。 ## 数据与记录 diff --git a/crews/main/skills/expert-wx-channel/tools/wechat-channels-publish/SKILL.md b/crews/main/skills/expert-wx-channel/tools/wechat-channels-publish/SKILL.md index a1fda7ad..f6debad1 100644 --- a/crews/main/skills/expert-wx-channel/tools/wechat-channels-publish/SKILL.md +++ b/crews/main/skills/expert-wx-channel/tools/wechat-channels-publish/SKILL.md @@ -1,6 +1,6 @@ --- name: wechat-channels-publish -description: 通过 camoufox-cli 持久化 session wechat-channel 发布视频到微信视频号,支持视频上传、标题描述填写、即时发布。 +description: 通过 camoufox-cli 持久化 session wechat-channel 发布视频到微信视频号,支持视频上传、视频简介填写、即时发布。 --- # wechat-channels-publish — 工具说明 @@ -9,7 +9,7 @@ description: 通过 camoufox-cli 持久化 session wechat-channel 发布视频 通过 **camoufox-cli** 持久化 session `wechat-channel`(有且只有一个,fail-first 队列:同 session 已有命令在跑时新命令直接 fail)在微信视频号创作者中心发布视频。视频号创作者中心使用 **wujie 微前端**,所有表单元素在 `::shadow-root` 内——camoufox-cli 的 `snapshot` 默认穿透 shadow DOM 拿 ref,后续 `click` / `type` / `upload` 按 ref 操作即可,无需 CDP hack。 -**输入**:本地视频文件(`.mp4` / `.mov` / `.avi` / `.webm`)、短标题(6-16 字,最长约 30 字)、描述文案(含话题标签,最长约 300 字)。 +**输入**:本地视频文件(`.mp4` / `.mov` / `.avi` / `.webm`)、视频简介(含话题标签,最长约 300 字)。视频号没有标题字段,不要把简介拆成短标题。 **输出**:视频号已发布作品;能取到时附带公开链接(`https://weixin.qq.com/sph/xxxx`)。 > **主力后端 = `target=camoufox`**。下方命令 / 示例只针对 `target=camoufox`。 @@ -65,25 +65,17 @@ camoufox-cli --session wechat-channel --persistent --json open "https://channels - 失败:`[class*="upload-fail"]` 或文本"上传失败" - **最长等待 3 分钟**(大视频转码可能较慢) -### Step 5: 填写标题 +### Step 5: 填写视频简介 ``` -1. snapshot 拿到标题输入框 ref:input[placeholder*="短标题"](在 shadow DOM 内) -2. camoufox-cli --session wechat-channel --persistent --json type <标题-ref> "短标题" - - 建议 6-16 字,最长约 30 字 -``` - -### Step 6: 填写描述 - -``` -1. snapshot 拿到描述输入框 ref:div[contenteditable][data-placeholder="添加描述"] -2. camoufox-cli --session wechat-channel --persistent --json click <描述-ref> 聚焦 -3. camoufox-cli --session wechat-channel --persistent --json type <描述-ref> "描述内容 #话题1 #话题2" - - 话题标签直接写在描述中 +1. snapshot 拿到视频简介输入框 ref:div[contenteditable][data-placeholder="添加描述"] +2. camoufox-cli --session wechat-channel --persistent --json click <简介-ref> 聚焦 +3. camoufox-cli --session wechat-channel --persistent --json type <简介-ref> "视频简介内容 #话题1 #话题2" + - 话题标签直接写在视频简介中 - 最长约 300 字 ``` -### Step 7: 发布 +### Step 6: 发布 > 视频号发布不必勾选"原创声明",发布后用户会在手机端补充。 @@ -94,19 +86,19 @@ camoufox-cli --session wechat-channel --persistent --json open "https://channels 4. 若弹出"原创声明弹窗",snapshot 拿"直接发表"按钮 ref → click ``` -### Step 8: 确认发布成功 +### Step 7: 确认发布成功 等待 4 秒后 `snapshot` 检查: - 页面自动跳转到视频管理列表页 - 或 URL 变为 `https://channels.weixin.qq.com/platform/post/list` - 刚发表的作品通常在第一个。但可能处于转码中——封面缩略图为灰色,转圈。每隔 5 秒 snapshot 看转码是否完成(封面缩略图出现),完成后才能取链接。 -### Step 9: 获取已发布视频链接 +### Step 8: 获取已发布视频链接 发布成功后,在视频号管理后台的视频列表页获取视频公开链接: ``` -1. snapshot 找到刚发布的视频(列表第一条,或按标题匹配)ref +1. snapshot 找到刚发布的视频(列表第一条,或按完整视频简介匹配)ref 2. snapshot 找该视频的"分享"按钮 ref → click 3. snapshot 在弹出的分享面板中找"复制视频链接"按钮 ref → click 4. snapshot eval 从剪贴板或弹窗读取链接: @@ -120,14 +112,14 @@ camoufox-cli --session wechat-channel --persistent --json open "https://channels ## 保存草稿 -在 Step 7 中 snapshot 找"存草稿"按钮 ref → click(而非"发表")。 +在 Step 6 中 snapshot 找"存草稿"按钮 ref → click(而非"发表")。 --- ## 手动模式 如果需要人工检查表单后再发布: -1. 完成到 Step 6(所有字段已填写) +1. 完成到 Step 5(所有字段已填写) 2. **不自动 click 发表**,告知用户在浏览器中手动检查并点击 3. 注意:不操作时标签页约 30 秒后可能被重置为空白页 @@ -195,6 +187,6 @@ camoufox-cli --session wechat-channel --persistent --json open "https://channels 本工具只管发布到视频号后台,**不做发布记录入库**;入库由 Content Production Workflow 编排(调 `published-track record`)。调用方必须注意: -> **`published-track record --platform wx_channel --title` 必须传 Step 6 填的完整描述文案**(含 hashtag,最长约 300 字),**不要传 Step 5 的短标题**。 +> **`published-track record --platform wx_channel --title` 必须传 Step 5 填的完整视频简介**(含 hashtag,最长约 300 字)。 -原因:视频号作品没有「标题」概念,作品管理页展示与 `wx-channel-engagement` 抓取匹配用的都是描述文案。`pub_wx_channel.title` 列存的就是完整 desc,传短标题会导致后续抓取匹配全部失败。 +原因:视频号作品没有「标题」概念,作品管理页展示与 `wx-channel-engagement` 抓取匹配用的都是完整视频简介。`pub_wx_channel.title` 是数据库字段名,语义为完整视频简介;传短标题会导致后续抓取匹配失败。 diff --git a/crews/main/skills/expert-wx-channel/tools/wx-channel-engagement/SKILL.md b/crews/main/skills/expert-wx-channel/tools/wx-channel-engagement/SKILL.md index 26480127..8fc0bc62 100644 --- a/crews/main/skills/expert-wx-channel/tools/wx-channel-engagement/SKILL.md +++ b/crews/main/skills/expert-wx-channel/tools/wx-channel-engagement/SKILL.md @@ -9,9 +9,9 @@ description: 微信视频号已发布作品数据抓取,写入 published-track 通过 **camoufox-cli + 与 `wechat-channels-publish` 共管的 `wechat-channel` 持久化 session + 视频号助手后台爬虫**,从视频号助手「内容管理 → 作品管理」页抓已发布视频的播放/点赞/评论/分享/收藏,写入 published-track 的 `pub_wx_channel` 表。 -**思路**:视频号助手后台 `channels.weixin.qq.com/platform/` 的作品管理页把每条已发布视频的播放/点赞/评论/分享/收藏列在行内,走「作品管理页 → 解析 innerText → 按标题匹配 → 提行内数字」。 +**思路**:视频号助手后台 `channels.weixin.qq.com/platform/` 的作品管理页把每条已发布视频的播放/点赞/评论/分享/收藏列在行内,走「作品管理页 → 解析 innerText → 按完整视频简介匹配 → 提行内数字」。 -**输入**:`--row-id`(pub_wx_channel 行 id,fetch 单篇)或不带参数(list / fetch-all 批量)。行内数据按 `row.title`(即完整描述文案)在作品管理页匹配。 +**输入**:`--row-id`(pub_wx_channel 行 id,fetch 单篇)或不带参数(list / fetch-all 批量)。行内数据按 `row.title`(数据库字段名,语义为完整视频简介)在作品管理页匹配。 **输出**:行内 metrics(plays / likes / comments / shares / favorites),并经 published-track `update-metrics` 写入 `pub_wx_channel`。 **限制**:仅支持用户**自己有后台权限的号**(视频号助手用微信扫码登录)。竞品号拿不到——这是产品约束,不是技术约束。 @@ -53,7 +53,7 @@ wx-channel-engagement login --reset # 删 profile 目录 + 重新 open,从 退出码: - `0` 成功 -- `1` 通用错误(参数错 / row 找不到 / 标题未匹配) +- `1` 通用错误(参数错 / row 找不到 / 完整视频简介未匹配) - `2` session 失效(后台首页跳登录页) - `3` session 正忙(fail-first 队列) @@ -94,7 +94,7 @@ wx-channel-engagement fetch-all # 批量刷新(心跳用): 4. **数据提取方式**:不依赖 selector,直接用 `document.body.innerText` 解析(穿透 shadow DOM 后)。页面 innerText 结构清晰: ``` - <视频标题> + <完整视频简介> <发布时间> <播放数> <点赞数> <评论数> <分享数> <收藏数> ``` @@ -110,7 +110,7 @@ wx-channel-engagement fetch-all # 批量刷新(心跳用): 3. 复用 wechat-channel 持久化 session(不开独立 session、不 import cookie): camoufox-cli --session wechat-channel --persistent --json open "https://channels.weixin.qq.com/platform/post/list" 4. eval JS 解析作品管理页 innerText -> [{title, metrics}, ...] -5. match_article(rows, row.title) -> 按标题归一化匹配 +5. match_article(rows, row.title) -> 按完整视频简介归一化匹配 6. update-metrics --platform wx_channel --id ... -> 写 pub_wx_channel 7. finally: close session(登录态在磁盘 profile,不留进程占内存;下次 fetch 按需重起无头 session,profile 桥接登录态) ``` diff --git a/crews/main/skills/expert-wx-channel/tools/wx-channel-engagement/scripts/fetch_engagement.py b/crews/main/skills/expert-wx-channel/tools/wx-channel-engagement/scripts/fetch_engagement.py index 9bb8bdf9..b97ef820 100644 --- a/crews/main/skills/expert-wx-channel/tools/wx-channel-engagement/scripts/fetch_engagement.py +++ b/crews/main/skills/expert-wx-channel/tools/wx-channel-engagement/scripts/fetch_engagement.py @@ -5,7 +5,7 @@ 评论数 / 分享数 / 收藏数,写入 published-track 的 pub_wx_channel 表。 与 wx-mp-engagement 同源方法:camoufox 打开创作者后台 → 解析 innerText → -按标题匹配 → 提行内数字。视频号助手后台使用 wujie 微前端,shadow DOM 内 +按完整视频简介匹配 → 提行内数字。视频号助手后台使用 wujie 微前端,shadow DOM 内 文本需用 eval 手写 document.querySelector('wujie-app').shadowRoot.innerText。 CLI 形态: @@ -647,9 +647,9 @@ def _extract_metrics(row: dict) -> dict: def match_post(rows: list[dict], target_desc: str, target_date: str | None = None) -> dict | None: - """按描述文案在后台列表里找最匹配的行,返回 {desc, metrics} + """按完整视频简介在后台列表里找最匹配的行,返回 {desc, metrics} - 视频号作品管理页展示的是描述文案(desc),不是短标题——DB 里 title 列存的 + 视频号作品管理页展示的是完整视频简介(desc),没有短标题——DB 里 title 列存的 也应是完整 desc(见 main AGENTS.md 发布工作流)。匹配策略(小贝建议): 1. 用发布日期±1天筛同日候选(后台行 published_at 形如「2026年08月03日 12:06」) 2. 拿 desc 前 60 字归一化包含匹配——避开 hashtag 噪声,够区分 diff --git a/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/SKILL.md b/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/SKILL.md index b7cbdf6e..f72a83e9 100644 --- a/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/SKILL.md +++ b/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/SKILL.md @@ -1,6 +1,6 @@ --- name: wx-channel-style-profiler -description: 为单条视频号视频提取 16 维 DNA report,按 DNA ID 聚合历史 report 生成 DNA 文档,并推导完整的 DNA template。 +description: 提取视频号账号级 DNA:单条视频生成 report,按 DNA ID 聚合定位、选题、视频简介、内容形式、发布节奏、高数据创意、社交分享与制作管线,推导 main agent 的 Brief template。 metadata: openclaw: emoji: 🧬 @@ -8,24 +8,24 @@ metadata: # wx-channel-style-profiler -> **维度版本 v0**(2026-08-27 用户确认):16 维划分、命名与 focus ID 见 `references/video-dna-dimensions.md`。调整维度需升版本并同步脚本 `DIMENSION_GROUPS` 与下方 Focus ID 表。 +视频号账号级 DNA 提取与聚合工具。输入是**口播脚本或逐字稿文本**与可选封面图;不接受视频文件或链接作为直接输入。首个一级标题只作为样本标签 / 视频简介摘要,不代表平台标题。视频观测信息由 Agent 结合原视频、拆解报告或用户提供信息补齐。 + +DNA 的用途是指导 main agent 选题、包装、账号表达和视频制作 Brief;不指导 main agent 直接产出全片。 ## 产物模型 ```text 单条视频 -> DNA report -同一个 DNA 目录下的全部 report + 权重/focus -> DNA 文档 -DNA 文档 -> DNA template +同一 DNA 下的全部 report + 权重/focus + 用户输入 -> DNA 文档 +DNA 文档 -> main agent Brief template ``` -- **DNA report**:单条视频的 16 维提取结果。 -- **DNA 文档**:聚合历史 report 后得到的风格与选题规则。 -- **DNA template**:由 DNA 文档推导出的创作模板,供视频生产时直接执行。 +- **DNA report**:单条样本的账号级观测与 14 维提取结果。 +- **DNA 文档**:聚合后的账号级规则与样本覆盖度说明。 +- **DNA template**:main agent 生成内容或委托 Content Producer 时使用的 Brief / 生产输入模板。 ## 存储结构 -DNA 以 DNA ID 为主体存储,一个 DNA 可以持续放入任意数量视频样本,样本可以来自一个或多个账号,甚至来自用户提供的脚本想法。 - ```text wx_channel/dna/{dna-id}/ reports/ @@ -36,50 +36,39 @@ wx_channel/dna/{dna-id}/ {dna-id}.template.md ``` -原始资料(口播脚本 / 逐字稿文本)可以临时来自任何位置(建议 `wx_channel/ref/{dna-id}/transcripts/`);生成后的 DNA report 必须进入对应 DNA 的 `reports/` 目录。 +原始脚本可临时放在 `wx_channel/ref/{dna-id}/transcripts/`;生成后的 report 必须进入目标 DNA 的 `reports/` 目录。 ## 职责边界 -- 输入正文支持 `.md` / `.txt` 的口播脚本或逐字稿;视频文件本身不是本工具输入,Agent 先取得文字稿(ASR 转写、用户提供或拆解报告整理)再进入本工具。 -- 统计只作为聚合证据底座,不评分、不替代定性判断。 -- 16 维语义判断由 Agent 回读脚本与原视频信息完成;封面图维度必须由视觉模型读取本地图片完成。 +- 单条样本只提供候选信号;账号比例、发布节奏、社交分享闭环、高数据共性必须由多样本或账号级数据聚合。 +- 统计只做证据底座,不评分、不替代定性判断。 +- 视觉语言有图片/关键帧证据时才由视觉模型分析;缺失写「未观测」。 +- 口播文案 DNA 独立聚合;样本不足时保持未启用。 - 不输出合规结论、账号权重或风格评分。 -- 不要求用户确认或登记 INDEX。 ## Report - 单条提取 ```bash wx-channel-style-profiler report \ - --input path/to/transcript.md \ + --input path/to/script.md \ --dna-id {dna-id} \ --sample-id {sample-id} \ --cover-image path/to/cover.jpg \ - --source-video "https://weixin.qq.com/sph/xxxx" -``` - -默认输出: - -```text -wx_channel/dna/{dna-id}/reports/{sample-id}.report.md -``` - -可配置权重: - -```bash ---weight 3 + --source-url "https://channels.weixin.qq.com/..." \ + --output-dir wx_channel/dna/{dna-id}/reports ``` -可限制该条只在某些维度参与借鉴: +- `--cover-image`:封面本地图片,用于视觉证据。 +- `--source-url`:原视频链接;本地素材无链接时省略。 +- `--weight`:样本权重,默认 1。 +- `--focus`:限制该样本只影响指定维度,可重复传入。 -```bash ---focus hook-design ---focus narration-language -``` - -`--source-video` 可选:记录源视频链接或本地路径,仅写入 frontmatter 备查。 +Agent 生成 scaffold 后必须: -Agent 生成 scaffold 后必须回读脚本原文,补齐每个维度的单条结论、脚本与画面证据和可复用创作信号,并补齐「视频信息」区(时长、形态、出镜占比、镜头字幕、BGM、数据线索——拿不到的写未提供,不得编造)。 -封面图维度必须读取 `--cover-image` 指向的本地图片,并通过视觉模型补齐主体、构图、色彩、光线、质感、风格、文字视觉(含封面三要素:身份/痛点/方案)、品牌元素、避免项和 AIGC 复现提示词要素。没有封面图时记录“未提供”,不得编造。 +1. 补齐「视频信息」与「样本与账号观测」:样本类型、账号与简介、发布时间、内容形式、数据线索、横竖屏、素材来源与授权信息。 +2. 回读脚本原文,补齐 14 维的单条结论、原文证据和可复用信号。 +3. 单条样本无法观测账号简介、比例、节奏时写「未观测」。 +4. 高数据样本必须回读创意与内容形式,不得只凭播放量下结论。 ## Build - 聚合 DNA 文档与模板 @@ -87,20 +76,14 @@ Agent 生成 scaffold 后必须回读脚本原文,补齐每个维度的单条 wx-channel-style-profiler build --dna-id {dna-id} ``` -默认读取: - -```text -wx_channel/dna/{dna-id}/reports/ -``` - -默认输出: +默认读取 `wx_channel/dna/{dna-id}/reports/`,输出: ```text wx_channel/dna/{dna-id}/{dna-id}.dna.md wx_channel/dna/{dna-id}/{dna-id}.template.md ``` -也可显式传入一个或多个 report 文件/目录: +也可显式传入 report 文件/目录: ```bash wx-channel-style-profiler build \ @@ -111,32 +94,25 @@ wx-channel-style-profiler build \ Agent 聚合时必须: 1. 读取全部 DNA report,不能只看统计表。 -2. 按每个 report 的 `weight` 和 `focus` 判断影响范围。 +2. 按 `weight` 与 `focus` 判断影响范围。 3. 区分高频共性、高权重偏好、局部借鉴、孤例和例外。 -4. 为每个维度写聚合结论、报告依据和创作规则。 -5. 确保 DNA 文档能够完整推导 template。 +4. 标注样本覆盖度;单条/少量样本不得称为稳定账号 DNA。 +5. 为每个维度写聚合结论、报告依据和可执行规则。 +6. 确保 DNA 文档能完整推导 template。 ## DNA Template -Template 是创作模板,不是概念解释。必须从 DNA 文档的 16 个维度推导,至少包含: - -- 选题角度、受众关系 -- 标题类型、参考标题、短标题与描述文案规则、封面图风格和封面 AIGC 提示词要素 -- 全局制作要求:时长与节奏、镜头与真人出镜、BGM 与音效 -- 钩子、共情、信任状、价值、收尾五个默认语义部分 -- 每个部分的本段任务、推进方式、句式、语气、素材、必须做和避免项 +Template 是 main agent 的 Brief / 内容生产输入模板,不是成片制作模板。固定语义段: -开头两项(选题、标题(含封面图与描述文案))跨平台通用;五个语义部分是脚手架默认骨架,来自视频号通用脚本结构(3 秒钩子 → 痛点 → 信任状 → 价值 → 收尾),分段数量最终以 DNA 文档的结构结论为准,不为凑齐五段而编造规则。 +1. 定位与核心传达 +2. 选题与简介包装 +3. 内容形式与发布节奏 +4. 高数据创意模式 +5. 互动与系列 +6. 制作交接与 Pipeline +7. 口播文案 DNA(可选) -五个部分必须充分吸收 DNA 文档中的维度结论: - -| 部分 | 主要推导来源 | -| --- | --- | -| 钩子 | 前3秒钩子、开场节奏与身份信号、选题角度、口播语言 | -| 共情 | 脚本结构、口播语言、语气与人设基调、选题角度 | -| 信任状 | 信任状、语气与人设基调、签名式标记 | -| 价值 | 价值密度、画面与节奏、时长与形态、脚本结构 | -| 收尾 | 收尾与转化、互动设计、转发动机设计、语气与人设基调 | +制作交接段必须写清 MainAgent 与 Content Producer 的交付物、Pipeline、素材授权和风格边界。Brief 未指定 Pipeline 时,Content Producer 可自由选择;指定 Pipeline 时必须直接采用。 ## Update - 增量聚合 @@ -147,60 +123,55 @@ wx-channel-style-profiler update \ --template wx_channel/dna/{dna-id}/{dna-id}.template.md ``` -脚本会合并 DNA 文档记录的历史 report 与新 report,重新计算加权统计,并保留 Agent 已完成内容。Agent 仍需重新审视聚合结论,再同步修订 DNA 文档和 template。 +脚本合并历史 report 与新 report,重新计算统计并保留 Agent 已完成内容。Agent 仍需重新审视聚合结论,并同步修订 DNA 文档和 template。 -`--input` 可省略。省略时表示没有新增样本,只基于历史 report、既有 DNA 文档和用户输入做融合;适用于采纳另一个 DNA 文档或 template 中的局部规则。此时仍必须通过 `--user-input` 传入要融合的要求,并由 Agent 转译到具体维度。 +`--input` 可省略,用于只融合用户输入或另一个 DNA 的局部规则;此时必须传 `--user-input`。 ## 用户输入转译 用户输入是参考信息,不是可直接入库的 DNA 规则。 ```bash ---user-input "开头冲突再前置一点" +--user-input "这个号偏真实口播,不要过度包装" ``` -Agent 必须把输入转译到具体维度,例如: +Agent 必须转译到具体维度,例如: ```text -hook-design:第一句直接抛反常识结论,身份介绍后移 -opening-pace:前 2 秒完成冲突,第 3 秒预告价值 -narration-language:钩子句控制在 15 字以内的短句 +content-form-mix:主形态为真人口播,减少纯动画 +narration-dna:口播保留自然停顿,不使用强促销句式 +production-pipeline:Brief 默认使用 video-producer:default(narrative) ``` 处理要求: -1. 在 DNA 文档的“用户输入转译区”记录 affected dimensions、DNA 修改和 template 修改。 -2. 把原话转译成可执行的聚合结论与创作规则。 -3. Template 只写转译后的执行规则,不直接抄用户原话。 -4. 与样本证据冲突时保留冲突说明,由用户选择优先级。 +1. 在 DNA 文档的「用户输入转译区」记录 affected dimensions、DNA 修改和 template 修改。 +2. 原话必须转译为可执行规则,不得直接抄进 template。 +3. 与样本证据冲突时保留冲突说明,由用户选择优先级。 ## Focus ID | ID | 维度 | |---|---| -| `topic-angle` | 选题角度 | -| `title-desc` | 标题与描述文案 | -| `cover-image` | 封面图 | -| `hook-design` | 前3秒钩子 | -| `opening-pace` | 开场节奏与身份信号 | -| `narration-language` | 口播语言 | -| `tone-persona` | 语气与人设基调 | -| `signature-expression` | 签名式标记 | -| `script-structure` | 脚本结构 | -| `visual-pacing` | 画面与节奏 | -| `duration-form` | 时长与形态 | -| `credibility-proof` | 信任状 | -| `value-density` | 价值密度 | -| `interaction-design` | 互动设计 | -| `share-motive` | 转发动机设计 | -| `cta-funnel` | 收尾与转化 | +| `positioning-core` | 定位与核心传达 | +| `topic-portfolio` | 选题组合 | +| `description-packaging` | 简介与包装 | +| `bio-profile` | 账号简介与主页表达 | +| `content-form-mix` | 内容形式与比例 | +| `publish-cadence` | 发布习惯 | +| `high-performer-patterns` | 高数据创意模式 | +| `visual-language` | 视觉语言 | +| `audio-language` | 声音语言 | +| `narration-dna` | 口播文案 DNA | +| `engagement-conversion` | 互动与转化 | +| `social-share-loop` | 社交分享闭环 | +| `series-signature` | 系列与签名 | +| `production-pipeline` | 制作管线倾向 | ## 统计与分词 -脚本统计口播脚本的句段、标点、人称等指标;中文高频信号使用相邻二字组合,仅作为候选线索。Agent 必须回读原文判断它是否真是口头禅或签名式表达。时长、镜头、出镜占比等视频观测不在文本统计范围内,由 Agent 补齐「视频信息」区。 - -当前不引入 jieba。若后续候选噪声明显,可把 jieba 作为候选词挖掘器加入仓库级依赖,但分词结果不能直接作为 DNA 结论。 +脚本统计口播脚本的句段、标点、人称等指标;中文高频信号使用相邻二字组合,仅作为候选线索。Agent 必须回读原文确认口头禅或签名式表达。时长、镜头、出镜占比等观测由 Agent 补齐,不由文本统计推断。 ## 参考资料 -- `references/video-dna-dimensions.md`(16 维 v0 定稿与 template 分段说明) +- `references/account-dna-framework.md`(视频号账号级 DNA 框架 v1、Pipeline 映射与聚合边界) diff --git a/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/references/account-dna-framework.md b/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/references/account-dna-framework.md new file mode 100644 index 00000000..f85c6eee --- /dev/null +++ b/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/references/account-dna-framework.md @@ -0,0 +1,84 @@ +# 视频号账号级 DNA 框架(v1) + +> 状态:v1。Focus ID、维度命名与 template 语义以本文件为准。调整维度必须升版本,并同步 `scripts/build_style_profile.py` 与工具 `SKILL.md`。 + +## 定位与边界 + +视频号 DNA 是**账号级运营框架**,不是单条视频的脚本或剪辑细节。视频号作品没有标题,只有视频简介。它回答账号长期如何被识别、如何选题、如何包装、如何安排内容形式与发布节奏,并指导 main agent 产出制作 Brief。 + +- **必须进入 DNA**:定位与核心传达、选题组合、视频简介、账号简介、内容形式及比例、发布习惯、高数据内容创意共性、视觉/声音倾向、口播文案 DNA、社交分享动机、系列标记、制作管线倾向。 +- **不进入主 DNA**:逐句口播、镜头表、转场参数、字幕样式、成片剪辑微操。口播文案 DNA 是独立可选块;没有证据时写「未观测」。 +- **MainAgent / Content Producer 分界**:main agent 负责已有视频素材的简单加工、长文/图文内容,以及视频全案 Brief。除 Brief 之外的成片制作由 Content Producer 执行。口播类视频的口播文案若由 DNA 明确启用,则由 main agent 写好并随 Brief 交付;Content Producer 只做声画制作。 +- **样本可以是账号或单条**:账号样本可提取简介与发布习惯;用户提供单条或多条单篇时,仍按同维度聚合,但账号级字段必须标「样本不足 / 未观测」。 + +## 维度(14 维) + +### 一、定位与选题 + +| # | ID | 维度 | 观测内容 | +|---|----|------|----------| +| 1 | `positioning-core` | 定位与核心传达 | 一句话定位、目标人群、长期承诺、内容主张边界 | +| 2 | `topic-portfolio` | 选题组合 | 常见选题类型、内容支柱、选题入口、系列关系 | +| 3 | `description-packaging` | 简介与包装 | 视频简介模式、封面承诺、话题标签策略;不规定具体字句 | +| 4 | `bio-profile` | 账号简介与主页表达 | 昵称、简介、置顶内容、主页承诺;仅账号样本可得 | + +### 二、账号节奏 + +| # | ID | 维度 | 观测内容 | +|---|----|------|----------| +| 5 | `content-form-mix` | 内容形式与比例 | 图文、视频、口播、实拍拼接、创意转场、纯 AIGC 动画等形式;如混合,记录比例或倾向 | +| 6 | `publish-cadence` | 发布习惯 | 发布时间段、频率、内容形式混合节奏、栏目化节奏 | +| 7 | `high-performer-patterns` | 高数据创意模式 | 高播放/高完播/高转发样本的选题、包装、形式与创意共性;数据只是证据 | + +### 三、表达与风格 + +| # | ID | 维度 | 观测内容 | +|---|----|------|----------| +| 8 | `visual-language` | 视觉语言 | 稳定画面风格、横竖屏、视觉记忆点;不规定镜头参数 | +| 9 | `audio-language` | 声音语言 | 配音类型/音色倾向、口播密度、BGM/音效倾向;不规定 TTS 参数 | +| 10 | `narration-dna` | 口播文案 DNA | 独立可选块:开头钩子、起承转合、收束、人称与语气、签名表达 | + +### 四、互动与制作 + +| # | ID | 维度 | 观测内容 | +|---|----|------|----------| +| 11 | `engagement-conversion` | 互动与转化 | 评论、关注、直播预约、私信等平台内行动与承接路径 | +| 12 | `social-share-loop` | 社交分享闭环 | 实用价值 / 情绪态度 / 社交人设三类转发动机、分享话术、被转发后的承接 | +| 13 | `series-signature` | 系列与签名 | 栏目名、固定开场/收尾、系列承诺、识别符号 | +| 14 | `production-pipeline` | 制作管线倾向 | 视频内容形态到 Content Producer Pipeline 的映射;未指定时交给 CP 自由发挥 | + +## Pipeline 映射 + +`production-pipeline` 只能写 Content Producer 已支持的管线,不得发明不存在的管线名。 + +| DNA 观测形态 | Brief 中的 Pipeline | +|--------------|---------------------| +| 影视解说 + 反转植入 | `dna-ad-video-pipeline` | +| 口播 / 真人讲述 | `video-producer:default`(narrative) | +| 实拍拼接 / 蒙太奇 | `video-producer:default`(montage) | +| 创意转场 / 动效 | `video-producer:default`(motion) | +| 纯 AIGC 动画 | `video-producer:default`(AIGC render) | + +Brief 未写 `pipeline` 时,Content Producer 按 `video-producer:default` 自由选择实现;Brief 写了 Pipeline 时必须直接采用,不得替换成自创流程。 + +## Report 与聚合规则 + +1. 单条 report 先记录「样本与账号观测」:样本类型、账号与简介、发布时间、内容形式、数据线索、视频形态、授权信息。缺失写「未观测」。 +2. 单条样本不能推导账号比例、发布节奏、社交分享闭环或高数据共性;聚合时标注覆盖度。 +3. 高数据样本必须回读创意与内容形式,不能只凭播放量归因。 +4. 口播文案 DNA 独立聚合;没有足够样本时保持未启用。 +5. 脚本统计只提供句长、人称、语气等证据;语义判断由 Agent 回读原文完成。 + +## Template 语义 + +DNA template 是 main agent 的 **Brief / 内容生产输入模板**,不是成片制作模板。固定语义段: + +1. 定位与核心传达 +2. 选题与简介包装 +3. 内容形式与发布节奏 +4. 高数据创意模式 +5. 互动与系列 +6. 制作交接与 Pipeline +7. 口播文案 DNA(可选) + +模板不得规定镜头表、逐句台词、转场参数或编码细节。视频全案的制作选择由 Content Producer 在 Pipeline 内完成。 diff --git a/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/references/video-dna-dimensions.md b/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/references/video-dna-dimensions.md deleted file mode 100644 index 4db023b9..00000000 --- a/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/references/video-dna-dimensions.md +++ /dev/null @@ -1,80 +0,0 @@ -# 视频号 DNA 提取与分析维度(v0) - -> 状态:**v0,2026-08-27 用户确认定稿**。维度划分、命名与 focus ID 以本文件为准;后续调整需升版本并同步本文件、`scripts/build_style_profile.py` 的 `DIMENSION_GROUPS` 与工具 `SKILL.md` 的 Focus ID 表,历史 report 的维度编号以生成时版本为准。 -> -> 定稿依据:包内起号参考手册(脚本结构、前 3 秒钩子、转发动机、互动设计、数据校准)、视频号运营策略师人设文档(社交推荐机制、平台调性)、微信 17 维框架的分组思路。 - -## 与微信 公众号 17 维的差异说明 - -视频号作品是短视频,不是图文: - -- 观测对象从「标题 + 正文段落」变为「短标题 + 描述文案 + 封面 + 口播脚本 + 画面节奏」。 -- 新增视频特有维度:前 3 秒钩子、开场节奏、脚本结构、画面与节奏、时长与形态、转发动机、收尾转化。 -- 图文特有维度(段落结构、文章结构模式、起承转合微操等)不照搬;其中表达类能力并入口播语言、语气与人设基调。 -- 社交推荐是视频号核心引擎,「分享」权重高于「点赞」,因此单独设立转发动机设计维度。 - -## 维度分组(16 维) - -### 一、选题与包装(对应 template 前两项) - -| # | ID | 维度 | 观测内容 | -|---|----|------|----------| -| 1 | `topic-angle` | 选题角度 | 目标人群、痛点/观看理由、内容支柱归属、系列化与合集价值(视频号长尾流量下系列内容更值钱) | -| 2 | `title-desc` | 标题与描述文案 | 短标题风格(6-16 字);描述文案结构(视频号作品无标题概念,描述即展示文本);话题标签策略 | -| 3 | `cover-image` | 封面图 | 封面三要素(身份 + 痛点 + 解决方案)、视觉风格、文字视觉、AIGC 复现要素 | - -### 二、钩子与开场 - -| # | ID | 维度 | 观测内容 | -|---|----|------|----------| -| 4 | `hook-design` | 前3秒钩子 | 钩子类型(矛盾前置 / 数据冲击 / 场景代入 / 悬念留白 / 结果反差)、冲突放置位置 | -| 5 | `opening-pace` | 开场节奏与身份信号 | 前 2 秒冲突、第 3 秒价值预告;身份信号是否在前 3 秒让用户知道「我是谁、在做什么、哪里不一样」 | - -### 三、口播与表达 - -| # | ID | 维度 | 观测内容 | -|---|----|------|----------| -| 6 | `narration-language` | 口播语言 | 口语化程度、句长节奏、用词习惯(脚本统计可给句长/人称/问句密度证据) | -| 7 | `tone-persona` | 语气与人设基调 | 温度感、专业度、人设张力、短板与真实感处理;视频号偏真实有温度,忌抖音式强刺激 | -| 8 | `signature-expression` | 签名式标记 | 口头禅、固定开场/收尾、记忆点动作或话术(高频词候选必须由 Agent 回读脚本确认) | - -### 四、结构与节奏 - -| # | ID | 维度 | 观测内容 | -|---|----|------|----------| -| 9 | `script-structure` | 脚本结构 | 钩子→共情/痛点→信任状→价值→收尾的组织方式与各段时间分配 | -| 10 | `visual-pacing` | 画面与节奏 | 镜头切换密度(经验值 3 秒一变画面、15 秒一个小高潮)、景别、画面主体稳定性、字幕/字卡使用 | -| 11 | `duration-form` | 时长与形态 | 时长区间、竖屏/横屏、真人出镜占比(建议 ≥ 60%)与配音/素材剪辑/AIGC 形态、系列化形态 | - -### 五、信任与价值 - -| # | ID | 维度 | 观测内容 | -|---|----|------|----------| -| 12 | `credibility-proof` | 信任状 | 身份、资历、真实案例/数据的呈现方式;真实短板与成长线的使用;「凭啥信你」的解答位置 | -| 13 | `value-density` | 价值密度 | 干货可带走程度(清单/步骤/模板)、演示方式、信息密度是否均匀 | - -### 六、互动与转化 - -| # | ID | 维度 | 观测内容 | -|---|----|------|----------| -| 14 | `interaction-design` | 互动设计 | 结尾评论引导(选择题优于开放式提问)、关注软引导、评论区承接风格 | -| 15 | `share-motive` | 转发动机设计 | 实用价值 / 情绪态度 / 社交人设三类动机的命中情况、转发引导话术;分享是视频号最强社交信号 | -| 16 | `cta-funnel` | 收尾与转化 | CTA 类型(关注 / 购物车 / 私信 / 直播预约 / 评论关键词)与密度、承接路径(主页 / 合集 / 企微私域) | - -## Template 语义部分(默认 5 段) - -开头两项跨平台通用:**选题**、**标题(含封面图,视频号还包括描述文案)**。第三项起默认按视频号脚本结构分 5 个语义部分: - -| 部分 | 对应时间段(参考) | 主要推导维度 | -|------|--------------------|--------------| -| [钩子部分] | 0-3s | hook-design、opening-pace、topic-angle、narration-language | -| [共情部分] | 3-10s | script-structure、narration-language、tone-persona、topic-angle | -| [信任状部分] | 10-20s | credibility-proof、tone-persona、signature-expression | -| [价值部分] | 20-40s | value-density、visual-pacing、duration-form、script-structure | -| [收尾部分] | 最后 5-10s | cta-funnel、interaction-design、share-motive、tone-persona | - -时间段来自起号手册的通用脚本结构,仅作默认骨架;分段数量与命名最终以 DNA 文档的结构结论为准(架构规范不固定段数)。另有一组全局制作要求(时长与节奏、镜头与真人出镜、BGM 与音效)写在选题/标题块之后、分段之前。 - -## 统计边界 - -脚本对输入的口播脚本/逐字稿做句段、标点、人称统计,作为聚合证据底座;不生成总分、不判断风格合格。时长、镜头、出镜占比等视频观测无法从纯文本统计得出,由 Agent 结合原视频或用户提供的信息补齐 report 的「视频信息」区。高频二字组合只是候选线索,口头禅与签名式表达必须经 Agent 回读原文确认。 diff --git a/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/scripts/build_style_profile.py b/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/scripts/build_style_profile.py index 7ff61a3a..61fb60b4 100644 --- a/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/scripts/build_style_profile.py +++ b/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/scripts/build_style_profile.py @@ -34,36 +34,29 @@ "a", "an", "is", "are", "and", "or", "of", "in", "for", "on", "with", "you", "we", } -# DNA 维度 v0(2026-08-27 用户确认定稿)。调整维度需升版本, -# 并同步 references/video-dna-dimensions.md 与 SKILL.md 的 Focus ID 表。 +# DNA 维度 v1。调整维度需升版本,并同步 references/account-dna-framework.md 与 SKILL.md 的 Focus ID 表。 DIMENSION_GROUPS = { - "选题与包装": [ - ("topic-angle", "选题角度"), - ("title-desc", "标题与描述文案"), - ("cover-image", "封面图"), + "定位与选题": [ + ("positioning-core", "定位与核心传达"), + ("topic-portfolio", "选题组合"), + ("description-packaging", "简介与包装"), + ("bio-profile", "账号简介与主页表达"), ], - "钩子与开场": [ - ("hook-design", "前3秒钩子"), - ("opening-pace", "开场节奏与身份信号"), + "账号节奏": [ + ("content-form-mix", "内容形式与比例"), + ("publish-cadence", "发布习惯"), + ("high-performer-patterns", "高数据创意模式"), ], - "口播与表达": [ - ("narration-language", "口播语言"), - ("tone-persona", "语气与人设基调"), - ("signature-expression", "签名式标记"), + "表达与风格": [ + ("visual-language", "视觉语言"), + ("audio-language", "声音语言"), + ("narration-dna", "口播文案 DNA"), ], - "结构与节奏": [ - ("script-structure", "脚本结构"), - ("visual-pacing", "画面与节奏"), - ("duration-form", "时长与形态"), - ], - "信任与价值": [ - ("credibility-proof", "信任状"), - ("value-density", "价值密度"), - ], - "互动与转化": [ - ("interaction-design", "互动设计"), - ("share-motive", "转发动机设计"), - ("cta-funnel", "收尾与转化"), + "互动与制作": [ + ("engagement-conversion", "互动与转化"), + ("social-share-loop", "社交分享闭环"), + ("series-signature", "系列与签名"), + ("production-pipeline", "制作管线倾向"), ], } DIMENSIONS = [] @@ -75,7 +68,6 @@ ) number += 1 - def split_sentences(text: str) -> list[str]: return [item.strip() for item in SENTENCE_SPLIT.split(text) if item.strip()] @@ -303,6 +295,7 @@ def statistics_markdown(statistics: dict) -> str: lines.extend( [ "", + f"样本覆盖度:`{statistics['report_count']}` 条 report;单条或少量样本不能推导账号比例、发布节奏或高数据共性。", f"权重模式:`{statistics['weighting']}`;总权重:`{statistics['total_weight']}`。", "统计只用于辅助聚合,不生成评分;定性判断必须回到各篇 DNA report。", ] @@ -371,28 +364,32 @@ def load_reports(paths: list[Path]) -> list[dict]: return reports +REPORT_DIMENSION_PROMPTS = { + 'positioning-core': '- 单条观测:本条暴露出的定位、目标人群与核心传达。\n- 账号级判断:单条样本只给候选,不直接判定账号稳定性。', + 'topic-portfolio': '- 单条观测:本条选题类型、入口和系列关系。', + 'description-packaging': '- 单条观测:视频简介 / 封面 / 话题标签包装模式;只记模式,不抄成固定字句。', + 'bio-profile': '- 账号级观测:账号简介、主页与置顶表达;单条样本无法观测时写未观测。', + 'content-form-mix': '- 单条观测:本条内容形式。\n- 聚合边界:图文/视频比例与混合节奏必须由多样本或账号级数据推导。', + 'publish-cadence': '- 单条观测:本条发布时间。\n- 聚合边界:时间段、频率和三图文对一视频等节奏必须由多样本或账号级数据推导。', + 'high-performer-patterns': '- 数据线索:记录本条互动/播放/阅读线索。\n- 创意判断:回读选题、包装、形式与创意,不得把高数据直接等同于风格好。', + 'visual-language': '- 视觉证据:有图片或关键帧时由视觉模型读取;无证据写未观测。\n- 边界:只记录账号级稳定视觉语言,不规定逐镜设计。', + 'audio-language': '- 声音证据:来自口播稿、原视频信息或用户说明;无证据写未观测。\n- 边界:只记录音色/语速/声音气质倾向,不规定 TTS 参数。', + 'narration-dna': '- 独立块:记录开头、起承转合、收束、人称与签名表达。\n- 聚合边界:样本不足时保持未启用,不把单条句式上升为账号 DNA。', + 'engagement-conversion': '- 单条观测:平台内行动引导与承接路径。', + 'social-share-loop': '- 单条观测:转发动机、分享话术和被转发后的承接。', + 'series-signature': '- 单条观测:栏目、固定表达或识别符号;高频词必须回读原文确认。', + 'production-pipeline': '- 管线映射:只写 Content Producer 已支持的 Pipeline;未确定时写待定。\n- 分界:DNA 指导 main agent 出 Brief,不规定成片制作细节。', +} + def report_dimension_markdown(dimension: dict) -> str: heading = f"### {dimension['number']}. {dimension['name']}" - if dimension["id"] == "cover-image": - return ( - f"{heading}\n\n" - "**视觉模型分析:**待 Agent 基于封面图补齐。\n\n" - "- 画面主体与场景:待 Agent 补齐。\n" - "- 构图与画幅:待 Agent 补齐。\n" - "- 色彩体系:待 Agent 补齐。\n" - "- 光线与质感:待 Agent 补齐。\n" - "- 风格与媒介:待 Agent 补齐。\n" - "- 文字视觉与图文关系(封面三要素:身份/痛点/方案):待 Agent 补齐。\n" - "- 品牌识别元素:待 Agent 补齐。\n" - "- 避免项:待 Agent 补齐。\n\n" - "**AIGC 复现提示词要素:**待 Agent 补齐;要求能据此生成风格高度一致的封面图。\n\n" - "**可复用创作信号:**待 Agent 补齐。" - ) + prompt = REPORT_DIMENSION_PROMPTS.get(dimension["id"], "- 单条观测:待 Agent 补齐。") return ( f"{heading}\n\n" - "**单篇结论:**待 Agent 补齐。\n\n" - "**脚本与画面证据:**待 Agent 补齐。\n\n" - "**可复用创作信号:**待 Agent 补齐。" + f"{prompt}\n\n" + "**单条结论:**待 Agent 补齐。\n\n" + "**原文证据:**待 Agent 补齐(逐字引用、账号信息、发布信息、画面/声音描述或数据线索;注明来源)。\n\n" + "**可复用信号:**待 Agent 补齐;样本不足时写未观测,不推导账号级稳定性。" ) @@ -426,9 +423,11 @@ def report_markdown( f"# {document['title_candidates'][-1]} 单条视频 DNA Report", "本文件只描述这一条视频。它不是聚合后的 DNA 文档,也不直接作为创作模板。", "## 单篇统计", - f"- 字符:{document['characters']}\n- 句子:{document['sentences']}\n- 段落/镜次:{document['paragraphs']}\n- 标题候选:{' / '.join(document['title_candidates'])}\n- 封面图:{cover_image or '未提供'}\n- 源视频:{source_video or '未提供'}", + f"- 字符:{document['characters']}\n- 句子:{document['sentences']}\n- 段落/镜次:{document['paragraphs']}\n- 简介摘要候选:{' / '.join(document['title_candidates'])}\n- 封面图:{cover_image or '未提供'}\n- 源视频:{source_video or '未提供'}", "## 视频信息(待 Agent 结合原视频 / 用户提供信息补齐)", "- 时长:待 Agent 补齐。\n- 视频形态:待 Agent 补齐(竖屏/横屏,真人出镜 / 配音解说 / 素材剪辑 / AIGC)。\n- 真人出镜占比:待 Agent 补齐。\n- 镜头与字幕要点:待 Agent 补齐。\n- BGM 与音效:待 Agent 补齐。\n- 数据线索(可选,播放 / 互动等):待 Agent 补齐,不得编造。", + "## 样本与账号观测", + '- 样本类型:待 Agent 补齐(账号作品 / 用户提供单条)。\n- 账号与简介:待 Agent 补齐;单条样本无法观测时写未观测。\n- 发布时间与时间段:待 Agent 补齐;单条样本只记录本条时间,不推导账号节奏。\n- 内容形式:待 Agent 补齐(口播 / 实拍拼接 / 创意转场 / 纯 AIGC 动画 / 图文 / 混合)。\n- 数据表现线索:待 Agent 补齐;只作证据,不直接判风格好坏。\n- 视频形态与授权信息:待 Agent 补齐(横竖屏、时长、素材来源、授权边界)。', f"## {len(DIMENSIONS)} 维单篇分析", "\n\n".join(dimensions), "## 单篇边界", @@ -472,19 +471,11 @@ def dna_document_markdown( if body: dimensions.append(f"{heading}\n\n{body}") else: - if dimension["id"] == "cover-image": - dimensions.append( - f"{heading}\n\n**聚合结论:**待 Agent 补齐。\n\n" - "**报告依据:**待 Agent 列出使用的封面图和 DNA report。\n\n" - "**视觉生成规则:**待 Agent 转成可执行的 AIGC 提示词要素。\n\n" - "**例外与约束:**待 Agent 补齐。" - ) - else: - dimensions.append( - f"{heading}\n\n**聚合结论:**待 Agent 补齐。\n\n" - "**报告依据:**待 Agent 列出使用的 DNA report、权重和 focus。\n\n" - "**创作规则:**待 Agent 补齐。" - ) + dimensions.append( + f"{heading}\n\n**聚合结论:**待 Agent 补齐。\n\n" + "**报告依据:**待 Agent 列出使用的 DNA report、权重和 focus。\n\n" + "**创作规则:**待 Agent 补齐。" + ) report_paths = "\n".join(report["report_path"] for report in reports) existing_user_inputs = ( extract_named_section(previous_dna, "## 用户输入转译区") if previous_dna else None @@ -562,52 +553,58 @@ def extract_named_section(markdown: str, heading: str) -> str: return "\n".join(body).strip() -TEMPLATE_STAGES = ("钩子", "共情", "信任状", "价值", "收尾") +TEMPLATE_STAGES = ("定位与核心传达", "选题与简介包装", "内容形式与发布节奏", "高数据创意模式", "互动与系列", "制作交接与Pipeline", "口播文案DNA") TEMPLATE_STAGE_FIELDS = { - "钩子": ( - "本段任务", - "钩子类型", - "冲突与身份信号", - "句式节奏", - "必须做", - "避免", + "定位与核心传达": ( + "一句话定位", + "目标人群", + "核心传达", + "账号简介与主页表达", + "不变承诺", ), - "共情": ( - "本段任务", - "共情方式", - "场景具体性", - "语言风格", - "必须做", - "避免", + "选题与简介包装": ( + "选题组合", + "视频简介模式", + "封面包装", + "禁用方向", ), - "信任状": ( - "本段任务", - "信任状类型", - "呈现方式", - "真实性边界", - "必须做", - "避免", + "内容形式与发布节奏": ( + "图文或视频比例", + "发布时间带", + "内容形式混合节奏", + "系列栏目", + ), + "高数据创意模式": ( + "高表现样本共性", + "可复用创意原型", + "触发条件", + "例外", ), - "价值": ( - "本段任务", - "信息密度与节奏", - "演示方式", - "字幕与字卡", + "互动与系列": ( + "互动目标", + "引导方式", + "社交分享动机", + "系列与签名标记", "必须做", "避免", ), - "收尾": ( - "行动目标", - "互动引导方式", - "转发引导", - "语气", + "制作交接与Pipeline": ( + "MainAgent交付物", + "ContentProducer交付物", + "Pipeline", + "素材与授权", + "风格边界", + ), + "口播文案DNA": ( + "是否启用", + "起承转合结构", + "语言与人称", + "声音倾向", "必须做", "避免", ), } - - def parse_template_fields(body: str) -> dict[str, str]: fields = {} for line in body.splitlines(): @@ -633,25 +630,6 @@ def template_segment(stage: str, values: dict[str, str] | None = None) -> str: return "\n".join(lines) -def extract_topic_title_body(previous_template: str | None) -> str: - if not previous_template: - return "" - for heading in ("## 生产模板", "## 选题与标题"): - body = extract_named_section(previous_template, heading) - if not body: - continue - topic_title_lines = [] - for line in body.splitlines(): - # 选题/标题块内含 "[标题]" 行,只在真正的语义分段 "[X部分]" 处截断 - if re.match(r"^\[[^\]]+部分\]$", line): - break - topic_title_lines.append(line) - rendered = "\n".join(topic_title_lines).strip() - if rendered: - return rendered - return "" - - def stage_values_from_template(old_sections: dict[str, str]) -> dict[str, dict[str, str]]: values = {stage: {} for stage in TEMPLATE_STAGES} for heading in sorted(old_sections, key=template_order): @@ -671,41 +649,24 @@ def template_markdown( old_sections = extract_template_sections(previous_template) stage_values = stage_values_from_template(old_sections) segments = [template_segment(stage, stage_values[stage]) for stage in TEMPLATE_STAGES] - topic_title = extract_topic_title_body(previous_template) or ( - "(选题角度推荐:待 Agent 补齐。)\n" - "(选题需考虑的受众关联角度:待 Agent 补齐。)\n" - "\n" - "[标题](类型为主:待 Agent 补齐。)\n" - "(参考:待 Agent 补齐。)\n" - "(短标题:待 Agent 补齐——6-16 字,最长约 30 字。)\n" - "(描述文案:待 Agent 补齐——作品展示文本,含话题标签,最长约 300 字。)\n" - "(封面图风格:待 Agent 补齐。)\n" - "(封面 AIGC 提示词要素:待 Agent 补齐。)\n" - "\n" - "(时长与节奏:待 Agent 补齐。)\n" - "(镜头与真人出镜:待 Agent 补齐。)\n" - "(BGM 与音效:待 Agent 补齐。)" - ) - if previous_template: - for field in ("(封面图风格:", "(封面 AIGC 提示词要素:", "(描述文案:"): - if field not in topic_title: - topic_title += f"\n{field}待 Agent 补齐。)" section_defaults = [ ( "## 生产模板", - topic_title + "\n\n" + "\n\n".join(segments), + "\n\n".join(segments), ), ( "## 用户输入转译后的执行规则", "- (来自用户输入:待 Agent 补齐来源。)\n" f"- (影响维度:待 Agent 映射到 {len(DIMENSIONS)} 维 ID。)\n" - "- (执行规则:待 Agent 写成创作时可直接执行的要求。)", + "- (执行规则:待 Agent 写成 Brief 或图文生产时可直接执行的要求。)", ), ( "## 使用检查", - "- 选题与标题(含描述文案、封面)是否符合 DNA 文档的选题角度、受众关联和标题/描述规则。\n" - "- 钩子、共情、信任状、价值、收尾各部分是否完成各自任务。\n" - "- 每个部分是否反映 DNA 文档中对应的钩子设计、开场节奏、口播语言、语气人设、信任状、价值密度、互动设计、转发动机与签名标记。\n" + "- 是否只用一个 DNA,且与本次账号 / 内容任务匹配。\n" + "- 定位、选题、视频简介包装、核心传达是否来自 DNA 文档。\n" + "- 图文/视频比例、发布节奏和高数据创意是否尊重样本覆盖度;样本不足时是否标注未观测。\n" + "- 视频全案是否只输出 Brief,且 Brief 明确 Pipeline、素材授权、验收标准和交付边界。\n" + "- 口播文案 DNA 是否独立启用;未启用时是否避免规定逐句口播。\n" "- 用户输入是否已转译为具体执行规则。", ), ] @@ -729,7 +690,7 @@ def template_markdown( f"generated_at: {yaml_value(generated_at())}\n" "---", f"# {dna_id} DNA Template", - "本模板是创作视频时直接执行的 production template,必须由 DNA 文档推导;不得引入 DNA 文档未确认的规则。", + "本模板是 main agent 的账号级内容生产 / Brief 输入模板,必须由 DNA 文档推导;不得引入 DNA 文档未确认的规则,也不规定成片制作细节。", *rendered_sections, ] ) + "\n" diff --git a/crews/main/skills/expert-wx-channel/workflows/account-benchmark.md b/crews/main/skills/expert-wx-channel/workflows/account-benchmark.md index 00b8f5f2..5bc2c82e 100644 --- a/crews/main/skills/expert-wx-channel/workflows/account-benchmark.md +++ b/crews/main/skills/expert-wx-channel/workflows/account-benchmark.md @@ -32,9 +32,9 @@ | 来源 | 处理 | | --- | --- | -| 自己账号的作品 | `wx-channel-engagement list` 拿描述文案与行内指标;完整口播仍需转写或用户提供 | +| 自己账号的作品 | `wx-channel-engagement list` 拿视频简介与行内指标;完整口播仍需转写或用户提供 | | 用户提供文字稿 / 视频文件 | 文字稿直接输入;视频文件先经顶层 `talking-head-cut` 转写 | -| 用户提供的截图 / 口述数据 | 作为数据线索保留在 report 证据中,标注来源 | +| 用户提供的截图 / 口述数据 / 账号信息 | 作为数据、账号简介、发布时间与内容形式线索保留在 report 证据中,标注来源 | | 跨平台参考(抖音 / B站 / 小红书链接) | 顶层 `viral-chaser` 下载 + 转写 + 拆解;跨平台样本只借鉴结构,不照搬平台调性 | 选择建议: @@ -42,7 +42,8 @@ 1. 优先选择分享、评论信号强的视频;播放量只作次要参考(视频号分享权重高于点赞)。 2. 对标账号的播放、互动数据只能来自用户提供或截图;不得编造,不得把运营效果等同于内容质量。 3. 账号级对标至少收集 10 条代表性视频样本;不足 10 条时提供全部并说明数量限制。 -4. 单条视频可以形成单条观察,但不得当成账号级稳定 DNA;多个样本才分析覆盖率和共性。 +4. 每条样本尽量记录账号简介、发布时间、内容形式、素材来源与授权;缺失写「未观测」,不虚构。 +5. 单条视频可以形成单条观察,但不得当成账号级稳定 DNA;多个样本才分析覆盖率和共性。 ### Step 2 - 建立对标 DNA @@ -52,7 +53,8 @@ wx-channel-style-profiler report \ --input path/to/transcript.md \ --dna-id {benchmark-dna-id} \ - --sample-id {sample-id} + --sample-id {sample-id} \ + --output-dir wx_channel/dna/{benchmark-dna-id}/reports ``` 再执行: @@ -68,7 +70,7 @@ wx_channel/dna/{benchmark-dna-id}/{benchmark-dna-id}.dna.md wx_channel/dna/{benchmark-dna-id}/{benchmark-dna-id}.template.md ``` -对标 DNA template 开头两项固定为选题、标题(含封面图与描述文案);脚本分段默认五个语义部分(钩子、共情、信任状、价值、收尾),分段数量以对对标 DNA 文档的结构结论为准;每一部分都要能从对标 DNA 文档推导。 +对标 DNA template 使用 v1 账号级语义段:定位与核心传达、选题与简介包装、内容形式与发布节奏、高数据创意模式、互动与系列、制作交接与 Pipeline、口播文案 DNA。每一部分都要能从对标 DNA 文档推导。 ### Step 3 - 选择比较基线 @@ -85,16 +87,16 @@ wx_channel/dna/{base-dna-id}/{base-dna-id}.template.md 比较必须覆盖两层: -1. **16 维 DNA 文档**:逐个维度比较规则、证据和适用条件;封面图需比较视觉特征与 AIGC 复现要素。 -2. **template**:逐项比较选题、标题(含描述文案与封面)与脚本各分段的执行方式。 +1. **14 维 DNA 文档**:逐个比较定位、选题、简介包装、账号简介、内容形式、发布节奏、高数据创意、视觉/声音、口播文案 DNA、互动转化、社交分享闭环、系列签名与制作管线。 +2. **template 语义段**:逐项比较定位与核心传达、选题与简介包装、内容形式与发布节奏、高数据创意、互动与系列、制作交接、口播文案 DNA。 -每个维度和模板部分都输出四类结论: +每个维度和模板语义段都输出四类结论: | 类别 | 判断标准 | | --- | --- | | 保持 | 基线已有优势,与目标观众和商业定位一致 | | 引入 | 对标更有效,且不冲突业务事实、合规边界和用户偏好 | -| 局部借鉴 | 只适合钩子、文案、结构、收尾等局部场景 | +| 局部借鉴 | 只适合选题、包装、内容形式、发布节奏、社交分享或 Pipeline 等局部场景 | | 不采纳 | 仅依赖孤例、冲突商业定位、风险高或难稳定执行 | 每项至少说明: @@ -104,34 +106,30 @@ wx_channel/dna/{base-dna-id}/{base-dna-id}.template.md 3. 差异原因或适用条件。 4. 是否建议更新基线。 -视频号特有比较点:社交推荐触发设计(转发动机是否明确)、前 3 秒钩子强度、真人出镜占比、私域承接路径是否完整。 - ### Step 5 - 采纳与更新 对比结果不自动更新基线 DNA。用户明确采纳后,选择一种方式: -1. **局部 DNA 融合**:用户决定采纳对标 DNA 的某个维度或模板部分时,直接读取对标 DNA 文档 / template 中对应内容,把它当作用户提供的一条融合要求,更新基线 DNA;不需要重新抓取参考视频,也不需要重新生成对标视频的基线 report。 -2. **局部样本借鉴**:仅在用户明确希望引入原文证据、权重或 focus 时,才将对标视频重新生成属于基线 DNA 的 report,并用 `--focus` 限定采纳维度。 -3. **偏好转译**:用户表达“钩子再冲突一点”“口播更生活化”等要求时,按 `style-dna.md` 的用户输入转译更新,并落到 template 的具体执行字段。 -4. **明确不改 DNA**:仅作为本次选题或创作参考,不落盘到 DNA。 +1. **局部 DNA 融合**:读取对标 DNA 文档 / template 中对应内容,把它当作用户提供的融合要求,更新基线 DNA。 +2. **局部样本借鉴**:仅在用户明确希望引入原视频证据、权重或 focus 时,才将对标视频重新生成属于基线 DNA 的 report,并用 `--focus` 限定采纳维度。 +3. **偏好转译**:用户表达“分享动机更明确”“口播更真实”等要求时,按 `style-dna.md` 的用户输入转译更新。 +4. **明确不改 DNA**:仅作为本次选题或制作参考,不落盘到 DNA。 局部 DNA 融合流程: -1. 明确采纳范围:16 维中的维度、模板中的部分,或两者组合。 +1. 明确采纳范围:14 维中的维度、template 语义段,或两者组合。 2. 读取对标 DNA 文档 / template 的对应规则、适用条件和例外。 -3. 将其整理为一条可转译的输入,必须包含来源 `dna-id`、采纳范围和具体规则。 +3. 整理为一条可转译输入,包含来源 `dna-id`、采纳范围和具体规则。 4. 在基线 DNA 上执行无新增样本的 update: ```bash wx-channel-style-profiler update \ --dna wx_channel/dna/{base-dna-id}/{base-dna-id}.dna.md \ --template wx_channel/dna/{base-dna-id}/{base-dna-id}.template.md \ - --user-input "采纳 {benchmark-dna-id} 的钩子部分:xxx" + --user-input "采纳 {benchmark-dna-id} 的社交分享闭环:xxx" ``` -5. Agent 把该输入视作用户提供的参考要求,转译为基线 DNA 的 affected dimensions、聚合结论、创作规则和 template 执行字段。 -6. 融合时必须检查与基线证据、业务事实、合规边界和用户偏好的冲突;冲突保留说明,不静默覆盖。 - -更新后必须同步修订基线 DNA 文档与 template,并保留来源说明。 +5. Agent 把该输入转译为基线 DNA 的 affected dimensions、聚合结论、创作规则和 template 执行字段。 +6. 融合时检查与基线证据、业务事实、合规边界和用户偏好的冲突;冲突保留说明,不静默覆盖。 -对标分析的过程记录(样本清单、数据线索来源、比较结论)落盘 `wx_channel/calibration/`,供后续复盘引用。 +更新后同步修订基线 DNA 文档与 template,并保留来源说明。 diff --git a/crews/main/skills/expert-wx-channel/workflows/account-setup.md b/crews/main/skills/expert-wx-channel/workflows/account-setup.md index fdcfe228..38ffd7c0 100644 --- a/crews/main/skills/expert-wx-channel/workflows/account-setup.md +++ b/crews/main/skills/expert-wx-channel/workflows/account-setup.md @@ -90,7 +90,7 @@ 2. 目标 DNA 固定为 `dna-0`。 3. 每条生成 report。 4. 运行 `wx-channel-style-profiler build --dna-id dna-0`,生成 DNA 文档与 template。 -5. 把选题、标题文案、脚本结构、语气、可复用规则和例外发送用户确认。 +5. 把定位、选题、简介包装、账号简介、内容形式与比例、发布习惯、高数据创意、社交分享、口播 DNA 与制作管线发送用户确认。 6. 用户调整意见按 `style-dna.md` 的“用户输入转译”更新到 `dna-0`。 用户没有明确权重时,所有 report 默认权重为 1。账号级初始参考建议收集 10 条视频样本;用户明确提供单条或少量样本时仍可建立 `dna-0`,但必须标注覆盖不足,不得称为账号级稳定 DNA。 @@ -107,7 +107,7 @@ Agent 基于 `business_knowledge.md` 提炼搜索关键词,建议用户到视 #### 路径 C:用户拒绝外部参考或已有明确想法 1. 结合 `business_knowledge.md`、用户定位、目标观众、变现方式和用户思路,提炼 `dna-0`。 -2. Agent 直接按 16 维框架(v0)编写: +2. Agent 直接按 14 维账号级框架编写: ```text wx_channel/dna/dna-0/dna-0.dna.md @@ -116,7 +116,7 @@ wx_channel/dna/dna-0/dna-0.template.md 3. DNA 文档必须标明来源是“业务信息 + 用户指定方向”,没有视频样本统计。 4. 能确定的规则写为执行要求;未确定维度写“待校准”,不得虚构单条证据。 -5. Template 仍必须给出可执行的脚本分段生产结构;不能只有抽象定位词。 +5. Template 仍必须给出可执行的 Brief / 内容生产语义段;不能只有抽象定位词。 6. 将 DNA 摘要和待校准点发给用户确认,再按用户意见转译修订。 后续获得真实视频样本时,按 `style-dna.md` 生成 report 并更新 `dna-0`。 @@ -135,7 +135,7 @@ wx_channel/dna/dna-0/dna-0.template.md 检查要求: - DNA 文档说明样本来源、覆盖数量、权重、例外和待校准点。 -- Template 能由 DNA 文档推导,直接指导选题、标题与描述文案、封面、脚本分段、句式和 CTA。 +- Template 能由 DNA 文档推导,直接指导定位、选题、简介包装、内容形式、发布节奏、高数据创意、社交分享、互动与制作交接。 - 用户输入已转译为维度级规则,不是原话堆积。 - 对标样本 report 不混入 `dna-0`;用户采纳后,只有转译后的规则通过局部 DNA 融合进入 `dna-0`。 @@ -143,10 +143,10 @@ wx_channel/dna/dna-0/dna-0.template.md ### Step 1 - 获取老号样本 -1. 自己账号:用 `wx-channel-engagement list` 拿作品管理页最近 20 条的描述文案与行内指标(播放/点赞/评论/分享/收藏)作为数据线索。 +1. 自己账号:用 `wx-channel-engagement list` 拿作品管理页最近 20 条的视频简介与行内指标(播放/点赞/评论/分享/收藏)作为数据线索。 2. 完整口播文案仍需转写:用户提供视频文件或逐字稿;拿不到全部时,请用户至少提供最近 5-10 条的代表性文案。 -3. 非自己账号:无后台权限,样本全部由用户提供(文字稿、视频文件、截图数据)。 -4. 剔除重复、已删除和无完整文案的样本。 +3. 非自己账号:无后台权限,样本全部由用户提供(文字稿、视频文件、截图数据、账号简介、发布时间)。 +4. 剔除重复、已删除和无完整文案的样本;保留账号简介、发布时间、内容形式、素材来源与授权信息,缺失写「未观测」。 ### Step 2A:账号基本可用,用户让 Agent 接手 @@ -160,7 +160,7 @@ wx_channel/dna/dna-0/dna-0.template.md ### Step 2B:用户认为账号不太可用,需要推倒重来 -1. 基于 Step 1 拿到的样本提炼出常用署名、核心卖点、选题方向、账号名称、核心 CTA 等基础信息,将这些信息与用户确认。 +1. 基于 Step 1 拿到的样本提炼定位、账号简介、选题组合、简介包装、内容形式、发布节奏、核心卖点与 CTA 等基础信息,将这些信息与用户确认。 2. 结合上述信息,走「从零开始打造新号」流程。 3. 如果 `business_knowledge.md` 不全,可以先从 Step 1 的样本中提取,不足的信息与用户讨论。 @@ -179,4 +179,4 @@ wx_channel/dna/dna-0/dna-0.template.md | 成交少 | 承接路径断、信任状弱、促单模糊 | 强化信任状与限时促单,理顺挂车/直播路径 | | 记不住人 | 身份信号弱、人设张力弱 | 加入真实场景、短板、判断标准或系列任务 | -诊断结论按账号审计字段落盘 `wx_channel/calibration/`:当前定位、主页信号、内容垂直度、观看理由、标题/封面、钩子、人设张力、评论质量、承接路径、合集、合规风险、优先修复动作。 +诊断结论按账号审计字段落盘 `wx_channel/calibration/`:当前定位、主页信号、内容垂直度、观看理由、视频简介/封面、钩子、人设张力、评论质量、承接路径、合集、合规风险、优先修复动作。 diff --git a/crews/main/skills/expert-wx-channel/workflows/content-production.md b/crews/main/skills/expert-wx-channel/workflows/content-production.md index 317f06d7..3ba11fca 100644 --- a/crews/main/skills/expert-wx-channel/workflows/content-production.md +++ b/crews/main/skills/expert-wx-channel/workflows/content-production.md @@ -1,6 +1,8 @@ # 视频号内容生产 Workflow -从选题到发布的完整视频生产。用户说“帮我做一条视频”“出几条视频”“这条的主题我们也做一条”“用我们的风格改写这条的脚本”走这个。 +从选题、Brief 到发布的完整视频运营流程。用户说“帮我做一条视频”“出几条视频”“这条的主题我们也做一条”走这个。 + +**分工硬边界**:main agent 只做已有视频素材的简单加工,并负责长文 / 图文内容;视频全案只产出 Brief,成片制作委托 `content-producer`。若口播文案 DNA 已启用,main agent 写口播终稿并随 Brief 交付;其余声画制作归 CP。 ## Step 0 - 入口判断 @@ -43,7 +45,7 @@ wx_channel/dna/{dna-id}/{dna-id}.dna.md wx_channel/dna/{dna-id}/{dna-id}.template.md ``` -DNA template 是选题、标题与描述文案、封面、脚本分段、句式、语气和表达路线的直接执行依据;DNA 文档用于理解稳定性、例外和适用条件。 +DNA template 是 main agent 的内容生产 / Brief 输入模板,覆盖定位与核心传达、选题与简介包装、内容形式与发布节奏、高数据创意、互动系列、制作交接与可选口播文案 DNA。它不规定镜头表、逐句台词、转场或编码细节。 ### 2. 读取业务知识 @@ -62,7 +64,7 @@ DNA template 是选题、标题与描述文案、封面、脚本分段、句式 | 项目 | 规则 | | --- | --- | -| 主题 / 方向 | 用户给了明确主题时不得另起炉灶,仅按 DNA template 细化选题和标题文案 | +| 主题 / 方向 | 用户给了明确主题时不得另起炉灶,仅按 DNA template 细化选题和视频简介文案 | | 素材 | 用户提供的文案、笔记、截图、链接、数据、案例、实拍素材必须优先使用 | | 目标观众 | 未指定时按 `business_knowledge.md` 和 DNA 受众关系推导(视频号主力人群 30-55 岁,偏真实、有深度、有温度) | | 时长 | 未指定时按 DNA template,再无要求默认 30-60 秒 | @@ -80,7 +82,7 @@ DNA template 是选题、标题与描述文案、封面、脚本分段、句式 > Agent 的一般创作判断 ``` -DNA 约束的是选题角度、标题文案、结构、句式、语气和表达方式;不能覆盖用户指定的出镜人、事实、来源、CTA 和合规边界。 +DNA 约束的是账号定位、选题组合、简介包装、核心传达、内容形式、发布节奏、社交分享与制作边界;口播文案 DNA 只在明确启用时约束口播。DNA 不能覆盖用户指定的出镜人、事实、来源、CTA 和合规边界。 ## Step 2 - 素材获取与整理 @@ -88,7 +90,7 @@ DNA 约束的是选题角度、标题文案、结构、句式、语气和表达 1. 按类型获取输入: - 抖音 / B站 / 小红书视频链接 -> 调顶层 `viral-chaser` 下载、转写与拆解,拆解报告与逐字稿进 `materials/`。 - - 视频号链接 -> 无公开下载路径:请用户提供视频文件或文字稿;自己账号的作品可用 `wx-channel-engagement list` 补描述文案与数据线索。 + - 视频号链接 -> 无公开下载路径:请用户提供视频文件或文字稿;自己账号的作品可用 `wx-channel-engagement list` 补视频简介与数据线索。 - 其他链接 -> 用浏览器工具读取。 - 本地 `.md` / `.txt` -> 直接读取;视频文件确认可读并记录格式(支持 `.mp4` / `.mov` / `.avi` / `.webm`)。 - PDF / Word -> 先提取正文文本再进入流程。 @@ -146,25 +148,25 @@ DNA 约束的是选题角度、标题文案、结构、句式、语气和表达 - 小调直接改。 - 换主题、换观众、换转化目标 -> 重做本步。 -## Step 4 - 标题与描述文案 +## Step 4 - 视频简介文案 -视频号作品没有标题概念:短标题 + 描述文案一起构成作品包装,描述文案就是作品展示文本。 +视频号作品没有标题概念,只有**视频简介**;视频简介(≤300 字,含话题标签)就是作品展示文本。 -1. **短标题**(6-16 字,最长约 30 字):按 DNA template 列出的标题类型,每个类型各拟一个候选。 -2. **描述文案**(最长约 300 字):按 DNA template 的描述文案规则写,含话题标签(2-5 个,直接写在描述中)。 +1. **视频简介**(≤300 字):按 DNA template 的视频简介模式写,含 2-5 个话题标签,直接写在简介中。 +2. 给 2-3 个候选,分别说明各自的观看理由、核心传达与话题标签策略。 3. 有参考视频或草稿时,必须参考其文案内容(如有),但不得照抄。 -4. 用户已指定标题或文案原则时,候选必须在该约束内生成,不得偷换方向。 +4. 用户已指定简介或文案原则时,候选必须在该约束内生成,不得偷换方向。 -> 描述文案后面要原样传给 `published-track record --title`,本步定稿后不再改动;确需改动时入库记录同步重跑。 +> 视频简介后面要原样传给 `published-track record --title`(该参数是入库字段名,语义为完整视频简介),本步定稿后不再改动;确需改动时入库记录同步重跑。 -## 【确认】标题与描述文案 +## 【确认】视频简介文案 -第二个必停节点。务必与用户就短标题和描述文案确认。用户如果提出修改意见,按用户意见修改,直至与用户达成一致。 +第二个必停节点。务必与用户就完整视频简介确认。用户如果提出修改意见,按用户意见修改,直至与用户达成一致。 ## Step 5 - 封面图 -1. 读取 DNA template 的封面图风格和封面 AIGC 提示词要素。 -2. 结合最终标题、目标观众和本条核心收益,补齐本次封面的主体、场景、构图、色彩、光线、质感、文字视觉(封面三要素:身份 + 痛点 + 解决方案)和负向约束。 +1. 读取 DNA template 的简介包装与视觉语言,只取账号级风格边界。 +2. 结合本条核心传达、目标观众和封面文字承诺,确定本次封面的主体、色彩、光线、文字视觉(身份 + 痛点 + 解决方案)和负向约束。 3. 优先路径: - 有实拍 / 成片素材 -> 从素材中截取候选帧,交用户挑选。 - 无合适素材 -> 使用 `siliconflow-img-gen` 生成。 @@ -175,66 +177,60 @@ DNA 约束的是选题角度、标题文案、结构、句式、语气和表达 第三个必停节点。封面图确认,如果用户有意见,按照他的意见修改,直至确认。 -## Step 6 - 简报与脚本初稿 +## Step 6 - 制作 Brief -每条视频先写简报,再写脚本。简报与脚本都写入 `wx_channel/outputs//script.md`(简报在前,脚本在后)。 +每条需要 CP 全案制作的视频,main agent 只产出 `wx_channel/outputs//brief.md`。Brief 是 main / CP 的唯一分界点。 -### 简报字段 +### Brief 必填字段 ```markdown -# 视频简报 - -- 账号定位: -- 内容支柱: -- 目标人群: -- 观看理由: -- 人设张力: -- 用户痛点: -- 3秒钩子: -- 信任状: -- 卖点/价值: -- 促单/关注引导: -- 画面/证据: -- 评论引导: -- 承接路径: -- 风险检查: +# 视频制作 Brief + +- dna_id: +- pipeline:dna-ad-video-pipeline / video-producer:default / 未指定 +- MainAgent 交付物:Brief、视频简介、素材清单、(如启用)口播终稿 +- ContentProducer 交付物:成片、封面或候选帧、交付说明、自检结果 +- 账号定位与核心传达: +- 目标人群与观看理由: +- 选题与内容支柱: +- 用户痛点与信任状: +- 内容形式:口播 / 实拍拼接 / 创意转场 / 纯 AIGC 动画 / 混合 +- 素材清单、来源与授权: +- 视觉与声音边界:横竖屏、画面风格、音色/BGM 倾向(不写逐镜细节) +- 互动与分享目标:评论、关注、转发、私信、直播预约选一个主目标 +- 时长带与交付验收: +- 风险检查:事实、承诺、授权、平台合规 - 发布后重点观察: ``` -### 脚本要求 - -严格按 DNA template 写脚本: +### 口播终稿规则 -1. 按 template 的脚本分段(默认:钩子、共情、信任状、价值、收尾)执行每段任务、切入方式、句式节奏和语气;分段时间分配参考 DNA 文档的结构结论。 -2. 前 3 秒必须落实钩子维度的执行规则;身份信号在前 3 秒可识别。 -3. 脚本标注字段:时间轴、景别、口播词、画面/字幕、BGM。 -4. 用户必用素材优先进入脚本,不得为了模板形状删除关键事实。 -5. 商业承诺、服务范围、价格和案例必须与 `business_knowledge.md` 一致。 -6. 用户指定 CTA 时逐字或按原意执行;未指定时只选择一个与业务目标匹配的行动指令。 -7. 收尾必须包含评论引导(优先选择题式提问)与转发引导(命中转发动机的明确话术)。 -8. 同主题改写:只改创作风格和结构呈现,不改事实含义;数据、案例、引用和结论边界保持一致。 +- **口播文案 DNA 已启用**:main agent 按 `narration-dna` 写口播终稿,保存为 `voiceover.md` 并在 Brief 中引用;这等同写一篇长文,CP 不重写策略文案,只做声画制作。 +- **口播文案 DNA 未启用**:Brief 不规定逐句口播,CP 在 Pipeline 内完成脚本与声画方案。 +- 用户必用事实、案例、承诺和 CTA 必须进入 Brief 或口播终稿,不得为了形式删除关键事实。 -## 【确认】脚本 +## 【确认】Brief -第四个必停节点。按用户要求修改,直至与用户达成一致。 +第四个必停节点。用户确认 Brief(及已启用的口播终稿)后才委托 CP。Brief 变更时更新版本并推送变更要点;已开工中间产物按新版取舍,弃用部分记入交付说明。 ## Step 7 - 视频制作 -脚本定稿后取得成片 `wx_channel/outputs//video.mp4`: +取得成片 `wx_channel/outputs//video.mp4`: | 情况 | 处理 | | --- | --- | | 用户直接提供成片 | 校验格式(`.mp4` / `.mov` / `.avi` / `.webm`)与时长,复制到作品目录 | -| 用户有素材但需加工 | 口播轻剪辑(去口癖 / 剪集锦)走顶层 `talking-head-cut`;拼接、字幕、BGM、片头片尾走顶层 `video-edit` | -| 从零制作 | 委托 `content-producer` subagent,交付 `script.md` 与 DNA template 的制作要求(时长、形态、镜头、BGM);成片回收后校验 | +| 用户有素材但需简单加工 | 口播轻剪辑(去口癖 / 剪集锦)走顶层 `talking-head-cut`;拼接、字幕、BGM、片头片尾走顶层 `video-edit` | +| 全案制作 | 委托 `content-producer`,只交付确认后的 Brief;若 Brief 指定 Pipeline,CP 必须采用,未指定则 CP 自由发挥 | -成片必须与脚本一致;制作中发现脚本无法执行(素材缺失、时长超标)时回到【确认】脚本节点处理,不擅自改脚本。 +成片必须兑现 Brief 与口播终稿(如有);制作中发现 Brief 无法执行(素材缺失、时长超标)时回到【确认】Brief 节点处理,不擅自改策略。 ## Step 8 - 存文件 定稿作品目录 `wx_channel/outputs//`: -- `script.md`(简报 + 定稿脚本) +- `brief.md`(定稿 Brief) +- `voiceover.md`(仅口播文案 DNA 启用时) - `video.mp4` 成片(或 `.mov` / `.avi` / `.webm`) - `cover.jpg` 封面图 - `materials/` 原始素材(Step 2 已放入) @@ -246,7 +242,7 @@ DNA 约束的是选题角度、标题文案、结构、句式、语气和表达 1. 打开创作者中心发布页,检查 `wechat-channel` session 登录态;失效时走该工具的无头截图扫码登录流,**Stop and wait** 等用户扫码确认。 2. 上传 `video.mp4`,等待上传与转码完成(最长 3 分钟)。 -3. 填短标题(Step 4 定稿),填描述文案(Step 4 定稿,含话题标签)。 +3. 只填视频简介(Step 4 定稿,含话题标签);不要把视频简介拆成短标题,也不要另填标题。 4. 点击“发表”;弹出原创声明弹窗时按工具说明处理。发布后用户会在手机端补充原创声明等事项。 5. 发布成功后在视频列表页等转码完成,通过分享面板复制视频链接(`https://weixin.qq.com/sph/xxxx`);审核中拿不到链接时留空,后续补充。 6. 遇到「session wechat-channel 正忙」(fail-first 队列)时等当前操作完成再重试,不盲试、不 close。 @@ -262,10 +258,10 @@ published-track record \ --platform wx_channel \ --source-folder wx_channel/outputs// \ --account <发布所用账号> \ - --title "" \ + --title "" \ --publish-url "" ``` -- `--title` **必须传完整描述文案**(含 hashtag),不要传短标题——`wx-channel-engagement` 抓取按它匹配后台作品。 +- `--title` 是数据库字段名,**必须传完整视频简介**(含 hashtag)——`wx-channel-engagement` 抓取按它匹配后台作品。 - `dna_id` 自动从 `dna-meta.json` 读取。 - 发布时没拿到链接的,后续用户提供或抓取到链接后用相同 `--source-folder` 重跑 `published-track record` 补 `--publish-url`——upsert 语义升级记录,不重复插行。 diff --git a/crews/main/skills/expert-wx-channel/workflows/editing.md b/crews/main/skills/expert-wx-channel/workflows/editing.md index b4df5d82..8d6f42b8 100644 --- a/crews/main/skills/expert-wx-channel/workflows/editing.md +++ b/crews/main/skills/expert-wx-channel/workflows/editing.md @@ -9,11 +9,11 @@ | 用户的说法 | 改的层级 | 怎么走 | |-----------|---------|--------| | “润色一下” / “口播顺一顺” | 口播措辞层 | 轻改,直接上手 | -| “开头不行 / 换个钩子” | 钩子层 | 按目标 DNA 的 hook-design / opening-pace 维度重做前 3 秒 | -| “换个语气” / “更接地气” | 内容风格层 | 选目标 DNA -> 按 template 改 -> 自检 | +| “开头不行 / 换个钩子” | 口播层 | 口播文案 DNA 已启用时按 `narration-dna` 重做;未启用时按用户要求与内容形式重做,不虚构 DNA 规则 | +| “换个语气” / “更接地气” | 内容风格层 | 核对 `positioning-core`、`narration-dna` 与用户要求 -> 改稿 -> 自检 | | “结构调一下” / “时间分配不对” | 结构层 | 先出调整方案(新分段 / 时间轴)-> 确认 -> 改 | | “方向不对,重做” | 方向层 | 回到 content-production.md 从选题重新走 | -| “换个封面” | 封面层 | 按目标 DNA 的封面图维度重出候选 -> 确认 | +| “换个封面” | 封面层 | 按 `description-packaging` 与 `visual-language` 重出候选 -> 确认 | | “压缩到 X 秒” / “展开到 X 秒” | 时长层 | 先问清楚保留什么砍掉什么 | ## 轻改(口播措辞 / 润色 / 去 AI 腔) @@ -26,8 +26,8 @@ 只重做前 3 秒,不动正文: -1. 读目标 DNA 的 hook-design 与 opening-pace 维度(目标 DNA 默认当前绑定的 DNA;用户说“照着这条的钩子来”时先按 `style-dna.md` 提炼该条的钩子信号)。 -2. 按钩子类型(矛盾前置 / 数据冲击 / 场景代入 / 悬念留白 / 结果反差)给 2-3 个候选,标明每个候选的冲突点和身份信号。 +1. 读目标 DNA 的 `narration-dna`;未启用时先记录「口播 DNA 未观测」,以用户要求和本条事实为准。 +2. 给 2-3 个候选,标明每个候选的冲突点、身份信号与核心传达。 3. 用户选定后同步改脚本对应段落与封面文案(如封面承载了旧钩子)。 ## 换风格 @@ -38,17 +38,17 @@ - 用户给了对标账号 -> 用那个号的 DNA(如果有,没有的话,通过 `style-dna` 新建) 改完先给开头(前 3 秒 + 共情段)预览,用户觉得味道对了再全文改,省得返工。 -全文改完对照目标 DNA template 逐项自检(无数字打分环节;发布后表现由 DNA 表现评估闭环跟踪)。 +全文改完对照定位、选题包装、内容形式、社交分享目标与口播 DNA(如启用)自检;发布后表现由 DNA 表现评估闭环跟踪。 ## 换结构 先出结构调整方案(新的分段、时间轴、素材位置),用户确认后再改脚本。 结构动了通常风格也要跟着调——别只动骨架不换肉。 -结构调整按 DNA template 的脚本分段重新出大纲(分段时间分配、口播任务、画面/字幕位置)。 +结构调整按核心传达、内容形式与口播 DNA(如启用)重新出大纲;具体分镜与画面位置属于制作执行,不在账号 DNA 中规定。 ## 换封面 -1. 读目标 DNA 的封面图维度(封面三要素:身份 + 痛点 + 解决方案)。 +1. 读目标 DNA 的 `description-packaging` 与 `visual-language`,保留封面三要素:身份 + 痛点 + 解决方案。 2. 按新主题出 2-3 个候选:素材截帧优先,无合适素材走 `siliconflow-img-gen` 生成。 3. 用户确认后替换 `wx_channel/outputs//cover.jpg`。 @@ -70,5 +70,5 @@ 用户要求改完后发布 / 重新发布时,走 `content-production.md` 的成片后流程(Step 7-10): 1. **成片同步**:脚本改动涉及画面或口播的,按 `content-production.md` Step 7 重新走视频制作(已有成片只需轻剪辑时走 `talking-head-cut` / `video-edit`)。 -2. **存文件**:脚本与成片归位 `wx_channel/outputs//`,同步更新 `script.md`、`cover.jpg` 与 `dna-meta.json`。 -3. **发布 + 入库**:按 `wechat-channels-publish` 工具说明发布,再按 `content-production.md` Step 10 调 `published-track record` 入库——`--title` 仍传完整描述文案;描述文案改过的,用新文案重跑记录。 +2. **存文件**:稿件与成片归位 `wx_channel/outputs//`,同步更新 `brief.md`、`voiceover.md`(如有)、`cover.jpg` 与 `dna-meta.json`。 +3. **发布 + 入库**:按 `wechat-channels-publish` 工具说明发布,再按 `content-production.md` Step 10 调 `published-track record` 入库——`--title` 仍传完整视频简介;视频简介改过的,用新文案重跑记录。 diff --git a/crews/main/skills/expert-wx-channel/workflows/review.md b/crews/main/skills/expert-wx-channel/workflows/review.md index ae33bd2c..840e79a2 100644 --- a/crews/main/skills/expert-wx-channel/workflows/review.md +++ b/crews/main/skills/expert-wx-channel/workflows/review.md @@ -29,20 +29,20 @@ 视频号助手作品管理页只给这 5 项行内指标。**完播率、社交推荐占比、观众来源构成**不在抓取范围内,需要用户提供创作者中心截图后才能作为证据;拿不到时相关漏斗层只能从相对趋势推断,并在报告中注明。 -### 互动漏斗 → template 部分 → 16 维映射 +### 互动漏斗 → template 语义段 → 14 维映射 -| 漏斗卡点 | 先怀疑的 template 部分 | 可回溯的 DNA 维度 | -|---------|----------------------|------------------| -| 播放低(曝光/推荐不足) | 选题、标题与描述、封面 | topic-angle、title-desc、cover-image | -| 完播低(前段流失,需用户提供完播数据) | 钩子部分、共情部分 | hook-design、opening-pace、duration-form | -| 中段流失(完播曲线塌腰) | 价值部分 | visual-pacing、value-density、script-structure | -| 分享低 | 价值部分、收尾部分 | share-motive、value-density、tone-persona | -| 评论低 | 共情部分、收尾部分 | interaction-design、tone-persona | -| 收藏低 | 价值部分 | value-density | -| 关注少 | 收尾部分 + 主页承诺 | cta-funnel、topic-angle | -| 转化少(私信/成交) | 收尾部分、信任状部分 | cta-funnel、credibility-proof | +| 漏斗卡点 | 先怀疑的 template 语义段 | 可回溯的 DNA 维度 | +|---------|--------------------------|------------------| +| 播放低(曝光/推荐不足) | 定位与核心传达、选题与简介包装 | positioning-core、topic-portfolio、description-packaging | +| 完播低(需用户提供完播数据) | 内容形式与发布节奏、口播文案 DNA | content-form-mix、narration-dna、high-performer-patterns | +| 中段流失 | 高数据创意模式、内容形式与发布节奏 | high-performer-patterns、content-form-mix | +| 分享低 | 互动与系列 | social-share-loop、positioning-core、high-performer-patterns | +| 评论低 | 互动与系列 | engagement-conversion、social-share-loop | +| 收藏低 | 高数据创意模式 | high-performer-patterns | +| 关注少 | 定位与核心传达、互动与系列 | bio-profile、series-signature、engagement-conversion | +| 转化少(私信/成交) | 定位与核心传达、互动与系列 | positioning-core、engagement-conversion | -交叉判断:完播高、分享低 → 内容好看但缺社交价值;分享高、完播低 → 钩子或标题虚,正文兑现不足。 +交叉判断:完播高、分享低 → 内容好看但缺社交价值;分享高、完播低 → 视频简介或定位承诺与内容兑现不足。 ### 平台混杂因素(归因前必排) @@ -80,7 +80,7 @@ content-calibrator eval --platform wx_channel --dna-id # 指定 DNA 1. 判定只看比值与走向,绝对值只作上下文。 2. 逐条排除平台混杂因素,特别注意长尾效应——近 3-7 天发布的数据成熟度不足时降级为观察。 -3. 回读 `wx_channel/dna//.dna.md` / `.template.md` 与待评估作品脚本(`source_folder` 内 `script.md`),把趋势变化落到 template 部分与 16 维。 +3. 回读 `wx_channel/dna//.dna.md` / `.template.md` 与待评估作品(`source_folder` 内 `brief.md`、`voiceover.md` 或成片说明),把趋势变化落到 template 语义段与 14 维。 4. 分享率异常(过高或过低)时优先核对转发动机设计是否命中,这是视频号区别于其他平台的第一归因点。 ### Step 3 - 报告与标记 diff --git a/crews/main/skills/expert-wx-channel/workflows/style-dna.md b/crews/main/skills/expert-wx-channel/workflows/style-dna.md index eebb1b6e..83362b48 100644 --- a/crews/main/skills/expert-wx-channel/workflows/style-dna.md +++ b/crews/main/skills/expert-wx-channel/workflows/style-dna.md @@ -1,45 +1,36 @@ -# 视频号风格 DNA 创建与更新 Workflow +# 视频号账号级 DNA 创建与更新 Workflow -本 Workflow 只负责风格 DNA 三层产物的创建与更新:判断样本来源、选择目标 DNA、获取样本文字稿与封面、调用 `wx-channel-style-profiler` 生成或更新 report / DNA 文档 / template。16 维(v0)提取与聚合方法以 `wx-channel-style-profiler` 为准。 +本 Workflow 负责 DNA report、DNA 文档与 DNA template 的创建与更新。维度框架以 `wx-channel-style-profiler` 的 `references/account-dna-framework.md` 为准(账号级 DNA v1)。 -边界说明: +## 边界 -- 账号初始化、定位梳理与默认 `dna-0` 初始化走 `account-setup.md`(该 workflow 会调用本 workflow 的样本获取与更新机制)。 -- 账号对标分析走 `account-benchmark.md`;本 workflow 只承担对标 DNA 的三层产物生成与更新,以及采纳后并入基线 DNA 的更新。 -- DNA 如何被用于视频生产(含仿照创作、脚本改写)由 `content-production.md` 规定,改稿由 `editing.md` 规定;本 workflow 不描述生产过程。 - -DNA 描述选题、标题与描述文案、封面、脚本结构与表达策略。视频成片的制作工艺(剪辑、特效)不属于 DNA。 - -> **维度版本**:16 维为 v0 定稿(2026-08-27 用户确认,见 `wx-channel-style-profiler` 的 `references/video-dna-dimensions.md`);后续维度如有升版本调整,历史 report 的维度编号以生成时版本为准。 +- DNA 是账号级运营框架,不是单条视频脚本或剪辑细节。 +- 账号初始化与默认 `dna-0` 建立走 `account-setup.md`;对标样本先走 `account-benchmark.md`。 +- DNA 如何用于内容生产走 `content-production.md`;改稿走 `editing.md`;数据复盘走 `review.md`。 +- DNA 指导 main agent 出内容或视频 Brief;全片制作委托 `content-producer`。 ## 入口判断 走本 Workflow: -- “建 DNA / 更新 DNA / 提炼这条视频的风格” +- “建 DNA / 更新 DNA / 提炼这个账号的风格” - “把这条视频落到某个 DNA 上” -- “提取下这个账号的视频风格 DNA” +- “用户提供单条或多条视频,帮我提炼可复用模式” 不走本 Workflow: -- “帮我做条视频 / 照着这条仿一条 / 这个主题我们也做一条”(使用 DNA 生产)-> 走 `content-production.md` -- “改改这条的脚本 / 换个钩子” -> 走 `editing.md` -- “看数据 / 复盘 / 诊断这条” -> 走 `review.md` -- “起号 / 梳理账号定位” -> 先走 `account-setup.md` -- “分析对标账号 / 对比风格差异” -> 走 `account-benchmark.md` +- 做内容 / 仿内容 → `content-production.md` +- 改脚本 / 换封面 / 压缩时长 → `editing.md` +- 看数据 / 评估 DNA → `review.md` +- 起号 / 定位 → `account-setup.md` +- 对标比较 → `account-benchmark.md` ## 目标 DNA 选择 -1. 用户明确指定 `dna-id` 时,使用该 DNA。 -2. 用户明确说“落到默认 DNA”或未指定目标时,使用 `dna-0`。 -3. 用户意图是账号对标、模式对比时,走 `account-benchmark.md`,对标样本必须进入独立 `dna-id`,不得直接写入 `dna-0`。 -4. 找不到目标 DNA 文档时,先走 `account-setup.md` 建立 `dna-0`,或按用户明确指定的新 `dna-id` 初始化。 - -默认规则: - -- 除 `account-benchmark.md` 分流的对标样本外,未特别分流的新参考视频样本累积到 `dna-0`。 -- `dna-0` 不代表某个参考账号,而是当前工作区的默认内容生产规则集。 -- 用户可以随时把样本、偏好或局部借鉴明确落到任意已有 DNA。 +1. 用户指定 `dna-id` 时使用该 DNA。 +2. 未指定或说“默认 DNA”时使用 `dna-0`。 +3. 对标样本必须进入独立 DNA,不直接写入 `dna-0`;采纳后再通过局部融合更新。 +4. 目标 DNA 不存在时,先走 `account-setup.md` 或按用户明确指定的新 `dna-id` 初始化。 ## 存储结构 @@ -53,59 +44,53 @@ wx_channel/dna/{dna-id}/ {dna-id}.template.md ``` -- 原始文字稿(口播脚本 / 逐字稿)可以临时保存在 `wx_channel/ref/{dna-id}/transcripts/`;也可以来自用户提供的任何位置,或由 Agent 转写后保存。 -- 生成后的 DNA report 必须进入目标 DNA 的 `reports/` 目录。 -- `sample-id` 必须可读且稳定;覆盖同名 report 前,先向用户说明该文件会被重算。 +原始脚本可临时放在 `wx_channel/ref/{dna-id}/transcripts/`。生成后的 report 必须进入目标 DNA 的 `reports/` 目录;覆盖同名 report 前先向用户说明。 ## 样本获取 -视频号没有公开文案抓取路径,样本一律先落成文字稿再进 profiler: - | 来源 | 处理 | | --- | --- | -| 用户提供口播脚本 / 逐字稿(`.md` / `.txt`) | 直接作为 profiler 输入 | -| 用户粘贴的文案、拆解笔记 | Agent 整理保存为 `.md` 再输入 | -| 本地视频文件(无文字稿) | 先用顶层 `talking-head-cut` ASR 转写拿逐字稿,保存为 `.md` 再输入;画面特征由 Agent 观看或用视觉模型抽帧补齐 | -| 抖音 / B站 / 小红书视频链接 | 走顶层 `viral-chaser` 下载 + 转写 + 拆解,产出拆解报告与逐字稿后作为输入(跨平台样本只借鉴结构,不照搬平台调性) | -| 视频号链接(`weixin.qq.com/sph/...`) | 无公开下载与文案路径:请用户提供文字稿或视频文件;自己账号的作品可用 `wx-channel-engagement list` 补描述文案与数据线索 | -| 自己账号的已发布作品 | `wx-channel-engagement list` 只能拿描述文案与行内指标;完整口播仍需转写或用户提供,描述文案不能冒充逐字稿 | -| `.docx` / 其他格式 | Agent 先提取为 `.md` / `.txt` 再进入 profiler | - -处理样本时: - -1. 剔除重复、已删除、纯搬运和无完整文案的样本。 -2. 保留来源信息(视频链接、账号名、发布时间、获取时间)作为报告线索;链接拿不到时保留用户口述来源。 -3. Profiler 本身不限制样本量;一个样本生成单篇 report,多个样本聚合统计。账号级初始化、老号诊断和对标比较可在 `account-setup.md` / `account-benchmark.md` 设置最低样本要求。 -4. 播放、互动等数据只能来自用户提供或 `wx-channel-engagement`(仅限自己账号),不得编造。 -5. 封面图优先用户提供的截图,或从视频文件抽帧截取;没有封面时保留缺失状态,不得用正文截图或想象补齐。 +| 用户提供脚本 / 逐字稿 | 整理为 `.md` / `.txt`,保留描述、数据与账号线索 | +| 用户提供视频文件 | 先取得文字稿或确认转写;视频观测信息由用户/原视频补充 | +| 用户提供账号信息 | 记录昵称、简介、置顶、发布时间与内容形式 | +| 用户想法 / 偏好 | 不生成 report,按用户输入转译进入 DNA | -## 建立或重建 DNA +每条样本尽量收集: -适用场景:目标 DNA 下还没有 report,或用户要求基于当前输入整体重建。 +- 样本类型:账号作品 / 用户提供单条 +- 账号名、简介、主页承诺 +- 发布时间、内容形式、素材来源与授权 +- 播放、完播、点赞、评论、转发等数据线索 +- 横竖屏、时长、口播/实拍/AIGC 形态、真人出镜占比 -### Step 1 - 准备样本 +缺失字段写「未观测」。视频号无公开对标抓取路径,数据必须来自用户或后台工具,不得编造。 -把可用文字稿整理为 `.md` / `.txt`,并确定目标 `dna-id`。未指定时使用 `dna-0`。 +## 建立或重建 DNA + +### Step 1 - 准备样本 -### Step 2 - 生成单篇 report +1. 把脚本/逐字稿整理为 `.md`,首个一级标题写样本标签 / 视频简介摘要。 +2. 确定目标 `dna-id`、`sample-id`、样本权重和 focus。 +3. 整理账号观测与视频观测信息,供 Agent 补进 report。 -每条视频执行一次: +### Step 2 - 生成单条 report ```bash wx-channel-style-profiler report \ - --input path/to/transcript.md \ + --input path/to/script.md \ --dna-id {dna-id} \ --sample-id {sample-id} \ --cover-image path/to/cover.jpg \ - --source-video "https://weixin.qq.com/sph/xxxx" + --source-url "https://channels.weixin.qq.com/..." \ + --output-dir wx_channel/dna/{dna-id}/reports ``` -可选参数: - -- `--weight N`:用户明确强调某条参考价值时使用。 -- `--focus DIMENSION`:用户明确说只借鉴钩子、文案、结构等局部时使用,可重复传入。 +生成 scaffold 后必须: -Agent 生成 scaffold 后必须回读文字稿,补齐 16 维单篇结论、脚本与画面证据、可复用信号,并补齐「视频信息」区(时长、形态、出镜占比、镜头字幕、BGM、数据线索——拿不到写未提供);封面图维度必须由视觉模型读取本地封面图,输出可复现的 AIGC 提示词要素。 +1. 补齐「视频信息」与「样本与账号观测」。 +2. 回读脚本原文,补齐 14 维的单条结论、证据和可复用信号。 +3. 单条样本不得推导账号比例、发布节奏、社交分享闭环或高数据共性。 +4. 视觉语言必须有图片/关键帧证据;口播文案 DNA 样本不足时保持未启用。 ### Step 3 - 聚合 DNA @@ -113,88 +98,55 @@ Agent 生成 scaffold 后必须回读文字稿,补齐 16 维单篇结论、脚 wx-channel-style-profiler build --dna-id {dna-id} ``` -Agent 聚合时必须读取全部 report,结合权重、focus、高频共性、高权重偏好、孤例和例外,修订: - -```text -wx_channel/dna/{dna-id}/{dna-id}.dna.md -wx_channel/dna/{dna-id}/{dna-id}.template.md -``` +Agent 必须读取全部 report,按权重/focus 聚合: -Template 聚合要求: - -1. 开头两项固定为选题、标题(含封面图,视频号还包括短标题、描述文案与全局制作要求:时长与节奏、镜头与真人出镜、BGM 与音效)。 -2. 脚本分段默认按五个语义部分输出:钩子、共情、信任状、价值、收尾;这是脚手架默认骨架,分段数量以 DNA 文档的结构结论为准,不为凑齐段数编造样本没有的规则。 -3. 每个部分必须写入 DNA 文档中对应维度的执行要求,包括本段任务、切入或推进方式、句式、语气、素材、必须做和避免项。 -4. 所有模板规则必须能从 DNA 文档推导。 - -多个样本时,定性结论必须说明覆盖多少条;单条特征只能写成单条观察,不得伪装成稳定共性。 +- 高频共性、高权重偏好、局部借鉴、孤例、例外分开写。 +- 标注样本覆盖度;单条/少量样本不得称为稳定账号 DNA。 +- 高数据内容要回读创意与内容形式,不能只归因播放量。 +- 为每个维度写聚合结论、报告依据和可执行规则。 +- 确保 DNA 文档能推导 template。 ## 更新已有 DNA -适用场景:目标 DNA 已存在,新增样本、偏好、局部借鉴或表现反馈。 - ### 新增样本 -1. 先为新视频生成属于目标 DNA 的 report。 -2. 再执行: +生成新 report 后运行: ```bash wx-channel-style-profiler update \ - --input wx_channel/dna/{dna-id}/reports/{sample-id}.report.md \ + --input wx_channel/dna/{dna-id}/reports/{new-sample}.report.md \ --dna wx_channel/dna/{dna-id}/{dna-id}.dna.md \ --template wx_channel/dna/{dna-id}/{dna-id}.template.md ``` -3. Agent 根据新的加权统计和 16 维证据,同步修订 DNA 文档与 template。 +再由 Agent 重新审视聚合结论并同步修订 template。 ### 用户偏好 -用户意见不是直接入库的规则。Agent 必须先理解其指向,再转译到具体维度和执行要求,例如“开头冲突再前置一点”应落到前3秒钩子、开场节奏、口播语言等维度。 - -来自另一个 DNA 文档或 template 的局部结论,也按用户提供的参考要求处理:必须记录来源 `dna-id`、采纳范围、具体规则和冲突说明,再转译到当前 DNA 的对应维度与 template。它不要求重新抓取或重新提取原文。 - -可传入: - -```bash ---user-input "结尾多用选择题引导评论" -``` - -转译结果写入 DNA 文档的“用户输入转译区”,并以可执行规则进入 template 的对应部分。原话不能成为 template 里的抽象口号。 +用户偏好不直接入库。通过 `--user-input` 传入后,Agent 映射到具体维度,并把原话转译为账号级规则与 Brief 规则。 ### 局部借鉴 -用户只希望借鉴某条的钩子、文案或结构时: - -1. 为该视频生成目标 DNA 的 report。 -2. 用 `--focus` 限定参与影响的维度。 -3. 更新 DNA 文档与 template,并在报告中保留来源和 focus 说明。 +对标 DNA 的局部规则必须先说明来源、适用条件和影响维度,再融合进目标 DNA;不得整包照搬。 ### 表现反馈 -来源:`content-calibrator` 的 DNA 表现评估报告(`wx_channel/dna/{dna-id}/evals/*.eval.md`)。评估回答「这个 DNA 好不好、哪些部分好/不好」,本 workflow 负责把**用户确认采纳**的评估结论转译进 DNA。 - -1. 前提:评估报告已存在,且用户逐条确认了要采纳的建议(未确认的建议不动 DNA)。 -2. 每条采纳建议按参考输入处理:转译到具体维度和执行要求(如「分享率持续走低,内容缺社交价值」→ 转发动机设计 + 价值密度维度),经 `wx-channel-style-profiler update --user-input` 传入。 -3. 转译结果写入 DNA 文档的「表现反馈区」(与「用户输入转译区」并列),每条记录:来源 eval 文件、affected dimensions、DNA 文档修改、template 修改。 -4. 同步修订 template 对应部分;表现反馈只改规则表达,不引入样本未覆盖的新风格。 -5. 趋势类证据(比值走向)只支持方向性调整(加强/弱化既有规则),不支持凭空新增维度规则——新增规则仍需样本或用户输入支撑。 +复盘产生的建议先列证据与影响维度,经用户确认后写入 DNA;不得把一次数据波动直接升格为账号规则。 ## DNA 使用接口 -本 Workflow 不描述如何用 DNA 生产:生产 workflow 自行读取 `wx_channel/dna/{dna-id}/{dna-id}.dna.md` 与 `{dna-id}.template.md`,按 template 执行,见 `content-production.md` / `editing.md`。 - -反馈回流判定:来自生产、改稿或复盘的成片/脚本修改意见,先判断是否可复用偏好——只有可复用偏好才经本 workflow「更新已有 DNA」进入 DNA;单次修改留在制作记录,不动 DNA。参考视频的风格吸收(“把这条的风格融入到我们的 DNA”)属于本 workflow 的新增样本 / 局部借鉴场景,不是生产流程的一部分。发布数据驱动的风格优化走「表现反馈」:`content-calibrator` 评估产出建议 → 用户逐条确认 → 本 workflow 转译进 DNA;评估本身不改 DNA。 +- **内容生产**:读取 DNA 文档与 template,确定定位、选题、简介包装、内容形式、发布节奏和高数据创意。 +- **视频全案**:main agent 产出 Brief;Brief 写明 Pipeline、素材授权、验收标准、交付边界。未指定 Pipeline 时 CP 自由发挥,指定时必须采用。 +- **口播类视频**:若口播文案 DNA 已启用,main agent 写口播文案并随 Brief 交付;CP 不重写策略文案。 +- **图文/长文**:main agent 直接生产。 ## 对标接口 -对标账号或一组对标视频不得默认落入现有 DNA;分析流程、逐项对比与采纳判断以 `account-benchmark.md` 为准。本 Workflow 只承担两件事: - -1. 对标 DNA 的三层产物生成与更新:使用独立 `dna-id`(如 `dna-benchmark-{slug}`,同组后续更新复用),样本获取、report、build/update 同本 workflow 的创建/更新流程。 -2. 用户采纳后,按「更新已有 DNA」执行并入基线 DNA 的更新:局部 DNA 融合、局部样本借鉴或偏好转译;默认优先局部 DNA 融合,不要求重新提取原文。 +对标样本进入独立 `dna-id`;比较时输出定位、选题、简介包装、内容形式、发布节奏、高数据创意、社交分享闭环与制作管线的差异。用户明确采纳后才融合进 `dna-0`。 ## 编排原则 -- Workflow 负责选择路径和衔接工具,不复制 profiler 的 16 维定义。 -- Agent 判断必须回读文字稿和 report,不能只依赖统计表。 -- 样本统计描述内容模式,不替代事实核查、合规审核和商业判断。 -- 用户确认只用于初始化方向或采纳建议,不是登记资产的前置门槛。 +- 一个生产任务只使用一个 DNA;需要融合时先更新 DNA。 +- 样本、用户输入、数据反馈必须可追溯。 +- 账号级结论必须有覆盖度;不足就写未观测。 +- Template 只写 main agent 可执行的输入/Brief 规则,不写成片制作细节。 diff --git a/crews/main/skills/expert-xhs/SKILL.md b/crews/main/skills/expert-xhs/SKILL.md index 4bd9adf6..69d4f40d 100644 --- a/crews/main/skills/expert-xhs/SKILL.md +++ b/crews/main/skills/expert-xhs/SKILL.md @@ -1,12 +1,12 @@ --- name: expert-xhs -description: 小红书运营专家。承接从定位起号、对标调研、选题文案、图文笔记生产发布到数据复盘的完整运营工作。零散的发布、下载笔记、抓数等操作也可以直接做。 +description: 小红书账号运营专家。承接定位起号、账号级 DNA、搜索意图、图文笔记生产、已有素材轻加工、视频全案 Brief、发布与数据复盘;全片制作委托 content-producer。 metadata: openclaw: emoji: 📕 --- -# 小红书运营专家 +# 小红书账号运营专家 ## 预设 Workflow @@ -15,7 +15,7 @@ metadata: | 场景 | Workflow | 什么时候触发 | |------|----------|-------------| | 内容 DNA 管理 | Style DNA | 建 / 更新内容 DNA(样本、偏好、局部借鉴、对标融合),决定样本落到哪个 DNA | -| 内容生产 | Content Production | 写一篇 / 做几篇小红书笔记(图文为主,视频笔记也可);输入可以是粗略想法、参考笔记(仿写 / 同主题改写)或已有草稿(风格转写) | +| 内容生产 | Content Production | 做一条 / 做几条小红书内容;图文由 main 直接生产,视频全案只产出并委托 Brief | | 起号与定位 | Account Setup | 新号起号、定位梳理、内容支柱搭建、老号接手与诊断 | | 账号对标 | Account Benchmark | 对标账号 / 对标笔记分析(关键词提取 + 低粉爆款搜索),并与默认或指定 DNA 逐项比较 | | 改稿与调整 | Editing | 改标题、改正文、换封面、换标签、换风格 | @@ -37,7 +37,7 @@ metadata: | 工具 | 用途 | 命令 | |------|------|------| -| `xhs-style-profiler` | 生成单篇笔记 16 维 DNA report,并聚合 DNA 文档与 DNA template | `xhs-style-profiler` | +| `xhs-style-profiler` | 生成单篇笔记的账号级 DNA report,并聚合 DNA 文档、搜索意图地图与 Brief template | `xhs-style-profiler` | | `xhs-content-ops` | 图文笔记下载(正文 / 图片 / 互动数据),对标与 DNA 采样的取数主力 | `xhs-content-ops` | | `xhs-publish` | 图文 / 视频笔记发布(creator COS 上传 + web_api,含登录态两步管理) | `xhs-publish` | | `xhs-engagement` | 创作者后台互动数抓取,写入 published-track 的 pub_xhs 表 | `xhs-engagement` | @@ -46,7 +46,11 @@ metadata: ## 风格与 DNA -账号内容风格 DNA 存储目录是 `xhs/dna/`。未指定 DNA 时默认使用并更新 `dna-0`。生产前同时读取 DNA 文档与 DNA template;对标分析先建立独立对标 DNA,不默认写入 `dna-0`。DNA 维度框架(16 维,初始版本已确认)位于 `xhs-style-profiler` 的 `references/xhs-note-dna-dimensions.md`。 +账号级 DNA 存储目录是 `xhs/dna/`。未指定 DNA 时默认使用并更新 `dna-0`。生产前同时读取 DNA 文档与 DNA template;对标分析先建立独立对标 DNA,不默认写入 `dna-0`。 + +DNA 是账号级框架:定位与核心传达、选题组合、标题包装、账号简介、图文/视频比例、发布习惯、高数据创意、视觉/声音倾向、口播文案 DNA、互动系列、搜索关键词与用户问题、制作管线。它指导 main agent 写图文或出视频 Brief,不规定成片制作细节。维度框架 v1 位于 `xhs-style-profiler` 的 `references/account-dna-framework.md`。 + +**分工硬边界**:图文和长文由 main 直接生产;已有视频素材轻加工由 main 做;视频全案只产出 Brief 并委托 `content-producer`。口播类视频若 DNA 启用口播文案 DNA,口播文案由 main 写好并随 Brief 交付;Content Producer 只负责声画制作。Brief 指定 Pipeline 时 CP 必须采用,未指定时 CP 自由发挥。 ## 数据与记录 diff --git a/crews/main/skills/expert-xhs/tools/xhs-style-profiler/SKILL.md b/crews/main/skills/expert-xhs/tools/xhs-style-profiler/SKILL.md index 772dc59f..66e62b06 100644 --- a/crews/main/skills/expert-xhs/tools/xhs-style-profiler/SKILL.md +++ b/crews/main/skills/expert-xhs/tools/xhs-style-profiler/SKILL.md @@ -1,28 +1,31 @@ --- name: xhs-style-profiler -description: 为单篇小红书笔记提取 16 维 DNA report,按 DNA ID 聚合历史 report 生成 DNA 文档,并推导完整的 DNA template。 +description: 提取小红书账号级 DNA:单篇笔记生成 report,聚合定位、选题、标题、图文视频比例、发布节奏、高数据创意、搜索关键词与用户问题,推导 main agent 的图文生产输入或视频 Brief template。 +metadata: + openclaw: + emoji: 🧬 --- # xhs-style-profiler -小红书图文笔记的内容风格提取与 DNA 生产工具。输入是**笔记的文本材料**(标题 + 正文 + 内联话题标签,由 Agent 先行整理成 `.md` / `.txt`)与封面图;不接受笔记链接或图片文件直接作为主输入(链接先用 `xhs-content-ops` 下载,图片作为 `--cover-image` 或视觉证据进入)。 +小红书账号级 DNA 提取与聚合工具。输入是**笔记文本材料**(标题 + 正文 + 内联话题标签,由 Agent 先整理成 `.md` / `.txt`)与可选封面图;不接受笔记链接或图片文件作为直接输入。链接先用 `xhs-content-ops` 下载,图片作为视觉证据进入。 + +DNA 的用途是指导 main agent 图文笔记生产、搜索意图覆盖和视频制作 Brief;不指导 main agent 直接产出全片。 ## 产物模型 ```text 单篇笔记 -> DNA report -同一个 DNA 目录下的全部 report + 权重/focus -> DNA 文档 -DNA 文档 -> DNA template +同一 DNA 下的全部 report + 权重/focus + 用户输入 -> DNA 文档 +DNA 文档 -> 图文生产输入 / 视频制作 Brief template ``` -- **DNA report**:单篇笔记的 16 维提取结果。 -- **DNA 文档**:聚合历史 report 后得到的内容与风格规则。 -- **DNA template**:由 DNA 文档推导出的生产模板,供生产时直接执行。 +- **DNA report**:单篇样本的账号级观测与 14 维提取结果。 +- **DNA 文档**:聚合后的账号级规则、搜索意图地图与样本覆盖度说明。 +- **DNA template**:main agent 生成图文或委托 Content Producer 制作视频时使用的输入模板。 ## 存储结构 -DNA 以 DNA ID 为主体存储,一个 DNA 可以持续放入任意数量笔记,样本可以来自一个或多个参考账号,甚至来自用户直接的想法。 - ```text xhs/dna/{dna-id}/ reports/ @@ -33,32 +36,30 @@ xhs/dna/{dna-id}/ {dna-id}.template.md ``` -原始笔记文本可以临时来自任何位置(建议 `xhs/ref/{dna-id}/notes/`);生成后的 DNA report 必须进入对应 DNA 的 `reports/` 目录。 +原始笔记文本可临时放在 `xhs/ref/{dna-id}/notes/`;生成后的 report 必须进入目标 DNA 的 `reports/` 目录。 ## 样本文件约定 -```markdown +```text # 笔记标题(首个一级标题行,≤20 字) -正文第 1-2 行(开头钩子) - +正文第 1-2 行(开头) 正文主体段落…… -空行分段,保留原有换行节奏与 emoji。 #话题1 #话题2 #话题3 ``` -- 首个 `# ` 一级标题行被识别为笔记标题,其余内容为正文。 -- 正文保持纯文本,不使用 markdown 小标题,避免与话题标签的 `#` 混淆。 -- 话题标签按小红书原样以 `#话题` 内联,脚本按它统计标签数。 -- 视频笔记样本必须先用 `viral-chaser` 拆解出转录与封面,再整理成本格式文本输入;本工具不解析视频。 +- 首个 `# ` 一级标题行识别为标题,其余内容为正文。 +- 正文保持纯文本,不使用 markdown 小标题。 +- 话题标签保持内联 `#话题` 形式。 ## 职责边界 -- 输入笔记文本支持 `.md` / `.txt`;笔记链接、图片不直接作为主输入。 -- 统计只作为聚合证据底座,不评分、不替代定性判断。 -- 16 维语义判断由 Agent 回读笔记原文完成;封面与图组维度必须由视觉模型读取本地图片完成。 -- 不输出选题价值判断、合规结论、账号权重或风格评分。 -- 不要求用户确认或登记 INDEX。 +- 单篇样本只提供候选信号;账号比例、发布节奏、高数据共性、搜索意图地图必须由多样本或账号级数据聚合。 +- 搜索关键词必须尽量落到用户可能提问;不能只写平台标签。 +- 统计只做证据底座,不评分、不替代定性判断。 +- 视觉语言有图片证据时才由视觉模型分析;缺失写「未观测」。 +- 口播文案 DNA 独立聚合;样本不足时保持未启用。 +- 不输出合规结论、账号权重或风格评分。 ## Report - 单篇提取 @@ -68,35 +69,21 @@ xhs-style-profiler report \ --dna-id {dna-id} \ --sample-id {sample-id} \ --cover-image path/to/cover.jpg \ - --source-url "https://www.xiaohongshu.com/explore/..." -``` - -默认输出: - -```text -xhs/dna/{dna-id}/reports/{sample-id}.report.md + --source-url "https://www.xiaohongshu.com/explore/..." \ + --output-dir xhs/dna/{dna-id}/reports ``` -参数说明: - -- `--cover-image`:封面本地图片(`xhs-content-ops` 下载的首图或用户提供的封面),进入视觉模型分析。 -- `--source-url`:原笔记链接,作为报告证据保留;本地原创素材无链接时省略。 +- `--cover-image`:封面本地图片,用于视觉证据。 +- `--source-url`:原笔记链接;本地素材无链接时省略。 +- `--weight`:样本权重,默认 1。 +- `--focus`:限制该样本只影响指定维度,可重复传入。 -可配置权重: - -```bash ---weight 3 -``` +Agent 生成 scaffold 后必须: -可限制该篇只在某些维度参与借鉴: - -```bash ---focus opening-hook ---focus cover-imageset -``` - -Agent 生成 scaffold 后必须回读笔记原文,补齐每个维度的单篇结论、原文证据和可复用创作信号;开头钩子维度必须逐字摘录正文前 1-2 行。 -封面与图组维度必须读取 `--cover-image` 指向的本地图片(图组其余图片如有路径一并读取),并通过视觉模型补齐封面承诺、版式、构图、色彩、文字视觉、风格一致性、品牌元素、避免项和 AIGC 复现提示词要素。没有图片时记录"未提供",不得编造。 +1. 补齐「样本与账号观测」:样本类型、账号与简介、发布时间、内容形式、数据线索、搜索关键词与用户可能提问、视频形态与授权信息。 +2. 回读笔记原文,补齐 14 维的单篇结论、原文证据和可复用信号。 +3. 单篇样本无法观测账号简介、图文/视频比例、发布节奏时写「未观测」。 +4. 高数据样本必须回读创意、关键词与内容形式,不得只凭阅读量下结论。 ## Build - 聚合 DNA 文档与模板 @@ -104,20 +91,14 @@ Agent 生成 scaffold 后必须回读笔记原文,补齐每个维度的单篇 xhs-style-profiler build --dna-id {dna-id} ``` -默认读取: - -```text -xhs/dna/{dna-id}/reports/ -``` - -默认输出: +默认读取 `xhs/dna/{dna-id}/reports/`,输出: ```text xhs/dna/{dna-id}/{dna-id}.dna.md xhs/dna/{dna-id}/{dna-id}.template.md ``` -也可显式传入一个或多个 report 文件/目录: +也可显式传入 report 文件/目录: ```bash xhs-style-profiler build \ @@ -128,31 +109,27 @@ xhs-style-profiler build \ Agent 聚合时必须: 1. 读取全部 DNA report,不能只看统计表。 -2. 按每个 report 的 `weight` 和 `focus` 判断影响范围。 +2. 按 `weight` 与 `focus` 判断影响范围。 3. 区分高频共性、高权重偏好、局部借鉴、孤例和例外。 -4. 为每个维度写聚合结论、报告依据和创作规则。 -5. 确保 DNA 文档能够完整推导 template。 +4. 标注样本覆盖度;单篇/少量样本不得称为稳定账号 DNA。 +5. 聚合「关键词 → 用户问题 → 内容形式」的搜索意图地图。 +6. 为每个维度写聚合结论、报告依据和可执行规则。 +7. 确保 DNA 文档能完整推导 template。 ## DNA Template -Template 是生产模板,不是概念解释。必须从 DNA 文档的 16 个维度推导,至少包含: - -- 选题角度、受众关系 -- 标题类型、参考标题、话题标签策略、封面图风格和封面 AIGC 提示词要素 -- 开头、承、结尾、CTA、图组五个语义部分(对应笔记的开头钩子、正文主体、收束、互动引导、图片轨道) -- 每个部分的本段任务、执行方式和必须做 / 避免项 +Template 是 main agent 的图文生产输入 / 视频制作 Brief 模板,不是成片制作模板。固定语义段: -模板整体固定为七个部分:选题、标题(含封面图)、开头、承、结尾、CTA、图组。固定的是语义结构,不是字数占比;任一部分可以对应笔记中的一段或多段文字、一张或多张图片,也可以在特定内容形态下弱化。 +1. 定位与核心传达 +2. 选题与标题包装 +3. 内容形式与发布节奏 +4. 高数据创意模式 +5. 互动与系列 +6. 搜索意图与用户问题 +7. 制作交接与 Pipeline +8. 口播文案 DNA(可选) -五个笔记部分必须充分吸收 DNA 文档中的维度结论: - -| 部分 | 主要推导来源 | -| --- | --- | -| 开头 | 开头钩子、选题角度、标题风格、口语语气与人设 | -| 承 | 正文结构、口语语气与人设、emoji与标点节奏、证据与人味、关键词与搜索流量 | -| 结尾 | 签名式标记、口语语气与人设、系列化设计 | -| CTA | 互动设计、行动引导与转化、话题标签策略 | -| 图组 | 封面与图组、图片构图与信息、视觉风格、证据与人味 | +图文笔记由 main agent 直接生产;视频全案只输出 Brief。制作交接段必须写清 MainAgent 与 Content Producer 的交付物、Pipeline、素材授权和风格边界。Brief 未指定 Pipeline 时,Content Producer 可自由选择;指定 Pipeline 时必须直接采用。 ## Update - 增量聚合 @@ -163,60 +140,55 @@ xhs-style-profiler update \ --template xhs/dna/{dna-id}/{dna-id}.template.md ``` -脚本会合并 DNA 文档记录的历史 report 与新 report,重新计算加权统计,并保留 Agent 已完成内容。Agent 仍需重新审视聚合结论,再同步修订 DNA 文档和 template。 +脚本合并历史 report 与新 report,重新计算统计并保留 Agent 已完成内容。Agent 仍需重新审视聚合结论,并同步修订 DNA 文档和 template。 -`--input` 可省略。省略时表示没有新增样本,只基于历史 report、既有 DNA 文档和用户输入做融合;适用于采纳另一个 DNA 文档或 template 中的局部规则。此时仍必须通过 `--user-input` 传入要融合的要求,并由 Agent 转译到具体维度。 +`--input` 可省略,用于只融合用户输入或另一个 DNA 的局部规则;此时必须传 `--user-input`。 ## 用户输入转译 用户输入是参考信息,不是可直接入库的 DNA 规则。 ```bash ---user-input "开头钩子再直接一点,第一行就给出收益" +--user-input "搜索流量优先,标题必须覆盖真实提问" ``` -Agent 必须把输入转译到具体维度,例如: +Agent 必须转译到具体维度,例如: ```text -opening-hook:钩子改为结果前置,第一行直接给收益数字 -body-structure:开头两行均为短句,不做背景铺垫 -title-style:标题同步采用结果前置型 +search-intent-map:标题优先覆盖长尾提问,不只堆品类词 +title-packaging:标题采用问题句式,并保留核心关键词 +topic-portfolio:优先选择能承接搜索意图的教程/避坑选题 ``` 处理要求: -1. 在 DNA 文档的"用户输入转译区"记录 affected dimensions、DNA 修改和 template 修改。 -2. 把原话转译成可执行的聚合结论与创作规则。 -3. Template 只写转译后的执行规则,不直接抄用户原话。 -4. 与样本证据冲突时保留冲突说明,由用户选择优先级。 +1. 在 DNA 文档的「用户输入转译区」记录 affected dimensions、DNA 修改和 template 修改。 +2. 原话必须转译为可执行规则,不得直接抄进 template。 +3. 与样本证据冲突时保留冲突说明,由用户选择优先级。 ## Focus ID | ID | 维度 | |---|---| -| `topic-angle` | 选题角度 | -| `title-style` | 标题风格 | -| `cover-imageset` | 封面与图组 | -| `keyword-seo` | 关键词与搜索流量 | -| `opening-hook` | 开头钩子 | -| `body-structure` | 正文结构 | -| `language-tone` | 口语语气与人设 | -| `emoji-rhythm` | emoji与标点节奏 | -| `image-composition` | 图片构图与信息 | -| `visual-style` | 视觉风格 | -| `credibility-proof` | 证据与人味 | -| `interaction-design` | 互动设计 | -| `tag-strategy` | 话题标签策略 | -| `cta-conversion` | 行动引导与转化 | -| `signature-mark` | 签名式标记 | -| `series-design` | 系列化设计 | +| `positioning-core` | 定位与核心传达 | +| `topic-portfolio` | 选题组合 | +| `title-packaging` | 标题与包装 | +| `bio-profile` | 账号简介与主页表达 | +| `content-form-mix` | 内容形式与比例 | +| `publish-cadence` | 发布习惯 | +| `high-performer-patterns` | 高数据创意模式 | +| `visual-language` | 视觉语言 | +| `audio-language` | 声音语言 | +| `narration-dna` | 口播文案 DNA | +| `engagement-conversion` | 互动与转化 | +| `series-signature` | 系列与签名 | +| `search-intent-map` | 搜索意图地图 | +| `production-pipeline` | 制作管线倾向 | ## 统计与分词 -脚本统计笔记文本的标题字数、句段、行数、标点、人称、emoji、话题标签等指标;中文高频信号使用相邻二字组合,仅作为候选线索。Agent 必须回读原文判断它是否真是口头禅或签名式表达。 - -当前不引入 jieba。若后续候选噪声明显,可把 jieba 作为候选词挖掘器加入仓库级依赖,但分词结果不能直接作为 DNA 结论。 +脚本统计标题字数、句段、行数、标点、人称、emoji、话题标签等指标;中文高频信号使用相邻二字组合,仅作为候选线索。Agent 必须回读原文确认口头禅或签名式表达。分词结果不能直接作为 DNA 结论。 ## 参考资料 -- `references/xhs-note-dna-dimensions.md`(小红书 16 维 DNA 分析框架,v0 初始版本,2026-08-28 用户确认) +- `references/account-dna-framework.md`(小红书账号级 DNA 框架 v1、搜索意图地图、Pipeline 映射与聚合边界) diff --git a/crews/main/skills/expert-xhs/tools/xhs-style-profiler/references/account-dna-framework.md b/crews/main/skills/expert-xhs/tools/xhs-style-profiler/references/account-dna-framework.md new file mode 100644 index 00000000..b08eb5b7 --- /dev/null +++ b/crews/main/skills/expert-xhs/tools/xhs-style-profiler/references/account-dna-framework.md @@ -0,0 +1,86 @@ +# 小红书账号级 DNA 框架(v1) + +> 状态:v1。Focus ID、维度命名与 template 语义以本文件为准。调整维度必须升版本,并同步 `scripts/build_style_profile.py` 与工具 `SKILL.md`。 + +## 定位与边界 + +小红书 DNA 是**账号级运营框架**,不是单篇笔记的写作模板。它回答账号长期如何被识别、如何选题、如何包装、如何安排图文/视频与发布节奏,并指导 main agent 产出图文内容或视频制作 Brief。 + +- **必须进入 DNA**:定位与核心传达、选题组合、标题与包装、账号简介、图文/视频比例、发布习惯、高数据内容创意共性、视觉/声音倾向、口播文案 DNA、互动转化、系列标记、搜索关键词与用户问题、制作管线倾向。 +- **不进入主 DNA**:逐句正文、单图构图细节、emoji 数量、镜头表、转场参数。口播文案 DNA 是独立可选块;没有证据时写「未观测」。 +- **搜索是小红书一级流量入口**:每个 DNA 样本都应尽量记录主关键词、相关词、长尾搜索句和用户可能提问;聚合后形成「关键词 → 用户问题 → 内容形式」的搜索意图地图。 +- **MainAgent / Content Producer 分界**:main agent 负责图文笔记、长文内容、已有视频素材的简单加工,以及视频全案 Brief。除 Brief 之外的成片制作由 Content Producer 执行。口播类视频的口播文案若由 DNA 明确启用,则由 main agent 写好并随 Brief 交付;Content Producer 只做声画制作。 +- **样本可以是账号或单篇**:账号样本可提取简介与发布习惯;用户提供单篇或多篇时,仍按同维度统计聚合,但账号级字段必须标「样本不足 / 未观测」。 + +## 维度(14 维) + +### 一、定位与选题 + +| # | ID | 维度 | 观测内容 | +|---|----|------|----------| +| 1 | `positioning-core` | 定位与核心传达 | 一句话定位、目标人群、长期承诺、内容主张边界 | +| 2 | `topic-portfolio` | 选题组合 | 常见选题类型、内容支柱、选题入口、搜索/推荐/分享各自服务的选题 | +| 3 | `title-packaging` | 标题与包装 | 标题模式、封面承诺、话题标签策略;不规定具体字句 | +| 4 | `bio-profile` | 账号简介与主页表达 | 昵称、简介、置顶笔记、主页视觉与承诺;仅账号样本可得 | + +### 二、账号节奏 + +| # | ID | 维度 | 观测内容 | +|---|----|------|----------| +| 5 | `content-form-mix` | 内容形式与比例 | 图文、视频、口播、实拍拼接、创意转场、纯 AIGC 动画等形式;如混合,记录比例或倾向 | +| 6 | `publish-cadence` | 发布习惯 | 发布时间段、频率、图文/视频混合节奏(如三图文对一视频)、栏目化节奏 | +| 7 | `high-performer-patterns` | 高数据创意模式 | 高阅读/收藏/搜索样本的选题、包装、关键词、形式与创意共性;数据只是证据 | + +### 三、表达与风格 + +| # | ID | 维度 | 观测内容 | +|---|----|------|----------| +| 8 | `visual-language` | 视觉语言 | 稳定画面风格、横竖屏、图组/视频视觉记忆点;不规定单图细节 | +| 9 | `audio-language` | 声音语言 | 配音类型/音色倾向、口播密度、BGM/音效倾向;不规定 TTS 参数 | +| 10 | `narration-dna` | 口播文案 DNA | 独立可选块:开头钩子、起承转合、收束、人称与语气、签名表达 | + +### 四、互动、搜索与制作 + +| # | ID | 维度 | 观测内容 | +|---|----|------|----------| +| 11 | `engagement-conversion` | 互动与转化 | 评论、收藏、关注、进店、咨询等平台内行动与承接路径 | +| 12 | `series-signature` | 系列与签名 | 栏目名、固定开场/收尾、系列承诺、识别符号 | +| 13 | `search-intent-map` | 搜索意图地图 | 主关键词、相关词、长尾句、用户可能提问、搜索意图层级与内容形式匹配 | +| 14 | `production-pipeline` | 制作管线倾向 | 视频内容形态到 Content Producer Pipeline 的映射;未指定时交给 CP 自由发挥 | + +## Pipeline 映射 + +`production-pipeline` 只能写 Content Producer 已支持的管线,不得发明不存在的管线名。 + +| DNA 观测形态 | Brief 中的 Pipeline | +|--------------|---------------------| +| 影视解说 + 反转植入 | `dna-ad-video-pipeline` | +| 口播 / 真人讲述 | `video-producer:default`(narrative) | +| 实拍拼接 / 蒙太奇 | `video-producer:default`(montage) | +| 创意转场 / 动效 | `video-producer:default`(motion) | +| 纯 AIGC 动画 | `video-producer:default`(AIGC render) | + +Brief 未写 `pipeline` 时,Content Producer 按 `video-producer:default` 自由选择实现;Brief 写了 Pipeline 时必须直接采用,不得替换成自创流程。 + +## Report 与聚合规则 + +1. 单篇 report 先记录「样本与账号观测」:样本类型、账号与简介、发布时间、内容形式、数据线索、关键词与用户问题、视频形态、授权信息。缺失写「未观测」。 +2. 单篇样本不能推导账号比例、发布节奏或高数据共性;聚合时标注覆盖度。 +3. 搜索关键词必须尽量落到用户可能提问,不只写平台标签;单篇只记候选,聚合后才形成搜索意图地图。 +4. 高数据样本必须回读创意、关键词与内容形式,不能只凭阅读量归因。 +5. 脚本统计只提供标题字数、句长、标签、emoji 等证据;语义判断由 Agent 回读原文完成。 + +## Template 语义 + +DNA template 是 main agent 的 **Brief / 内容生产输入模板**,不是成片制作模板。固定语义段: + +1. 定位与核心传达 +2. 选题与标题包装 +3. 内容形式与发布节奏 +4. 高数据创意模式 +5. 互动与系列 +6. 搜索意图与用户问题 +7. 制作交接与 Pipeline +8. 口播文案 DNA(可选) + +图文笔记由 main agent 直接生产;视频全案只到 Brief 为止,制作选择由 Content Producer 在 Pipeline 内完成。模板不得规定逐句正文、单图构图、镜头表、转场参数或编码细节。 diff --git a/crews/main/skills/expert-xhs/tools/xhs-style-profiler/references/xhs-note-dna-dimensions.md b/crews/main/skills/expert-xhs/tools/xhs-style-profiler/references/xhs-note-dna-dimensions.md deleted file mode 100644 index 13d20cdf..00000000 --- a/crews/main/skills/expert-xhs/tools/xhs-style-profiler/references/xhs-note-dna-dimensions.md +++ /dev/null @@ -1,68 +0,0 @@ -# 小红书笔记 DNA 提取与分析维度(v0) - -> 状态:**v0,2026-08-28 用户确认定稿**。维度划分、命名与 focus ID 以本文件为准;后续调整需升版本并同步本文件、`scripts/build_style_profile.py` 的 `DIMENSION_GROUPS` 与工具 `SKILL.md` 的 Focus ID 表,历史 report 的维度编号以生成时版本为准。 -> -> 定稿依据:包内起号作战手册(流量模型、笔记简报模板、对标记录表、数据诊断漏斗)、小红书文案风格改写规则(标题手法、emoji 与标签约定、口语化表达)、对标关键词方法论(关键词分层、搜索流量预埋)、微信公众号 17 维与视频号 16 维的分组思路。 - -小红书笔记是「图文短内容」:观测对象是短标题(≤20 字)+ 短正文(≤1000 字)+ 封面与图组(≤18 张)+ 内联话题标签(≤10 个);流量来自「搜索 + 推荐 + 分享」三入口,收藏(实用价值)与搜索预埋权重显著。 - -## 维度分组(16 维) - -### 一、选题与包装(对应 template 前两项) - -| # | ID | 维度 | 观测内容 | -|---|----|------|----------| -| 1 | `topic-angle` | 选题角度 | 笔记任务(搜索 / 信任 / 互动 / 证明 / 转化 / 留存)、目标读者、点击理由、选题入口(现象 / 问题 / 冲突 / 数据 / 热点 / 个人经历)、系列潜力 | -| 2 | `title-style` | 标题风格 | 标题类型(痛点 / 数字 / 反差 / 悬念 / 身份点名 / 搜索长尾)、正负刺激手法、字数(≤20 字硬限制)、标题原文、关键词预埋 | -| 3 | `cover-imageset` | 封面与图组 | 封面承诺(用户点进来的可见理由)、版式(大字报 / 实拍场景 / 对比图 / 清单卡片)、色彩与文字视觉、封面与图组风格一致性、AIGC 复现要素 | -| 4 | `keyword-seo` | 关键词与搜索流量 | 主关键词与相关词、标题/正文/标签中的关键词自然覆盖、长尾搜索句预埋、关键词层级(核心词 / 痛点词 / 场景词 / 转化词) | - -### 二、正文与表达 - -| # | ID | 维度 | 观测内容 | -|---|----|------|----------| -| 5 | `opening-hook` | 开头钩子 | 正文前 1-2 行的钩子类型(痛点前置 / 结果前置 / 反差 / 悬念 / 身份点名)、与封面承诺是否对应、正文是否兑现;钩子原文逐字摘录 | -| 6 | `body-structure` | 正文结构 | 行数与换行节奏(短句分行 / 空行分段)、组织方式(清单体 / 教程步骤 / 故事线 / 对比 / 观点输出)、信息密度、每段「一个观点 + 一个细节/证据」的执行情况 | -| 7 | `language-tone` | 口语语气与人设 | 口语化程度、人称偏好(我/你/家人们/姐妹们)、亲密度与距离感、人设感(真实、具体、有缺点)、人味细节(真实场景、限制、错误、瞬间) | -| 8 | `emoji-rhythm` | emoji与标点节奏 | emoji 密度与摆放位置(标题 / 段首 / 列表符 / 结尾)、是否重复、感叹号与省略号的情绪用法、换行形成的视觉节奏 | - -### 三、视觉与证据 - -| # | ID | 维度 | 观测内容 | -|---|----|------|----------| -| 9 | `image-composition` | 图片构图与信息 | 图片数量与顺序、构图类型(产品展示 / 场景 / 文字卡片 / 对比图 / 过程图)、图文信息分工(图承载什么、文承载什么) | -| 10 | `visual-style` | 视觉风格 | 色调与滤镜倾向、版式一致性、质感(杂志感 / 手写感 / 真实生活感 / 干货卡片感)、系列视觉符号 | -| 11 | `credibility-proof` | 证据与人味 | 案例截图、过程记录、前后对比、真实数据、可合规展示的证明;真实短板与限制的使用;「凭啥信你」的解答方式 | - -### 四、互动与转化 - -| # | ID | 维度 | 观测内容 | -|---|----|------|----------| -| 12 | `interaction-design` | 互动设计 | 评论引导(具体问题优于开放式提问)、收藏引导(清单 / 实用性)、关注引导(系列承诺 / 主页价值);引导位置与密度;不以利益换互动 | -| 13 | `tag-strategy` | 话题标签策略 | 标签数量(≤10 硬限制)与结构(品类词 / 场景词 / 人群词 / 痛点词 / 热点词)、与标题正文的相关度、标签承载搜索意图的方式 | -| 14 | `cta-conversion` | 行动引导与转化 | 与笔记任务匹配的平台内行动(评论 / 收藏 / 关注 / 进店 / 咨询)、每篇只放一个行动引导、合规边界(不隐藏站外联系方式、不绕检测) | - -### 五、签名与系列 - -| # | ID | 维度 | 观测内容 | -|---|----|------|----------| -| 15 | `signature-mark` | 签名式标记 | 固定开场 / 收尾、口头禅与标志性表达、固定版式符号或视觉记号(高频词候选必须由 Agent 回读原文确认) | -| 16 | `series-design` | 系列化设计 | 系列 / 栏目设定、固定栏目感、系列与内容支柱的对应、下一篇预告钩子 | - -## Template 语义部分(默认 5 段) - -开头两项跨平台通用:**选题**、**标题(含封面图)**。第三项起按小红书笔记的生产结构分 5 个语义部分: - -| 部分 | 观测位置 | 主要推导维度 | -|------|----------|--------------| -| [开头部分] | 正文前 1-2 行 | opening-hook、topic-angle、title-style | -| [承部分] | 正文主体段落 | body-structure、language-tone、emoji-rhythm、credibility-proof | -| [结尾部分] | 正文收束句 | signature-mark、language-tone、series-design | -| [CTA部分] | 结尾引导 + 标签区 | interaction-design、cta-conversion、tag-strategy | -| [图组部分] | 封面 + 配图(与正文并行的生产轨道) | cover-imageset、image-composition、visual-style、credibility-proof | - -分段为默认骨架;分段数量与命名最终以 DNA 文档的结构结论为准(架构规范不固定段数)。 - -## 统计边界 - -脚本对输入的笔记文本做标题字数、句长、行数、人称、问句、感叹号、emoji、话题标签等统计,作为聚合证据底座;不生成总分、不判断风格合格。封面、图组构图、色调等视觉观测无法从纯文本统计得出,由视觉模型读取本地图片补齐。高频二字组合只是候选线索,口头禅与签名式表达必须经 Agent 回读原文确认。 diff --git a/crews/main/skills/expert-xhs/tools/xhs-style-profiler/scripts/build_style_profile.py b/crews/main/skills/expert-xhs/tools/xhs-style-profiler/scripts/build_style_profile.py index ed07224d..f93cb203 100755 --- a/crews/main/skills/expert-xhs/tools/xhs-style-profiler/scripts/build_style_profile.py +++ b/crews/main/skills/expert-xhs/tools/xhs-style-profiler/scripts/build_style_profile.py @@ -57,31 +57,27 @@ } DIMENSION_GROUPS = { - "选题与包装": [ - ("topic-angle", "选题角度"), - ("title-style", "标题风格"), - ("cover-imageset", "封面与图组"), - ("keyword-seo", "关键词与搜索流量"), + "定位与选题": [ + ("positioning-core", "定位与核心传达"), + ("topic-portfolio", "选题组合"), + ("title-packaging", "标题与包装"), + ("bio-profile", "账号简介与主页表达"), ], - "正文与表达": [ - ("opening-hook", "开头钩子"), - ("body-structure", "正文结构"), - ("language-tone", "口语语气与人设"), - ("emoji-rhythm", "emoji与标点节奏"), + "账号节奏": [ + ("content-form-mix", "内容形式与比例"), + ("publish-cadence", "发布习惯"), + ("high-performer-patterns", "高数据创意模式"), ], - "视觉与证据": [ - ("image-composition", "图片构图与信息"), - ("visual-style", "视觉风格"), - ("credibility-proof", "证据与人味"), + "表达与风格": [ + ("visual-language", "视觉语言"), + ("audio-language", "声音语言"), + ("narration-dna", "口播文案 DNA"), ], - "互动与转化": [ - ("interaction-design", "互动设计"), - ("tag-strategy", "话题标签策略"), - ("cta-conversion", "行动引导与转化"), - ], - "签名与系列": [ - ("signature-mark", "签名式标记"), - ("series-design", "系列化设计"), + "互动、搜索与制作": [ + ("engagement-conversion", "互动与转化"), + ("series-signature", "系列与签名"), + ("search-intent-map", "搜索意图地图"), + ("production-pipeline", "制作管线倾向"), ], } DIMENSIONS = [] @@ -93,7 +89,6 @@ ) number += 1 - def split_sentences(text: str) -> list[str]: return [item.strip() for item in SENTENCE_SPLIT.split(text) if item.strip()] @@ -341,6 +336,7 @@ def statistics_markdown(statistics: dict) -> str: lines.extend( [ "", + f"样本覆盖度:`{statistics['report_count']}` 条 report;单条或少量样本不能推导账号比例、发布节奏或高数据共性。", f"权重模式:`{statistics['weighting']}`;总权重:`{statistics['total_weight']}`。", "话题标签数按正文内联 `#话题` 统计;emoji 统计只作证据底座,不构成「必须凑满几个」的规则。", "统计只用于辅助聚合,不生成评分;定性判断必须回到各篇 DNA report。", @@ -410,43 +406,32 @@ def load_reports(paths: list[Path]) -> list[dict]: return reports +REPORT_DIMENSION_PROMPTS = { + 'positioning-core': '- 单条观测:本条暴露出的定位、目标人群与核心传达。\n- 账号级判断:单条样本只给候选,不直接判定账号稳定性。', + 'topic-portfolio': '- 单条观测:本条选题类型、入口和系列关系。', + 'title-packaging': '- 单条观测:标题 / 描述 / 封面包装模式;只记模式,不抄成固定字句。', + 'bio-profile': '- 账号级观测:账号简介、主页与置顶表达;单条样本无法观测时写未观测。', + 'content-form-mix': '- 单条观测:本条内容形式。\n- 聚合边界:图文/视频比例与混合节奏必须由多样本或账号级数据推导。', + 'publish-cadence': '- 单条观测:本条发布时间。\n- 聚合边界:时间段、频率和三图文对一视频等节奏必须由多样本或账号级数据推导。', + 'high-performer-patterns': '- 数据线索:记录本条互动/播放/阅读线索。\n- 创意判断:回读选题、包装、形式与创意,不得把高数据直接等同于风格好。', + 'visual-language': '- 视觉证据:有图片或关键帧时由视觉模型读取;无证据写未观测。\n- 边界:只记录账号级稳定视觉语言,不规定逐镜设计。', + 'audio-language': '- 声音证据:来自口播稿、原视频信息或用户说明;无证据写未观测。\n- 边界:只记录音色/语速/声音气质倾向,不规定 TTS 参数。', + 'narration-dna': '- 独立块:记录开头、起承转合、收束、人称与签名表达。\n- 聚合边界:样本不足时保持未启用,不把单条句式上升为账号 DNA。', + 'engagement-conversion': '- 单条观测:平台内行动引导与承接路径。', + 'series-signature': '- 单条观测:栏目、固定表达或识别符号;高频词必须回读原文确认。', + 'search-intent-map': '- 搜索证据:主关键词、相关词、长尾句和用户可能提问。\n- 聚合边界:单篇只给候选;账号级搜索意图地图需多样本聚合。', + 'production-pipeline': '- 管线映射:只写 Content Producer 已支持的 Pipeline;未确定时写待定。\n- 分界:DNA 指导 main agent 出 Brief,不规定成片制作细节。', +} + def report_dimension_markdown(dimension: dict) -> str: heading = f"### {dimension['number']}. {dimension['name']}" - if dimension["id"] == "cover-imageset": - return ( - f"{heading}\n\n" - "**视觉模型分析:**待 Agent 基于封面图(及图组图片,如有)补齐。\n\n" - "- 封面承诺(用户点进来的可见理由):待 Agent 补齐。\n" - "- 版式与构图(大字报 / 实拍场景 / 对比图 / 清单卡片等):待 Agent 补齐。\n" - "- 色彩体系:待 Agent 补齐。\n" - "- 文字视觉与图文关系(封面字 / 贴纸 / 标题框):待 Agent 补齐。\n" - "- 封面与图组的风格一致性:待 Agent 补齐。\n" - "- 品牌识别元素:待 Agent 补齐。\n" - "- 避免项:待 Agent 补齐。\n\n" - "**AIGC 复现提示词要素:**待 Agent 补齐;要求能据此生成风格高度一致的封面图。\n\n" - "**可复用创作信号:**待 Agent 补齐。" - ) - if dimension["id"] == "opening-hook": - return ( - f"{heading}\n\n" - "**钩子原文(正文前 1-2 行):**待 Agent 逐字摘录。\n\n" - "**单条结论:**待 Agent 补齐(钩子类型、与封面承诺是否对应、正文是否兑现)。\n\n" - "**原文证据:**待 Agent 补齐。\n\n" - "**可复用创作信号:**待 Agent 补齐。" - ) - if dimension["id"] == "image-composition": - return ( - f"{heading}\n\n" - "**图组清单与路径:**待 Agent 列出(没有图片时写「未提供」,不得编造)。\n\n" - "**单条结论:**待 Agent 补齐(图片数量、构图类型分布、图文信息分工)。\n\n" - "**原文证据:**待 Agent 补齐(逐图描述构图与信息点)。\n\n" - "**可复用创作信号:**待 Agent 补齐。" - ) + prompt = REPORT_DIMENSION_PROMPTS.get(dimension["id"], "- 单条观测:待 Agent 补齐。") return ( f"{heading}\n\n" + f"{prompt}\n\n" "**单条结论:**待 Agent 补齐。\n\n" - "**原文证据:**待 Agent 补齐(正文逐字引用或图片描述)。\n\n" - "**可复用创作信号:**待 Agent 补齐。" + "**原文证据:**待 Agent 补齐(逐字引用、账号信息、发布信息、画面/声音描述或数据线索;注明来源)。\n\n" + "**可复用信号:**待 Agent 补齐;样本不足时写未观测,不推导账号级稳定性。" ) @@ -481,6 +466,8 @@ def report_markdown( "本文件只描述这一篇笔记。它不是聚合后的 DNA 文档,也不直接作为生产模板。", "## 单篇统计", f"- 总字符:{document['characters']}\n- 标题字数:{document['title_chars']}\n- 句子数:{document['sentences']}\n- 正文行数:{document['line_count']}\n- emoji 数:{document['emoji_count']}\n- 话题标签数:{document['tag_count']}\n- 标题候选:{' / '.join(document['title_candidates'])}\n- 来源链接:{source_url or '未提供'}\n- 封面图:{cover_image or '未提供'}", + "## 样本与账号观测", + '- 样本类型:待 Agent 补齐(账号作品 / 用户提供单篇)。\n- 账号与简介:待 Agent 补齐;单篇样本无法观测时写未观测。\n- 发布时间与时间段:待 Agent 补齐;单篇样本只记录本篇时间,不推导账号节奏。\n- 内容形式:待 Agent 补齐(图文 / 视频 / 口播 / 实拍拼接 / 创意转场 / 纯 AIGC 动画 / 混合)。\n- 数据表现线索:待 Agent 补齐;只作证据,不直接判风格好坏。\n- 搜索关键词与用户可能提问:待 Agent 补齐;只记录本篇候选,不直接判定账号级搜索意图。\n- 视频形态与授权信息:待 Agent 补齐(横竖屏、时长、素材来源、授权边界)。', f"## {len(DIMENSIONS)} 维单篇分析", "\n\n".join(dimensions), "## 单篇边界", @@ -524,19 +511,11 @@ def dna_document_markdown( if body: dimensions.append(f"{heading}\n\n{body}") else: - if dimension["id"] in ("cover-imageset", "image-composition"): - dimensions.append( - f"{heading}\n\n**聚合结论:**待 Agent 补齐。\n\n" - "**报告依据:**待 Agent 列出使用的封面 / 图组图片和 DNA report。\n\n" - "**视觉生成规则:**待 Agent 转成可执行的 AIGC 提示词要素或配图规格。\n\n" - "**例外与约束:**待 Agent 补齐。" - ) - else: - dimensions.append( - f"{heading}\n\n**聚合结论:**待 Agent 补齐。\n\n" - "**报告依据:**待 Agent 列出使用的 DNA report、权重和 focus。\n\n" - "**创作规则:**待 Agent 补齐。" - ) + dimensions.append( + f"{heading}\n\n**聚合结论:**待 Agent 补齐。\n\n" + "**报告依据:**待 Agent 列出使用的 DNA report、权重和 focus。\n\n" + "**创作规则:**待 Agent 补齐。" + ) report_paths = "\n".join(report["report_path"] for report in reports) existing_user_inputs = ( extract_named_section(previous_dna, "## 用户输入转译区") if previous_dna else None @@ -552,7 +531,7 @@ def dna_document_markdown( f"generated_at: {yaml_value(generated_at())}\n" "---", f"# {dna_id} DNA 文档", - "本文件聚合历史 DNA report。它是账号当前采用的小红书笔记内容与风格规则,也必须能推导出生产模板。", + "本文件聚合历史 DNA report。它是账号当前采用的账号级运营规则,也必须能推导出 main agent 的图文生产输入或视频制作 Brief。", "## 报告与权重", "\n".join( f"- `{report['report_path']}`:weight `{report['weight']}`,focus `{', '.join(report['focus']) or 'all'}`" @@ -614,54 +593,64 @@ def extract_named_section(markdown: str, heading: str) -> str: return "\n".join(body).strip() -TEMPLATE_STAGES = ("开头", "承", "结尾", "CTA", "图组") +TEMPLATE_STAGES = ("定位与核心传达", "选题与标题包装", "内容形式与发布节奏", "高数据创意模式", "互动与系列", "搜索意图与用户问题", "制作交接与Pipeline", "口播文案DNA") TEMPLATE_STAGE_FIELDS = { - "开头": ( - "本段任务", - "钩子类型", - "开头句式", - "与封面承诺的兑现", - "必须做", - "避免", + "定位与核心传达": ( + "一句话定位", + "目标人群", + "核心传达", + "账号简介与主页表达", + "不变承诺", ), - "承": ( - "本段任务", - "推进逻辑", - "信息密度", - "段落与换行节奏", - "emoji 使用", - "证据与人味细节", - "必须做", - "避免", + "选题与标题包装": ( + "选题组合", + "标题模式", + "封面包装", + "禁用方向", ), - "结尾": ( - "本段任务", - "收束方式", - "情绪落点", - "签名式标记", - "必须做", - "避免", + "内容形式与发布节奏": ( + "图文与视频比例", + "发布时间带", + "内容形式混合节奏", + "系列栏目", + ), + "高数据创意模式": ( + "高表现样本共性", + "可复用创意原型", + "触发条件", + "例外", ), - "CTA": ( - "行动目标", - "引导方式(评论/收藏/关注)", - "平台内动作", + "互动与系列": ( + "互动目标", + "引导方式", + "系列与签名标记", "必须做", "避免", ), - "图组": ( - "图片数量与顺序", - "构图类型", - "文字卡片设计", - "风格一致性", - "图文信息分工", + "搜索意图与用户问题": ( + "主关键词", + "相关词与长尾句", + "用户可能提问", + "搜索意图分层", + "标签与正文覆盖策略", + ), + "制作交接与Pipeline": ( + "MainAgent交付物", + "ContentProducer交付物", + "Pipeline", + "素材与授权", + "风格边界", + ), + "口播文案DNA": ( + "是否启用", + "起承转合结构", + "语言与人称", + "声音倾向", "必须做", "避免", ), } - - def parse_template_fields(body: str) -> dict[str, str]: fields = {} for line in body.splitlines(): @@ -687,25 +676,6 @@ def template_segment(stage: str, values: dict[str, str] | None = None) -> str: return "\n".join(lines) -def extract_topic_title_body(previous_template: str | None) -> str: - if not previous_template: - return "" - for heading in ("## 生产模板", "## 选题与标题"): - body = extract_named_section(previous_template, heading) - if not body: - continue - topic_title_lines = [] - for line in body.splitlines(): - # 只在真正的模板分段([开头部分] 等)处截断;[标题] 属于选题与标题区,必须保留。 - if re.match(r"^\[[^\]]+部分\]$", line.strip()): - break - topic_title_lines.append(line) - rendered = "\n".join(topic_title_lines).strip() - if rendered: - return rendered - return "" - - def stage_values_from_template(old_sections: dict[str, str]) -> dict[str, dict[str, str]]: values = {stage: {} for stage in TEMPLATE_STAGES} for heading in sorted(old_sections, key=template_order): @@ -725,37 +695,24 @@ def template_markdown( old_sections = extract_template_sections(previous_template) stage_values = stage_values_from_template(old_sections) segments = [template_segment(stage, stage_values[stage]) for stage in TEMPLATE_STAGES] - topic_title = extract_topic_title_body(previous_template) or ( - "(选题角度推荐:待 Agent 补齐。)\n" - "(选题需考虑的受众关联角度:待 Agent 补齐。)\n" - "\n" - "[标题](类型为主:待 Agent 补齐。)\n" - "(参考:待 Agent 补齐。)\n" - "(话题标签策略:待 Agent 补齐。)\n" - "(封面图风格:待 Agent 补齐。)\n" - "(封面 AIGC 提示词要素:待 Agent 补齐。)" - ) - if previous_template: - for field in ("(封面图风格:", "(封面 AIGC 提示词要素:"): - if field not in topic_title: - topic_title += f"\n{field}待 Agent 补齐。)" section_defaults = [ ( "## 生产模板", - topic_title + "\n\n" + "\n\n".join(segments), + "\n\n".join(segments), ), ( "## 用户输入转译后的执行规则", "- (来自用户输入:待 Agent 补齐来源。)\n" f"- (影响维度:待 Agent 映射到 {len(DIMENSIONS)} 维 ID。)\n" - "- (执行规则:待 Agent 写成生产时可直接执行的要求。)", + "- (执行规则:待 Agent 写成 Brief 或图文生产时可直接执行的要求。)", ), ( "## 使用检查", - "- 选题与标题是否符合 DNA 文档的选题角度、受众关联和标题类型。\n" - "- 开头、承、结尾、CTA、图组五个部分是否完成各自任务。\n" - "- 每个部分是否反映 DNA 文档中对应的钩子、正文结构、语气、emoji 节奏、视觉、互动与签名标记。\n" - "- 标题 ≤ 20 字、正文 ≤ 1000 字、图片 ≤ 18 张、话题标签 ≤ 10 个是否满足(平台硬限制)。\n" + "- 是否只用一个 DNA,且与本次账号 / 内容任务匹配。\n" + "- 定位、选题、标题包装、核心传达是否来自 DNA 文档。\n" + "- 图文/视频比例、发布节奏和高数据创意是否尊重样本覆盖度;样本不足时是否标注未观测。\n" + "- 视频全案是否只输出 Brief,且 Brief 明确 Pipeline、素材授权、验收标准和交付边界。\n" + "- 口播文案 DNA 是否独立启用;未启用时是否避免规定逐句口播。\n" "- 用户输入是否已转译为具体执行规则。", ), ] @@ -779,7 +736,7 @@ def template_markdown( f"generated_at: {yaml_value(generated_at())}\n" "---", f"# {dna_id} DNA Template", - "本模板是小红书笔记生产时直接执行的 production template,必须由 DNA 文档推导;不得引入 DNA 文档未确认的规则。", + "本模板是 main agent 的账号级内容生产 / Brief 输入模板,必须由 DNA 文档推导;不得引入 DNA 文档未确认的规则,也不规定成片制作细节。", *rendered_sections, ] ) + "\n" diff --git a/crews/main/skills/expert-xhs/workflows/account-benchmark.md b/crews/main/skills/expert-xhs/workflows/account-benchmark.md index ad9bb42f..9e52e96f 100644 --- a/crews/main/skills/expert-xhs/workflows/account-benchmark.md +++ b/crews/main/skills/expert-xhs/workflows/account-benchmark.md @@ -2,7 +2,7 @@ 用于分析对标账号或一组对标笔记,形成独立 DNA,并与默认或指定 DNA 逐项比较。对标样本不得直接写入 `dna-0`;用户采纳后,只有转译后的规则可融合进基线 DNA。 -本 workflow 同时承担「寻找对标参考」与「起号时寻找依据」两类需求:从用户诉求中提取有效关键词,按关键词去小红书搜索**低粉爆款**,下载、分析、建对标 DNA。 +本 workflow 同时承担「寻找对标参考」与「起号时寻找依据」两类需求:从用户诉求中提取有效关键词与用户可能提问,按关键词去小红书搜索**低粉爆款**,下载、分析、建对标 DNA。 ## 入口判断 @@ -93,7 +93,7 @@ xhs-content-ops --note-id --xsec-token --xsec-source pc_feed \ - 脚本返回正文、图片列表、作者、`stats`(点赞/收藏/评论/分享);`noteType: video` 会报错提示走 `viral-chaser`。 - cookie 回退仍失败(exit 2)-> 走 `login-manager` 重登后重试一次;笔记不可访问(已删除/私密)-> 跳过。 -下载后按 `style-dna.md` 的样本整理规范生成样本文件,互动数据作为信号线索记录。 +下载后按 `style-dna.md` 的样本整理规范生成样本文件,记录互动数据、账号简介、发布时间、内容形式、搜索关键词与用户可能提问;缺失写「未观测」。 ## Step 4 - 建立对标 DNA @@ -106,6 +106,7 @@ xhs-style-profiler report \ --sample-id {sample-id} \ --cover-image path/to/cover.jpg \ --source-url "https://www.xiaohongshu.com/explore/..." + --output-dir xhs/dna/{benchmark-dna-id}/reports ``` 再执行: @@ -121,13 +122,13 @@ xhs/dna/{benchmark-dna-id}/{benchmark-dna-id}.dna.md xhs/dna/{benchmark-dna-id}/{benchmark-dna-id}.template.md ``` -对标 DNA template 也必须固定为七个部分:选题、标题(含封面图)、开头、承、结尾、CTA、图组。后五个部分是语义结构,不限制实际字数占比;每一部分都要能从对标 DNA 文档推导。 +对标 DNA template 使用 v1 账号级语义段:定位与核心传达、选题与标题包装、内容形式与发布节奏、高数据创意模式、互动与系列、搜索意图与用户问题、制作交接与 Pipeline、口播文案 DNA。每一部分都要能从对标 DNA 文档推导。 ## Step 5 - 模式分析与差异化(agent 推理) 基于对标 DNA 与样本数据,回答三个问题(不下没有证据的结论): -1. **它为什么有效**:对标账号的高表现内容在钩子、正文结构、封面、人设、互动设计上有什么共性?哪些信号在多篇笔记中稳定出现? +1. **它为什么有效**:对标账号的高表现内容在选题、标题包装、搜索意图、内容形式、视觉语言、互动设计上有什么共性?哪些信号在多篇笔记中稳定出现? 2. **相对表现**:同一账号内部,哪类内容明显高于其他篇(用下载到的互动数据判断;无数据时只做内容面分析,不编数据)。 3. **差异化切入点**:我们的账号比对标强在哪、弱在哪?有哪些内容空白或人群空白可以切入?每个切入点说明依据和建议的验证方式(一篇笔记验证一个变量)。 @@ -167,16 +168,16 @@ xhs/dna/{base-dna-id}/{base-dna-id}.template.md 比较必须覆盖两层: -1. **16 维 DNA 文档**:逐个维度比较规则、证据和适用条件;封面与图组需比较视觉特征与 AIGC 复现要素。 -2. **七部分 template**:逐项比较选题、标题(含封面图)、开头、承、结尾、CTA、图组的执行方式。 +1. **14 维 DNA 文档**:逐个比较定位、选题、标题包装、账号简介、图文/视频比例、发布节奏、高数据创意、视觉/声音、口播文案 DNA、互动系列、搜索意图地图与制作管线。 +2. **template 语义段**:逐项比较定位与核心传达、选题与标题包装、内容形式与发布节奏、高数据创意、互动与系列、搜索意图与用户问题、制作交接、口播文案 DNA。 -每个维度和模板部分都输出四类结论: +每个维度和模板语义段都输出四类结论: | 类别 | 判断标准 | | --- | --- | | 保持 | 基线已有优势,与目标读者和商业定位一致 | | 引入 | 对标更有效,且不冲突业务事实、合规边界和用户偏好 | -| 局部借鉴 | 只适合钩子、封面、正文结构、标签等局部场景 | +| 局部借鉴 | 只适合选题、标题包装、搜索意图、图文/视频比例、发布节奏或 Pipeline 等局部场景 | | 不采纳 | 仅依赖孤例、冲突商业定位、风险高或难稳定执行 | 每项至少说明: @@ -190,29 +191,29 @@ xhs/dna/{base-dna-id}/{base-dna-id}.template.md 对比结果不自动更新基线 DNA。用户明确采纳后,选择一种方式: -1. **局部 DNA 融合**:用户决定采纳对标 DNA 的某个维度或模板部分时,直接读取对标 DNA 文档 / template 中对应内容,把它当作用户提供的一条融合要求,更新基线 DNA;不需要重新下载参考笔记,也不需要重新生成对标笔记的基线 report。 +1. **局部 DNA 融合**:读取对标 DNA 文档 / template 中对应内容,把它当作用户提供的融合要求,更新基线 DNA。 2. **局部样本借鉴**:仅在用户明确希望引入原笔记证据、权重或 focus 时,才将对标笔记重新生成属于基线 DNA 的 report,并用 `--focus` 限定采纳维度。 -3. **偏好转译**:用户表达"钩子再直接一点""封面换成大字报"等要求时,按 `style-dna.md` 的用户输入转译更新,并落到七部分 template 的具体执行字段。 +3. **偏好转译**:用户表达“搜索词更贴近提问”“图文视频比例调整”等要求时,按 `style-dna.md` 的用户输入转译更新。 4. **明确不改 DNA**:仅作为本次选题或制作参考,不落盘到 DNA。 局部 DNA 融合流程: -1. 明确采纳范围:16 维中的维度、七部分中的模板部分,或两者组合。 +1. 明确采纳范围:14 维中的维度、template 语义段,或两者组合。 2. 读取对标 DNA 文档 / template 的对应规则、适用条件和例外。 -3. 将其整理为一条可转译的输入,必须包含来源 `dna-id`、采纳范围和具体规则。 +3. 整理为一条可转译输入,包含来源 `dna-id`、采纳范围和具体规则。 4. 在基线 DNA 上执行无新增样本的 update: ```bash xhs-style-profiler update \ --dna xhs/dna/{base-dna-id}/{base-dna-id}.dna.md \ --template xhs/dna/{base-dna-id}/{base-dna-id}.template.md \ - --user-input "采纳 {benchmark-dna-id} 的开头部分:xxx" + --user-input "采纳 {benchmark-dna-id} 的搜索意图地图:xxx" ``` -5. Agent 把该输入视作用户提供的参考要求,转译为基线 DNA 的 affected dimensions、聚合结论、创作规则和七部分 template 执行字段。 -6. 融合时必须检查与基线证据、业务事实、合规边界和用户偏好的冲突;冲突保留说明,不静默覆盖。 +5. Agent 把该输入转译为基线 DNA 的 affected dimensions、聚合结论、创作规则和 template 执行字段。 +6. 融合时检查与基线证据、业务事实、合规边界和用户偏好的冲突;冲突保留说明,不静默覆盖。 -更新后必须同步修订基线 DNA 文档与 template,并保留来源说明。 +更新后同步修订基线 DNA 文档与 template,并保留来源说明。 ## 对标记录表(交付格式) diff --git a/crews/main/skills/expert-xhs/workflows/account-setup.md b/crews/main/skills/expert-xhs/workflows/account-setup.md index 0967e5f0..9732dcec 100644 --- a/crews/main/skills/expert-xhs/workflows/account-setup.md +++ b/crews/main/skills/expert-xhs/workflows/account-setup.md @@ -154,7 +154,7 @@ 2. 目标 DNA 固定为 `dna-0`。 3. 每篇生成 report。 4. 运行 `xhs-style-profiler build --dna-id dna-0`,生成 DNA 文档与 template。 -5. 把选题、钩子、正文结构、图组、可复用规则和例外发送用户确认。 +5. 把定位、选题、标题包装、账号简介、图文/视频比例、发布习惯、高数据创意、搜索关键词与用户问题、口播 DNA 与制作管线发送用户确认。 6. 用户调整意见按 `style-dna.md` 的"用户输入转译"更新到 `dna-0`。 用户没有明确权重时,所有 report 默认权重为 1。 @@ -174,7 +174,7 @@ Agent 基于 `business_knowledge.md` 和关键词宇宙提炼搜索关键词, #### 路径 C:用户拒绝外部参考或已有明确想法 1. 结合 `business_knowledge.md`、用户定位、目标读者、变现方式和用户思路,提炼 `dna-0`。 -2. Agent 直接按 16 维框架编写: +2. Agent 直接按 14 维账号级框架编写: ```text xhs/dna/dna-0/dna-0.dna.md @@ -183,7 +183,7 @@ xhs/dna/dna-0/dna-0.template.md 3. DNA 文档必须标明来源是"业务信息 + 用户指定方向",没有笔记样本统计。 4. 能确定的规则写为执行要求;未确定维度写"待校准",不得虚构单篇证据。 -5. Template 仍必须给出可执行的七部分生产结构;不能只有抽象定位词。 +5. Template 仍必须给出可执行的图文输入 / 视频 Brief 语义段;不能只有抽象定位词。 6. 将 DNA 摘要和待校准点发给用户确认,再按用户意见转译修订。 后续获得真实笔记样本时,按 `style-dna.md` 生成 report 并更新 `dna-0`。 @@ -202,7 +202,7 @@ xhs/dna/dna-0/dna-0.template.md 检查要求: - DNA 文档说明样本来源、覆盖数量、权重、例外和待校准点。 -- Template 能由 DNA 文档推导,直接指导选题、标题、开头、正文、图组和互动。 +- Template 能由 DNA 文档推导,直接指导定位、选题、标题包装、内容形式、发布节奏、高数据创意、搜索意图、互动与制作交接。 - 用户输入已转译为维度级规则,不是原话堆积。 - 对标样本 report 不混入 `dna-0`;用户采纳后,只有转译后的规则通过局部 DNA 融合进入 `dna-0`。 @@ -211,7 +211,7 @@ xhs/dna/dna-0/dna-0.template.md ### Step 1 - 获取老号样本 1. 请用户提供老号最近的代表性笔记链接(原则上至少 10 篇,账号作品不足 10 篇时提供全部并说明)。 -2. 图文笔记用 `xhs-content-ops` 逐篇下载(间隔 5-10 秒),视频笔记走 `viral-chaser`;剔除重复、删除和无效样本;保留标题、正文、图组、互动数据和作者信息。 +2. 图文笔记用 `xhs-content-ops` 逐篇下载(间隔 5-10 秒),视频笔记走 `viral-chaser`;剔除重复、删除和无效样本;保留标题、正文、发布时间、账号简介、内容形式、关键词/用户问题、图组、互动数据和作者信息。 3. 用户也可补充创作者中心后台截图(阅读、粉丝画像),作为诊断线索,不编造。 ### Step 2A - 账号基本可用,用户让 Agent 接手 @@ -226,13 +226,13 @@ xhs/dna/dna-0/dna-0.template.md ### Step 2B - 用户认为账号不太可用,需要推倒重来 -1. 基于 Step 1 拿到的样本提炼出常用署名、核心卖点、选题方向、账号名称、核心 CTA 等基础信息,将这些信息与用户确认。 +1. 基于 Step 1 拿到的样本提炼定位、账号简介、选题组合、标题包装、图文/视频比例、发布节奏、搜索关键词与用户问题、核心卖点与 CTA 等基础信息,将这些信息与用户确认。 2. 结合上述信息,走「从零开始打造新号」流程。 3. 如果 `business_knowledge.md` 不全,可以先从 Step 1 的样本中提取,不足的信息与用户讨论。 ### 老号诊断速查 -有样本时先自行分析(内容支柱、垂直度、开头钩子、标题、封面、互动设计、发布频率),再向用户补问缺失信息。按笔记失败环节倒推: +有样本时先自行分析(定位、内容支柱、标题包装、搜索意图、图文/视频比例、互动设计、发布节奏),再向用户补问缺失信息。按笔记失败环节倒推: | 现象 | 可能问题 | 修正方向 | | --- | --- | --- | diff --git a/crews/main/skills/expert-xhs/workflows/content-production.md b/crews/main/skills/expert-xhs/workflows/content-production.md index f6eda8b0..dbc99607 100644 --- a/crews/main/skills/expert-xhs/workflows/content-production.md +++ b/crews/main/skills/expert-xhs/workflows/content-production.md @@ -1,209 +1,226 @@ # 小红书内容生产 Workflow -从选题到发布的完整内容生产,覆盖两种产出形态:**图文笔记**(默认)与**视频笔记**(用户明确要求发小红书视频时)。用户说"帮我写一篇小红书笔记""发个小红书""这篇照着做一篇""把这个草稿改成小红书风格"走这个。 +从选题到发布的内容运营流程,覆盖**图文笔记**与**视频笔记**。用户说“帮我写一篇小红书笔记”“发个小红书”“这篇照着做一篇”“把这个草稿改成小红书风格”走这个。 -视频笔记的制作分工与抖音同边界:main 只做**基于已有素材的轻加工**,出脚本、从零端到端制作一律委托 `content-producer`。图文笔记的文案与图片编排由 main 直接完成。 +**分工硬边界**: + +- 图文笔记、长文内容:main agent 直接生产。 +- 已有视频素材的简单加工:main agent 直接做。 +- 视频全案:main agent 只产出 **Brief**,成片制作委托 `content-producer`。若口播文案 DNA 已启用,口播终稿由 main 写好并随 Brief 交付;CP 只做声画制作。 +- Brief 指定 Pipeline 时,CP 必须采用;未指定时 CP 自由发挥。 ## Step 0 - 入口判断 ### 1. 识别产出形态 -| 形态 | 判断 | 差异点 | +| 形态 | 判断 | 处理 | | --- | --- | --- | | 图文笔记 | 默认 | 标题 + 正文 + 图组;`xhs-publish --mode image` | -| 视频笔记 | 用户明确说视频笔记 / 发视频 | 视频为主体;`xhs-publish --mode video`;制作分工同抖音(轻加工自做,从零委托 `content-producer`) | +| 视频笔记 | 用户明确说视频笔记 / 发视频 | 视频为主体;已有素材轻加工由 main 做,全案走 Brief 委托 CP | ### 2. 识别用户输入类型 | 输入 | 模式 | | --- | --- | | 只有粗略想法或方向 | 想法模式 | -| 参考笔记(小红书链接)或参考文章(其他来源) | 参考模式 | -| 已有草稿(本地 `.md` / `.txt`、别处写好的文案、用户直接贴来的一大段内容) | 草稿模式 | +| 参考笔记 / 文章 | 参考模式 | +| 已有草稿 | 草稿模式 | +| 已有视频素材 | 素材模式 | +| 用户要求完整视频全案 | Brief 模式 | -草稿模式包含「风格转写」需求:"把这段文案改成小红书风格""用小红书的写法重写这篇"——按 DNA template 转写表达,保留事实,见 Step 5。 +草稿模式包含「风格转写」:保留事实与结论边界,只按账号 DNA 与小红书语境重写表达。 ### 3. 小红书链接的意图判断 -用户给小红书笔记链接时,先判断意图: - -- **风格吸收**:"把这篇的风格提取出来""以后照着这个味道写" -> 切换到 `style-dna.md`,生成 report 并更新 DNA。 -- **内容参考**:"照着这篇做一篇""这个选题我们也做一篇" -> 继续本流程(参考模式)。 -- 意图不明时先问用户:是吸收风格,还是参考内容。 +- **风格吸收**:“把这篇的风格提取出来”“以后照着这个味道写” → `style-dna.md` +- **内容参考**:“照着这篇做一篇”“这个选题我们也做一篇” → 本流程 +- 意图不明时先问用户。 ### 4. 与改稿的边界 -用户对一篇已完成的笔记(通常是我们此前产出的)改标题、改正文、换封面、换图,走 `editing.md`。本流程把用户输入当作素材,走完整生产链路产出新笔记。 +已完成笔记的改标题、改正文、换封面、换图走 `editing.md`。本流程把用户输入当作素材,产出新笔记。 ## Step 1 - 生产契约锁定 ### 1. 确定 DNA -生产必须绑定一个 DNA: - -1. 用户明确指定 `dna-id` 时,只用该 DNA。 -2. 用户没有指定时,用默认 `dna-0`。 -3. 不得临场凭感觉拼一个风格。 -4. 若目标 DNA 不存在,先按 `account-setup.md` / `style-dna.md` 建立或更新 DNA;完成前不进入写作。 -5. 用户意图是把参考笔记的风格吸收进 DNA 时,先按 `style-dna.md` 处理,完成 DNA 更新后再回到本流程;仅参考主题内容时直接按本流程生产,不动 DNA。 +1. 用户指定 `dna-id` 时只用该 DNA。 +2. 未指定时用默认 `dna-0`。 +3. 不得临场拼风格。 +4. 目标 DNA 不存在时,先走 `account-setup.md` / `style-dna.md`。 +5. 参考风格要吸收进 DNA 时,先更新 DNA 再生产。 -写前必须读取两份文件: +写前读取: ```text xhs/dna/{dna-id}/{dna-id}.dna.md xhs/dna/{dna-id}/{dna-id}.template.md ``` -DNA template 是选题、标题、开头钩子、正文结构、语气、图组和互动路线的直接执行依据;DNA 文档用于理解稳定性、例外和适用条件。委托 `content-producer` 制作视频笔记时,把 template 的执行要求写进制作简报。 +DNA template 是 main agent 的图文生产 / 视频 Brief 输入模板,覆盖定位与核心传达、选题与标题包装、内容形式与发布节奏、高数据创意、互动系列、搜索意图与用户问题、制作交接与可选口播文案 DNA。它不规定逐句正文、单图构图、镜头表、转场或编码细节。 ### 2. 读取业务知识 -内容必须为 Workspace 根的 `business_knowledge.md` 服务。写前提取: +从 `business_knowledge.md` 提取: -- 产品 / 服务概括和核心差异 -- 目标读者与具体痛点 -- 转化目标或下一步动作 -- 红线、合规限制和品牌语气 +- 产品 / 服务与核心差异 +- 目标读者与痛点 +- 转化目标与平台内动作 +- 红线、合规限制与品牌语气 -内容选题、案例、承诺、互动引导和商业表述必须与这些信息一致。关键信息缺失时先问用户,不虚构。 +关键信息缺失先问用户,不虚构。 ### 3. 采集生产要求 -根据输入和 Memory 确定以下信息是否齐全,如有缺失一次性向用户问清: - | 项目 | 规则 | | --- | --- | -| 产出形态 | 图文笔记或视频笔记,未明确时默认图文 | -| 主题 / 方向 | 用户给了明确主题时不得另起炉灶,仅按 DNA template 细化选题和钩子 | -| 素材 | 用户提供的图片、文案、截图、案例必须优先使用 | -| 目标读者 | 未指定时按 `business_knowledge.md` 和 DNA 受众关系推导 | -| 标题 | 用户指定时逐字使用;未指定时按 DNA template 标题维度拟候选(硬限制 ≤ 20 字) | -| 图组 | 用户提供图片优先;未提供时按 Step 5 生成(硬限制 ≤ 18 张) | -| 行动引导 | 用户指定时优先执行;未指定时按业务目标与 DNA template 推导,只放一个平台内动作 | - -优先级固定为: +| 产出形态 | 图文 / 视频;未明确默认图文 | +| 主题 / 方向 | 用户指定时不得另起炉灶 | +| 搜索意图 | 从 DNA 搜索意图地图取主关键词、相关词、用户可能提问;无结论时向用户确认 | +| 素材 | 用户提供的图片、文案、截图、案例、视频片段优先 | +| 目标读者 | 未指定时按业务知识与 DNA 定位推导 | +| 标题 | 硬限制 ≤ 20 字;覆盖主关键词或用户问题 | +| 图组 | 硬限制 ≤ 18 张;用户提供优先 | +| 行动引导 | 只放一个平台内动作 | +| Pipeline | 视频全案已确定时写 CP 支持的 Pipeline;未确定则省略 | + +优先级: ```text 用户明确交付要求 + 业务事实 / 红线 > 用户提供的素材 > business_knowledge.md -> DNA template 的风格规则 +> DNA 的账号级规则 > Agent 的一般内容判断 ``` -DNA 约束的是选题、标题、开头、正文结构、语气、视觉和互动方式;不能覆盖用户指定素材、事实、合规边界和转化要求。 - ## Step 2 - 素材获取与整理 -先建立笔记目录 `xhs/outputs//`(note-name 用主题的短 slug;选题尚未确定时可先用暂代名,选题确定后随之定名),下设 `materials/` 子目录,获取到的素材统一放入 `materials/`。 - -1. 按类型获取输入: - - 小红书图文笔记链接 -> `xhs-content-ops --url --output-dir xhs/outputs//materials/` 下载正文与图片(仿照制作时它就是参考素材)。 - - 小红书视频笔记链接 -> self-spawn subagent 走 `viral-chaser` 拆解。 - - 其他来源链接 -> 用 web_fetch 或浏览器工具读取正文。 - - 本地图片 / 文案 -> 复制进 `materials/`。 - - 用户直接输入的文字(想法、草稿、要点)-> 保存为 `.md` 放入 `materials/`。 -2. 建立素材清单:用户原话、事实、数据、案例、图片、可用观点、待确认信息。 -3. 区分三类输入: - - **必用素材**:用户明确要求采用的事实、观点、图片和文案。 - - **参考素材**:提供方向或表达参考,不直接替代用户结论。 - - **对标素材**:只用于模式比较,不能覆盖用户素材和业务事实。 -4. 素材不足时先补问;确实无法补充时,在内容中避免无证据断言。素材之间冲突时列出冲突并问用户;商业事实以 `business_knowledge.md` 和用户最新说明为准。 -5. 用户意图是"保留内容、只换小红书的表达方式"(同主题改写 / 风格转写)时,整理**内容资产清单**而不是风格分析:主题、核心观点、事实、数据、案例、必须保留的关键词与专有名词。不能确认的信息标为「待核实」,禁止为了表达顺滑而补事实。 +先建 `xhs/outputs//`,下设 `materials/`。 -## Step 3 - 选题 +1. 图文笔记链接 → `xhs-content-ops` 下载正文、图片、互动数据。 +2. 视频笔记链接 → self-spawn subagent 走 `viral-chaser`。 +3. 本地图片 / 视频 / 音频 / 文档 → 复制或提取进 `materials/`。 +4. 建立素材清单:用户原话、事实、数据、案例、画面素材、授权信息、待确认项。 +5. 区分必用素材、参考素材、对标素材;冲突时问用户,商业事实以 `business_knowledge.md` 和用户最新说明为准。 -### 选题调研(给出选题方向前执行) +## Step 3 - 选题与搜索意图 -先提炼 1-3 个核心关键词,用 `smart-search` 技能与站内检索做一轮调研,再给选题方向: +### 选题调研 -1. **站内调研优先**:小红书是这个选题的主场时,按 `account-benchmark.md` 的关键词思路,在小红书搜索关键词看真实笔记的讨论角度、高频痛点与高热形态(搜索只读不互动,控制频率)。 -2. **跨平台补充**:`smart-search` 按主题选 1-2 个贴合的社交平台(生活方式 / 真实体验 -> 小红书;深度问答 -> 知乎;热点 -> 微博),提炼真实讨论、痛点与争议点。 -3. 有价值的内容线索按 Step 2 素材清单归档为「参考素材」。 +1. 从 DNA 搜索意图地图取主关键词、相关词、长尾句与用户可能提问。 +2. 用 `smart-search` 做平台侧调研,优先小红书 / 知乎 / 抖音等贴合平台。 +3. 提炼真实用户讨论、痛点、争议点、常见提问与高热内容形式。 +4. 有价值线索归档为参考素材,不直接替代用户事实。 -- ✅ 拿真实用户讨论和站内已有内容形态做选题依据。 -- ❌ 把通用搜索引擎当选题调研首选——通用网页结果缺少真实用户声音和互动数据。仅在没有贴合的社交平台、或需核实客观事实数据时才兜底使用。 +### 选题输出 -### 想法模式 +给出 1-3 个选题方向,每个方向必须说明: -结合用户想法、选题调研结论与 DNA template 的选题维度,给出 **3 个选题方向**,分别阐述理由,让用户挑一个。推荐理由必须同时覆盖: +- 覆盖哪个主关键词 / 用户问题 +- 使用哪些用户素材 +- 服务哪个业务目标 +- 符合 DNA 的哪些选题组合、内容形式与高数据创意 +- 产出形态建议:图文 / 视频 / 混合节奏中的哪一环 -1. 使用了哪些用户素材。 -2. 服务哪个业务目标。 -3. 符合 DNA template 的哪些选题、钩子与受众特征。 -4. 选题调研发现:相关主题的讨论热度或用户痛点。 +用户已明确主题时只做轻量核实,不另起炉灶。 -### 参考 / 草稿模式 +## 【确认】选题与搜索意图 -从参考笔记或用户草稿中提取选题方向,与 DNA template 的选题维度对比:选题已由参考内容或草稿确定时,可只做轻量调研(1 个平台、1 次查询)核实热度和痛点,不做全量调研: +第一个必停节点。确认主题、主关键词与目标用户问题。 -- **相符** -> 直接采纳。 -- **不相符** -> 明确指出差异,跟用户确认:综合调整以贴合 DNA,还是就用参考的选题。 -- 用户明确换了主题("照着这篇的结构,主题换成 XX")-> 用用户给的新主题,参考笔记只提供结构与钩子参考。 - -选题必须优先消耗用户素材,并服务于 `business_knowledge.md` 中的产品服务或转化目标。 +## Step 4 - 标题与话题标签 -## 【确认】选题 +1. 标题 ≤ 20 字,优先覆盖主关键词或用户问题的自然表达。 +2. 标题模式按 DNA 的 `title-packaging`,不照抄参考标题。 +3. 话题标签 ≤ 10 个,内联在正文中;按核心词 / 场景词 / 人群词 / 痛点词组织,不堆砌。 +4. 用户指定标题或标签时逐字或按原意执行。 +5. 简介与正文可提及产品与业务,但不得出现联系方式、二维码或站外引流;禁止谐音绕检测。 -第一个必停节点。确认的是方向,不是每个字。 +## 【确认】标题与标签 -- 小调直接改。 -- 换主题、换读者、换转化目标 -> 重做本步。 +第二个必停节点。用户确认后才进入正文或视频 Brief。 + +## Step 5A - 图文正文 + +图文笔记由 main 直接写 `xhs/outputs//note.md`。 + +1. 正文 ≤ 1000 字,首个一级标题行写标题,正文保留换行节奏。 +2. 按 DNA 的定位、核心传达、搜索意图、互动目标组织内容;不套用固定逐句模板。 +3. 开头必须兑现标题与封面承诺,优先回答用户问题或给出核心结论。 +4. 用户必用素材优先进入正文,不得为了形式删除关键事实。 +5. 商业承诺、服务范围、价格和案例必须与 `business_knowledge.md` 一致。 +6. 只选择一个平台内动作:评论 / 收藏 / 关注 / 进店 / 咨询。 +7. 风格转写只改表达与结构呈现,不改事实含义、数据、案例、引用和结论边界。 +8. 同主题改写可借鉴参考结构,文字必须用自己的素材与表达重写,禁止搬运。 + +## Step 5B - 视频全案 Brief + +视频全案不写全片脚本,main 只产出 `xhs/outputs//brief.md`: + +```markdown +# 小红书视频制作 Brief + +- dna_id: +- pipeline:dna-ad-video-pipeline / video-producer:default / 未指定 +- MainAgent 交付物:Brief、标题与正文、素材清单、(如启用)口播终稿 +- ContentProducer 交付物:成片、封面候选、交付说明、自检结果 +- 账号定位与核心传达: +- 目标人群与观看理由: +- 选题与搜索意图:主关键词、相关词、用户可能提问 +- 内容形式:口播 / 实拍拼接 / 创意转场 / 纯 AIGC 动画 / 混合 +- 素材清单、来源与授权: +- 视觉与声音边界:横竖屏、画面风格、音色/BGM 倾向(不写逐镜细节) +- 互动与转化目标: +- 时长带与验收标准: +- 风险检查:事实、承诺、授权、平台合规 +``` -## Step 4 - 标题与话题标签 +口播文案 DNA 已启用时,main 按 `narration-dna` 写 `voiceover.md`,CP 不重写策略文案;未启用时,CP 在 Pipeline 内完成脚本与声画方案。 -按 DNA template 列出的标题类型拟标题,按话题标签策略拟标签: +## 【确认】正文 / Brief -- 有参考笔记或草稿时,必须参考其标题内容(如有),但不得照抄。 -- 用户已指定标题时,候选必须在该约束内生成,不得偷换方向。 -- 标题硬限制:**不超过 20 字**(`xhs-publish` 强制校验)。 -- 正文硬限制:**不超过 1000 字**;话题标签硬限制:**不超过 10 个**(超出会被静默丢弃或限流)。 -- 话题标签以 `#话题` 形式内联在正文中,按 DNA template 的标签策略组织(品类词 / 场景词 / 人群词 / 痛点词),不堆砌。 -- 简介与正文可提及产品与业务,但不放明显引流信息;禁止二维码、联系方式、谐音绕检测;可引导用户主动搜索或看主页。 +第三个必停节点: -## 【确认】标题与标签 +- 图文:确认正文与图组方向。 +- 视频:确认 Brief(及口播终稿,如有)后才委托 CP。 +- Brief 变更时更新版本并推送变更要点;已开工中间产物按新版取舍,弃用部分记入交付说明。 -第二个必停节点。务必与用户就标题确认。用户如果提出修改意见,按用户意见修改,直至与用户达成一致。 +## Step 6 - 图组 / 视频制作 -## Step 5 - 正文 +### 图文图组 -初稿直接写入 `xhs/outputs//note.md`(首个一级标题行写标题,正文保留换行节奏)。 +1. 读取 DNA 的视觉语言与标题包装,只取账号级风格边界。 +2. 图片来源优先级:用户素材 → `campaign_assets/` → `siliconflow-img-gen` → `pexels-footage` / `pixabay-footage`。 +3. 封面必须存在,兑现标题承诺;生成图发用户确认。 +4. 图文建议 3:4 竖版,图片 ≤ 18 张,顺序按信息推进。 +5. 图片与正文分工:图承载证据、过程、对比或清单;文承载判断、细节与行动。 -1. 严格按 DNA template 写:开头、承、结尾、CTA 各部分的任务、句式节奏、语气与执行方式。 -2. 开头 1-2 行必须兑现标题与封面的承诺,按 template 钩子类型起步。 -3. 用户必用素材优先进入正文,不得为了模板形状删除关键事实。 -4. 商业承诺、服务范围、价格和案例必须与 `business_knowledge.md` 一致。 -5. 用户指定 CTA 时逐字或按原意执行;未指定时只选择一个与业务目标匹配的平台内动作(评论 / 收藏 / 关注 / 进店 / 咨询),不堆叠多个转化要求。 -6. **风格转写(草稿模式)**:只改表达方式和结构呈现,不改事实含义;数据、案例、引用和结论边界保持一致。转写按 DNA template 的语气、正文结构与 emoji 节奏执行;DNA 没有结论时按小红书常规语感(口语化、短句分行、适度 emoji),不强制固定 emoji 数量。 -7. 同主题改写:参考笔记的正文结构可借鉴,文字必须用自己的素材与表达重写,禁止搬运他人内容。 +### 视频制作 -## Step 6 - 图组 +| 情况 | 处理 | +| --- | --- | +| 用户直接提供成片 | 校验格式与时长,复制到作品目录 | +| 已有素材需简单加工 | main 用 `video-edit` / `talking-head-cut` 处理 | +| 全案制作 | 委托 `content-producer`,只交付确认后的 Brief;指定 Pipeline 必须采用,未指定由 CP 自由发挥 | -1. 读取 DNA template 的图组部分与封面图风格、封面 AIGC 提示词要素。 -2. 图片来源优先级(与 workspace 约定一致): - - 用户提供的素材(优先使用,确认后复制进 `xhs/outputs//`)。 - - `campaign_assets/` 积累的素材图。 - - 技能生成:`siliconflow-img-gen` 优先;不可用时 `pexels-footage` / `pixabay-footage` 下载免版权图。 -3. 封面图(首图)必须存在:按 template 的封面承诺与版式要求准备;生成图发用户确认。 -4. 图文建议 3:4 竖版;图片总数 ≤ 18 张;图片顺序按正文信息推进排列。 -5. 视频笔记的封面:优先从成片选帧;需要更强视觉冲击时用 `siliconflow-img-gen`。 +视频封面优先从成片选帧;需要更强视觉冲击时用 `siliconflow-img-gen`。 -## 【确认】正文与图组 +## 【确认】正文与图组 / 成片与封面 -第三个必停节点。正文、图片与封面确认后才可发布。用户有意见按意见修改,直至确认。 +第四个必停节点。图文确认正文、图组与封面;视频确认成片、封面、标题正文与发布描述。 ## Step 7 - 存文件 -定稿笔记目录 `xhs/outputs//`: +`xhs/outputs//`: -- `note.md`(首个一级标题行 = 标题,正文含内联 `#话题`) -- 封面图与图组图片(与 note.md 同目录,文件名互不重名) -- `materials/` 原始素材(Step 2 已放入) -- `dna-meta.json` 内容形如 `{"platform": "xhs", "dna_id": ""}`,记录本篇所用 DNA(发布记录时 `published-track record` 自动读取建立关联,供后续 DNA 表现评估) +- 图文:`note.md`、封面与图组图片、`materials/`、`dna-meta.json` +- 视频:`brief.md`、`voiceover.md`(如启用)、`note.md`(标题 + 发布正文)、`video.mp4`、`cover.jpg`、`materials/`、`dna-meta.json` +- `dna-meta.json`:`{"platform":"xhs","dna_id":""}` ## Step 8 - 发布 -发布走 `xhs-publish`(工具说明见包内 `xhs-publish` 文档): +发布走 `xhs-publish`: ```bash # 1. 探活(批量发布只探活一次) @@ -217,15 +234,22 @@ xhs-publish --mode image --title "标题" --body "正文(含 #话题1 #话题2 xhs-publish --mode video --title "标题" --body "正文" --video video.mp4 --cover cover.jpg ``` -- `--body` 必须传实际文字,不能传文件路径或 `$(cat file)`(exec sandbox 禁用命令替换);正文直接从 `note.md` 读出后硬编码进命令。 -- AIGC 生成的内容按平台规则标注(脚本默认声明);纯实拍素材不声明。 -- 返回 `{"ok": true, "note_id": ..., "url": ...}` 才算发布成功;`AUTH_EXPIRED` 重登后重试一次;`RATE_LIMIT` 等 30 分钟后重试。 -- **限频**:单账号每天 ≤ 1-3 篇(仅在产能和质量都跟得上时),可持续节奏优先;触发风控立即降级,30 分钟内不重试。 -- xhs 风控敏感度显著高于其他平台:发布失败伴随登录异常迹象时,停止重试,记下来走重登流程,不要批量重试撞风控。 +- `--body` 必须传实际文字,不能传文件路径或 `$(cat file)`;从 `note.md` 读出后硬编码进命令。 +- AIGC 生成内容按平台规则标注;纯实拍素材不声明。 +- 返回 `{"ok": true, "note_id": ..., "url": ...}` 才算成功;`AUTH_EXPIRED` 重登后重试一次;`RATE_LIMIT` 等 30 分钟后重试。 +- 单账号每天 ≤ 1-3 篇;触发风控立即降级,30 分钟内不重试。 +- 发布失败伴随登录异常迹象时停止重试,记下来走重登流程,不批量撞风控。 ## Step 9 - 记录 -发布成功(拿到笔记链接)后入库: +发布成功后: + +```bash +published-track record \ + --platform xhs \ + --source-folder xhs/outputs// \ + --account <发布账号 alias> \ + --publish-url +``` -1. 调 `published-track record`:`--platform xhs`、`--source-folder xhs/outputs//`、`--account <发布所用账号 alias>`、`--publish-url `(`dna_id` 自动从 `dna-meta.json` 读取)。 -2. 发布流程到此结束;互动数据由每日定时采集任务统一抓取(`xhs-engagement`),复盘走 `review.md`。 +`dna_id` 自动从 `dna-meta.json` 读取。互动数据由每日任务统一抓取,复盘走 `review.md`。 diff --git a/crews/main/skills/expert-xhs/workflows/editing.md b/crews/main/skills/expert-xhs/workflows/editing.md index 10548bae..08465f26 100644 --- a/crews/main/skills/expert-xhs/workflows/editing.md +++ b/crews/main/skills/expert-xhs/workflows/editing.md @@ -10,28 +10,28 @@ | 用户的说法 | 改的层级 | 怎么走 | |-----------|---------|--------| -| "改下标题" | 标题层 | 按 DNA template 标题维度重写,给 2-3 个候选 | -| "正文改改 / 精简 / 扩写 / 换开头" | 正文层 | 按 DNA template 对应部分(开头/承/结尾/CTA)局部调整 | +| "改下标题" | 标题层 | 按 `title-packaging` 与 `search-intent-map` 重写,给 2-3 个候选 | +| "正文改改 / 精简 / 扩写 / 换开头" | 正文层 | 按 `positioning-core`、`search-intent-map` 与互动目标局部调整 | | "标签换一下 / 加标签" | 标签层 | 按 template 话题标签策略重组(≤10 个硬限制) | -| "换封面 / 换图 / 加图" | 图组层 | 用户指定 / `siliconflow-img-gen` 按 template 封面规则重做 | +| "换封面 / 换图 / 加图" | 图组层 | 用户指定 / `siliconflow-img-gen` 按 `visual-language` 重做 | | "结构调一下 / 改成清单体" | 结构层 | 先出调整方案(新的段落组织与信息顺序)-> 确认 -> 重写正文 | | "换个风格 / 用另一个 DNA 写" | 方向层 | 明确目标 `dna-id`,按目标 template 重写;等于一次小型再生产 | | "方向不对 / 选题要换" | 选题层 | 回到 `content-production.md` 从选题重新走 | ## 标题层 -1. 目标 DNA 不确定时先确认(用户指定或默认 `dna-0`),读取 template 标题维度。 +1. 目标 DNA 不确定时先确认(用户指定或默认 `dna-0`),读取 `title-packaging` 与 `search-intent-map`。 2. 硬限制 ≤ 20 字;有参考标题时参考但不照抄。 3. 给 2-3 个候选并说明各自侧重的标题类型,用户选定后更新 `xhs/outputs//note.md` 的标题行。 ## 正文层 -按目标 DNA template 的对应部分修改: +按目标 DNA 的账号级约束修改: -- 改开头:按 template 开头部分的钩子类型与句式重写前 1-2 行,确保与标题/封面承诺对应。 -- 改主体:按承部分的推进逻辑、信息密度与换行节奏调整;必用素材与业务事实不得在修改中丢失。 -- 改结尾与 CTA:按结尾与 CTA 部分重写,只保留一个平台内行动引导。 -- 改 emoji 与标点:按 emoji 节奏维度调整密度与位置,不强制固定数量。 +- 改开头:确保兑现标题与封面承诺,并优先回答用户问题或给出核心结论。 +- 改主体:围绕核心传达与搜索意图调整信息顺序;必用素材与业务事实不得丢失。 +- 改结尾与 CTA:只保留一个平台内行动引导,与 `engagement-conversion` 一致。 +- emoji 与标点按小红书语感和用户偏好调整,不作为 DNA 维度机械规定。 - 正文硬限制 ≤ 1000 字;改完同步更新 `note.md`。 风格转写类修改("改成更口语一点""换成干货风"):转译为具体维度要求再动笔,不把形容词直接当指令。 @@ -45,14 +45,14 @@ ## 图组层 1. 用户直接指定图片 -> 直接使用,复制进笔记目录。 -2. 用户说"重做封面" -> 按 DNA template 封面图风格与 AIGC 提示词要素,用 `siliconflow-img-gen` 生成,用户确认后替换。 +2. 用户说"重做封面" -> 按 DNA `visual-language`、标题承诺与核心收益,用 `siliconflow-img-gen` 生成,用户确认后替换。 3. 增删配图:总数 ≤ 18 张;顺序按正文信息推进重排;替换后更新 `note.md` 同目录图片与引用。 4. 图文建议 3:4 竖版;封面(首图)必须存在。 ## 结构层 先出结构调整方案(新的段落组织、信息顺序、删留内容),用户确认后再动笔。 -结构动了通常钩子和节奏也要跟着调——按目标 DNA template 的开头、承、结尾重新核对各段任务,别只动骨架不换节奏。 +结构动了先核对核心传达、搜索意图与用户问题是否仍被回答;不要套用固定逐句模板。 ## 方向层 diff --git a/crews/main/skills/expert-xhs/workflows/review.md b/crews/main/skills/expert-xhs/workflows/review.md index d1eb6544..223b8cb8 100644 --- a/crews/main/skills/expert-xhs/workflows/review.md +++ b/crews/main/skills/expert-xhs/workflows/review.md @@ -35,17 +35,17 @@ xhs 互动数据来自创作者后台笔记管理页 5 列(`xhs-engagement` - 精确的传播系数(每次分享带来多少新观众)不可得;只能用 分享/(点赞+评论) 作为传播效率的代理估算。 - 用户可提供创作者中心后台截图(流量来源、粉丝画像、搜索词),作为更高置信度的证据;没有就用库内指标。 -### 互动漏斗 → template 七部分 → 16 维映射 - -| 漏斗卡点 | 先怀疑的 template 部分 | 可回溯的 DNA 维度 | -|---------|----------------------|------------------| -| 阅读低(推荐/点击瓶颈) | 选题、标题(含封面图) | topic-angle、title-style、cover-imageset、keyword-seo | -| 阅读正常但收藏低 | 承(实用价值密度) | body-structure、credibility-proof、keyword-seo | -| 阅读正常但评论低 | CTA(互动设计) | interaction-design、opening-hook | -| 点赞低 | 承、结尾(价值感与情绪落点) | language-tone、credibility-proof、emoji-rhythm | -| 分享低 | 选题、CTA(社交货币) | topic-angle、cta-conversion | -| 关注转化低 | 结尾、CTA(账号承诺) | signature-mark、series-design、topic-angle | -| 搜索流量弱 | 标题(含封面图)、标签 | keyword-seo、tag-strategy | +### 互动漏斗 → template 语义段 → 14 维映射 + +| 漏斗卡点 | 先怀疑的 template 语义段 | 可回溯的 DNA 维度 | +|---------|--------------------------|------------------| +| 阅读低(推荐/点击瓶颈) | 定位与核心传达、选题与标题包装、搜索意图 | positioning-core、topic-portfolio、title-packaging、search-intent-map | +| 阅读正常但收藏低 | 搜索意图与用户问题、高数据创意模式 | search-intent-map、high-performer-patterns、positioning-core | +| 阅读正常但评论低 | 互动与系列 | engagement-conversion、search-intent-map | +| 点赞低 | 高数据创意模式、定位与核心传达 | high-performer-patterns、positioning-core | +| 分享低 | 选题与标题包装、互动与系列 | topic-portfolio、engagement-conversion | +| 关注转化低 | 定位与核心传达、互动与系列 | bio-profile、series-signature、engagement-conversion | +| 搜索流量弱 | 搜索意图与用户问题、选题与标题包装 | search-intent-map、title-packaging、content-form-mix | ### 平台混杂因素(归因前必排) @@ -86,7 +86,7 @@ content-calibrator eval --platform xhs --dna-id # 指定 DNA 1. 判定只看比值与走向,绝对值只作上下文。 2. 逐条排除平台混杂因素,输出替代假设检验结果。 -3. 回读 `xhs/dna//.dna.md` / `.template.md` 与待评估作品原文(`source_folder` 下的 `note.md` 与图片),把趋势变化落到 template 七部分与 16 维。 +3. 回读 `xhs/dna//.dna.md` / `.template.md` 与待评估作品(`source_folder` 下的 `note.md`、`brief.md`、`voiceover.md` 与图片/成片线索),把趋势变化落到 template 语义段与 14 维。 ### Step 3 - 报告与标记 diff --git a/crews/main/skills/expert-xhs/workflows/style-dna.md b/crews/main/skills/expert-xhs/workflows/style-dna.md index 9716d568..6bc5779d 100644 --- a/crews/main/skills/expert-xhs/workflows/style-dna.md +++ b/crews/main/skills/expert-xhs/workflows/style-dna.md @@ -1,43 +1,37 @@ -# 小红书内容 DNA 创建与更新 Workflow +# 小红书账号级 DNA 创建与更新 Workflow -本 Workflow 只负责内容 DNA 三层产物的创建与更新:判断样本来源、选择目标 DNA、获取笔记样本材料(`xhs-content-ops` 下载图文 / `viral-chaser` 拆解视频笔记)、调用 `xhs-style-profiler` 生成或更新 report / DNA 文档 / template。16 维提取与聚合方法以 `xhs-style-profiler` 为准(维度框架见其 `references/xhs-note-dna-dimensions.md`,初始版本已确认)。 +本 Workflow 负责 DNA report、DNA 文档、搜索意图地图与 DNA template 的创建与更新。维度框架以 `xhs-style-profiler` 的 `references/account-dna-framework.md` 为准(账号级 DNA v1)。 -边界说明: +## 边界 -- 账号初始化、定位梳理与默认 `dna-0` 初始化走 `account-setup.md`(该 workflow 会调用本 workflow 的样本获取与更新机制)。 -- 账号对标分析走 `account-benchmark.md`;本 workflow 只承担对标 DNA 的三层产物生成与更新,以及采纳后并入基线 DNA 的更新。 -- DNA 如何被用于内容生产(含仿写、草稿改写)由 `content-production.md` 规定,改稿与调整由 `editing.md` 规定;本 workflow 不描述生产过程。 - -DNA 描述选题、标题、开头钩子、正文结构、语气、视觉与互动标记等内容规则;不描述发布操作与数据复盘。 +- DNA 是账号级运营框架,不是单篇笔记写作模板。 +- 账号初始化与默认 `dna-0` 建立走 `account-setup.md`;对标样本先走 `account-benchmark.md`。 +- DNA 如何用于内容生产走 `content-production.md`;改稿走 `editing.md`;数据复盘走 `review.md`。 +- 图文由 main agent 直接生产;视频全案只到 Brief,制作委托 `content-producer`。 ## 入口判断 走本 Workflow: -- "建 DNA / 更新 DNA / 提炼这篇笔记的风格" -- "把这篇笔记落到某个 DNA 上" -- "提取下这个账号的内容 DNA" +- “建 DNA / 更新 DNA / 提炼这个账号的风格” +- “把这篇笔记落到某个 DNA 上” +- “用户提供单篇或多篇笔记,帮我提炼可复用模式” +- “整理这个账号的搜索关键词和用户问题” 不走本 Workflow: -- "帮我写一篇笔记 / 照着这篇仿一篇 / 这个选题我们也做一篇"(使用 DNA 生产)-> 走 `content-production.md` -- "改改这篇的标题 / 正文 / 封面" -> 走 `editing.md` -- "看数据 / 复盘 / 评估这个 DNA" -> 走 `review.md` -- "起号 / 梳理账号定位" -> 先走 `account-setup.md` -- "分析对标账号 / 找低粉爆款 / 对比风格差异" -> 走 `account-benchmark.md` +- 做内容 / 仿内容 → `content-production.md` +- 改标题 / 正文 / 封面 / 标签 → `editing.md` +- 看数据 / 评估 DNA → `review.md` +- 起号 / 定位 → `account-setup.md` +- 对标比较 → `account-benchmark.md` ## 目标 DNA 选择 -1. 用户明确指定 `dna-id` 时,使用该 DNA。 -2. 用户明确说"落到默认 DNA"或未指定目标时,使用 `dna-0`。 -3. 用户意图是账号对标、模式对比时,走 `account-benchmark.md`,对标样本必须进入独立 `dna-id`,不得直接写入 `dna-0`。 -4. 找不到目标 DNA 文档时,先走 `account-setup.md` 建立 `dna-0`,或按用户明确指定的新 `dna-id` 初始化。 - -默认规则: - -- 除 `account-benchmark.md` 分流的对标样本外,未特别分流的新参考笔记累积到 `dna-0`。 -- `dna-0` 不代表某个参考账号,而是当前工作区的默认内容生产规则集。 -- 用户可以随时把样本、偏好或局部借鉴明确落到任意已有 DNA。 +1. 用户指定 `dna-id` 时使用该 DNA。 +2. 未指定或说“默认 DNA”时使用 `dna-0`。 +3. 对标样本必须进入独立 DNA,不直接写入 `dna-0`;采纳后再通过局部融合更新。 +4. 目标 DNA 不存在时,先走 `account-setup.md` 或按用户明确指定的新 `dna-id` 初始化。 ## 存储结构 @@ -51,59 +45,55 @@ xhs/dna/{dna-id}/ {dna-id}.template.md ``` -- 原始笔记文本可以临时保存在 `xhs/ref/{dna-id}/notes/`,也可以来自用户提供的路径。 -- 生成后的 DNA report 必须进入目标 DNA 的 `reports/` 目录。 -- `sample-id` 必须可读且稳定;覆盖同名 report 前,先向用户说明该文件会被重算。 +原始笔记文本可临时放在 `xhs/ref/{dna-id}/notes/`。生成后的 report 必须进入目标 DNA 的 `reports/` 目录;覆盖同名 report 前先向用户说明。 ## 样本获取 -小红书样本的观测物是图文笔记:标题、正文(含内联话题标签)、封面与图组、互动线索。获取方式: - | 来源 | 处理 | | --- | --- | -| 小红书图文笔记链接(`xiaohongshu.com/explore/...` / `xhslink.com` 短链) | `xhs-content-ops --url --output-dir xhs/ref/{dna-id}/notes/{sample-id}/` 下载正文、图片与互动数据 | -| 小红书视频笔记链接 | self-spawn subagent 走 `viral-chaser` 拆解(转录、封面、互动线索),转录整理为笔记文本 | -| 用户提供的文字稿 / 笔记草稿 / 口述要点 | Agent 整理为样本 `.md` 后直接作为 profiler 输入 | -| 用户直接的想法 / 偏好 | 不生成 report,按"用户偏好"转译进入目标 DNA | - -下载后的笔记整理为样本文件(格式约定见 `xhs-style-profiler` 的「样本文件约定」): - -1. 首个一级标题行写笔记标题,正文保留原换行节奏、emoji 与内联 `#话题`。 -2. 下载脚本返回的 `stats`(点赞/收藏/评论/分享)与作者、来源 URL 记在样本文件末尾的线索区或报告中,作为互动信号,不写进正文统计。 -3. 首图(或用户指定的封面)作为 `--cover-image`;图组其余图片保留在素材目录,供视觉模型分析图组维度时读取。 -4. 剔除重复、已删除、正文缺失或纯广告样本。 -5. 保留来源 URL、作者、发布时间和获取时间作为报告线索。 -6. Profiler 本身不限制样本量;一个样本生成单篇 report,多个样本聚合统计。账号级初始化、老号诊断和对标比较可在 `account-setup.md` / `account-benchmark.md` 设置最低样本要求。 -7. 互动数据只来自 `xhs-content-ops` / `viral-chaser` 返回或用户提供的数据线索,不得编造;选样本时可参考互动信号,但数据好坏不直接等于风格好坏。 -8. 封面 / 图组缺失时保留缺失状态,不得用正文内容或想象补齐视觉证据。 +| 图文笔记链接 | `xhs-content-ops` 下载正文、图片与互动数据 | +| 视频笔记链接 | self-spawn subagent 走 `viral-chaser` 拆解 | +| 用户提供的草稿 / 文字稿 | 整理为 `.md` / `.txt`,保留数据与账号线索 | +| 用户想法 / 偏好 | 不生成 report,按用户输入转译进入 DNA | -## 建立或重建 DNA +每篇样本尽量收集: + +- 样本类型:账号作品 / 用户提供单篇 +- 账号名、简介、置顶笔记、主页承诺 +- 发布时间、内容形式(图文/视频/混合)、素材来源与授权 +- 阅读、点赞、收藏、评论、搜索来源等数据线索 +- 主关键词、相关词、长尾句、用户可能提问 +- 横竖屏、时长、口播/实拍/AIGC 形态 -适用场景:目标 DNA 下还没有 report,或用户要求基于当前输入整体重建。 +缺失字段写「未观测」。数据只作证据,不自动判断风格好坏。 + +## 建立或重建 DNA ### Step 1 - 准备样本 -把可用笔记整理为样本 `.md`(含封面图路径与图组目录),并确定目标 `dna-id`。未指定时使用 `dna-0`。 +1. 把笔记整理为 `.md`:首个一级标题为标题,正文保留换行与内联话题标签。 +2. 确定目标 `dna-id`、`sample-id`、样本权重和 focus。 +3. 整理账号观测、搜索关键词与用户问题,供 Agent 补进 report。 ### Step 2 - 生成单篇 report -每篇笔记执行一次: - ```bash xhs-style-profiler report \ --input path/to/note.md \ --dna-id {dna-id} \ --sample-id {sample-id} \ --cover-image path/to/cover.jpg \ - --source-url "https://www.xiaohongshu.com/explore/..." + --source-url "https://www.xiaohongshu.com/explore/..." \ + --output-dir xhs/dna/{dna-id}/reports ``` -可选参数: - -- `--weight N`:用户明确说某篇参考价值非常高时使用。 -- `--focus DIMENSION`:用户明确说只借鉴钩子、封面、标签等局部时使用,可重复传入。 +生成 scaffold 后必须: -Agent 生成 scaffold 后必须回读笔记原文(必要时回看图组图片),补齐 16 维单篇结论、原文证据和可复用信号;开头钩子维度必须逐字摘录正文前 1-2 行;封面与图组维度必须由视觉模型读取本地图片,输出可复现的 AIGC 生成要素。 +1. 补齐「样本与账号观测」。 +2. 回读笔记原文,补齐 14 维的单篇结论、证据和可复用信号。 +3. 搜索关键词必须落到用户可能提问;单篇只记候选,不直接判定账号级搜索意图。 +4. 单篇样本不得推导账号简介、图文/视频比例、发布节奏或高数据共性。 +5. 视觉语言必须有图片证据;口播文案 DNA 样本不足时保持未启用。 ### Step 3 - 聚合 DNA @@ -111,89 +101,56 @@ Agent 生成 scaffold 后必须回读笔记原文(必要时回看图组图片 xhs-style-profiler build --dna-id {dna-id} ``` -Agent 聚合时必须读取全部 report,结合权重、focus、高频共性、高权重偏好、孤例和例外,修订: - -```text -xhs/dna/{dna-id}/{dna-id}.dna.md -xhs/dna/{dna-id}/{dna-id}.template.md -``` - -Template 聚合要求: - -1. 固定输出七个部分:选题、标题(含封面图)、开头、承、结尾、CTA、图组。 -2. 选题和标题保持 profiler 规定的形态(选题角度推荐、受众关联角度;标题类型、参考标题、话题标签策略、封面图风格与 AIGC 提示词要素)。 -3. 开头、承、结尾、CTA、图组是固定语义部分(对应笔记的开头钩子、正文主体、收束、互动引导、图片轨道),不是固定字数占比;每个部分可对应笔记中的一段或多段文字、一张或多张图片。 -4. 每个部分必须写入 DNA 文档中对应维度的执行要求,包括本段任务、执行方式、必须做和避免项。 -5. 所有模板规则必须能从 DNA 文档推导;Agent 不得为了填满七部分而编造样本没有的规则。 +Agent 必须读取全部 report,按权重/focus 聚合: -多个样本时,定性结论必须说明覆盖多少篇;单篇特征只能写成单篇观察,不得伪装成稳定共性。 +- 高频共性、高权重偏好、局部借鉴、孤例、例外分开写。 +- 标注样本覆盖度;单篇/少量样本不得称为稳定账号 DNA。 +- 聚合「关键词 → 用户问题 → 内容形式」的搜索意图地图。 +- 高数据内容要回读创意、关键词与内容形式,不能只归因阅读量。 +- 为每个维度写聚合结论、报告依据和可执行规则。 +- 确保 DNA 文档能推导 template。 ## 更新已有 DNA -适用场景:目标 DNA 已存在,新增样本、偏好、局部借鉴或表现反馈。 - ### 新增样本 -1. 先为新笔记生成属于目标 DNA 的 report。 -2. 再执行: +生成新 report 后运行: ```bash xhs-style-profiler update \ - --input xhs/dna/{dna-id}/reports/{sample-id}.report.md \ + --input xhs/dna/{dna-id}/reports/{new-sample}.report.md \ --dna xhs/dna/{dna-id}/{dna-id}.dna.md \ --template xhs/dna/{dna-id}/{dna-id}.template.md ``` -3. Agent 根据新的加权统计和 16 维证据,同步修订 DNA 文档与 template。 +再由 Agent 重新审视聚合结论并同步修订 template。 ### 用户偏好 -用户意见不是直接入库的规则。Agent 必须先理解其指向,再转译到具体维度和执行要求,例如"开头再直接一点"应落到开头钩子、正文结构、标题风格等维度。 - -来自另一个 DNA 文档或 template 的局部结论,也按用户提供的参考要求处理:必须记录来源 `dna-id`、采纳范围、具体规则和冲突说明,再转译到当前 DNA 的对应维度与七部分 template。它不要求重新下载或重新提取原笔记。 - -可传入: - -```bash ---user-input "开头钩子再直接一点,第一行就给出收益" -``` - -转译结果写入 DNA 文档的"用户输入转译区",并以可执行规则进入 template 的对应部分。原话不能成为 template 里的抽象口号。 +用户偏好不直接入库。通过 `--user-input` 传入后,Agent 映射到具体维度,并把原话转译为账号级规则与 Brief/图文生产规则。 ### 局部借鉴 -用户只希望借鉴某篇笔记的钩子、封面或标签策略时: - -1. 为该笔记生成目标 DNA 的 report。 -2. 用 `--focus` 限定参与影响的维度。 -3. 更新 DNA 文档与 template,并在报告中保留来源和 focus 说明。 +对标 DNA 的局部规则必须先说明来源、适用条件和影响维度,再融合进目标 DNA;不得整包照搬。 ### 表现反馈 -来源:`content-calibrator` 的 DNA 表现评估报告(`xhs/dna/{dna-id}/evals/*.eval.md`)。评估回答"这个 DNA 好不好、哪些部分好/不好",本 workflow 负责把**用户确认采纳**的评估结论转译进 DNA。 - -1. 前提:评估报告已存在,且用户逐条确认了要采纳的建议(未确认的建议不动 DNA)。 -2. 每条采纳建议按参考输入处理:转译到具体维度和执行要求(如"收藏持续走低,清单结构不够可执行"→ 正文结构维度 + 承部分信息密度),经 `xhs-style-profiler update --user-input` 传入。 -3. 转译结果写入 DNA 文档的「表现反馈区」(与「用户输入转译区」并列),每条记录:来源 eval 文件、affected dimensions、DNA 文档修改、template 修改。 -4. 同步修订 template 对应部分;表现反馈只改规则表达,不引入样本未覆盖的新风格。 -5. 趋势类证据(比值走向)只支持方向性调整(加强/弱化既有规则),不支持凭空新增维度规则——新增规则仍需样本或用户输入支撑。 +复盘产生的建议先列证据与影响维度,经用户确认后写入 DNA;不得把一次数据波动直接升格为账号规则。 ## DNA 使用接口 -本 Workflow 不描述如何用 DNA 生产:生产 workflow 自行读取 `xhs/dna/{dna-id}/{dna-id}.dna.md` 与 `{dna-id}.template.md`,按 template 七部分执行,见 `content-production.md` / `editing.md`。 - -反馈回流判定:来自生产、改稿或复盘的成品修改意见,先判断是否可复用偏好——只有可复用偏好才经本 workflow「更新已有 DNA」进入 DNA;单次修改留在稿件审阅记录,不动 DNA。参考笔记的风格吸收("把这篇的风格融入到我们的 DNA")属于本 workflow 的新增样本 / 局部借鉴场景,不是生产流程的一部分。发布数据驱动的风格优化走「表现反馈」:`content-calibrator` 评估产出建议 → 用户逐条确认 → 本 workflow 转译进 DNA;评估本身不改 DNA。 +- **图文生产**:读取 DNA 文档与 template,确定定位、选题、标题包装、搜索意图、内容形式与发布节奏,由 main agent 直接写图文。 +- **视频全案**:main agent 产出 Brief;Brief 写明 Pipeline、素材授权、验收标准、交付边界。未指定 Pipeline 时 CP 自由发挥,指定时必须采用。 +- **口播类视频**:若口播文案 DNA 已启用,main agent 写口播文案并随 Brief 交付;CP 不重写策略文案。 +- **搜索优化**:选题和标题必须覆盖主关键词、长尾句与用户可能提问,不堆砌标签。 ## 对标接口 -对标账号或一组对标笔记不得默认落入现有 DNA;分析流程、逐项对比与采纳判断以 `account-benchmark.md` 为准。本 Workflow 只承担两件事: - -1. 对标 DNA 的三层产物生成与更新:使用独立 `dna-id`(如 `dna-benchmark-{slug}`,同组后续更新复用),样本获取、report、build/update 同本 workflow 的创建/更新流程。 -2. 用户采纳后,按「更新已有 DNA」执行并入基线 DNA 的更新:局部 DNA 融合、局部样本借鉴或偏好转译;默认优先局部 DNA 融合,不要求重新提取原笔记。 +对标样本进入独立 `dna-id`;比较时输出定位、选题、标题包装、内容形式、发布节奏、高数据创意、搜索意图与制作管线的差异。用户明确采纳后才融合进 `dna-0`。 ## 编排原则 -- Workflow 负责选择路径和衔接工具,不复制 profiler 的 16 维定义。 -- Agent 判断必须回读笔记原文和 report,不能只依赖统计表。 -- 样本统计描述内容模式,不替代事实核查、合规审核和商业判断。 -- 用户确认只用于初始化方向或采纳建议,不是登记资产的前置门槛。 +- 一个生产任务只使用一个 DNA;需要融合时先更新 DNA。 +- 样本、用户输入、数据反馈必须可追溯。 +- 账号级结论必须有覆盖度;不足就写未观测。 +- Template 只写 main agent 可执行的图文输入/Brief 规则,不写成片制作细节。 diff --git a/crews/main/skills/published-track/SKILL.md b/crews/main/skills/published-track/SKILL.md index 4472ef38..c8764002 100644 --- a/crews/main/skills/published-track/SKILL.md +++ b/crews/main/skills/published-track/SKILL.md @@ -51,7 +51,7 @@ published-track init-db 每张表共享通用字段:`id`(自增主键)、`title`、`content_type`(article/video/post)、`source_folder`(原始文件夹,如 `wx_mp/outputs/xxx`,**不做唯一约束,同内容可同平台多次发布**)、`publish_url`、`publish_date`(YYYY-MM-DD)、`distribute_status`(0=待分发,1=无需分发,2=已分发)、`notes`、`created_at`、`updated_at`。各平台特有互动指标默认 0,另有 `top_comment`(主要留言摘要)。 -> **视频号(`pub_wx_channel`)特例**:视频号作品没有「标题」概念,只有描述文案——`title` 列存的是**完整描述文案**(含 hashtag,最长约 300 字),即 `wechat-channels-publish` Step 6 填的描述。`wx-channel-engagement` 抓取按它匹配后台作品管理页。调用方调 `record.sh --platform wx_channel --title` 必须传完整描述,不要传短标题。 +> **视频号(`pub_wx_channel`)特例**:视频号作品没有「标题」概念,只有视频简介——`title` 列存的是**完整视频简介**(含 hashtag,最长约 300 字),即 `wechat-channels-publish` Step 5 填的视频简介。`wx-channel-engagement` 抓取按它匹配后台作品管理页。调用方调 `record.sh --platform wx_channel --title` 必须传完整视频简介,不要拆短标题。 ### DNA 关联字段(v3 schema) diff --git a/crews/main/skills/published-track/scripts/validate_content.py b/crews/main/skills/published-track/scripts/validate_content.py index 97edc5cd..532eca12 100644 --- a/crews/main/skills/published-track/scripts/validate_content.py +++ b/crews/main/skills/published-track/scripts/validate_content.py @@ -56,7 +56,7 @@ class MediaConstraint: "linkedin": TextConstraint(title_max=200, desc_max=3000), # Our own additions (not from AiToEarn) "wx_mp": TextConstraint(title_max=64, title_required=True, desc_max=20000, desc_required=True), - # 视频号作品没有「标题」概念,只有描述文案(含 hashtag)——desc 才是主文案,title 不校验 + # 视频号作品没有「标题」概念,只有视频简介(含 hashtag)——desc 才是主文案,title 不校验 "wx_channel": TextConstraint(desc_max=1000, desc_required=True), "toutiao": TextConstraint(title_max=30, title_required=True), "juejin": TextConstraint(title_max=128, title_required=True), diff --git a/docs/expert-pack-dna-architecture.md b/docs/expert-pack-dna-architecture.md index 5e9d4f93..6663e815 100644 --- a/docs/expert-pack-dna-architecture.md +++ b/docs/expert-pack-dna-architecture.md @@ -1,6 +1,6 @@ # 专家包(Expert Pack)+ DNA 架构规划 -> 日期:2026-08-14;2026-08-18 更新 DNA 生产模式与跨平台 profiler 规范;2026-08-20 明确跨平台 DNA 维度边界与 template 通用开头;2026-08-20 废除 rubric、数据直连 DNA(见第 11 节);2026-08-29 新增平台运营文件夹规范(见 4.2 节);2026-08-29 `dna/` 与 `calibration/` 由集中目录下沉进各平台运营文件夹(见 4.2 节) +> 日期:2026-08-14;2026-08-18 更新 DNA 生产模式与跨平台 profiler 规范;2026-08-20 明确跨平台 DNA 维度边界与 template 通用开头;2026-08-20 废除 rubric、数据直连 DNA(见第 11 节);2026-08-29 新增平台运营文件夹规范(见 4.2 节);2026-08-29 `dna/` 与 `calibration/` 由集中目录下沉进各平台运营文件夹(见 4.2 节);2026-09-08 抖音 / 视频号 / 小红书升级为账号级 DNA v1(见 4.7 节) > 首个改造对象:`crews/main`(小贝 / main agent) --- @@ -393,21 +393,25 @@ crews//skills/expert-/tools/-style-profiler/ 5. **用户输入**:进入转译区,由 Agent 映射到平台维度后同步 DNA 文档与 template。 6. **统计边界**:脚本只做证据底座,不评分、不替代定性判断。 7. **更新模式**:合并历史 report 与新 report,保留 Agent 已完成结论和自定义段落。 -8. **template 开头两项**:template 开头两项统一为选题、标题(含封面图);第三项起平台自定义。 +8. **template 开头两项**:微信及未升级平台固定为选题、标题(含封面图);第三项起平台自定义。抖音 / 视频号 / 小红书 v1 按下方账号级例外执行。 + +**账号级 DNA v1(抖音 / 视频号 / 小红书,2026-09-08)**:这三个平台的 DNA 是账号级运营框架,不再采用单篇成片生产模板。主 DNA 覆盖定位与核心传达、选题组合、包装文案、账号简介、内容形式与比例、发布习惯、高数据创意模式、视觉/声音倾向、互动与系列、制作管线;口播文案 DNA 是独立可选块。小红书额外必须有搜索关键词与用户问题地图。视频号没有标题字段,包装文案只使用视频简介;`published-track` 的 `--title` 只是数据库字段名,语义为完整视频简介。DNA template 是 main agent 的图文生产输入 / 视频制作 Brief 输入,不规定逐句文案、镜头表、转场或编码细节。用户提供的单篇 / 多篇样本仍按同维度聚合,但账号简介、比例、节奏等字段必须标注样本不足或未观测。 + +MainAgent 与 Content Producer 的分界点是 **Brief**:main 负责已有视频素材的简单加工、长文 / 图文内容和视频全案 Brief;全片制作由 Content Producer 执行。Brief 指定 Pipeline 时 CP 必须采用;未指定时 CP 自由发挥。当前已支持的 Pipeline 为 `video-producer:default` 与 `dna-ad-video-pipeline`。口播类视频若口播文案 DNA 已启用,口播终稿由 main 写好并随 Brief 交付。 各平台允许不同、且必须单独讨论的部分只有: - DNA 提取与分析维度(可以与微信 17 维截然不同;本规范不要求其他平台采纳 17 维); - 维度分组和命名; - 平台特有观测物,例如图文的标题与分段、视频的分镜与节奏、电商的卖点组织; -- template 第三项起的分段结构(开头两项固定为选题、标题(含封面图)); +- template 第三项起的分段结构(微信仍固定开头两项为选题、标题(含封面图);抖音 / 视频号 / 小红书 v1 使用账号级 Brief 语义段); - focus ID 集合。 其他平台改造固定顺序: 1. **先搭架构**:复制 wechat-style-profiler 的命令契约、存储结构、三层产物、权重/focus、用户输入转译和 update 机制,仅保留通用维度占位。 2. **再定义维度**:与用户讨论并确认该平台的 DNA 提取与分析维度;维度可以与微信截然不同,本规范不要求采纳 17 维,没有确认前不得直接照搬。 -3. **最后接入工作流**:将内容生产(含仿写)、改稿等下游环节改为读取该平台 DNA template 直接执行。 +3. **最后接入工作流**:将内容生产(含仿写)、改稿等下游环节改为读取该平台 DNA template;抖音 / 视频号 / 小红书 v1 中,视频全案只生成 Brief,图文 / 长文直接生产。 4. **配齐 workflow 基线集**:每个平台的专家包至少包含与 wx_mp 对应的 6 类 workflow——`style-dna`(DNA 创建与更新)、`content-production`(内容生产,含仿写 / 改写等输入分支)、`account-setup`(起号与定位)、`account-benchmark`(对标比较)、`editing`(改稿与调整)、`review`(数据复盘)。命名可按平台语境微调,职责不能缺;平台特有场景(如 wx_mp 的小绿书)优先作为 content-production 的产出分支,确有必要才独立成 workflow。 ### 4.8 Markdown 引用与运行时数据边界 @@ -487,7 +491,7 @@ crews//skills/expert-/tools/-style-profiler/ ### Phase 4:DNA 工具链 + 多平台扩展(后续) - 每个平台专家包先落地同构 `-style-profiler`:统一 report / build / update 命令、DNA ID 存储和用户输入转译机制。 -- 平台架构完成后,与用户确认该平台独有的 DNA 提取与分析维度;维度可以与微信截然不同、不必对齐 17 维,但 template 开头两项(选题、标题(含封面图))通用。 +- 平台架构完成后,与用户确认该平台独有的 DNA 提取与分析维度;维度可以与微信截然不同、不必对齐 17 维。微信保持 template 开头两项(选题、标题(含封面图))通用;抖音 / 视频号 / 小红书 v1 使用账号级 Brief 语义段。 - 维度确认后再接入内容生产、改稿等下游 workflow,并配齐 4.7 要求的 6 类 workflow 基线集。 - 逐步沉淀更多 DNA:每个 DNA 持续追加 report,并通过 update 重聚合同步 DNA 文档与 template。 From 1a2dbddcfe024299a2ad9eebb635e14742ffbae6 Mon Sep 17 00:00:00 2001 From: bigbrother666sh Date: Wed, 9 Sep 2026 17:56:30 +0800 Subject: [PATCH 07/34] correct: dna v1 update --- crews/content-producer/AGENTS.md | 11 +---------- .../skills/video-producer/SKILL.md | 10 +++------- crews/main/AGENTS.md | 2 +- docs/expert-pack-dna-architecture.md | 19 ++----------------- 4 files changed, 7 insertions(+), 35 deletions(-) diff --git a/crews/content-producer/AGENTS.md b/crews/content-producer/AGENTS.md index d856b684..ac18cc46 100644 --- a/crews/content-producer/AGENTS.md +++ b/crews/content-producer/AGENTS.md @@ -18,15 +18,6 @@ - **每接到一个活儿先建工作区**:视频类走 `output_videos//`(由 `video-producer` 内脚本建);平台专家包委托并传入现成项目目录(`/outputs//`)时直接沿用,不另建;平面设计类走 `design_assets/YYYY-MM-DD-<任务名>/`(由 `design-full init` 建) - **Brief 确认前不得干活**:用户直接发起时,先整理 brief 发用户确认;main agent 委托时,只接受带确认状态与闸门批准人的 brief。Brief 已确认且 GATE A 已由 main 代理批准时,不重开需求讨论;缺关键字段先向 Brief owner 澄清 - **成片/成稿交付前必跑自检**:视频走公共 `video-review`,平面设计走视觉 review(对照 brief + DESIGN.md) -- **封面**:默认交付含封面主文案的封面图(平台有标题时可用标题;视频号无标题时用核心传达),走公共 `siliconflow-img-gen`;Brief 明确要求无字高情绪帧时按 Brief 执行并记录例外 +- **封面**:默认交付含封面主文案的封面图(平台有标题时可用标题;视频号无标题时用核心传达),走公共 `siliconflow-img-gen`; - **不许声称没做过的事**:没有 tool result 或产物文件证明,不许声称已生成/已渲染/已改动 - **平台运营不在 CP**:发布到抖音/B站/小红书等归 main agent 的各 publish 技能,CP 不碰 - -## 衔接关系 - -- **viral-chaser → CP**:main agent 的 viral-chaser 只出追爆报告,制作委托 CP。接手时把报告当 brief 的一部分;Brief 已指定 Pipeline 或已锁定概念时直接按 Brief 执行,不重开概念阶段。否则走 `video-producer` 的 reference-driven 阶段——**CP 只吃报告出 2–3 个差异化概念 + 成本**,不做视频下载/转写/抽帧(那是 viral-chaser 的活,CP 不重复造);无报告则跳过该阶段直入出脚本 -- **main 的 video-edit → CP**:用户要从零做视频 → main 只出 Brief,CP 按 `video-producer` 或指定 Pipeline 制作;用户给已有素材要轻剪辑/拼接/烧字幕 → 仍归 main 的 `video-edit` -- **main / CP 分界点**:Brief。main 负责已有视频素材的简单加工、长文/图文内容和视频全案 Brief;CP 负责 Brief 之外的成片制作。Brief 指定 Pipeline 时必须采用;未指定时 CP 自由发挥。口播文案 DNA 已启用时,main 交付口播终稿,CP 不重写策略文案 -- **机器资源约束**:线程数、分辨率上限、低载编码等部署差异读本 workspace `MEMORY.md`;代码仓 Pipeline 不写死本机参数 -- **main 的 talking-head-cut**:口播类去口气词/高光剪辑归 main,CP 不做 -- **字幕原子例外**:CP 不承接 main 的语义级 `video-edit` 素材深剪;但 Pipeline 需要烧字幕且无 CP 等价子命令时,可调用已暴露的 `video-edit subtitles` 原子,并在交付说明记录 diff --git a/crews/content-producer/skills/video-producer/SKILL.md b/crews/content-producer/skills/video-producer/SKILL.md index ac5feac8..580c17f8 100644 --- a/crews/content-producer/skills/video-producer/SKILL.md +++ b/crews/content-producer/skills/video-producer/SKILL.md @@ -9,9 +9,6 @@ metadata: - python3 - ffmpeg - ffprobe - env: - - AWK_API_KEY - primaryEnv: AWK_API_KEY --- # 视频制作与片段修整(video-producer) @@ -20,7 +17,7 @@ metadata: 本技能有两种用法,agent 据用户请求判断走哪条: -**模式 A:端到端生产**——用户给主题/关键词/已有脚本/已有素材中的任一组合,或 main agent 交付已确认 Brief,要求从零做完整视频。默认走 Stage 0→14 全流程;Brief 指定 Pipeline 时先读取对应 Pipeline 文档并按其编排。另可接收 **main agent 喂入的 viral-chaser 追爆报告**(作为 brief 的一部分,本技能不做视频下载/转写/抽帧——那是 viral-chaser 的活)。 +**模式 A:端到端生产**——用户给主题/关键词/已有脚本/已有素材中的任一组合,或 main agent 交付已确认 Brief,要求从零做完整视频。默认走 Stage 0→14 全流程;Brief 指定 Pipeline 时先读取对应 Pipeline 文档并按其编排。 **模式 B:给定素材剪辑**——不涉及从零开剧本,编辑已有素材,直接用 Stage 12 工具箱(见下方"Stage 12 工具箱"段): @@ -56,14 +53,13 @@ Brief 的 `pipeline` 字段是制作契约: - Brief 未指定 Pipeline 时,CP 按默认全流程自由选择实现。 - Brief 指定 Pipeline 时,必须先读对应 Pipeline 文档并直接采用,不得替换为自创流程。 - Pipeline 只负责编排与内容套路;原子能力仍使用本技能子命令与公共技能,不新增脚本。 -- main / CP 的分界点是 Brief。main 交付的 Brief 已含确认与代理闸门批准时,CP 不重开需求讨论;缺关键字段时向 Brief owner 澄清。 -- 机器资源限制、线程数、分辨率上限等部署环境差异,从 CP workspace `MEMORY.md` 或 Brief 的环境约束读取,不写入 Pipeline。 +- main 交付的 Brief 已含确认与代理闸门批准时,CP 不重开需求讨论;缺关键字段时向 Brief owner 澄清。 --- ## 工作区目录约定 -调用方传入了现成项目目录时(如平台专家包委托制作,传入 `/outputs//`,brief 已在其中)直接沿用;否则在 `output_videos/` 下建项目文件夹 `/`: +调用方传入了现成项目目录时直接沿用;否则在 `output_videos/` 下建项目文件夹 `/`: ``` / # 即 /outputs// 或 output_videos// diff --git a/crews/main/AGENTS.md b/crews/main/AGENTS.md index 542fe62a..a73a9451 100644 --- a/crews/main/AGENTS.md +++ b/crews/main/AGENTS.md @@ -23,7 +23,7 @@ - 专家包按任务路由,互不越界:推特/小红书评论区获客 / 截流等 BD 场景走 `expert-bd`,不走平台运营包。 - 商业模式打磨(接触投资人前的前置环节)不属于任何专家包:结合 `business_knowledge.md` 直接与用户对话完成(多路径权衡用 `council`),结论落 `MEMORY.md`。 -- **零星工作兜底**:未被专家包覆盖的任务,可直接调用手头的工具完成(skill 清单会话时会自动加载,此处不列出);更适合其他 crew 承担的工作,以 spawn subagent 的方式委托(如视频全案由 main 出 Brief 后交 content-producer,技术问题、系统排障与环境配置交 IT engineer)。找不到匹配的专家包或工具时,先询问用户或保守处理,不猜测平台规则与 DNA。 +- **零星工作兜底**:未被专家包覆盖的任务,可直接调用手头的工具完成(skill 清单会话时会自动加载,此处不列出);更适合其他 crew 承担的工作,以 spawn subagent 的方式委托(如视频全案出具brief后交 content-producer,技术问题、系统排障与环境配置交 IT engineer)。找不到匹配的专家包或工具时,先询问用户或保守处理,不猜测平台规则与 DNA。 - crew 生命周期管理(启用/停用/调整其他 crew)是你的固有职责,不经专家包路由,见下文「crew 管理」段。 ## 数据存储 diff --git a/docs/expert-pack-dna-architecture.md b/docs/expert-pack-dna-architecture.md index 6663e815..67f5af97 100644 --- a/docs/expert-pack-dna-architecture.md +++ b/docs/expert-pack-dna-architecture.md @@ -395,24 +395,9 @@ crews//skills/expert-/tools/-style-profiler/ 7. **更新模式**:合并历史 report 与新 report,保留 Agent 已完成结论和自定义段落。 8. **template 开头两项**:微信及未升级平台固定为选题、标题(含封面图);第三项起平台自定义。抖音 / 视频号 / 小红书 v1 按下方账号级例外执行。 -**账号级 DNA v1(抖音 / 视频号 / 小红书,2026-09-08)**:这三个平台的 DNA 是账号级运营框架,不再采用单篇成片生产模板。主 DNA 覆盖定位与核心传达、选题组合、包装文案、账号简介、内容形式与比例、发布习惯、高数据创意模式、视觉/声音倾向、互动与系列、制作管线;口播文案 DNA 是独立可选块。小红书额外必须有搜索关键词与用户问题地图。视频号没有标题字段,包装文案只使用视频简介;`published-track` 的 `--title` 只是数据库字段名,语义为完整视频简介。DNA template 是 main agent 的图文生产输入 / 视频制作 Brief 输入,不规定逐句文案、镜头表、转场或编码细节。用户提供的单篇 / 多篇样本仍按同维度聚合,但账号简介、比例、节奏等字段必须标注样本不足或未观测。 +**(抖音 / 视频号 / 小红书,2026-09-08)**:这三个平台的 DNA 是账号级运营框架,不再采用单篇成片生产模板。主 DNA 覆盖定位与核心传达、选题组合、包装文案、账号简介、内容形式与比例、发布习惯、高数据创意模式、视觉/声音倾向、互动与系列、制作管线;口播文案 DNA 是独立可选块。小红书额外必须有搜索关键词与用户问题地图;DNA template 是 main agent 的图文生产输入 / 视频制作 Brief 输入,不规定逐句文案、镜头表、转场或编码细节。用户提供的单篇 / 多篇样本仍按同维度聚合,但账号简介、比例、节奏等字段必须标注样本不足或未观测。 -MainAgent 与 Content Producer 的分界点是 **Brief**:main 负责已有视频素材的简单加工、长文 / 图文内容和视频全案 Brief;全片制作由 Content Producer 执行。Brief 指定 Pipeline 时 CP 必须采用;未指定时 CP 自由发挥。当前已支持的 Pipeline 为 `video-producer:default` 与 `dna-ad-video-pipeline`。口播类视频若口播文案 DNA 已启用,口播终稿由 main 写好并随 Brief 交付。 - -各平台允许不同、且必须单独讨论的部分只有: - -- DNA 提取与分析维度(可以与微信 17 维截然不同;本规范不要求其他平台采纳 17 维); -- 维度分组和命名; -- 平台特有观测物,例如图文的标题与分段、视频的分镜与节奏、电商的卖点组织; -- template 第三项起的分段结构(微信仍固定开头两项为选题、标题(含封面图);抖音 / 视频号 / 小红书 v1 使用账号级 Brief 语义段); -- focus ID 集合。 - -其他平台改造固定顺序: - -1. **先搭架构**:复制 wechat-style-profiler 的命令契约、存储结构、三层产物、权重/focus、用户输入转译和 update 机制,仅保留通用维度占位。 -2. **再定义维度**:与用户讨论并确认该平台的 DNA 提取与分析维度;维度可以与微信截然不同,本规范不要求采纳 17 维,没有确认前不得直接照搬。 -3. **最后接入工作流**:将内容生产(含仿写)、改稿等下游环节改为读取该平台 DNA template;抖音 / 视频号 / 小红书 v1 中,视频全案只生成 Brief,图文 / 长文直接生产。 -4. **配齐 workflow 基线集**:每个平台的专家包至少包含与 wx_mp 对应的 6 类 workflow——`style-dna`(DNA 创建与更新)、`content-production`(内容生产,含仿写 / 改写等输入分支)、`account-setup`(起号与定位)、`account-benchmark`(对标比较)、`editing`(改稿与调整)、`review`(数据复盘)。命名可按平台语境微调,职责不能缺;平台特有场景(如 wx_mp 的小绿书)优先作为 content-production 的产出分支,确有必要才独立成 workflow。 +在全案视频制作(非已有素材简单剪辑),MainAgent 与 Content Producer 的分工分界点是 **Brief**:main 负责已有视频素材的简单加工、长文 / 图文内容和出具视频全案 Brief;全片制作由 Content Producer 执行。口播类视频若口播文案 DNA 已启用,口播终稿由 main 写好并随 Brief 交付。 ### 4.8 Markdown 引用与运行时数据边界 From 434524f2a45e668716c01bbe235ead915280c5f6 Mon Sep 17 00:00:00 2001 From: codes-factory-of-bg Date: Thu, 10 Sep 2026 02:35:07 +0800 Subject: [PATCH 08/34] =?UTF-8?q?feat(dna+expert-pack):=20DNA=20v2=20?= =?UTF-8?q?=E6=8C=89=E4=BD=9C=E5=93=81=E7=B1=BB=E5=9E=8B=E5=88=86=E6=A1=86?= =?UTF-8?q?=E6=9E=B6=EF=BC=9Bcontent-producer=20=E5=BC=95=E5=85=A5?= =?UTF-8?q?=E4=B8=93=E5=AE=B6=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 一、修正 douyin / xhs / wx_channel 的 DNA 设计(回退 09-08「账号级 DNA」的过度解读) - 明确不存在「平台级 / 账号级 DNA」:仍是单篇作品提取 report → 一批样本聚合 DNA 文档 → 推导 template;样本可来自多个账号,也可来自用户指定的一个账号批量提取。 - 按作品类型分框架:douyin / xhs 各两套(video-dna-framework.md + note-dna-framework.md), wx_channel 只有视频;一个 dna-id 只承载一种 kind,混型 build 直接报错。 - 维度重做:选题与观看理由 / 标题与封面(wx_channel = 短标题 + 视频描述)/ 内容创意 / (xhs)匹配的用户问题 / 视频内容形态与制作指向 / 制作规格与视听倾向 / 口播文案子模块(可选,参考公众号起承转合)/ 账号运营子模块(对标账号样本才有: 简介写法、内容形式比例、发布习惯;只进 DNA 文档不进 template)。 - 视频 template = Brief 正文模板 + 口播文案模板(可选);图文 template = 图文写作模板。 - 三个 build_style_profile.py 重写为同构实现(--kind、按 kind 的维度/统计/template 段、 混型守卫、focus 按 kind 校验),references 与工具 SKILL.md 同步。 - workflows(style-dna / content-production / account-benchmark / account-setup / editing / review)与三个专家包 SKILL.md 只做适应性微调,大流程架构不变。 - viral-chaser 升级:视频 meta(宽高与横竖屏、发布时间、作者简介、话题标签、分享/收藏)、 关键帧覆盖全片(补 25%/50%/63%/75%/90%,反转点常落在 55%-76%)、逐帧画面分析 + 按时间占比的结构拆解与反转点 + 内容形态判定与制作指向 + DNA 样本文字稿归档格式; 抖音图集走 note 分支(只下图片与文本,不转写)。 - 视频号发布页改版:短标题与视频描述都由 main 拟定、发布时都必须填;入库 (published-track --title)与 wx-channel-engagement 匹配只用视频描述,短标题不写库。 二、content-producer 引入专家包(expert-video / expert-design) - 四个技能整合为两个包:video-producer / collage-broll → expert-video/tools, design-full → expert-design/tools,manim-explainer 删除;PATH wrapper 名与子命令不变。 - pipelines/ → workflows/:dna-ad-video-pipeline.md 重构为 reversal-ad.md(「万万没想到」式 影视解说 + 反转植入:三段结构占比 + 四种反转手法 + 意象桥/钩连句);新增 narration-video.md(口播类声画实现);collage-broll 改为 workflow + tool(新增 check-setup / gate3 wrapper,不再让 agent 拼脚本路径)。 - expert-video/SKILL.md 删除「Pipeline 机制」段;阶段链更新:无 viral-chaser 报告输入 (reference-concepts 降为可选工具)、Stage 0 统一为 Brief 确认(直接对接用户模式下 引导讨论并代拟 Brief)、Stage 1 = intent-router、新增 Stage 13c 响度归一化必跑。 - AGENTS.md 重写:两种工作模式(subagent / 直接对接用户)都坚持乙方角色;交接契约、 各自建各自工作区、素材绝对路径与存在性确认。 - main 侧同步:Brief 不含 DNA、不替 CP 建工作区、口播文案与真人录音由 main 出具, 成片与封面按 CP 回报的绝对路径取回。 三、基础设施与文档 - sync_crew_skills / expose_skill_wrappers 增加悬挂软链清理(技能改名或收纳进 tools/ 后, workspace skills 与 ~/.openclaw/bin 的旧链会指向不存在的仓路径)。 - collect_skill_script_commands 增扫 skills//tools/*/scripts,避免包内脚本掉出 allowlist。 - setup-crew 同步 BUILTIN_SKILLS / DENIED_SKILLS(陈旧条目会让 openclaw.json 的 skills allowlist 指向已改名技能,crew 直接失去能力);保留实例自装条目。 - docs:expert-pack-dna-architecture.md 4.7 改写(v2 维度表 + main/CP 交接契约)、 新增 4.9(content-producer 专家包);d21 变更历史补记;历史调研文档加状态注记。 --- crews/content-producer/AGENTS.md | 53 +- crews/content-producer/BUILTIN_SKILLS | 6 +- crews/content-producer/IDENTITY.md | 5 +- .../openclaw_setting_sample.json | 6 +- crews/content-producer/scripts/README.md | 12 +- .../skills/collage-broll/SKILL.md | 389 ----------- .../skills/design-full/SKILL.md | 262 ------- .../skills/expert-design/SKILL.md | 160 +++++ .../expert-design/tools/design-full/SKILL.md | 62 ++ .../tools}/design-full/design-full.sh | 12 +- .../design-full/design-systems/airbnb.md | 0 .../design-full/design-systems/apple.md | 0 .../tools}/design-full/design-systems/bmw.md | 0 .../design-full/design-systems/figma.md | 0 .../design-full/design-systems/framer.md | 0 .../tools}/design-full/design-systems/ibm.md | 0 .../design-full/design-systems/index.json | 0 .../design-full/design-systems/linear.md | 0 .../design-full/design-systems/notion.md | 0 .../design-full/design-systems/shopify.md | 0 .../design-full/design-systems/spotify.md | 0 .../design-full/design-systems/starbucks.md | 0 .../design-full/design-systems/stripe.md | 0 .../design-full/design-systems/supabase.md | 0 .../design-full/design-systems/tesla.md | 0 .../design-full/design-systems/vercel.md | 0 .../tools}/design-full/scripts/init.sh | 0 .../tools}/design-full/scripts/pick.sh | 0 .../skills/expert-design/workflows/app-ui.md | 32 + .../expert-design/workflows/brand-visual.md | 31 + .../expert-design/workflows/web-page.md | 29 + .../skills/expert-video/SKILL.md | 296 ++++++++ .../expert-video/tools/collage-broll/SKILL.md | 32 + .../tools/collage-broll/collage-broll.sh | 46 ++ .../collage-broll/scripts/check_setup.sh | 0 .../tools}/collage-broll/scripts/run_gate3.py | 0 .../tools/video-producer/SKILL.md | 76 ++ .../scripts/add-silent-audio.py | 0 .../tools}/video-producer/scripts/assemble.py | 2 +- .../video-producer/scripts/asset-resolve.py | 2 +- .../video-producer/scripts/audio-mix.py | 0 .../scripts/character-register.py | 2 +- .../video-producer/scripts/clip-trim.py | 0 .../scripts/delivery-promise-lock.py | 2 +- .../video-producer/scripts/intent-router.py | 14 +- .../video-producer/scripts/make-cover.py | 4 +- .../video-producer/scripts/make-outro.py | 2 +- .../video-producer/scripts/mix-audio.py | 2 +- .../video-producer/scripts/motion-audit.py | 2 +- .../video-producer/scripts/narration-align.py | 2 +- .../scripts/reference-concepts.py | 86 +++ .../video-producer/scripts/render-shot.py | 2 +- .../video-producer/scripts/scene-compose.py | 2 +- .../scripts/script-self-eval.py | 2 +- .../video-producer/scripts/script-write.py | 2 +- .../video-producer/scripts/shot-decompose.py | 2 +- .../video-producer/scripts/slideshow-risk.py | 2 +- .../video-producer/scripts/slot-plan.py | 2 +- .../video-producer/scripts/story-develop.py | 2 +- .../scripts/storyboard-build.py | 2 +- .../scripts/timeline-compose.py | 2 +- .../tools}/video-producer/video-producer.sh | 22 +- .../expert-video/workflows/collage-broll.md | 260 +++++++ .../expert-video/workflows/narration-video.md | 59 ++ .../expert-video/workflows/reversal-ad.md | 116 ++++ .../skills/manim-explainer/SKILL.md | 115 --- .../assets/network_graph_scene.py | 52 -- .../skills/manim-explainer/manim-explainer.sh | 10 - .../manim-explainer/scripts/render-manim.sh | 52 -- .../skills/video-producer/SKILL.md | 339 --------- .../pipelines/dna-ad-video-pipeline.md | 139 ---- .../scripts/reference-concepts.py | 85 --- crews/main/AGENTS.md | 7 +- crews/main/skills/_shared/volc_asr.py | 2 +- crews/main/skills/expert-douyin/SKILL.md | 12 +- .../tools/douyin-style-profiler/SKILL.md | 200 +++--- .../references/account-dna-framework.md | 83 --- .../references/note-dna-framework.md | 102 +++ .../references/video-dna-framework.md | 117 ++++ .../scripts/build_style_profile.py | 634 ++++++++++------- .../workflows/account-benchmark.md | 15 +- .../expert-douyin/workflows/account-setup.md | 6 +- .../workflows/content-production.md | 54 +- .../skills/expert-douyin/workflows/editing.md | 10 +- .../skills/expert-douyin/workflows/review.md | 21 +- .../expert-douyin/workflows/style-dna.md | 74 +- crews/main/skills/expert-wx-channel/SKILL.md | 14 +- .../tools/wechat-channels-publish/SKILL.md | 38 +- .../tools/wx-channel-engagement/SKILL.md | 12 +- .../scripts/fetch_engagement.py | 6 +- .../tools/wx-channel-style-profiler/SKILL.md | 157 ++--- .../references/account-dna-framework.md | 84 --- .../references/video-dna-framework.md | 117 ++++ .../scripts/build_style_profile.py | 652 +++++++++++------- .../workflows/account-benchmark.md | 17 +- .../workflows/account-setup.md | 8 +- .../workflows/content-production.md | 98 +-- .../expert-wx-channel/workflows/editing.md | 12 +- .../expert-wx-channel/workflows/review.md | 22 +- .../expert-wx-channel/workflows/style-dna.md | 60 +- crews/main/skills/expert-xhs/SKILL.md | 12 +- .../tools/xhs-style-profiler/SKILL.md | 220 +++--- .../references/account-dna-framework.md | 86 --- .../references/note-dna-framework.md | 105 +++ .../references/video-dna-framework.md | 120 ++++ .../scripts/build_style_profile.py | 637 ++++++++++------- .../expert-xhs/workflows/account-benchmark.md | 13 +- .../expert-xhs/workflows/account-setup.md | 6 +- .../workflows/content-production.md | 60 +- .../skills/expert-xhs/workflows/editing.md | 14 +- .../skills/expert-xhs/workflows/review.md | 30 +- .../skills/expert-xhs/workflows/style-dna.md | 64 +- crews/main/skills/published-track/SKILL.md | 2 +- crews/main/skills/viral-chaser/SKILL.md | 172 ++++- .../viral-chaser/scripts/platforms/douyin.ts | 40 +- .../viral-chaser/scripts/platforms/xhs.ts | 3 + .../viral-chaser/scripts/viral_chaser.ts | 90 ++- docs/d21-symlink-skill.md | 4 +- docs/expert-pack-dna-architecture.md | 78 ++- .../video-capability-replanning-2026-07-25.md | 3 +- scripts/lib/agent-skills.sh | 20 + scripts/lib/crew-workspaces.sh | 82 +++ scripts/lib/skill-wrappers.sh | 14 + scripts/setup-crew.sh | 3 + 124 files changed, 4281 insertions(+), 3129 deletions(-) delete mode 100644 crews/content-producer/skills/collage-broll/SKILL.md delete mode 100644 crews/content-producer/skills/design-full/SKILL.md create mode 100644 crews/content-producer/skills/expert-design/SKILL.md create mode 100644 crews/content-producer/skills/expert-design/tools/design-full/SKILL.md rename crews/content-producer/skills/{ => expert-design/tools}/design-full/design-full.sh (73%) rename crews/content-producer/skills/{ => expert-design/tools}/design-full/design-systems/airbnb.md (100%) rename crews/content-producer/skills/{ => expert-design/tools}/design-full/design-systems/apple.md (100%) rename crews/content-producer/skills/{ => expert-design/tools}/design-full/design-systems/bmw.md (100%) rename crews/content-producer/skills/{ => expert-design/tools}/design-full/design-systems/figma.md (100%) rename crews/content-producer/skills/{ => expert-design/tools}/design-full/design-systems/framer.md (100%) rename crews/content-producer/skills/{ => expert-design/tools}/design-full/design-systems/ibm.md (100%) rename crews/content-producer/skills/{ => expert-design/tools}/design-full/design-systems/index.json (100%) rename crews/content-producer/skills/{ => expert-design/tools}/design-full/design-systems/linear.md (100%) rename crews/content-producer/skills/{ => expert-design/tools}/design-full/design-systems/notion.md (100%) rename crews/content-producer/skills/{ => expert-design/tools}/design-full/design-systems/shopify.md (100%) rename crews/content-producer/skills/{ => expert-design/tools}/design-full/design-systems/spotify.md (100%) rename crews/content-producer/skills/{ => expert-design/tools}/design-full/design-systems/starbucks.md (100%) rename crews/content-producer/skills/{ => expert-design/tools}/design-full/design-systems/stripe.md (100%) rename crews/content-producer/skills/{ => expert-design/tools}/design-full/design-systems/supabase.md (100%) rename crews/content-producer/skills/{ => expert-design/tools}/design-full/design-systems/tesla.md (100%) rename crews/content-producer/skills/{ => expert-design/tools}/design-full/design-systems/vercel.md (100%) rename crews/content-producer/skills/{ => expert-design/tools}/design-full/scripts/init.sh (100%) rename crews/content-producer/skills/{ => expert-design/tools}/design-full/scripts/pick.sh (100%) create mode 100644 crews/content-producer/skills/expert-design/workflows/app-ui.md create mode 100644 crews/content-producer/skills/expert-design/workflows/brand-visual.md create mode 100644 crews/content-producer/skills/expert-design/workflows/web-page.md create mode 100644 crews/content-producer/skills/expert-video/SKILL.md create mode 100644 crews/content-producer/skills/expert-video/tools/collage-broll/SKILL.md create mode 100755 crews/content-producer/skills/expert-video/tools/collage-broll/collage-broll.sh rename crews/content-producer/skills/{ => expert-video/tools}/collage-broll/scripts/check_setup.sh (100%) rename crews/content-producer/skills/{ => expert-video/tools}/collage-broll/scripts/run_gate3.py (100%) create mode 100644 crews/content-producer/skills/expert-video/tools/video-producer/SKILL.md rename crews/content-producer/skills/{ => expert-video/tools}/video-producer/scripts/add-silent-audio.py (100%) rename crews/content-producer/skills/{ => expert-video/tools}/video-producer/scripts/assemble.py (99%) rename crews/content-producer/skills/{ => expert-video/tools}/video-producer/scripts/asset-resolve.py (96%) rename crews/content-producer/skills/{ => expert-video/tools}/video-producer/scripts/audio-mix.py (100%) rename crews/content-producer/skills/{ => expert-video/tools}/video-producer/scripts/character-register.py (96%) rename crews/content-producer/skills/{ => expert-video/tools}/video-producer/scripts/clip-trim.py (100%) rename crews/content-producer/skills/{ => expert-video/tools}/video-producer/scripts/delivery-promise-lock.py (95%) rename crews/content-producer/skills/{ => expert-video/tools}/video-producer/scripts/intent-router.py (84%) rename crews/content-producer/skills/{ => expert-video/tools}/video-producer/scripts/make-cover.py (85%) rename crews/content-producer/skills/{ => expert-video/tools}/video-producer/scripts/make-outro.py (97%) rename crews/content-producer/skills/{ => expert-video/tools}/video-producer/scripts/mix-audio.py (97%) rename crews/content-producer/skills/{ => expert-video/tools}/video-producer/scripts/motion-audit.py (96%) rename crews/content-producer/skills/{ => expert-video/tools}/video-producer/scripts/narration-align.py (98%) create mode 100644 crews/content-producer/skills/expert-video/tools/video-producer/scripts/reference-concepts.py rename crews/content-producer/skills/{ => expert-video/tools}/video-producer/scripts/render-shot.py (96%) rename crews/content-producer/skills/{ => expert-video/tools}/video-producer/scripts/scene-compose.py (98%) rename crews/content-producer/skills/{ => expert-video/tools}/video-producer/scripts/script-self-eval.py (94%) rename crews/content-producer/skills/{ => expert-video/tools}/video-producer/scripts/script-write.py (95%) rename crews/content-producer/skills/{ => expert-video/tools}/video-producer/scripts/shot-decompose.py (95%) rename crews/content-producer/skills/{ => expert-video/tools}/video-producer/scripts/slideshow-risk.py (95%) rename crews/content-producer/skills/{ => expert-video/tools}/video-producer/scripts/slot-plan.py (96%) rename crews/content-producer/skills/{ => expert-video/tools}/video-producer/scripts/story-develop.py (94%) rename crews/content-producer/skills/{ => expert-video/tools}/video-producer/scripts/storyboard-build.py (94%) rename crews/content-producer/skills/{ => expert-video/tools}/video-producer/scripts/timeline-compose.py (98%) rename crews/content-producer/skills/{ => expert-video/tools}/video-producer/video-producer.sh (73%) create mode 100644 crews/content-producer/skills/expert-video/workflows/collage-broll.md create mode 100644 crews/content-producer/skills/expert-video/workflows/narration-video.md create mode 100644 crews/content-producer/skills/expert-video/workflows/reversal-ad.md delete mode 100644 crews/content-producer/skills/manim-explainer/SKILL.md delete mode 100644 crews/content-producer/skills/manim-explainer/assets/network_graph_scene.py delete mode 100755 crews/content-producer/skills/manim-explainer/manim-explainer.sh delete mode 100755 crews/content-producer/skills/manim-explainer/scripts/render-manim.sh delete mode 100644 crews/content-producer/skills/video-producer/SKILL.md delete mode 100644 crews/content-producer/skills/video-producer/pipelines/dna-ad-video-pipeline.md delete mode 100644 crews/content-producer/skills/video-producer/scripts/reference-concepts.py delete mode 100644 crews/main/skills/expert-douyin/tools/douyin-style-profiler/references/account-dna-framework.md create mode 100644 crews/main/skills/expert-douyin/tools/douyin-style-profiler/references/note-dna-framework.md create mode 100644 crews/main/skills/expert-douyin/tools/douyin-style-profiler/references/video-dna-framework.md delete mode 100644 crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/references/account-dna-framework.md create mode 100644 crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/references/video-dna-framework.md delete mode 100644 crews/main/skills/expert-xhs/tools/xhs-style-profiler/references/account-dna-framework.md create mode 100644 crews/main/skills/expert-xhs/tools/xhs-style-profiler/references/note-dna-framework.md create mode 100644 crews/main/skills/expert-xhs/tools/xhs-style-profiler/references/video-dna-framework.md diff --git a/crews/content-producer/AGENTS.md b/crews/content-producer/AGENTS.md index ac18cc46..cf7dcef0 100644 --- a/crews/content-producer/AGENTS.md +++ b/crews/content-producer/AGENTS.md @@ -1,23 +1,46 @@ # content-producer — Workflow -我是专业内容制作者,接到活儿先按下表选一条路,**首个匹配行即执行**,不向下评估。 +我是专业内容制作者,**始终是乙方**:甲方给需求(Brief)与已有素材,我出成品。接到活儿先按下表选专家包,包内再按 workflow 执行,**首个匹配行即执行**,不向下评估。 + +## 两种工作模式 + +| 模式 | 甲方 | 我的起点 | +|------|------|---------| +| A · Subagent 承制 | main agent(小贝) | 读甲方 Brief + 素材绝对路径 → 核对字段 → 缺关键字段向 Brief owner 澄清;**不重开需求讨论** | +| B · 直接对接用户 | 用户(已绑工作 channel) | 用户给了 Brief → 核对确认;没给 → 先引导讨论,**代用户整理 brief 并发用户确认后才开工** | + +模式 B 的用户不一定专业,我要替他把需求收敛清楚: + +1. **先明确 brief**:做什么、给谁看、要传达什么、成品形态与规格、什么时候要;整理成 `brief.md` 发用户确认。用户的模糊想法("做个短片""帮我策划")**不算确认**。 +2. **涉及已有素材必须落实位置**:让用户给出素材**绝对路径**(或明确授权从哪个目录取),逐条确认文件真实存在、可打开;缺什么明说,不许拿"待补"开工。 +3. **口播内容**:口播文案由甲方出。用户只给大意时可代拟,但必须发用户确认定稿;明确是用户真人口播时,必须拿到用户的录音文件(绝对路径)。 ## 能力方向路由 -| 入口信号 | 走哪条路 | 入口技能 | -|---------|---------|---------| -| Brief 中指定 `pipeline`(如 `dna-ad-video-pipeline`) | 指定 Pipeline 制作 | `video-producer` + 对应 `pipelines/` 文档 | -| 用户要"从零做视频""出一支完整视频""按这个脚本/主题拍片子" | 端到端视频制作 | `video-producer` Stage 0→14 全流程 | -| 用户要对已有素材进行剪辑、修整、拼接等 | 已有素材剪辑 | `video-producer` Stage 12 工具箱 | -| 用户要"把这句话/这句口播做成拼贴 B-roll""纸拼贴动画""半调拼贴" | 纸拼贴组装动画 | `collage-broll` | -| 用户要"用 Manim 做技术演示""流程图/架构图动起来""指标可视化动画" | 技术演示视频 | `manim-explainer` | -| 用户要"做网页/落地页/APP 界面/品牌视觉体系"等平面设计 | 平面设计全案 | `design-full` | +| 入口信号 | 专家包 | Workflow | +|---------|--------|----------| +| Brief 指定 `workflow`(如 `reversal-ad`) | `expert-video` | 按 Brief 指定的 workflow 制作 | +| "从零做视频""出一支完整视频""按这个主题拍片子" | `expert-video` | 通用阶段链 Stage 0→14 | +| 影视解说 / 剧情解说 + 突然反转插入品宣("万万没想到"式) | `expert-video` | Reversal Ad | +| 甲方交付口播文案或真人口播录音,要合成声画 | `expert-video` | Narration Video | +| "把这句口播做成拼贴 B-roll""纸拼贴动画""半调拼贴" | `expert-video` | Collage B-roll | +| 已有素材要剪辑、修整、拼接、配音、烧字幕 | `expert-video` | 通用链的 Stage 12 工具箱 | +| "做网页/落地页/APP 界面/品牌视觉体系" | `expert-design` | Web Page / App UI / Brand Visual | + +## 交接契约(硬边界) + +- **甲方给我**:Brief(绝对路径)+ 已有素材(绝对路径 + 来源授权)+ 口播文案 / 口播录音(如有)。Brief 里不含甲方的 DNA 等内部资产,我也不去翻甲方工作区找规则。 +- **我给甲方**:成片 + 封面 + 交付说明(`final-deliver.md`),回报三者的**绝对路径**。 +- **甲方不替我建工作区,我也不替甲方建**:同一个活儿各自建各自的工作区。双方都是 T3 权限,需要时可直接读对方工作区取文件。 +- **甲乙方关系不破**:需求方向、品牌事实、卖点承诺、发布文案归甲方;工作区、制作方案、分镜、素材实现、渲染参数归我。缺信息就问甲方,不自行脑补,也不反过来指挥甲方。 ## 通用约定 -- **每接到一个活儿先建工作区**:视频类走 `output_videos//`(由 `video-producer` 内脚本建);平台专家包委托并传入现成项目目录(`/outputs//`)时直接沿用,不另建;平面设计类走 `design_assets/YYYY-MM-DD-<任务名>/`(由 `design-full init` 建) -- **Brief 确认前不得干活**:用户直接发起时,先整理 brief 发用户确认;main agent 委托时,只接受带确认状态与闸门批准人的 brief。Brief 已确认且 GATE A 已由 main 代理批准时,不重开需求讨论;缺关键字段先向 Brief owner 澄清 -- **成片/成稿交付前必跑自检**:视频走公共 `video-review`,平面设计走视觉 review(对照 brief + DESIGN.md) -- **封面**:默认交付含封面主文案的封面图(平台有标题时可用标题;视频号无标题时用核心传达),走公共 `siliconflow-img-gen`; -- **不许声称没做过的事**:没有 tool result 或产物文件证明,不许声称已生成/已渲染/已改动 -- **平台运营不在 CP**:发布到抖音/B站/小红书等归 main agent 的各 publish 技能,CP 不碰 +- **每接到一个活儿先自建工作区**:视频类走 `output_videos//`,平面设计类走 `design_assets/YYYY-MM-DD-<任务名>/`(由 `design-full init` 建)。甲方传入的现成目录只作素材来源,不当自己的工作区。 +- **Brief 确认前不得干活**:模式 B 先整理 brief 发用户确认;模式 A 只接受带确认状态与闸门批准人的 Brief,Brief 已确认且 GATE A 已由 main 代理批准时不重开需求讨论。 +- **成品交付前必跑自检**:视频走公共 `video-review` + 响度归一化(-14 LUFS 必跑),平面设计走视觉 review(对照 brief + DESIGN.md)。 +- **封面**:视频成片默认交付含封面主文案的封面图,主文案来自 Brief(有平台标题用标题,视频号用短标题),走公共 `siliconflow-img-gen`。 +- **不许声称没做过的事**:没有 tool result 或产物文件证明,不许声称已生成/已渲染/已改动。 +- **平台运营不在 CP**:发布到抖音/视频号/小红书/B站等归 main agent 的各平台专家包,CP 不碰;也不私信用户、不代拟运营话术。 +- **语义级剪辑不在 CP**:已有素材的高光剪辑、去口气词归 main 的 `talking-head-cut` / `video-edit`;CP 只做几何级修整(切段/拼接/混音/烧字幕/补轨)。 +- **机器资源约束**:线程数、分辨率上限、低载编码等部署差异读本 workspace `MEMORY.md` 或 Brief 的环境约束,不写死在技能包里。 diff --git a/crews/content-producer/BUILTIN_SKILLS b/crews/content-producer/BUILTIN_SKILLS index 57338e5f..7371e377 100644 --- a/crews/content-producer/BUILTIN_SKILLS +++ b/crews/content-producer/BUILTIN_SKILLS @@ -1,4 +1,2 @@ -video-producer -collage-broll -manim-explainer -design-full +expert-video +expert-design diff --git a/crews/content-producer/IDENTITY.md b/crews/content-producer/IDENTITY.md index 5340090f..4e8f5c4d 100644 --- a/crews/content-producer/IDENTITY.md +++ b/crews/content-producer/IDENTITY.md @@ -13,6 +13,5 @@ content-producer(内容制作者) 🎬 ## Role -专业内容制作者,main agent 的助手。承担四条能力方向的执行: -端到端视频制作(出脚本/分镜/渲染/组装/交付)+ 视觉拼贴动画 + 技术演示动画 + 平面设计全案。 -既接受 main agent 在工作流中下发任务,也接受用户直接对话。 +专业内容制作者,乙方。两个专家包:`expert-video`(视频制作)+ `expert-design`(平面设计)。 +既接受 main agent 派单,也接受用户直接对话。 diff --git a/crews/content-producer/openclaw_setting_sample.json b/crews/content-producer/openclaw_setting_sample.json index 7a932a29..2c6e1033 100644 --- a/crews/content-producer/openclaw_setting_sample.json +++ b/crews/content-producer/openclaw_setting_sample.json @@ -20,10 +20,8 @@ "smart-search", "video-review", "wxwork-drive", - "video-producer", - "collage-broll", - "manim-explainer", - "design-full" + "expert-video", + "expert-design" ], "subagents": { "allowAgents": [ diff --git a/crews/content-producer/scripts/README.md b/crews/content-producer/scripts/README.md index 615be930..2e48a353 100644 --- a/crews/content-producer/scripts/README.md +++ b/crews/content-producer/scripts/README.md @@ -2,17 +2,17 @@ 五个后期脚本补我们没做的后期环节。**两个必跑、三个可选**——必跑的是发布质量硬伤,可选的是用户要才跑。 -完整接入契约(落点 / 旁路条件 / 干湿分离)在 `../AGENTS.md` 的 `## 脚本清单` 段;本文件只做脚本速查索引,**不重复契约**。 +完整接入契约(落点 / 旁路条件 / 干湿分离)在 `../skills/expert-video/tools/video-producer/SKILL.md` 的「后期脚本」段;本文件只做脚本速查索引,**不重复契约**。 ## 索引 | 脚本 | 用途 | 必跑/可选 | 落点 | |------|------|---------|------| -| `normalize.py` | ffmpeg loudnorm 双 pass 把成片归一化到 -14 LUFS(抖音/视频号/B 竍竖屏发布通用标准) | **必跑** | AGENTS.md Step 5.5,exportMp4 出片后、汇报前强制跑 | -| `burn-srt.py` | ffmpeg `subtitles` 滤镜(libass)把 SRT 硬烧进画面,不可关 | 可选 | Step 5.6,仅用户明确要字幕时跑 | -| `duck.py` | ffmpeg `sidechaincompress` 旁白作 sidechain 触发 BGM 自动压低(threshold=-25dB / ratio=8:1) | 可选 | Step 5.7,仅用户要专业混音且可分轨时跑 | -| `denoise.py` | ffmpeg `afftdn`(默认)或 `arnndn`(RNN,要模型文件)给音频去环境噪声 | 可选 | Step 3.5,仅用户素材音质差时跑(AI 生成视频音轨本来就干净,跳过) | -| `interp.py` | ffmpeg `minterpolate` 补帧到 30/60fps | 可选 | Step 5.8,仅低 fps 源材(如 24fps AI 生成片)补到 30fps 顺滑 | +| `normalize.py` | ffmpeg loudnorm 双 pass 把成片归一化到 -14 LUFS(抖音/视频号/B站竖屏发布通用标准) | **必跑** | `expert-video` 阶段链 Stage 13c,成片合成后、交付前强制跑 | +| `burn-srt.py` | ffmpeg `subtitles` 滤镜(libass)把 SRT 硬烧进画面,不可关 | 可选 | Brief / 甲方要字幕时跑 | +| `duck.py` | ffmpeg `sidechaincompress` 旁白作 sidechain 触发 BGM 自动压低(threshold=-25dB / ratio=8:1) | 可选 | 甲方要专业混音且可分轨时跑 | +| `denoise.py` | ffmpeg `afftdn`(默认)或 `arnndn`(RNN,要模型文件)给音频去环境噪声 | 可选 | 素材入库后,仅甲方素材音质差时跑(AI 生成视频音轨本来就干净,跳过) | +| `interp.py` | ffmpeg `minterpolate` 补帧到 30/60fps | 可选 | 渲染或拼接前,仅低 fps 源材(如 24fps AI 生成片)补到 30fps 顺滑 | ## 调用模板 diff --git a/crews/content-producer/skills/collage-broll/SKILL.md b/crews/content-producer/skills/collage-broll/SKILL.md deleted file mode 100644 index a468f052..00000000 --- a/crews/content-producer/skills/collage-broll/SKILL.md +++ /dev/null @@ -1,389 +0,0 @@ ---- -name: collage-broll -description: 将约 5 秒口播文稿、观点句或抽象概念做成高级 editorial halftone paper-collage / 半调纸拼贴 B-roll。用户说"collage b-roll""纸拼贴 b-roll""半调拼贴""拼贴风格配画面""用这段文稿做拼贴动画",或希望把一句文稿转成拼贴视觉隐喻时,必须使用此 skill。强制采用三阶段审批:先只提视觉隐喻,用户确认后用 siliconflow-img-gen 生成彩色拼贴静帧,静帧再次确认后用公共 aigc-video-gen 技能(i2v 首尾帧插值)组装动画。视频生成走百炼 happyhorse-1.1-i2v 候选链(沿链 fallback)或火山 Seedance(视 env 配置)。 -metadata: - openclaw: - emoji: 🗞️ - requires: - bins: - - python3 - - ffmpeg - - ffprobe - env: - - AWK_API_KEY - primaryEnv: AWK_API_KEY - homepage: https://www.volcengine.com/docs/82379/1541523 ---- - -# Collage B-roll(纸拼贴组装动画) - -把一句约 5 秒的口播压成一个 sharp visual idea,再做成高级编辑风纸拼贴组装动画。 - -默认链路: - -1. 只设计视觉隐喻,等待用户确认(Gate 1) -2. 只生成最终静帧,等待用户确认(Gate 2) -3. 自动调 `aigc-video-gen` 生成视频并完成 QA(Gate 3) - -这两个确认闸门是工作流的一部分。它们让用户把注意力放在审美和方向上,同时避免错误隐喻或错误静帧直接消耗视频生成成本。 - -## 强制审批协议 - -### Gate 1:隐喻确认 - -收到文稿后,先提视觉隐喻,不生成图片、不生成视频、不调用任何视频模型。 - -向用户交付每条的: - -- 核心意思 -- 情绪 -- 一句话视觉命题 -- 3–6 个关键物件 -- 建议底色与局部点色 -- 预期组装顺序 - -然后明确停下,等待用户回复"可以""通过""全部通过"或给出逐条修改意见。 - -如果用户只确认部分编号,只让通过的条目进入 Gate 2;未通过条目继续修改隐喻。 - -### Gate 2:静帧确认 - -隐喻确认后,才写 visual spec 和 imagegen prompt,并用 `siliconflow-img-gen` 技能生成最终静帧。 - -把原图保存到项目目录,生成带编号的静帧 contact sheet,向用户展示并再次停下。此阶段仍然不调 `aigc-video-gen`,也不生成视频。 - -如果用户只确认部分静帧,只让通过的条目进入 Gate 3;需要修改的静帧先重生并重新确认。 - -### Gate 3:视频生成 - -静帧确认后,不再询问使用哪个视频模型,直接调公共 `aigc-video-gen` 技能走 i2v 首尾帧插值——默认走百炼 `happyhorse-1.1-i2v`(沿链 fallback 到 1.0 → wan2.7),百炼没配走火山 Seedance Fast → Normal → Mini。只有用户明确指定其他模型时才 `aigc-video-gen --model ` 覆盖。 - -## 成功标准 - -- 一句话只表达一个清晰隐喻 -- 同一批画面有统一设计语言,但不强制全部蓝底 -- 背景是强烈、平坦、均匀的色场,可按语意变化 -- 主体以黑白 halftone photographic cut-outs 为骨架 -- 关键卡片、按钮、胶片、规则册等允许使用红、黄、青、橙、紫、奶油白等彩色纸张 -- 所有纸片有清晰裁切边、奶油白 keyline、低透明度柔和阴影和纸张颗粒 -- 动作是 assemble-from-empty,而不是轻微漂移、晃动或慢 zoom -- 无字幕、无口播全文、无 logo、无水印、无 UI -- 默认交付 9:16、5 秒、720×1280、有声画同出(`aigc-video-gen` 默认 `audio: true`,旁白/BGM/环境音写在 prompt 里)MP4 - -## 什么时候不要用 - -- 需要精确控制图层、遮挡、镜头穿越或可编辑时间线:改用分层动画工具 -- 只需要视频提示词,不需要生成成片:直接写 prompt 即可,不用走本流程 -- 需要真实人物产品广告或口播演员:不要走本拼贴流程 -- 用户明确要可逐层修改的透明素材:本 skill 默认不拆透明图层 - -## 默认项目目录 - -路径契约——调用方指定了项目目录时(平台运营内容为 `/outputs//`)落在其下;否则落在 `output_videos/` 下,名 ``: - -```text -/ # 即 /outputs// 或 output_videos// -├── brief.md # 文稿 + Gate 1 隐喻清单 -├── visual-spec.json # Gate 2 视觉规格 -├── imagegen-prompts.md # Gate 2 Seedream prompt 留档 -├── gen-jobs.json # Gate 3 aigc-video-gen 批量调用清单 -├── gate2-qa.md # 静帧 QA 结论 -├── gate3-qa.md # 视频 QA 结论 -├── still-contact-sheet.jpg # Gate 2 静帧总图 -├── video-contact-sheet-all.jpg # Gate 3 全部成片逐秒抽帧 -├── end-frame-comparison-all.jpg # 确认静帧 vs 视频末帧并排 -├── 01-/ -│ ├── gen-prompt.txt # aigc-video-gen --prompt 内容(声画同出描述) -│ ├── frames/ -│ │ ├── still.png # Gate 2 确认的完成帧(原图) -│ │ ├── last-frame.png # 统一裁到 720x1280 的尾帧 -│ │ └first-frame.png # 纯色空首帧(同底色 hex) -│ └gen-runs/run-v01/ -│ ├── final-5s.mp4 # aigc-video-gen 产物 -│ ├── final-5s-noaudio.mp4 # 强制无声交付(拼贴动画无声) -│ ├── contact-sheet.jpg # 逐秒抽帧总图 -│ └ video-last-frame.jpg -│ └ end-frame-comparison.jpg -└── 02-/... -``` - -## Phase 1:设计视觉隐喻 - -先把文稿压成一个视觉命题。 - -提取: - -- 核心意思:观众最终要看懂什么 -- 情绪:冷静、惊讶、紧迫、豁然开朗、荒诞、反讽 -- 动作动词:打开、连接、漏掉、装订、归档、点亮、压缩、分叉、组装 -- 可视化隐喻:机器、时钟、胶片、档案柜、控制台、规则册、漏斗、轨道、棋子 - -不要把文稿逐字放进画面。默认一条文稿只做一个隐喻,控制在 3–6 个关键物件;元素过多会让语意变弱,也会让 i2v 组装不稳定。 - -批量隐喻优先形成前后叙事:例如先表现手工消耗与经验流失,再表现规范沉淀与人机分工。 - -### Gate 1 输出示例 - -```text -1. 核心意思:经验每次都在重复消耗 - 视觉隐喻:熟练剪辑师围着巨大的胶片时钟逐帧裁切,时钟走完一圈却只得到一小段成片 - 关键物件:胶片时钟、剪辑师、剪刀、短胶片 - 色彩:焦橙底,奶油白与浅青点色 - 组装顺序:时钟 → 人物与剪刀 → 胶片 → 最终短输出 -``` - -输出后停下等待确认。 - -## Phase 2:生成彩色拼贴静帧 - -隐喻确认后,先写自包含的 `visual-spec.json`,再写 imagegen prompt。 - -### Visual spec - -```json -{ - "script_meaning": "", - "visual_metaphor": "", - "style_signature": "flat bold color field, mixed black-and-white halftone cut-outs and colored cardstock accents, crisp cut edges, cream keylines, soft paper shadows, editorial paper collage", - "aspect_ratio": "9:16", - "color_field": { - "background_hex": "", - "accent_colors": [], - "paper_grain": "fine uncoated-paper fiber" - }, - "elements": [ - { - "what": "", - "role": "", - "motion": "", - "placement": "" - } - ], - "composition": { - "layout": "", - "negative_space": "", - "final_frame": "" - }, - "motion_plan": "structure first, subject or cards second, action and result last", - "avoid": "typography, readable letters, numerals, logos, watermark, UI, subtitles, glossy 3D, photoreal environment" -} -``` - -### 色彩规则 - -不要把 cobalt blue 当成唯一默认值。根据语意挑选强色场,并在一批作品中保持"同设计语言、不同底色": - -- 焦橙 / 红:时间消耗、劳动、紧迫 -- 芥末黄:工具、警示、经验漏失 -- 墨绿:认知、审美、系统重置 -- 深紫:规范、沉淀、长期记忆 -- 青绿:判断、协作、自动执行 - -主体可以黑白半调为主,但局部彩色纸张必须服务信息层级,不要为了彩色而彩色。 - -### Imagegen prompt 模板(siliconflow-img-gen / Seedream) - -用 `siliconflow-img-gen` 技能(Seedream doubao-seedream-4.5,fallback doubao-seedream-5.0-lite): - -```bash -siliconflow-img-gen --prompt "<下面整段>" --image-size 1600x2848 --out-dir /01-/frames/ -``` - -Prompt 模板(英文,Seedream 对英文 prompt 响应更好): - -```text -Use case: ads-marketing -Asset type: final still frame for a 9:16 image-to-video B-roll clip -Primary request: Create a finished editorial paper-collage image expressing [一句话视觉命题]. -Scene/backdrop: perfectly flat [颜色] paper field [hex] with subtle uncoated paper fiber. -Style/medium: premium editorial stop-motion paper collage; black-and-white halftone photographic cut-outs mixed with selective [点色] colored cardstock. -Composition/framing: vertical 9:16 locked poster frame; central subject within the middle 70 percent; generous clean color-field negative space; 3–6 large separable paper groups for later assemble-from-empty animation. -Materials/textures: visible printed halftone dots, crisp machine-cut edges, thin warm-cream paper keylines, soft low-opacity physical drop shadows. -Constraints: [本条隐喻必须一眼看懂的关系]. -Avoid: no typography, no readable letters, no numerals, no logos, no watermark, no UI, no subtitles, no glossy 3D, no photoreal environment, no clutter. -``` - -Seedream 不支持参考图锁定风格,所以"同设计语言"靠**同一批用同一 `style_signature` 字串 + 同一 `color_field` 范围**在 prompt 里复用,不靠参考图。 - -### 静帧 QA - -- 隐喻是否一眼看懂 -- 主体是否集中 -- 是否有假字、logo、水印或 UI -- 是否保留足够纯色场,方便从空场组装 -- 是否是 3–6 个清晰大组,而不是满屏碎片 -- 同一批是否统一质感但有色彩变化 - -将通过 QA 的原图复制到 `/frames/still.png`,生成带编号的静帧 contact sheet,展示给用户并停下等待 Gate 2 确认。静帧 QA 结论写入 `/gate2-qa.md`。 - -如果用户要求重生部分静帧,重生后生成 `still-contact-sheet-v2.jpg`(后续轮次递增 v3、v4…),保留旧版 contact sheet 不覆盖,方便对比。 - -拼静帧 contact sheet 用 ffmpeg tile: - -```bash -ffmpeg -y -pattern_type glob -i "/*/frames/still.png" \ - -vf "scale=270:480,tile=5x1" \ - -frames:v 1 /still-contact-sheet.jpg -``` - -段数 > 5 时分多行(`tile=5x2`、`5x3`…)。 - -## Phase 3:用 aigc-video-gen i2v 生成视频 - -### 1. 准备首尾帧 - -保留 imagegen 原图 `still.png`,再统一尾帧到 720x1280(`aigc-video-gen` i2v 收 720P/1080P,默认 720P): - -```bash -ffmpeg -y -i /frames/still.png \ - -vf "scale=720:1280:force_original_aspect_ratio=increase,crop=720:1280" \ - /frames/last-frame.png -``` - -首帧默认是与尾帧相同底色的纯色空纸面(assemble-from-empty 的核心——从空场开始组装): - -```bash -ffmpeg -y -f lavfi -i color=c=0x:s=720x1280 \ - -frames:v 1 /frames/first-frame.png -``` - -如果用户明确要求不从完全空白开始,首帧才保留一个基础物件。 - -### 2. 写 aigc-video-gen 动画 prompt - -动作顺序默认采用: - -```text -基础结构 → 人物或关键卡片 → 连接件 → 动作 → 最终结果 -``` - -`aigc-video-gen` 的 `--prompt` 是**声画同出**描述(中文,happyhorse / Seedance 对中文响应好)。组装顺序与约束中文化写进 prompt: - -- 组装顺序段:"画面从纯色空场开始,依次滑入 [基础结构] → [人物/卡片] → [连接件] → [动作],最终定格在已确认的完成构图" -- 机位与镜头约束:"固定机位,无切镜、无 zoom、无变形" -- 画面禁字:"画面无文字、无 logo、无水印、无 UI" -- 声画同出补充(`aigc-video-gen` 默认有声):"音频:纸片滑入的嗒嗰声 + 卡位时的咔嗒声 + 最终定格的短促 BGM 收尾" - -prompt 模板: - -```text -画面从纯色空场开始,依次滑入 [基础结构] → [人物/卡片] → [连接件] → [动作],最终定格在已确认的完成构图。固定机位,无切镜、无 zoom、无变形。画面无文字、无 logo、无水印、无 UI。音频:纸片滑入的嗒嗰声 + 卡位时的咔嗒声 + 最终定格的短促 BGM 收尾。 -``` - -每条 prompt 都要明确 `--image first-frame.png` 是空首帧、`--last-frame last-frame.png` 是确认过的完成帧。最终构图必须贴近 last-frame,不让模型自由改造尾帧。 - -### 3. 批量调用 aigc-video-gen - -创建 `gen-jobs.json`。每个 job 用首尾帧插值(i2v 模式): - -```json -{ - "prompt": "", - "first_frame": "/frames/first-frame.png", - "last_frame": "/frames/last-frame.png", - "output": "/gen-runs/run-v01/final-5s.mp4", - "ratio": "9:16", - "resolution": "720P", - "duration": 5 -} -``` - -逐条调公共 `aigc-video-gen` 走 i2v 模式(首尾帧插值): - -```bash -aigc-video-gen --mode i2v \ - --image /frames/first-frame.png \ - --last-frame /frames/last-frame.png \ - --prompt "" \ - --output /gen-runs/run-v01/final-5s.mp4 \ - --ratio 9:16 --resolution 720P --duration 5 -``` - -`aigc-video-gen` 内部已带候选链 fallback(百炼 happyhorse-1.1-i2v 沿链 1.1 → 1.0 → wan2.7,百炼没配走火山 Seedance Fast → Normal → Mini)+ decisions.log 落盘,agent 只需逐条调度。 - -如果出现 i2v 不收首尾帧的报错(`aigc-video-gen` 退出码非 0),检查 first-frame.png / last-frame.png 是否真存在、是否 720x1280——`aigc-video-gen` 要求相对路径在 `output_videos/` 或 `/outputs/` 下,**调用时 workdir 必须是 workspace 根**。 - -### 4. 强制无声交付 - -拼贴动画默认无声交付,但 `aigc-video-gen` 声画同出模式会出声。Gate 3 出片后用 ffmpeg 抽无声版交付: - -```bash -ffmpeg -y -i /final-5s.mp4 \ - -map 0:v:0 -c:v copy -an \ - /final-5s-noaudio.mp4 -``` - -默认交付 `final-5s-noaudio.mp4`,保留原始 `final-5s.mp4` 作为中间产物。 - -如果用户明确要"带声"——拼贴动画的纸片嗰声 + BGM 是 `aigc-video-gen` 声画同出出的,可能挺贴——就不抽无声,直接交付 `final-5s.mp4`。但默认走无声。 - -## 视频 QA - -不要只看尾帧,必须检查组装过程和最终落位。 - -### Contact sheet - -```bash -ffmpeg -y -i /final-5s-noaudio.mp4 \ - -vf "fps=1,scale=270:480,tile=5x1" \ - -frames:v 1 /contact-sheet.jpg -``` - -通过标准: - -- 首帧接近纯色空场;边缘轻微提前露出纸片可以接受 -- 中段能看到结构、人物或卡片逐步进入,而不是整体淡入 -- 没有切镜、zoom、3D 化或写实场景漂移 -- 没有假字、logo、水印或 UI -- 最终帧与确认静帧一致;轻微姿态或细节漂移(如人物姿势微变、小零件增减)只要不影响隐喻语义即可判通过,不要为此重跑 -- 成片为 720×1280、有声画同出(`final-5s.mp4`)或无声(`final-5s-noaudio.mp4`)、5 秒 - -另外抽取视频末帧,与确认静帧并排生成 `end-frame-comparison.jpg`。批量项目再合并三张总览图: - -- `video-contact-sheet-all.jpg`:全部成片逐秒抽帧 -- `video-first-frame-all.jpg`:全部成片实际首帧,验证真的从空色场开始 -- `end-frame-comparison-all.jpg`:确认静帧与视频末帧并排对照 - -逐条 QA 结论(含带瑕疵通过的判定理由)写入 `/gate3-qa.md`。 - -### 成片技术自检(强制闸门) - -视觉 QA(contact sheet 看组装过程与落位)完成后,**必须**再跑公共 `video-review` 技术自检闸门,verdict=pass 才进交付: - -```bash -video-review /final-5s-noaudio.mp4 -# 或带声版:video-review /final-5s.mp4 -``` - -`video-review` 查的是技术层硬伤(ffprobe 全字段 / 5 位抽帧黑帧扫 / 音频电平 / 时长分辨率一致性),与上面的视觉 QA(看隐喻是否一眼看懂、组装过程是否成立)**互补不重叠**——视觉 QA 评审美与语义,video-review 评技术合规。verdict=fail 按 critical 项修或重生对应 job,verdict=warn 向用户复述由其决定。详见 `video-review` 技能 SKILL.md。 - -> 拼贴动画默认无声交付时,`audio_absent` warning 是预期(`final-5s-noaudio.mp4` 本就是抽音轨版),warn 可放行;带声版 `final-5s.mp4` 出 `audio_absent` 则 critical——`aigc-video-gen` 声画同出模式该出声没出声是硬伤,退回重生成。 - -### 常见问题 - -- 首帧边缘提前露出:轻微可接受;严格空场需求改用更坚定的 first-frame(纯色 + 边缘 padding) -- 组装感弱:缩短元素数量,并把 prompt 改为明确的逐件"滑入 / 卡位"顺序 -- 尾帧漂移:强化 prompt 里"最终定格在已确认的完成构图",`aigc-video-gen` i2v 的 last-frame 权重高 -- 出现假字:先回到静帧重生(Seedream 也可能出假字),不要直接用视频 prompt 修补 -- 个别视频失败:只重跑对应 job,不要重跑已经通过的条目 -- i2v 报错(`aigc-video-gen` 退出码非 0):检查首尾帧是否 720x1280、是否真存在、workdir 是否 workspace 根 - -## 默认交付 - -向用户交付: - -- 每条 `/gen-runs/run-v01/final-5s-noaudio.mp4`(或 `final-5s.mp4` 若用户要带声) -- 每条 contact sheet -- 批量总 contact sheet -- 最终帧对照图 -- 一句说明每条文稿如何转成视觉隐喻 - -如果成片问题来自 `aigc-video-gen` i2v 的生成限制(组装感弱 / 尾帧漂移),直接说明;只有需要精确图层控制时,才建议切换到其他方案(如 Manim 科学动画 → manim-explainer)。 - -## 脚本清单 - -| 脚本 | 文件名 | 用途 | -|------|--------|------| -| Gate 3 批量调度 | `scripts/run_gate3.py` | 读 gen-jobs.json,逐条调公共 `aigc-video-gen` i2v 模式(首尾帧插值),落产物 + decisions.log | - -visual-spec.json 生成、imagegen prompt 拼装、contact sheet 拼图、首尾帧 ffmpeg 处理——这些靠 agent 直接调 `siliconflow-img-gen` + ffmpeg 完成,不单独上脚本(agent 直接调更灵活,且避免脚本重复造轮子)。 diff --git a/crews/content-producer/skills/design-full/SKILL.md b/crews/content-producer/skills/design-full/SKILL.md deleted file mode 100644 index 133f0a47..00000000 --- a/crews/content-producer/skills/design-full/SKILL.md +++ /dev/null @@ -1,262 +0,0 @@ ---- -name: design-full -description: 平面设计全案——完整网页/落地页、APP/产品界面、品牌视觉体系。从需求 brief 到设计系统选取、素材获取、HTML/CSS 编写、视觉 review、交付归档的完整工作流。接到"做网页/落地页/APP 界面/品牌视觉"类平面设计需求时走本技能。 -metadata: - openclaw: - emoji: 🎨 - requires: - bins: - - python3 ---- - -# 平面设计全案(design-full) - -## 适用场景 - -用户要做以下任一平面设计工作: - -- 完整网页 / 落地页 / 团队介绍页 / 404 页等 -- APP / 产品界面 / 管理后台 / SaaS 面板原型 -- 品牌视觉体系(色彩 / 字体 / 组件 / 间距规范) - -不适用:视频制作(→ `video-producer` / `collage-broll` / `manim-explainer`)。 - ---- - -## 工作流 - -### Step 1:建工作区(强制起点) - -每项设计任务开始前**必须**先建独立文件夹,所有产出归档其中: - -```bash -design-full init <任务名> -``` - -产出目录结构(落在工作区根的 `design_assets/` 下): - -``` -design_assets/YYYY-MM-DD-<任务名>/ -├── brief.md # 设计需求模板(待填写,确认后不可跳过) -├── prompts.json # 生图参数记录 -├── source/ # 原始素材(参考图、品牌资产等) -└── output/ # 成品输出(HTML/CSS 文件、组件预览页) -``` - -`design_assets/` 同时建 `references/` 与 `brand/` 两个共享子目录(跨任务复用参考素材与品牌资产)。 - -### Step 2:Brief 确认(强制闸门) - -把需求整理写入 `brief.md`,**发给用户确认,等待明确同意**。确认前不得进入后续步骤。后续视觉 review 以 brief 为基准对照。 - -brief 至少含:产品类型 / 页面或界面清单 / 功能范围 / 风格方向 / 品牌约束 / 参考素材。 - -### Step 3:设计系统选取 - -每项任务在 brief 确认后、进具体设计前**必须**调本技能确定设计系统: - -```bash -design-full pick "<风格描述>" -``` - -按风格描述从内置 14 套设计系统库匹配最合适的 1–3 套,展示匹配结果及推荐理由给用户,**等待确认选定**。用户也可指定参考品牌或自定义风格,本技能据此生成定制 DESIGN.md。 - -选定后把该设计系统规范写入任务 `DESIGN.md`,后续所有 HTML/CSS 产出的色彩、字体、间距、组件样式都遵循该规范。 - -### Step 4:素材获取 - -页面所需配图 / 背景图 / 参考图: - -- **优先**:公共 `pexels-footage` / `pixabay-footage` 搜索下载 -- **备选**:公共 `siliconflow-img-gen` 生成 -- 下载或生成的素材保存到 `source/` 目录 - -### Step 5:HTML + CSS 编写 - -- CSS custom properties 定义设计 token(颜色、间距、字号、阴影)——严格遵循 DESIGN.md -- 语义化标签(header / main / section / footer) -- 响应式(min-width: 768px / 1024px 断点) -- hover / focus / active 状态完备 -- 图片引用 `source/` 中的素材 - -### Step 6:视觉 Review(强制闸门) - -生成页面 / 组件后**必须**调视觉模型 review,不得跳过: - -1. 用 `image` 工具查看生成结果 -2. 对照 `brief.md` 和 `DESIGN.md` 逐项检查:风格一致性、组件规范遵循度、响应式表现、交互状态完整性 -3. 发现偏差 → 调整 CSS token 或 HTML 结构后重新输出(**最多 3 轮**) -4. Review 通过 → 发送给用户 - -### Step 7:交付归档 - -最终确认后把文件保存到任务文件夹 `output/` 目录,归档并更新 `index.md`。 - ---- - -## 三条子工作流(按任务类型择) - -按 brief 里的产品类型择一条子工作流执行。Step 1/2/3/6/7 是三条共用骨架,下面只列各子工作流的 Step 4/5 差异。 - -### 工作流 A:完整网页 / 落地页设计 - -``` -Step 2 brief 含: - - 页面类型(产品介绍页/活动落地页/团队介绍/404 页...) - - 页面清单与信息架构(Sections 列表) - - 交互功能范围(纯静态展示/含表单/含轮播...) - - 风格参考(可提供品牌名或描述词) - - 是否需要深色模式 - - 品牌约束(品牌色、字体、LOGO — 从 MEMORY.md 获取) -Step 4 素材:页面所需配图/背景图 → pexels-footage / pixabay-footage 优先,siliconflow-img-gen 备选 -Step 5 编写: - - CSS custom properties 定义设计 token —— 严格遵循 DESIGN.md - - 语义化标签(header / main / section / footer) - - 响应式(min-width: 768px / 1024px 断点) - - hover / focus / active 状态 - - 图片引用 source/ 中的素材 -最终交付:HTML/CSS 文件 → output/,归档更新 index.md -``` - -### 工作流 B:APP / 产品界面设计 - -``` -Step 2 brief 含: - - 产品类型(移动 APP / Web APP / 管理后台 / SaaS 面板...) - - 核心页面清单(登录/首页/列表/详情/设置...) - - 交互模式(导航方式、手势支持、状态管理...) - - 风格参考 - - 品牌约束 -Step 3 后另写 DESIGN.md 设计规范: - - 色彩系统(语义色名 + hex + 用途:primary/secondary/surface/error/...) - - 字体系统(font-family + 层级表:display/heading/body/caption/overline) - - 间距系统(4px/8px/12px/16px/24px/32px/48px 基准) - - 组件样式规范(Button/Input/Card/Nav/Modal/Toast 等,含各状态) - - 阴影/圆角/动效规范 -Step 5 编写关键页面 HTML + CSS 原型: - - 严格遵循 DESIGN.md 中的 token - - 移动端优先(如为 APP 界面,按 375px 基准设计) - - 包含交互状态(hover/focus/disabled/loading) -最终交付:DESIGN.md + 所有页面 HTML/CSS → output/ -``` - -### 工作流 C:品牌视觉体系构建 - -``` -Step 2 brief 含: - - 品牌定位(行业、目标客群、核心价值) - - 风格方向(1-3 个关键词,如"专业+科技+温暖") - - 现有品牌资产(Logo、已有色彩偏好等) - - 应用场景(官网/APP/社交媒体/印刷品...) -Step 5 构建完整 DESIGN.md: - - Visual Theme & Atmosphere:设计哲学、情感基调、密度 - - Color Palette & Roles:语义名 + hex + 功能角色 - - Typography Rules:字体族 + 完整层级表 - - Component Stylings:核心组件样式 + 状态 - - Layout Principles:间距系统、网格、留白哲学 - - Depth & Elevation:阴影系统、表面层级 - - Responsive Behavior:断点、触控目标、折叠策略 - - Do's and Don'ts:设计护栏 -Step 5 另编写组件预览页面(preview.html): - - 展示色彩色板、字体层级、按钮/卡片/输入框等核心组件 - - 包含亮色和暗色两种表面 -最终交付:DESIGN.md + preview.html → output/ - - 将 DESIGN.md 核心信息同步到 MEMORY.md 的 Brand Assets 区 -``` - ---- - -## CSS 设计 Token 规范 - -所有 HTML/CSS 产出必须使用 CSS Custom Properties 定义设计 token: - -```css -:root { - /* 语义色彩 */ - --color-primary: oklch(...); - --color-surface: oklch(...); - --color-text: oklch(...); - - /* 字体层级 */ - --text-display: clamp(3rem, 1rem + 7vw, 8rem); - --text-body: clamp(1rem, 0.9rem + 0.5vw, 1.125rem); - - /* 间距系统 */ - --space-xs: 4px; - --space-sm: 8px; - --space-md: 16px; - --space-lg: 24px; - --space-xl: 32px; - --space-2xl: 48px; - - /* 动效 */ - --duration-normal: 300ms; - --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1); -} -``` - ---- - -## 品牌规范应用原则 - -- 若 MEMORY.md 中有品牌色 / 字体记录 → 在 DESIGN.md 和 CSS token 中**强制指定** -- 若无 → 第一次设计后询问用户是否认可当前色彩体系,认可则记入 MEMORY.md -- 核心品牌色 / Logo 不得随意替换,其余设计 token 可根据设计系统适配 - ---- - -## 内置设计系统库 - -14 套知名品牌设计系统,每套 8 段规范(Visual Theme / Color / Typography / Components / Layout / Depth / Do's & Don'ts / Responsive): - -| 设计系统 | 风格关键词 | 适用场景 | -|---------|----------|---------| -| Stripe | 紫色渐变、优雅、金融科技 | SaaS 产品页、支付/金融科技落地页 | -| Vercel | 黑白极简、精密、Geist | 开发者工具、技术产品官网 | -| Linear | 超极简、紫色点缀、精确 | 项目管理、效率工具 | -| Notion | 暖色极简、衬线标题、柔和 | 知识管理、内容平台 | -| Apple | 极致留白、电影级影像 | 消费电子、高端品牌官网 | -| Supabase | 暗色翡翠绿、代码优先 | 数据库/后端服务、开源工具 | -| Shopify | 暗色电影感、霓虹绿 | 电商平台、商业服务 | -| Figma | 多彩活泼、专业、创意 | 创意工具、设计平台 | -| Spotify | 鲜明绿、大胆排版 | 媒体/娱乐平台 | -| Tesla | 极致减法、全屏影像 | 汽车/硬件、极简品牌 | -| Framer | 黑蓝、动效优先 | 网站构建、交互展示 | -| Airbnb | 暖色珊瑚、摄影驱动 | 旅游/生活服务、社区平台 | -| BMW | 巴伐利亚蓝、暗色奢华、金属质感 | 奢侈品牌、高端产品 | -| IBM | 企业蓝、Carbon 系统、数据密集 | 企业级产品、B2B 服务、数据平台 | -| Starbucks | Siren 绿、温暖社区、自然质感 | 生活品牌、餐饮/零售、社区平台 | - -库文件落在本技能目录 `design-systems/.md`,索引 `design-systems/index.json`。 - -### 自定义设计系统 - -内置库无法覆盖所有风格需求时,基于用户描述自行构建设计系统,输出格式参照内置 DESIGN.md 的标准 8 段结构。 - -也可从上游仓库 [VoltAgent/awesome-design-md](https://github.com/VoltAgent/awesome-design-md) 查找并导入: - -1. 访问上述仓库查看完整设计系统列表,或直接访问 `https://getdesign.md//design-md` 查看特定品牌 -2. 选取匹配的设计系统后,将内容下载为 `design-systems/.md`,补全缺失段落确保 8 段完整 -3. 在 `design-systems/index.json` 中添加条目(字段:`id` / `name` / `category` / `keywords` / `description` / `colorPrimary` / `darkMode` / `bestFor` / `file`) - -完成后即可通过 `design-full pick` 搜索到该设计系统。 - ---- - -## 子命令清单 - -| 子命令 | 用途 | 退出码 | -|--------|------|--------| -| `design-full init <任务名>` | 建任务文件夹 + brief 模板 | 0 成功 / 1 参数错 | -| `design-full pick "<风格描述>"` | 从内置库匹配 1–3 套设计系统 | 0 成功 / 1 参数错 | - -> Step 2(brief 确认)/ Step 4(素材获取)/ Step 5(HTML+CSS 编写)/ Step 6(视觉 review)/ Step 7(交付归档)由 agent 按 SKILL.md 工作流直接执行,不经本 wrapper——这些是创意判断与对话协作环节,不上脚本。 - ---- - -## 禁止事项(强制) - -- **禁止 brief 未确认就动手**:Step 2 闸门强制,确认前不得进 Step 3 -- **禁止跳过设计系统选取**:每项任务 Step 3 必跑 `design-full pick`,不得凭印象直接写 CSS -- **禁止跳过视觉 Review 交付**:Step 6 闸门强制,对照 brief + DESIGN.md 逐项查,不得裸交 -- **禁止凭空捏造品牌色**:MEMORY.md 有记录则强制遵循,无记录则设计后问用户认可才记入 diff --git a/crews/content-producer/skills/expert-design/SKILL.md b/crews/content-producer/skills/expert-design/SKILL.md new file mode 100644 index 00000000..65d59a69 --- /dev/null +++ b/crews/content-producer/skills/expert-design/SKILL.md @@ -0,0 +1,160 @@ +--- +name: expert-design +description: 平面设计专家(乙方)。承接完整网页/落地页、APP 与产品界面原型、品牌视觉体系(DESIGN.md + 组件预览)三类平面设计全案,从需求 brief、设计系统选取、素材获取、HTML/CSS 编写到视觉 review 与交付归档。两种工作模式:作为 main agent 的 subagent 接 Brief,或直接对接用户。不做视频(走 expert-video),不做平台发布与运营。 +metadata: + openclaw: + emoji: 🎨 + requires: + bins: + - python3 +--- + +# 平面设计专家(expert-design) + +## 角色:始终是乙方 + +不管任务来自谁,本包都是**乙方(承制方)**:按 Brief 交付设计成品,不自作主张改需求方向,也不替甲方定品牌事实与投放策略。 + +- 需求以 **Brief** 为唯一契约;Brief 未写的先问甲方,不脑补品牌色、卖点与合规承诺。 +- 设计实现(设计系统选取、token 组织、页面结构、组件写法)是乙方专业范围。 +- 交付边界:HTML/CSS 成品或 DESIGN.md + 预览页。**不发布上线、不做平台运营、不私信客户**。 + +## 两种工作模式 + +| 模式 | 甲方 | Stage/Step 起点 | +|------|------|-----------------| +| A · Subagent 承制 | main agent | 读甲方 Brief → 核对字段 → 缺口向 Brief owner 澄清,**不重开需求讨论** | +| B · 直接对接用户 | 用户(已配 channel) | 用户已给 Brief → 核对确认;没给 → 引导讨论并**代用户整理 brief.md,发用户确认后才开工** | + +模式 B 下用户不一定专业,乙方要替他把需求收敛清楚:做什么(网页/界面/品牌体系)、给谁看、要什么感觉、有哪些必须遵循的品牌资产、涉及哪些页面或组件、有没有参考站点、什么时候要。涉及用户已有素材(Logo、参考图、品牌资产)时,必须让用户给出**绝对路径**并逐条确认真实存在。 + +## 资源命名约定 + +- Workflows、Tools 名称是本技能包内的**逻辑资源名**,不是 Workspace 路径,不要拼成相对路径执行。 +- 技能部署后整个包通过软链进入运行环境;不要假设包内资源被展开到 Workspace 下。 +- 文档中出现的 `design_assets/` 才是 Workspace 相对路径,从 Content Producer workspace 根解析。 +- 只有工具清单中列出的 wrapper 名称可以直接作为 shell 命令调用。 + +## Workflow 清单(按设计任务类型选) + +| 任务类型 / 入口信号 | Workflow | +|--------------------|----------| +| 完整网页 / 落地页 / 团队介绍页 / 404 页 | Web Page | +| APP / Web APP / 管理后台 / SaaS 面板界面原型 | App UI | +| 品牌视觉体系(色彩 / 字体 / 组件 / 间距规范) | Brand Visual | + +三条 workflow 共用下方 Step 1/2/3/6/7 骨架,差异只在 brief 必含字段与 Step 4/5。 + +不适用:视频 / 动画 / 封面图 → `expert-video`(封面走其 Stage 14a);单张配图生成 → 公共 `siliconflow-img-gen`。 + +## 工具清单 + +| 工具 | 用途 | 命令 | +|------|------|------| +| `design-full` | 建任务工作区 + brief 模板;从内置 14+ 套设计系统库匹配风格 | `design-full init <任务名>` / `design-full pick "<风格描述>"` | + +跨领域公共技能:`pexels-footage` / `pixabay-footage`(配图与背景图首选)、`siliconflow-img-gen`(配图备选)、`smart-search`(参考站点调研)。 + +## 通用骨架(七步,两闸门) + +### Step 1:建工作区(强制起点,自建,甲方不代建) + +```bash +design-full init <任务名> +``` + +产出目录(落 Content Producer workspace 的 `design_assets/` 下): + +``` +design_assets/YYYY-MM-DD-<任务名>/ +├── brief.md # 设计需求(甲方交付拷贝入档,或模式 B 与用户定稿) +├── DESIGN.md # Step 3 选定的设计系统规范 +├── prompts.json # 生图参数记录 +├── source/ # 原始素材(参考图、品牌资产、甲方给的 Logo) +└── output/ # 成品(HTML/CSS、组件预览页) +``` + +`design_assets/` 同时建 `references/` 与 `brand/` 两个共享子目录(跨任务复用参考素材与品牌资产)。 + +### Step 2:Brief 确认(强制闸门) + +把需求整理写入 `brief.md`,**发甲方确认,等明确同意**。确认前不得进后续步骤;后续视觉 review 以 brief 为基准对照。 + +brief 至少含:产品类型 / 页面或界面清单 / 功能范围 / 风格方向 / 品牌约束 / 参考素材(绝对路径)。 + +### Step 3:设计系统选取(强制,不得凭印象直接写 CSS) + +```bash +design-full pick "<风格描述>" +``` + +从内置设计系统库匹配 1–3 套,展示匹配结果与推荐理由,**等甲方确认选定**;甲方也可指定参考品牌或自定义风格,据此生成定制 DESIGN.md。选定后把规范写入任务 `DESIGN.md`,后续所有 HTML/CSS 的色彩、字体、间距、组件样式都遵循它。 + +### Step 4:素材获取 + +- **优先**:公共 `pexels-footage` / `pixabay-footage` 搜索下载 +- **备选**:公共 `siliconflow-img-gen` 生成(参数记 `prompts.json`) +- 甲方给的素材入 `source/`,记录来源与授权 + +### Step 5:HTML + CSS 编写 + +- CSS custom properties 定义设计 token(颜色、间距、字号、阴影),严格遵循 DESIGN.md +- 语义化标签(header / main / section / footer) +- 响应式(min-width: 768px / 1024px 断点;APP 界面按 375px 基准移动优先) +- hover / focus / active / disabled / loading 状态完备 +- 图片引用 `source/` 中的素材 + +### Step 6:视觉 Review(强制闸门) + +1. 用 `image` 工具查看渲染结果 +2. 对照 `brief.md` 与 `DESIGN.md` 逐项检查:风格一致性、组件规范遵循度、响应式表现、交互状态完整性 +3. 发现偏差 → 调 CSS token 或 HTML 结构后重新输出(**最多 3 轮**) +4. Review 通过 → 交甲方 + +### Step 7:交付归档 + +最终确认后把文件保存到任务 `output/`,归档并更新 `index.md`;交付时回报成品**绝对路径**与关键决策(选定的设计系统、token 例外、遗留问题)。 + +## CSS 设计 Token 规范 + +```css +:root { + /* 语义色彩 */ + --color-primary: oklch(...); + --color-surface: oklch(...); + --color-text: oklch(...); + + /* 字体层级 */ + --text-display: clamp(3rem, 1rem + 7vw, 8rem); + --text-body: clamp(1rem, 0.9rem + 0.5vw, 1.125rem); + + /* 间距系统 */ + --space-xs: 4px; + --space-sm: 8px; + --space-md: 16px; + --space-lg: 24px; + --space-xl: 32px; + --space-2xl: 48px; + + /* 动效 */ + --duration-normal: 300ms; + --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1); +} +``` + +## 品牌规范应用原则 + +- MEMORY.md 中有品牌色 / 字体记录 → 在 DESIGN.md 与 CSS token 中**强制指定** +- 无记录 → 第一次设计后询问甲方是否认可当前色彩体系,认可才记入 MEMORY.md +- 核心品牌色 / Logo 不得随意替换;其余 token 可按设计系统适配 +- 品牌事实(产品名、能力表述、资质)只以 Brief 与 `business_knowledge.md` 为准,不自创 + +## 禁止事项(强制) + +- **禁止 brief 未确认就动手**:Step 2 闸门强制,确认前不得进 Step 3 +- **禁止跳过设计系统选取**:每项任务 Step 3 必跑 `design-full pick` +- **禁止跳过视觉 Review 交付**:Step 6 闸门强制,对照 brief + DESIGN.md 逐项查,不得裸交 +- **禁止凭空捏造品牌色**:MEMORY.md 有记录则强制遵循,无记录则设计后问甲方认可才记入 +- **禁止声称没做过的事**:没有产物文件或 tool result 证明,不许声称已生成/已渲染/已改 +- **禁止让甲方建工作区**:工作区自建(`design-full init`),也不要把产物写进甲方目录 +- **禁止越界做视频与发布**:视频走 `expert-video`,发布与运营归 main agent diff --git a/crews/content-producer/skills/expert-design/tools/design-full/SKILL.md b/crews/content-producer/skills/expert-design/tools/design-full/SKILL.md new file mode 100644 index 00000000..ba30d73c --- /dev/null +++ b/crews/content-producer/skills/expert-design/tools/design-full/SKILL.md @@ -0,0 +1,62 @@ +--- +name: design-full +description: 平面设计原子工具——建任务工作区与 brief 模板(init)、从内置设计系统库匹配风格(pick)。 +metadata: + openclaw: + emoji: 🎨 + requires: + bins: + - python3 +--- + +# design-full — 工具说明 + +> 本文是 `expert-design` 专家包内的工具说明书,不独立出现在技能列表中。设计流程(brief 闸门、设计系统确认、素材、编写、视觉 review、交付)由包内 SKILL.md 与 `workflows/` 编排。 + +**调用方式**:`design-full <子命令> [参数...]`(wrapper 转发到 `scripts/`,零路径拼接)。`design-full help` 查用法。 + +| 子命令 | 入 | 出 | 退出码 | +|--------|----|----|--------| +| `design-full init <任务名>` | 任务名(英文短横线式) | `design_assets/YYYY-MM-DD-<任务名>/`(含 `brief.md` 模板、`source/`、`output/`),并确保 `design_assets/references/`、`design_assets/brand/` 存在 | 0 成功 / 1 参数错 | +| `design-full pick "<风格描述>"` | 风格描述(中文关键词即可,如"科技感暗色主题") | stdout 列出全部可用设计系统 + 按匹配分排序的 1–3 套推荐及理由 | 0 成功 / 1 参数错或索引缺失 | + +**注意事项**: + +- `init` 按**当前工作目录**建 `design_assets/`,调用时 workdir 必须是 Content Producer workspace 根。 +- `init` 幂等:已存在的 `brief.md` 不覆盖(保留已填内容)。 +- `pick` 只做匹配与展示,**不写文件**;选定结果由 agent 写入任务目录的 `DESIGN.md`。 +- 匹配是关键词打分(keywords / category / name / description),不是语义检索;描述里多放风格词、行业词、色彩词命中率更高。 + +## 内置设计系统库 + +每套 8 段规范(Visual Theme / Color / Typography / Components / Layout / Depth / Do's & Don'ts / Responsive),文件在工具目录 `design-systems/.md`,索引 `design-systems/index.json`。 + +| 设计系统 | 风格关键词 | 适用场景 | +|---------|----------|---------| +| Stripe | 紫色渐变、优雅、金融科技 | SaaS 产品页、支付/金融科技落地页 | +| Vercel | 黑白极简、精密、Geist | 开发者工具、技术产品官网 | +| Linear | 超极简、紫色点缀、精确 | 项目管理、效率工具 | +| Notion | 暖色极简、衬线标题、柔和 | 知识管理、内容平台 | +| Apple | 极致留白、电影级影像 | 消费电子、高端品牌官网 | +| Supabase | 暗色翡翠绿、代码优先 | 数据库/后端服务、开源工具 | +| Shopify | 暗色电影感、霓虹绿 | 电商平台、商业服务 | +| Figma | 多彩活泼、专业、创意 | 创意工具、设计平台 | +| Spotify | 鲜明绿、大胆排版 | 媒体/娱乐平台 | +| Tesla | 极致减法、全屏影像 | 汽车/硬件、极简品牌 | +| Framer | 黑蓝、动效优先 | 网站构建、交互展示 | +| Airbnb | 暖色珊瑚、摄影驱动 | 旅游/生活服务、社区平台 | +| BMW | 巴伐利亚蓝、暗色奢华、金属质感 | 奢侈品牌、高端产品 | +| IBM | 企业蓝、Carbon 系统、数据密集 | 企业级产品、B2B 服务、数据平台 | +| Starbucks | Siren 绿、温暖社区、自然质感 | 生活品牌、餐饮/零售、社区平台 | + +## 自定义设计系统 + +内置库覆盖不到时,基于甲方描述自行构建,输出格式参照内置 DESIGN.md 的标准 8 段结构。 + +也可从上游仓库 [VoltAgent/awesome-design-md](https://github.com/VoltAgent/awesome-design-md) 导入: + +1. 访问上述仓库查看完整设计系统列表,或直接访问 `https://getdesign.md//design-md` 查看特定品牌。 +2. 下载为 `design-systems/.md`,补全缺失段落确保 8 段完整。 +3. 在 `design-systems/index.json` 添加条目(字段:`id` / `name` / `category` / `keywords` / `description` / `colorPrimary` / `darkMode` / `bestFor` / `file`)。 + +完成后即可通过 `design-full pick` 搜到。 diff --git a/crews/content-producer/skills/design-full/design-full.sh b/crews/content-producer/skills/expert-design/tools/design-full/design-full.sh similarity index 73% rename from crews/content-producer/skills/design-full/design-full.sh rename to crews/content-producer/skills/expert-design/tools/design-full/design-full.sh index 4ce8f0dd..ebf2d5ee 100755 --- a/crews/content-producer/skills/design-full/design-full.sh +++ b/crews/content-producer/skills/expert-design/tools/design-full/design-full.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# design-full.sh — design-full 顶层 wrapper(薄转发,子命令范式) +# design-full.sh — design-full 工具 wrapper(薄转发,子命令范式;expert-design 包内工具) # 让 agent 用 `design-full <子命令> [参数...]` 走 PATH,零路径拼接。 # 子命令: # init <任务名> 建任务文件夹 + brief 模板(落 design_assets/YYYY-MM-DD-<任务名>/) @@ -22,18 +22,18 @@ case "$SUBCMD" in ;; -h|--help|help) cat <<'HELP' -design-full — 平面设计全案(wrapper) +design-full — 平面设计原子工具(wrapper,expert-design 包内工具) 用法: design-full init <任务名> 建任务文件夹 + brief 模板 design-full pick "<风格描述>" 从内置设计系统库匹配最合适的 1–3 套 design-full help 本帮助 -子命令是 design-full SKILL.md 工作流里的原子步骤: - Step 1 建工作区 → design-full init - Step 3 设计系统选取 → design-full pick +子命令是 expert-design 通用骨架里的原子步骤: + Step 1 建工作区 → design-full init(workdir 必须是 Content Producer workspace 根) + Step 3 设计系统选取 → design-full pick 其余步骤(brief 确认、素材获取、HTML/CSS 编写、视觉 review、交付归档)由 agent -按 SKILL.md 工作流直接执行,不经本 wrapper。 +按 expert-design 包内 SKILL.md 与 workflows/ 直接执行,不经本 wrapper。 HELP ;; *) diff --git a/crews/content-producer/skills/design-full/design-systems/airbnb.md b/crews/content-producer/skills/expert-design/tools/design-full/design-systems/airbnb.md similarity index 100% rename from crews/content-producer/skills/design-full/design-systems/airbnb.md rename to crews/content-producer/skills/expert-design/tools/design-full/design-systems/airbnb.md diff --git a/crews/content-producer/skills/design-full/design-systems/apple.md b/crews/content-producer/skills/expert-design/tools/design-full/design-systems/apple.md similarity index 100% rename from crews/content-producer/skills/design-full/design-systems/apple.md rename to crews/content-producer/skills/expert-design/tools/design-full/design-systems/apple.md diff --git a/crews/content-producer/skills/design-full/design-systems/bmw.md b/crews/content-producer/skills/expert-design/tools/design-full/design-systems/bmw.md similarity index 100% rename from crews/content-producer/skills/design-full/design-systems/bmw.md rename to crews/content-producer/skills/expert-design/tools/design-full/design-systems/bmw.md diff --git a/crews/content-producer/skills/design-full/design-systems/figma.md b/crews/content-producer/skills/expert-design/tools/design-full/design-systems/figma.md similarity index 100% rename from crews/content-producer/skills/design-full/design-systems/figma.md rename to crews/content-producer/skills/expert-design/tools/design-full/design-systems/figma.md diff --git a/crews/content-producer/skills/design-full/design-systems/framer.md b/crews/content-producer/skills/expert-design/tools/design-full/design-systems/framer.md similarity index 100% rename from crews/content-producer/skills/design-full/design-systems/framer.md rename to crews/content-producer/skills/expert-design/tools/design-full/design-systems/framer.md diff --git a/crews/content-producer/skills/design-full/design-systems/ibm.md b/crews/content-producer/skills/expert-design/tools/design-full/design-systems/ibm.md similarity index 100% rename from crews/content-producer/skills/design-full/design-systems/ibm.md rename to crews/content-producer/skills/expert-design/tools/design-full/design-systems/ibm.md diff --git a/crews/content-producer/skills/design-full/design-systems/index.json b/crews/content-producer/skills/expert-design/tools/design-full/design-systems/index.json similarity index 100% rename from crews/content-producer/skills/design-full/design-systems/index.json rename to crews/content-producer/skills/expert-design/tools/design-full/design-systems/index.json diff --git a/crews/content-producer/skills/design-full/design-systems/linear.md b/crews/content-producer/skills/expert-design/tools/design-full/design-systems/linear.md similarity index 100% rename from crews/content-producer/skills/design-full/design-systems/linear.md rename to crews/content-producer/skills/expert-design/tools/design-full/design-systems/linear.md diff --git a/crews/content-producer/skills/design-full/design-systems/notion.md b/crews/content-producer/skills/expert-design/tools/design-full/design-systems/notion.md similarity index 100% rename from crews/content-producer/skills/design-full/design-systems/notion.md rename to crews/content-producer/skills/expert-design/tools/design-full/design-systems/notion.md diff --git a/crews/content-producer/skills/design-full/design-systems/shopify.md b/crews/content-producer/skills/expert-design/tools/design-full/design-systems/shopify.md similarity index 100% rename from crews/content-producer/skills/design-full/design-systems/shopify.md rename to crews/content-producer/skills/expert-design/tools/design-full/design-systems/shopify.md diff --git a/crews/content-producer/skills/design-full/design-systems/spotify.md b/crews/content-producer/skills/expert-design/tools/design-full/design-systems/spotify.md similarity index 100% rename from crews/content-producer/skills/design-full/design-systems/spotify.md rename to crews/content-producer/skills/expert-design/tools/design-full/design-systems/spotify.md diff --git a/crews/content-producer/skills/design-full/design-systems/starbucks.md b/crews/content-producer/skills/expert-design/tools/design-full/design-systems/starbucks.md similarity index 100% rename from crews/content-producer/skills/design-full/design-systems/starbucks.md rename to crews/content-producer/skills/expert-design/tools/design-full/design-systems/starbucks.md diff --git a/crews/content-producer/skills/design-full/design-systems/stripe.md b/crews/content-producer/skills/expert-design/tools/design-full/design-systems/stripe.md similarity index 100% rename from crews/content-producer/skills/design-full/design-systems/stripe.md rename to crews/content-producer/skills/expert-design/tools/design-full/design-systems/stripe.md diff --git a/crews/content-producer/skills/design-full/design-systems/supabase.md b/crews/content-producer/skills/expert-design/tools/design-full/design-systems/supabase.md similarity index 100% rename from crews/content-producer/skills/design-full/design-systems/supabase.md rename to crews/content-producer/skills/expert-design/tools/design-full/design-systems/supabase.md diff --git a/crews/content-producer/skills/design-full/design-systems/tesla.md b/crews/content-producer/skills/expert-design/tools/design-full/design-systems/tesla.md similarity index 100% rename from crews/content-producer/skills/design-full/design-systems/tesla.md rename to crews/content-producer/skills/expert-design/tools/design-full/design-systems/tesla.md diff --git a/crews/content-producer/skills/design-full/design-systems/vercel.md b/crews/content-producer/skills/expert-design/tools/design-full/design-systems/vercel.md similarity index 100% rename from crews/content-producer/skills/design-full/design-systems/vercel.md rename to crews/content-producer/skills/expert-design/tools/design-full/design-systems/vercel.md diff --git a/crews/content-producer/skills/design-full/scripts/init.sh b/crews/content-producer/skills/expert-design/tools/design-full/scripts/init.sh similarity index 100% rename from crews/content-producer/skills/design-full/scripts/init.sh rename to crews/content-producer/skills/expert-design/tools/design-full/scripts/init.sh diff --git a/crews/content-producer/skills/design-full/scripts/pick.sh b/crews/content-producer/skills/expert-design/tools/design-full/scripts/pick.sh similarity index 100% rename from crews/content-producer/skills/design-full/scripts/pick.sh rename to crews/content-producer/skills/expert-design/tools/design-full/scripts/pick.sh diff --git a/crews/content-producer/skills/expert-design/workflows/app-ui.md b/crews/content-producer/skills/expert-design/workflows/app-ui.md new file mode 100644 index 00000000..7a2d4902 --- /dev/null +++ b/crews/content-producer/skills/expert-design/workflows/app-ui.md @@ -0,0 +1,32 @@ +# Workflow:App UI(APP / 产品界面原型设计) + +任务类型:移动 APP、Web APP、管理后台、SaaS 面板的界面原型。Step 1/2/3/6/7 按 `expert-design` SKILL.md 的通用骨架执行,本文只写本类型的 brief 必含字段与 Step 3/5 差异。 + +## Step 2:brief 必含字段 + +- 产品类型(移动 APP / Web APP / 管理后台 / SaaS 面板…) +- 核心页面清单(登录 / 首页 / 列表 / 详情 / 设置…) +- 交互模式(导航方式、手势支持、状态管理) +- 数据形态:真实数据样例或占位规则(不得自造业务数字) +- 风格参考与品牌约束 + +## Step 3 之后:另写 DESIGN.md 设计规范 + +在设计系统选定结果基础上补全界面专用规范: + +- 色彩系统(语义色名 + hex + 用途:primary / secondary / surface / error…) +- 字体系统(font-family + 层级表:display / heading / body / caption / overline) +- 间距系统(4 / 8 / 12 / 16 / 24 / 32 / 48px 基准) +- 组件样式规范(Button / Input / Card / Nav / Modal / Toast 等,含各状态) +- 阴影 / 圆角 / 动效规范 + +## Step 5:编写关键页面 HTML + CSS 原型 + +- 严格遵循 DESIGN.md 的 token +- 移动 APP 界面按 375px 基准移动优先;后台/面板按桌面断点 +- 交互状态齐全:hover / focus / disabled / loading +- 空态、错误态、加载态至少各给一个页面示例 + +## 交付 + +DESIGN.md + 所有页面 HTML/CSS 落 `output/`;回报绝对路径 + 页面清单 + 视觉 review 结论 + 未覆盖的状态说明。 diff --git a/crews/content-producer/skills/expert-design/workflows/brand-visual.md b/crews/content-producer/skills/expert-design/workflows/brand-visual.md new file mode 100644 index 00000000..1e5a94f7 --- /dev/null +++ b/crews/content-producer/skills/expert-design/workflows/brand-visual.md @@ -0,0 +1,31 @@ +# Workflow:Brand Visual(品牌视觉体系构建) + +任务类型:为品牌建立可复用的视觉规范(色彩 / 字体 / 组件 / 间距 / 阴影 / 响应式 / 护栏)。Step 1/2/3/6/7 按 `expert-design` SKILL.md 的通用骨架执行,本文只写本类型的 brief 必含字段与 Step 5 差异。 + +## Step 2:brief 必含字段 + +- 品牌定位(行业、目标客群、核心价值) +- 风格方向(1–3 个关键词,如"专业 + 科技 + 温暖") +- 现有品牌资产(Logo、已有色彩偏好、字体授权情况;给绝对路径) +- 应用场景(官网 / APP / 社交媒体 / 印刷品…)——决定 token 粒度与暗色表面是否必需 + +## Step 5:构建完整 DESIGN.md(八段) + +1. Visual Theme & Atmosphere:设计哲学、情感基调、密度 +2. Color Palette & Roles:语义名 + hex + 功能角色 +3. Typography Rules:字体族 + 完整层级表 +4. Component Stylings:核心组件样式 + 状态 +5. Layout Principles:间距系统、网格、留白哲学 +6. Depth & Elevation:阴影系统、表面层级 +7. Responsive Behavior:断点、触控目标、折叠策略 +8. Do's and Don'ts:设计护栏 + +## Step 5 另编写组件预览页(preview.html) + +- 展示色彩色板、字体层级、按钮 / 卡片 / 输入框等核心组件 +- 包含亮色与暗色两种表面 +- 所有样式走 CSS custom properties,便于直接复用到后续页面 + +## 交付 + +DESIGN.md + preview.html 落 `output/`;把 DESIGN.md 核心信息(品牌色、字体、护栏)同步到 MEMORY.md 的 Brand Assets 区——**同步前须经甲方确认**。回报绝对路径 + 视觉 review 结论。 diff --git a/crews/content-producer/skills/expert-design/workflows/web-page.md b/crews/content-producer/skills/expert-design/workflows/web-page.md new file mode 100644 index 00000000..427ffa90 --- /dev/null +++ b/crews/content-producer/skills/expert-design/workflows/web-page.md @@ -0,0 +1,29 @@ +# Workflow:Web Page(完整网页 / 落地页设计) + +任务类型:产品介绍页、活动落地页、团队介绍页、404 页等完整网页。Step 1/2/3/6/7 按 `expert-design` SKILL.md 的通用骨架执行,本文只写本类型的 brief 必含字段与 Step 4/5 差异。 + +## Step 2:brief 必含字段 + +- 页面类型(产品介绍页 / 活动落地页 / 团队介绍 / 404 页…) +- 页面清单与信息架构(Sections 列表,按顺序) +- 交互功能范围(纯静态展示 / 含表单 / 含轮播 / 含锚点导航…) +- 风格参考(品牌名或描述词,供 Step 3 `design-full pick` 使用) +- 是否需要深色模式 +- 品牌约束(品牌色、字体、Logo——从 MEMORY.md 或甲方素材取,注明绝对路径) +- 文案来源:甲方给终稿则逐字使用;未给时明确由谁补,不得自造卖点与数据 + +## Step 4:素材 + +页面所需配图 / 背景图 / 参考图:`pexels-footage` / `pixabay-footage` 优先,`siliconflow-img-gen` 备选,全部落 `source/` 并记 `prompts.json`。 + +## Step 5:编写 + +- CSS custom properties 定义设计 token,严格遵循 DESIGN.md +- 语义化标签(header / main / section / footer) +- 响应式:min-width 768px / 1024px 断点 +- hover / focus / active 状态完备 +- 图片引用 `source/` 中的素材,不热链外站 + +## 交付 + +HTML/CSS 文件落 `output/`,归档更新 `index.md`;回报绝对路径 + Step 6 视觉 review 结论 + 遗留问题(如缺文案、缺真实数据占位)。 diff --git a/crews/content-producer/skills/expert-video/SKILL.md b/crews/content-producer/skills/expert-video/SKILL.md new file mode 100644 index 00000000..7990c9c8 --- /dev/null +++ b/crews/content-producer/skills/expert-video/SKILL.md @@ -0,0 +1,296 @@ +--- +name: expert-video +description: 视频制作专家(乙方)。承接端到端视频制作——口播类、实拍拼接/蒙太奇、影视解说+反转植入(「万万没想到」)、纯 AIGC 动画、纸拼贴 B-roll 等类型,以及已有素材的成片加工,交付成片 + 封面。两种工作模式:作为 main agent 的 subagent 接 Brief,或直接对接用户。甲方只给 Brief 与已有素材绝对路径,工作区、制作方案、分镜与实现全部由本包自行负责;不发布、不做平台运营。 +metadata: + openclaw: + emoji: 🎬 + requires: + bins: + - python3 + - ffmpeg + - ffprobe +--- + +# 视频制作专家(expert-video) + +## 角色:始终是乙方 + +不管任务来自谁,本包都是**乙方(承制方)**:按 Brief 交付成片与封面,不自作主张改需求,也不替甲方做选题、标题、简介与发布运营。 + +- 需求以 **Brief** 为唯一契约。Brief 未写的,先问甲方,不自行脑补品牌事实、授权与承诺。 +- 制作实现(工作区、分镜、素材方案、渲染参数、剪辑手法)是乙方的专业范围,甲方不插手,本包也不向甲方要这些决策。 +- 交付边界:成片 `video.mp4` + 封面 `cover.jpg` + 交付说明 `final-deliver.md`。**不发布到任何平台、不私信用户、不代拟运营话术**(发布用文案由甲方给)。 + +## 两种工作模式 + +| 模式 | 甲方 | 触发 | Stage 0 做什么 | +|------|------|------|----------------| +| A · Subagent 承制 | main agent | 被 spawn,收到 Brief(+ 已有素材绝对路径) | 读 Brief → 核对字段齐全 → 缺关键字段向 Brief owner 澄清;**不重开需求讨论** | +| B · 直接对接用户 | 用户(已配 channel) | 用户在自己的 channel 里直接提需求 | 用户已给 Brief → 核对确认;用户没给 → 引导讨论并**代用户整理 Brief,发用户确认后才开工** | + +模式 B 的额外要求(用户不一定专业,乙方要替他把需求收敛清楚): + +1. **先明确 Brief**:至少问清 —— 做什么类型视频、给谁看、要传达什么、时长与横竖屏、有没有现成素材、要不要口播(谁的声音)、什么时候要。整理成 `brief.md` 发用户确认。 +2. **涉及已有素材必须落实位置**:让用户给出素材的**绝对路径**(或明确授权从哪个目录取),逐条 `ls` 确认真实存在、可解码;缺素材就明说缺什么,不许拿"待补"开工。 +3. **口播内容**:口播文案由甲方出(模式 A 是 main agent,模式 B 是用户)。用户只会说大意时,本包可代拟文案,但必须发用户确认后才算定稿。明确是用户真人口播时,必须拿到用户的录音文件(绝对路径)。 +4. 用户提出的模糊想法("做个短片""帮我策划一下")**不算确认**,不得据此调渲染类工具。 + +## 资源命名约定 + +- Workflows、Tools 等名称是本技能包内的**逻辑资源名**,不是 Workspace 路径,不要拼成相对路径执行。 +- 技能部署后整个包通过软链进入运行环境;不要假设包内资源被展开到 Workspace 下。 +- 文档中出现的 `output_videos/`、`design_assets/` 才是 Workspace 相对路径,从 Content Producer workspace 根解析。 +- 只有工具清单中列出的 wrapper 名称可以直接作为 shell 命令调用;其余 Tool 名称仅用于定位工具说明。 + +## Workflow 清单(按视频类型选) + +Workflow = **某一类型视频怎么做**。制作流程本身是高度程式化、跨类型一致的(见下方「通用制作流程」),类型之间的差别只在叙事套路、素材来源与声画组织方式——那部分写在 workflow 里。 + +| 视频类型 / 入口信号 | Workflow | Brief `workflow` 字段值 | 说明 | +|--------------------|----------|------------------------|------| +| Brief 未指定类型,或"从零做一支视频""按这个主题拍片子" | 通用制作流程(本文下方阶段链) | 省略 / `未指定` | 按 Stage 1 定档位:故事讲述型 / 纯画面动效型 / 蒙太奇剪接型 | +| 影视解说 / 剧情解说 + 突然反转插入品宣("万万没想到"式) | Reversal Ad | `reversal-ad` | 解说正文占大头,反转点后集中植入,给人猝不及防感 | +| 口播类视频(甲方交付口播文案或真人口播录音,要合成声画) | Narration Video | `narration-video` | 文案已定稿不重写,本包负责声画合成、字幕、素材配画面 | +| "把这句口播做成拼贴 B-roll""纸拼贴动画""半调拼贴" | Collage B-roll | `collage-broll` | 一句文稿 → 一个视觉隐喻 → 静帧 → i2v 组装动画(三道闸门) | +| 已有素材要剪辑、修整、拼接、配音、烧字幕 | 通用制作流程的 Stage 12 工具箱 | 省略 | 只做几何级修整,不做语义级高光剪辑(那归 main 的 `video-edit` / `talking-head-cut`) | + +Workflow 文档在包内 `workflows/<字段值>.md`。Brief 指定了 `workflow` 时**必须先读对应文档并直接采用**,不得替换成自创流程;未指定时按通用流程自由选择实现。 + +不适用(交回甲方或转其他专家包): + +- 平面设计 / 网页 / APP 界面 / 品牌视觉 → `expert-design` +- 语义级高光剪辑(去口气词、智能剪重点)→ main 的 `talking-head-cut` / `video-edit` +- 视频下载、爆款拆解、转录抽帧 → main 的 `viral-chaser`(本包不自己下载转写) +- 平台发布与运营 → main 的各平台专家包 + +## 工具清单 + +零散活儿直接调用,不必走完整 workflow。 + +| 工具 | 用途 | 命令 | +|------|------|------| +| `video-producer` | 视频制作原子能力集(意图路由、故事/剧本/分镜、素材 slot 与解析、渲染、混音对齐、拼接合成、动效审计、封面) | `video-producer <子命令>` | +| `collage-broll` | 纸拼贴 B-roll 的环境自检与 Gate 3 批量视频生成调度 | `collage-broll <子命令>` | + +跨领域公共技能:`aigc-video-gen`(视频片段生成 / i2v 首尾帧插值)、`siliconflow-img-gen`(静帧、角色三视图、封面)、`awk-tts`(旁白 TTS,带字级时间戳)、`bgm-library` / `pexels-footage` / `pixabay-footage`(免版税 BGM 与素材)、`video-review`(成片技术自检闸门)、`video-edit subtitles`(烧字幕原子命令)。 + +## 交接契约 + +### 输入(甲方交付) + +| 项 | 要求 | +|----|------| +| `brief.md` | 绝对路径。含视频类型/workflow、主题与观看理由、核心传达、时长与横竖屏、素材清单、封面要求、交付与验收、闸门批准人。**不含 DNA 信息**(甲方内部资产,本包不读也不用) | +| 已有素材 | 绝对路径逐条列出,含来源与授权说明;本包只做入库校验与技术处理 | +| 口播文案 | 有口播时由甲方出具(`voiceover.md`,绝对路径)。本包不重写策略文案,只做声画实现 | +| 口播录音 | 明确真人口播时,甲方提供录音文件绝对路径 | + +- ✅ 缺字段 → 向 Brief owner 澄清后再开工。 +- ❌ 缺字段 → 自己猜品牌卖点、自己编授权、自己改需求方向。 + +### 输出(本包交付) + +| 项 | 位置 | +|----|------| +| 成片 | `/video.mp4`(过 `video-review`,verdict=pass) | +| 封面 | `/cover.jpg`(含封面主文案,主文案来自 Brief) | +| 交付说明 | `/final-deliver.md`:素材来源与授权、各段实际时长、自检结果、弃用中间产物、fallback 决策、遗留问题 | + +交付时回报**三个文件的绝对路径**。模式 A 回报给 main agent(甲方自行取文件并做后续发布);模式 B 发给用户。本包不替甲方把成片拷进平台目录,也不代为发布。 + +## 工作区目录约定 + +**每个活儿自建工作区**,甲方不指定、也不代建(甲方只给 Brief 与素材绝对路径)。落在 Content Producer workspace 的 `output_videos//`;slug 取自 Brief 的视频名或主题英文短横线式,便于与甲方对账。 + +``` +output_videos// # +├── brief.md # 甲方交付(拷贝入档)或 Stage 0 与用户定稿 +├── voiceover.md # 甲方交付的口播文案(如有) +├── reference/ # 可选:甲方(多为模式 B 的用户)给的参考拆解报告与差异化概念 +│ ├── reference-report.md +│ └── concepts.md +├── script/ +│ ├── intent.json # Stage 1 +│ ├── story.md # Stage 2 +│ ├── script.md # Stage 3(甲方交付口播时 = 落稿锁定版) +│ ├── self-eval.json # Stage 3b 自评 +│ └── decisions.json # 决策审计链(跨阶段累积) +├── storyboard/ +│ ├── storyboard.json # Stage 4 镜头表 +│ └── shot_decompose.json # Stage 5 每镜首尾帧 + 运动 + variation_type +├── characters/ +│ ├── registry.json # Stage 6 static/dynamic features +│ └── /{front,side,back}.png +├── gates/ +│ ├── gate-a.md # GATE A 文本闸门评审产物(含批准人与批准范围) +│ └── gate-b.md # GATE B 素材闸门评审产物 +├── raw_materials/ # 甲方素材入库副本 + 授权记录 +├── slots/ +│ ├── slot-plan.json # Stage 7 +│ ├── asset-resolve.json # Stage 8(含 rejected_picks) +│ ├── slideshow-risk.json # Stage 9a 六维打分 +│ └── delivery-promise.json # Stage 9b 承诺锁定 +├── render/ +│ └── shot-NN/ # Stage 10 每镜渲染产物(first-frame.png / last-frame.png / shot.mp4) +├── audio/ +│ ├── narration.mp3 # 旁白(awk-tts 或甲方录音) +│ ├── narration-segments.json # 字级时间戳 +│ ├── bgm.mp3 +│ └── subtitles.srt +├── artifacts/ # Stage 12 按镜顺序的最终段(01_*.mp4 … NN_*.mp4) +├── video.mp4 # Stage 12 成片 +├── review/ +│ ├── verdict.json # Stage 13a 公共 video-review +│ ├── frames/ +│ └── motion-audit.json # Stage 13b +├── cover.jpg # Stage 14a +└── final-deliver.md # Stage 14b +``` + +Workflow 文档在技能包内,不是项目目录内容;项目目录只放 Brief、素材、脚本、渲染与交付产物。 + +## 通用制作流程(阶段链 Stage 0→14,两闸门) + +所有视频类型共用这条链;类型差异由 Workflow 文档补充。每段的子命令是 `video-producer` 工具下的一个独立脚本,按本流程逐个调。**产物文件存在性即 checkpoint**——子命令先查产物文件是否存在,存在则 load 不重生成(允许手改 JSON 后续跑)。 + +``` +Stage 0 Brief 确认 模式 A:读甲方 Brief,核对字段,缺口向 Brief owner 澄清 + 模式 B:用户未给 Brief 时引导讨论 → 代拟 brief.md → 发用户确认 + (两种模式的 Stage 0 都是"先把 Brief 定下来",无子命令) +Stage 1 intent-router 据 Brief 定档位(workflow 已指定类型时按 workflow 约束校验) + · 故事讲述型(narrative)——重情节、有人物弧光、含旁白;默认 3–5 镜/场 + · 纯画面动效型(motion)——重节奏感/视觉冲击/少对白;默认 5–8 镜快切 + · 蒙太奇剪接型(montage)——重氛围/抽象/纯视觉;默认 4–7 镜无叙事 +Stage 2 story-develop idea → 故事(受众/类型显式复述,100–200 词梗概,人物,分场) + 甲方已交付口播文案时跳过:叙事以口播稿为准,不另起故事 +Stage 3 script-write 故事 → 分场剧本(同时间同地点分一场;可拍化描述;enhancer 润色) + 甲方已交付口播文案时改为落稿锁定:原样落 script/script.md,不重写策略文案 +Stage 3b script-self-eval 脚本自评 N 维打分,任一维 <3 必返工(落稿锁定时只做检查,不改写) +Stage 4 storyboard-build 剧本 → 镜头表(每镜叙事目的/机位复用/位置朝向/不写不可见) +Stage 5 shot-decompose 每镜拆首帧静照/尾帧静照/运动描述(variation_type 三档) +Stage 6 character-register 角色三视图 front/side/back + static/dynamic features 拆分 + ────── GATE A:文本闸门(脚本+分镜+机位+角色全齐,停,发甲方审)────── +Stage 7 slot-plan 素材 slot 规划(template + hero slot + tone→slot 数) +Stage 8 asset-resolve 按 slot 取素材(Fast path:多源并发搜 + 缩略图人核 + rejected_picks 落盘) + 甲方已给素材时:先入库校验(可解码、分辨率/帧率/时长/音轨、授权记录),缺口才补搜 +Stage 9a slideshow-risk 六维幻灯风险打分(pre-compose 闸门,≥4.0 fail 不许进 compose) +Stage 9b delivery-promise-lock 交付承诺八类锁定 + motion_ratio 预估 + ────── GATE B:素材闸门(素材齐+计划过审,停,发甲方看 contact sheet)────── +Stage 10 render-shot 按 slot 渲染(AIGC 走 aigc-video-gen i2v 首尾帧插值;静图走 siliconflow-img-gen) +Stage 11 mix-audio 配音配乐四场景分流(A 人物对话声画同出 / B 旁白一次性 TTS 带字级时间戳 + 对齐 / + C BGM 成片后统一生成(优先 bgm-library 免版税曲库,pexels/pixabay 并列;定制风格用 + aigc-video-gen music)/ D 甲方口播录音 → ASR 时间戳 → 按时间戳补素材) +Stage 12 assemble 按序拼接成片(原子工具箱,见下节,agent 按场景组合,不写死流程) +Stage 13a video-review 公共 video-review 技术自检(强制闸门,verdict=pass 才继续) +Stage 13b motion-audit motion_led 抽查(兑付 delivery-promise) +Stage 13c normalize 响度归一化到 -14 LUFS(必跑,crew 级后期脚本,见「后期脚本」) +Stage 14a make-cover 封面(siliconflow-img-gen,必含封面主文案) +Stage 14b 交付 回报成片 + 封面 + final-deliver.md 的绝对路径与关键参数 +``` + +> Stage 0–6 全是**文本产物**,付费生成前必停——GATE A 落在这条边界上。GATE B 落在素材就绪、pre-compose 闸门通过后,确认渲染前最终计划。指定 Workflow 时以 Workflow 文档的阶段映射为准;若甲方已在 Brief 中代理批准 GATE A,记录批准范围后继续。 + +可选工具:`reference-concepts`(甲方给了参考视频拆解报告时,据报告出 2–3 个差异化概念落 `reference/concepts.md`)。本包不自己下载视频、不自己做转录与抽帧。 + +## Stage 12 工具箱(场景化组合,不写死流程) + +Stage 12 不规定固定顺序——下面的原子工具由 agent 按实际场景组合。 + +| 工具 | 干什么 | 关键参数 | +|------|--------|---------| +| `clip-trim` | 精确切素材段(入点/出点/倍速/前置缓冲,视频和音频分别处理) | `--input/--output/--start/--end/--speed/--sync-audio/--pre-buffer` | +| `audio-mix` | 多轨混音(每轨独立延时和音量) | `--track(可重复)/--delay/--volume/--output/--duration` | +| `timeline-compose` | 按时间轴 JSON 调 clip-trim + audio-mix 合成片段 | ` --timeline timeline.json [--transition ...]` | +| `assemble` | 按序拼接已就绪的段 + 可选转场 + 自动分辨率归一化 + 自动统一音频格式 | ` [--transition hard/fade/dissolve/xfade] [--width 1080] [--fps 30] [--audio-format 24000/mono] [--low-memory] [--preview-duration 30]` | +| `add-silent-audio` | 给无音频视频片段补静音音轨(concat 前置,assemble 内部也自动调) | `--input/--output/--duration/--sample-rate 24000/--channels mono` | +| `scene-compose` | 单 Scene 分段合成(片段+旁白+对白 → 一个 Scene 片段) | ` --scene scene.json [--output scene-01.mp4]` | +| `make-outro` | 片尾制作(形象图+黑边+烧字幕+静音轨 → 标准比例片尾) | ` --image <形象图> --slogan <文本> [--color color.json] [--duration 5] [--width 1080] [--fps 30]` | + +### 场景化组合示例(非强制,按实际判断) + +**场景 A:无旁白直拼** —— 段就绪、无需切素材与混音:`assemble --transition fade` 一把过。 + +**场景 B:有旁白走时间轴** —— 旁白一次性 TTS + `narration-align` 拿字级时间戳后按时间戳对齐各段素材: +1. 据 `narration-segments.json` 各段 start/end 定素材入点/出点,写 `timeline.json` +2. `timeline-compose --timeline timeline.json`(内部调 clip-trim 切段 + audio-mix 叠旁白) +3. 全片 BGM 走 `timeline.json` 的 `audio_globals` 混入 + +**场景 C:分段先合再合(scene-compose 两阶段)** —— 长片或某些段需独立预合: +1. 写 `scene-01.json`(clips + narration + dialogue)→ `scene-compose --scene scene-01.json --output scene-01.mp4` +2. 同样出 `scene-02.mp4` +3. 两个 scene 当段素材 → `assemble --source-dir scenes --transition fade` + +**场景 D:素材尺寸不一** —— AIGC 720x1280、录屏 1080x2384、片尾 784x1176 混拼:`assemble --width 1080 --fps 30` 归一化后再 concat。 + +**场景 E:精确调速某段** —— `clip-trim --input <段> --output <快放段> --speed 2 --sync-audio`,快放段当段素材再拼。 + +**场景 F:低内存机器** —— `assemble --transition fade --low-memory`(preset=ultrafast、crf=28),避免 x264 缓冲爆内存。 + +**场景 G:先试听再合成** —— `assemble --preview-duration 30`,成片照常落,额外产 `video-preview.mp4`。 + +**场景 H:AIGC 无音频段混拼 + 旁白切段吞首字** —— `assemble` 已自动统一音频格式(默认 24000/mono,无音频段补静音)+ 不传 `--width/--fps` 时自动探测并统一到最低公共规格;旁白切段走 `clip-trim --pre-buffer 0.5`。 + +## 工具调用 + +- `video-producer <子命令>`:视频制作原子能力(意图路由、故事/剧本/分镜、素材 slot 与解析、渲染、混音对齐、拼接合成、动效审计、封面)。**完整子命令的入参、产物路径与退出码见 `video-producer` 工具说明**,不在本文重复。 +- `collage-broll check-setup` / `collage-broll gate3 --batch [--dry-run]`:纸拼贴 B-roll 的环境自检与 Gate 3 批量 i2v 调度(0 全通 / 1 参数错 / 2 部分失败,只重跑失败条目)。 +- 后期脚本(crew 级,Workspace `scripts/`):`normalize.py`(**必跑**,-14 LUFS)、`burn-srt.py`、`duck.py`、`denoise.py`、`interp.py`(后四个按 Brief 与素材状况可选)。从 workspace 根 `python3 scripts/.py` 调用,全部干湿分离不覆盖输入;落点与旁路条件见 `video-producer` 工具说明。 + +## 强制闸门与护栏 + +### GATE A(Stage 6 后):文本闸门 + +文本产物全齐(脚本+分镜+机位+角色),**停下发甲方审**: + +- 呈交摘要:档位或 workflow、场次数、镜数、角色数、关键决策(路径/模型/风格选择的备选+置信度+理由) +- **结束本轮回复**,不许在同条回复里进 Stage 7 +- 批准人是 Brief owner(模式 A = main agent,模式 B = 用户);甲方已在 Brief 中代理批准时,把批准范围落 `gates/gate-a.md` 后继续 +- 批准是**逐闸门的**——早先的一句"你继续"不覆盖本闸门 +- 要改哪段就重跑对应子命令(产物文件存在性即 checkpoint,不会重生成未改的) + +### GATE B(Stage 9 后):素材闸门 + +素材齐 + 计划过 slideshow_risk + delivery_promise 锁,**停下发甲方看 contact sheet**: + +- 呈交:slot 总数、素材就绪率、slideshow_risk 六维分与 verdict、delivery_promise 八类与 motion_ratio 预估、素材 contact sheet +- 授权与来源记录必须一并呈交 +- 同 GATE A 收尾纪律 + +### 返工与耗时上限 + +- 每阶段最多返工 **3 次**;全片最多 **3 次** send-back +- 每阶段 wall-time 默认上限 **20 分钟**——卡住要报,不要反复撞 +- 技术故障(缺 key、依赖缺失、渲染报错)按 Dispatch Protocol spawn IT engineer,不静默卡死 + +### 决策审计链 + +每个选择(路径/模型/风格/音色/任何 fallback)记 `备选 + 置信度 + 理由`,跨阶段累积进 `script/decisions.json`。 + +## 依赖 + +| 依赖 | 来源 | 用在哪 | +|------|------|------| +| python3 / ffmpeg / ffprobe | 系统 | 各阶段脚本 | +| 公共 `aigc-video-gen` | skills/ | Stage 8/10 视频片段生成(百炼/火山声画同出,i2v 首尾帧插值) | +| 公共 `siliconflow-img-gen` | skills/ | Stage 6 角色三视图 / Stage 10 静帧 / Stage 14a 封面 | +| 公共 `awk-tts` | skills/ | Stage 11B 旁白一次性 TTS(OpenClaw 内置 TTS 优先 → awk-tts fallback;`--enable-subtitle` 让火山流式 HTTP 原生返回字级时间戳,落 `narration.subtitle.json`) | +| 火山 ASR 凭据 `VOLC_ASR_*` | 实例 env | Stage 11b narration-align 回退路径 + Stage 11D 甲方口播录音转写拿时间戳(旧控制台双头 `VOLC_ASR_APP_ID`+`VOLC_ASR_ACCESS_KEY`,或新控制台单头 `VOLC_ASR_APP_KEY`) | +| 公共 `pexels-footage` / `pixabay-footage` | skills/ | Stage 8 素材补充 / Stage 11C BGM 搜 | +| 公共 `bgm-library` | skills/ | Stage 11C BGM(ccMixter 免版税 + 自动 TASL 署名,商用安全,优先用) | +| 公共 `video-review` | skills/ | Stage 13a 成片技术自检闸门 | +| `video-edit subtitles` | main crew 暴露的 wrapper 原子 | 需要烧字幕时使用;不可用时向 Brief owner 报工具缺口,不手写 ffmpeg | +| `requests` | 仓根 requirements.txt | 各脚本 HTTP 调用 | + +## 禁止事项(强制) + +- **禁止跳过 GATE A/B 交付**:两闸门是流程的一部分,呈交摘要后必须结束本轮回复等甲方批 +- **禁止跳过 video-review 与响度归一化交付**:Stage 13a verdict=pass、Stage 13c 归一化已跑,才进 Stage 14 +- **禁止声称没做过的事**:没有 tool result 或产物文件证明,不许声称已渲染/已生成/已改动 +- **禁止替甲方做需求决策**:选题方向、品牌事实、卖点承诺、发布文案不由本包定;Brief 没写就问 +- **禁止让甲方建工作区**:工作区自建;也不要把中间产物写进甲方(main / 用户)的目录 +- **禁止把模糊想法擅自扩成多场多镜**:默认 1 场 3–5 镜,甲方要扩才扩 +- **禁止直接写 ffmpeg 命令**:所有 ffmpeg 调用走 `video-producer` / `collage-broll` 子命令、crew 级后期脚本或公共技能子命令;唯一例外是 Workflow 里给出的既定 ffmpeg 模板(如 Collage B-roll 的首尾帧处理与 contact sheet 拼图),照抄执行不自创 +- **禁止自己做视频下载/转写/抽帧**:那是 main 的 `viral-chaser` 的活 +- **禁止引入 CLIP / torch 系本地模型**:素材匹配走 Fast path 人核缩略图 +- **禁止扩充图库源**:保 Pexels + Pixabay 两源 +- **禁止批量生成撞运气**:逐条精做 diff --git a/crews/content-producer/skills/expert-video/tools/collage-broll/SKILL.md b/crews/content-producer/skills/expert-video/tools/collage-broll/SKILL.md new file mode 100644 index 00000000..a4383138 --- /dev/null +++ b/crews/content-producer/skills/expert-video/tools/collage-broll/SKILL.md @@ -0,0 +1,32 @@ +--- +name: collage-broll +description: 纸拼贴 B-roll 的原子工具——环境自检与 Gate 3 批量 i2v 视频生成调度。 +--- + +# collage-broll — 工具说明 + +> 本文是 `expert-video` 专家包内的工具说明书,不独立出现在技能列表中。制作流程(视觉隐喻 → 静帧 → 视频三道闸门)由包内 Collage B-roll Workflow 编排。 + +**用途**:代理纸拼贴 B-roll 的两个脚本,让 agent 走 PATH 调用、零路径拼接。 + +**输入 / 输出**: + +| 子命令 | 入 | 出 | 退出码 | +|--------|----|----|--------| +| `collage-broll check-setup` | 无(读环境变量与 PATH) | stdout 逐项 PASS/FAIL | 0 全通 / 1 有缺项 | +| `collage-broll gate3` | `--batch /gen-jobs.json`(每条含 prompt / first_frame / last_frame / output / ratio / resolution / duration),可选 `--dry-run` | 逐条调公共 `aigc-video-gen` i2v 落 MP4 + decisions.log | 0 全通 / 1 参数错、jobs 文件不存在或格式错、`aigc-video-gen` 不在 PATH / 2 部分 job 失败(stderr 报失败清单,已跑通的保留) | + +**调用方式**: + +```bash +collage-broll check-setup +collage-broll gate3 --batch output_videos//gen-jobs.json --dry-run +collage-broll gate3 --batch output_videos//gen-jobs.json +``` + +**注意事项**: + +- `gate3` 串行调度:视频生成是异步轮询任务,并行会撞平台并发限。 +- `gen-jobs.json` 的 `output` 必须是相对 workspace 根、且落在 `output_videos/` 下的路径(`aigc-video-gen` 的 ensure_safe_output 要求);调用时 workdir 必须是 Content Producer workspace 根。 +- 依赖:`ffmpeg` / `ffprobe` / Python ≥ 3.10;`AWK_API_KEY`(Gate 2 静帧);`MODELSTUDIO_API_KEY` 或 `DASHSCOPE_API_KEY`(百炼)或 `AWK_GEN_KEY`(火山)。缺项由 `check-setup` 报出,补齐属 IT engineer 职责。 +- 模型候选链 fallback 与 decisions.log 由 `aigc-video-gen` 自带,本工具不重复实现。 diff --git a/crews/content-producer/skills/expert-video/tools/collage-broll/collage-broll.sh b/crews/content-producer/skills/expert-video/tools/collage-broll/collage-broll.sh new file mode 100755 index 00000000..60d36095 --- /dev/null +++ b/crews/content-producer/skills/expert-video/tools/collage-broll/collage-broll.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +# collage-broll.sh — collage-broll 工具 wrapper(薄转发,子命令范式) +# 让 agent 用 `collage-broll <子命令> [参数...]` 走 PATH,零路径拼接。 +# 子命令: +# check-setup 环境自检(ffmpeg/ffprobe/AWK_API_KEY/视频平台 key/python 版本) +# gate3 --batch [--dry-run] Gate 3 批量调度 i2v 生成 +# 纸拼贴 B-roll 的完整制作流程见 expert-video 包内 Collage B-roll Workflow。 +set -euo pipefail +SELF="${BASH_SOURCE[0]}" +# Resolve symlink (wrapper is ln -sfn'd into ~/.openclaw/bin) so SCRIPT_DIR points at the real tool dir. +while [ -L "$SELF" ]; do SELF="$(readlink -f "$SELF")"; done +SCRIPT_DIR="$(cd "$(dirname "$SELF")" && pwd)" + +SUBCMD="${1:?用法: collage-broll [参数...]}" +shift +case "$SUBCMD" in + check-setup) + exec bash "$SCRIPT_DIR/scripts/check_setup.sh" "$@" + ;; + gate3) + exec python3 "$SCRIPT_DIR/scripts/run_gate3.py" "$@" + ;; + -h|--help|help) + cat <<'HELP' +collage-broll — 纸拼贴 B-roll 原子工具(wrapper) + +用法: + collage-broll check-setup 环境自检(依赖与 key) + collage-broll gate3 --batch Gate 3 批量 i2v 生成(串行调 aigc-video-gen) + collage-broll gate3 --batch <...> --dry-run 只打印调度计划不真调 + collage-broll help 本帮助 + +退出码: + check-setup 0 全通 / 1 有缺项 + gate3 0 全部 job 跑通 / 1 参数错或 wrapper 缺失 / 2 部分 job 失败(已跑通的保留) + +制作流程(隐喻 → 静帧 → 视频三道闸门)见 expert-video 包内 Collage B-roll Workflow; +本 wrapper 只代理脚本,不含流程语义。 +HELP + ;; + *) + echo "未知子命令: $SUBCMD" >&2 + echo "用 collage-broll help 查可用子命令" >&2 + exit 1 + ;; +esac diff --git a/crews/content-producer/skills/collage-broll/scripts/check_setup.sh b/crews/content-producer/skills/expert-video/tools/collage-broll/scripts/check_setup.sh similarity index 100% rename from crews/content-producer/skills/collage-broll/scripts/check_setup.sh rename to crews/content-producer/skills/expert-video/tools/collage-broll/scripts/check_setup.sh diff --git a/crews/content-producer/skills/collage-broll/scripts/run_gate3.py b/crews/content-producer/skills/expert-video/tools/collage-broll/scripts/run_gate3.py similarity index 100% rename from crews/content-producer/skills/collage-broll/scripts/run_gate3.py rename to crews/content-producer/skills/expert-video/tools/collage-broll/scripts/run_gate3.py diff --git a/crews/content-producer/skills/expert-video/tools/video-producer/SKILL.md b/crews/content-producer/skills/expert-video/tools/video-producer/SKILL.md new file mode 100644 index 00000000..079e4fcd --- /dev/null +++ b/crews/content-producer/skills/expert-video/tools/video-producer/SKILL.md @@ -0,0 +1,76 @@ +--- +name: video-producer +description: 视频制作原子能力集——意图路由、故事/剧本/分镜、素材 slot 与解析、渲染、混音对齐、拼接合成、动效审计、封面。子命令范式,产物文件存在性即 checkpoint。 +metadata: + openclaw: + emoji: 🎬 + requires: + bins: + - python3 + - ffmpeg + - ffprobe +--- + +# video-producer — 工具说明 + +> 本文是 `expert-video` 专家包内的工具说明书,不独立出现在技能列表中。制作流程(阶段链、两闸门、workflow 选择)由包内 SKILL.md 与 `workflows/` 编排,本文只写每个子命令的输入、输出与调用方式。 + +**调用方式**:`video-producer <子命令> [参数...]`(wrapper 转发到 `scripts/<子命令>.py`,子命令名即脚本名,零路径拼接)。`video-producer help` 列可用子命令。 + +**通用约定**: + +- 多数子命令第一个位置参数是 ``(工作区目录),产物落该目录下约定子路径。 +- **产物文件存在性即 checkpoint**:子命令先查产物文件是否存在,存在则 load 不重生成(允许手改 JSON 后续跑)。 +- 退出码:`0` 成功 / `1` 参数错 / `2` env 未配(如 `AWK_API_KEY`、`VOLC_ASR_*`)。 + +## 子命令清单 + +| 子命令 | 入 | 出 | 用途 | +|--------|----|----|------| +| `intent-router` | brief.md(主题/关键词/类型) | `script/intent.json`(档位+主题) | 意图路由三档:故事讲述型 narrative / 纯画面动效型 motion / 蒙太奇剪接型 montage | +| `reference-concepts` | 甲方给的参考拆解报告(可选) | `reference/concepts.md` | 据报告出 2–3 个差异化概念;不做下载/转写/抽帧 | +| `story-develop` | intent.json | `script/story.md` | idea → 故事(受众/类型复述、100–200 词梗概、人物、分场) | +| `script-write` | story.md | `script/script.md`(含 enhancement_cues 六型 + delivery_cues) | 故事 → 分场剧本(同时间同地点分一场、可拍化描述、enhancer 润色) | +| `script-self-eval` | script.md | `script/self-eval.json` | 脚本自评 N 维打分,任一维 <3 必返工 | +| `storyboard-build` | script.md | `storyboard/storyboard.json` | 剧本 → 镜头表(每镜叙事目的/机位复用/位置朝向/不写不可见) | +| `shot-decompose` | storyboard.json | `storyboard/shot_decompose.json` | 每镜拆首帧静照/尾帧静照/运动描述(variation_type 三档) | +| `character-register` | storyboard.json + brief.md | `characters/registry.json` + 三视图 png | 角色 static/dynamic features 拆分 + front/side/back(调 `siliconflow-img-gen`) | +| `slot-plan` | storyboard.json + shot_decompose.json | `slots/slot-plan.json` | 素材 slot 规划(template + hero slot + tone→slot 数) | +| `asset-resolve` | slot-plan.json | `slots/asset-resolve.json`(含 rejected_picks)+ 素材落 `raw_materials/` | 按 slot 拉素材(Fast path:多源并发搜 + 缩略图人核;调 pexels-footage / pixabay-footage / aigc-video-gen) | +| `slideshow-risk` | storyboard.json + slot-plan.json + asset-resolve.json | `slots/slideshow-risk.json` | 六维幻灯风险打分(pre-compose 闸门,≥4.0 fail) | +| `delivery-promise-lock` | storyboard.json + brief.md | `slots/delivery-promise.json` | 交付承诺八类锁定 + motion_ratio 预估 | +| `render-shot` | shot_decompose.json + characters/ + slot-picks | `render/shot-NN/` 下产物 | 按 slot 渲染(AIGC 走 `aigc-video-gen` i2v 首尾帧插值;静图走 `siliconflow-img-gen`) | +| `mix-audio` | script.md(delivery_cues) | `audio/` 目录 + `subtitles.srt` 模板 | 配音配乐四场景分流:A 人物对话声画同出 / B 旁白一次性 TTS 带字级时间戳 + 对齐 / C BGM 成片后统一生成 / D 甲方口播录音 → ASR 时间戳 → 按时间戳补素材 | +| `narration-align` | audio/narration.mp3 + audio/narration.subtitle.json | `audio/narration-segments.json` | 旁白字级时间戳对齐(优先复用 `awk-tts --enable-subtitle` 的原生时间戳,缺失时回退火山 ASR 极速版,凭据 `VOLC_ASR_*`) | +| `clip-trim` | `--input/--output/--start/--end/--speed/--sync-audio/--pre-buffer` | 切好的片段 | 精确切素材段(入点/出点/倍速/前置缓冲,视频与音频分别处理;`--pre-buffer 0.5` 防切 MP3 吞首字) | +| `audio-mix` | `--track(可重复)/--delay/--volume/--output/--duration` | 混合音频 | 多轨混音(每轨独立延时与音量) | +| `timeline-compose` | ` --timeline timeline.json [--transition ...]` | 合成片段 | 按时间轴 JSON 调 clip-trim + audio-mix 合成(`audio_mode=concat` 出连续轨;`audio_globals` 混全片 BGM) | +| `scene-compose` | ` --scene scene.json [--output scene-01.mp4]` | 单 Scene 片段 | 分段合成(clips + narration + dialogue → 一个 Scene);内部调 clip-trim + audio-mix + assemble | +| `assemble` | ` [--transition hard/fade/dissolve/xfade] [--width] [--fps] [--audio-format] [--low-memory] [--preview-duration] [--source-dir]` | `video.mp4`(+ 可选 `video-preview.mp4`) | 按序拼接成片:可选转场、分辨率/帧率归一化、自动统一音频格式(无音频段补静音)、低内存模式(ultrafast/crf28)、前 N 秒试听版 | +| `add-silent-audio` | `--input/--output/--duration/--sample-rate/--channels` | 含静音音轨的视频 | 给无音频片段补静音轨(concat 前置;assemble 内部也自动调) | +| `make-outro` | ` --image <形象图> --slogan <文本> [--color color.json] [--duration 5] [--width 1080] [--fps 30]` | 标准比例片尾段 | 形象图 + 黑边 + 烧字幕 + 静音轨 | +| `motion-audit` | video.mp4 + delivery-promise.json | `review/motion-audit.json` | motion_led 抽查(兑付交付承诺) | +| `make-cover` | brief.md(封面主文案)+ storyboard 关键帧 | `cover.jpg` | 封面生成(调 `siliconflow-img-gen`,必含封面主文案) | + +## 注意事项 + +- **不自己下载/转写/抽帧**:参考视频拆解归 main 的 `viral-chaser`;本工具只吃甲方给的报告或素材。 +- **不引入 CLIP / torch 系本地模型**:素材匹配走 Fast path 人核缩略图。 +- **图库源固定**:Pexels + Pixabay 两源,不扩充。 +- **AIGC 输出路径约束**:`aigc-video-gen` 要求输出相对路径落在 `output_videos/` 下,调用时 workdir 必须是 Content Producer workspace 根。 +- **env 依赖**:`AWK_API_KEY`(静帧/视频生成)、`VOLC_ASR_*`(narration-align 回退与口播录音转写)。缺 env 时子命令 exit 2,补齐属 IT engineer 职责,不要静默降级。 +- **闸门不是子命令**:GATE A / GATE B 由 agent 按包内 SKILL.md 执行(呈交摘要 → 结束本轮回复 → 等甲方逐闸门批准)。 + +## 后期脚本(crew 级,Workspace `scripts/`) + +以下五个不在本工具 wrapper 内,从 Content Producer workspace 根按 `python3 scripts/.py` 调用;全部干湿分离(输出落 `_<处理名>.mp4`,不覆盖输入)。 + +| 脚本 | 用途 | 必跑/可选 | 落点 | +|------|------|----------|------| +| `normalize.py` | ffmpeg loudnorm 双 pass 归一化到 -14 LUFS(抖音/视频号/B站竖屏通用) | **必跑** | 成片合成后、自检与交付前 | +| `burn-srt.py` | libass 把 SRT 硬烧进画面 | 可选(Brief 或甲方要字幕时) | 归一化前后均可,串联时以上一步产物为输入 | +| `duck.py` | sidechaincompress 让旁白触发 BGM 自动压低 | 可选(要专业混音且可分轨时) | 混音阶段 | +| `denoise.py` | afftdn(默认)/ arnndn 去环境噪声 | 可选(仅甲方素材音质差时;AIGC 音轨干净跳过) | 素材入库后 | +| `interp.py` | minterpolate 补帧到 30/60fps | 可选(仅低 fps 源材) | 渲染或拼接前 | + +旁路条件:`normalize.py` 无声轨/音频畸变 → exit 2 退回重生,已在 ±0.3 LUFS 内 → 自动跳过;`burn-srt.py` ffmpeg 无 libass → exit 1 改发外挂 SRT;`duck.py` 声画同出混轨不可分 → 报甲方决策;`interp.py` 源 fps ≥ 目标 → 自动跳过拷贝。 diff --git a/crews/content-producer/skills/video-producer/scripts/add-silent-audio.py b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/add-silent-audio.py similarity index 100% rename from crews/content-producer/skills/video-producer/scripts/add-silent-audio.py rename to crews/content-producer/skills/expert-video/tools/video-producer/scripts/add-silent-audio.py diff --git a/crews/content-producer/skills/video-producer/scripts/assemble.py b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/assemble.py similarity index 99% rename from crews/content-producer/skills/video-producer/scripts/assemble.py rename to crews/content-producer/skills/expert-video/tools/video-producer/scripts/assemble.py index 7dc5e10d..9c01ce38 100644 --- a/crews/content-producer/skills/video-producer/scripts/assemble.py +++ b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/assemble.py @@ -281,7 +281,7 @@ def concat_xfade(segments: list[tuple[str, Path, float]], out: Path, transition: def main() -> None: parser = argparse.ArgumentParser(description="Stage 12 assemble 按序拼接") - parser.add_argument("project_dir", help="项目目录(output_videos// 或平台运营目录 /outputs//)") + parser.add_argument("project_dir", help="项目目录(CP 自建工作区 output_videos//)") parser.add_argument("--source-dir", default=None, help="段目录(默认 render/;timeline-compose 调时传 artifacts/timeline/)") parser.add_argument("--output", default="video.mp4", help="输出名(相对 project_dir,默认 video.mp4)") parser.add_argument("--transition", default="hard", choices=sorted(VALID_TRANSITIONS)) diff --git a/crews/content-producer/skills/video-producer/scripts/asset-resolve.py b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/asset-resolve.py similarity index 96% rename from crews/content-producer/skills/video-producer/scripts/asset-resolve.py rename to crews/content-producer/skills/expert-video/tools/video-producer/scripts/asset-resolve.py index fc8d21c8..b5882bba 100644 --- a/crews/content-producer/skills/video-producer/scripts/asset-resolve.py +++ b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/asset-resolve.py @@ -37,7 +37,7 @@ def die(msg: str) -> None: def main() -> None: parser = argparse.ArgumentParser(description="Stage 8 asset-resolve") - parser.add_argument("project_dir", help="项目目录(output_videos// 或平台运营目录 /outputs//)") + parser.add_argument("project_dir", help="项目目录(CP 自建工作区 output_videos//)") parser.add_argument("--source", default="both", choices=["pexels", "pixabay", "both"]) parser.add_argument("--no-confirm", action="store_true", help="agent 已人核完毕,不再呈交") args = parser.parse_args() diff --git a/crews/content-producer/skills/video-producer/scripts/audio-mix.py b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/audio-mix.py similarity index 100% rename from crews/content-producer/skills/video-producer/scripts/audio-mix.py rename to crews/content-producer/skills/expert-video/tools/video-producer/scripts/audio-mix.py diff --git a/crews/content-producer/skills/video-producer/scripts/character-register.py b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/character-register.py similarity index 96% rename from crews/content-producer/skills/video-producer/scripts/character-register.py rename to crews/content-producer/skills/expert-video/tools/video-producer/scripts/character-register.py index f861e360..943cf7c5 100644 --- a/crews/content-producer/skills/video-producer/scripts/character-register.py +++ b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/character-register.py @@ -29,7 +29,7 @@ def die(msg: str) -> None: def main() -> None: parser = argparse.ArgumentParser(description="Stage 6 character-register") - parser.add_argument("project_dir", help="项目目录(output_videos// 或平台运营目录 /outputs//)") + parser.add_argument("project_dir", help="项目目录(CP 自建工作区 output_videos//)") args = parser.parse_args() project = Path(args.project_dir).resolve() diff --git a/crews/content-producer/skills/video-producer/scripts/clip-trim.py b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/clip-trim.py similarity index 100% rename from crews/content-producer/skills/video-producer/scripts/clip-trim.py rename to crews/content-producer/skills/expert-video/tools/video-producer/scripts/clip-trim.py diff --git a/crews/content-producer/skills/video-producer/scripts/delivery-promise-lock.py b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/delivery-promise-lock.py similarity index 95% rename from crews/content-producer/skills/video-producer/scripts/delivery-promise-lock.py rename to crews/content-producer/skills/expert-video/tools/video-producer/scripts/delivery-promise-lock.py index 637bf68b..dfc65dc6 100644 --- a/crews/content-producer/skills/video-producer/scripts/delivery-promise-lock.py +++ b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/delivery-promise-lock.py @@ -33,7 +33,7 @@ def die(msg: str) -> None: def main() -> None: parser = argparse.ArgumentParser(description="Stage 9b delivery-promise-lock") - parser.add_argument("project_dir", help="项目目录(output_videos// 或平台运营目录 /outputs//)") + parser.add_argument("project_dir", help="项目目录(CP 自建工作区 output_videos//)") args = parser.parse_args() project = Path(args.project_dir).resolve() diff --git a/crews/content-producer/skills/video-producer/scripts/intent-router.py b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/intent-router.py similarity index 84% rename from crews/content-producer/skills/video-producer/scripts/intent-router.py rename to crews/content-producer/skills/expert-video/tools/video-producer/scripts/intent-router.py index 0415762f..c8f673d7 100644 --- a/crews/content-producer/skills/video-producer/scripts/intent-router.py +++ b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/intent-router.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""Stage 0 — intent-router:把用户意图路由成三档脚本模板。 +"""Stage 1 — intent-router:把 Brief 意图路由成三档脚本模板。 三档(内部 key 保留英文,user-facing 表述用中文名): - narrative(故事讲述型)——重情节、有人物弧光、含旁白 → 默认 3–5 镜/场 @@ -7,9 +7,9 @@ - montage(蒙太奇剪接型)——重氛围/抽象/纯视觉 → 默认 4–7 镜无叙事 Usage: - python3 scripts/intent-router.py [--user-text "..."] [--report-file path] + video-producer intent-router [--user-text "..."] [--report-file path] -入:project_dir(output_videos// 或平台运营目录 /outputs//),可选用户原文或 viral-chaser 报告路径 +入:project_dir(CP 自建工作区 output_videos//),可选甲方原文或参考拆解报告路径 出:project_dir/script/intent.json(档位 + 主题 + 受众 + 时长目标 + 备选 + 决策理由) 产物文件存在性即 checkpoint:intent.json 已存在则打印现状退出,不重生成(用户手改后续跑)。 @@ -43,10 +43,10 @@ def detect_genre(text: str) -> tuple[str, str]: def main() -> None: - parser = argparse.ArgumentParser(description="Stage 0 intent-router") - parser.add_argument("project_dir", help="项目目录(output_videos// 或平台运营目录 /outputs//)") - parser.add_argument("--user-text", default=None, help="用户原文") - parser.add_argument("--report-file", default=None, help="main 喂入的 viral-chaser 报告路径(可选)") + parser = argparse.ArgumentParser(description="Stage 1 intent-router") + parser.add_argument("project_dir", help="项目目录(CP 自建工作区 output_videos//)") + parser.add_argument("--user-text", default=None, help="甲方原文(Brief 主题句或用户描述)") + parser.add_argument("--report-file", default=None, help="甲方给的参考拆解报告路径(可选)") parser.add_argument("--genre", default=None, choices=sorted(VALID_GENRES), help="强制档位,跳过自动判定") parser.add_argument("--duration", type=int, default=None, help="时长目标(秒),不传走档位默认") parser.add_argument("--audience", default=None, help="受众描述") diff --git a/crews/content-producer/skills/video-producer/scripts/make-cover.py b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/make-cover.py similarity index 85% rename from crews/content-producer/skills/video-producer/scripts/make-cover.py rename to crews/content-producer/skills/expert-video/tools/video-producer/scripts/make-cover.py index df813a09..60b679c0 100644 --- a/crews/content-producer/skills/video-producer/scripts/make-cover.py +++ b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/make-cover.py @@ -7,7 +7,7 @@ 入:project_dir/brief.md(封面主文案)+ storyboard 关键帧 出:project_dir/cover.jpg(含封面主文案的封面图) -封面硬约束:必含封面主文案。平台有标题时可用标题;视频号无标题时用核心传达。siliconflow-img-gen 不一定能把中文封面主文案烤进图, +封面硬约束:必含封面主文案。主文案由甲方在 Brief 中给出(有平台标题时用标题,视频号用短标题);Brief 未给时回退核心传达。siliconflow-img-gen 不一定能把中文封面主文案烤进图, agent 生成后用 image 工具看,确认封面主文案可见——不可见就用 ImageMagick/Pillow 烧字上去。 """ @@ -24,7 +24,7 @@ def die(msg: str) -> None: def main() -> None: parser = argparse.ArgumentParser(description="Stage 14a make-cover") - parser.add_argument("project_dir", help="项目目录(output_videos// 或平台运营目录 /outputs//)") + parser.add_argument("project_dir", help="项目目录(CP 自建工作区 output_videos//)") parser.add_argument("--title", default=None, help="封面主文案,不传则从 brief.md 抽") args = parser.parse_args() diff --git a/crews/content-producer/skills/video-producer/scripts/make-outro.py b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/make-outro.py similarity index 97% rename from crews/content-producer/skills/video-producer/scripts/make-outro.py rename to crews/content-producer/skills/expert-video/tools/video-producer/scripts/make-outro.py index af27922a..5545ae35 100644 --- a/crews/content-producer/skills/video-producer/scripts/make-outro.py +++ b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/make-outro.py @@ -77,7 +77,7 @@ def hex_to_ffmpeg_color(hex_color: str) -> str: def main() -> None: parser = argparse.ArgumentParser(description="make-outro 片尾制作") - parser.add_argument("project_dir", help="项目目录(output_videos// 或平台运营目录 /outputs//)") + parser.add_argument("project_dir", help="项目目录(CP 自建工作区 output_videos//)") parser.add_argument("--image", required=True, help="形象图路径(PNG/JPG)") parser.add_argument("--slogan", required=True, help="slogan 文本(烧录到画面中央)") parser.add_argument("--color", default=None, help="颜色配置 JSON 路径") diff --git a/crews/content-producer/skills/video-producer/scripts/mix-audio.py b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/mix-audio.py similarity index 97% rename from crews/content-producer/skills/video-producer/scripts/mix-audio.py rename to crews/content-producer/skills/expert-video/tools/video-producer/scripts/mix-audio.py index ffcf20a0..df734fb7 100644 --- a/crews/content-producer/skills/video-producer/scripts/mix-audio.py +++ b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/mix-audio.py @@ -40,7 +40,7 @@ def main() -> None: parser = argparse.ArgumentParser(description="Stage 11 mix-audio") - parser.add_argument("project_dir", help="项目目录(output_videos// 或平台运营目录 /outputs//)") + parser.add_argument("project_dir", help="项目目录(CP 自建工作区 output_videos//)") args = parser.parse_args() project = Path(args.project_dir).resolve() diff --git a/crews/content-producer/skills/video-producer/scripts/motion-audit.py b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/motion-audit.py similarity index 96% rename from crews/content-producer/skills/video-producer/scripts/motion-audit.py rename to crews/content-producer/skills/expert-video/tools/video-producer/scripts/motion-audit.py index beabb671..de669562 100644 --- a/crews/content-producer/skills/video-producer/scripts/motion-audit.py +++ b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/motion-audit.py @@ -32,7 +32,7 @@ def die(msg: str) -> None: def main() -> None: parser = argparse.ArgumentParser(description="Stage 13b motion-audit") - parser.add_argument("project_dir", help="项目目录(output_videos// 或平台运营目录 /outputs//)") + parser.add_argument("project_dir", help="项目目录(CP 自建工作区 output_videos//)") args = parser.parse_args() project = Path(args.project_dir).resolve() diff --git a/crews/content-producer/skills/video-producer/scripts/narration-align.py b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/narration-align.py similarity index 98% rename from crews/content-producer/skills/video-producer/scripts/narration-align.py rename to crews/content-producer/skills/expert-video/tools/video-producer/scripts/narration-align.py index 51362d02..bbaa6437 100644 --- a/crews/content-producer/skills/video-producer/scripts/narration-align.py +++ b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/narration-align.py @@ -154,7 +154,7 @@ def main() -> None: load_env_file() parser = argparse.ArgumentParser(description="Stage 11b narration-align") - parser.add_argument("project_dir", help="项目目录(output_videos// 或平台运营目录 /outputs//)") + parser.add_argument("project_dir", help="项目目录(CP 自建工作区 output_videos//)") parser.add_argument( "--audio", default=None, diff --git a/crews/content-producer/skills/expert-video/tools/video-producer/scripts/reference-concepts.py b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/reference-concepts.py new file mode 100644 index 00000000..dda6117d --- /dev/null +++ b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/reference-concepts.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python3 +"""reference-concepts(可选工具)— 吃甲方给的参考拆解报告出 2–3 差异化概念。 + +本工具不做视频下载/转写/抽帧——那是 main 的 viral-chaser 的活。只接报告原档当输入。 +默认流程不含本阶段:仅当甲方(多为直接对话模式下的用户)提供了参考拆解报告时才用。 + +Usage: + video-producer reference-concepts --report-file path + +入:project_dir(CP 自建工作区 output_videos//)+ 参考拆解报告路径 +出:project_dir/reference/concepts.md(2–3 差异化概念 + 成本 + 备选路径) + +无报告则跳过(本脚本不报错退出),agent 直接按通用阶段链推进。 +""" + +import argparse +import sys +from pathlib import Path + +def main() -> None: + parser = argparse.ArgumentParser(description="reference-concepts(可选:据参考拆解报告出差异化概念)") + parser.add_argument("project_dir", help="项目目录(CP 自建工作区 output_videos//)") + parser.add_argument("--report-file", default=None, help="甲方给的参考拆解报告路径(如 main 的 viral-chaser 产物)") + args = parser.parse_args() + + project = Path(args.project_dir).resolve() + out_dir = project / "reference" + out_dir.mkdir(parents=True, exist_ok=True) + concepts_path = out_dir / "concepts.md" + + # checkpoint + if concepts_path.is_file(): + print(f"[checkpoint] concepts.md 已存在,沿用:{concepts_path}") + return + + if not args.report_file: + print("[skip] 无参考拆解报告输入,跳过本工具") + return + + report = Path(args.report_file) + if not report.is_file(): + print(f"[warn] 报告文件不存在: {args.report_file},跳过本工具") + return + + # 复制报告原档到工作区(不做任何下载/转写/抽帧,只存档供后续阶段参考) + import shutil + archived = out_dir / "reference-report.md" + if not archived.is_file(): + shutil.copy2(report, archived) + + # 提示 agent 据报告出 2–3 差异化概念写入 concepts.md + stub = f"""# 据参考片出的差异化概念 + +## 参考片来源 + +{archived.name}(甲方给的参考拆解报告原档,本工具未做下载/转写/抽帧)。 + +## 概念候选(agent 据报告填) + +> agent 读 archived 报告,出 2–3 个**差异化**概念(不抄原片,做差异化),每个概念含: +> - 名称与一句话定位 +> - 与参考片的差异化点(节奏/钩子/结构/调性任一的差异化) +> - 预算估算(USD) +> - 备选路径(如该概念走不通的 fallback) + +### 概念 1 +(agent 填) + +### 概念 2 +(agent 填) + +### 概念 3(可选) +(agent 填) + +## 用户选定 + +> 呈交甲方(Brief owner)选定一个概念,写入 brief.md。未选定前不进 Stage 2。 +""" + concepts_path.write_text(stub, encoding="utf-8") + print(f"[done] 报告已存档:{archived}") + print(f"[stub] concepts.md 模板已落:{concepts_path}") + print(f"[next] agent 据报告填概念 → 呈交甲方选定 → 回通用阶段链(story-develop)") + + +if __name__ == "__main__": + main() diff --git a/crews/content-producer/skills/video-producer/scripts/render-shot.py b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/render-shot.py similarity index 96% rename from crews/content-producer/skills/video-producer/scripts/render-shot.py rename to crews/content-producer/skills/expert-video/tools/video-producer/scripts/render-shot.py index 10e9674d..1a11c31b 100644 --- a/crews/content-producer/skills/video-producer/scripts/render-shot.py +++ b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/render-shot.py @@ -30,7 +30,7 @@ def die(msg: str) -> None: def main() -> None: parser = argparse.ArgumentParser(description="Stage 10 render-shot") - parser.add_argument("project_dir", help="项目目录(output_videos// 或平台运营目录 /outputs//)") + parser.add_argument("project_dir", help="项目目录(CP 自建工作区 output_videos//)") parser.add_argument("--shot-id", default=None, help="只渲某镜,不传则提示 agent 逐镜跑") parser.add_argument("--dry-run", action="store_true", help="只打印调用计划不真渲") args = parser.parse_args() diff --git a/crews/content-producer/skills/video-producer/scripts/scene-compose.py b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/scene-compose.py similarity index 98% rename from crews/content-producer/skills/video-producer/scripts/scene-compose.py rename to crews/content-producer/skills/expert-video/tools/video-producer/scripts/scene-compose.py index efab8f26..b2ba34f5 100644 --- a/crews/content-producer/skills/video-producer/scripts/scene-compose.py +++ b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/scene-compose.py @@ -70,7 +70,7 @@ def run(cmd: list[str]) -> subprocess.CompletedProcess: def main() -> None: parser = argparse.ArgumentParser(description="scene-compose 单 Scene 分段合成") - parser.add_argument("project_dir", help="项目目录(output_videos// 或平台运营目录 /outputs//)") + parser.add_argument("project_dir", help="项目目录(CP 自建工作区 output_videos//)") parser.add_argument("--scene", required=True, help="Scene JSON 路径(相对 project_dir 或绝对)") parser.add_argument("--output", default=None, help="输出 Scene 片段名(相对 project_dir;默认从 scene JSON 推导)") diff --git a/crews/content-producer/skills/video-producer/scripts/script-self-eval.py b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/script-self-eval.py similarity index 94% rename from crews/content-producer/skills/video-producer/scripts/script-self-eval.py rename to crews/content-producer/skills/expert-video/tools/video-producer/scripts/script-self-eval.py index 930aa5d3..44e7b86f 100644 --- a/crews/content-producer/skills/video-producer/scripts/script-self-eval.py +++ b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/script-self-eval.py @@ -35,7 +35,7 @@ def main() -> None: parser = argparse.ArgumentParser(description="Stage 3b script-self-eval") - parser.add_argument("project_dir", help="项目目录(output_videos// 或平台运营目录 /outputs//)") + parser.add_argument("project_dir", help="项目目录(CP 自建工作区 output_videos//)") args = parser.parse_args() project = Path(args.project_dir).resolve() diff --git a/crews/content-producer/skills/video-producer/scripts/script-write.py b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/script-write.py similarity index 95% rename from crews/content-producer/skills/video-producer/scripts/script-write.py rename to crews/content-producer/skills/expert-video/tools/video-producer/scripts/script-write.py index e4332b23..b08555c5 100644 --- a/crews/content-producer/skills/video-producer/scripts/script-write.py +++ b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/script-write.py @@ -23,7 +23,7 @@ def main() -> None: parser = argparse.ArgumentParser(description="Stage 3 script-write") - parser.add_argument("project_dir", help="项目目录(output_videos// 或平台运营目录 /outputs//)") + parser.add_argument("project_dir", help="项目目录(CP 自建工作区 output_videos//)") args = parser.parse_args() project = Path(args.project_dir).resolve() diff --git a/crews/content-producer/skills/video-producer/scripts/shot-decompose.py b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/shot-decompose.py similarity index 95% rename from crews/content-producer/skills/video-producer/scripts/shot-decompose.py rename to crews/content-producer/skills/expert-video/tools/video-producer/scripts/shot-decompose.py index cc688eab..e1641499 100644 --- a/crews/content-producer/skills/video-producer/scripts/shot-decompose.py +++ b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/shot-decompose.py @@ -25,7 +25,7 @@ def main() -> None: parser = argparse.ArgumentParser(description="Stage 5 shot-decompose") - parser.add_argument("project_dir", help="项目目录(output_videos// 或平台运营目录 /outputs//)") + parser.add_argument("project_dir", help="项目目录(CP 自建工作区 output_videos//)") args = parser.parse_args() project = Path(args.project_dir).resolve() diff --git a/crews/content-producer/skills/video-producer/scripts/slideshow-risk.py b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/slideshow-risk.py similarity index 95% rename from crews/content-producer/skills/video-producer/scripts/slideshow-risk.py rename to crews/content-producer/skills/expert-video/tools/video-producer/scripts/slideshow-risk.py index ecdd4781..de13cf15 100644 --- a/crews/content-producer/skills/video-producer/scripts/slideshow-risk.py +++ b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/slideshow-risk.py @@ -36,7 +36,7 @@ def main() -> None: parser = argparse.ArgumentParser(description="Stage 9a slideshow-risk") - parser.add_argument("project_dir", help="项目目录(output_videos// 或平台运营目录 /outputs//)") + parser.add_argument("project_dir", help="项目目录(CP 自建工作区 output_videos//)") args = parser.parse_args() project = Path(args.project_dir).resolve() diff --git a/crews/content-producer/skills/video-producer/scripts/slot-plan.py b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/slot-plan.py similarity index 96% rename from crews/content-producer/skills/video-producer/scripts/slot-plan.py rename to crews/content-producer/skills/expert-video/tools/video-producer/scripts/slot-plan.py index e573ab20..4d132185 100644 --- a/crews/content-producer/skills/video-producer/scripts/slot-plan.py +++ b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/slot-plan.py @@ -35,7 +35,7 @@ def main() -> None: parser = argparse.ArgumentParser(description="Stage 7 slot-plan") - parser.add_argument("project_dir", help="项目目录(output_videos// 或平台运营目录 /outputs//)") + parser.add_argument("project_dir", help="项目目录(CP 自建工作区 output_videos//)") parser.add_argument("--tone", default=None, choices=sorted(TONE_SLOT_TABLE), help="调性,不传走 narrative 默认") args = parser.parse_args() diff --git a/crews/content-producer/skills/video-producer/scripts/story-develop.py b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/story-develop.py similarity index 94% rename from crews/content-producer/skills/video-producer/scripts/story-develop.py rename to crews/content-producer/skills/expert-video/tools/video-producer/scripts/story-develop.py index 0872e806..582ec67d 100644 --- a/crews/content-producer/skills/video-producer/scripts/story-develop.py +++ b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/story-develop.py @@ -18,7 +18,7 @@ def main() -> None: parser = argparse.ArgumentParser(description="Stage 2 story-develop") - parser.add_argument("project_dir", help="项目目录(output_videos// 或平台运营目录 /outputs//)") + parser.add_argument("project_dir", help="项目目录(CP 自建工作区 output_videos//)") args = parser.parse_args() project = Path(args.project_dir).resolve() diff --git a/crews/content-producer/skills/video-producer/scripts/storyboard-build.py b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/storyboard-build.py similarity index 94% rename from crews/content-producer/skills/video-producer/scripts/storyboard-build.py rename to crews/content-producer/skills/expert-video/tools/video-producer/scripts/storyboard-build.py index 4763fe3b..2186f167 100644 --- a/crews/content-producer/skills/video-producer/scripts/storyboard-build.py +++ b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/storyboard-build.py @@ -24,7 +24,7 @@ def main() -> None: parser = argparse.ArgumentParser(description="Stage 4 storyboard-build") - parser.add_argument("project_dir", help="项目目录(output_videos// 或平台运营目录 /outputs//)") + parser.add_argument("project_dir", help="项目目录(CP 自建工作区 output_videos//)") args = parser.parse_args() project = Path(args.project_dir).resolve() diff --git a/crews/content-producer/skills/video-producer/scripts/timeline-compose.py b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/timeline-compose.py similarity index 98% rename from crews/content-producer/skills/video-producer/scripts/timeline-compose.py rename to crews/content-producer/skills/expert-video/tools/video-producer/scripts/timeline-compose.py index 1af6a4f6..c9fbb1bc 100644 --- a/crews/content-producer/skills/video-producer/scripts/timeline-compose.py +++ b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/timeline-compose.py @@ -74,7 +74,7 @@ def probe_duration(path: Path) -> float: def main() -> None: parser = argparse.ArgumentParser(description="timeline-compose 时间轴合成") - parser.add_argument("project_dir", help="项目目录(output_videos// 或平台运营目录 /outputs//)") + parser.add_argument("project_dir", help="项目目录(CP 自建工作区 output_videos//)") parser.add_argument("--timeline", required=True, help="时间轴 JSON 路径(相对 project_dir 或绝对)") parser.add_argument("--output", default="video.mp4", help="输出合成片段名(相对 project_dir,默认 video.mp4)") parser.add_argument("--transition", default="hard", choices=["hard", "fade", "dissolve", "xfade"]) diff --git a/crews/content-producer/skills/video-producer/video-producer.sh b/crews/content-producer/skills/expert-video/tools/video-producer/video-producer.sh similarity index 73% rename from crews/content-producer/skills/video-producer/video-producer.sh rename to crews/content-producer/skills/expert-video/tools/video-producer/video-producer.sh index 82ee9863..8069219e 100755 --- a/crews/content-producer/skills/video-producer/video-producer.sh +++ b/crews/content-producer/skills/expert-video/tools/video-producer/video-producer.sh @@ -1,9 +1,9 @@ #!/usr/bin/env bash -# video-producer.sh — video-producer 顶层 wrapper(薄转发,子命令范式) +# video-producer.sh — video-producer 工具 wrapper(薄转发,子命令范式;expert-video 包内工具) # 让 agent 用 `video-producer <子命令> [参数...]` 走 PATH,零路径拼接。 # 子命令即 scripts/ 下同名 .py,wrapper 转发到对应脚本,不改语义。 -# 子命令清单见 SKILL.md;每阶段是 scripts/ 下一个独立脚本, -# agent 按 SKILL.md 工作流逐个调,产物文件存在性即 checkpoint(不引状态机)。 +# 子命令入出参见本工具 SKILL.md;制作流程(阶段链 / 两闸门 / 各类型 workflow) +# 见 expert-video 包内 SKILL.md 与 workflows/。产物文件存在性即 checkpoint(不引状态机)。 set -euo pipefail SELF="${BASH_SOURCE[0]}" # Resolve symlink (wrapper is ln -sfn'd into ~/.openclaw/bin) so SCRIPT_DIR points at the real skill dir. @@ -15,19 +15,19 @@ shift case "$SUBCMD" in -h|--help|help) cat <<'HELP' -video-producer — 端到端视频制作(wrapper) +video-producer — 视频制作原子能力(wrapper,expert-video 包内工具) 用法: video-producer <子命令> [参数...] 跑对应阶段的原子脚本 video-producer help 列可用子命令 -Pipeline: - Brief 指定 pipeline 时,先读技能包内 pipelines/.md,再按其映射调用下列子命令。 - 未指定 pipeline 时走 SKILL.md 的 Stage 0→14 默认流程。 +流程: + Brief 指定 workflow 时,先读 expert-video 包内 workflows/.md,再按其阶段裁剪调用下列子命令。 + 未指定 workflow 时走 expert-video SKILL.md 的通用阶段链 Stage 0→14。 -子命令(按工作流阶段序): - intent-router Stage 0 意图路由 → 三档脚本模板(故事讲述型/纯画面动效型/蒙太奇剪接型) - reference-concepts Stage 1 吃 viral-chaser 报告出 2–3 差异化概念(可选,无报告跳过) +子命令(按阶段序): + intent-router Stage 1 意图路由 → 三档脚本模板(故事讲述型/纯画面动效型/蒙太奇剪接型) + reference-concepts 可选 吃甲方给的参考拆解报告出 2–3 差异化概念 story-develop Stage 2 idea → 故事(分场) script-write Stage 3 故事 → 分场剧本(含 enhancement_cues + delivery_cues) script-self-eval Stage 3 脚本自评 N 维打分 @@ -48,7 +48,7 @@ Pipeline: make-cover Stage 14 封面(siliconflow-img-gen,必含标题文字) 闸门不是子命令——GATE A(Stage 6 后文本闸门)与 GATE B(Stage 9 后素材闸门)由 agent -按 SKILL.md 工作流执行:呈交摘要 → 结束本轮回复 → 等用户逐闸门批准。 +按 expert-video SKILL.md 执行:呈交摘要 → 结束本轮回复 → 等甲方(main agent 或用户)逐闸门批准。 产物文件存在性即 checkpoint:每个子命令先查产物文件是否存在,存在则 load 不重生成。 HELP diff --git a/crews/content-producer/skills/expert-video/workflows/collage-broll.md b/crews/content-producer/skills/expert-video/workflows/collage-broll.md new file mode 100644 index 00000000..a1410329 --- /dev/null +++ b/crews/content-producer/skills/expert-video/workflows/collage-broll.md @@ -0,0 +1,260 @@ +# Workflow:Collage B-roll(纸拼贴组装动画) + +Brief 里写 `workflow: collage-broll`,或甲方要"把这句口播做成拼贴 B-roll""纸拼贴动画""半调拼贴"时使用。把一句约 5 秒的口播压成一个 sharp visual idea,再做成高级编辑风纸拼贴组装动画。 + +## 三道闸门 + +| 闸门 | 停在哪 | 交付给甲方看什么 | +|------|--------|-----------------| +| Gate 1 隐喻确认 | 只设计视觉隐喻,**不生成图片、不生成视频、不调任何视频模型** | 每条的核心意思、情绪、一句话视觉命题、3–6 个关键物件、建议底色与点色、预期组装顺序 | +| Gate 2 静帧确认 | 隐喻确认后才写 visual spec 与 imagegen prompt,用 `siliconflow-img-gen` 生成静帧 | 带编号的静帧 contact sheet + `gate2-qa.md` | +| Gate 3 视频生成 | 静帧确认后不再问用哪个模型,直接调 `collage-broll gate3` 批量跑 i2v | 逐条 contact sheet + 末帧对照 + `gate3-qa.md` + `video-review` 结论 | + +- 每道闸门都要**结束本轮回复**等甲方批;甲方只确认部分编号时,只让通过的条目进下一道。 +- Gate 1 / Gate 2 分别对应通用链的 GATE A(文本)/ GATE B(素材)语义:付费生成前必停。 +- 甲方已在 Brief 中代理批准某道闸门时,把批准范围记进对应 QA 文件后继续。 + +## 成功标准 + +- 一句话只表达一个清晰隐喻;不要把文稿逐字放进画面。 +- 一条文稿控制在 3–6 个关键物件;元素过多语意变弱,i2v 组装也不稳定。 +- 同一批画面有统一设计语言,但**不强制全部蓝底**。 +- 背景是强烈、平坦、均匀的色场,可按语意变化。 +- 主体以黑白 halftone photographic cut-outs 为骨架;关键卡片、按钮、胶片、规则册等允许红、黄、青、橙、紫、奶油白等彩色纸张。 +- 所有纸片有清晰裁切边、奶油白 keyline、低透明度柔和阴影和纸张颗粒。 +- 动作是 assemble-from-empty,不是轻微漂移、晃动或慢 zoom。 +- 无字幕、无口播全文、无 logo、无水印、无 UI。 +- 默认交付 9:16、5 秒、720×1280、无声 MP4。 + +批量隐喻优先形成前后叙事(例如先表现手工消耗与经验流失,再表现规范沉淀与人机分工)。 + +## 不适用 + +- 需要精确控制图层、遮挡、镜头穿越或可编辑时间线 → 改用分层动画方案,并向甲方说明本 workflow 做不到。 +- 只要视频提示词、不要成片 → 直接写 prompt 交付,不走本流程。 +- 需要真实人物产品广告或口播演员 → 走 Narration Video 或通用流程。 +- 甲方明确要可逐层修改的透明素材 → 本 workflow 默认不拆透明图层。 + +## 项目目录 + +自建工作区 `output_videos//`(甲方不指定、不代建): + +```text +/ +├── brief.md # 文稿 + Gate 1 隐喻清单 +├── visual-spec.json # Gate 2 视觉规格 +├── imagegen-prompts.md # Gate 2 Seedream prompt 留档 +├── gen-jobs.json # Gate 3 批量调用清单 +├── gate2-qa.md # 静帧 QA 结论 +├── gate3-qa.md # 视频 QA 结论 +├── still-contact-sheet.jpg # Gate 2 静帧总图 +├── video-contact-sheet-all.jpg # Gate 3 全部成片逐秒抽帧 +├── video-first-frame-all.jpg # 全部成片实际首帧(验证真的从空色场开始) +├── end-frame-comparison-all.jpg # 确认静帧 vs 视频末帧并排 +├── 01-/ +│ ├── gen-prompt.txt # aigc-video-gen --prompt 内容 +│ ├── frames/ +│ │ ├── still.png # Gate 2 确认的完成帧(原图) +│ │ ├── last-frame.png # 统一裁到 720x1280 的尾帧 +│ │ └── first-frame.png # 纯色空首帧(同底色 hex) +│ └── gen-runs/run-v01/ +│ ├── final-5s.mp4 # aigc-video-gen 产物(声画同出) +│ ├── final-5s-noaudio.mp4 # 默认交付版(无声) +│ ├── contact-sheet.jpg +│ ├── video-last-frame.jpg +│ └── end-frame-comparison.jpg +└── 02-/... +``` + +## Phase 1:设计视觉隐喻(Gate 1) + +先把文稿压成一个视觉命题,提取: + +- **核心意思**:观众最终要看懂什么 +- **情绪**:冷静、惊讶、紧迫、豁然开朗、荒诞、反讽 +- **动作动词**:打开、连接、漏掉、装订、归档、点亮、压缩、分叉、组装 +- **可视化隐喻**:机器、时钟、胶片、档案柜、控制台、规则册、漏斗、轨道、棋子 + +输出格式: + +```text +1. 核心意思:经验每次都在重复消耗 + 视觉隐喻:熟练剪辑师围着巨大的胶片时钟逐帧裁切,时钟走完一圈却只得到一小段成片 + 关键物件:胶片时钟、剪辑师、剪刀、短胶片 + 色彩:焦橙底,奶油白与浅青点色 + 组装顺序:时钟 → 人物与剪刀 → 胶片 → 最终短输出 +``` + +输出后停下等确认。 + +## Phase 2:生成彩色拼贴静帧(Gate 2) + +### visual-spec.json + +```json +{ + "script_meaning": "", + "visual_metaphor": "", + "style_signature": "flat bold color field, mixed black-and-white halftone cut-outs and colored cardstock accents, crisp cut edges, cream keylines, soft paper shadows, editorial paper collage", + "aspect_ratio": "9:16", + "color_field": { + "background_hex": "", + "accent_colors": [], + "paper_grain": "fine uncoated-paper fiber" + }, + "elements": [{ "what": "", "role": "", "motion": "", "placement": "" }], + "composition": { "layout": "", "negative_space": "", "final_frame": "" }, + "motion_plan": "structure first, subject or cards second, action and result last", + "avoid": "typography, readable letters, numerals, logos, watermark, UI, subtitles, glossy 3D, photoreal environment" +} +``` + +### 色彩规则 + +不要把 cobalt blue 当唯一默认值。按语意挑强色场,一批作品保持"同设计语言、不同底色": + +| 色场 | 语意 | +|------|------| +| 焦橙 / 红 | 时间消耗、劳动、紧迫 | +| 芥末黄 | 工具、警示、经验漏失 | +| 墨绿 | 认知、审美、系统重置 | +| 深紫 | 规范、沉淀、长期记忆 | +| 青绿 | 判断、协作、自动执行 | + +主体以黑白半调为主,局部彩色纸张必须服务信息层级,不为彩色而彩色。 + +### imagegen prompt(siliconflow-img-gen / Seedream) + +```bash +siliconflow-img-gen --prompt "<下面整段>" --image-size 1600x2848 --out-dir /01-/frames/ +``` + +Prompt 用英文(Seedream 对英文响应更好): + +```text +Use case: ads-marketing +Asset type: final still frame for a 9:16 image-to-video B-roll clip +Primary request: Create a finished editorial paper-collage image expressing [一句话视觉命题]. +Scene/backdrop: perfectly flat [颜色] paper field [hex] with subtle uncoated paper fiber. +Style/medium: premium editorial stop-motion paper collage; black-and-white halftone photographic cut-outs mixed with selective [点色] colored cardstock. +Composition/framing: vertical 9:16 locked poster frame; central subject within the middle 70 percent; generous clean color-field negative space; 3–6 large separable paper groups for later assemble-from-empty animation. +Materials/textures: visible printed halftone dots, crisp machine-cut edges, thin warm-cream paper keylines, soft low-opacity physical drop shadows. +Constraints: [本条隐喻必须一眼看懂的关系]. +Avoid: no typography, no readable letters, no numerals, no logos, no watermark, no UI, no subtitles, no glossy 3D, no photoreal environment, no clutter. +``` + +Seedream 不支持参考图锁风格,"同设计语言"靠同一批复用同一 `style_signature` 字串 + 同一 `color_field` 范围。 + +### 静帧 QA + +检查:隐喻是否一眼看懂 / 主体是否集中 / 是否有假字、logo、水印、UI / 是否保留足够纯色场便于从空场组装 / 是否 3–6 个清晰大组而非满屏碎片 / 同批是否统一质感但有色彩变化。 + +通过的原图复制到 `/frames/still.png`,拼 contact sheet 后交甲方确认,结论写 `gate2-qa.md`。要求重生部分静帧时,新版 contact sheet 递增命名(`still-contact-sheet-v2.jpg`),保留旧版便于对比。 + +```bash +ffmpeg -y -pattern_type glob -i "/*/frames/still.png" \ + -vf "scale=270:480,tile=5x1" -frames:v 1 /still-contact-sheet.jpg +``` + +段数 > 5 时分多行(`tile=5x2`、`5x3`…)。 + +## Phase 3:i2v 生成视频(Gate 3) + +### 1. 准备首尾帧 + +```bash +# 尾帧:确认静帧统一裁到 720x1280 +ffmpeg -y -i /frames/still.png \ + -vf "scale=720:1280:force_original_aspect_ratio=increase,crop=720:1280" \ + /frames/last-frame.png + +# 首帧:与尾帧同底色的纯色空纸面(assemble-from-empty 的核心) +ffmpeg -y -f lavfi -i color=c=0x:s=720x1280 -frames:v 1 /frames/first-frame.png +``` + +甲方明确要求不从完全空白开始时,首帧才保留一个基础物件。 + +### 2. 写动画 prompt(中文,声画同出) + +动作顺序默认:`基础结构 → 人物或关键卡片 → 连接件 → 动作 → 最终结果`。 + +```text +画面从纯色空场开始,依次滑入 [基础结构] → [人物/卡片] → [连接件] → [动作],最终定格在已确认的完成构图。固定机位,无切镜、无 zoom、无变形。画面无文字、无 logo、无水印、无 UI。音频:纸片滑入的嗒嗒声 + 卡位时的咔嗒声 + 最终定格的短促 BGM 收尾。 +``` + +每条 prompt 明确首帧是空首帧、尾帧是确认过的完成帧;最终构图必须贴近 last-frame,不让模型自由改造尾帧。 + +### 3. 批量生成 + +写 `gen-jobs.json`(每条含 `prompt` / `first_frame` / `last_frame` / `output` / `ratio` / `resolution` / `duration`),然后: + +```bash +collage-broll gate3 --batch /gen-jobs.json +collage-broll gate3 --batch /gen-jobs.json --dry-run # 先看调度计划 +``` + +内部串行逐条调公共 `aigc-video-gen` i2v(视频生成是异步轮询任务,并行会撞平台并发限),候选链 fallback 与 decisions.log 由 `aigc-video-gen` 自带。退出码 2 = 部分 job 失败:只重跑失败条目,已通过的不重跑。 + +开工前先跑 `collage-broll check-setup` 自检 ffmpeg / ffprobe / AWK_API_KEY / 视频平台 key / Python 版本。 + +> `aigc-video-gen` 要求输出相对路径落在 `output_videos/` 下,**调用时 workdir 必须是 Content Producer workspace 根**;i2v 报错先查首尾帧是否真存在、是否 720x1280。 + +### 4. 强制无声交付 + +```bash +ffmpeg -y -i /final-5s.mp4 -map 0:v:0 -c:v copy -an /final-5s-noaudio.mp4 +``` + +默认交付 `final-5s-noaudio.mp4`,保留 `final-5s.mp4` 作中间产物。甲方明确要带声时直接交付 `final-5s.mp4`。 + +## 视频 QA + +不要只看尾帧,必须检查组装过程与最终落位。 + +```bash +ffmpeg -y -i /final-5s-noaudio.mp4 -vf "fps=1,scale=270:480,tile=5x1" -frames:v 1 /contact-sheet.jpg +``` + +通过标准: + +- 首帧接近纯色空场(边缘轻微提前露出纸片可接受) +- 中段能看到结构、人物或卡片逐步进入,而不是整体淡入 +- 没有切镜、zoom、3D 化或写实场景漂移 +- 没有假字、logo、水印或 UI +- 最终帧与确认静帧一致;轻微姿态或细节漂移只要不影响隐喻语义即判通过,不为此重跑 +- 成片为 720×1280、5 秒 + +另抽视频末帧与确认静帧并排生成 `end-frame-comparison.jpg`;批量项目再合三张总览图(`video-contact-sheet-all.jpg` / `video-first-frame-all.jpg` / `end-frame-comparison-all.jpg`)。逐条 QA 结论(含带瑕疵通过的理由)写 `gate3-qa.md`。 + +### 技术自检(强制) + +视觉 QA 后必须再跑公共 `video-review`,verdict=pass 才交付: + +```bash +video-review /final-5s-noaudio.mp4 +``` + +视觉 QA 评美与语义,`video-review` 评技术合规(ffprobe 全字段 / 抽帧黑帧扫 / 音频电平 / 时长分辨率一致性),互补不重叠。fail 按 critical 项修或重生对应 job;warn 向甲方复述由其决定。 + +> 默认无声交付时 `audio_absent` warning 是预期,可放行;带声版出 `audio_absent` 是 critical(声画同出该出声没出声),退回重生成。 + +### 常见问题 + +| 症状 | 处理 | +|------|------| +| 首帧边缘提前露出 | 轻微可接受;严格空场需求改用更坚定的 first-frame(纯色 + 边缘 padding) | +| 组装感弱 | 缩短元素数量,prompt 改为明确的逐件"滑入 / 卡位"顺序 | +| 尾帧漂移 | 强化 prompt 里"最终定格在已确认的完成构图"(i2v 的 last-frame 权重高) | +| 出现假字 | 回到静帧重生,不要用视频 prompt 修补 | +| 个别视频失败 | 只重跑对应 job | +| i2v 报错 | 查首尾帧是否 720x1280、是否真存在、workdir 是否 workspace 根 | + +## 交付 + +- 每条 `/gen-runs/run-v01/final-5s-noaudio.mp4`(甲方要带声则 `final-5s.mp4`) +- 每条 contact sheet、批量总 contact sheet、末帧对照图 +- `gate2-qa.md` / `gate3-qa.md` / `video-review` 结论 +- 一句说明每条文稿如何转成视觉隐喻 +- 回报产物**绝对路径** + +成片问题来自 i2v 生成限制(组装感弱 / 尾帧漂移)时直接说明;只有需要精确图层控制时才建议换方案,并向甲方报清代价。 diff --git a/crews/content-producer/skills/expert-video/workflows/narration-video.md b/crews/content-producer/skills/expert-video/workflows/narration-video.md new file mode 100644 index 00000000..2e6f1cb0 --- /dev/null +++ b/crews/content-producer/skills/expert-video/workflows/narration-video.md @@ -0,0 +1,59 @@ +# Workflow:Narration Video(口播类视频) + +Brief 里写 `workflow: narration-video`,或 Brief 交付了口播文案 / 真人口播录音时使用。本 workflow 只写口播类视频的声画实现套路与阶段裁剪;原子能力、闸门纪律、工作区约定按 `expert-video` 的 SKILL.md 执行。 + +## 类型定义 + +以**人声讲述**为主干的视频。口播文案是甲方(main agent 或用户)的策略产物,已定稿交付;本包负责声画实现——配音或录音处理、字级时间戳对齐、按语义配画面、字幕、BGM、成片与封面。 + +## 三种输入形态 + +| 形态 | 甲方交付 | 本包做什么 | +|------|---------|-----------| +| TTS 旁白 + 画面 | `voiceover.md`(口播终稿)+ 素材(可选) | `awk-tts` 生成旁白(音色按 Brief)→ `narration-align` 拿字级时间戳 → 按时间戳切配画面 → 字幕 → BGM | +| 真人口播录音 + 画面 | 录音文件绝对路径 + 素材(可选) | ASR 拿时间戳(Stage 11 场景 D)→ 按时间戳排画面 → 字幕 → 音质差时降噪 → BGM ducking | +| 真人出镜口播录像 | 录像素材绝对路径 | 加字幕、BGM、片头片尾、包装、封面;语义级高光剪辑与去口气词交回 main 的 `talking-head-cut` | + +## 硬边界 + +- **口播文案原样落稿锁定**:落 `script/script.md`,不重写、不"顺手优化"措辞、不增删卖点。 +- 发现文案有问题(超时长带、合规风险、抽象到无法配画面)→ **报甲方**,不自行改。 +- 模式 B(用户直接对话)下用户只给大意时,可代拟文案,但必须发用户确认,定稿后才算 `voiceover`。 +- 语义级剪辑(去口气词、智能剪重点)归 main 的 `talking-head-cut`;本包只做几何级修整(切段/拼接/混音/烧字幕/补轨)。 + +## 阶段裁剪(对通用链的差异) + +| 阶段 | 本 workflow 的做法 | +|------|--------------------| +| Stage 0 | 除 Brief 外,必须确认口播文案 / 录音**已到位**:绝对路径存在、可解码、时长可读;缺 → 向甲方要,不开工 | +| Stage 1 | 档位一般为故事讲述型;纯画面动效型不适用(口播是主干) | +| Stage 2–3 | 跳过 `story-develop` / `script-write`;口播稿原样落 `script/script.md` | +| Stage 3b | `script-self-eval` 只做检查,不改写:总字数 vs 目标时长(按 6–8 字/秒折算)、句长、合规敏感词、是否存在无法配画面的抽象段 | +| Stage 4 | 分镜按口播段落切:每段"讲什么 → 看什么"写清;不写不可见 | +| Stage 5 | 画面全来自现成素材时跳过;需要 AIGC 补画面时保留 | +| Stage 6 | 无 AIGC 角色时跳过 | +| Stage 7–8 | slot 按口播语义规划;甲方给了素材先入库校验(可解码/规格/授权),缺口才补搜 | +| Stage 9a | 幻灯风险重点查"一句口播一张静图"的幻灯片感 | +| Stage 9b | 交付承诺必须含音画同步与字幕样式 | +| Stage 11 | **核心阶段**:场景 B(TTS 一次性生成 + 字级时间戳)或场景 D(甲方录音 → ASR 时间戳);字幕必须来自对齐后的时间轴 | +| Stage 12 | `timeline-compose` 按字级时间戳对齐:每句口播不得越过对应镜头边界,连续口播保留呼吸间隔;切旁白段用 `clip-trim --pre-buffer 0.5` 防吞首字 | +| Stage 13 | `video-review` + `motion-audit` + **响度归一化必跑** | +| Stage 14 | 封面主文案来自 Brief;口播金句作候选时需 Brief 允许 | + +## 声音规范 + +- **音色**:按 Brief 指定;未指定时选与内容气质匹配的音色,把备选 + 置信度 + 理由记 `script/decisions.json`。 +- **语速**:默认 6–8 字/秒;Brief 指定优先。 +- **字幕**:来自对齐时间轴;烧录前查目标画幅安全区,不落画面中部、不遮主体。 +- **BGM**:口播下 ducking(人声主、BGM 从);免版税曲库(`bgm-library`)优先。 +- **录音质量差**:走 crew 级 `scripts/denoise.py`(afftdn),只降噪不改内容。 + +## 验收检查 + +1. 口播与甲方交付逐字一致(除甲方书面同意的修改)。 +2. 音画同步:每句口播对应画面不越界,无黑屏空转、无长静帧。 +3. 字幕与口播一致、无错字、在安全区内。 +4. 响度归一化(-14 LUFS)已跑并记录。 +5. 无吞字、无爆音;BGM 不盖人声。 +6. `video-review` verdict=pass。 +7. `final-deliver.md` 含音色 / 语速 / BGM 选择与理由、素材来源与授权、各段实际时长、遗留问题。 diff --git a/crews/content-producer/skills/expert-video/workflows/reversal-ad.md b/crews/content-producer/skills/expert-video/workflows/reversal-ad.md new file mode 100644 index 00000000..26be4be6 --- /dev/null +++ b/crews/content-producer/skills/expert-video/workflows/reversal-ad.md @@ -0,0 +1,116 @@ +# Workflow:Reversal Ad(「万万没想到」式反转植入) + +Brief 里写 `workflow: reversal-ad` 时使用。本 workflow 只写这一类型视频的套路与阶段裁剪;原子能力、闸门纪律、工作区约定一律按 `expert-video` 的 SKILL.md 执行。 + +## 类型定义 + +一整段**视频解说**(影视解说 / 剧情解说 / 纪录片式解说)铺垫,观众以为自己在看内容;到某一帧突然反转插入品宣——猝不及防、万万没想到。笑点与记忆点全部来自「预期落差」,所以反转前必须让观众完全相信这是一条内容视频。 + +## 三段结构 + +| 段 | 时长占比 | 功能 | 硬要求 | +|----|---------|------|--------| +| 解说正文 | 63%–76% | 讲一个自洽、有冲突、有悬念的故事 | 主悬念一句话可复述,且贯穿到反转点;段尾停在「求助 / 任务 / 待发 / 冲突」节点 | +| 反转过渡 | 3%–13% | 用一句话或一帧把剧情指向产品 | 必须双关或因果可追,不许硬切;口播明写因果 | +| 产品植入 | 15%–27% | 集中讲产品,讲完立即收尾 | 3–4 句单点深打,覆盖 Brief 允许的 ≥3 个价值点,每点有对应画面 | + +反转点落在总时长 **55%–76%** 之间(集中植入偏前,两段式偏后)。占比是创作约束,不是硬编码参数;Brief 指定时以 Brief 为准。 + +## 反转手法(四选一,或叠加) + +| 手法 | 机制 | 实现要点 | +|------|------|----------| +| 任务指引式 | 剧情停在求助节点 → 指路者给出答案,答案就是产品 | 「去往 X 就能化解 Y」句式;产品信息用标注贴图/攻略图呈现(箭头 + 关键词),不做广告贴片感 | +| 双关置换式 | 剧情核心意象与产品同名同形 | 意象必须在解说正文里先建立,反转时复用同一意象(沙盘 / 天池 / 秘境之类) | +| 身份彩蛋式 | 主角身份在反转处变成与产品相关的角色 | 身份转换要由剧情自然推出(求职、比赛、任务),不靠旁白硬说 | +| 戏中戏式 | 剧情内的媒介(录像机屏幕、照片、窗外、手机屏)先出现产品画面 | 口播仍讲剧情句,画面先行;随后全屏切产品段 | + +## 叙事与植入规则 + +- **单线闭环**:主角动机一句话说清并在片内闭环;素材撑不住就改写,不留断头线。 +- **节拍 ≤ 6**:每节拍一句,句间因果可追。 +- **钩连句**:转生、化身、置换等转折必须用口播明写因果,并用同色、同物或同动作的画面衔接。 +- **意象桥**:产品段第一镜必须复用解说正文的核心意象;禁止无关风景空镜硬切。 +- **反转前零产品提及**:不插 punch 句、不散落卖点、不出现品牌暗示(含 logo、包装、界面)。 +- **收束**:产品段讲完立即收尾,不加无信息量的仪式句或氛围空镜。 +- **音效**:可在反转瞬间用 whoosh 等音效标记剧情→产品切换,但不得用音效替代因果衔接。 +- **合规**:正片零 URL、零域名、零联系方式;官网、仓库、价格、活动入口只写进 `final-deliver.md` 交甲方(发布说明由甲方拟)。产品能力只讲 Brief 允许的范围,不承诺收益数字。 + +## 口播(旁白) + +- 甲方交付 `voiceover.md` 时**原样落稿锁定**(Stage 3 不重写策略文案,仍跑 `script-self-eval` 做检查)。 +- 未交付时由本包写,GATE A 交审。默认规范:第三人称解说体;避免问句、感叹号、第二人称(你/您/家人们)与促销信号词(「接下来介绍」「这是我们的」);句长 10–15 字,语速 7–8 字/秒。 +- 真人口播录音(甲方提供)走 Stage 11 场景 D:ASR 拿时间戳后按时间戳排素材,不重配旁白。 + +## 素材 sourcing + +来源模式只允许以下四类(含混合),每段素材都必须能追溯到来源模式与授权记录: + +1. **open_license_footage**:Brief 指定的开源/免版权片源。记录原片 URL、许可证、署名要求,发布说明如实署名。不得把"网上能下载"等同于可商用或可改编。 +2. **user_provided**:甲方/用户给现成片段。入库前记录文件参数、来源说明、授权背景;版权风险由甲方确认承担,本包只做技术处理,不做授权背书。 +3. **aigc**:按 Brief 风格边界生成(公共 `aigc-video-gen`)。记录 prompt、模型、生成时间与产物 metadata;交付说明按平台要求标注 AI 生成。 +4. **mixed**:以上混合。 + +通用入库要求: + +- 逐条校验可解码,记录分辨率、帧率、时长、音轨。 +- 建素材清单与候选区索引;候选区文件名能反查源素材时间点。 +- **素材四查**:URL / 域名、旧品牌名或水印、残缺元素、字幕遮挡。发现风险优先换干净素材窗口,裁切只能作为确认安全后的次选。 +- 素材支撑度不足时回到 Brief owner,不为了凑成片编造叙事。 + +## Brief 必备字段 + +缺任一项先向 Brief owner 澄清,不自行补品牌事实、授权或许可证: + +| 字段 | 要求 | +|------|------| +| platform | douyin / wx_channel / xhs 等,用于画幅、时长带与合规边界 | +| core_message | 本条必须传达的核心信息 | +| product_points | 产品/服务事实、允许讲的能力、禁用承诺(只以 Brief 为准,不内置品牌事实) | +| twist_variant | 任务指引式 / 双关置换式 / 身份彩蛋式 / 戏中戏式;未指定时由本包据素材与故事选定,并在 GATE A 说明理由 | +| story_source | 解说正文的故事来源(开源片名 / 用户素材 / AIGC 生成) | +| voiceover | 甲方交付口播终稿路径;未交付时写明由本包起草 | +| source_mode | open_license_footage / user_provided / aigc / mixed | +| assets | 素材绝对路径、来源 URL、许可证、授权确认记录 | +| form | 横竖屏、时长带、画面风格、配音音色与 BGM 倾向 | +| subtitle_style | 是否烧字幕、样式与安全区;未指定按平台常规可读性处理 | +| gates | GATE A/B 批准人;甲方代理批准时写明批准范围 | +| acceptance | 交付物、验收标准、遗留问题记录要求 | + +## 阶段裁剪(对通用链的差异) + +| 阶段 | 本 workflow 的做法 | +|------|--------------------| +| Stage 1 | 档位固定 **故事讲述型(narrative)** | +| Stage 2 | 甲方交付口播时跳过;未交付时故事梗概必须写明三段结构与反转手法 | +| Stage 3 | `script/script.md` 按段标注(解说 / 反转 / 植入)+ 每段时长占比 + 反转点时间码 | +| Stage 4 | 镜头表每镜标所属段;反转点前后各一镜写明衔接意象(意象桥) | +| Stage 6 | 现成素材轨且无 AIGC 角色时跳过角色三视图 | +| Stage 7–8 | slot 按段规划,解说段 slot 数最多;植入段 slot 必须能承载 Brief 的价值点 | +| Stage 9b | delivery_promise 中记录反转点位置与植入段占比承诺 | +| Stage 11 | 场景 B(旁白一次性 TTS + 对齐)或 D(甲方录音);BGM 优先沿用素材原生配乐轨并 ducking | +| Stage 12 | 解说段与植入段允许色调对比(冷暗 → 明亮实景/界面),但必须用钩连句 + 意象桥缝合 | +| Stage 14a | 封面优先用素材高情绪帧直用;封面主文案来自 Brief,无标题平台用核心传达 | + +## 制作护栏 + +1. **规格一次锁定**:横竖屏、目标时长、分辨率与帧率由 Brief 决定;不为"看起来高级"擅自放大素材。需要统一规格时先确认素材源质量与环境约束,再写入 timeline 计划与决策日志。 +2. **帧率一致性**:混排来源素材时先确认各段帧率;不一致必须用 `assemble` 归一化参数或分段预处理统一,不得不同帧率直接 concat 交付。输出后核对时长、音画同步与段边界。 +3. **旁白排布**:用 `mix-audio` + `narration-align` 的实测时长 / 字级时间戳排布,不用文本长度估算。每句旁白不得越过对应镜头边界;连续旁白保留呼吸间隔,累计漂移可追溯到每段实际时长。 +4. **字幕安全区**:`subtitles.srt` 必须来自对齐后的口播时间轴;烧录前检查目标画幅安全区,避免字幕落到画面中部或关键主体上。字幕样式由 Brief 决定,不硬编码项目个案参数。 +5. **AIGC 原声**:AIGC 声画同出素材如含有效环境音,可作低音量音床并在口播下 ducking;无有效音轨时补静音,不用噪声填充。 +6. **决策审计**:素材取舍、规格、音色、字幕样式、反转手法选择、fallback 与弃用中间产物都写入 `script/decisions.json` 或 `final-deliver.md`。 + +机器资源限制、编码线程数、分辨率上限、低载参数属部署环境差异,读 Content Producer workspace 的 `MEMORY.md` 或 Brief 的 `environment_constraints`,不写进本 workflow。 + +## 验收检查 + +1. 正片零 URL、零域名、零联系方式。 +2. 品牌名、产品名、能力表述与 Brief 一致;无收益承诺。 +3. 叙事单线闭环,节拍 ≤ 6,转折因果可追。 +4. 反转前零产品提及;意象桥成立(产品段第一镜复用剧情核心意象)。 +5. 产品段占比与反转点位置落在承诺区间,且与选定手法一致。 +6. 每段素材来源与授权可追溯;AIGC 标注完整。 +7. 口播、字幕、画面、音效与 Brief / `voiceover.md` 一致。 +8. `video-review` verdict=pass;未通过不得交付。 +9. `final-deliver.md` 含素材来源与协议、口播终稿、各段实际时长、自检结果、弃用产物与遗留问题。 diff --git a/crews/content-producer/skills/manim-explainer/SKILL.md b/crews/content-producer/skills/manim-explainer/SKILL.md deleted file mode 100644 index 0ac931b9..00000000 --- a/crews/content-producer/skills/manim-explainer/SKILL.md +++ /dev/null @@ -1,115 +0,0 @@ ---- -name: manim-explainer -description: Build reusable Manim explainers for technical concepts, graphs, system - diagrams, and product walkthroughs, then hand off to the wider video stack if needed. - Use when the user wants a clean animated explainer rather than a generic talking-head - script. -metadata: - openclaw: - emoji: 🎬 - requires: - bins: - - python3 - - manim - - ffmpeg ---- - -# Manim Explainer - -Use Manim for technical explainers where motion, structure, and clarity matter more than photorealism. - -## When to Activate - -- the user wants a technical explainer animation -- the concept is a graph, workflow, architecture, metric progression, or system diagram -- the user wants a short product or launch explainer for X or a landing page -- the visual should feel precise instead of generically cinematic - -## Tool Requirements - -- `manim` CLI for scene rendering -- `ffmpeg` for post-processing if needed -- `video-edit assemble` for combining rendered video with TTS audio -- `awk-tts` for voiceover generation - -## Default Output - -- short 16:9 MP4 -- one thumbnail or poster frame -- storyboard plus scene plan - -## Workflow - -1. Define the core visual thesis in one sentence. -2. Break the concept into 3 to 6 scenes. -3. Decide what each scene proves. -4. Write the scene outline before writing Manim code. -5. Render the smallest working version first. -6. Tighten typography, spacing, color, and pacing after the render works. -7. Hand off to the wider video stack only if it adds value. - -## Scene Planning Rules - -- each scene should prove one thing -- avoid overstuffed diagrams -- prefer progressive reveal over full-screen clutter -- use motion to explain state change, not just to keep the screen busy -- title cards should be short and loaded with meaning - -## Network Graph Default - -For social-graph and network-optimization explainers: - -- show the current graph before showing the optimized graph -- distinguish low-signal follow clutter from high-signal bridges -- highlight warm-path nodes and target clusters -- if useful, add a final scene showing the self-improvement lineage that informed the skill - -## Render Conventions - -- default to 16:9 landscape unless the user asks for vertical -- start with a low-quality smoke test render -- only push to higher quality after composition and timing are stable -- export one clean thumbnail frame that reads at social size - -```bash -# 冒烟测试(低质量,优先用此验证构图) -manim-explainer .py low ./output - -# 中等质量预览 -manim-explainer .py medium ./output - -# 正式输出(高质量) -manim-explainer .py high ./output -``` - -脚本自动完成:渲染 → 定位 MP4 → 导出第 2 秒封面帧,最后输出 JSON: -```json -{"ok": true, "video": "./output/scene_Class_low.mp4", "thumbnail": "./output/scene_Class_thumbnail.png"} -``` - -## Reusable Starter - -Use [assets/network_graph_scene.py](assets/network_graph_scene.py) as a starting point for network-graph explainers. - -Example smoke test: - -```bash -manim-explainer assets/network_graph_scene.py NetworkGraphExplainer low ./output -``` - -## Output Format - -Return: - -- core visual thesis -- storyboard -- scene outline -- render plan -- any follow-on polish recommendations - -## Related Skills - -- `video-edit assemble` for combining rendered video with TTS audio -- `awk-tts` for voiceover generation -- `content-check` for verifying output quality and duration diff --git a/crews/content-producer/skills/manim-explainer/assets/network_graph_scene.py b/crews/content-producer/skills/manim-explainer/assets/network_graph_scene.py deleted file mode 100644 index 74651a29..00000000 --- a/crews/content-producer/skills/manim-explainer/assets/network_graph_scene.py +++ /dev/null @@ -1,52 +0,0 @@ -from manim import DOWN, LEFT, RIGHT, UP, Circle, Create, FadeIn, FadeOut, Scene, Text, VGroup, CurvedArrow - - -class NetworkGraphExplainer(Scene): - def construct(self): - title = Text("Connections Optimizer", font_size=40).to_edge(UP) - subtitle = Text("Prune low-signal follows. Strengthen warm paths.", font_size=20).next_to(title, DOWN) - - you = Circle(radius=0.45, color="#4F8EF7").shift(LEFT * 4 + DOWN * 0.5) - you_label = Text("You", font_size=22).move_to(you.get_center()) - - stale_a = Circle(radius=0.32, color="#7A7A7A").shift(LEFT * 1.6 + UP * 1.2) - stale_b = Circle(radius=0.32, color="#7A7A7A").shift(LEFT * 1.2 + DOWN * 1.4) - bridge = Circle(radius=0.38, color="#21A179").shift(RIGHT * 0.2 + UP * 0.2) - target = Circle(radius=0.42, color="#FF9F1C").shift(RIGHT * 3.2 + UP * 0.7) - new_target = Circle(radius=0.42, color="#FF9F1C").shift(RIGHT * 3.0 + DOWN * 1.4) - - stale_a_label = Text("stale", font_size=18).move_to(stale_a.get_center()) - stale_b_label = Text("noise", font_size=18).move_to(stale_b.get_center()) - bridge_label = Text("bridge", font_size=18).move_to(bridge.get_center()) - target_label = Text("target", font_size=18).move_to(target.get_center()) - new_target_label = Text("add", font_size=18).move_to(new_target.get_center()) - - edge_stale_a = CurvedArrow(you.get_right(), stale_a.get_left(), angle=0.2, color="#7A7A7A") - edge_stale_b = CurvedArrow(you.get_right(), stale_b.get_left(), angle=-0.2, color="#7A7A7A") - edge_bridge = CurvedArrow(you.get_right(), bridge.get_left(), angle=0.0, color="#21A179") - edge_target = CurvedArrow(bridge.get_right(), target.get_left(), angle=0.1, color="#21A179") - edge_new_target = CurvedArrow(bridge.get_right(), new_target.get_left(), angle=-0.12, color="#21A179") - - self.play(FadeIn(title), FadeIn(subtitle)) - self.play( - Create(you), - FadeIn(you_label), - Create(stale_a), - Create(stale_b), - Create(bridge), - Create(target), - FadeIn(stale_a_label), - FadeIn(stale_b_label), - FadeIn(bridge_label), - FadeIn(target_label), - ) - self.play(Create(edge_stale_a), Create(edge_stale_b), Create(edge_bridge), Create(edge_target)) - - optimize = Text("Optimize the graph", font_size=24).to_edge(DOWN) - self.play(FadeIn(optimize)) - self.play(FadeOut(stale_a), FadeOut(stale_b), FadeOut(stale_a_label), FadeOut(stale_b_label), FadeOut(edge_stale_a), FadeOut(edge_stale_b)) - self.play(Create(new_target), FadeIn(new_target_label), Create(edge_new_target)) - - final_group = VGroup(you, you_label, bridge, bridge_label, target, target_label, new_target, new_target_label) - self.play(final_group.animate.shift(UP * 0.1)) - self.wait(1) diff --git a/crews/content-producer/skills/manim-explainer/manim-explainer.sh b/crews/content-producer/skills/manim-explainer/manim-explainer.sh deleted file mode 100755 index 757c885a..00000000 --- a/crews/content-producer/skills/manim-explainer/manim-explainer.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env bash -# manim-explainer.sh — manim-explainer 顶层 wrapper(薄转发) -# 让 agent 用 `manim-explainer ` 走 PATH,零路径拼接。 -# 内部转发到 scripts/render-manim.sh;wrapper 自身只是 exec 转发,不改语义。 -set -euo pipefail -SELF="${BASH_SOURCE[0]}" -# Resolve symlink (wrapper is ln -sfn'd into ~/.openclaw/bin) so SCRIPT_DIR points at the real skill dir. -while [ -L "$SELF" ]; do SELF="$(readlink -f "$SELF")"; done -SCRIPT_DIR="$(cd "$(dirname "$SELF")" && pwd)" -exec "$SCRIPT_DIR/scripts/render-manim.sh" "$@" diff --git a/crews/content-producer/skills/manim-explainer/scripts/render-manim.sh b/crews/content-producer/skills/manim-explainer/scripts/render-manim.sh deleted file mode 100755 index a37de10e..00000000 --- a/crews/content-producer/skills/manim-explainer/scripts/render-manim.sh +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env bash -# render-manim.sh — Manim 场景渲染 + 封面帧导出 -# -# Usage: render-manim.sh [quality] [output_dir] -# quality : low(冒烟测试,默认)| medium(预览)| high(正式输出) -# output_dir: 输出目录(默认 ./output) -# -# 输出: -# /__.mp4 -# /__thumbnail.png -# stdout 最后一行:JSON {"ok":true,"video":"...","thumbnail":"..."} - -set -euo pipefail - -SCENE_FILE="${1:?Usage: render-manim.sh [quality] [output_dir]}" -CLASS_NAME="${2:?Missing ClassName}" -QUALITY="${3:-low}" -OUTPUT_DIR="${4:-./output}" - -[[ -f "$SCENE_FILE" ]] || { echo "ERROR: 场景文件不存在: $SCENE_FILE"; exit 1; } - -case "$QUALITY" in - low) Q_FLAG="-ql" ;; - medium) Q_FLAG="-qm" ;; - high) Q_FLAG="-qh" ;; - *) echo "ERROR: quality 必须是 low/medium/high"; exit 1 ;; -esac - -mkdir -p "$OUTPUT_DIR" -SCENE_BASE=$(basename "$SCENE_FILE" .py) - -# 使用临时 media 目录,避免污染工作目录 -MEDIA_DIR=$(mktemp -d) -trap "rm -rf '$MEDIA_DIR'" EXIT - -echo ">>> 渲染: $CLASS_NAME ($QUALITY)" -manim "$Q_FLAG" "$SCENE_FILE" "$CLASS_NAME" --media_dir "$MEDIA_DIR" - -# 找到渲染输出的 MP4 -VIDEO_PATH=$(find "$MEDIA_DIR/videos" -name "*.mp4" | head -1) -[[ -n "$VIDEO_PATH" ]] || { echo "ERROR: 未找到渲染输出文件"; exit 1; } - -FINAL_VIDEO="$OUTPUT_DIR/${SCENE_BASE}_${CLASS_NAME}_${QUALITY}.mp4" -cp "$VIDEO_PATH" "$FINAL_VIDEO" -echo ">>> 视频: $FINAL_VIDEO" - -# 导出封面帧(第 2 秒) -THUMBNAIL="$OUTPUT_DIR/${SCENE_BASE}_${CLASS_NAME}_thumbnail.png" -ffmpeg -y -i "$FINAL_VIDEO" -ss 2 -frames:v 1 "$THUMBNAIL" -loglevel error -echo ">>> 封面帧: $THUMBNAIL" - -echo "{\"ok\":true,\"video\":\"$FINAL_VIDEO\",\"thumbnail\":\"$THUMBNAIL\"}" diff --git a/crews/content-producer/skills/video-producer/SKILL.md b/crews/content-producer/skills/video-producer/SKILL.md deleted file mode 100644 index 580c17f8..00000000 --- a/crews/content-producer/skills/video-producer/SKILL.md +++ /dev/null @@ -1,339 +0,0 @@ ---- -name: video-producer -description: 视频制作全能工具,两种用法——(A) 端到端生产:按 Brief 或指定 Pipeline 从零做完整视频,覆盖脚本、分镜、素材、闸门、渲染、自检、交付;(B) 给定素材剪辑:已有几个片段要拼一下、配音合成、剪辑烧字幕等,直接用 Stage 12 工具箱。 -metadata: - openclaw: - emoji: 🎬 - requires: - bins: - - python3 - - ffmpeg - - ffprobe ---- - -# 视频制作与片段修整(video-producer) - -## 双模式 - -本技能有两种用法,agent 据用户请求判断走哪条: - -**模式 A:端到端生产**——用户给主题/关键词/已有脚本/已有素材中的任一组合,或 main agent 交付已确认 Brief,要求从零做完整视频。默认走 Stage 0→14 全流程;Brief 指定 Pipeline 时先读取对应 Pipeline 文档并按其编排。 - -**模式 B:给定素材剪辑**——不涉及从零开剧本,编辑已有素材,直接用 Stage 12 工具箱(见下方"Stage 12 工具箱"段): - -- 几个片段拼一下 → `assemble`(自动统一分辨率/帧率/音频格式 + 转场) -- 一个片段配个旁白/配音 → `audio-mix`(多轨混音,延时/音量可控) -- 稍微剪一下(入点/出点/倍速)→ `clip-trim`(`--pre-buffer` 避免切 MP3 吞首字) -- 按时间轴切素材+混音 → `timeline-compose`(`audio_mode=concat` 出连续轨) -- 单个 Scene 合成(片段+旁白+对白)→ `scene-compose` -- 无音频片段补静音轨 → `add-silent-audio` -- 片尾制作(形象图+黑边+烧字幕)→ `make-outro` - -不适用: - -- 纸拼贴 B-roll → `collage-broll` -- Manim 技术演示动画 → `manim-explainer` -- 平面设计 → `design-full` -- 基于已有素材的深度高光剪辑(去口气词/智能剪重点)→ main 的 `video-edit` / `talking-head-cut`(本技能模式 B 只做几何级修整:切段/拼接/混音/烧字幕/补轨,不做语义级剪辑) -- 视频下载与爆款分析 → main 的 `viral-chaser` - ---- - -## Pipeline 机制 - -Brief 的 `pipeline` 字段是制作契约: - -| Pipeline | 何时使用 | 文档 | -| --- | --- | --- | -| `video-producer:default` | Brief 未指定 Pipeline,或用户直接发起从零制作 | 本 SKILL.md 的 Stage 0→14 | -| `dna-ad-video-pipeline` | Brief 明确指定,用于影视解说 + 反转植入 | `pipelines/dna-ad-video-pipeline.md` | - -规则: - -- Brief 未指定 Pipeline 时,CP 按默认全流程自由选择实现。 -- Brief 指定 Pipeline 时,必须先读对应 Pipeline 文档并直接采用,不得替换为自创流程。 -- Pipeline 只负责编排与内容套路;原子能力仍使用本技能子命令与公共技能,不新增脚本。 -- main 交付的 Brief 已含确认与代理闸门批准时,CP 不重开需求讨论;缺关键字段时向 Brief owner 澄清。 - ---- - -## 工作区目录约定 - -调用方传入了现成项目目录时直接沿用;否则在 `output_videos/` 下建项目文件夹 `/`: - -``` -/ # 即 /outputs// 或 output_videos// -├── brief.md # 用户/main 交付或 Stage 0/1 产出的制作契约(可含 pipeline) -├── reference-driven/ # Stage 1(可选,仅当 main 喂了 viral-chaser 报告) -│ ├── viral-chaser-report.md # main 喂入的追爆报告原档(本技能不自己跑 viral-chaser) -│ ├── concepts.md # 据报告出的 2–3 差异化概念 + 成本 + 备选路径 -│ └: 无下载产物、无 transcript、无关键帧——那些归 viral-chaser -├── script/ -│ ├── intent.json # Stage 0 -│ ├── story.md # Stage 2 -│ ├── script.md # Stage 3(含 enhancement_cues 六型 + delivery_cues) -│ ├── self-eval.json # Stage 3 自评 -│ ├── decisions.json # 决策审计链(跨阶段累积) -├── storyboard/ -│ ├── storyboard.json # Stage 4 镜头表 -│ ├── shot_decompose.json # Stage 5 每镜首尾帧+运动+variation_type -├── characters/ -│ ├── registry.json # Stage 6 static/dynamic features -│ ├── /front.png # 三视图(调 siliconflow-img-gen 生成) -│ ├── /side.png -│ └── /back.png -├── gates/ -│ ├── gate-a.md # GATE A 文本闸门评审产物 -│ ├── gate-b.md # GATE B 素材闸门评审产物 -├── slots/ -│ ├── slot-plan.json # Stage 7 -│ ├── asset-resolve.json # Stage 8(含 rejected_picks) -│ ├── slideshow-risk.json # Stage 9 六维打分 -│ ├── delivery-promise.json # Stage 9 承诺锁定 -├── render/ -│ ├── shot-NN/ # Stage 10 每镜渲染产物 -│ │ ├── first-frame.png # 首帧静照(生成或素材裁切) -│ │ ├── last-frame.png # 尾帧静照 -│ │ ├── gen-run-v01.mp4 # aigc-video-gen i2v 产物 -│ │ └ettings.log -│ │ └ulti-best.mp4 # 多候选择优胜出(多候选时) -├── audio/ -│ ├── narration.mp3 # awk-tts 旁白 -│ ├── bgm.mp3 # BGM(bgm-library 免版税曲库优先 / pexels/pixabay / aigc-video-gen music 生成) -│ ├── subtitles.srt # 字幕 -├── artifacts/ # Stage 12 按镜顺序的最终段 -│ ├── 01_*.mp4 -│ └ NN_*.mp4 -├── video.mp4 # Stage 12 拼接成片 -├── review/ # Stage 13 公共 video-review 产物 -│ ├── verdict.json -│ ├── frames/ -│ ├── motion-audit.json # CP 侧 motion_led 抽查 -├── cover.jpg # Stage 14 封面 -└── final-deliver.md # Stage 14 交付清单 -``` - -Pipeline 文档位于技能包内 `pipelines/`,不是项目目录;项目目录只保存 Brief、素材、脚本、渲染与交付产物。 - ---- - -## 阶段链(15 段,两闸门) - -每段子命令是 `scripts/` 下一个独立 .py,agent 按本 SKILL.md 工作流逐个调。**产物文件存在性即 checkpoint**——每个子命令先查产物文件是否存在,存在则 load 不重生成(允许用户手改 JSON 后续跑)。 - -``` -Stage 0 intent-router 意图路由 → 三档脚本模板(故事讲述型/纯画面动效型/蒙太奇剪接型) - · 故事讲述型(narrative)——重情节、有人物弧光、含旁白;默认 3–5 镜/场 - · 纯画面动效型(motion)——重节奏感/视觉冲击/少对白;默认 5–8 镜快切 - · 蒙太奇剪接型(montage)——重氛围/抽象/纯视觉;默认 4–7 镜无叙事 -Stage 1 reference-concepts 若 main 喂了 viral-chaser 报告 → 吃报告出 2–3 差异化概念;无报告跳过 -Stage 2 story-develop idea → 故事(含受众/类型显式复述,100–200 词梗概,人物,分场) -Stage 3 script-write 故事 → 分场剧本(同时间同地点分一场;可拍化描述;enhancer 润色) -Stage 3b script-self-eval 脚本自评 N 维打分,任一维 <3 必返工 -Stage 4 storyboard-build 剧本 → 镜头表(每镜叙事目的/机位复用/位置朝向/不写不可见) -Stage 5 shot-decompose 每镜拆首帧静照/尾帧静照/运动描述(variation_type 三档) -Stage 6 character-register 角色三视图 front/side/back + static/dynamic features 拆分 - ────── GATE A:文本闸门(脚本+分镜+机位+角色全齐,停,发用户审)────── -Stage 7 slot-plan 素材 slot 规划(template + hero slot + tone→slot 数) -Stage 8 asset-resolve 按 slot 拉素材(Fast path:多源并发搜 + 缩略图人核 + rejected_picks 落盘) -Stage 9a slideshow-risk 六维幻灯风险打分(pre-compose 闸门,≥4.0 fail 不许进 compose) -Stage 9b delivery-promise-lock 交付承诺八类锁定 + motion_ratio 预估 - ────── GATE B:素材闸门(素材齐+计划过审,停,发用户看 contact sheet)────── -Stage 10 render-shot 按 slot 渲染(AIGC 走 aigc-video-gen i2v 首尾帧插值;静图走 siliconflow-img-gen) -Stage 11 mix-audio 配音配乐四场景分流(A 人物对话声画同出 / B 旁白一次性 TTS 带字级时间戳 + 对齐 / C BGM 成片后统一生成(优先 bgm-library 免版税曲库,pexels/pixabay 并列;定制风格用 aigc-video-gen music)/ D 用户口播录音 → ASR 时间戳 → 按时间戳补素材) -Stage 12 assemble 按序拼接成片(原子工具箱:clip-trim 切段 / audio-mix 混音 / timeline-compose 时间轴合成 / assemble 拼接,agent 按 §Stage 12 工具箱场景化组合,不写死 Workflow) -Stage 13a video-review 公共 video-review 技术自检(强制闸门) -Stage 13b motion-audit CP 侧 motion_led 抽查(兑付 delivery-promise) -Stage 14a make-cover 封面(siliconflow-img-gen,必含封面主文案) -Stage 14b 交付 向用户呈交成片+封面+关键参数 -``` - -> Stage 0–6 全是**文本产物**,付费生成前必停——GATE A 落在这条边界上。GATE B 落在素材就绪、pre-compose 闸门通过后,确认渲染前最终计划。指定 Pipeline 时,以 Pipeline 文档的阶段映射为准;若 main 已在 Brief 中代理批准 GATE A,记录批准范围后继续。 - ---- - -## 子命令调用清单 - -| 子命令 | 入 | 出 | 用途 | -|--------|----|----|------| -| `intent-router` | brief.md(主题/关键词,或 viral-chaser 报告) | `script/intent.json`(档位+主题) | Stage 0 | -| `reference-concepts` | viral-chaser 报告(main 喂入,可选) | `reference-driven/concepts.md` | Stage 1:只吃报告出概念,不做下载/转写/抽帧;无报告跳过 | -| `story-develop` | intent.json | `script/story.md` | Stage 2 | -| `script-write` | story.md | `script/script.md`(含 enhancement_cues + delivery_cues) | Stage 3 | -| `script-self-eval` | script.md | `script/self-eval.json`(N 维分,任一维 <3 必返工) | Stage 3b | -| `storyboard-build` | script.md | `storyboard/storyboard.json` | Stage 4 | -| `shot-decompose` | storyboard.json | `storyboard/shot_decompose.json`(每镜首尾帧+运动+variation_type) | Stage 5 | -| `character-register` | storyboard.json + 人物描述 | `characters/registry.json` + 三视图 PNG | Stage 6(三视图调 siliconflow-img-gen) | -| `slot-plan` | storyboard.json + tone | `slots/slot-plan.json` | Stage 7 | -| `asset-resolve` | slot-plan.json | `slots/asset-resolve.json`(含 rejected_picks)+ 素材落 `raw_materials/` | Stage 8(调 pexels-footage / pixabay-footage / aigc-video-gen) | -| `slideshow-risk` | storyboard.json + slot-plan.json + asset-resolve.json | `slots/slideshow-risk.json`(六维分) | Stage 9a | -| `delivery-promise-lock` | storyboard.json + brief.md | `slots/delivery-promise.json`(八类锁) | Stage 9b | -| `render-shot` | shot_decompose.json + characters/ + slot-picks | `render/shot-NN/` 下产物 | Stage 10(调 aigc-video-gen i2v / siliconflow-img-gen) | -| `mix-audio` | script.md(delivery_cues) | `audio/` 目录 + `subtitles.srt` 模板 | Stage 11(四场景分流:A 声画同出 / B 旁白一次性 TTS+ASR 对齐 / C BGM 成片后生成(优先 bgm-library 免版税曲库 / pexels/pixabay 并列;定制风格用 aigc-video-gen music)/ D 用户口播录音 → ASR 时间戳 → 按时间戳补素材) | -| `narration-align` | audio/narration.mp3 + audio/narration.subtitle.json | `audio/narration-segments.json`(统一 segments 格式) | Stage 11b(优先复用 awk-tts --enable-subtitle 落盘的 TTS 原生字级时间戳,缺失时回退火山 ASR 极速版,凭据复用 viral-chaser 同池 `VOLC_ASR_*`) | -| `assemble` | render/ 顺序 | `video.mp4` | Stage 12(按序拼接 + 可选转场 + 可选分辨率归一化 + 自动补静音/统一音频格式) | -| `add-silent-audio` | 无音频视频片段 | 含静音音轨的视频 | Stage 12 原子工具:concat 前补齐音轨,保持连续 | -| `clip-trim` | 素材路径 + 入点/出点/倍速 | 切好的片段 | Stage 12 原子工具:精确切素材,视频和音频分别处理 | -| `audio-mix` | 多条音轨 + 各自延时/音量 | 混合音频 | Stage 12 原子工具:多轨混音 | -| `timeline-compose` | timeline.json(每段素材/入点/出点/倍速/音轨及延时) | 合成片段 | Stage 12 原子工具:按时间轴调 clip-trim + audio-mix 合成 | -| `motion-audit` | video.mp4 + delivery-promise.json | `review/motion-audit.json`(motion_led 抽查) | Stage 13b(补公共 video-review) | -| `make-cover` | brief.md(封面主文案)+ storyboard 关键帧 | `cover.jpg` | Stage 14a(调 siliconflow-img-gen) | - -> wrapper `video-producer.sh` 内部 `exec python3 "$SCRIPT_DIR/scripts/<子命令>.py" "$@"`——子命令名即脚本名,零路径拼接。 - ---- - -## 强制闸门与护栏 - -### GATE A(Stage 6 后):文本闸门 - -文本产物全齐(脚本+分镜+机位+角色),**停下发用户审**: - -- 呈交摘要:档位、场次数、镜数、角色数、关键决策(路径/模型/风格选择的备选+置信度+理由) -- **结束本轮回复**,不许在同条回复里进 Stage 7 -- 批准人是 Brief owner(用户或 main agent);main 已代理批准时,必须把批准范围落 `gates/gate-a.md` -- 批准是**逐闸门的**——早先的一句"你继续"不覆盖本闸门 -- 用户要改哪段就重跑对应子命令(产物文件存在性即 checkpoint,不会重生成未改的) - -### GATE B(Stage 9 后):素材闸门 - -素材齐 + 计划过 slideshow_risk + delivery_promise 锁,**停下发用户看 contact sheet**: - -- 呈交:slot 总数、素材就绪率、slideshow_risk 六维分与 verdict、delivery_promise 八类与 motion_ratio 预估、素材 contact sheet -- 批准人是 Brief owner;授权与来源记录必须一并呈交 -- 同 GATE A 收尾纪律 - -### 返工与耗时上限 - -- 每阶段最多返工 **3 次** -- 全片最多 **3 次** send-back -- 每阶段 wall-time 默认上限 **20 分钟**——卡住要报,不要反复撞 - -### 不许声称没做过的事 - -没有 tool result 或产物文件证明,不许声称已渲染/已生成/已改动。 - -### 模糊意图不算确认 - -- 用户说"做个短片""帮我策划"**不算确认**,必须先问清楚走哪条 workflow(故事讲述型/纯画面动效型/蒙太奇剪接型)、时长、受众;main 交付的 Brief 需带确认状态与闸门批准人 -- 起草/讨论脚本属对话协助,**不许调 render 工具** -- 默认**小规模**:1 场 3–5 镜,不许把模糊想法擅自扩成多场多镜;用户要扩才扩 - -### 决策审计链 - -每个选择(路径/模型/风格/音色/任何 fallback)记 `备选 + 置信度 + 理由`,跨阶段累积进 `script/decisions.json`。 - ---- - -## 依赖 - -| 依赖 | 来源 | 用在哪 | -|------|------|------| -| python3 / ffmpeg / ffprobe | 系统 | 各阶段脚本 | -| 公共 `aigc-video-gen` | skills/ | Stage 8/10 视频片段生成(百炼/火山声画同出,i2v 首尾帧插值) | -| 公共 `siliconflow-img-gen` | skills/ | Stage 6 角色三视图 / Stage 10 静帧 / Stage 14 封面 | -| 公共 `awk-tts` | skills/ | Stage 11B 旁白一次性 TTS(OpenClaw 内置 TTS 优先 → awk-tts fallback;加 `--enable-subtitle` 让火山单向流式 HTTP 原生返回字级时间戳,落 `narration.subtitle.json`) | -| 火山 ASR 凭据 `VOLC_ASR_*` | 实例 env | Stage 11b narration-align 回退路径 + Stage 11D 用户口播录音转写拿时间戳(复用 viral-chaser 同池:旧控制台双头 `VOLC_ASR_APP_ID`+`VOLC_ASR_ACCESS_KEY`,或新控制台单头 `VOLC_ASR_APP_KEY`) | -| 公共 `pexels-footage` / `pixabay-footage` | skills/ | Stage 8 Stock Footage 素材补充 / Stage 11C BGM 搜 | -| 公共 `bgm-library` | skills/ | Stage 11C BGM 搜(ccMixter 免版税 + 自动 TASL 署名,免 key,商用安全;与 pexels/pixabay 并列,优先用) | -| 公共 `video-review` | skills/ | Stage 13a 成片技术自检闸门 | -| 已暴露 `video-edit subtitles` | main crew 的 wrapper 原子 | 指定 Pipeline 需要烧字幕时使用;不可用时向 Brief owner 报工具缺口 | -| `requests` | 仓根 requirements.txt | 各脚本 HTTP 调用 | - ---- - -## 子命令清单(wrapper 视角) - -| 子命令 | 用途 | 退出码 | -|--------|------|--------| -| `video-producer intent-router` | 意图路由三档 | 0 成功 / 1 参数错 / 2 env 未配 | -| `video-producer reference-concepts` | 吃 viral-chaser 报告出概念 | 0 成功 / 1 参数错 | -| `video-producer story-develop` | idea → 故事 | 0 成功 / 1 参数错 | -| `video-producer script-write` | 故事 → 分场剧本 | 0 成功 / 1 参数错 | -| `video-producer script-self-eval` | 脚本自评 N 维 | 0 成功 / 1 参数错 | -| `video-producer storyboard-build` | 剧本 → 镜头表 | 0 成功 / 1 参数错 | -| `video-producer shot-decompose` | 每镜拆首尾帧+运动 | 0 成功 / 1 参数错 | -| `video-producer character-register` | 角色三视图 + features | 0 成功 / 1 参数错 | -| `video-producer slot-plan` | 素材 slot 规划 | 0 成功 / 1 参数错 | -| `video-producer asset-resolve` | 按 slot 拉素材 | 0 成功 / 1 参数错 / 2 env 未配 | -| `video-producer slideshow-risk` | 六维幻灯风险打分 | 0 成功 / 1 参数错 | -| `video-producer delivery-promise-lock` | 八类承诺锁定 | 0 成功 / 1 参数错 | -| `video-producer render-shot` | 按 slot 渲染 | 0 成功 / 1 参数错 / 2 env 未配 | -| `video-producer mix-audio` | 三场景分流占位 | 0 成功 / 1 参数错 | -| `video-producer narration-align` | 旁白 ASR 对齐时间戳 | 0 成功 / 1 参数错 / 2 env 未配 | -| `video-producer assemble` | 按序拼接成片 | 0 成功 / 1 参数错 | -| `video-producer add-silent-audio` | 给无音频视频补静音轨 | 0 成功 / 1 参数错 | -| `video-producer scene-compose` | 单 Scene 分段合成 | 0 成功 / 1 参数错 | -| `video-producer make-outro` | 片尾制作 | 0 成功 / 1 参数错 | -| `video-producer clip-trim` | 切素材段 | 0 成功 / 1 参数错 | -| `video-producer audio-mix` | 多轨混音 | 0 成功 / 1 参数错 | -| `video-producer timeline-compose` | 时间轴合成 | 0 成功 / 1 参数错 | -| `video-producer motion-audit` | motion_led 抽查 | 0 成功 / 1 参数错 | -| `video-producer make-cover` | 封面(必含封面主文案) | 0 成功 / 1 参数错 / 2 env 未配 | - ---- - -## Stage 12 工具箱(场景化组合,不写死 Workflow) - -Stage 12 段**不规定固定 Workflow**——下面四个原子工具 agent 按实际场景灵活组合。 - -### 原子工具清单 - -| 工具 | 干什么 | 关键参数 | -|------|--------|---------| -| `clip-trim` | 精确切素材段(入点/出点/倍速/前置缓冲,视频和音频分别处理) | `--input/--output/--start/--end/--speed/--sync-audio/--pre-buffer` | -| `audio-mix` | 多轨混音(每轨独立延时和音量) | `--track(可重复)/--delay/--volume/--output/--duration` | -| `timeline-compose` | 按时间轴 JSON 调 clip-trim + audio-mix 合成片段 | ` --timeline timeline.json [--transition ...]` | -| `assemble` | 按序拼接已就绪的段 + 可选转场 + 自动分辨率归一化 + 自动统一音频格式 | ` [--transition hard/fade/dissolve/xfade] [--width 1080] [--fps 30] [--audio-format 24000/mono] [--low-memory] [--preview-duration 30]` | -| `add-silent-audio` | 给无音频视频片段补静音音轨(concat 前置,assemble 内部也自动调) | `--input/--output/--duration/--sample-rate 24000/--channels mono` | -| `scene-compose` | 单 Scene 分段合成(片段+旁白+对白 → 一个 Scene 片段) | ` --scene scene.json [--output scene-01.mp4]` | -| `make-outro` | 片尾制作(形象图+黑边+烧字幕+静音轨 → 标准比例片尾) | ` --image <形象图> --slogan <文本> [--color color.json] [--duration 5] [--width 1080] [--fps 30]` | - -### 场景化组合示例(非强制,agent 按实际判断) - -**场景 A:无旁白直拼** -段就绪、无需切素材、无需混音——`assemble --transition fade` 一把过。 - -**场景 B:有旁白走时间轴** -旁白一次性 TTS 生成 + narration-align 拿字级时间戳后,按时间戳把各段素材对齐旁白: -1. agent 据 narration-segments.json 各段 start/end 冡素材入点/出点,写 timeline.json -2. `timeline-compose --timeline timeline.json` → 调 clip-trim 切段 + audio-mix 把旁白按延时叠到各段 -3. 全段 BGM 走 timeline.json 的 `audio_globals` 字段混入 - -**场景 C:分段先合再合(scene-compose 两阶段)** -长片或某些段需独立预合(如先合 shot-01~03 为一场 Scene,再合 shot-04~06 为另一场,最后两场 concat): -1. 写 `scene-01.json`(clips 片段列表 + narration 旁白 + dialogue 对白),跑 `scene-compose --scene scene-01.json --output scene-01.mp4` -2. 同样出 `scene-02.mp4` -3. 把 scene-01.mp4 / scene-02.mp4 当段素材,再跑 `assemble --source-dir scenes --transition fade` 合片 -scene-compose 内部调 clip-trim 切段 + audio-mix 混旁白/对白 + assemble 拼段,一步出单 Scene 完整片段。 - -**场景 D:素材尺寸不一** -AIGC 720x1280、录屏 1080x2384、片尾 784x1176 混拼——`assemble` 传 `--width 1080 --fps 30` 归一化(scale + pad 16:9 + sar + fps 统一)后再 concat。 - -**场景 E:精确调速某段** -某段需 2x 快放——`clip-trim --input <段> --output <快放段> --speed 2 --sync-audio` 切好后,把快放段当段素材再拼。 - -**场景 F:低内存机器** -机器内存吃紧(< 4G 或容器限额)——`assemble --transition fade --low-memory`,preset 切 ultrafast、crf 放宽到 28,避免 x264 缓冲爆内存。打印里会标注 `低内存模式:preset=ultrafast, crf=28`。 - -**场景 G:先试听再合成** -长片合成前想先听前 30 秒验证旁白/BGM 平衡——`assemble --preview-duration 30`,成片照常落,额外产 `video-preview.mp4`(前 30 秒,`-c copy` 秒切)。试听满意后再决定是否重混。 - -**场景 H:AIGC 无音频段混拼 + 旁白切段吞首字** -AIGC i2v 产物常无音频流,与有音频段直拼会断续;旁白用 `clip-trim --ss` 切 MP3 会吞第一个字。`assemble` 现在自动统一音频格式(默认 24000/mono,无音频段补静音,规格不符段重采样)+ 不传 `--width/--fps` 时自动探测各段分辨率/帧率、不一时统一到最低公共规格。旁白切段走 `clip-trim --pre-buffer 0.5`,实际入点提前 0.5s 保留段头音频,逻辑入点不变。 - -agent 据剧本实际选场景组合,可混合多场景(如 B+E:旁白时间轴 + 某段快放)。脚本不预设顺序。 - ---- - -## 禁止事项(强制) - -- **禁止跳过 GATE A/B 交付**:两闸门是工作流的一部分,呈交摘要后必须结束本轮回复等用户批 -- **禁止声称没做过的事**:没有 tool result 或产物文件证明,不许声称已渲染/已生成/已改动 -- **禁止把模糊想法擅自扩成多场多镜**:默认 1 场 3–5 镜,用户要扩才扩 -- **禁止跳过 video-review 交付**:Stage 13a 强制闸门,verdict=pass 才进 Stage 14 -- **禁止直接写 ffmpeg 命令**:所有 ffmpeg 调用走本技能子命令脚本或公共 video-edit 子命令 -- **禁止自己做视频下载/转写/抽帧**:那是 viral-chaser 的活,本技能只吃 main 喂入的报告 -- **禁止引入 CLIP / torch 系本地模型**:素材匹配走 Fast path 人核缩略图 -- **禁止扩充图库源**:保 Pexels + Pixabay 两源 -- **禁止批量生成**:逐条精做,不批量撞运气 diff --git a/crews/content-producer/skills/video-producer/pipelines/dna-ad-video-pipeline.md b/crews/content-producer/skills/video-producer/pipelines/dna-ad-video-pipeline.md deleted file mode 100644 index db893f9e..00000000 --- a/crews/content-producer/skills/video-producer/pipelines/dna-ad-video-pipeline.md +++ /dev/null @@ -1,139 +0,0 @@ -# Pipeline:dna-ad-video-pipeline(影视解说 + 反转植入) - -## 定位 - -用于生产「叙事短片前段 + 后段集中产品/服务反转」的短视频。Brief 中写: - -```yaml -pipeline: dna-ad-video-pipeline -``` - -Brief 未写 `pipeline` 时不强制使用本 Pipeline,Content Producer 可按 `video-producer:default` 自由选择;一旦指定本 Pipeline,必须直接采用,不得替换为自创流程。 - -本 Pipeline 只写编排与内容套路;原子能力全部来自 `video-producer` 与公共技能,不新增脚本。 - -## Brief 输入契约 - -MainAgent 交付的 Brief 至少包含: - -| 字段 | 要求 | -| --- | --- | -| platform | douyin / wx_channel / xhs 等,用于发布说明与合规边界 | -| dna_id | 使用的账号级 DNA | -| core_message | 本条必须传达的核心信息 | -| packaging_copy | 选题、观看理由、平台包装文案方向;wx_channel 为视频简介 | -| product_points | 产品/服务事实、允许讲的能力、禁用承诺;只以 Brief 为准,不内置品牌事实 | -| voiceover | 若口播文案 DNA 已启用,main 交付口播终稿 `voiceover.md`;未启用时 CP 可在 Pipeline 内写脚本 | -| source_mode | open_license_footage / user_provided / aigc / mixed | -| assets | 素材路径、来源 URL、许可证、授权确认记录 | -| form | 横竖屏、时长带、画面风格、配音/BGM 倾向;只写账号级边界,不写逐镜细节 | -| subtitle_style | 是否烧字幕、样式与安全区要求;未指定时按平台常规可读性处理 | -| environment_constraints | 可选:质量、时长、资源或交付约束;本机资源限制仍以 CP workspace `MEMORY.md` 为准 | -| variant | two_stage / concentrated_reversal,未指定时由 CP 根据素材与 Brief 选择 | -| gates | GATE A/B 批准人(用户或 main agent);若 main 已代理批准,需写明批准范围 | -| acceptance | 交付物、验收标准、遗留问题记录要求 | - -缺失关键字段时先向 Brief owner 澄清,不得自行补品牌事实、授权或许可证。 - -## 素材 sourcing - -来源模式只允许以下四类(含混合): - -1. **open_license_footage**:使用 Brief 指定的开源/免版权片源。main 需提供本地素材或明确授权直链;CP 只做素材入库与技术处理,不做对标拆解、转写或爆款分析。必须记录原片 URL、许可证、署名要求;发布说明中如实署名。不得把“网上能下载”等同于可商用或可改编。 -2. **user_provided**:用户提供现成片段。入库前记录文件参数、来源说明、授权背景;版权风险由用户确认承担,CP 只做技术处理,不做授权背书。 -3. **aigc**:按 Brief 与 DNA 风格边界生成。调用公共 `aigc-video-gen`,记录 prompt、模型、生成时间与产物 metadata;发布说明按平台要求标注 AI 生成。 -4. **mixed**:以上模式混合。每一段素材都必须能追溯到来源模式与授权记录。 - -通用入库要求: - -- 每个素材通过工具校验可解码,并记录分辨率、帧率、时长、音轨。 -- 建立素材清单与候选区索引;候选区文件名必须能反查源素材时间点。 -- 检查素材中是否有旧品牌名、水印、URL、域名、平台标识或不可授权元素。 -- 素材支撑度不足时,回到 Brief owner 处理;不得为了凑成片编造叙事。 - -## 叙事与植入套路 - -### 通用规则 - -- **单线目标**:主角动机一句话说清,并在片内闭环;素材支撑不了就改写,不留断头线。 -- **节拍 ≤ 6**:剧情段每节拍一句,句间因果可追;段尾悬停在求助、任务、待发或冲突节点。 -- **钩连句**:转生、化身、置换等转折必须用口播明写因果,并用同色、同物或同动作的画面衔接。 -- **意象桥**:产品/服务段第一镜必须复用剧情核心意象;禁止用无关风景空镜硬切。 -- **产品段**:3-4 句单点深打,至少覆盖 Brief 允许的 3 个功能/价值点,每点有对应画面;产品段占比 20%-26%。 -- **反转点**:two_stage 变体 63%-76%;concentrated_reversal 变体 55%-65%。比例是创作约束,不是硬编码参数。 -- **收束**:产品/服务段结束后立即收尾,不加无信息量的仪式句或氛围空镜。 -- **合规**:正片零 URL、零域名、零联系方式;官网、仓库、价格、活动入口只进入发布说明。产品能力与承诺只讲 Brief 允许的范围,不承诺收益数字。 -- **旁白语气**:除非 Brief 另有要求,使用第三人称解说体;避免问句、感叹号、第二人称和促销信号词。若口播文案 DNA 已启用,以 main 交付的 `voiceover.md` 为准。 - -### concentrated_reversal 变体 - -- 反转点之前**零产品提及**:不插 punch 句、不散落卖点、不出现品牌暗示。 -- 产品/服务介绍集中在反转后的一段完成,介绍完即收尾。 -- 开源免费、安装方式、活动入口等只在 Brief 要求时进入产品段口播或贴图;域名只进入发布说明。 -- 可在反转瞬间使用 whoosh 等音效标记剧情→产品切换,但不得用音效替代因果衔接。 - -## 制作护栏 - -1. **规格一次锁定**:横竖屏、目标时长、分辨率与帧率由 Brief / DNA 决定。不得为了“看起来高级”擅自放大素材;需要统一规格时,先确认素材源质量和部署环境约束,再写入 timeline 计划与决策日志。 -2. **帧率一致性**:混排来源素材时,先确认各段帧率;不一致必须通过 `assemble` 的归一化参数或分段预处理统一,不得把不同帧率直接 concat 后交付。输出后核对时长、音画同步与段边界。 -3. **旁白排布**:使用 `mix-audio` + `narration-align` 的实测时长 / 字级时间戳排布,不用文本长度估算。每句旁白不得越过对应镜头边界;连续旁白保留呼吸间隔,累计漂移要能追溯到每段实际时长。 -4. **字幕安全区**:`subtitles.srt` 必须来自对齐后的口播时间轴;烧录前检查目标画幅安全区,避免字幕落到画面中部或关键主体上。字幕样式由 Brief / DNA 的视觉边界决定,不硬编码项目个案参数。 -5. **素材四查**:候选帧与成片抽帧检查 URL / 域名、旧品牌名或水印、残缺元素、字幕遮挡。发现风险优先换干净素材窗口;裁切只能作为确认安全后的次选。 -6. **AIGC 原声**:AIGC 声画同出素材如含有效环境音,可作为低音量音床并在口播下 ducking;无有效音轨时补静音,不用噪声填充。 -7. **决策审计**:素材取舍、规格、音色、字幕样式、fallback 与弃用中间产物都写入 `script/decisions.json` 或 `final-deliver.md`,不留在口头说明。 - -## 原子能力映射 - -| Pipeline 环节 | 使用能力 | -| --- | --- | -| Brief 解析与决策审计 | Agent 读取 Brief;决策写 `script/decisions.json` | -| 口播/脚本 | main 交付 `voiceover.md` 时,原样落为 `script/script.md` 并锁定,不重写策略文案,仍跑 `script-self-eval` 做检查;未交付时用 `story-develop` → `script-write` → `script-self-eval` | -| 分镜与素材 slot | `storyboard-build` → `shot-decompose` → `slot-plan` | -| 素材获取 | `asset-resolve`(stock/AIGC slot)或按 Brief 素材清单入库;AIGC 段用 `render-shot` | -| 口播与字幕 | `mix-audio`;需要逐句对齐时 `narration-align` | -| 字幕烧录 | 使用已暴露的字幕原子命令(当前为 `video-edit subtitles`);不可用时向 Brief owner 报工具缺口,不手写 ffmpeg | -| 闸门前评估 | `slideshow-risk` → `delivery-promise-lock` | -| 切段与时间轴 | `clip-trim` / `timeline-compose` | -| 混音 | `audio-mix`;环境音、口播、BGM、音效按 Brief 与素材音轨处理 | -| 合成 | `assemble`;需要分段预合时 `scene-compose` | -| 动效审计 | `motion-audit` | -| 技术自检 | 公共 `video-review` | -| 封面 | `make-cover`,封面主文案与视觉承诺来自 Brief/DNA;wx_channel 无平台标题,封面主文案用核心传达 | -| 交付 | `final-deliver.md` | - -禁止直接手写 ffmpeg 命令;所有 ffmpeg 操作必须通过上述子命令或公共工具完成。机器资源限制、编码线程数、分辨率上限、低载参数等属于部署环境差异,读取 Content Producer workspace 的 `MEMORY.md` 或 Brief 中的环境约束,不写入本 Pipeline。 - -## 闸门 - -- **GATE A(文本闸门)**:口播/脚本、叙事节拍、意象桥、产品段与素材支撑计划齐备后,向 Brief owner 呈交摘要。若 main 已在 Brief 中代理批准 GATE A,记录批准范围后继续;否则停下等待批准。 -- **GATE B(素材闸门)**:素材清单、授权记录、候选区 contact sheet、风险检查与交付承诺齐备后,向 Brief owner 呈交。批准人是用户或 main,按 Brief `gates` 字段执行。 -- Brief 变更时升版本;已开工中间产物按新版取舍,弃用部分记入 `final-deliver.md`。 - -## 交付物 - -```text -/ - brief.md - voiceover.md # main 交付口播时 - script/ # 脚本、分镜、决策与自评 - materials/ # 原始素材与授权记录 - render/ # AIGC / 处理后素材 - audio/ # 口播、字幕、混音 - video.mp4 - cover.jpg - publish-desc.md # 平台包装文案或视频简介/署名/AI 标注/允许出现的链接 - final-deliver.md # 素材来源、授权、段时长、自检、遗留问题 -``` - -`publish-desc.md` 是发布说明,不是成片内容。Content Producer 不发布、不私信用户;预览由 Brief owner 或用户侧渠道发送。 - -## 验收检查 - -1. 正片零 URL、零域名、零联系方式。 -2. 品牌名、产品名、能力表述与 Brief 一致;无收益承诺。 -3. 叙事单线闭环,节拍 ≤ 6,转折因果可追。 -4. 产品段占比、反转点位置与选定变体一致。 -5. 每段素材来源与授权可追溯;AIGC 标注完整。 -6. 口播、字幕、画面、音效与 Brief / `voiceover.md` 一致。 -7. `video-review` verdict 为 pass;未通过不得交付。 -8. 交付说明包含弃用中间产物、fallback 决策和遗留问题。 diff --git a/crews/content-producer/skills/video-producer/scripts/reference-concepts.py b/crews/content-producer/skills/video-producer/scripts/reference-concepts.py deleted file mode 100644 index 1e97f0eb..00000000 --- a/crews/content-producer/skills/video-producer/scripts/reference-concepts.py +++ /dev/null @@ -1,85 +0,0 @@ -#!/usr/bin/env python3 -"""Stage 1 — reference-concepts:吃 main 喂入的 viral-chaser 报告出 2–3 差异化概念。 - -本技能不做视频下载/转写/抽帧——那是 viral-chaser 的活。只接报告原档当输入。 - -Usage: - python3 scripts/reference-concepts.py --report-file path - -入:project_dir(output_videos// 或平台运营目录 /outputs//)+ viral-chaser 报告路径 -出:project_dir/reference-driven/concepts.md(2–3 差异化概念 + 成本 + 备选路径) - -无报告则跳过本阶段,agent 直入 Stage 2 story-develop(本脚本不报错退出)。 -""" - -import argparse -import sys -from pathlib import Path - -def main() -> None: - parser = argparse.ArgumentParser(description="Stage 1 reference-concepts") - parser.add_argument("project_dir", help="项目目录(output_videos// 或平台运营目录 /outputs//)") - parser.add_argument("--report-file", default=None, help="main 喂入的 viral-chaser 报告路径") - args = parser.parse_args() - - project = Path(args.project_dir).resolve() - out_dir = project / "reference-driven" - out_dir.mkdir(parents=True, exist_ok=True) - concepts_path = out_dir / "concepts.md" - - # checkpoint - if concepts_path.is_file(): - print(f"[checkpoint] concepts.md 已存在,沿用:{concepts_path}") - return - - if not args.report_file: - print("[skip] 无 viral-chaser 报告输入,跳过 Stage 1,直入 Stage 2 story-develop") - return - - report = Path(args.report_file) - if not report.is_file(): - print(f"[warn] 报告文件不存在: {args.report_file},跳过 Stage 1") - return - - # 复制报告原档到工作区(不做任何下载/转写/抽帧,只存档供后续阶段参考) - import shutil - archived = out_dir / "viral-chaser-report.md" - if not archived.is_file(): - shutil.copy2(report, archived) - - # 提示 agent 据报告出 2–3 差异化概念写入 concepts.md - stub = f"""# 据参考片出的差异化概念(Stage 1) - -## 参考片来源 - -{archived.name}(main agent 喂入的 viral-chaser 追爆报告原档,未做下载/转写/抽帧)。 - -## 概念候选(agent 据报告填) - -> agent 读 archived 报告,出 2–3 个**差异化**概念(不抄原片,做差异化),每个概念含: -> - 名称与一句话定位 -> - 与参考片的差异化点(节奏/钩子/结构/调性任一的差异化) -> - 预算估算(USD) -> - 备选路径(如该概念走不通的 fallback) - -### 概念 1 -(agent 填) - -### 概念 2 -(agent 填) - -### 概念 3(可选) -(agent 填) - -## 用户选定 - -> 呈交用户选定一个概念,写入 brief.md。未选定前不进 Stage 2。 -""" - concepts_path.write_text(stub, encoding="utf-8") - print(f"[done] 报告已存档:{archived}") - print(f"[stub] concepts.md 模板已落:{concepts_path}") - print(f"[next] agent 据报告填概念 → 呈交用户选定 → 跑 story-develop(Stage 2)") - - -if __name__ == "__main__": - main() diff --git a/crews/main/AGENTS.md b/crews/main/AGENTS.md index a73a9451..ca4e4b08 100644 --- a/crews/main/AGENTS.md +++ b/crews/main/AGENTS.md @@ -77,7 +77,12 @@ index.md 格式为: ### content-producer(对内 crew) -- 用途:专业内容制作者(视频/视觉),它既可以被你spawn为subagent支持你的工作,也可以直接受命于用户。 +- 用途:专业内容制作者(视频/视觉),它既可以被你spawn为subagent支持你的工作,也可以直接受命于用户。它有两个专家包:`expert-video`(视频制作)与 `expert-design`(平面设计)。 +- **甲乙方分工**:你(甲方)负责选题策划、按 DNA 出 `brief.md`、拟定标题/短标题/简介、准备素材(简单预处理、`ui-demo` 录屏、从 `campaign_assets/` 挑选)并把**绝对路径**写进 Brief、口播类的口播文案(真人口播时向用户取录音文件)、监督推动 CP 进度、成片后的发布与运营;CP(乙方)只按 Brief 制作成片与封面。 +- **交接物**:你给「Brief + 已有素材绝对路径 + 口播文案/录音(如有)」,CP 回「成片 + 封面 + 交付说明」的绝对路径,你取回作品目录后再发布。 +- **Brief 不写 DNA**:CP 查不到你的 DNA,Brief 里不出现 dna-id、DNA 文档路径或 DNA 规则原文;DNA 结论由你消化成 Brief 的具体要求(选题、创意、形态与制作指向、规格、封面主文案)。 +- **不替 CP 建工作区**:CP 在自己的 workspace 下自建工作区;双方都是 T3,可互访对方工作区取文件。 +- Brief 的 `workflow` 字段写 CP 支持的视频类型(`reversal-ad` 影视解说+反转植入 / `narration-video` 口播类 / `collage-broll` 纸拼贴),形态未确定就省略,由 CP 自选。 - 启用流程: 1. **先判断** `openclaw.json` 的 `channels` 段是否已配置飞书 channel 或企业微信 channel。 2. **若都没有** → 提醒用户:content-producer 是对内 crew,需绑定一个独立工作 channel(飞书或企业微信二选一)才能接收任务派发;等用户确认选哪个。 diff --git a/crews/main/skills/_shared/volc_asr.py b/crews/main/skills/_shared/volc_asr.py index d82ed0e3..f4e9ceb2 100644 --- a/crews/main/skills/_shared/volc_asr.py +++ b/crews/main/skills/_shared/volc_asr.py @@ -2,7 +2,7 @@ 抽出前散在三处: - crews/main/skills/talking-head-cut/scripts/cut_plan.py 的 volc_asr() - - crews/content-producer/skills/video-producer/scripts/narration-align.py 的 fallback_asr() + - crews/content-producer/skills/expert-video/tools/video-producer/scripts/narration-align.py 的 fallback_asr() - crews/main/skills/viral-chaser/scripts/transcriber.ts 的 PYTHON_SCRIPT 内联段 三方调同一火山接口(volc.bigasr.auc_turbo),凭据同池: diff --git a/crews/main/skills/expert-douyin/SKILL.md b/crews/main/skills/expert-douyin/SKILL.md index 71065614..f14e33fd 100644 --- a/crews/main/skills/expert-douyin/SKILL.md +++ b/crews/main/skills/expert-douyin/SKILL.md @@ -1,6 +1,6 @@ --- name: expert-douyin -description: 抖音账号运营专家。承接定位起号、账号级 DNA、选题与包装、长文图文、已有素材轻加工、视频全案 Brief、发布与数据复盘;全片制作委托 content-producer。 +description: 抖音账号运营专家。承接定位起号、内容 DNA(视频 / 图文两套框架)、选题与包装、图文生产、已有素材轻加工、视频全案 Brief 与口播文案、发布与数据复盘;全片制作委托 content-producer。 metadata: openclaw: emoji: 🎵 @@ -14,7 +14,7 @@ metadata: | 场景 | Workflow | 什么时候触发 | |------|----------|-------------| -| 内容 DNA 管理 | Style DNA | 建 / 更新内容 DNA(样本、偏好、局部借鉴、对标融合),决定样本落到哪个 DNA | +| 内容 DNA 管理 | Style DNA | 建 / 更新内容 DNA(样本、偏好、局部借鉴、对标融合):先判作品类型,再决定样本落到哪个 DNA | | 内容生产 | Content Production | 做一条 / 做几条抖音内容;main 直接做已有素材轻加工,视频全案只产出并委托 Brief | | 起号与定位 | Account Setup | 新号起号、定位梳理、内容支柱搭建、老号接手与诊断 | | 账号对标 | Account Benchmark | 对标账号 / 对标视频分析,并与默认或指定 DNA 逐项比较 | @@ -37,7 +37,7 @@ metadata: | 工具 | 用途 | 命令 | |------|------|------| -| `douyin-style-profiler` | 生成单条视频的账号级 DNA report,并聚合 DNA 文档与 Brief template | `douyin-style-profiler` | +| `douyin-style-profiler` | 生成单篇作品(视频 / 图文,`--kind`)的 DNA report,并聚合 DNA 文档与 template(视频 = Brief + 口播文案模板;图文 = 写作模板) | `douyin-style-profiler` | | `douyin-comments` | 抓取抖音视频评论(对标分析 / 标签反推用,纯 HTTP 不起浏览器) | `douyin-comments` | | `douyin-publish` | 成片 → 抖音创作者中心发布(浏览器自动化) | `douyin-publish` | @@ -45,13 +45,13 @@ metadata: 制作链相关技能(边界见 Content Production Workflow):`video-edit`(素材加工拼接)、`talking-head-cut`(口播轻剪辑)、`ui-demo`(产品操作录屏)、`video-review`(成片质检闸门)、`siliconflow-img-gen`(封面图)、`pexels-footage` / `pixabay-footage`(免版权素材)。 -**分工硬边界**:main 只做已有视频素材的简单加工和长文 / 图文内容;视频全案只产出 Brief 并委托 `content-producer`。口播类视频若 DNA 启用口播文案 DNA,口播文案由 main 写好并随 Brief 交付;Content Producer 只负责声画制作。Brief 指定 Pipeline 时 CP 必须采用,未指定时 CP 自由发挥。 +**分工硬边界**:main 负责选题策划、按 DNA 出 **Brief**、拟定标题与简介、准备素材(用户素材预处理 / `ui-demo` 录屏 / 从 `campaign_assets/` 挑选,绝对路径写进 Brief)、监督推动 CP、成片后的发布与运营,也直接做图文内容与已有素材轻加工;视频全案的成片制作委托 `content-producer`。口播类视频的口播文案由 main 按 `narration-script` 子模块写好并随 Brief 交付(真人口播时向用户取得录音文件),CP 不重写策略文案。Brief 指定 `workflow` 时 CP 必须采用,未指定时 CP 按通用阶段链自由发挥。Brief **不含 DNA 信息**,main 也不替 CP 建工作区(双方 T3 权限可互访取文件)。 ## 风格与 DNA -账号级 DNA 存储目录是 `douyin/dna/`。未指定 DNA 时默认使用并更新 `dna-0`。生产前同时读取 DNA 文档与 DNA template;对标分析先建立独立对标 DNA,不默认写入 `dna-0`。 +DNA 存储目录是 `douyin/dna/`。未指定 DNA 时默认使用并更新 `dna-0`(视频);图文另建 dna-id(如 `dna-0-note`)。生产前同时读取 DNA 文档与 DNA template;对标分析先建立独立对标 DNA,不默认写入 `dna-0`。 -DNA 是账号级框架:定位与核心传达、选题组合、标题包装、账号简介、内容形式比例、发布习惯、高数据创意、视觉/声音倾向、口播文案 DNA、互动系列与制作管线。它指导 main agent 出内容或 Brief,不规定成片制作细节。维度框架 v1 位于 `douyin-style-profiler` 的 `references/account-dna-framework.md`。 +DNA 是**从一批作品样本提取并聚合出的内容生产规则集**(不存在「平台级 / 账号级 DNA」):视频 8 维——选题与观看理由、标题与封面、内容创意、视频内容形态与制作指向、制作规格与视听倾向,加可选的口播文案子模块与账号运营子模块(简介写法、内容形式比例、发布习惯);图文 8 维——选题、标题与封面图组、内容创意、正文表达与语气、图组视觉、互动引导与转化,加账号运营子模块。它指导 main agent 出图文内容或视频 Brief(+ 口播文案),不规定创作细节与成片制作。维度框架 v2 位于 `douyin-style-profiler` 的 `references/video-dna-framework.md` 与 `references/note-dna-framework.md`。 ## 数据与记录 diff --git a/crews/main/skills/expert-douyin/tools/douyin-style-profiler/SKILL.md b/crews/main/skills/expert-douyin/tools/douyin-style-profiler/SKILL.md index c3ebf8d1..9d6e5cf2 100644 --- a/crews/main/skills/expert-douyin/tools/douyin-style-profiler/SKILL.md +++ b/crews/main/skills/expert-douyin/tools/douyin-style-profiler/SKILL.md @@ -1,6 +1,6 @@ --- name: douyin-style-profiler -description: 提取抖音账号级 DNA:单条视频生成 report,按 DNA ID 聚合定位、选题、标题、内容形式、发布节奏、高数据创意与制作管线,推导 main agent 的 Brief template。 +description: 提取抖音作品 DNA:单篇作品(视频 / 图文)生成 report,按 dna-id 聚合选题、标题与封面、内容创意、视频形态与制作指向、制作规格、口播文案子模块与账号运营子模块,推导 main agent 的 Brief / 图文生产 template。 metadata: openclaw: emoji: 🧬 @@ -8,21 +8,34 @@ metadata: # douyin-style-profiler -抖音账号级 DNA 提取与聚合工具。输入是**视频转录文本**(口播全文 + 标题/描述/时长/互动线索,由 Agent 先整理成 `.md` / `.txt`)与可选封面 / 首帧图;不接受视频文件或链接作为直接输入。 +抖音作品 DNA 提取与聚合工具。输入是**视频转录文本**(口播全文 + 标题 / 描述 / 时长 / 互动线索)或**图文文本**(标题 + 正文 + 话题标签),由 Agent 先整理成 `.md` / `.txt`;可选封面 / 首帧 / 配图作为视觉证据。不接受视频文件或链接作为直接输入(取数走 `viral-chaser` 或平台工具)。 -DNA 的用途是指导 main agent 选题、包装、账号表达和视频制作 Brief;不指导 main agent 直接产出全片。 +DNA 的用途是指导 main agent 选题、包装、出内容或出视频制作 Brief;**不指导成片制作**——创作细节、脚本结构、镜头与编码参数归 Content Producer。 + +## 作品类型(`--kind`) + +抖音有两套维度框架,用 `--kind` 选择;**一个 `dna-id` 只承载一种作品类型**: + +| kind | 作品 | 维度框架 | 默认 dna-id | +|------|------|----------|-------------| +| `video` | 视频 | `references/video-dna-framework.md` | `dna-0` | +| `note` | 图文 | `references/note-dna-framework.md` | `dna-0-note` | + +- 不传 `--kind` 时默认 `video`(抖音的主作品类型)。 +- `build` / `update` 会校验同一 DNA 目录下 report 的 kind 一致,混型直接报错——另一种类型请另建 dna-id。 +- 默认 dna-id 只是约定:用户可以指定任意 dna-id,脚本不强制命名。 ## 产物模型 ```text -单条视频 -> DNA report -同一 DNA 下的全部 report + 权重/focus + 用户输入 -> DNA 文档 -DNA 文档 -> main agent Brief template +单篇作品 -> DNA report +同一 dna-id 下全部 report + 权重 / focus + 用户输入转译 -> DNA 文档 +DNA 文档 -> DNA template ``` -- **DNA report**:单条样本的账号级观测与 13 维提取结果。 -- **DNA 文档**:聚合后的账号级规则与样本覆盖度说明。 -- **DNA template**:main agent 生成内容或委托 Content Producer 时使用的 Brief / 生产输入模板。 +- **DNA report**:单篇作品的样本观测 + 维度提取结果(不是账号级结论,也不是模板)。 +- **DNA 文档**:聚合后的生产规则、样本覆盖度、子模块结论与用户输入转译区。 +- **DNA template**:main agent 的生产输入模板(视频 = Brief 正文 + 口播文案;图文 = 写作模板)。 ## 存储结构 @@ -36,87 +49,79 @@ douyin/dna/{dna-id}/ {dna-id}.template.md ``` -原始转录文本可临时放在 `douyin/ref/{dna-id}/transcripts/`;生成后的 report 必须进入目标 DNA 的 `reports/` 目录。 +原始转录 / 正文文本可临时放在 `douyin/ref/{dna-id}/transcripts/`;生成后的 report 必须进入目标 DNA 的 `reports/` 目录。 ## 职责边界 -- 单条样本只提供候选信号;账号比例、发布节奏、高数据共性必须由多样本或账号级数据聚合。 -- 统计只做证据底座,不评分、不替代定性判断。 -- 视觉语言有图片/关键帧证据时才由视觉模型分析;缺失写「未观测」。 -- 口播文案 DNA 独立聚合;样本不足时保持未启用。 +- 抖音视频与图文都常见:先判作品类型再选框架,不要把图文笔记塞进视频 DNA。 +- 单篇 report 只提供候选信号,不判断跨篇稳定性;共性、偏好、孤例由聚合阶段判断。 +- 统计只做证据底座,不评分、不判定风格是否合格。 +- 视觉维度必须有图片 / 关键帧证据,由视觉模型读取;缺失写「未提供」,不得凭文本想象补齐。 +- 口播文案子模块只在口播类作品启用;样本不足写「未启用」。 +- 账号运营子模块(简介写法、内容形式比例、发布习惯)只在样本来自对标账号批量提取时填写,且**不进 template**。 - 不输出合规结论、账号权重或风格评分。 -## Report - 单条提取 +## Report — 单篇提取 ```bash +# 视频样本(不传 --kind,默认 video) douyin-style-profiler report \ --input path/to/transcript.md \ --dna-id {dna-id} \ --sample-id {sample-id} \ + --duration 36 \ --cover-image path/to/cover.jpg \ --source-url "https://www.douyin.com/video/..." \ - --duration 89 \ --output-dir douyin/dna/{dna-id}/reports + +# 图文样本 +douyin-style-profiler report \ + --input path/to/note.md \ + --kind note \ + --dna-id {dna-id}-note \ + --sample-id {sample-id} \ + --cover-image path/to/cover.jpg \ + --output-dir douyin/dna/{dna-id}-note/reports ``` -- `--cover-image`:封面或首帧本地图片,用于视觉证据。 -- `--source-url`:原视频链接;本地素材无链接时省略。 -- `--duration`:视频时长(秒),用于口播密度统计。 +- `--kind`:作品类型(见上);不传走默认。 +- `--cover-image`:封面 / 首帧 / 首图本地文件,作为视觉证据(自动拷进 `covers/`)。 +- `--source-url`:原作品链接;本地素材无链接时省略。 +- `--duration`:视频时长(秒),用于口播密度统计;图文忽略。 - `--weight`:样本权重,默认 1。 -- `--focus`:限制该样本只影响指定维度,可重复传入。 +- `--focus`:限制该样本只影响指定维度 ID,可重复传入。 Agent 生成 scaffold 后必须: -1. 补齐「样本与账号观测」:样本类型、账号与简介、发布时间、内容形式、数据线索、视频形态与授权信息。 -2. 回读转录原文,补齐 13 维的单条结论、原文证据和可复用信号。 -3. 对账号简介、内容比例、发布节奏等账号级字段,单条样本无法观测时写「未观测」。 -4. 高数据样本必须回读创意与内容形式,不得只凭播放量下结论。 +1. 补齐「样本观测」:作品类型、样本来源、账号与简介、发布时间、数据线索、素材来源与授权。 +2. 回读原文,补齐各维度的单篇结论、原文证据与可复用信号。 +3. 视频形态必须给出**制作指向**(Content Producer `expert-video` 的某个 workflow,或 main 的某个素材加工技能),只写真实存在的资源名。 +4. 账号运营子模块无法从单篇观测时写「未观测」。 +5. 高数据样本必须回读创意与形态再归因,不得只凭播放 / 阅读量下结论。 -## Build - 聚合 DNA 文档与模板 +## Build — 聚合 DNA 文档与模板 ```bash -douyin-style-profiler build --dna-id {dna-id} +douyin-style-profiler build --dna-id {dna-id} # 视频 DNA(默认 kind=video) +douyin-style-profiler build --dna-id {dna-id}-note --kind note # 图文 DNA ``` -默认读取 `douyin/dna/{dna-id}/reports/`,输出: - -```text -douyin/dna/{dna-id}/{dna-id}.dna.md -douyin/dna/{dna-id}/{dna-id}.template.md -``` - -也可显式传入 report 文件/目录: +默认读取 `douyin/dna/{dna-id}/reports/`,输出 `{dna-id}.dna.md` 与 `{dna-id}.template.md`。也可显式传 report 文件 / 目录: ```bash -douyin-style-profiler build \ - --input path/to/reports \ - --dna-id {dna-id} +douyin-style-profiler build --input path/to/reports --dna-id {dna-id} ``` Agent 聚合时必须: 1. 读取全部 DNA report,不能只看统计表。 2. 按 `weight` 与 `focus` 判断影响范围。 -3. 区分高频共性、高权重偏好、局部借鉴、孤例和例外。 -4. 标注样本覆盖度;单条/少量样本不得称为稳定账号 DNA。 -5. 为每个维度写聚合结论、报告依据和可执行规则。 -6. 确保 DNA 文档能完整推导 template。 - -## DNA Template - -Template 是 main agent 的 Brief / 内容生产输入模板,不是成片制作模板。固定语义段: +3. 区分高覆盖共性、高权重偏好、局部借鉴、孤例与例外。 +4. 标注样本覆盖度;少量样本不得称为稳定结论。 +5. 为每个维度写聚合结论、报告依据与可执行创作规则。 +6. 确保 DNA 文档能完整推导 template(template 不得引入 DNA 文档未确认的规则)。 -1. 定位与核心传达 -2. 选题与标题包装 -3. 内容形式与发布节奏 -4. 高数据创意模式 -5. 互动与系列 -6. 制作交接与 Pipeline -7. 口播文案 DNA(可选) - -制作交接段必须写清 MainAgent 与 Content Producer 的交付物、Pipeline、素材授权和风格边界。Brief 未指定 Pipeline 时,Content Producer 可自由选择;指定 Pipeline 时必须直接采用。 - -## Update - 增量聚合 +## Update — 增量聚合 ```bash douyin-style-profiler update \ @@ -125,54 +130,91 @@ douyin-style-profiler update \ --template douyin/dna/{dna-id}/{dna-id}.template.md ``` -脚本合并历史 report 与新 report,重新计算统计并保留 Agent 已完成内容。Agent 仍需重新审视聚合结论,并同步修订 DNA 文档和 template。 +脚本合并历史 report 与新 report、重算统计并保留 Agent 已写内容;kind 从 DNA 文档 frontmatter 继承(也可用 `--kind` 显式指定,冲突时报错)。Agent 仍需重新审视聚合结论并同步修订 template。 `--input` 可省略,用于只融合用户输入或另一个 DNA 的局部规则;此时必须传 `--user-input`。 +## DNA Template + +**视频作品 template(= Brief 正文模板 + 口播文案模板)** + +1. 选题 +2. 标题与封面 +3. 内容创意 +4. 视频形态与制作指向 +5. 制作规格 +6. 口播文案 + +**图文作品 template(= 图文写作模板)** + +1. 选题 +2. 标题与封面 +3. 内容创意与结构 +4. 正文表达 +5. 图组 +6. 互动与标签 + +- 开头两段(**选题**、**标题与封面**)跨平台通用。 +- 视频 template 的各段直接对应 Brief 正文字段;**Brief 不含 DNA 信息**(Content Producer 看不到 main 的 DNA),素材清单与授权、验收标准、闸门批准人按平台 Content Production Workflow 填。 +- 账号运营子模块不进 template,只留在 DNA 文档。 + ## 用户输入转译 -用户输入是参考信息,不是可直接入库的 DNA 规则。 +用户输入是参考信息,不是可直接入库的 DNA 规则: ```bash ---user-input "账号更偏实拍拼接,少用纯动画" +--user-input "以后多做实拍拼接,少用纯动画" ``` -Agent 必须转译到具体维度,例如: +Agent 必须映射到具体维度并转译为可执行规则,例如: ```text -content-form-mix:主形态为实拍拼接,纯动画仅作补充 -production-pipeline:Brief 默认使用 video-producer:default(montage) -visual-language:画面保持实拍质感,不采用插画化风格 +video-form:主形态为影视解说 + 反转植入,制作指向 expert-video 的 Reversal Ad workflow +production-spec:竖屏 9:16、时长带 30-45s、画面偏冷暗解说段 + 明亮植入段 +narration-script:口播保持第三人称解说体,句长 10-15 字 ``` 处理要求: -1. 在 DNA 文档的「用户输入转译区」记录 affected dimensions、DNA 修改和 template 修改。 +1. 在 DNA 文档「用户输入转译区」记录 raw_input、affected dimensions、DNA 修改、template 修改与状态。 2. 原话必须转译为可执行规则,不得直接抄进 template。 3. 与样本证据冲突时保留冲突说明,由用户选择优先级。 ## Focus ID +### video(视频) + | ID | 维度 | |---|---| -| `positioning-core` | 定位与核心传达 | -| `topic-portfolio` | 选题组合 | -| `title-packaging` | 标题与包装 | -| `bio-profile` | 账号简介与主页表达 | -| `content-form-mix` | 内容形式与比例 | -| `publish-cadence` | 发布习惯 | -| `high-performer-patterns` | 高数据创意模式 | -| `visual-language` | 视觉语言 | -| `audio-language` | 声音语言 | -| `narration-dna` | 口播文案 DNA | -| `engagement-conversion` | 互动与转化 | -| `series-signature` | 系列与签名 | -| `production-pipeline` | 制作管线倾向 | +| `topic-angle` | 选题与观看理由 | +| `title-cover` | 标题与封面 | +| `content-idea` | 内容创意 | +| `video-form` | 视频内容形态与制作指向 | +| `production-spec` | 制作规格与视听倾向 | +| `narration-script` | 口播文案子DNA | +| `account-bio` | 账号简介写法 | +| `content-mix-cadence` | 内容形式比例与发布习惯 | + +### note(图文) + +| ID | 维度 | +|---|---| +| `topic-angle` | 选题与观看理由 | +| `title-cover` | 标题与封面图组 | +| `content-idea` | 内容创意 | +| `body-voice` | 正文表达与语气 | +| `imageset-visual` | 图组视觉风格 | +| `interaction-cta` | 互动引导与转化 | +| `account-bio` | 账号简介写法 | +| `content-mix-cadence` | 内容形式比例与发布习惯 | + +`--focus` 按作品类型校验:视频 report 不接受图文维度 ID,反之亦然。 ## 统计与分词 -脚本统计转录文本的句段、标点、人称、口播密度等指标;中文高频信号使用相邻二字组合,仅作为候选线索。Agent 必须回读原文确认口头禅或签名式表达。分词结果不能直接作为 DNA 结论。 +脚本统计句长、问句与人称密度、感叹号密度、口播密度(视频)/ 标题字数、正文行数、emoji 密度、话题标签数(图文)等指标作为聚合证据底座;中文高频信号使用相邻二字组合,仅作候选线索。口头禅与签名式表达必须由 Agent 回读原文确认,分词结果不能直接当 DNA 结论。 ## 参考资料 -- `references/account-dna-framework.md`(抖音账号级 DNA 框架 v1、Pipeline 映射与聚合边界) +- `references/video-dna-framework.md`(抖音视频作品 DNA 框架 v2:维度定义、制作指向映射、聚合边界与 Focus ID) +- `references/note-dna-framework.md`(抖音图文作品 DNA 框架 v2:维度定义、制作指向映射、聚合边界与 Focus ID) diff --git a/crews/main/skills/expert-douyin/tools/douyin-style-profiler/references/account-dna-framework.md b/crews/main/skills/expert-douyin/tools/douyin-style-profiler/references/account-dna-framework.md deleted file mode 100644 index 9a95a304..00000000 --- a/crews/main/skills/expert-douyin/tools/douyin-style-profiler/references/account-dna-framework.md +++ /dev/null @@ -1,83 +0,0 @@ -# 抖音账号级 DNA 框架(v1) - -> 状态:v1。Focus ID、维度命名与 template 语义以本文件为准。调整维度必须升版本,并同步 `scripts/build_style_profile.py` 与工具 `SKILL.md`。 - -## 定位与边界 - -抖音 DNA 是**账号级运营框架**,不是单条视频的制作细节清单。它回答这个账号长期如何被识别、如何选题、如何包装、如何安排内容形式与发布节奏,并指导 main agent 产出制作 Brief。 - -- **必须进入 DNA**:定位与核心传达、选题组合、标题与包装、账号简介、图文/视频等内容形式及比例、发布时间段与混合节奏、高数据内容的创意共性、稳定视觉/声音倾向、口播文案 DNA、互动与系列标记、制作管线倾向。 -- **不进入主 DNA**:单镜运镜、逐句文案、转场参数、字幕样式、成片剪辑微操。口播文案 DNA 是独立可选块;没有证据时写「未观测」,不强行规定。 -- **MainAgent / Content Producer 分界**:main agent 负责已有视频素材的简单加工、长文/图文内容,以及视频全案 Brief。除 Brief 之外的成片制作由 Content Producer 执行。口播类视频的口播文案若由 DNA 明确启用,则由 main agent 写好并随 Brief 交付;Content Producer 只做声画制作。 -- **样本可以是账号或单条**:账号样本可提取账号简介与发布习惯;用户提供单条或多条单篇时,仍按同维度统计聚合,但账号简介、比例、发布节奏等字段必须标「样本不足 / 未观测」。 - -## 维度(13 维) - -### 一、定位与选题 - -| # | ID | 维度 | 观测内容 | -|---|----|------|----------| -| 1 | `positioning-core` | 定位与核心传达 | 一句话定位、目标人群、账号长期让用户记住的核心承诺、每条内容的主张边界 | -| 2 | `topic-portfolio` | 选题组合 | 常见选题类型、内容支柱、选题入口、选题之间的系列关系;不判断单条选题好坏 | -| 3 | `title-packaging` | 标题与包装 | 标题/描述模式、封面或首帧包装承诺、话题标签策略;不规定具体字句 | -| 4 | `bio-profile` | 账号简介与主页表达 | 昵称、简介、置顶内容、主页视觉与承诺;仅账号样本可得 | - -### 二、账号节奏 - -| # | ID | 维度 | 观测内容 | -|---|----|------|----------| -| 5 | `content-form-mix` | 内容形式与比例 | 图文、视频、口播、实拍拼接、创意转场、纯 AIGC 动画等形式;如混合,记录比例或倾向 | -| 6 | `publish-cadence` | 发布习惯 | 发布时间段、频率、内容形式混合节奏(如三图文对一视频)、栏目化节奏 | -| 7 | `high-performer-patterns` | 高数据创意模式 | 高互动/高播放样本的选题、包装、形式与创意共性;数据只是证据,不自动等于风格 | - -### 三、表达与风格 - -| # | ID | 维度 | 观测内容 | -|---|----|------|----------| -| 8 | `visual-language` | 视觉语言 | 稳定画面风格、横竖屏、视觉记忆点;不规定镜头参数与逐镜设计 | -| 9 | `audio-language` | 声音语言 | 配音类型/音色倾向、口播密度、BGM/音效倾向;不规定 TTS 参数 | -| 10 | `narration-dna` | 口播文案 DNA | 独立可选块:开头钩子、起承转合、收束、人称与语气、签名表达;与主 DNA 分开判断稳定性 | - -### 四、互动与制作 - -| # | ID | 维度 | 观测内容 | -|---|----|------|----------| -| 11 | `engagement-conversion` | 互动与转化 | 评论/关注/私信等平台内行动、承接路径、引导强度 | -| 12 | `series-signature` | 系列与签名 | 栏目名、固定开场/收尾、系列承诺、识别符号 | -| 13 | `production-pipeline` | 制作管线倾向 | 视频内容形态到 Content Producer Pipeline 的映射;未指定时交给 CP 自由发挥 | - -## Pipeline 映射 - -`production-pipeline` 只能写 Content Producer 已支持的管线,不得发明不存在的管线名。 - -| DNA 观测形态 | Brief 中的 Pipeline | -|--------------|---------------------| -| 影视解说 + 反转植入 | `dna-ad-video-pipeline` | -| 口播 / 真人讲述 | `video-producer:default`(narrative) | -| 实拍拼接 / 蒙太奇 | `video-producer:default`(montage) | -| 创意转场 / 动效 | `video-producer:default`(motion) | -| 纯 AIGC 动画 | `video-producer:default`(AIGC render) | - -Brief 未写 `pipeline` 时,Content Producer 按 `video-producer:default` 自由选择实现;Brief 写了 Pipeline 时必须直接采用,不得替换成自创流程。 - -## Report 与聚合规则 - -1. 单条 report 先记录「样本与账号观测」:样本类型、账号与简介、发布时间、内容形式、数据线索、视频形态、授权信息。缺失写「未观测」。 -2. 单条样本不能推导账号比例、发布节奏或高数据共性;聚合时至少要有多个样本或账号级数据,并在 DNA 文档标注覆盖度。 -3. 高数据样本必须回读创意与内容形式,不能只凭播放量归因。 -4. 口播文案 DNA 独立聚合;没有足够样本时保持未启用。 -5. 脚本统计只提供句长、人称、口播密度等证据;语义判断由 Agent 回读原文完成。 - -## Template 语义 - -DNA template 是 main agent 的 **Brief / 内容生产输入模板**,不是成片制作模板。固定语义段: - -1. 定位与核心传达 -2. 选题与标题包装 -3. 内容形式与发布节奏 -4. 高数据创意模式 -5. 互动与系列 -6. 制作交接与 Pipeline -7. 口播文案 DNA(可选) - -模板不得规定镜头表、逐句台词、转场参数或编码细节。视频全案的制作选择由 Content Producer 在 Pipeline 内完成。 diff --git a/crews/main/skills/expert-douyin/tools/douyin-style-profiler/references/note-dna-framework.md b/crews/main/skills/expert-douyin/tools/douyin-style-profiler/references/note-dna-framework.md new file mode 100644 index 00000000..83af0eb8 --- /dev/null +++ b/crews/main/skills/expert-douyin/tools/douyin-style-profiler/references/note-dna-framework.md @@ -0,0 +1,102 @@ +# 抖音图文作品 DNA 框架(v2) + +> 状态:v2(2026-09-10)。Focus ID、维度命名与 template 语义以本文件为准;调整维度必须升版本,并同步 `scripts/build_style_profile.py` 与工具 `SKILL.md`。 +> 抖音有两套框架:视频作品用 `video-dna-framework.md`,本文件是**图文作品**框架。 + +## 定位与边界 + +DNA 不是「平台级 DNA」,也不是账号人设说明书,而是**从一批作品样本中提取、聚合出的内容生产规则集**。生产方式与其他平台完全一致,仍是三层产物: + +```text +单篇作品 -> DNA report(本框架的维度逐项提取) +同一 dna-id 下全部 report + 权重 / focus + 用户输入转译 -> DNA 文档 +DNA 文档 -> DNA template +``` + +- 同一批样本可以来自多个不同账号,也可以来自用户指定的一个账号(从其发布列表批量提取);是否归入同一个 DNA 由用户指定,未指定入 `dna-0`。 +- 一个 `dna-id` 只承载一种作品类型:抖音默认 dna-0 为视频,图文样本请另建 dna-id(如 dna-0-note)。 +- **与其他平台的差别只在维度**:本平台作品的观测物是标题 + 正文 + 图组 + 标签,因此不照搬公众号 17 维里的表层 / 深层写作维度(用词习惯、句式节奏、修辞手法、论证逻辑等)。 + +### 进 DNA 与不进 DNA + +| 进 DNA | 不进 DNA | +|--------|----------| +| 选题与观看理由、标题与封面写法、内容创意原型、正文表达与语气、图组视觉、互动引导与转化、(对标账号)账号运营子模块 | 创作细节、脚本结构、逐句台词、镜头表、转场与编码参数——那些归 Content Producer | + +图文 DNA 的用途是指导 main agent 直接生产图文作品;template 就是图文写作模板。账号运营子模块(简介写法、内容形式比例、发布习惯)只写进 DNA 文档,**不进 template**。 + +## 维度(8 维) + +### 一、选题与包装 + +| # | ID | 维度 | 观测内容 | +|---|----|------|----------| +| 1 | `topic-angle` | 选题与观看理由 | 选题类型、选题入口(现象 / 问题 / 冲突 / 数据 / 热点 / 挑战 / 个人经历)、目标人群为什么要看完(理解 / 判断 / 行动 / 避坑 / 身份认同 / 情绪共鸣)、系列与内容支柱归属 | +| 2 | `title-cover` | 标题与封面图组 | 标题类型与原文、封面承诺(用户点进来的可见理由)、版式(大字报 / 实拍场景 / 对比图 / 清单卡片)、色彩与文字视觉、封面与图组风格一致性、AIGC 复现要素 | + +### 二、内容创意 + +| # | ID | 维度 | 观测内容 | +|---|----|------|----------| +| 3 | `content-idea` | 内容创意 | 一句话创意内核、创意类型、展开逻辑(悬念 / 反转 / 递进 / 对比 / 清单 / 实测)、记忆点;这个套路换主题还能怎么用 | + +### 三、正文与视觉 + +| # | ID | 维度 | 观测内容 | +|---|----|------|----------| +| 4 | `body-voice` | 正文表达与语气 | 开头钩子、正文组织方式(清单体 / 教程步骤 / 故事线 / 对比 / 观点输出)、分行与段落节奏、口语化程度与人称、emoji 与标点用法、签名式表达 | +| 5 | `imageset-visual` | 图组视觉风格 | 图片数量与顺序、构图类型(产品展示 / 场景 / 文字卡片 / 对比图 / 过程图)、图文信息分工、色调与质感、版式一致性、文字视觉 | + +### 四、互动与转化 + +| # | ID | 维度 | 观测内容 | +|---|----|------|----------| +| 6 | `interaction-cta` | 互动引导与转化 | 评论 / 收藏 / 关注 / 进店 / 咨询等平台内行动的引导方式与位置、每篇行动引导数量、话题标签承载的意图、合规边界 | + +### 五、账号运营子模块(对标账号样本才有) + +| # | ID | 维度 | 观测内容 | +|---|----|------|----------| +| 7 | `account-bio` | 账号简介写法 | 账号昵称、简介写法、主页与置顶表达、对外承诺(仅对标账号样本可得) | +| 8 | `content-mix-cadence` | 内容形式比例与发布习惯 | 图文 / 视频等内容形式比例、发布时间段与频率、内容形式混合节奏(如三篇图文对一篇视频)、栏目化节奏(仅对标账号批量样本可得) | + +## Report 与聚合规则 + +1. 单篇 report 先填「样本观测」:作品类型、样本来源、账号与简介、发布时间、数据线索、图片数量与来源、关键词与标签。缺失一律写「未观测」,不得编造。 +2. 单篇 report 不判断跨篇稳定性;聚合时区分高覆盖共性、高权重样本偏好、局部借鉴(focus)、孤例与例外,并标注样本覆盖度。 +3. 视觉维度(封面 / 首帧 / 图组)必须有图片证据,由视觉模型读取本地图片后反推 AIGC 复现要素;没有图片写「未提供」。 +4. 正文表达维度里的高频词、口头禅与签名表达只是候选线索,必须回读原文确认后才能进 DNA 文档。 +5. 账号运营子模块(`account-bio`、`content-mix-cadence`)只在样本来自对标账号批量提取时填写;用户只提供单篇时写「未观测」。 +6. 脚本统计只作证据底座(标题字数、正文行数、句长、问句密度、emoji 密度、话题标签数),不生成总分、不判定风格是否合格。 +7. 高数据样本必须回读创意、结构与关键词再归因,不得把高阅读直接等同于风格好。 + +## Template 语义 + +DNA template = **图文写作模板**,固定语义段如下(脚本 `build` / `update` 自动生成骨架,字段由 Agent 从 DNA 文档推导填写): + +1. **[选题部分]** — 选题角度推荐、选题需考虑的受众关联角度、内容支柱与系列关系、避免 +2. **[标题与封面部分]** — 标题类型、参考标题、封面或首帧风格、封面 AIGC 提示词要素、话题标签策略 +3. **[内容创意与结构部分]** — 创意原型、正文组织方式、信息密度、记忆点、避免 +4. **[正文表达部分]** — 开头钩子、推进方式、段落与分行节奏、人称与语气、emoji 与标点、签名式表达、必须做、避免 +5. **[图组部分]** — 图片数量与顺序、构图类型、色调与质感、版式一致性、文字视觉、AIGC 提示词要素 +6. **[互动与标签部分]** — 互动目标、引导方式、话题标签策略、合规边界 + +- 开头两段(**选题**、**标题与封面**)跨平台通用:任何生产都先解决「做什么」和「怎么命名、怎么呈现封面」。 +- `[图组部分]` 与 `[正文表达部分]` 是两条并行的生产轨道:图组规则管视觉,正文规则管文字。 +- 账号运营子模块不进 template:`account-bio` 与 `content-mix-cadence` 的结论只留在 DNA 文档,供起号、对标与发布节奏决策使用。 +- template 必须能从 DNA 文档推导,不得引入 DNA 文档未确认的规则;用户输入必须先映射到维度再转译为执行规则。 +- 分段数量由 DNA 文档的结构结论决定,不固定为三段或四段;固定的是语义部分,不是物理段落数量。 + +## Focus ID 表 + +`--focus` 只接受本框架的维度 ID;限制某篇样本只影响这些维度。 + +| 分组 | Focus ID | +|------|----------| +| 选题与包装 | `topic-angle` `title-cover` | +| 内容创意 | `content-idea` | +| 正文与视觉 | `body-voice` `imageset-visual` | +| 互动与转化 | `interaction-cta` | +| 账号运营子模块(对标账号样本才有) | `account-bio` `content-mix-cadence` | + +focus 校验按作品类型执行:视频 report 不接受图文维度 ID,反之亦然。 diff --git a/crews/main/skills/expert-douyin/tools/douyin-style-profiler/references/video-dna-framework.md b/crews/main/skills/expert-douyin/tools/douyin-style-profiler/references/video-dna-framework.md new file mode 100644 index 00000000..360651c6 --- /dev/null +++ b/crews/main/skills/expert-douyin/tools/douyin-style-profiler/references/video-dna-framework.md @@ -0,0 +1,117 @@ +# 抖音视频作品 DNA 框架(v2) + +> 状态:v2(2026-09-10)。Focus ID、维度命名与 template 语义以本文件为准;调整维度必须升版本,并同步 `scripts/build_style_profile.py` 与工具 `SKILL.md`。 +> 抖音有两套框架:图文作品用 `note-dna-framework.md`,本文件是**视频作品**框架。 + +## 定位与边界 + +DNA 不是「平台级 DNA」,也不是账号人设说明书,而是**从一批作品样本中提取、聚合出的内容生产规则集**。生产方式与其他平台完全一致,仍是三层产物: + +```text +单篇作品 -> DNA report(本框架的维度逐项提取) +同一 dna-id 下全部 report + 权重 / focus + 用户输入转译 -> DNA 文档 +DNA 文档 -> DNA template +``` + +- 同一批样本可以来自多个不同账号,也可以来自用户指定的一个账号(从其发布列表批量提取);是否归入同一个 DNA 由用户指定,未指定入 `dna-0`。 +- 一个 `dna-id` 只承载一种作品类型:抖音默认 dna-0 为视频,图文样本请另建 dna-id(如 dna-0-note)。 +- **与其他平台的差别只在维度**:本平台作品的观测物是口播转录文本 + 画面 + 声音 + 结构节奏,因此不照搬公众号 17 维里的表层 / 深层写作维度(用词习惯、句式节奏、修辞手法、论证逻辑等)。 + +### 进 DNA 与不进 DNA + +| 进 DNA | 不进 DNA | +|--------|----------| +| 选题与观看理由、标题与封面写法、内容创意原型、视频内容形态与制作指向、制作规格与视听倾向、(口播类)口播文案子DNA、(对标账号)账号运营子模块 | 创作细节、脚本结构、逐句台词、镜头表、转场与编码参数——那些归 Content Producer | + +视频 DNA 的用途是指导 main agent 出具 **Brief.md** 与(口播类的)**口播文案**。template 就是 Brief 正文模板 + 口播文案模板(可选),不是成片制作模板。账号运营子模块(简介写法、内容形式比例、发布习惯)只写进 DNA 文档,**不进 template**。 + +## 维度(8 维) + +### 一、选题与包装 + +| # | ID | 维度 | 观测内容 | +|---|----|------|----------| +| 1 | `topic-angle` | 选题与观看理由 | 选题类型、选题入口(现象 / 问题 / 冲突 / 数据 / 热点 / 挑战 / 个人经历)、目标人群为什么要看完(理解 / 判断 / 行动 / 避坑 / 身份认同 / 情绪共鸣)、系列与内容支柱归属 | +| 2 | `title-cover` | 标题与封面 | 标题类型(痛点 / 数字 / 反差 / 悬念 / 身份点名 / 搜索长尾)、标题与描述原文、话题标签策略;封面或首帧的视觉特征与 AIGC 复现要素 | + +### 二、内容创意 + +| # | ID | 维度 | 观测内容 | +|---|----|------|----------| +| 3 | `content-idea` | 内容创意 | 一句话创意内核、创意类型、展开逻辑(悬念 / 反转 / 递进 / 对比 / 清单 / 实测)、记忆点;这个套路换主题还能怎么用 | + +### 三、形态与规格 + +| # | ID | 维度 | 观测内容 | +|---|----|------|----------| +| 4 | `video-form` | 视频内容形态与制作指向 | 视频内容形态(口播 / 实拍拼接 / 影视解说+反转植入 / 纯 AIGC 动画 / 创意转场动效 / 录屏演示 / 图文卡片视频 / 混合)+ 判定依据 + **制作指向**(见下方映射表) | +| 5 | `production-spec` | 制作规格与视听倾向 | 横屏或竖屏、时长带、画面风格(色调、质感、字幕样式倾向、信息密度)、配音音色与声音形态(原声口播 / TTS / 旁白 / 纯画面字幕)、BGM 与音效倾向、封面规格 | + +### 四、口播文案子模块(可选,仅口播类启用) + +| # | ID | 维度 | 观测内容 | +|---|----|------|----------| +| 6 | `narration-script` | 口播文案子DNA | 起(从什么起步)、承(靠什么推进)、转(转折触发)、合(收束与 CTA)、人称与语气、句长与语速、签名式表达——用于指导 main agent 写同类型视频的口播文案 | + +### 五、账号运营子模块(对标账号样本才有) + +| # | ID | 维度 | 观测内容 | +|---|----|------|----------| +| 7 | `account-bio` | 账号简介写法 | 账号昵称、简介写法、主页与置顶表达、对外承诺(仅对标账号样本可得) | +| 8 | `content-mix-cadence` | 内容形式比例与发布习惯 | 图文 / 视频等内容形式比例、发布时间段与频率、内容形式混合节奏(如三篇图文对一篇视频)、栏目化节奏(仅对标账号批量样本可得) | + +## Report 与聚合规则 + +1. 单篇 report 先填「样本观测」:作品类型、样本来源、账号与简介、发布时间、数据线索、横竖屏与时长、素材来源与授权、转录与关键帧来源。缺失一律写「未观测」,不得编造。 +2. 单篇 report 不判断跨篇稳定性;聚合时区分高覆盖共性、高权重样本偏好、局部借鉴(focus)、孤例与例外,并标注样本覆盖度。 +3. 视觉维度(封面 / 首帧 / 图组)必须有图片证据,由视觉模型读取本地图片后反推 AIGC 复现要素;没有图片写「未提供」。 +4. 口播文案子模块只在口播类作品启用;非口播类或样本不足写「未启用 / 未观测」,不得把单篇句式上升为规则。 +5. 账号运营子模块(`account-bio`、`content-mix-cadence`)只在样本来自对标账号批量提取时填写;用户只提供单篇时写「未观测」。 +6. 脚本统计只作证据底座(句长、问句密度、人称密度、感叹号密度、口播密度),不生成总分、不判定风格是否合格。 +7. 高数据样本必须回读创意、形态与包装再归因,不得把高播放直接等同于风格好。 + +## Template 语义 + +DNA template = **Brief.md 正文模板 + 口播文案模板(可选)**,固定语义段如下(脚本 `build` / `update` 自动生成骨架,字段由 Agent 从 DNA 文档推导填写): + +1. **[选题部分]** — 选题角度推荐、选题需考虑的受众关联角度、内容支柱与系列关系、避免 +2. **[标题与封面部分]** — 标题类型、参考标题、封面或首帧风格、封面 AIGC 提示词要素、话题标签策略 +3. **[内容创意部分]** — 创意原型、展开逻辑、记忆点与反转设计、触发条件、避免 +4. **[视频形态与制作指向部分]** — 视频内容形态、制作指向、委托边界、未指定形态时 +5. **[制作规格部分]** — 横屏或竖屏、时长带、画面风格、配音音色与声音形态、BGM 与音效、字幕 +6. **[口播文案部分]** — 是否启用、起、承、转、合、人称与语气、句长与语速、签名式表达、必须做、避免 + +- 开头两段(**选题**、**标题与封面**)跨平台通用:任何生产都先解决「做什么」和「怎么命名、怎么呈现封面」。 +- `[视频形态与制作指向部分]` 的「制作指向」必须写下方映射表里的真实资源名,Brief 的 `workflow` 字段据此填写。 +- 账号运营子模块不进 template:`account-bio` 与 `content-mix-cadence` 的结论只留在 DNA 文档,供起号、对标与发布节奏决策使用。 +- template 必须能从 DNA 文档推导,不得引入 DNA 文档未确认的规则;用户输入必须先映射到维度再转译为执行规则。 + +## 制作指向映射 + +`video-form` 维度提炼出的内容形态,必须能明确指向后续用什么技能制作: + +| DNA 观测到的内容形态 | 制作指向(只能写真实存在的资源名) | Brief `workflow` 字段值 | +|---------------------|-----------------------------------|------------------------| +| 影视解说 / 剧情解说 + 反转植入(「万万没想到」式) | Content Producer `expert-video` → **Reversal Ad** workflow | `reversal-ad` | +| 口播类(真人口播出镜,或旁白 + 画面) | Content Producer `expert-video` → **Narration Video** workflow | `narration-video` | +| 一句文稿转视觉隐喻的纸拼贴动画 | Content Producer `expert-video` → **Collage B-roll** workflow | `collage-broll` | +| 纯 AIGC 动画 / 剧情短片 / 蒙太奇拼接(需从零出脚本分镜) | Content Producer `expert-video` → 通用阶段链(narrative / motion / montage 档) | 省略(由 CP 定档位) | +| 已有素材简单拼接、加旁白、烧字幕 | main `video-edit`(不委托 CP) | — | +| 已有真人口播素材去口气词、剪高光 | main `talking-head-cut`(不委托 CP) | — | +| 产品操作录屏 | main `ui-demo`(不委托 CP) | — | + +Brief 写了 `workflow` 时 Content Producer 必须直接采用,不得替换成自创流程;未写时按通用阶段链自由选择实现。 + +## Focus ID 表 + +`--focus` 只接受本框架的维度 ID;限制某篇样本只影响这些维度。 + +| 分组 | Focus ID | +|------|----------| +| 选题与包装 | `topic-angle` `title-cover` | +| 内容创意 | `content-idea` | +| 形态与规格 | `video-form` `production-spec` | +| 口播文案子模块(可选,仅口播类启用) | `narration-script` | +| 账号运营子模块(对标账号样本才有) | `account-bio` `content-mix-cadence` | + +focus 校验按作品类型执行:视频 report 不接受图文维度 ID,反之亦然。 diff --git a/crews/main/skills/expert-douyin/tools/douyin-style-profiler/scripts/build_style_profile.py b/crews/main/skills/expert-douyin/tools/douyin-style-profiler/scripts/build_style_profile.py index 966d6d1d..7f1dc717 100644 --- a/crews/main/skills/expert-douyin/tools/douyin-style-profiler/scripts/build_style_profile.py +++ b/crews/main/skills/expert-douyin/tools/douyin-style-profiler/scripts/build_style_profile.py @@ -1,4 +1,14 @@ #!/usr/bin/env python3 +"""douyin-style-profiler:按作品类型(video / note)生成定性 DNA 资产。 + +三层产物(与 docs/expert-pack-dna-architecture.md 4.6 一致): + 单篇作品 -> DNA report + 同一 DNA 目录下全部 report + 权重/focus + 用户输入转译 -> DNA 文档 + DNA 文档 -> DNA template + +维度框架见本工具 references/ 下的 FRAMEWORK 文档(视频 / 图文各一份)。 +脚本只做 scaffold 与统计证据底座:不评分、不判定风格合格,定性结论由 Agent 回读原文补齐。 +""" import argparse, json, math, re, shutil from collections import Counter, defaultdict from datetime import datetime, timezone @@ -7,6 +17,7 @@ SENTENCE_SPLIT = re.compile(r"[。!?!?]+") +PARAGRAPH_SPLIT = re.compile(r"\n\s*\n") TOKEN_RE = re.compile(r"[一-鿿A-Za-z0-9_]+") ENGLISH_WORD_RE = re.compile(r"[A-Za-z0-9_]+") SECOND_PERSON_RE = re.compile(r"你们?|you", re.IGNORECASE) @@ -16,66 +27,204 @@ ID_RE = re.compile(r"^[a-z0-9][a-z0-9-]{1,63}$") SOURCE_BLOCK_RE = re.compile(r"", re.DOTALL) REPORT_BLOCK_RE = re.compile(r"", re.DOTALL) +# 图文正文内联话题标签:#话题(样本文件约定正文为纯文本,行首 # 只出现在标题行) +TAG_RE = re.compile(r"(?m)(?:^|(?<=\s))#[^\s#]\S*") +EMOJI_RE = re.compile( + "[" + "\U0001F1E6-\U0001F1FF" + "\U0001F300-\U0001F5FF" + "\U0001F600-\U0001F64F" + "\U0001F680-\U0001F6FF" + "\U0001F700-\U0001F77F" + "\U0001F780-\U0001F7FF" + "\U0001F800-\U0001F8FF" + "\U0001F900-\U0001F9FF" + "\U0001FA00-\U0001FA6F" + "\U0001FA70-\U0001FAFF" + "☀-⛿" + "✀-➿" + "⬀-⯿" + "️" + "]" +) + +# ── CONFIG-BEGIN ── +PLATFORM = "douyin" + +PLATFORM_LABEL = "抖音" + +PLATFORM_DESC = "抖音短视频与图文作品" + +KINDS = ("video", "note") + +DEFAULT_KIND = "video" + +KIND_LABELS = {"video": "视频作品", "note": "图文作品"} + +KIND_SUFFIX_HINT = "抖音默认 dna-0 为视频,图文样本请另建 dna-id(如 dna-0-note)" + +FRAMEWORK_DOCS = {"video": "video-dna-framework.md", "note": "note-dna-framework.md"} + +# DNA 维度 v2(按作品类型分框架)。调整维度必须升版本,并同步 references/ 下的框架文档与工具 SKILL.md 的 Focus ID 表。 +DIMENSION_GROUPS = { + "video": { + "选题与包装": [("topic-angle", "选题与观看理由"), ("title-cover", "标题与封面")], + "内容创意": [("content-idea", "内容创意")], + "形态与规格": [("video-form", "视频内容形态与制作指向"), ("production-spec", "制作规格与视听倾向")], + "口播文案子模块(可选,仅口播类启用)": [("narration-script", "口播文案子DNA")], + "账号运营子模块(对标账号样本才有)": [("account-bio", "账号简介写法"), ("content-mix-cadence", "内容形式比例与发布习惯")], + }, + "note": { + "选题与包装": [("topic-angle", "选题与观看理由"), ("title-cover", "标题与封面图组")], + "内容创意": [("content-idea", "内容创意")], + "正文与视觉": [("body-voice", "正文表达与语气"), ("imageset-visual", "图组视觉风格")], + "互动与转化": [("interaction-cta", "互动引导与转化")], + "账号运营子模块(对标账号样本才有)": [("account-bio", "账号简介写法"), ("content-mix-cadence", "内容形式比例与发布习惯")], + }, +} + +STATISTICS_METRICS = { + "video": [ + "avg_sentence_tokens", + "question_density_per_100_sentences", + "second_person_density_per_100_sentences", + "exclamation_density_per_1000_characters", + "speech_chars_per_minute", + ], + "note": [ + "title_chars", + "line_count", + "avg_sentence_tokens", + "question_density_per_100_sentences", + "second_person_density_per_100_sentences", + "emoji_density_per_100_characters", + "tag_count", + ], +} + +REPORT_DIMENSION_PROMPTS = { + "topic-angle": "- 单篇观测:选题类型、选题入口(现象 / 问题 / 冲突 / 数据 / 热点 / 挑战 / 个人经历)、目标人群为什么要看完(理解 / 判断 / 行动 / 避坑 / 身份认同 / 情绪共鸣)。\n- 边界:只记本篇,不判断跨篇稳定性;不评价选题好坏。", + "title-cover": "- 单篇观测:标题类型(痛点 / 数字 / 反差 / 悬念 / 身份点名 / 搜索长尾)、标题与描述原文、话题标签策略。\n- 视觉证据:封面或首帧必须由视觉模型读取图片,至少提取画面主体与场景、构图与画幅、色彩体系、光线与质感、风格与媒介、文字视觉与图文关系、品牌识别元素、避免项,并反推为可执行的 AIGC 提示词要素;无图片写「未提供」,不得凭正文或标题想象补齐。", + "content-idea": "- 单篇观测:一句话创意内核、创意类型、展开逻辑(悬念 / 反转 / 递进 / 对比 / 清单 / 实测)、记忆点。\n- 可复用信号:这个创意套路换成别的主题还能怎么用。\n- 边界:只记创意层,不记创作细节(逐句台词、镜头表、脚本结构、转场与编码参数)。", + "video-form": "- 单篇观测:视频内容形态(口播 / 实拍拼接 / 影视解说+反转植入 / 纯 AIGC 动画 / 创意转场动效 / 录屏演示 / 图文卡片视频 / 混合)与判定依据(画面证据、口播占比、素材来源)。\n- 制作指向:必须落到真实存在的资源名——Content Producer `expert-video` 的某个 workflow(Reversal Ad / Narration Video / Collage B-roll / 通用阶段链),或 main 的素材加工技能(`video-edit` / `talking-head-cut` / `ui-demo`);不得发明不存在的名字。", + "production-spec": "- 单篇观测:横屏或竖屏、时长带、画面风格(色调、质感、字幕样式倾向、信息密度)、配音音色与声音形态(原声口播 / TTS / 旁白 / 纯画面字幕)、BGM 与音效倾向、封面规格。\n- 边界:只记规格与倾向,不规定镜头参数、逐镜设计、转场与编码细节——那些归 Content Producer。", + "narration-script": "- 子模块(仅口播类作品启用):起(从什么起步)、承(靠什么推进)、转(转折触发)、合(收束与 CTA),以及人称与语气、句长与语速、签名式表达。\n- 边界:非口播类或证据不足时写「未启用 / 未观测」;不得把单篇句式直接上升为规则。", + "body-voice": "- 单篇观测:开头钩子(原文摘录)、正文组织方式(清单体 / 教程步骤 / 故事线 / 对比 / 观点输出)、分行与段落节奏、口语化程度与人称、emoji 与标点用法、签名式表达。\n- 证据边界:脚本统计只给句长 / 行数 / emoji / 标签等线索;口头禅与签名表达必须回读原文确认。", + "imageset-visual": "- 单篇观测:图片数量与顺序、构图类型(产品展示 / 场景 / 文字卡片 / 对比图 / 过程图)、图文信息分工、色调与质感、版式一致性、文字视觉。\n- 视觉证据:必须由视觉模型读取本地图片并反推 AIGC 复现要素;无图片写「未提供」,不得凭正文想象补齐。", + "interaction-cta": "- 单篇观测:评论 / 收藏 / 关注 / 进店 / 咨询等平台内行动的引导方式与位置、每篇行动引导数量、话题标签承载的意图。\n- 合规边界:不隐藏站外联系方式、不绕平台检测、不以利益换互动,记为必须避免项。", + "account-bio": "- 子模块(仅对标账号样本可得):账号昵称、简介写法、主页与置顶表达、对外承诺。\n- 边界:用户提供的单篇样本无法观测时写「未观测」,不得推导。", + "content-mix-cadence": "- 子模块(仅对标账号批量样本可得):图文 / 视频等内容形式比例、发布时间段与频率、内容形式混合节奏(如三篇图文对一篇视频)、栏目化节奏。\n- 边界:必须由账号发布列表的批量样本推导;单篇样本只记本篇发布时间。", +} + +REPORT_OBSERVATION_PROMPTS = { + "video": "- 作品类型:视频(本框架只用于视频作品;图文样本走 note-dna-framework)。\n- 样本来源:待 Agent 补齐(对标账号批量作品 / 用户提供的单篇或多篇 / 用户想法转译)。\n- 账号与简介:待 Agent 补齐;非账号样本写未观测。\n- 发布时间与时间段:待 Agent 补齐;单篇只记本篇时间,不推导账号节奏。\n- 数据线索:待 Agent 补齐(播放 / 点赞 / 评论 / 分享 / 收藏);只作证据,不直接判风格好坏。\n- 横竖屏与时长:待 Agent 补齐。\n- 素材来源与授权:待 Agent 补齐(实拍 / 影视或开源片源 / AIGC / 录屏 / 混合)。\n- 转录与关键帧来源:待 Agent 补齐(如 main 的 viral-chaser 产物路径);缺失写未提供,不得编造。", + "note": "- 作品类型:图文(本框架只用于图文作品;视频样本走 video-dna-framework)。\n- 样本来源:待 Agent 补齐(对标账号批量作品 / 用户提供的单篇或多篇 / 用户想法转译)。\n- 账号与简介:待 Agent 补齐;非账号样本写未观测。\n- 发布时间与时间段:待 Agent 补齐;单篇只记本篇时间,不推导账号节奏。\n- 数据线索:待 Agent 补齐(阅读 / 点赞 / 收藏 / 评论 / 分享);只作证据,不直接判风格好坏。\n- 图片数量与来源:待 Agent 补齐(封面 + 配图张数、实拍 / 截图 / AIGC)。\n- 关键词与标签:待 Agent 补齐(标题、正文与标签区原样记录)。", +} + +TEMPLATE_STAGES = { + "video": ("选题", "标题与封面", "内容创意", "视频形态与制作指向", "制作规格", "口播文案"), + "note": ("选题", "标题与封面", "内容创意与结构", "正文表达", "图组", "互动与标签"), +} -STATISTICS_METRICS = [ +TEMPLATE_STAGE_FIELDS = { + "选题": ("选题角度推荐", "选题需考虑的受众关联角度", "内容支柱与系列关系", "避免"), + "标题与封面": ("标题类型", "参考标题", "封面或首帧风格", "封面 AIGC 提示词要素", "话题标签策略"), + "内容创意": ("创意原型", "展开逻辑", "记忆点与反转设计", "触发条件", "避免"), + "视频形态与制作指向": ("视频内容形态", "制作指向", "委托边界", "未指定形态时"), + "制作规格": ("横屏或竖屏", "时长带", "画面风格", "配音音色与声音形态", "BGM 与音效", "字幕"), + "口播文案": ("是否启用", "起", "承", "转", "合", "人称与语气", "句长与语速", "签名式表达", "必须做", "避免"), + "内容创意与结构": ("创意原型", "正文组织方式", "信息密度", "记忆点", "避免"), + "正文表达": ("开头钩子", "推进方式", "段落与分行节奏", "人称与语气", "emoji 与标点", "签名式表达", "必须做", "避免"), + "图组": ("图片数量与顺序", "构图类型", "色调与质感", "版式一致性", "文字视觉", "AIGC 提示词要素"), + "互动与标签": ("互动目标", "引导方式", "话题标签策略", "合规边界"), +} + +TEMPLATE_INTROS = { + "video": "本模板是 main agent 出具**视频制作 Brief(brief.md)**与(口播类)**口播文案**的输入模板,必须由 DNA 文档推导,不得引入 DNA 文档未确认的规则。\n\n- 下列各段直接对应 Brief 的正文字段;Brief 的其余字段(素材清单与授权、验收标准、闸门批准人)由平台 Content Production Workflow 规定。\n- Brief **不含 DNA 信息**:Content Producer 看不到 main 的 DNA,只按 Brief 制作。\n- 模板不规定创作细节:逐句台词、镜头表、转场与编码参数归 Content Producer。\n- 账号运营子模块(简介写法、内容形式比例、发布习惯)写在 DNA 文档,不进本模板。", + "note": "本模板是 main agent 直接生产**图文作品**的写作模板,必须由 DNA 文档推导,不得引入 DNA 文档未确认的规则。\n\n- 开头两段(选题、标题与封面)跨平台通用。\n- 固定的是语义部分,不是物理段落数量:任一部分可对应一个或多个自然段,也可略过。\n- 每条规则必须能从 DNA 文档的聚合结论推导,避免「专业」「亲切」这类空泛形容词。\n- 账号运营子模块写在 DNA 文档,不进本模板。", +} + +TEMPLATE_CHECKLISTS = { + "video": "- 是否只用一个 DNA,且作品类型与该 DNA 的 `kind` 一致。\n- 选题、标题 / 描述、封面是否来自 DNA 文档。\n- 内容创意是否落到可复用的创意原型,而不是照抄样本主题。\n- 视频形态是否明确指向 Content Producer `expert-video` 的某个 workflow,或 main 的某个素材加工技能。\n- Brief 是否只含制作所需信息(不含 DNA 内容),素材是否给了绝对路径与授权说明。\n- 口播类是否附口播文案(或真人口播录音路径);口播子模块未启用时是否避免规定逐句口播。\n- 制作规格(横竖屏、时长带、画面风格、配音音色)是否尊重样本覆盖度;样本不足时是否标注未观测。\n- 用户输入是否已转译为具体执行规则。", + "note": "- 是否只用一个 DNA,且作品类型与该 DNA 的 `kind` 一致。\n- 选题、标题与封面图组是否来自 DNA 文档。\n- 正文表达的每条规则是否可从 DNA 文档推导,未使用空泛形容词。\n- 图组数量、构图与视觉风格是否与 DNA 一致;视觉结论是否有图片证据。\n- 互动引导是否每篇只放一个行动,且不越合规红线。\n- 用户输入是否已转译为具体执行规则。", +} + +DNA_SUBMODULE_NOTE = "- **口播文案子模块**(`narration-script`):仅口播类视频启用,用于指导 main agent 写同类型视频的口播文案;它不是独立 DNA,未启用时写「未启用」。\n- **账号运营子模块**(`account-bio`、`content-mix-cadence`):只在样本来自用户提供的对标账号(可从账号发布列表批量提取)时填写;结论只写进本 DNA 文档,不进 template;样本不足写「未观测」。" +# ── CONFIG-END ── + +STATISTICS_METRICS_ALL = [ + "title_chars", + "line_count", + "paragraphs", + "avg_paragraph_tokens", "avg_sentence_tokens", "question_density_per_100_sentences", "second_person_density_per_100_sentences", "first_person_density_per_100_sentences", "exclamation_density_per_1000_characters", "speech_chars_per_minute", + "emoji_density_per_100_characters", + "tag_count", ] +METRIC_LABELS = { + "title_chars": "标题字数", + "line_count": "正文行数", + "paragraphs": "段落数", + "avg_paragraph_tokens": "平均每段长度(token)", + "avg_sentence_tokens": "平均句长(token)", + "question_density_per_100_sentences": "问句密度 / 百句", + "second_person_density_per_100_sentences": "第二人称密度 / 百句", + "first_person_density_per_100_sentences": "第一人称密度 / 百句", + "exclamation_density_per_1000_characters": "感叹号密度 / 千字", + "speech_chars_per_minute": "口播密度(字/分钟)", + "emoji_density_per_100_characters": "emoji 密度 / 百字", + "tag_count": "话题标签数", +} + STOP_TERMS = { "一个", "我们", "你们", "这里", "不会", "这个", "那个", "什么", "可以", "因为", "但是", "所以", "还是", "以及", "如果", "他们", "自己", "的时候", "to", "the", "a", "an", "is", "are", "and", "or", "of", "in", "for", "on", "with", "you", "we", } -DIMENSION_GROUPS = { - "定位与选题": [ - ("positioning-core", "定位与核心传达"), - ("topic-portfolio", "选题组合"), - ("title-packaging", "标题与包装"), - ("bio-profile", "账号简介与主页表达"), - ], - "账号节奏": [ - ("content-form-mix", "内容形式与比例"), - ("publish-cadence", "发布习惯"), - ("high-performer-patterns", "高数据创意模式"), - ], - "表达与风格": [ - ("visual-language", "视觉语言"), - ("audio-language", "声音语言"), - ("narration-dna", "口播文案 DNA"), - ], - "互动与制作": [ - ("engagement-conversion", "互动与转化"), - ("series-signature", "系列与签名"), - ("production-pipeline", "制作管线倾向"), - ], -} -DIMENSIONS = [] -number = 1 -for group, dimensions in DIMENSION_GROUPS.items(): - for dimension_id, name in dimensions: - DIMENSIONS.append( - {"id": dimension_id, "number": number, "name": name, "group": group} - ) - number += 1 + +def dimensions_for(kind: str) -> list[dict]: + """把某个作品类型的分组配置摊平成带序号的维度列表。""" + dimensions = [] + number = 1 + for group, items in DIMENSION_GROUPS[kind].items(): + for dimension_id, name in items: + dimensions.append({"id": dimension_id, "number": number, "name": name, "group": group}) + number += 1 + return dimensions + + +def statistics_metrics(kind: str) -> list[str]: + wanted = STATISTICS_METRICS[kind] + return [metric for metric in STATISTICS_METRICS_ALL if metric in wanted] + def split_sentences(text: str) -> list[str]: return [item.strip() for item in SENTENCE_SPLIT.split(text) if item.strip()] + +def split_paragraphs(text: str) -> list[str]: + return [item.strip() for item in PARAGRAPH_SPLIT.split(text) if item.strip()] + + def tokenize(text: str) -> list[str]: return TOKEN_RE.findall(text) + def rounded(value: float) -> float: return round(value, 4) -def safe_ratio(numerator: int, denominator: int, multiplier: float = 1) -> float: + +def safe_ratio(numerator: int, denominator: float, multiplier: float = 1) -> float: return rounded((numerator / denominator) * multiplier) if denominator else 0.0 + def average(values: list[float]) -> float: return rounded(sum(values) / len(values)) if values else 0.0 @@ -98,24 +247,39 @@ def extract_terms(text: str) -> Counter: return terms -def title_candidates(path: Path, text: str) -> list[str]: - candidates = [path.stem] +def split_title_body(text: str) -> tuple[str, str]: + """样本文件约定:首个一级标题行为作品标题,其余为正文(图文正文可含内联 #话题)。""" + title = "" + body_lines = [] for line in text.splitlines(): stripped = line.strip() - if stripped.startswith("# ") and len(stripped) > 2: - candidates.append(stripped[2:].strip()) - break + if not title and stripped.startswith("# ") and len(stripped) > 2: + title = stripped[2:].strip() + continue + body_lines.append(line) + return title, "\n".join(body_lines).strip() + + +def title_candidates(path: Path, text: str) -> list[str]: + candidates = [path.stem] + title, _ = split_title_body(text) + if title: + candidates.append(title) return candidates def document_metrics(path: Path, text: str, duration: float = 0.0) -> dict: + """统计证据底座:视频与图文共用一套超集,展示哪些指标由 STATISTICS_METRICS[kind] 决定。""" + title, body = split_title_body(text) sentences = split_sentences(text) sentence_lengths = [len(tokenize(sentence)) for sentence in sentences] sentence_count = len(sentences) character_count = len(text) - speech_chars_per_minute = ( - rounded(character_count / duration * 60) if duration > 0 else 0.0 - ) + body_lines = [line for line in body.splitlines() if line.strip()] + paragraphs = split_paragraphs(body) + paragraph_lengths = [len(tokenize(paragraph)) for paragraph in paragraphs] + emoji_count = len(EMOJI_RE.findall(body)) + speech_chars_per_minute = rounded(character_count / duration * 60) if duration > 0 else 0.0 return { "source_transcript": str(path.resolve()), @@ -123,20 +287,19 @@ def document_metrics(path: Path, text: str, duration: float = 0.0) -> dict: "characters": character_count, "sentences": sentence_count, "duration": duration, + "title_chars": len(title), + "line_count": len(body_lines), + "paragraphs": len(paragraphs), + "avg_paragraph_tokens": average([float(value) for value in paragraph_lengths]), "avg_sentence_tokens": average([float(value) for value in sentence_lengths]), - "question_density_per_100_sentences": safe_ratio( - len(QUESTION_RE.findall(text)), sentence_count, 100 - ), - "second_person_density_per_100_sentences": safe_ratio( - len(SECOND_PERSON_RE.findall(text)), sentence_count, 100 - ), - "first_person_density_per_100_sentences": safe_ratio( - len(FIRST_PERSON_RE.findall(text)), sentence_count, 100 - ), - "exclamation_density_per_1000_characters": safe_ratio( - len(EXCLAMATION_RE.findall(text)), character_count, 1000 - ), + "question_density_per_100_sentences": safe_ratio(len(QUESTION_RE.findall(text)), sentence_count, 100), + "second_person_density_per_100_sentences": safe_ratio(len(SECOND_PERSON_RE.findall(text)), sentence_count, 100), + "first_person_density_per_100_sentences": safe_ratio(len(FIRST_PERSON_RE.findall(text)), sentence_count, 100), + "exclamation_density_per_1000_characters": safe_ratio(len(EXCLAMATION_RE.findall(text)), character_count, 1000), "speech_chars_per_minute": speech_chars_per_minute, + "emoji_count": emoji_count, + "emoji_density_per_100_characters": safe_ratio(emoji_count, character_count, 100), + "tag_count": len(TAG_RE.findall(body)), "terms": dict(extract_terms(text)), } @@ -187,15 +350,7 @@ def generated_at() -> str: def metric_label(metric_name: str) -> str: - labels = { - "avg_sentence_tokens": "平均口播句长(token)", - "question_density_per_100_sentences": "问句密度 / 百句", - "second_person_density_per_100_sentences": "第二人称密度 / 百句", - "first_person_density_per_100_sentences": "第一人称密度 / 百句", - "exclamation_density_per_1000_characters": "感叹号密度 / 千字", - "speech_chars_per_minute": "口播密度(字/分钟)", - } - return labels.get(metric_name, metric_name) + return METRIC_LABELS.get(metric_name, metric_name) def weighted_median(pairs: list[tuple[float, float]]) -> float: @@ -218,10 +373,10 @@ def weighted_mad(pairs: list[tuple[float, float]], center: float) -> float: return weighted_median(deviations) -def build_statistics(reports: list[dict]) -> dict: +def build_statistics(reports: list[dict], kind: str) -> dict: total_weight = rounded(sum(report["weight"] for report in reports)) numeric_metrics = {} - for metric_name in STATISTICS_METRICS: + for metric_name in statistics_metrics(kind): pairs = [ (float(report["document"][metric_name]), float(report["weight"])) for report in reports @@ -277,7 +432,7 @@ def build_statistics(reports: list[dict]) -> dict: } -def statistics_markdown(statistics: dict) -> str: +def statistics_markdown(statistics: dict, kind: str) -> str: lines = [ "| 指标 | 加权中位数 | 加权 MAD | 最小值 | 最大值 |", "|---|---:|---:|---:|---:|", @@ -289,10 +444,16 @@ def statistics_markdown(statistics: dict) -> str: lines.extend( [ "", - f"样本覆盖度:`{statistics['report_count']}` 条 report;单条或少量样本不能推导账号比例、发布节奏或高数据共性。", + f"样本覆盖度:`{statistics['report_count']}` 条 {KIND_LABELS[kind]} report。", f"权重模式:`{statistics['weighting']}`;总权重:`{statistics['total_weight']}`。", - "口播密度(字/分钟)仅在 report 提供 `duration` 时有意义;未提供时该行只是 0 值占位。", - "统计只用于辅助聚合,不生成评分;定性判断必须回到各篇 DNA report。", + ] + ) + if kind == "video": + lines.append("口播密度(字/分钟)仅在 report 提供 `duration` 时有意义;未提供时该行只是 0 值占位。") + lines.extend( + [ + "「账号运营子模块」维度(简介写法、内容形式比例、发布习惯)不能由单篇样本推导:样本非对标账号批量时写未观测。", + "统计只用于辅助聚合,不生成评分;定性判断必须回到各篇 DNA report 与原文。", ] ) return "\n".join(lines) @@ -360,6 +521,7 @@ def load_reports(paths: list[Path]) -> list[dict]: "report_path": str(path.resolve()), "dna_id": parse_quoted(metadata.get("dna-id", "")), "report_id": parse_quoted(metadata.get("report-id", path.name.removesuffix(".report.md"))), + "kind": parse_quoted(metadata.get("kind", DEFAULT_KIND)), "title": parse_quoted(metadata.get("title", document["title_candidates"][-1])), "weight": float(metadata.get("weight", "1")), "focus": parse_json_list(metadata.get("focus", "[]")), @@ -369,53 +531,78 @@ def load_reports(paths: list[Path]) -> list[dict]: return reports -REPORT_DIMENSION_PROMPTS = { - 'positioning-core': '- 单条观测:本条暴露出的定位、目标人群与核心传达。\n- 账号级判断:单条样本只给候选,不直接判定账号稳定性。', - 'topic-portfolio': '- 单条观测:本条选题类型、入口和系列关系。', - 'title-packaging': '- 单条观测:标题 / 描述 / 封面包装模式;只记模式,不抄成固定字句。', - 'bio-profile': '- 账号级观测:账号简介、主页与置顶表达;单条样本无法观测时写未观测。', - 'content-form-mix': '- 单条观测:本条内容形式。\n- 聚合边界:图文/视频比例与混合节奏必须由多样本或账号级数据推导。', - 'publish-cadence': '- 单条观测:本条发布时间。\n- 聚合边界:时间段、频率和三图文对一视频等节奏必须由多样本或账号级数据推导。', - 'high-performer-patterns': '- 数据线索:记录本条互动/播放/阅读线索。\n- 创意判断:回读选题、包装、形式与创意,不得把高数据直接等同于风格好。', - 'visual-language': '- 视觉证据:有图片或关键帧时由视觉模型读取;无证据写未观测。\n- 边界:只记录账号级稳定视觉语言,不规定逐镜设计。', - 'audio-language': '- 声音证据:来自口播稿、原视频信息或用户说明;无证据写未观测。\n- 边界:只记录音色/语速/声音气质倾向,不规定 TTS 参数。', - 'narration-dna': '- 独立块:记录开头、起承转合、收束、人称与签名表达。\n- 聚合边界:样本不足时保持未启用,不把单条句式上升为账号 DNA。', - 'engagement-conversion': '- 单条观测:平台内行动引导与承接路径。', - 'series-signature': '- 单条观测:栏目、固定表达或识别符号;高频词必须回读原文确认。', - 'production-pipeline': '- 管线映射:只写 Content Producer 已支持的 Pipeline;未确定时写待定。\n- 分界:DNA 指导 main agent 出 Brief,不规定成片制作细节。', -} +def enforce_single_kind(reports: list[dict], kind: str | None) -> str: + """一个 DNA 只承载一种作品类型:返回该 DNA 的 kind,混型直接报错。""" + kinds = {report["kind"] for report in reports} + if len(kinds) > 1: + raise SystemExit( + "Reports mix work kinds: " + + ", ".join(sorted(kinds)) + + f"。一个 dna-id 只能承载一种作品类型:{KIND_SUFFIX_HINT}。" + ) + resolved = kinds.pop() if kinds else (kind or DEFAULT_KIND) + if kind and resolved != kind: + raise SystemExit( + f"--kind {kind} 与 report 内的 kind {resolved} 不一致;一个 dna-id 只能承载一种作品类型。" + ) + if resolved not in KINDS: + raise SystemExit(f"Unknown kind: {resolved}({PLATFORM_LABEL}支持:{', '.join(KINDS)})") + return resolved + def report_dimension_markdown(dimension: dict) -> str: heading = f"### {dimension['number']}. {dimension['name']}" - prompt = REPORT_DIMENSION_PROMPTS.get(dimension["id"], "- 单条观测:待 Agent 补齐。") + prompt = REPORT_DIMENSION_PROMPTS.get(dimension["id"], "- 单篇观测:待 Agent 补齐。") return ( f"{heading}\n\n" f"{prompt}\n\n" - "**单条结论:**待 Agent 补齐。\n\n" - "**原文证据:**待 Agent 补齐(逐字引用、账号信息、发布信息、画面/声音描述或数据线索;注明来源)。\n\n" - "**可复用信号:**待 Agent 补齐;样本不足时写未观测,不推导账号级稳定性。" + "**单篇结论:**待 Agent 补齐。\n\n" + "**原文证据:**待 Agent 补齐(逐字引用、账号/发布信息、画面或声音描述、数据线索;注明来源)。\n\n" + "**可复用信号:**待 Agent 补齐;证据不足时写未观测,不推导跨篇稳定性。" ) def report_markdown( dna_id: str, report_id: str, + kind: str, weight: float, focus: list[str], document: dict, cover_image: str, source_url: str, ) -> str: - dimensions = [] - for dimension in DIMENSIONS: - dimensions.append(report_dimension_markdown(dimension)) + dimensions = dimensions_for(kind) + dimension_blocks = [report_dimension_markdown(dimension) for dimension in dimensions] duration_line = f"duration: {document['duration']}" + statistics_lines = [ + f"- 转录/正文字符:{document['characters']}", + f"- 句子数:{document['sentences']}", + ] + if kind == "video": + statistics_lines.append(f"- 视频时长:{document['duration'] or '未提供'}") + else: + statistics_lines.extend( + [ + f"- 标题字数:{document['title_chars']}", + f"- 正文行数:{document['line_count']}", + f"- 话题标签数:{document['tag_count']}", + ] + ) + statistics_lines.extend( + [ + f"- 标题候选:{' / '.join(document['title_candidates'])}", + f"- 来源链接:{source_url or '未提供'}", + f"- 封面 / 首帧图:{cover_image or '未提供'}", + ] + ) return "\n\n".join( [ "---\n" f"dna-id: {yaml_value(dna_id)}\n" f"report-id: {yaml_value(report_id)}\n" "type: dna-report\n" + f"kind: {yaml_value(kind)}\n" f"title: {yaml_value(document['title_candidates'][-1])}\n" f"source-transcript: {yaml_value(document['source_transcript'])}\n" f"source-url: {yaml_value(source_url)}\n" @@ -426,31 +613,34 @@ def report_markdown( "sample_count: 1\n" f"generated_at: {yaml_value(generated_at())}\n" "---", - f"# {document['title_candidates'][-1]} 单条 DNA Report", - "本文件只描述这一条视频。它不是聚合后的 DNA 文档,也不直接作为生产模板。", - "## 单条统计", - f"- 转录字符:{document['characters']}\n- 口播句子:{document['sentences']}\n- 视频时长:{document['duration'] or '未提供'}\n- 标题候选:{' / '.join(document['title_candidates'])}\n- 来源链接:{source_url or '未提供'}\n- 封面 / 首帧图:{cover_image or '未提供'}", - "## 样本与账号观测", - '- 样本类型:待 Agent 补齐(账号作品 / 用户提供单条)。\n- 账号与简介:待 Agent 补齐;单条样本无法观测时写未观测。\n- 发布时间与时间段:待 Agent 补齐;单条样本只记录本条时间,不推导账号节奏。\n- 内容形式:待 Agent 补齐(口播 / 实拍拼接 / 创意转场 / 纯 AIGC 动画 / 图文 / 混合)。\n- 数据表现线索:待 Agent 补齐;只作证据,不直接判风格好坏。\n- 视频形态与授权信息:待 Agent 补齐(横竖屏、时长、素材来源、授权边界)。', - f"## {len(DIMENSIONS)} 维单条分析", - "\n\n".join(dimensions), - "## 单条边界", - "- 这里记录本条视频的可复用信号,不判断跨条稳定性。\n- 聚合时由 Agent 根据 DNA report、权重和 focus 判断共性、偏好和例外。", + f"# {document['title_candidates'][-1]} 单篇 DNA Report({KIND_LABELS[kind]})", + "本文件只描述这一篇作品。它不是聚合后的 DNA 文档,也不直接作为生产模板。", + "## 单篇统计", + "\n".join(statistics_lines), + "## 样本观测", + REPORT_OBSERVATION_PROMPTS[kind], + f"## {len(dimensions)} 维单篇分析", + "\n\n".join(dimension_blocks), + "## 单篇边界", + "- 这里记录本篇作品的可复用信号,不判断跨篇稳定性。\n" + "- 聚合时由 Agent 根据全部 DNA report、权重和 focus 判断共性、偏好和例外。\n" + f"- 维度定义与边界见本工具 `references/{FRAMEWORK_DOCS[kind]}`。", f"", - *( [f""] if cover_image else [] ), + *([f""] if cover_image else []), ] ) + "\n" -def user_input_markdown(user_inputs: list[str], existing_body: str | None = None) -> str: +def user_input_markdown(user_inputs: list[str], kind: str, existing_body: str | None = None) -> str: if not user_inputs: return existing_body or "暂无待转译输入。" + dimension_count = len(dimensions_for(kind)) entries = [] for index, user_input in enumerate(user_inputs, start=1): entries.append( f"### 输入 {index}\n" f"- raw_input: {yaml_value(user_input)}\n" - f"- affected_dimensions: 待 Agent 映射到 {len(DIMENSIONS)} 个维度 ID\n" + f"- affected_dimensions: 待 Agent 映射到 {dimension_count} 个维度 ID\n" "- dna_document_change: 待 Agent 转译为聚合结论 / 报告依据 / 创作规则\n" "- template_change: 待 Agent 转译为具体执行规则\n" "- status: pending" @@ -462,20 +652,22 @@ def user_input_markdown(user_inputs: list[str], existing_body: str | None = None def dna_document_markdown( dna_id: str, + kind: str, reports: list[dict], statistics: dict, user_inputs: list[str] | None = None, previous_dna: str | None = None, ) -> str: + dimensions = dimensions_for(kind) old_sections = extract_markdown_sections(previous_dna, "### ") - dimensions = [] - for dimension in DIMENSIONS: + dimension_blocks = [] + for dimension in dimensions: heading = f"### {dimension['number']}. {dimension['name']}" body = old_sections.get(heading) if body: - dimensions.append(f"{heading}\n\n{body}") + dimension_blocks.append(f"{heading}\n\n{body}") else: - dimensions.append( + dimension_blocks.append( f"{heading}\n\n**聚合结论:**待 Agent 补齐。\n\n" "**报告依据:**待 Agent 列出使用的 DNA report、权重和 focus。\n\n" "**创作规则:**待 Agent 补齐。" @@ -489,25 +681,33 @@ def dna_document_markdown( "---\n" f"dna-id: {yaml_value(dna_id)}\n" "type: dna-document\n" + f"kind: {yaml_value(kind)}\n" + f"platform: {yaml_value(PLATFORM)}\n" f"report_count: {statistics['report_count']}\n" f"total_weight: {statistics['total_weight']}\n" f"weighting: {statistics['weighting']}\n" f"generated_at: {yaml_value(generated_at())}\n" "---", - f"# {dna_id} DNA 文档", - "本文件聚合历史 DNA report。它是账号当前采用的账号级运营规则,也必须能推导出 main agent 的内容生产 / Brief 模板。", + f"# {dna_id} DNA 文档({PLATFORM_LABEL} · {KIND_LABELS[kind]})", + "本文件聚合该 DNA 目录下的全部 DNA report,形成当前采用的内容生产规则,并且必须能推导出 DNA template。" + "样本可以来自多个账号,也可以来自用户指定的一个账号的批量作品。", "## 报告与权重", "\n".join( f"- `{report['report_path']}`:weight `{report['weight']}`,focus `{', '.join(report['focus']) or 'all'}`" for report in reports ), - statistics_markdown(statistics), - f"## {len(DIMENSIONS)} 维聚合", - "\n\n".join(dimensions), + statistics_markdown(statistics, kind), + f"## {len(dimensions)} 维聚合", + "\n\n".join(dimension_blocks), + "## 子模块说明", + DNA_SUBMODULE_NOTE, "## 用户输入转译区", - user_input_markdown(user_inputs or [], existing_user_inputs), + user_input_markdown(user_inputs or [], kind, existing_user_inputs), "## 推导规则", - "- 聚合结论必须能追溯到 DNA report。\n- 用户输入必须先映射到具体维度,再修改聚合结论和创作规则;不得把原话直接当成 DNA 规则。\n- 模板必须由本文件推导,不能引入本文件未确认的规则。", + "- 聚合结论必须能追溯到 DNA report;区分高覆盖共性、高权重偏好、局部借鉴、孤例与例外。\n" + "- 用户输入必须先映射到具体维度,再修改聚合结论和创作规则;不得把原话直接当成 DNA 规则。\n" + "- template 必须由本文件推导,不能引入本文件未确认的规则。\n" + "- 账号运营子模块(简介写法、内容形式比例、发布习惯)只写进本文件,不进 template。", f"", ] ) + "\n" @@ -557,57 +757,6 @@ def extract_named_section(markdown: str, heading: str) -> str: return "\n".join(body).strip() -TEMPLATE_STAGES = ("定位与核心传达", "选题与标题包装", "内容形式与发布节奏", "高数据创意模式", "互动与系列", "制作交接与Pipeline", "口播文案DNA") - -TEMPLATE_STAGE_FIELDS = { - "定位与核心传达": ( - "一句话定位", - "目标人群", - "核心传达", - "账号简介与主页表达", - "不变承诺", - ), - "选题与标题包装": ( - "选题组合", - "标题模式", - "封面或首帧包装", - "禁用方向", - ), - "内容形式与发布节奏": ( - "图文或视频比例", - "发布时间带", - "内容形式混合节奏", - "系列栏目", - ), - "高数据创意模式": ( - "高表现样本共性", - "可复用创意原型", - "触发条件", - "例外", - ), - "互动与系列": ( - "互动目标", - "引导方式", - "系列与签名标记", - "必须做", - "避免", - ), - "制作交接与Pipeline": ( - "MainAgent交付物", - "ContentProducer交付物", - "Pipeline", - "素材与授权", - "风格边界", - ), - "口播文案DNA": ( - "是否启用", - "起承转合结构", - "语言与人称", - "声音倾向", - "必须做", - "避免", - ), -} def parse_template_fields(body: str) -> dict[str, str]: fields = {} for line in body.splitlines(): @@ -617,8 +766,8 @@ def parse_template_fields(body: str) -> dict[str, str]: return fields -def template_stage_from_heading(heading: str) -> str | None: - for stage in TEMPLATE_STAGES: +def template_stage_from_heading(heading: str, kind: str) -> str | None: + for stage in TEMPLATE_STAGES[kind]: if heading.startswith(f"[{stage}部分]"): return stage return None @@ -633,10 +782,10 @@ def template_segment(stage: str, values: dict[str, str] | None = None) -> str: return "\n".join(lines) -def stage_values_from_template(old_sections: dict[str, str]) -> dict[str, dict[str, str]]: - values = {stage: {} for stage in TEMPLATE_STAGES} - for heading in sorted(old_sections, key=template_order): - stage = template_stage_from_heading(heading) +def stage_values_from_template(old_sections: dict[str, str], kind: str) -> dict[str, dict[str, str]]: + values = {stage: {} for stage in TEMPLATE_STAGES[kind]} + for heading in sorted(old_sections, key=lambda item: template_order(item, kind)): + stage = template_stage_from_heading(heading, kind) if not stage: continue for field, value in parse_template_fields(old_sections[heading]).items(): @@ -646,32 +795,23 @@ def stage_values_from_template(old_sections: dict[str, str]) -> dict[str, dict[s def template_markdown( dna_id: str, + kind: str, source_dna: str, previous_template: str | None = None, ) -> str: - old_sections = extract_template_sections(previous_template) - stage_values = stage_values_from_template(old_sections) - segments = [template_segment(stage, stage_values[stage]) for stage in TEMPLATE_STAGES] + stages = TEMPLATE_STAGES[kind] + old_sections = extract_template_sections(previous_template, kind) + stage_values = stage_values_from_template(old_sections, kind) + segments = [template_segment(stage, stage_values[stage]) for stage in stages] section_defaults = [ - ( - "## 生产模板", - "\n\n".join(segments), - ), + ("## 生产模板", "\n\n".join(segments)), ( "## 用户输入转译后的执行规则", "- (来自用户输入:待 Agent 补齐来源。)\n" - f"- (影响维度:待 Agent 映射到 {len(DIMENSIONS)} 维 ID。)\n" - "- (执行规则:待 Agent 写成 Brief 或图文生产时可直接执行的要求。)", - ), - ( - "## 使用检查", - "- 是否只用一个 DNA,且与本次账号 / 内容任务匹配。\n" - "- 定位、选题、标题包装、核心传达是否来自 DNA 文档。\n" - "- 图文/视频比例、发布节奏和高数据创意是否尊重样本覆盖度;样本不足时是否标注未观测。\n" - "- 视频全案是否只输出 Brief,且 Brief 明确 Pipeline、素材授权、验收标准和交付边界。\n" - "- 口播文案 DNA 是否独立启用;未启用时是否避免规定逐句口播。\n" - "- 用户输入是否已转译为具体执行规则。", + f"- (影响维度:待 Agent 映射到 {len(dimensions_for(kind))} 维 ID。)\n" + "- (执行规则:待 Agent 写成生产时可直接执行的要求。)", ), + ("## 使用检查", TEMPLATE_CHECKLISTS[kind]), ] rendered_sections = [] for heading, default_body in section_defaults: @@ -689,17 +829,18 @@ def template_markdown( "---\n" f"dna-id: {yaml_value(dna_id)}\n" "type: dna-template\n" + f"kind: {yaml_value(kind)}\n" f"source_dna: {yaml_value(source_dna)}\n" f"generated_at: {yaml_value(generated_at())}\n" "---", - f"# {dna_id} DNA Template", - "本模板是 main agent 的账号级内容生产 / Brief 输入模板,必须由 DNA 文档推导;不得引入 DNA 文档未确认的规则,也不规定成片制作细节。", + f"# {dna_id} DNA Template({PLATFORM_LABEL} · {KIND_LABELS[kind]})", + TEMPLATE_INTROS[kind], *rendered_sections, ] ) + "\n" -def extract_template_sections(markdown: str | None) -> dict[str, str]: +def extract_template_sections(markdown: str | None, kind: str) -> dict[str, str]: if not markdown: return {} sections = {} @@ -726,10 +867,10 @@ def extract_template_sections(markdown: str | None) -> dict[str, str]: return sections -def template_order(heading: str) -> tuple[int, str]: - stage = template_stage_from_heading(heading) +def template_order(heading: str, kind: str) -> tuple[int, str]: + stage = template_stage_from_heading(heading, kind) if stage: - return (TEMPLATE_STAGES.index(stage) + 1, heading) + return (TEMPLATE_STAGES[kind].index(stage) + 1, heading) return (10_000, heading) @@ -738,11 +879,19 @@ def validate_id(value: str, label: str) -> None: raise SystemExit(f"{label} must be 2-64 chars: lowercase letters, digits, and hyphens") -def validate_focus(focus: list[str]) -> None: - valid = {dimension["id"] for dimension in DIMENSIONS} +def validate_kind(value: str | None) -> str | None: + if value is None: + return None + if value not in KINDS: + raise SystemExit(f"Unknown --kind: {value}({PLATFORM_LABEL}支持:{', '.join(KINDS)})") + return value + + +def validate_focus(focus: list[str], kind: str) -> None: + valid = {dimension["id"] for dimension in dimensions_for(kind)} unknown = sorted(set(focus) - valid) if unknown: - raise SystemExit(f"Unknown focus: {', '.join(unknown)}") + raise SystemExit(f"Unknown focus for kind '{kind}': {', '.join(unknown)}") def validate_duration(value: str) -> float: @@ -769,38 +918,39 @@ def inputs_from_args(args: argparse.Namespace) -> list[str]: def report_command(args: argparse.Namespace) -> None: validate_id(args.dna_id, "--dna-id") validate_id(args.sample_id, "--sample-id") - validate_focus(args.focus) + kind = validate_kind(args.kind) or DEFAULT_KIND + validate_focus(args.focus, kind) weight = float(args.weight) if weight <= 0 or not math.isfinite(weight): raise SystemExit("--weight must be a positive finite number") duration = validate_duration(args.duration) if args.duration else 0.0 + if kind != "video" and duration: + print(f"[warn] --duration 只对视频作品有意义,{KIND_LABELS[kind]} report 忽略该值。") + duration = 0.0 paths = collect_input_paths(inputs_from_args(args), {".md", ".txt"}) if len(paths) != 1: raise SystemExit("report command accepts exactly one transcript; use build to aggregate reports") - document = document_metrics( - paths[0], paths[0].read_text(encoding="utf-8", errors="ignore"), duration - ) - output_dir = Path(args.output_dir or f"douyin/dna/{args.dna_id}/reports") + document = document_metrics(paths[0], paths[0].read_text(encoding="utf-8", errors="ignore"), duration) + output_dir = Path(args.output_dir or f"{PLATFORM}/dna/{args.dna_id}/reports") cover_image = "" if args.cover_image: source_cover = validate_cover_image(args.cover_image) - cover_image = str( - persist_cover_image(source_cover, output_dir, args.sample_id).resolve() - ) + cover_image = str(persist_cover_image(source_cover, output_dir, args.sample_id).resolve()) output = output_dir / f"{args.sample_id}.report.md" write_text( output, report_markdown( - args.dna_id, args.sample_id, weight, args.focus, document, cover_image, + args.dna_id, args.sample_id, kind, weight, args.focus, document, cover_image, args.source_url or "", ), ) - print(f"Wrote single-video DNA report: {output}") + print(f"Wrote single-work DNA report ({KIND_LABELS[kind]}): {output}") + print(f"[next] Agent 补齐「样本观测」与 {len(dimensions_for(kind))} 维单篇结论后,跑 build 聚合。") def build_command(args: argparse.Namespace) -> None: validate_id(args.dna_id, "--dna-id") - input_values = args.input or [f"douyin/dna/{args.dna_id}/reports"] + input_values = args.input or [f"{PLATFORM}/dna/{args.dna_id}/reports"] paths = collect_input_paths(input_values, {".md"}) report_paths = [path for path in paths if path.name.endswith(".report.md")] if not report_paths: @@ -808,22 +958,17 @@ def build_command(args: argparse.Namespace) -> None: reports = load_reports(report_paths) foreign_reports = [report["report_id"] for report in reports if report["dna_id"] != args.dna_id] if foreign_reports: - raise SystemExit( - f"Reports belong to another dna-id: {', '.join(foreign_reports)}" - ) - statistics = build_statistics(reports) - output_dir = Path(args.output_dir or f"douyin/dna/{args.dna_id}") + raise SystemExit(f"Reports belong to another dna-id: {', '.join(foreign_reports)}") + kind = enforce_single_kind(reports, validate_kind(args.kind)) + for report in reports: + validate_focus(report["focus"], kind) + statistics = build_statistics(reports, kind) + output_dir = Path(args.output_dir or f"{PLATFORM}/dna/{args.dna_id}") dna_path = output_dir / f"{args.dna_id}.dna.md" template_path = output_dir / f"{args.dna_id}.template.md" - write_text( - dna_path, - dna_document_markdown(args.dna_id, reports, statistics, args.user_input), - ) - write_text( - template_path, - template_markdown(args.dna_id, dna_path.name, None), - ) - print(f"Wrote DNA document and template: {output_dir}") + write_text(dna_path, dna_document_markdown(args.dna_id, kind, reports, statistics, args.user_input)) + write_text(template_path, template_markdown(args.dna_id, kind, dna_path.name, None)) + print(f"Wrote DNA document and template ({KIND_LABELS[kind]}): {output_dir}") def update_command(args: argparse.Namespace) -> None: @@ -836,7 +981,8 @@ def update_command(args: argparse.Namespace) -> None: metadata = parse_frontmatter(previous_dna) dna_id = parse_quoted(metadata.get("dna-id", dna_path.name.removesuffix(".dna.md"))) validate_id(dna_id, "--dna-id") - validate_focus(args.focus) + declared_kind = parse_quoted(metadata.get("kind", "")) or None + kind = validate_kind(args.kind) or validate_kind(declared_kind) input_values = args.input or [] new_paths = collect_input_paths(input_values, {".md"}) if input_values else [] @@ -848,37 +994,29 @@ def update_command(args: argparse.Namespace) -> None: reports = load_reports(sorted(all_paths.values(), key=lambda path: str(path))) foreign_reports = [report["report_id"] for report in reports if report["dna_id"] != dna_id] if foreign_reports: - raise SystemExit( - f"Reports belong to another dna-id: {', '.join(foreign_reports)}" - ) - statistics = build_statistics(reports) + raise SystemExit(f"Reports belong to another dna-id: {', '.join(foreign_reports)}") + kind = enforce_single_kind(reports, kind) + for report in reports: + validate_focus(report["focus"], kind) + validate_focus(args.focus, kind) + statistics = build_statistics(reports, kind) user_inputs = list(args.user_input or []) - write_text( - dna_path, - dna_document_markdown( - dna_id, - reports, - statistics, - user_inputs, - previous_dna, - ), - ) - write_text( - template_path, - template_markdown(dna_id, dna_path.name, previous_template), - ) - print(f"Updated DNA document and template: {dna_path}") + write_text(dna_path, dna_document_markdown(dna_id, kind, reports, statistics, user_inputs, previous_dna)) + write_text(template_path, template_markdown(dna_id, kind, dna_path.name, previous_template)) + print(f"Updated DNA document and template ({KIND_LABELS[kind]}): {dna_path}") def build_parser() -> argparse.ArgumentParser: - parser = argparse.ArgumentParser(description="Build qualitative Douyin short-video DNA assets") + parser = argparse.ArgumentParser(description=f"Build qualitative {PLATFORM_LABEL} DNA assets per work kind") subparsers = parser.add_subparsers(dest="command", required=True) - report = subparsers.add_parser("report", help="Create one single-video DNA report") + kind_help = f"作品类型:{' / '.join(KINDS)}(默认 {DEFAULT_KIND})" + report = subparsers.add_parser("report", help="Create one single-work DNA report") report.add_argument("--input", action="append", required=True) + report.add_argument("--kind", help=kind_help) report.add_argument("--cover-image", help="Local cover / first-frame image used by visual-model analysis") - report.add_argument("--source-url", help="Original video URL kept as report evidence") - report.add_argument("--duration", help="Video duration in seconds, used for speech-density statistics") + report.add_argument("--source-url", help="Original work URL kept as report evidence") + report.add_argument("--duration", help="Video duration in seconds (video kind only), used for speech-density statistics") report.add_argument("--dna-id", required=True) report.add_argument("--sample-id", required=True) report.add_argument("--weight", default="1") @@ -888,6 +1026,7 @@ def build_parser() -> argparse.ArgumentParser: build = subparsers.add_parser("build", help="Aggregate DNA reports into DNA document and template") build.add_argument("--input", action="append") + build.add_argument("--kind", help=kind_help) build.add_argument("--dna-id", required=True) build.add_argument("--user-input", action="append", default=[]) build.add_argument("--output-dir") @@ -895,6 +1034,7 @@ def build_parser() -> argparse.ArgumentParser: update = subparsers.add_parser("update", help="Merge reports and translate user input") update.add_argument("--input", action="append") + update.add_argument("--kind", help=kind_help) update.add_argument("--dna", required=True) update.add_argument("--template", required=True) update.add_argument("--focus", action="append", default=[]) diff --git a/crews/main/skills/expert-douyin/workflows/account-benchmark.md b/crews/main/skills/expert-douyin/workflows/account-benchmark.md index d8369648..b376ce0f 100644 --- a/crews/main/skills/expert-douyin/workflows/account-benchmark.md +++ b/crews/main/skills/expert-douyin/workflows/account-benchmark.md @@ -39,8 +39,9 @@ 1. 优先选择点赞、评论、分享信号强的视频;播放量只作参考之一。优先低粉高播账号的作品(内容形式更可学习)。 2. 抖音没有账号作品列表抓取工具;请用户提供对标账号的代表性视频链接,或由 `smart-search` 辅助发现候选账号后请用户确认。 -3. 账号级对标至少收集 10 条代表性视频;账号可获取视频不足 10 条时,提供全部并说明数量限制。 -4. 单条视频可以形成单条观察,但不得当成账号级稳定 DNA;多个样本才分析覆盖率和共性。 +3. 对标账号至少批量收集 10 条代表性作品(从账号发布列表提取,视频与图文分开建 DNA);可获取作品不足 10 条时,提供全部并说明数量限制。 +4. 单篇作品可以形成单篇观察,但不得当成稳定结论;多个样本才分析覆盖率和共性。 +5. 批量账号样本必须填**账号运营子模块**:账号简介写法(`account-bio`)、内容形式比例与发布习惯(`content-mix-cadence`,含发布时间段与混合节奏,如三篇图文对一篇视频);这两项只写进 DNA 文档,不进 template。 5. 每条样本尽量记录账号简介、发布时间、内容形式、素材来源与授权;缺失写「未观测」,不虚构。 6. 互动数据线索只说明"用户怎么投票",不直接等于内容质量;归因前先按 `review.md` 的混杂因素清单排除账号成熟度、投流、选题热度等干扰。 @@ -72,7 +73,7 @@ douyin/dna/{benchmark-dna-id}/{benchmark-dna-id}.dna.md douyin/dna/{benchmark-dna-id}/{benchmark-dna-id}.template.md ``` -对标 DNA template 使用 v1 账号级语义段:定位与核心传达、选题与标题包装、内容形式与发布节奏、高数据创意模式、互动与系列、制作交接与 Pipeline、口播文案 DNA。每一部分都要能从对标 DNA 文档推导。 +对标 DNA template 用语义段与目标 DNA 一致:视频 = 选题、标题与封面、内容创意、视频形态与制作指向、制作规格、口播文案(可选);图文 = 选题、标题与封面、内容创意与结构、正文表达、图组、互动与标签。每一部分都要能从对标 DNA 文档推导;账号运营子模块不进 template。 ### Step 3 - 模式分析与差异化(agent 推理) @@ -99,8 +100,8 @@ douyin/dna/{base-dna-id}/{base-dna-id}.template.md 比较必须覆盖两层: -1. **13 维 DNA 文档**:逐个比较定位、选题、标题包装、账号简介、内容形式、发布节奏、高数据创意、视觉/声音、口播文案 DNA、互动系列与制作管线。 -2. **template 语义段**:逐项比较定位与核心传达、选题与标题包装、内容形式与发布节奏、高数据创意、互动与系列、制作交接、口播文案 DNA。 +1. **DNA 文档**:逐个比较选题与观看理由、标题与封面、内容创意、视频形态与制作指向、制作规格、口播文案子模块,以及账号运营子模块(简介写法、内容形式比例、发布习惯)。 +2. **template 语义段**:按作品类型逐项比较(视频看 Brief 相关段,图文看写作段)。 每个维度和模板语义段都输出四类结论: @@ -108,7 +109,7 @@ douyin/dna/{base-dna-id}/{base-dna-id}.template.md | --- | --- | | 保持 | 基线已有优势,与目标观众和商业定位一致 | | 引入 | 对标更有效,且不冲突业务事实、合规边界和用户偏好 | -| 局部借鉴 | 只适合选题、包装、内容形式、发布节奏、Pipeline 等局部场景 | +| 局部借鉴 | 只适合选题、标题与封面、内容创意、制作指向、发布习惯等局部场景 | | 不采纳 | 仅依赖孤例、冲突商业定位、风险高或难稳定执行 | 每项至少说明: @@ -129,7 +130,7 @@ douyin/dna/{base-dna-id}/{base-dna-id}.template.md 局部 DNA 融合流程: -1. 明确采纳范围:13 维中的维度、template 语义段,或两者组合。 +1. 明确采纳范围:维度、template 语义段,或两者组合。 2. 读取对标 DNA 文档 / template 的对应规则、适用条件和例外。 3. 整理为一条可转译输入,包含来源 `dna-id`、采纳范围和具体规则。 4. 在基线 DNA 上执行无新增样本的 update: diff --git a/crews/main/skills/expert-douyin/workflows/account-setup.md b/crews/main/skills/expert-douyin/workflows/account-setup.md index 3f015177..e8ba4d55 100644 --- a/crews/main/skills/expert-douyin/workflows/account-setup.md +++ b/crews/main/skills/expert-douyin/workflows/account-setup.md @@ -106,7 +106,7 @@ Agent 基于 `business_knowledge.md` 和关键词地图提炼搜索关键词, 1. 优先找观众相近、承诺清晰、近期仍活跃的账号;优先低粉高播账号(粉丝少但播放高,说明内容形式可学习)。 2. 优先选择点赞、评论、分享信号强的视频;播放量只作参考之一。 -3. 账号级初始参考建议收集 10 条视频;用户明确提供单条或少量样本时仍可建立 `dna-0`,但必须标注覆盖不足,不得称为账号级稳定 DNA。 +3. 对标账号初始参考建议批量收集 10 条作品(视频进 `dna-0`,图文进 `dna-0-note`);用户明确提供单篇或少量样本时仍可建立 DNA,但必须标注覆盖不足,不得称为稳定结论。批量账号样本才填账号运营子模块(简介写法、内容形式比例、发布习惯)。 4. 抖音没有账号作品列表抓取工具;用户找到账号或视频链接后,Agent 再用 `viral-chaser` 拆解和提炼。 用户补回账号名或视频链接后,回到路径 A。 @@ -114,7 +114,7 @@ Agent 基于 `business_knowledge.md` 和关键词地图提炼搜索关键词, #### 路径 C:用户拒绝外部参考或已有明确想法 1. 结合 `business_knowledge.md`、用户定位、目标观众、变现方式和用户思路,提炼 `dna-0`。 -2. Agent 直接按 13 维账号级框架编写: +2. Agent 直接按维度框架编写(视频用 `video-dna-framework.md`,图文用 `note-dna-framework.md`): ```text douyin/dna/dna-0/dna-0.dna.md @@ -123,7 +123,7 @@ douyin/dna/dna-0/dna-0.template.md 3. DNA 文档必须标明来源是"业务信息 + 用户指定方向",没有视频样本统计。 4. 能确定的规则写为执行要求;未确定维度写"待校准",不得虚构单条证据。 -5. Template 仍必须给出可执行的 Brief / 内容生产语义段;不能只有抽象定位词。 +5. Template 仍必须给出可执行语义段:视频 = Brief 正文 + 口播文案模板;图文 = 图文写作模板。不能只有抽象定位词。 6. 将 DNA 摘要和待校准点发给用户确认,再按用户意见转译修订。 后续获得真实视频样本时,按 `style-dna.md` 生成 report 并更新 `dna-0`。 diff --git a/crews/main/skills/expert-douyin/workflows/content-production.md b/crews/main/skills/expert-douyin/workflows/content-production.md index b9e41288..b4c76333 100644 --- a/crews/main/skills/expert-douyin/workflows/content-production.md +++ b/crews/main/skills/expert-douyin/workflows/content-production.md @@ -2,7 +2,7 @@ 从选题到发布的完整内容生产。用户说"帮我做条抖音视频""发个抖音""这条照着做一条""这个选题我们也做一条"走这个。 -视频制作分工是硬边界:main agent 只做**已有视频素材的简单加工**,并负责长文 / 图文内容;除此之外的视频全案,main agent 只产出 **Brief**,成片制作委托 `content-producer`。本 workflow 的价值在于:用账号级 DNA 锁定定位、选题、包装、内容形式与制作边界,编排输入分支与确认节点,衔接制作并把关发布记录。 +**分工硬边界**:main agent 负责选题策划、按 DNA 出 **Brief**、拟定标题与简介、准备素材(用户素材预处理 / `ui-demo` 录屏 / 从 `campaign_assets/` 挑选,绝对路径写进 Brief)、监督并推动作为 subagent 的 `content-producer`、成片后的发布与运营;也直接做图文内容与**已有视频素材的简单加工**(`video-edit` / `talking-head-cut`)。视频全案的成片制作一律委托 `content-producer`。本 workflow 的价值在于:用 DNA 锁定选题、包装、内容创意、视频形态与制作规格,编排输入分支与确认节点,衔接制作并把关发布记录。 ## Step 0 - 入口判断 @@ -54,7 +54,7 @@ douyin/dna/{dna-id}/{dna-id}.dna.md douyin/dna/{dna-id}/{dna-id}.template.md ``` -DNA template 是 main agent 的内容生产 / Brief 输入模板,覆盖定位与核心传达、选题与标题包装、内容形式与发布节奏、高数据创意、互动系列、制作交接与可选口播文案 DNA。它不规定镜头表、逐句台词、转场或编码细节。委托 `content-producer` 时,把 template 的账号级约束转成 Brief;具体制作方案由 CP 在指定 Pipeline 内完成。 +DNA template 是 main agent 的生产输入模板:**视频 DNA 的 template = Brief 正文模板 +(可选)口播文案模板**,覆盖选题与观看理由、标题与封面、内容创意、视频形态与制作指向、制作规格、口播文案;**图文 DNA 的 template = 图文写作模板**。它不规定创作细节:逐句台词、镜头表、转场或编码参数归 Content Producer。 ### 2. 读取业务知识 @@ -91,7 +91,7 @@ DNA template 是 main agent 的内容生产 / Brief 输入模板,覆盖定位 > Agent 的一般内容判断 ``` -DNA 约束的是账号定位、选题组合、标题包装、核心传达、内容形式、发布节奏、互动与制作边界;口播文案 DNA 只在明确启用时约束口播。DNA 不能覆盖用户指定素材、事实、合规边界和转化要求。 +DNA 约束的是选题与观看理由、标题与封面写法、内容创意原型、视频形态与制作指向、制作规格;口播文案子模块只在明确启用时约束口播;账号运营子模块(简介写法、内容形式比例、发布习惯)只用于起号、对标与发布节奏决策,不进 Brief。DNA 不能覆盖用户指定素材、事实、合规边界和转化要求。 ## Step 2 - 素材获取与整理 @@ -174,7 +174,7 @@ DNA 约束的是账号定位、选题组合、标题包装、核心传达、内 只处理已有素材的简单加工,不升格为全案制作: -1. 按 DNA 的核心传达、内容形式与互动目标整理剪辑顺序清单;若口播文案 DNA 已启用且需要新口播,由 main 先写口播稿。 +1. 按 DNA 的核心传达、内容形式与互动目标整理剪辑顺序清单;若口播文案子模块已启用且需要新口播,由 main 先写口播稿。 2. 口播类素材去口气词、剪高光 -> `talking-head-cut`。 3. 抽段拼接、加旁白 / BGM、烧字幕、编号合成 -> `video-edit`。 4. 素材缺口经 AIGC 片段(`aigc-video-gen`)或免费素材库(`pexels-footage` / `pixabay-footage`)补充,补充素材在清单中标注来源。 @@ -183,17 +183,39 @@ DNA 约束的是账号定位、选题组合、标题包装、核心传达、内 ### 路线 B / C:委托 content-producer 制作 -1. 产出**制作简报** `douyin/outputs//brief.md`。Brief 是 main / CP 的唯一分界点,至少包含: - - 选题、观看理由、标题与文案、核心传达 - - 账号级 DNA 约束:内容形式、视觉/声音倾向、互动目标、禁用方向 - - `pipeline`:已确定则写 Content Producer 支持的 Pipeline(如 `dna-ad-video-pipeline`);未确定则省略,由 CP 自由发挥 - - 素材清单、来源与授权 - - 时长带、横竖屏、交付物与验收标准 - - 若口播文案 DNA 已启用:main 写好的口播终稿,CP 不得重写策略文案 -2. 参考模式附上 `viral-chaser` 拆解报告路径,作为 brief 的一部分。 -3. spawn `content-producer`,由其按 Brief 完成制作;Brief 之外的制作方案、分镜与实现细节归 CP。 -4. Brief 变更时更新版本并推送变更要点;已开工中间产物按新版取舍,弃用部分记入交付说明。 -5. 成品视频回传 `douyin/outputs//`。 +1. 产出**制作简报** `douyin/outputs//brief.md`(Brief 是 main / CP 的唯一交接物): + +```markdown +# 抖音视频制作 Brief + +- 视频名 / slug: +- platform:douyin +- workflow:reversal-ad / narration-video / collage-broll / 未指定(未指定时 CP 按通用阶段链自选档位) +- 选题与观看理由: +- 核心传达: +- 内容创意:创意原型 + 展开逻辑 + 记忆点(+ 反转设计,如为反转植入类) +- 标题与简介:发布标题、简介文案、话题标签(main 定稿) +- 封面要求:封面主文案 + 视觉方向 +- 制作规格:横屏 / 竖屏、时长带、画面风格、配音音色与声音形态、BGM 与音效、字幕 +- 口播文案:`voiceover.md` 绝对路径(口播类必填)/ 真人口播录音绝对路径 / 不适用 +- 素材清单:逐条**绝对路径** + 来源 + 授权(无素材时写「无,由 CP 按 Brief 取材」) +- 交付物与验收:`video.mp4` + `cover.jpg` + `final-deliver.md`,回报三者绝对路径;验收标准 +- 闸门:GATE A / GATE B 批准人(用户或 main 代理批准 + 批准范围) +- 禁止事项:事实与承诺边界、合规红线、禁用方向 +``` + +Brief 硬性规则: + +- **不写 DNA**:Brief 里不出现 dna-id、DNA 文档路径或 DNA 规则原文——CP 看不到 main 的 DNA,只按 Brief 制作。DNA 的结论由 main 消化后写成 Brief 的具体要求。 +- **不建工作区**:main 不替 CP 建目录、不指定项目路径;CP 在自己的 workspace 下自建工作区。双方 T3 权限可互访取文件。 +- **素材给绝对路径**:main 负责素材准备(用户素材预处理、`ui-demo` 录屏、从 `campaign_assets/` 挑选),把绝对路径写进 Brief。 +- **甲乙方关系**:需求方向、品牌事实、发布文案归 main;制作方案、分镜、渲染参数归 CP。 + +2. 口播类视频:按 DNA 的 `narration-script` 子模块写口播终稿 `douyin/outputs//voiceover.md`,Brief 里给绝对路径;真人口播时先向用户取得录音文件。 +3. 参考模式下,把选题与创意结论写进 Brief 的「内容创意」段即可;`viral-chaser` 拆解报告是 main 的采样材料,**不作为 Brief 附件交给 CP**。 +4. spawn `content-producer` 委托制作:只交 Brief + 素材绝对路径 + 口播文案 / 录音;不指定 CP 的工作区与制作方案。 +5. Brief 变更时更新版本并推送变更要点;已开工中间产物按新版取舍,弃用部分记入交付说明。 +6. CP 交付后,按其回报的绝对路径把成片与封面取回 `douyin/outputs//`(`video.mp4` / `cover.jpg`),并把交付说明要点记入作品目录。 ## Step 6 - 成片自检 @@ -205,7 +227,7 @@ DNA 约束的是账号定位、选题组合、标题包装、核心传达、内 ## Step 7 - 封面 -1. 读取 DNA template 的标题包装与视觉语言,只取账号级风格边界,不搬逐镜制作细节。 +1. 读取 DNA template 的标题与封面段,只取风格边界,不搬逐镜制作细节。 2. 结合最终标题、目标观众和本条核心收益,确定本次封面的主体、色彩、文字视觉和负向约束。 3. 优先从成片中选帧作为封面(与内容一致);需要更强视觉冲击时用 `siliconflow-img-gen` 生成。 4. 用户已提供封面时直接使用;用户提供素材不足时作为生成参考。 diff --git a/crews/main/skills/expert-douyin/workflows/editing.md b/crews/main/skills/expert-douyin/workflows/editing.md index 92ecb994..73cc0ba1 100644 --- a/crews/main/skills/expert-douyin/workflows/editing.md +++ b/crews/main/skills/expert-douyin/workflows/editing.md @@ -10,16 +10,16 @@ | 用户的说法 | 改的层级 | 怎么走 | |-----------|---------|--------| -| "改下标题 / 简介 / 话题" | 文案层 | 按 DNA `title-packaging` 重写,直接给候选 | +| "改下标题 / 简介 / 话题" | 文案层 | 按 DNA `title-cover` 重写,直接给候选 | | "剪紧一点 / 去口气词 / 把高光剪出来" | 轻剪层 | `talking-head-cut`(去口气词、结巴、静音,按发言内容剪高光) | | "加 BGM / 加字幕 / 补片头片尾 / 插一段素材" | 加工层 | `video-edit`(audio-mix / subtitles / extract / assemble) | | "结构调一下 / 把第二段提前 / 换个讲法" | 结构层 | 先出调整方案(新的段落顺序与时点)-> 确认 -> `video-edit` 重剪 | -| "换个封面" | 封面层 | 成片抽帧或 `siliconflow-img-gen` 按 DNA `visual-language` 重做 | +| "换个封面" | 封面层 | 成片抽帧或 `siliconflow-img-gen` 按 DNA `production-spec`(画面风格)重做 | | "换个风格 / 方向不对" | 方向层 | 回到 `content-production.md` 从选题重新走 | ## 文案层 -标题、简介、话题标签按目标 DNA `title-packaging` 重写: +标题、简介、话题标签按目标 DNA `title-cover` 重写: 1. 目标 DNA 不确定时先确认(用户指定或默认 `dna-0`)。 2. 标题 ≤ 30 字;简介与话题按 template 策略;禁止引流信息。 @@ -38,13 +38,13 @@ ## 结构层 先出结构调整方案(新的段落顺序、每段时点、删留内容),用户确认后再动剪。 -结构调整先核对核心传达与内容形式;若口播文案 DNA 已启用,再按 `narration-dna` 核对口播结构。不要把账号级 DNA 扩写成逐镜规则。 +结构调整先核对 `content-idea`(内容创意)与 `video-form`(视频形态);若口播文案子模块已启用,再按 `narration-script` 核对口播结构。不要把 DNA 扩写成逐镜规则——镜头与剪辑细节归 Content Producer。 ## 换封面 1. 用户直接指定封面图 -> 直接使用。 2. 用户说"从片子里选一帧" -> 抽帧候选给用户挑。 -3. 用户说"重做一张" -> 按 DNA `visual-language` 与本次标题/核心收益,用 `siliconflow-img-gen` 生成,用户确认后替换 `cover.jpg`。 +3. 用户说"重做一张" -> 按 DNA `production-spec`(画面风格)与本次标题/核心收益,用 `siliconflow-img-gen` 生成,用户确认后替换 `cover.jpg`。 ## 改完必做 diff --git a/crews/main/skills/expert-douyin/workflows/review.md b/crews/main/skills/expert-douyin/workflows/review.md index 697b76ab..ca857c64 100644 --- a/crews/main/skills/expert-douyin/workflows/review.md +++ b/crews/main/skills/expert-douyin/workflows/review.md @@ -34,17 +34,20 @@ - 用户级留存数据不可得。 - 用户可提供创作者中心后台截图(完播、粉丝画像、流量来源),作为更高置信度的证据;没有就用库内指标。 -### 互动漏斗 → template 语义段 → 13 维映射 +### 互动漏斗 → template 语义段 → DNA 维度映射(视频作品) | 漏斗卡点 | 先怀疑的 template 语义段 | 可回溯的 DNA 维度 | |---------|--------------------------|------------------| -| 播放低(推荐/点击瓶颈) | 定位与核心传达、选题与标题包装 | positioning-core、topic-portfolio、title-packaging | -| 点击后快速划走(完播估算低) | 内容形式与发布节奏、口播文案 DNA | content-form-mix、narration-dna、high-performer-patterns | -| 点赞低 | 高数据创意模式、互动与系列 | positioning-core、high-performer-patterns、engagement-conversion | -| 评论低 | 互动与系列 | engagement-conversion、series-signature | -| 分享低 | 选题与标题包装、高数据创意模式 | topic-portfolio、high-performer-patterns | -| 收藏低 | 高数据创意模式 | high-performer-patterns、positioning-core | -| 关注转化低 | 定位与核心传达、互动与系列 | bio-profile、series-signature、engagement-conversion | +| 播放低(推荐/点击瓶颈) | 选题、标题与封面 | topic-angle、title-cover | +| 点击后快速划走(完播估算低) | 内容创意、制作规格、口播文案 | content-idea、production-spec、narration-script | +| 点赞低 | 内容创意、选题 | content-idea、topic-angle | +| 评论低 | 内容创意(讨论点)、口播文案 | content-idea、narration-script | +| 分享低 | 选题、内容创意 | topic-angle、content-idea | +| 收藏低 | 内容创意、制作规格 | content-idea、production-spec | +| 关注转化低 | 账号运营子模块、内容创意 | account-bio、content-mix-cadence、content-idea | +| 画面/时长/音色被吐槽 | 制作规格、视频形态与制作指向 | production-spec、video-form | + +图文作品(`dna-*-note`)改按图文 template 语义段归因:选题、标题与封面、内容创意与结构、正文表达、图组、互动与标签,对应维度 `topic-angle`、`title-cover`、`content-idea`、`body-voice`、`imageset-visual`、`interaction-cta`。 ### 平台混杂因素(归因前必排) @@ -84,7 +87,7 @@ content-calibrator eval --platform douyin --dna-id # 指定 DNA 1. 判定只看比值与走向,绝对值只作上下文。 2. 逐条排除平台混杂因素,输出替代假设检验结果。 -3. 回读 `douyin/dna//.dna.md` / `.template.md` 与待评估作品原文(`source_folder` 下的转录 / Brief / 口播终稿),把趋势变化落到 template 语义段与 13 维。 +3. 回读 `douyin/dna//.dna.md` / `.template.md` 与待评估作品原文(`source_folder` 下的转录 / Brief / 口播终稿),把趋势变化落到 template 语义段与 DNA 维度。 ### Step 3 - 报告与标记 diff --git a/crews/main/skills/expert-douyin/workflows/style-dna.md b/crews/main/skills/expert-douyin/workflows/style-dna.md index feffe0a8..1a814a9a 100644 --- a/crews/main/skills/expert-douyin/workflows/style-dna.md +++ b/crews/main/skills/expert-douyin/workflows/style-dna.md @@ -1,13 +1,13 @@ -# 抖音账号级 DNA 创建与更新 Workflow +# 抖音 DNA 创建与更新 Workflow -本 Workflow 负责 DNA report、DNA 文档与 DNA template 的创建与更新。维度框架以 `douyin-style-profiler` 的 `references/account-dna-framework.md` 为准(账号级 DNA v1)。 +本 Workflow 负责 DNA report、DNA 文档与 DNA template 的创建与更新。维度框架以 `douyin-style-profiler` 的 `references/video-dna-framework.md`(视频)与 `references/note-dna-framework.md`(图文)为准(DNA v2)。 ## 边界 -- DNA 是账号级运营框架,不是单条视频制作细节。 +- DNA 是从一批作品样本中提取、聚合出的内容生产规则集,不存在「平台级 DNA」或「账号级 DNA」的说法;样本可以来自多个账号,也可以来自用户指定的一个账号的发布列表批量提取。 - 账号初始化与默认 `dna-0` 建立走 `account-setup.md`;对标样本先走 `account-benchmark.md`。 - DNA 如何用于内容生产走 `content-production.md`;改片走 `editing.md`;数据复盘走 `review.md`。 -- DNA 指导 main agent 出内容或视频 Brief;全片制作委托 `content-producer`。 +- DNA 指导 main agent 出图文内容、视频 Brief 与(口播类的)口播文案;全片制作委托 `content-producer`。 ## 入口判断 @@ -29,8 +29,9 @@ 1. 用户指定 `dna-id` 时使用该 DNA。 2. 未指定或说“默认 DNA”时使用 `dna-0`。 -3. 对标样本必须进入独立 DNA,不直接写入 `dna-0`;采纳后再通过局部融合更新。 -4. 目标 DNA 不存在时,先走 `account-setup.md` 或按用户明确指定的新 `dna-id` 初始化。 +3. **作品类型分流**:一个 `dna-id` 只承载一种作品类型。抖音默认 `dna-0` 是视频(`--kind video`),图文样本另建 dna-id(如 `dna-0-note`);混型 `build` 会直接报错。 +4. 对标样本必须进入独立 DNA,不直接写入 `dna-0`;采纳后再通过局部融合更新。 +5. 目标 DNA 不存在时,先走 `account-setup.md` 或按用户明确指定的新 `dna-id` 初始化。 ## 存储结构 @@ -48,6 +49,8 @@ douyin/dna/{dna-id}/ ## 样本获取 +**先判作品类型**(视频 / 图文):它决定用哪套维度框架、`--kind` 取值与目标 dna-id;同一个 DNA 不混型。 + | 来源 | 处理 | | --- | --- | | 抖音视频链接 | self-spawn subagent 走 `viral-chaser`,取得转录、时长、标题/描述、互动线索与关键帧 | @@ -69,13 +72,15 @@ douyin/dna/{dna-id}/ ### Step 1 - 准备样本 -1. 把视频整理为转录 `.md`,首个一级标题写标题/描述。 -2. 确定目标 `dna-id`、`sample-id`、样本权重和 focus。 -3. 整理账号观测信息,供 Agent 补进 report。 +1. 判定作品类型(视频 / 图文),据此选框架与目标 `dna-id`。 +2. 把视频整理为转录 `.md`,首个一级标题写标题/描述。 +3. 确定目标 `dna-id`、`sample-id`、样本权重和 focus。 +4. 整理账号观测信息,供 Agent 补进 report。 -### Step 2 - 生成单条 report +### Step 2 - 生成单篇 report ```bash +# 视频样本(不传 --kind,默认 video) douyin-style-profiler report \ --input path/to/transcript.md \ --dna-id {dna-id} \ @@ -84,28 +89,40 @@ douyin-style-profiler report \ --source-url "https://www.douyin.com/video/..." \ --duration 89 \ --output-dir douyin/dna/{dna-id}/reports + +# 图文样本 +douyin-style-profiler report \ + --input path/to/note.md \ + --kind note \ + --dna-id {dna-id}-note \ + --sample-id {sample-id} \ + --cover-image path/to/cover.jpg \ + --output-dir douyin/dna/{dna-id}-note/reports ``` 生成 scaffold 后必须: -1. 补齐「样本与账号观测」。 -2. 回读转录原文,补齐 13 维的单条结论、证据和可复用信号。 -3. 单条样本不得推导账号比例、发布节奏或高数据共性;相关字段写「未观测」。 -4. 视觉语言必须有图片/关键帧证据;口播文案 DNA 样本不足时保持未启用。 +1. 补齐「样本观测」:作品类型、样本来源、账号与简介、发布时间、数据线索、素材来源与授权(缺失写「未观测」)。 +2. 回读原文,补齐各维度的单篇结论、原文证据与可复用信号。 +3. 视频样本必须给出**视频内容形态**与**制作指向**(Content Producer `expert-video` 的某个 workflow,或 main 的 `video-edit` / `talking-head-cut` / `ui-demo`),只写真实存在的资源名。 +4. 视觉维度必须有封面 / 首帧 / 关键帧图片证据;口播文案子模块样本不足时写「未启用」。 +5. 账号运营子模块(简介写法、内容形式比例、发布习惯)只在样本来自对标账号批量提取时填写,单篇样本写「未观测」。 ### Step 3 - 聚合 DNA ```bash -douyin-style-profiler build --dna-id {dna-id} +douyin-style-profiler build --dna-id {dna-id} # 视频 DNA(默认 kind=video) +douyin-style-profiler build --dna-id {dna-id}-note --kind note # 图文 DNA ``` Agent 必须读取全部 report,按权重/focus 聚合: - 高频共性、高权重偏好、局部借鉴、孤例、例外分开写。 -- 标注样本覆盖度;单条/少量样本不得称为稳定账号 DNA。 -- 高数据内容要回读创意与内容形式,不能只归因播放量。 +- 标注样本覆盖度;单篇或少量样本不得称为稳定结论。 +- 视频形态必须聚合成明确的**制作指向**(Content Producer `expert-video` 的某个 workflow,或 main 的素材加工技能),供 Brief 的 `workflow` 字段直接引用。 +- 高数据内容要回读创意、形态与包装,不能只归因播放量 / 阅读量。 - 为每个维度写聚合结论、报告依据和可执行规则。 -- 确保 DNA 文档能推导 template。 +- 确保 DNA 文档能推导 template;账号运营子模块的结论只留在 DNA 文档。 ## 更新已有 DNA @@ -124,7 +141,7 @@ douyin-style-profiler update \ ### 用户偏好 -用户偏好不直接入库。通过 `--user-input` 传入后,Agent 映射到具体维度,并把原话转译为账号级规则与 Brief 规则。 +用户偏好不直接入库。通过 `--user-input` 传入后,Agent 映射到具体维度,并把原话转译为具体维度的创作规则与 Brief 规则。 ### 局部借鉴 @@ -132,22 +149,23 @@ douyin-style-profiler update \ ### 表现反馈 -复盘产生的建议先列证据与影响维度,经用户确认后写入 DNA;不得把一次数据波动直接升格为账号规则。 +复盘产生的建议先列证据与影响维度,经用户确认后写入 DNA;不得把一次数据波动直接升格为 DNA 规则。 ## DNA 使用接口 -- **内容生产**:读取 DNA 文档与 template,确定定位、选题、标题包装、内容形式、发布节奏和高数据创意。 -- **视频全案**:main agent 产出 Brief;Brief 写明 Pipeline、素材授权、验收标准、交付边界。未指定 Pipeline 时 CP 自由发挥,指定时必须采用。 -- **口播类视频**:若口播文案 DNA 已启用,main agent 写口播文案并随 Brief 交付;CP 不重写策略文案。 -- **图文/长文**:main agent 直接生产。 +- **图文内容**:读取图文 DNA 文档与 template,main agent 直接生产。 +- **视频全案**:读取视频 DNA 文档与 template,main agent 产出 **Brief**(+ 口播类的口播文案)。Brief 写明选题与观看理由、标题与简介、内容创意、`workflow`(视频形态的制作指向)、制作规格(横竖屏 / 时长带 / 画面风格 / 配音音色)、素材清单与授权(绝对路径)、验收标准、闸门批准人。 +- **Brief 不含 DNA 信息**:Content Producer 看不到 main 的 DNA,只按 Brief 制作;也不要把 DNA 文档路径写进 Brief。 +- **口播类视频**:口播文案子模块启用时,口播终稿由 main agent 写好并随 Brief 交付;真人口播时由 main agent 向用户取得录音文件。CP 不重写策略文案。 +- **工作区**:main 不替 CP 建工作区,也不指定项目目录;CP 自建工作区,双方 T3 权限可互访取文件。 ## 对标接口 -对标样本进入独立 `dna-id`;比较时输出定位、选题、标题包装、内容形式、发布节奏、高数据创意与制作管线的差异。用户明确采纳后才融合进 `dna-0`。 +对标样本进入独立 `dna-id`(同样按作品类型分流);比较时输出选题、标题与封面、内容创意、视频形态与制作指向、制作规格的差异,以及(账号批量样本才有的)账号运营子模块差异:简介写法、内容形式比例、发布习惯。用户明确采纳后才通过局部融合写进目标 DNA。 ## 编排原则 -- 一个生产任务只使用一个 DNA;需要融合时先更新 DNA。 +- 一个生产任务只使用一个 DNA,且作品类型与任务一致;需要融合时先更新 DNA。 - 样本、用户输入、数据反馈必须可追溯。 -- 账号级结论必须有覆盖度;不足就写未观测。 -- Template 只写 main agent 可执行的输入/Brief 规则,不写成片制作细节。 +- 账号运营子模块(简介写法、内容形式比例、发布习惯)只在对标账号批量样本下填写,且只进 DNA 文档不进 template;覆盖度不足就写未观测。 +- Template 只写 main agent 可执行的规则:视频类 = Brief 正文模板 + 口播文案模板;图文类 = 图文写作模板。都不写成片制作细节。 diff --git a/crews/main/skills/expert-wx-channel/SKILL.md b/crews/main/skills/expert-wx-channel/SKILL.md index 7a49beab..db4f4842 100644 --- a/crews/main/skills/expert-wx-channel/SKILL.md +++ b/crews/main/skills/expert-wx-channel/SKILL.md @@ -1,6 +1,6 @@ --- name: expert-wx-channel -description: 微信视频号账号运营专家。承接定位起号、账号级 DNA、选题包装、已有素材轻加工、视频全案 Brief、发布与数据复盘;全片制作委托 content-producer。 +description: 微信视频号账号运营专家。承接定位起号、内容 DNA、选题与短标题/视频描述包装、已有素材轻加工、视频全案 Brief 与口播文案、发布与数据复盘;全片制作委托 content-producer。 metadata: openclaw: emoji: 📺 @@ -14,7 +14,7 @@ metadata: | 场景 | Workflow | 什么时候触发 | |------|----------|-------------| -| 内容 DNA 管理 | Style DNA | 建 / 更新内容 DNA(样本、偏好、局部借鉴、对标融合),决定样本落到哪个 DNA | +| 内容 DNA 管理 | Style DNA | 建 / 更新内容 DNA(样本、偏好、局部借鉴、对标融合):先判作品类型,再决定样本落到哪个 DNA | | 内容生产 | Content Production | 做一条 / 做几条视频号内容;main 直接做已有素材轻加工,视频全案只产出并委托 Brief | | 起号与定位 | Account Setup | 新号起号、定位梳理、内容支柱搭建、冷启动方案、老号接手与诊断 | | 账号对标 | Account Benchmark | 对标账号 / 对标视频分析,并与默认或指定 DNA 逐项比较 | @@ -37,7 +37,7 @@ metadata: | 工具 | 用途 | 命令 | |------|------|------| -| `wx-channel-style-profiler` | 生成单条视频的账号级 DNA report,并聚合 DNA 文档与 Brief template | `wx-channel-style-profiler` | +| `wx-channel-style-profiler` | 生成单条视频的 DNA report,并聚合 DNA 文档与 template(= Brief + 口播文案模板) | `wx-channel-style-profiler` | | `wechat-channels-publish` | 发布视频到视频号创作者中心(camoufox-cli 持久化 session `wechat-channel`) | 无 wrapper,按工具说明驱动 `camoufox-cli` | | `wx-channel-engagement` | 视频号助手后台作品数据抓取,写入 published-track 的 `pub_wx_channel` 表 | `wx-channel-engagement` | @@ -45,12 +45,12 @@ metadata: 视频制作链路(内容生产时按需编排,不属于本专家包):`content-producer` subagent(吃 Brief 制作成片)、`video-edit`(已有素材加工拼接)、`talking-head-cut`(口播轻剪辑)、`viral-chaser`(抖音/B站/小红书视频追爆拆解)。 -**分工硬边界**:main 只做已有视频素材的简单加工和长文 / 图文内容;视频全案只产出 Brief 并委托 `content-producer`。口播类视频若 DNA 启用口播文案 DNA,口播文案由 main 写好并随 Brief 交付;Content Producer 只负责声画制作。Brief 指定 Pipeline 时 CP 必须采用,未指定时 CP 自由发挥。 +**分工硬边界**:main 负责选题策划、按 DNA 出 **Brief**、拟定短标题与视频描述、准备素材(绝对路径写进 Brief)、监督推动 CP、成片后的发布与运营,也直接做已有素材轻加工;视频全案的成片制作委托 `content-producer`。口播类视频的口播文案由 main 按 `narration-script` 子模块写好并随 Brief 交付(真人口播时向用户取得录音文件),CP 不重写策略文案。Brief 指定 `workflow` 时 CP 必须采用,未指定时 CP 按通用阶段链自由发挥。Brief **不含 DNA 信息**,main 也不替 CP 建工作区(双方 T3 权限可互访取文件)。 ## 平台速查 - 视频号核心引擎是**社交推荐 > 算法推荐**:分享(转发朋友圈/群聊)权重高于点赞;判断内容健康度交叉看「完播 × 分享」。 -- 视频号作品**没有标题概念**:视频简介(≤300 字,含 hashtag)就是作品展示文本;`published-track record --platform wx_channel --title` 必须传完整视频简介;`--title` 只是数据库字段名,不代表平台标题。 +- 视频号发布页可同时填 **视频描述**(≤300 字,含 hashtag)与 **短标题**,官方称填短标题能获得更多流量:**两项都必须有、发布时都必须填**,都由 main agent 拟定。但作品管理页不展示短标题,所以取数、`wx-channel-engagement` 匹配与 `published-track record --platform wx_channel --title` 一律只用完整视频描述(`--title` 只是数据库字段名),**短标题不入库**。 - 发布与取数共用持久化 session `wechat-channel`(fail-first 队列):读到「session 正忙」就等当前操作完成再重试,不自动 close。 - 前 3 秒决定去留:封面三要素(身份 + 痛点 + 解决方案),前 2 秒抛冲突,第 3 秒预告价值。 - 真人出镜占比建议 ≥ 60%;起号期前 5 条必须垂直打透一个定位,周更 3-5 条。 @@ -60,9 +60,9 @@ metadata: ## 风格与 DNA -账号级 DNA 存储目录是 `wx_channel/dna/`。未指定 DNA 时默认使用并更新 `dna-0`。生产前同时读取 DNA 文档与 DNA template;对标分析先建立独立对标 DNA,不默认写入 `dna-0`。 +DNA 存储目录是 `wx_channel/dna/`。未指定 DNA 时默认使用并更新 `dna-0`。生产前同时读取 DNA 文档与 DNA template;对标分析先建立独立对标 DNA,不默认写入 `dna-0`。 -DNA 是账号级框架:定位与核心传达、选题组合、视频简介包装、账号简介、内容形式比例、发布习惯、高数据创意、视觉/声音倾向、口播文案 DNA、社交分享闭环、互动系列与制作管线。它指导 main agent 出内容或 Brief,不规定成片制作细节。维度框架 v1 位于 `wx-channel-style-profiler` 的 `references/account-dna-framework.md`。 +DNA 是**从一批作品样本提取并聚合出的内容生产规则集**(不存在「平台级 / 账号级 DNA」):8 维——选题与观看理由、短标题与视频描述与封面、内容创意、视频内容形态与制作指向、制作规格与视听倾向,加可选的口播文案子模块与账号运营子模块(简介写法、发布习惯)。它指导 main agent 出 Brief(+ 口播文案)与发布文案,不规定创作细节与成片制作。维度框架 v2 位于 `wx-channel-style-profiler` 的 `references/video-dna-framework.md`。 ## 数据与记录 diff --git a/crews/main/skills/expert-wx-channel/tools/wechat-channels-publish/SKILL.md b/crews/main/skills/expert-wx-channel/tools/wechat-channels-publish/SKILL.md index f6debad1..d1d82e88 100644 --- a/crews/main/skills/expert-wx-channel/tools/wechat-channels-publish/SKILL.md +++ b/crews/main/skills/expert-wx-channel/tools/wechat-channels-publish/SKILL.md @@ -1,6 +1,6 @@ --- name: wechat-channels-publish -description: 通过 camoufox-cli 持久化 session wechat-channel 发布视频到微信视频号,支持视频上传、视频简介填写、即时发布。 +description: 通过 camoufox-cli 持久化 session wechat-channel 发布视频到微信视频号,支持视频上传、视频描述与短标题填写、即时发布。 --- # wechat-channels-publish — 工具说明 @@ -9,9 +9,11 @@ description: 通过 camoufox-cli 持久化 session wechat-channel 发布视频 通过 **camoufox-cli** 持久化 session `wechat-channel`(有且只有一个,fail-first 队列:同 session 已有命令在跑时新命令直接 fail)在微信视频号创作者中心发布视频。视频号创作者中心使用 **wujie 微前端**,所有表单元素在 `::shadow-root` 内——camoufox-cli 的 `snapshot` 默认穿透 shadow DOM 拿 ref,后续 `click` / `type` / `upload` 按 ref 操作即可,无需 CDP hack。 -**输入**:本地视频文件(`.mp4` / `.mov` / `.avi` / `.webm`)、视频简介(含话题标签,最长约 300 字)。视频号没有标题字段,不要把简介拆成短标题。 +**输入**:本地视频文件(`.mp4` / `.mov` / `.avi` / `.webm`)、**视频描述**(含话题标签,最长约 300 字)、**短标题**(6-16 字)。发布页改版后两项都能填,官方明确「填写短标题会获得更多流量」,因此**两项都必须填**,都由 main agent 拟定后交给本工具。 **输出**:视频号已发布作品;能取到时附带公开链接(`https://weixin.qq.com/sph/xxxx`)。 +> **短标题只在发布页存在**:作品管理页与 `wx-channel-engagement` 抓取都拿不到短标题,所以入库与匹配一律只用视频描述(见文末「入库衔接约束」)。 + > **主力后端 = `target=camoufox`**。下方命令 / 示例只针对 `target=camoufox`。 > **`target=host` / `target=node`**:只按本说明书的「流程 + 提示事项」走——全部无头 / 频率限制 / 错误处理约定是**后端无关**的,照本说明书执行。不要照搬 `camoufox-cli ...` 命令,用你当前后端自带的浏览器工具语义调用即可。 @@ -65,16 +67,24 @@ camoufox-cli --session wechat-channel --persistent --json open "https://channels - 失败:`[class*="upload-fail"]` 或文本"上传失败" - **最长等待 3 分钟**(大视频转码可能较慢) -### Step 5: 填写视频简介 +### Step 5: 填写视频描述 + 短标题(两项都必填) ``` -1. snapshot 拿到视频简介输入框 ref:div[contenteditable][data-placeholder="添加描述"] -2. camoufox-cli --session wechat-channel --persistent --json click <简介-ref> 聚焦 -3. camoufox-cli --session wechat-channel --persistent --json type <简介-ref> "视频简介内容 #话题1 #话题2" - - 话题标签直接写在视频简介中 +1. snapshot 拿到视频描述输入框 ref:div[contenteditable][data-placeholder="添加描述"] +2. camoufox-cli --session wechat-channel --persistent --json click <描述-ref> 聚焦 +3. camoufox-cli --session wechat-channel --persistent --json type <描述-ref> "视频描述内容 #话题1 #话题2" + - 话题标签直接写在视频描述中 - 最长约 300 字 +4. snapshot 找短标题输入框 ref:placeholder / 标签文本含「短标题」(官方提示形如「填写短标题会获得更多流量」) +5. camoufox-cli --session wechat-channel --persistent --json click <短标题-ref> +6. camoufox-cli --session wechat-channel --persistent --json type <短标题-ref> "短标题文本" + - 6-16 字,不与视频描述重复堆砌 + - 页面对短标题有字数上限提示时按页面为准裁到上限内 +7. snapshot 复核两个字段都已落入文本(shadow DOM 内 contenteditable 的文本要真的读到),任一为空则重填后再发布 ``` +> 改版后发布页字段名与提示文案可能微调:**以 snapshot 读到的实际 placeholder / 标签文本为准**定位,不要写死选择器。找不到短标题字段时(页面回滚或灰度未开),只填视频描述并在回报里注明「短标题字段未出现」,不要把它塞进描述。 + ### Step 6: 发布 > 视频号发布不必勾选"原创声明",发布后用户会在手机端补充。 @@ -98,7 +108,7 @@ camoufox-cli --session wechat-channel --persistent --json open "https://channels 发布成功后,在视频号管理后台的视频列表页获取视频公开链接: ``` -1. snapshot 找到刚发布的视频(列表第一条,或按完整视频简介匹配)ref +1. snapshot 找到刚发布的视频(列表第一条,或按完整视频描述匹配)ref 2. snapshot 找该视频的"分享"按钮 ref → click 3. snapshot 在弹出的分享面板中找"复制视频链接"按钮 ref → click 4. snapshot eval 从剪贴板或弹窗读取链接: @@ -162,6 +172,12 @@ camoufox-cli --session wechat-channel --persistent --json open "https://channels - **症状**:跳转到扫码登录页,无用户名/密码选项 - **workaround**:走前置条件的无头截图扫码流程(screenshot QR PNG → 发用户扫码 → 轮询 URL 确认登录就位) +### pitfall: short_title_field_missing + +- **触发**:发布页填写短标题时 +- **症状**:snapshot 里找不到「短标题」输入框(页面灰度未开或改版回滚) +- **workaround**:只填视频描述并发布,回报里注明「短标题字段未出现」;不要把短标题拼进视频描述,也不要把描述截断当短标题 + ### pitfall: form_reset_on_idle - **触发**:填写完表单后长时间不操作(旧版 camoufox-cli daemon idle 60s 自退后新起 daemon,page 变空白;2026-08-22 起已默认关闭 idle 自退,此 pitfall 应不再复现) @@ -177,7 +193,7 @@ camoufox-cli --session wechat-channel --persistent --json open "https://channels | 未登录 | 走前置条件的无头截图扫码登录流,重试一次 | | 上传失败 | 检查视频格式(mp4/mov/avi/webm),重试一次 | | 转码超时 | 增加超时时间,或告知用户稍后在创作者中心检查 | -| 发表按钮 disabled | 检查必填字段是否已填写(视频是否上传完成) | +| 发表按钮 disabled | 检查必填字段是否已填写(视频是否上传完成、视频描述与短标题是否都落入文本) | | shadow DOM 元素找不到 | 等待更长时间让 wujie 初始化,或刷新页面 | | session 正忙(fail-first) | 等当前操作完成再重试,不要盲试 | @@ -187,6 +203,6 @@ camoufox-cli --session wechat-channel --persistent --json open "https://channels 本工具只管发布到视频号后台,**不做发布记录入库**;入库由 Content Production Workflow 编排(调 `published-track record`)。调用方必须注意: -> **`published-track record --platform wx_channel --title` 必须传 Step 5 填的完整视频简介**(含 hashtag,最长约 300 字)。 +> **`published-track record --platform wx_channel --title` 必须传 Step 5 填的完整视频描述**(含 hashtag,最长约 300 字);**短标题不入库**。 -原因:视频号作品没有「标题」概念,作品管理页展示与 `wx-channel-engagement` 抓取匹配用的都是完整视频简介。`pub_wx_channel.title` 是数据库字段名,语义为完整视频简介;传短标题会导致后续抓取匹配失败。 +原因:作品管理页只展示视频描述,`wx-channel-engagement` 抓取匹配用的也是视频描述。`pub_wx_channel.title` 是数据库字段名,语义为完整视频描述;把短标题写进去会导致后续抓取匹配失败。短标题只留在作品目录的 `publish-copy.md` 里备查。 diff --git a/crews/main/skills/expert-wx-channel/tools/wx-channel-engagement/SKILL.md b/crews/main/skills/expert-wx-channel/tools/wx-channel-engagement/SKILL.md index 8fc0bc62..44f754c9 100644 --- a/crews/main/skills/expert-wx-channel/tools/wx-channel-engagement/SKILL.md +++ b/crews/main/skills/expert-wx-channel/tools/wx-channel-engagement/SKILL.md @@ -9,9 +9,11 @@ description: 微信视频号已发布作品数据抓取,写入 published-track 通过 **camoufox-cli + 与 `wechat-channels-publish` 共管的 `wechat-channel` 持久化 session + 视频号助手后台爬虫**,从视频号助手「内容管理 → 作品管理」页抓已发布视频的播放/点赞/评论/分享/收藏,写入 published-track 的 `pub_wx_channel` 表。 -**思路**:视频号助手后台 `channels.weixin.qq.com/platform/` 的作品管理页把每条已发布视频的播放/点赞/评论/分享/收藏列在行内,走「作品管理页 → 解析 innerText → 按完整视频简介匹配 → 提行内数字」。 +**思路**:视频号助手后台 `channels.weixin.qq.com/platform/` 的作品管理页把每条已发布视频的播放/点赞/评论/分享/收藏列在行内,走「作品管理页 → 解析 innerText → 按完整视频描述匹配 → 提行内数字」。 -**输入**:`--row-id`(pub_wx_channel 行 id,fetch 单篇)或不带参数(list / fetch-all 批量)。行内数据按 `row.title`(数据库字段名,语义为完整视频简介)在作品管理页匹配。 +**输入**:`--row-id`(pub_wx_channel 行 id,fetch 单篇)或不带参数(list / fetch-all 批量)。行内数据按 `row.title`(数据库字段名,语义为完整视频描述)在作品管理页匹配。 + +> **只用视频描述,不用短标题**:视频号发布页改版后可以同时填视频描述与短标题,但作品管理页只展示视频描述,短标题抓不到。因此入库(`published-track record --title`)与本工具的匹配一律用完整视频描述,短标题不写库、不参与匹配。 **输出**:行内 metrics(plays / likes / comments / shares / favorites),并经 published-track `update-metrics` 写入 `pub_wx_channel`。 **限制**:仅支持用户**自己有后台权限的号**(视频号助手用微信扫码登录)。竞品号拿不到——这是产品约束,不是技术约束。 @@ -53,7 +55,7 @@ wx-channel-engagement login --reset # 删 profile 目录 + 重新 open,从 退出码: - `0` 成功 -- `1` 通用错误(参数错 / row 找不到 / 完整视频简介未匹配) +- `1` 通用错误(参数错 / row 找不到 / 完整视频描述未匹配) - `2` session 失效(后台首页跳登录页) - `3` session 正忙(fail-first 队列) @@ -94,7 +96,7 @@ wx-channel-engagement fetch-all # 批量刷新(心跳用): 4. **数据提取方式**:不依赖 selector,直接用 `document.body.innerText` 解析(穿透 shadow DOM 后)。页面 innerText 结构清晰: ``` - <完整视频简介> + <完整视频描述> <发布时间> <播放数> <点赞数> <评论数> <分享数> <收藏数> ``` @@ -110,7 +112,7 @@ wx-channel-engagement fetch-all # 批量刷新(心跳用): 3. 复用 wechat-channel 持久化 session(不开独立 session、不 import cookie): camoufox-cli --session wechat-channel --persistent --json open "https://channels.weixin.qq.com/platform/post/list" 4. eval JS 解析作品管理页 innerText -> [{title, metrics}, ...] -5. match_article(rows, row.title) -> 按完整视频简介归一化匹配 +5. match_article(rows, row.title) -> 按完整视频描述归一化匹配 6. update-metrics --platform wx_channel --id ... -> 写 pub_wx_channel 7. finally: close session(登录态在磁盘 profile,不留进程占内存;下次 fetch 按需重起无头 session,profile 桥接登录态) ``` diff --git a/crews/main/skills/expert-wx-channel/tools/wx-channel-engagement/scripts/fetch_engagement.py b/crews/main/skills/expert-wx-channel/tools/wx-channel-engagement/scripts/fetch_engagement.py index b97ef820..bba249b2 100644 --- a/crews/main/skills/expert-wx-channel/tools/wx-channel-engagement/scripts/fetch_engagement.py +++ b/crews/main/skills/expert-wx-channel/tools/wx-channel-engagement/scripts/fetch_engagement.py @@ -5,7 +5,7 @@ 评论数 / 分享数 / 收藏数,写入 published-track 的 pub_wx_channel 表。 与 wx-mp-engagement 同源方法:camoufox 打开创作者后台 → 解析 innerText → -按完整视频简介匹配 → 提行内数字。视频号助手后台使用 wujie 微前端,shadow DOM 内 +按完整视频描述匹配 → 提行内数字。视频号助手后台使用 wujie 微前端,shadow DOM 内 文本需用 eval 手写 document.querySelector('wujie-app').shadowRoot.innerText。 CLI 形态: @@ -647,9 +647,9 @@ def _extract_metrics(row: dict) -> dict: def match_post(rows: list[dict], target_desc: str, target_date: str | None = None) -> dict | None: - """按完整视频简介在后台列表里找最匹配的行,返回 {desc, metrics} + """按完整视频描述在后台列表里找最匹配的行,返回 {desc, metrics} - 视频号作品管理页展示的是完整视频简介(desc),没有短标题——DB 里 title 列存的 + 视频号作品管理页只展示完整视频描述(desc);发布页虽可另填短标题,但管理页不展示、抓不到——DB 里 title 列存的 也应是完整 desc(见 main AGENTS.md 发布工作流)。匹配策略(小贝建议): 1. 用发布日期±1天筛同日候选(后台行 published_at 形如「2026年08月03日 12:06」) 2. 拿 desc 前 60 字归一化包含匹配——避开 hashtag 噪声,够区分 diff --git a/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/SKILL.md b/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/SKILL.md index f72a83e9..cb759ef3 100644 --- a/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/SKILL.md +++ b/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/SKILL.md @@ -1,6 +1,6 @@ --- name: wx-channel-style-profiler -description: 提取视频号账号级 DNA:单条视频生成 report,按 DNA ID 聚合定位、选题、视频简介、内容形式、发布节奏、高数据创意、社交分享与制作管线,推导 main agent 的 Brief template。 +description: 提取微信视频号作品 DNA:单条视频生成 report,按 dna-id 聚合选题、短标题与视频描述、内容创意、视频形态与制作指向、制作规格、口播文案子模块与账号运营子模块,推导 main agent 的视频 Brief template。 metadata: openclaw: emoji: 🧬 @@ -8,21 +8,25 @@ metadata: # wx-channel-style-profiler -视频号账号级 DNA 提取与聚合工具。输入是**口播脚本或逐字稿文本**与可选封面图;不接受视频文件或链接作为直接输入。首个一级标题只作为样本标签 / 视频简介摘要,不代表平台标题。视频观测信息由 Agent 结合原视频、拆解报告或用户提供信息补齐。 +微信视频号作品 DNA 提取与聚合工具。输入是**视频转录文本**(口播全文 + 视频描述 / 时长 / 互动线索),由 Agent 先整理成 `.md` / `.txt`;可选封面 / 首帧 / 配图作为视觉证据。不接受视频文件或链接作为直接输入(取数走 `viral-chaser` 或平台工具)。 -DNA 的用途是指导 main agent 选题、包装、账号表达和视频制作 Brief;不指导 main agent 直接产出全片。 +DNA 的用途是指导 main agent 选题、包装、出内容或出视频制作 Brief;**不指导成片制作**——创作细节、脚本结构、镜头与编码参数归 Content Producer。 + +## 作品类型 + +微信视频号作品只有视频一种,维度框架见 `references/video-dna-framework.md`;`--kind` 只接受 `video`(默认即 `video`,通常不用传)。 ## 产物模型 ```text -单条视频 -> DNA report -同一 DNA 下的全部 report + 权重/focus + 用户输入 -> DNA 文档 -DNA 文档 -> main agent Brief template +单篇作品 -> DNA report +同一 dna-id 下全部 report + 权重 / focus + 用户输入转译 -> DNA 文档 +DNA 文档 -> DNA template ``` -- **DNA report**:单条样本的账号级观测与 14 维提取结果。 -- **DNA 文档**:聚合后的账号级规则与样本覆盖度说明。 -- **DNA template**:main agent 生成内容或委托 Content Producer 时使用的 Brief / 生产输入模板。 +- **DNA report**:单篇作品的样本观测 + 维度提取结果(不是账号级结论,也不是模板)。 +- **DNA 文档**:聚合后的生产规则、样本覆盖度、子模块结论与用户输入转译区。 +- **DNA template**:main agent 的生产输入模板(视频 = Brief 正文 + 口播文案;图文 = 写作模板)。 ## 存储结构 @@ -36,85 +40,69 @@ wx_channel/dna/{dna-id}/ {dna-id}.template.md ``` -原始脚本可临时放在 `wx_channel/ref/{dna-id}/transcripts/`;生成后的 report 必须进入目标 DNA 的 `reports/` 目录。 +原始转录 / 正文文本可临时放在 `wx_channel/ref/{dna-id}/transcripts/`;生成后的 report 必须进入目标 DNA 的 `reports/` 目录。 ## 职责边界 -- 单条样本只提供候选信号;账号比例、发布节奏、社交分享闭环、高数据共性必须由多样本或账号级数据聚合。 -- 统计只做证据底座,不评分、不替代定性判断。 -- 视觉语言有图片/关键帧证据时才由视觉模型分析;缺失写「未观测」。 -- 口播文案 DNA 独立聚合;样本不足时保持未启用。 +- **短标题与视频描述是两项独立内容**:发布页两项都可填,官方称填短标题能获得更多流量;但作品管理页不展示短标题,取数时只能拿到视频描述。report 里短标题拿不到就写「未观测(管理页不展示)」,不得把视频描述当短标题;两者都由 main agent 拟定,发布时都必须填。 +- 单篇 report 只提供候选信号,不判断跨篇稳定性;共性、偏好、孤例由聚合阶段判断。 +- 统计只做证据底座,不评分、不判定风格是否合格。 +- 视觉维度必须有图片 / 关键帧证据,由视觉模型读取;缺失写「未提供」,不得凭文本想象补齐。 +- 口播文案子模块只在口播类作品启用;样本不足写「未启用」。 +- 账号运营子模块(简介写法、内容形式比例、发布习惯)只在样本来自对标账号批量提取时填写,且**不进 template**。 - 不输出合规结论、账号权重或风格评分。 -## Report - 单条提取 +## Report — 单篇提取 ```bash wx-channel-style-profiler report \ - --input path/to/script.md \ + --input path/to/transcript.md \ --dna-id {dna-id} \ --sample-id {sample-id} \ --cover-image path/to/cover.jpg \ - --source-url "https://channels.weixin.qq.com/..." \ + --source-url "https://..." \ + --duration 36 \ --output-dir wx_channel/dna/{dna-id}/reports ``` -- `--cover-image`:封面本地图片,用于视觉证据。 -- `--source-url`:原视频链接;本地素材无链接时省略。 +- `--kind`:作品类型(见上);不传走默认。 +- `--cover-image`:封面 / 首帧 / 首图本地文件,作为视觉证据(自动拷进 `covers/`)。 +- `--source-url`:原作品链接;本地素材无链接时省略。 +- `--duration`:视频时长(秒),用于口播密度统计;图文忽略。 - `--weight`:样本权重,默认 1。 -- `--focus`:限制该样本只影响指定维度,可重复传入。 +- `--focus`:限制该样本只影响指定维度 ID,可重复传入。 Agent 生成 scaffold 后必须: -1. 补齐「视频信息」与「样本与账号观测」:样本类型、账号与简介、发布时间、内容形式、数据线索、横竖屏、素材来源与授权信息。 -2. 回读脚本原文,补齐 14 维的单条结论、原文证据和可复用信号。 -3. 单条样本无法观测账号简介、比例、节奏时写「未观测」。 -4. 高数据样本必须回读创意与内容形式,不得只凭播放量下结论。 +1. 补齐「样本观测」:作品类型、样本来源、账号与简介、发布时间、数据线索、素材来源与授权。 +2. 回读原文,补齐各维度的单篇结论、原文证据与可复用信号。 +3. 视频形态必须给出**制作指向**(Content Producer `expert-video` 的某个 workflow,或 main 的某个素材加工技能),只写真实存在的资源名。 +4. 账号运营子模块无法从单篇观测时写「未观测」。 +5. 高数据样本必须回读创意与形态再归因,不得只凭播放 / 阅读量下结论。 -## Build - 聚合 DNA 文档与模板 +## Build — 聚合 DNA 文档与模板 ```bash wx-channel-style-profiler build --dna-id {dna-id} -``` -默认读取 `wx_channel/dna/{dna-id}/reports/`,输出: - -```text -wx_channel/dna/{dna-id}/{dna-id}.dna.md -wx_channel/dna/{dna-id}/{dna-id}.template.md ``` -也可显式传入 report 文件/目录: +默认读取 `wx_channel/dna/{dna-id}/reports/`,输出 `{dna-id}.dna.md` 与 `{dna-id}.template.md`。也可显式传 report 文件 / 目录: ```bash -wx-channel-style-profiler build \ - --input path/to/reports \ - --dna-id {dna-id} +wx-channel-style-profiler build --input path/to/reports --dna-id {dna-id} ``` Agent 聚合时必须: 1. 读取全部 DNA report,不能只看统计表。 2. 按 `weight` 与 `focus` 判断影响范围。 -3. 区分高频共性、高权重偏好、局部借鉴、孤例和例外。 -4. 标注样本覆盖度;单条/少量样本不得称为稳定账号 DNA。 -5. 为每个维度写聚合结论、报告依据和可执行规则。 -6. 确保 DNA 文档能完整推导 template。 - -## DNA Template - -Template 是 main agent 的 Brief / 内容生产输入模板,不是成片制作模板。固定语义段: - -1. 定位与核心传达 -2. 选题与简介包装 -3. 内容形式与发布节奏 -4. 高数据创意模式 -5. 互动与系列 -6. 制作交接与 Pipeline -7. 口播文案 DNA(可选) +3. 区分高覆盖共性、高权重偏好、局部借鉴、孤例与例外。 +4. 标注样本覆盖度;少量样本不得称为稳定结论。 +5. 为每个维度写聚合结论、报告依据与可执行创作规则。 +6. 确保 DNA 文档能完整推导 template(template 不得引入 DNA 文档未确认的规则)。 -制作交接段必须写清 MainAgent 与 Content Producer 的交付物、Pipeline、素材授权和风格边界。Brief 未指定 Pipeline 时,Content Producer 可自由选择;指定 Pipeline 时必须直接采用。 - -## Update - 增量聚合 +## Update — 增量聚合 ```bash wx-channel-style-profiler update \ @@ -123,55 +111,68 @@ wx-channel-style-profiler update \ --template wx_channel/dna/{dna-id}/{dna-id}.template.md ``` -脚本合并历史 report 与新 report,重新计算统计并保留 Agent 已完成内容。Agent 仍需重新审视聚合结论,并同步修订 DNA 文档和 template。 +脚本合并历史 report 与新 report、重算统计并保留 Agent 已写内容;kind 从 DNA 文档 frontmatter 继承(也可用 `--kind` 显式指定,冲突时报错)。Agent 仍需重新审视聚合结论并同步修订 template。 `--input` 可省略,用于只融合用户输入或另一个 DNA 的局部规则;此时必须传 `--user-input`。 +## DNA Template + +**视频作品 template(= Brief 正文模板 + 口播文案模板)** + +1. 选题 +2. 短标题与视频描述 +3. 内容创意 +4. 视频形态与制作指向 +5. 制作规格 +6. 口播文案 + +- 开头两段(**选题**、**标题与封面**)跨平台通用。 +- 视频 template 的各段直接对应 Brief 正文字段;**Brief 不含 DNA 信息**(Content Producer 看不到 main 的 DNA),素材清单与授权、验收标准、闸门批准人按平台 Content Production Workflow 填。 +- 账号运营子模块不进 template,只留在 DNA 文档。 + ## 用户输入转译 -用户输入是参考信息,不是可直接入库的 DNA 规则。 +用户输入是参考信息,不是可直接入库的 DNA 规则: ```bash ---user-input "这个号偏真实口播,不要过度包装" +--user-input "以后多做实拍拼接,少用纯动画" ``` -Agent 必须转译到具体维度,例如: +Agent 必须映射到具体维度并转译为可执行规则,例如: ```text -content-form-mix:主形态为真人口播,减少纯动画 -narration-dna:口播保留自然停顿,不使用强促销句式 -production-pipeline:Brief 默认使用 video-producer:default(narrative) +video-form:主形态为影视解说 + 反转植入,制作指向 expert-video 的 Reversal Ad workflow +production-spec:竖屏 9:16、时长带 30-45s、画面偏冷暗解说段 + 明亮植入段 +narration-script:口播保持第三人称解说体,句长 10-15 字 ``` 处理要求: -1. 在 DNA 文档的「用户输入转译区」记录 affected dimensions、DNA 修改和 template 修改。 +1. 在 DNA 文档「用户输入转译区」记录 raw_input、affected dimensions、DNA 修改、template 修改与状态。 2. 原话必须转译为可执行规则,不得直接抄进 template。 3. 与样本证据冲突时保留冲突说明,由用户选择优先级。 ## Focus ID +### Focus ID + | ID | 维度 | |---|---| -| `positioning-core` | 定位与核心传达 | -| `topic-portfolio` | 选题组合 | -| `description-packaging` | 简介与包装 | -| `bio-profile` | 账号简介与主页表达 | -| `content-form-mix` | 内容形式与比例 | -| `publish-cadence` | 发布习惯 | -| `high-performer-patterns` | 高数据创意模式 | -| `visual-language` | 视觉语言 | -| `audio-language` | 声音语言 | -| `narration-dna` | 口播文案 DNA | -| `engagement-conversion` | 互动与转化 | -| `social-share-loop` | 社交分享闭环 | -| `series-signature` | 系列与签名 | -| `production-pipeline` | 制作管线倾向 | +| `topic-angle` | 选题与观看理由 | +| `title-cover` | 短标题、视频描述与封面 | +| `content-idea` | 内容创意 | +| `video-form` | 视频内容形态与制作指向 | +| `production-spec` | 制作规格与视听倾向 | +| `narration-script` | 口播文案子DNA | +| `account-bio` | 账号简介写法 | +| `content-mix-cadence` | 内容形式比例与发布习惯 | + +`--focus` 按作品类型校验:视频 report 不接受图文维度 ID,反之亦然。 ## 统计与分词 -脚本统计口播脚本的句段、标点、人称等指标;中文高频信号使用相邻二字组合,仅作为候选线索。Agent 必须回读原文确认口头禅或签名式表达。时长、镜头、出镜占比等观测由 Agent 补齐,不由文本统计推断。 +脚本统计句长、问句与人称密度、感叹号密度、口播密度等指标作为聚合证据底座;中文高频信号使用相邻二字组合,仅作候选线索。口头禅与签名式表达必须由 Agent 回读原文确认,分词结果不能直接当 DNA 结论。 ## 参考资料 -- `references/account-dna-framework.md`(视频号账号级 DNA 框架 v1、Pipeline 映射与聚合边界) +- `references/video-dna-framework.md`(微信视频号视频作品 DNA 框架 v2:维度定义、制作指向映射、聚合边界与 Focus ID) diff --git a/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/references/account-dna-framework.md b/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/references/account-dna-framework.md deleted file mode 100644 index f85c6eee..00000000 --- a/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/references/account-dna-framework.md +++ /dev/null @@ -1,84 +0,0 @@ -# 视频号账号级 DNA 框架(v1) - -> 状态:v1。Focus ID、维度命名与 template 语义以本文件为准。调整维度必须升版本,并同步 `scripts/build_style_profile.py` 与工具 `SKILL.md`。 - -## 定位与边界 - -视频号 DNA 是**账号级运营框架**,不是单条视频的脚本或剪辑细节。视频号作品没有标题,只有视频简介。它回答账号长期如何被识别、如何选题、如何包装、如何安排内容形式与发布节奏,并指导 main agent 产出制作 Brief。 - -- **必须进入 DNA**:定位与核心传达、选题组合、视频简介、账号简介、内容形式及比例、发布习惯、高数据内容创意共性、视觉/声音倾向、口播文案 DNA、社交分享动机、系列标记、制作管线倾向。 -- **不进入主 DNA**:逐句口播、镜头表、转场参数、字幕样式、成片剪辑微操。口播文案 DNA 是独立可选块;没有证据时写「未观测」。 -- **MainAgent / Content Producer 分界**:main agent 负责已有视频素材的简单加工、长文/图文内容,以及视频全案 Brief。除 Brief 之外的成片制作由 Content Producer 执行。口播类视频的口播文案若由 DNA 明确启用,则由 main agent 写好并随 Brief 交付;Content Producer 只做声画制作。 -- **样本可以是账号或单条**:账号样本可提取简介与发布习惯;用户提供单条或多条单篇时,仍按同维度聚合,但账号级字段必须标「样本不足 / 未观测」。 - -## 维度(14 维) - -### 一、定位与选题 - -| # | ID | 维度 | 观测内容 | -|---|----|------|----------| -| 1 | `positioning-core` | 定位与核心传达 | 一句话定位、目标人群、长期承诺、内容主张边界 | -| 2 | `topic-portfolio` | 选题组合 | 常见选题类型、内容支柱、选题入口、系列关系 | -| 3 | `description-packaging` | 简介与包装 | 视频简介模式、封面承诺、话题标签策略;不规定具体字句 | -| 4 | `bio-profile` | 账号简介与主页表达 | 昵称、简介、置顶内容、主页承诺;仅账号样本可得 | - -### 二、账号节奏 - -| # | ID | 维度 | 观测内容 | -|---|----|------|----------| -| 5 | `content-form-mix` | 内容形式与比例 | 图文、视频、口播、实拍拼接、创意转场、纯 AIGC 动画等形式;如混合,记录比例或倾向 | -| 6 | `publish-cadence` | 发布习惯 | 发布时间段、频率、内容形式混合节奏、栏目化节奏 | -| 7 | `high-performer-patterns` | 高数据创意模式 | 高播放/高完播/高转发样本的选题、包装、形式与创意共性;数据只是证据 | - -### 三、表达与风格 - -| # | ID | 维度 | 观测内容 | -|---|----|------|----------| -| 8 | `visual-language` | 视觉语言 | 稳定画面风格、横竖屏、视觉记忆点;不规定镜头参数 | -| 9 | `audio-language` | 声音语言 | 配音类型/音色倾向、口播密度、BGM/音效倾向;不规定 TTS 参数 | -| 10 | `narration-dna` | 口播文案 DNA | 独立可选块:开头钩子、起承转合、收束、人称与语气、签名表达 | - -### 四、互动与制作 - -| # | ID | 维度 | 观测内容 | -|---|----|------|----------| -| 11 | `engagement-conversion` | 互动与转化 | 评论、关注、直播预约、私信等平台内行动与承接路径 | -| 12 | `social-share-loop` | 社交分享闭环 | 实用价值 / 情绪态度 / 社交人设三类转发动机、分享话术、被转发后的承接 | -| 13 | `series-signature` | 系列与签名 | 栏目名、固定开场/收尾、系列承诺、识别符号 | -| 14 | `production-pipeline` | 制作管线倾向 | 视频内容形态到 Content Producer Pipeline 的映射;未指定时交给 CP 自由发挥 | - -## Pipeline 映射 - -`production-pipeline` 只能写 Content Producer 已支持的管线,不得发明不存在的管线名。 - -| DNA 观测形态 | Brief 中的 Pipeline | -|--------------|---------------------| -| 影视解说 + 反转植入 | `dna-ad-video-pipeline` | -| 口播 / 真人讲述 | `video-producer:default`(narrative) | -| 实拍拼接 / 蒙太奇 | `video-producer:default`(montage) | -| 创意转场 / 动效 | `video-producer:default`(motion) | -| 纯 AIGC 动画 | `video-producer:default`(AIGC render) | - -Brief 未写 `pipeline` 时,Content Producer 按 `video-producer:default` 自由选择实现;Brief 写了 Pipeline 时必须直接采用,不得替换成自创流程。 - -## Report 与聚合规则 - -1. 单条 report 先记录「样本与账号观测」:样本类型、账号与简介、发布时间、内容形式、数据线索、视频形态、授权信息。缺失写「未观测」。 -2. 单条样本不能推导账号比例、发布节奏、社交分享闭环或高数据共性;聚合时标注覆盖度。 -3. 高数据样本必须回读创意与内容形式,不能只凭播放量归因。 -4. 口播文案 DNA 独立聚合;没有足够样本时保持未启用。 -5. 脚本统计只提供句长、人称、语气等证据;语义判断由 Agent 回读原文完成。 - -## Template 语义 - -DNA template 是 main agent 的 **Brief / 内容生产输入模板**,不是成片制作模板。固定语义段: - -1. 定位与核心传达 -2. 选题与简介包装 -3. 内容形式与发布节奏 -4. 高数据创意模式 -5. 互动与系列 -6. 制作交接与 Pipeline -7. 口播文案 DNA(可选) - -模板不得规定镜头表、逐句台词、转场参数或编码细节。视频全案的制作选择由 Content Producer 在 Pipeline 内完成。 diff --git a/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/references/video-dna-framework.md b/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/references/video-dna-framework.md new file mode 100644 index 00000000..ff9adc50 --- /dev/null +++ b/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/references/video-dna-framework.md @@ -0,0 +1,117 @@ +# 微信视频号视频作品 DNA 框架(v2) + +> 状态:v2(2026-09-10)。Focus ID、维度命名与 template 语义以本文件为准;调整维度必须升版本,并同步 `scripts/build_style_profile.py` 与工具 `SKILL.md`。 +> 微信视频号只有视频作品,本文件即该平台的全部维度框架。 + +## 定位与边界 + +DNA 不是「平台级 DNA」,也不是账号人设说明书,而是**从一批作品样本中提取、聚合出的内容生产规则集**。生产方式与其他平台完全一致,仍是三层产物: + +```text +单篇作品 -> DNA report(本框架的维度逐项提取) +同一 dna-id 下全部 report + 权重 / focus + 用户输入转译 -> DNA 文档 +DNA 文档 -> DNA template +``` + +- 同一批样本可以来自多个不同账号,也可以来自用户指定的一个账号(从其发布列表批量提取);是否归入同一个 DNA 由用户指定,未指定入 `dna-0`。 +- 视频号作品都是视频,无需按作品类型分 DNA。 +- **与其他平台的差别只在维度**:本平台作品的观测物是口播转录文本 + 画面 + 声音 + 结构节奏,因此不照搬公众号 17 维里的表层 / 深层写作维度(用词习惯、句式节奏、修辞手法、论证逻辑等)。 + +### 进 DNA 与不进 DNA + +| 进 DNA | 不进 DNA | +|--------|----------| +| 选题与观看理由、标题与封面写法、内容创意原型、视频内容形态与制作指向、制作规格与视听倾向、(口播类)口播文案子DNA、(对标账号)账号运营子模块 | 创作细节、脚本结构、逐句台词、镜头表、转场与编码参数——那些归 Content Producer | + +视频 DNA 的用途是指导 main agent 出具 **Brief.md** 与(口播类的)**口播文案**。template 就是 Brief 正文模板 + 口播文案模板(可选),不是成片制作模板。账号运营子模块(简介写法、内容形式比例、发布习惯)只写进 DNA 文档,**不进 template**。 + +## 维度(8 维) + +### 一、选题与包装 + +| # | ID | 维度 | 观测内容 | +|---|----|------|----------| +| 1 | `topic-angle` | 选题与观看理由 | 选题类型、选题入口(现象 / 问题 / 冲突 / 数据 / 热点 / 挑战 / 个人经历)、目标人群为什么要看完(理解 / 判断 / 行动 / 避坑 / 身份认同 / 情绪共鸣)、系列与内容支柱归属 | +| 2 | `title-cover` | 短标题、视频描述与封面 | **短标题**与**视频描述**两者的写法模式与原文(发布页两项都可填,官方称填短标题能获得更多流量;作品管理页不展示短标题,取数时拿不到)、话题标签策略;封面或首帧的视觉特征与 AIGC 复现要素 | + +### 二、内容创意 + +| # | ID | 维度 | 观测内容 | +|---|----|------|----------| +| 3 | `content-idea` | 内容创意 | 一句话创意内核、创意类型、展开逻辑(悬念 / 反转 / 递进 / 对比 / 清单 / 实测)、记忆点;这个套路换主题还能怎么用 | + +### 三、形态与规格 + +| # | ID | 维度 | 观测内容 | +|---|----|------|----------| +| 4 | `video-form` | 视频内容形态与制作指向 | 视频内容形态(口播 / 实拍拼接 / 影视解说+反转植入 / 纯 AIGC 动画 / 创意转场动效 / 录屏演示 / 图文卡片视频 / 混合)+ 判定依据 + **制作指向**(见下方映射表) | +| 5 | `production-spec` | 制作规格与视听倾向 | 横屏或竖屏、时长带、画面风格(色调、质感、字幕样式倾向、信息密度)、配音音色与声音形态(原声口播 / TTS / 旁白 / 纯画面字幕)、BGM 与音效倾向、封面规格 | + +### 四、口播文案子模块(可选,仅口播类启用) + +| # | ID | 维度 | 观测内容 | +|---|----|------|----------| +| 6 | `narration-script` | 口播文案子DNA | 起(从什么起步)、承(靠什么推进)、转(转折触发)、合(收束与 CTA)、人称与语气、句长与语速、签名式表达——用于指导 main agent 写同类型视频的口播文案 | + +### 五、账号运营子模块(对标账号样本才有) + +| # | ID | 维度 | 观测内容 | +|---|----|------|----------| +| 7 | `account-bio` | 账号简介写法 | 账号昵称、简介写法、主页与置顶表达、对外承诺(仅对标账号样本可得) | +| 8 | `content-mix-cadence` | 内容形式比例与发布习惯 | 图文 / 视频等内容形式比例、发布时间段与频率、内容形式混合节奏(如三篇图文对一篇视频)、栏目化节奏(仅对标账号批量样本可得) | + +## Report 与聚合规则 + +1. 单篇 report 先填「样本观测」:作品类型、样本来源、账号与简介、发布时间、数据线索、横竖屏与时长、素材来源与授权、转录与关键帧来源。缺失一律写「未观测」,不得编造。 +2. 单篇 report 不判断跨篇稳定性;聚合时区分高覆盖共性、高权重样本偏好、局部借鉴(focus)、孤例与例外,并标注样本覆盖度。 +3. 视觉维度(封面 / 首帧 / 图组)必须有图片证据,由视觉模型读取本地图片后反推 AIGC 复现要素;没有图片写「未提供」。 +4. 口播文案子模块只在口播类作品启用;非口播类或样本不足写「未启用 / 未观测」,不得把单篇句式上升为规则。 +5. 账号运营子模块(`account-bio`、`content-mix-cadence`)只在样本来自对标账号批量提取时填写;用户只提供单篇时写「未观测」。 +6. 脚本统计只作证据底座(句长、问句密度、人称密度、感叹号密度、口播密度),不生成总分、不判定风格是否合格。 +7. 高数据样本必须回读创意、形态与包装再归因,不得把高播放直接等同于风格好。 + +## Template 语义 + +DNA template = **Brief.md 正文模板 + 口播文案模板(可选)**,固定语义段如下(脚本 `build` / `update` 自动生成骨架,字段由 Agent 从 DNA 文档推导填写): + +1. **[选题部分]** — 选题角度推荐、选题需考虑的受众关联角度、内容支柱与系列关系、避免 +2. **[短标题与视频描述部分]** — 短标题模式、参考短标题、视频描述结构、参考视频描述、话题标签策略、封面风格与 AIGC 要素 +3. **[内容创意部分]** — 创意原型、展开逻辑、记忆点与反转设计、触发条件、避免 +4. **[视频形态与制作指向部分]** — 视频内容形态、制作指向、委托边界、未指定形态时 +5. **[制作规格部分]** — 横屏或竖屏、时长带、画面风格、配音音色与声音形态、BGM 与音效、字幕 +6. **[口播文案部分]** — 是否启用、起、承、转、合、人称与语气、句长与语速、签名式表达、必须做、避免 + +- 开头两段(**选题**、**标题与封面**)跨平台通用:任何生产都先解决「做什么」和「怎么命名、怎么呈现封面」。 +- `[视频形态与制作指向部分]` 的「制作指向」必须写下方映射表里的真实资源名,Brief 的 `workflow` 字段据此填写。 +- 账号运营子模块不进 template:`account-bio` 与 `content-mix-cadence` 的结论只留在 DNA 文档,供起号、对标与发布节奏决策使用。 +- template 必须能从 DNA 文档推导,不得引入 DNA 文档未确认的规则;用户输入必须先映射到维度再转译为执行规则。 + +## 制作指向映射 + +`video-form` 维度提炼出的内容形态,必须能明确指向后续用什么技能制作: + +| DNA 观测到的内容形态 | 制作指向(只能写真实存在的资源名) | Brief `workflow` 字段值 | +|---------------------|-----------------------------------|------------------------| +| 影视解说 / 剧情解说 + 反转植入(「万万没想到」式) | Content Producer `expert-video` → **Reversal Ad** workflow | `reversal-ad` | +| 口播类(真人口播出镜,或旁白 + 画面) | Content Producer `expert-video` → **Narration Video** workflow | `narration-video` | +| 一句文稿转视觉隐喻的纸拼贴动画 | Content Producer `expert-video` → **Collage B-roll** workflow | `collage-broll` | +| 纯 AIGC 动画 / 剧情短片 / 蒙太奇拼接(需从零出脚本分镜) | Content Producer `expert-video` → 通用阶段链(narrative / motion / montage 档) | 省略(由 CP 定档位) | +| 已有素材简单拼接、加旁白、烧字幕 | main `video-edit`(不委托 CP) | — | +| 已有真人口播素材去口气词、剪高光 | main `talking-head-cut`(不委托 CP) | — | +| 产品操作录屏 | main `ui-demo`(不委托 CP) | — | + +Brief 写了 `workflow` 时 Content Producer 必须直接采用,不得替换成自创流程;未写时按通用阶段链自由选择实现。 + +## Focus ID 表 + +`--focus` 只接受本框架的维度 ID;限制某篇样本只影响这些维度。 + +| 分组 | Focus ID | +|------|----------| +| 选题与包装 | `topic-angle` `title-cover` | +| 内容创意 | `content-idea` | +| 形态与规格 | `video-form` `production-spec` | +| 口播文案子模块(可选,仅口播类启用) | `narration-script` | +| 账号运营子模块(对标账号样本才有) | `account-bio` `content-mix-cadence` | + +focus 校验按作品类型执行:视频 report 不接受图文维度 ID,反之亦然。 diff --git a/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/scripts/build_style_profile.py b/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/scripts/build_style_profile.py index 61fb60b4..1fcd2021 100644 --- a/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/scripts/build_style_profile.py +++ b/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/scripts/build_style_profile.py @@ -1,4 +1,14 @@ #!/usr/bin/env python3 +"""wx_channel-style-profiler:按作品类型(video / note)生成定性 DNA 资产。 + +三层产物(与 docs/expert-pack-dna-architecture.md 4.6 一致): + 单篇作品 -> DNA report + 同一 DNA 目录下全部 report + 权重/focus + 用户输入转译 -> DNA 文档 + DNA 文档 -> DNA template + +维度框架见本工具 references/ 下的 FRAMEWORK 文档(视频 / 图文各一份)。 +脚本只做 scaffold 与统计证据底座:不评分、不判定风格合格,定性结论由 Agent 回读原文补齐。 +""" import argparse, json, math, re, shutil from collections import Counter, defaultdict from datetime import datetime, timezone @@ -11,78 +21,183 @@ TOKEN_RE = re.compile(r"[一-鿿A-Za-z0-9_]+") ENGLISH_WORD_RE = re.compile(r"[A-Za-z0-9_]+") SECOND_PERSON_RE = re.compile(r"你们?|you", re.IGNORECASE) +FIRST_PERSON_RE = re.compile(r"我们?|I|we", re.IGNORECASE) QUESTION_RE = re.compile(r"[??]") EXCLAMATION_RE = re.compile(r"[!!]") ID_RE = re.compile(r"^[a-z0-9][a-z0-9-]{1,63}$") -SOURCE_BLOCK_RE = re.compile(r"", re.DOTALL) +SOURCE_BLOCK_RE = re.compile(r"", re.DOTALL) REPORT_BLOCK_RE = re.compile(r"", re.DOTALL) - +# 图文正文内联话题标签:#话题(样本文件约定正文为纯文本,行首 # 只出现在标题行) +TAG_RE = re.compile(r"(?m)(?:^|(?<=\s))#[^\s#]\S*") +EMOJI_RE = re.compile( + "[" + "\U0001F1E6-\U0001F1FF" + "\U0001F300-\U0001F5FF" + "\U0001F600-\U0001F64F" + "\U0001F680-\U0001F6FF" + "\U0001F700-\U0001F77F" + "\U0001F780-\U0001F7FF" + "\U0001F800-\U0001F8FF" + "\U0001F900-\U0001F9FF" + "\U0001FA00-\U0001FA6F" + "\U0001FA70-\U0001FAFF" + "☀-⛿" + "✀-➿" + "⬀-⯿" + "️" + "]" +) + +# ── CONFIG-BEGIN ── PLATFORM = "wx_channel" -STATISTICS_METRICS = [ - "avg_sentence_tokens", +PLATFORM_LABEL = "微信视频号" + +PLATFORM_DESC = "微信视频号视频作品" + +KINDS = ("video",) + +DEFAULT_KIND = "video" + +KIND_LABELS = {"video": "视频作品", "note": "图文作品"} + +KIND_SUFFIX_HINT = "视频号只有视频作品,无需分类型建 DNA" + +FRAMEWORK_DOCS = {"video": "video-dna-framework.md", "note": "note-dna-framework.md"} + +# DNA 维度 v2(按作品类型分框架)。调整维度必须升版本,并同步 references/ 下的框架文档与工具 SKILL.md 的 Focus ID 表。 +DIMENSION_GROUPS = { + "video": { + "选题与包装": [("topic-angle", "选题与观看理由"), ("title-cover", "短标题、视频描述与封面")], + "内容创意": [("content-idea", "内容创意")], + "形态与规格": [("video-form", "视频内容形态与制作指向"), ("production-spec", "制作规格与视听倾向")], + "口播文案子模块(可选,仅口播类启用)": [("narration-script", "口播文案子DNA")], + "账号运营子模块(对标账号样本才有)": [("account-bio", "账号简介写法"), ("content-mix-cadence", "内容形式比例与发布习惯")], + }, +} + +STATISTICS_METRICS = { + "video": [ + "avg_sentence_tokens", + "question_density_per_100_sentences", + "second_person_density_per_100_sentences", + "exclamation_density_per_1000_characters", + "speech_chars_per_minute", + ], +} + +REPORT_DIMENSION_PROMPTS = { + "topic-angle": "- 单篇观测:选题类型、选题入口(现象 / 问题 / 冲突 / 数据 / 热点 / 挑战 / 个人经历)、目标人群为什么要看完(理解 / 判断 / 行动 / 避坑 / 身份认同 / 情绪共鸣)。\n- 边界:只记本篇,不判断跨篇稳定性;不评价选题好坏。", + "title-cover": "- 单篇观测:**短标题**(发布页可填,官方称填了能获得更多流量;作品管理页不展示,取数时拿不到)与**视频描述**(含话题标签)两者的原文与写法模式。\n- 取数边界:抓取到的作品只有视频描述;短标题拿不到时写「未观测(管理页不展示)」,不得把描述当短标题。\n- 视觉证据:封面或首帧必须由视觉模型读取图片,至少提取画面主体与场景、构图与画幅、色彩体系、光线与质感、风格与媒介、文字视觉、品牌识别元素、避免项,并反推为可执行的 AIGC 提示词要素;无图片写「未提供」。", + "content-idea": "- 单篇观测:一句话创意内核、创意类型、展开逻辑(悬念 / 反转 / 递进 / 对比 / 清单 / 实测)、记忆点。\n- 可复用信号:这个创意套路换成别的主题还能怎么用。\n- 边界:只记创意层,不记创作细节(逐句台词、镜头表、脚本结构、转场与编码参数)。", + "video-form": "- 单篇观测:视频内容形态(口播 / 实拍拼接 / 影视解说+反转植入 / 纯 AIGC 动画 / 创意转场动效 / 录屏演示 / 图文卡片视频 / 混合)与判定依据(画面证据、口播占比、素材来源)。\n- 制作指向:必须落到真实存在的资源名——Content Producer `expert-video` 的某个 workflow(Reversal Ad / Narration Video / Collage B-roll / 通用阶段链),或 main 的素材加工技能(`video-edit` / `talking-head-cut` / `ui-demo`);不得发明不存在的名字。", + "production-spec": "- 单篇观测:横屏或竖屏、时长带、画面风格(色调、质感、字幕样式倾向、信息密度)、配音音色与声音形态(原声口播 / TTS / 旁白 / 纯画面字幕)、BGM 与音效倾向、封面规格。\n- 边界:只记规格与倾向,不规定镜头参数、逐镜设计、转场与编码细节——那些归 Content Producer。", + "narration-script": "- 子模块(仅口播类作品启用):起(从什么起步)、承(靠什么推进)、转(转折触发)、合(收束与 CTA),以及人称与语气、句长与语速、签名式表达。\n- 边界:非口播类或证据不足时写「未启用 / 未观测」;不得把单篇句式直接上升为规则。", + "account-bio": "- 子模块(仅对标账号样本可得):账号昵称、简介写法、主页与置顶表达、对外承诺。\n- 边界:用户提供的单篇样本无法观测时写「未观测」,不得推导。", + "content-mix-cadence": "- 子模块(仅对标账号批量样本可得):图文 / 视频等内容形式比例、发布时间段与频率、内容形式混合节奏(如三篇图文对一篇视频)、栏目化节奏。\n- 边界:必须由账号发布列表的批量样本推导;单篇样本只记本篇发布时间。", +} + +REPORT_OBSERVATION_PROMPTS = { + "video": "- 作品类型:视频(本框架只用于视频作品;图文样本走 note-dna-framework)。\n- 样本来源:待 Agent 补齐(对标账号批量作品 / 用户提供的单篇或多篇 / 用户想法转译)。\n- 账号与简介:待 Agent 补齐;非账号样本写未观测。\n- 发布时间与时间段:待 Agent 补齐;单篇只记本篇时间,不推导账号节奏。\n- 数据线索:待 Agent 补齐(播放 / 点赞 / 评论 / 分享 / 收藏);只作证据,不直接判风格好坏。\n- 横竖屏与时长:待 Agent 补齐。\n- 素材来源与授权:待 Agent 补齐(实拍 / 影视或开源片源 / AIGC / 录屏 / 混合)。\n- 转录与关键帧来源:待 Agent 补齐(如 main 的 viral-chaser 产物路径);缺失写未提供,不得编造。", +} + +TEMPLATE_STAGES = { + "video": ("选题", "短标题与视频描述", "内容创意", "视频形态与制作指向", "制作规格", "口播文案"), +} + +TEMPLATE_STAGE_FIELDS = { + "选题": ("选题角度推荐", "选题需考虑的受众关联角度", "内容支柱与系列关系", "避免"), + "短标题与视频描述": ("短标题模式", "参考短标题", "视频描述结构", "参考视频描述", "话题标签策略", "封面风格与 AIGC 要素"), + "内容创意": ("创意原型", "展开逻辑", "记忆点与反转设计", "触发条件", "避免"), + "视频形态与制作指向": ("视频内容形态", "制作指向", "委托边界", "未指定形态时"), + "制作规格": ("横屏或竖屏", "时长带", "画面风格", "配音音色与声音形态", "BGM 与音效", "字幕"), + "口播文案": ("是否启用", "起", "承", "转", "合", "人称与语气", "句长与语速", "签名式表达", "必须做", "避免"), +} + +TEMPLATE_INTROS = { + "video": "本模板是 main agent 出具**视频制作 Brief(brief.md)**与(口播类)**口播文案**的输入模板,必须由 DNA 文档推导,不得引入 DNA 文档未确认的规则。\n\n- 下列各段直接对应 Brief 的正文字段;Brief 的其余字段(素材清单与授权、验收标准、闸门批准人)由平台 Content Production Workflow 规定。\n- Brief **不含 DNA 信息**:Content Producer 看不到 main 的 DNA,只按 Brief 制作。\n- 模板不规定创作细节:逐句台词、镜头表、转场与编码参数归 Content Producer。\n- 账号运营子模块(简介写法、内容形式比例、发布习惯)写在 DNA 文档,不进本模板。", +} + +TEMPLATE_CHECKLISTS = { + "video": "- 是否只用一个 DNA,且作品类型与该 DNA 的 `kind` 一致。\n- 短标题与视频描述是否都已拟定(视频号两者都必须填),且写法来自 DNA 文档。\n- 内容创意是否落到可复用的创意原型,而不是照抄样本主题。\n- 视频形态是否明确指向 Content Producer `expert-video` 的某个 workflow,或 main 的某个素材加工技能。\n- Brief 是否只含制作所需信息(不含 DNA 内容),素材是否给了绝对路径与授权说明。\n- 口播类是否附口播文案(或真人口播录音路径);口播子模块未启用时是否避免规定逐句口播。\n- 制作规格(横竖屏、时长带、画面风格、配音音色)是否尊重样本覆盖度;样本不足时是否标注未观测。\n- 用户输入是否已转译为具体执行规则。", +} + +DNA_SUBMODULE_NOTE = "- **口播文案子模块**(`narration-script`):仅口播类视频启用,用于指导 main agent 写同类型视频的口播文案;它不是独立 DNA,未启用时写「未启用」。\n- **账号运营子模块**(`account-bio`、`content-mix-cadence`):只在样本来自用户提供的对标账号(可从账号发布列表批量提取)时填写;结论只写进本 DNA 文档,不进 template;样本不足写「未观测」。" +# ── CONFIG-END ── + +STATISTICS_METRICS_ALL = [ + "title_chars", + "line_count", + "paragraphs", "avg_paragraph_tokens", - "avg_sentences_per_paragraph", + "avg_sentence_tokens", "question_density_per_100_sentences", "second_person_density_per_100_sentences", + "first_person_density_per_100_sentences", "exclamation_density_per_1000_characters", + "speech_chars_per_minute", + "emoji_density_per_100_characters", + "tag_count", ] +METRIC_LABELS = { + "title_chars": "标题字数", + "line_count": "正文行数", + "paragraphs": "段落数", + "avg_paragraph_tokens": "平均每段长度(token)", + "avg_sentence_tokens": "平均句长(token)", + "question_density_per_100_sentences": "问句密度 / 百句", + "second_person_density_per_100_sentences": "第二人称密度 / 百句", + "first_person_density_per_100_sentences": "第一人称密度 / 百句", + "exclamation_density_per_1000_characters": "感叹号密度 / 千字", + "speech_chars_per_minute": "口播密度(字/分钟)", + "emoji_density_per_100_characters": "emoji 密度 / 百字", + "tag_count": "话题标签数", +} + STOP_TERMS = { "一个", "我们", "你们", "这里", "不会", "这个", "那个", "什么", "可以", "因为", "但是", "所以", "还是", "以及", "如果", "他们", "自己", "的时候", "to", "the", "a", "an", "is", "are", "and", "or", "of", "in", "for", "on", "with", "you", "we", } -# DNA 维度 v1。调整维度需升版本,并同步 references/account-dna-framework.md 与 SKILL.md 的 Focus ID 表。 -DIMENSION_GROUPS = { - "定位与选题": [ - ("positioning-core", "定位与核心传达"), - ("topic-portfolio", "选题组合"), - ("description-packaging", "简介与包装"), - ("bio-profile", "账号简介与主页表达"), - ], - "账号节奏": [ - ("content-form-mix", "内容形式与比例"), - ("publish-cadence", "发布习惯"), - ("high-performer-patterns", "高数据创意模式"), - ], - "表达与风格": [ - ("visual-language", "视觉语言"), - ("audio-language", "声音语言"), - ("narration-dna", "口播文案 DNA"), - ], - "互动与制作": [ - ("engagement-conversion", "互动与转化"), - ("social-share-loop", "社交分享闭环"), - ("series-signature", "系列与签名"), - ("production-pipeline", "制作管线倾向"), - ], -} -DIMENSIONS = [] -number = 1 -for group, dimensions in DIMENSION_GROUPS.items(): - for dimension_id, name in dimensions: - DIMENSIONS.append( - {"id": dimension_id, "number": number, "name": name, "group": group} - ) - number += 1 + +def dimensions_for(kind: str) -> list[dict]: + """把某个作品类型的分组配置摊平成带序号的维度列表。""" + dimensions = [] + number = 1 + for group, items in DIMENSION_GROUPS[kind].items(): + for dimension_id, name in items: + dimensions.append({"id": dimension_id, "number": number, "name": name, "group": group}) + number += 1 + return dimensions + + +def statistics_metrics(kind: str) -> list[str]: + wanted = STATISTICS_METRICS[kind] + return [metric for metric in STATISTICS_METRICS_ALL if metric in wanted] + def split_sentences(text: str) -> list[str]: return [item.strip() for item in SENTENCE_SPLIT.split(text) if item.strip()] + def split_paragraphs(text: str) -> list[str]: return [item.strip() for item in PARAGRAPH_SPLIT.split(text) if item.strip()] + def tokenize(text: str) -> list[str]: return TOKEN_RE.findall(text) + def rounded(value: float) -> float: return round(value, 4) -def safe_ratio(numerator: int, denominator: int, multiplier: float = 1) -> float: + +def safe_ratio(numerator: int, denominator: float, multiplier: float = 1) -> float: return rounded((numerator / denominator) * multiplier) if denominator else 0.0 + def average(values: list[float]) -> float: return rounded(sum(values) / len(values)) if values else 0.0 @@ -105,44 +220,59 @@ def extract_terms(text: str) -> Counter: return terms -def title_candidates(path: Path, text: str) -> list[str]: - candidates = [path.stem] +def split_title_body(text: str) -> tuple[str, str]: + """样本文件约定:首个一级标题行为作品标题,其余为正文(图文正文可含内联 #话题)。""" + title = "" + body_lines = [] for line in text.splitlines(): stripped = line.strip() - if stripped.startswith("# ") and len(stripped) > 2: - candidates.append(stripped[2:].strip()) - break + if not title and stripped.startswith("# ") and len(stripped) > 2: + title = stripped[2:].strip() + continue + body_lines.append(line) + return title, "\n".join(body_lines).strip() + + +def title_candidates(path: Path, text: str) -> list[str]: + candidates = [path.stem] + title, _ = split_title_body(text) + if title: + candidates.append(title) return candidates -def document_metrics(path: Path, text: str) -> dict: +def document_metrics(path: Path, text: str, duration: float = 0.0) -> dict: + """统计证据底座:视频与图文共用一套超集,展示哪些指标由 STATISTICS_METRICS[kind] 决定。""" + title, body = split_title_body(text) sentences = split_sentences(text) - paragraphs = split_paragraphs(text) sentence_lengths = [len(tokenize(sentence)) for sentence in sentences] - paragraph_lengths = [len(tokenize(paragraph)) for paragraph in paragraphs] - paragraph_sentence_counts = [max(len(split_sentences(paragraph)), 1) for paragraph in paragraphs] sentence_count = len(sentences) - paragraph_count = len(paragraphs) character_count = len(text) + body_lines = [line for line in body.splitlines() if line.strip()] + paragraphs = split_paragraphs(body) + paragraph_lengths = [len(tokenize(paragraph)) for paragraph in paragraphs] + emoji_count = len(EMOJI_RE.findall(body)) + speech_chars_per_minute = rounded(character_count / duration * 60) if duration > 0 else 0.0 return { - "source_article": str(path.resolve()), + "source_transcript": str(path.resolve()), "title_candidates": title_candidates(path, text), "characters": character_count, "sentences": sentence_count, - "paragraphs": paragraph_count, - "avg_sentence_tokens": average([float(value) for value in sentence_lengths]), + "duration": duration, + "title_chars": len(title), + "line_count": len(body_lines), + "paragraphs": len(paragraphs), "avg_paragraph_tokens": average([float(value) for value in paragraph_lengths]), - "avg_sentences_per_paragraph": average([float(value) for value in paragraph_sentence_counts]), - "question_density_per_100_sentences": safe_ratio( - len(QUESTION_RE.findall(text)), sentence_count, 100 - ), - "second_person_density_per_100_sentences": safe_ratio( - len(SECOND_PERSON_RE.findall(text)), sentence_count, 100 - ), - "exclamation_density_per_1000_characters": safe_ratio( - len(EXCLAMATION_RE.findall(text)), character_count, 1000 - ), + "avg_sentence_tokens": average([float(value) for value in sentence_lengths]), + "question_density_per_100_sentences": safe_ratio(len(QUESTION_RE.findall(text)), sentence_count, 100), + "second_person_density_per_100_sentences": safe_ratio(len(SECOND_PERSON_RE.findall(text)), sentence_count, 100), + "first_person_density_per_100_sentences": safe_ratio(len(FIRST_PERSON_RE.findall(text)), sentence_count, 100), + "exclamation_density_per_1000_characters": safe_ratio(len(EXCLAMATION_RE.findall(text)), character_count, 1000), + "speech_chars_per_minute": speech_chars_per_minute, + "emoji_count": emoji_count, + "emoji_density_per_100_characters": safe_ratio(emoji_count, character_count, 100), + "tag_count": len(TAG_RE.findall(body)), "terms": dict(extract_terms(text)), } @@ -193,15 +323,7 @@ def generated_at() -> str: def metric_label(metric_name: str) -> str: - labels = { - "avg_sentence_tokens": "平均句长(token)", - "avg_paragraph_tokens": "平均每段/每镜长度(token)", - "avg_sentences_per_paragraph": "平均每段/每镜句数", - "question_density_per_100_sentences": "问句密度 / 百句", - "second_person_density_per_100_sentences": "第二人称密度 / 百句", - "exclamation_density_per_1000_characters": "感叹号密度 / 千字", - } - return labels.get(metric_name, metric_name) + return METRIC_LABELS.get(metric_name, metric_name) def weighted_median(pairs: list[tuple[float, float]]) -> float: @@ -224,10 +346,10 @@ def weighted_mad(pairs: list[tuple[float, float]], center: float) -> float: return weighted_median(deviations) -def build_statistics(reports: list[dict]) -> dict: +def build_statistics(reports: list[dict], kind: str) -> dict: total_weight = rounded(sum(report["weight"] for report in reports)) numeric_metrics = {} - for metric_name in STATISTICS_METRICS: + for metric_name in statistics_metrics(kind): pairs = [ (float(report["document"][metric_name]), float(report["weight"])) for report in reports @@ -283,7 +405,7 @@ def build_statistics(reports: list[dict]) -> dict: } -def statistics_markdown(statistics: dict) -> str: +def statistics_markdown(statistics: dict, kind: str) -> str: lines = [ "| 指标 | 加权中位数 | 加权 MAD | 最小值 | 最大值 |", "|---|---:|---:|---:|---:|", @@ -295,9 +417,16 @@ def statistics_markdown(statistics: dict) -> str: lines.extend( [ "", - f"样本覆盖度:`{statistics['report_count']}` 条 report;单条或少量样本不能推导账号比例、发布节奏或高数据共性。", + f"样本覆盖度:`{statistics['report_count']}` 条 {KIND_LABELS[kind]} report。", f"权重模式:`{statistics['weighting']}`;总权重:`{statistics['total_weight']}`。", - "统计只用于辅助聚合,不生成评分;定性判断必须回到各篇 DNA report。", + ] + ) + if kind == "video": + lines.append("口播密度(字/分钟)仅在 report 提供 `duration` 时有意义;未提供时该行只是 0 值占位。") + lines.extend( + [ + "「账号运营子模块」维度(简介写法、内容形式比例、发布习惯)不能由单篇样本推导:样本非对标账号批量时写未观测。", + "统计只用于辅助聚合,不生成评分;定性判断必须回到各篇 DNA report 与原文。", ] ) return "\n".join(lines) @@ -330,6 +459,14 @@ def parse_json_list(value: str) -> list: return [] +def parse_duration(value: str) -> float: + try: + duration = float(value) + except ValueError: + return 0.0 + return duration if math.isfinite(duration) and duration > 0 else 0.0 + + def hidden_path(block_match, path_key: str = "paths") -> list[Path]: if not block_match: return [] @@ -343,18 +480,21 @@ def load_reports(paths: list[Path]) -> list[dict]: metadata = parse_frontmatter(markdown) source_match = SOURCE_BLOCK_RE.search(markdown) source_paths = hidden_path(source_match, "path") - source_path = source_paths[0] if source_paths else Path(parse_quoted(metadata.get("source-article", ""))) + source_path = source_paths[0] if source_paths else Path(parse_quoted(metadata.get("source-transcript", ""))) if not source_path.is_file(): - raise SystemExit(f"DNA report source article does not exist: {source_path}") + raise SystemExit(f"DNA report source transcript does not exist: {source_path}") + duration = parse_duration(metadata.get("duration", "0")) document = document_metrics( source_path, source_path.read_text(encoding="utf-8", errors="ignore"), + duration, ) reports.append( { "report_path": str(path.resolve()), "dna_id": parse_quoted(metadata.get("dna-id", "")), "report_id": parse_quoted(metadata.get("report-id", path.name.removesuffix(".report.md"))), + "kind": parse_quoted(metadata.get("kind", DEFAULT_KIND)), "title": parse_quoted(metadata.get("title", document["title_candidates"][-1])), "weight": float(metadata.get("weight", "1")), "focus": parse_json_list(metadata.get("focus", "[]")), @@ -364,89 +504,116 @@ def load_reports(paths: list[Path]) -> list[dict]: return reports -REPORT_DIMENSION_PROMPTS = { - 'positioning-core': '- 单条观测:本条暴露出的定位、目标人群与核心传达。\n- 账号级判断:单条样本只给候选,不直接判定账号稳定性。', - 'topic-portfolio': '- 单条观测:本条选题类型、入口和系列关系。', - 'description-packaging': '- 单条观测:视频简介 / 封面 / 话题标签包装模式;只记模式,不抄成固定字句。', - 'bio-profile': '- 账号级观测:账号简介、主页与置顶表达;单条样本无法观测时写未观测。', - 'content-form-mix': '- 单条观测:本条内容形式。\n- 聚合边界:图文/视频比例与混合节奏必须由多样本或账号级数据推导。', - 'publish-cadence': '- 单条观测:本条发布时间。\n- 聚合边界:时间段、频率和三图文对一视频等节奏必须由多样本或账号级数据推导。', - 'high-performer-patterns': '- 数据线索:记录本条互动/播放/阅读线索。\n- 创意判断:回读选题、包装、形式与创意,不得把高数据直接等同于风格好。', - 'visual-language': '- 视觉证据:有图片或关键帧时由视觉模型读取;无证据写未观测。\n- 边界:只记录账号级稳定视觉语言,不规定逐镜设计。', - 'audio-language': '- 声音证据:来自口播稿、原视频信息或用户说明;无证据写未观测。\n- 边界:只记录音色/语速/声音气质倾向,不规定 TTS 参数。', - 'narration-dna': '- 独立块:记录开头、起承转合、收束、人称与签名表达。\n- 聚合边界:样本不足时保持未启用,不把单条句式上升为账号 DNA。', - 'engagement-conversion': '- 单条观测:平台内行动引导与承接路径。', - 'social-share-loop': '- 单条观测:转发动机、分享话术和被转发后的承接。', - 'series-signature': '- 单条观测:栏目、固定表达或识别符号;高频词必须回读原文确认。', - 'production-pipeline': '- 管线映射:只写 Content Producer 已支持的 Pipeline;未确定时写待定。\n- 分界:DNA 指导 main agent 出 Brief,不规定成片制作细节。', -} +def enforce_single_kind(reports: list[dict], kind: str | None) -> str: + """一个 DNA 只承载一种作品类型:返回该 DNA 的 kind,混型直接报错。""" + kinds = {report["kind"] for report in reports} + if len(kinds) > 1: + raise SystemExit( + "Reports mix work kinds: " + + ", ".join(sorted(kinds)) + + f"。一个 dna-id 只能承载一种作品类型:{KIND_SUFFIX_HINT}。" + ) + resolved = kinds.pop() if kinds else (kind or DEFAULT_KIND) + if kind and resolved != kind: + raise SystemExit( + f"--kind {kind} 与 report 内的 kind {resolved} 不一致;一个 dna-id 只能承载一种作品类型。" + ) + if resolved not in KINDS: + raise SystemExit(f"Unknown kind: {resolved}({PLATFORM_LABEL}支持:{', '.join(KINDS)})") + return resolved + def report_dimension_markdown(dimension: dict) -> str: heading = f"### {dimension['number']}. {dimension['name']}" - prompt = REPORT_DIMENSION_PROMPTS.get(dimension["id"], "- 单条观测:待 Agent 补齐。") + prompt = REPORT_DIMENSION_PROMPTS.get(dimension["id"], "- 单篇观测:待 Agent 补齐。") return ( f"{heading}\n\n" f"{prompt}\n\n" - "**单条结论:**待 Agent 补齐。\n\n" - "**原文证据:**待 Agent 补齐(逐字引用、账号信息、发布信息、画面/声音描述或数据线索;注明来源)。\n\n" - "**可复用信号:**待 Agent 补齐;样本不足时写未观测,不推导账号级稳定性。" + "**单篇结论:**待 Agent 补齐。\n\n" + "**原文证据:**待 Agent 补齐(逐字引用、账号/发布信息、画面或声音描述、数据线索;注明来源)。\n\n" + "**可复用信号:**待 Agent 补齐;证据不足时写未观测,不推导跨篇稳定性。" ) def report_markdown( dna_id: str, report_id: str, + kind: str, weight: float, focus: list[str], document: dict, cover_image: str, - source_video: str, + source_url: str, ) -> str: - dimensions = [] - for dimension in DIMENSIONS: - dimensions.append(report_dimension_markdown(dimension)) + dimensions = dimensions_for(kind) + dimension_blocks = [report_dimension_markdown(dimension) for dimension in dimensions] + duration_line = f"duration: {document['duration']}" + statistics_lines = [ + f"- 转录/正文字符:{document['characters']}", + f"- 句子数:{document['sentences']}", + ] + if kind == "video": + statistics_lines.append(f"- 视频时长:{document['duration'] or '未提供'}") + else: + statistics_lines.extend( + [ + f"- 标题字数:{document['title_chars']}", + f"- 正文行数:{document['line_count']}", + f"- 话题标签数:{document['tag_count']}", + ] + ) + statistics_lines.extend( + [ + f"- 标题候选:{' / '.join(document['title_candidates'])}", + f"- 来源链接:{source_url or '未提供'}", + f"- 封面 / 首帧图:{cover_image or '未提供'}", + ] + ) return "\n\n".join( [ "---\n" f"dna-id: {yaml_value(dna_id)}\n" f"report-id: {yaml_value(report_id)}\n" "type: dna-report\n" + f"kind: {yaml_value(kind)}\n" f"title: {yaml_value(document['title_candidates'][-1])}\n" - f"source-article: {yaml_value(document['source_article'])}\n" - f"source-video: {yaml_value(source_video)}\n" + f"source-transcript: {yaml_value(document['source_transcript'])}\n" + f"source-url: {yaml_value(source_url)}\n" f"cover-image: {yaml_value(cover_image)}\n" + f"{duration_line}\n" f"weight: {weight}\n" f"focus: {yaml_value(focus)}\n" "sample_count: 1\n" f"generated_at: {yaml_value(generated_at())}\n" "---", - f"# {document['title_candidates'][-1]} 单条视频 DNA Report", - "本文件只描述这一条视频。它不是聚合后的 DNA 文档,也不直接作为创作模板。", + f"# {document['title_candidates'][-1]} 单篇 DNA Report({KIND_LABELS[kind]})", + "本文件只描述这一篇作品。它不是聚合后的 DNA 文档,也不直接作为生产模板。", "## 单篇统计", - f"- 字符:{document['characters']}\n- 句子:{document['sentences']}\n- 段落/镜次:{document['paragraphs']}\n- 简介摘要候选:{' / '.join(document['title_candidates'])}\n- 封面图:{cover_image or '未提供'}\n- 源视频:{source_video or '未提供'}", - "## 视频信息(待 Agent 结合原视频 / 用户提供信息补齐)", - "- 时长:待 Agent 补齐。\n- 视频形态:待 Agent 补齐(竖屏/横屏,真人出镜 / 配音解说 / 素材剪辑 / AIGC)。\n- 真人出镜占比:待 Agent 补齐。\n- 镜头与字幕要点:待 Agent 补齐。\n- BGM 与音效:待 Agent 补齐。\n- 数据线索(可选,播放 / 互动等):待 Agent 补齐,不得编造。", - "## 样本与账号观测", - '- 样本类型:待 Agent 补齐(账号作品 / 用户提供单条)。\n- 账号与简介:待 Agent 补齐;单条样本无法观测时写未观测。\n- 发布时间与时间段:待 Agent 补齐;单条样本只记录本条时间,不推导账号节奏。\n- 内容形式:待 Agent 补齐(口播 / 实拍拼接 / 创意转场 / 纯 AIGC 动画 / 图文 / 混合)。\n- 数据表现线索:待 Agent 补齐;只作证据,不直接判风格好坏。\n- 视频形态与授权信息:待 Agent 补齐(横竖屏、时长、素材来源、授权边界)。', - f"## {len(DIMENSIONS)} 维单篇分析", - "\n\n".join(dimensions), + "\n".join(statistics_lines), + "## 样本观测", + REPORT_OBSERVATION_PROMPTS[kind], + f"## {len(dimensions)} 维单篇分析", + "\n\n".join(dimension_blocks), "## 单篇边界", - "- 这里记录本条视频的可复用信号,不判断跨篇稳定性。\n- 聚合时由 Agent 根据 DNA report、权重和 focus 判断共性、偏好和例外。", - f"", - *( [f""] if cover_image else [] ), + "- 这里记录本篇作品的可复用信号,不判断跨篇稳定性。\n" + "- 聚合时由 Agent 根据全部 DNA report、权重和 focus 判断共性、偏好和例外。\n" + f"- 维度定义与边界见本工具 `references/{FRAMEWORK_DOCS[kind]}`。", + f"", + *([f""] if cover_image else []), ] ) + "\n" -def user_input_markdown(user_inputs: list[str], existing_body: str | None = None) -> str: +def user_input_markdown(user_inputs: list[str], kind: str, existing_body: str | None = None) -> str: if not user_inputs: return existing_body or "暂无待转译输入。" + dimension_count = len(dimensions_for(kind)) entries = [] for index, user_input in enumerate(user_inputs, start=1): entries.append( f"### 输入 {index}\n" f"- raw_input: {yaml_value(user_input)}\n" - f"- affected_dimensions: 待 Agent 映射到 {len(DIMENSIONS)} 个维度 ID\n" + f"- affected_dimensions: 待 Agent 映射到 {dimension_count} 个维度 ID\n" "- dna_document_change: 待 Agent 转译为聚合结论 / 报告依据 / 创作规则\n" "- template_change: 待 Agent 转译为具体执行规则\n" "- status: pending" @@ -458,20 +625,22 @@ def user_input_markdown(user_inputs: list[str], existing_body: str | None = None def dna_document_markdown( dna_id: str, + kind: str, reports: list[dict], statistics: dict, user_inputs: list[str] | None = None, previous_dna: str | None = None, ) -> str: + dimensions = dimensions_for(kind) old_sections = extract_markdown_sections(previous_dna, "### ") - dimensions = [] - for dimension in DIMENSIONS: + dimension_blocks = [] + for dimension in dimensions: heading = f"### {dimension['number']}. {dimension['name']}" body = old_sections.get(heading) if body: - dimensions.append(f"{heading}\n\n{body}") + dimension_blocks.append(f"{heading}\n\n{body}") else: - dimensions.append( + dimension_blocks.append( f"{heading}\n\n**聚合结论:**待 Agent 补齐。\n\n" "**报告依据:**待 Agent 列出使用的 DNA report、权重和 focus。\n\n" "**创作规则:**待 Agent 补齐。" @@ -485,25 +654,33 @@ def dna_document_markdown( "---\n" f"dna-id: {yaml_value(dna_id)}\n" "type: dna-document\n" + f"kind: {yaml_value(kind)}\n" + f"platform: {yaml_value(PLATFORM)}\n" f"report_count: {statistics['report_count']}\n" f"total_weight: {statistics['total_weight']}\n" f"weighting: {statistics['weighting']}\n" f"generated_at: {yaml_value(generated_at())}\n" "---", - f"# {dna_id} DNA 文档", - "本文件聚合历史 DNA report。它是账号/作者当前采用的视频号内容风格与选题规则,也必须能推导出生产模板。", + f"# {dna_id} DNA 文档({PLATFORM_LABEL} · {KIND_LABELS[kind]})", + "本文件聚合该 DNA 目录下的全部 DNA report,形成当前采用的内容生产规则,并且必须能推导出 DNA template。" + "样本可以来自多个账号,也可以来自用户指定的一个账号的批量作品。", "## 报告与权重", "\n".join( f"- `{report['report_path']}`:weight `{report['weight']}`,focus `{', '.join(report['focus']) or 'all'}`" for report in reports ), - statistics_markdown(statistics), - f"## {len(DIMENSIONS)} 维聚合", - "\n\n".join(dimensions), + statistics_markdown(statistics, kind), + f"## {len(dimensions)} 维聚合", + "\n\n".join(dimension_blocks), + "## 子模块说明", + DNA_SUBMODULE_NOTE, "## 用户输入转译区", - user_input_markdown(user_inputs or [], existing_user_inputs), + user_input_markdown(user_inputs or [], kind, existing_user_inputs), "## 推导规则", - "- 聚合结论必须能追溯到 DNA report。\n- 用户输入必须先映射到具体维度,再修改聚合结论和创作规则;不得把原话直接当成 DNA 规则。\n- 模板必须由本文件推导,不能引入本文件未确认的规则。", + "- 聚合结论必须能追溯到 DNA report;区分高覆盖共性、高权重偏好、局部借鉴、孤例与例外。\n" + "- 用户输入必须先映射到具体维度,再修改聚合结论和创作规则;不得把原话直接当成 DNA 规则。\n" + "- template 必须由本文件推导,不能引入本文件未确认的规则。\n" + "- 账号运营子模块(简介写法、内容形式比例、发布习惯)只写进本文件,不进 template。", f"", ] ) + "\n" @@ -553,58 +730,6 @@ def extract_named_section(markdown: str, heading: str) -> str: return "\n".join(body).strip() -TEMPLATE_STAGES = ("定位与核心传达", "选题与简介包装", "内容形式与发布节奏", "高数据创意模式", "互动与系列", "制作交接与Pipeline", "口播文案DNA") - -TEMPLATE_STAGE_FIELDS = { - "定位与核心传达": ( - "一句话定位", - "目标人群", - "核心传达", - "账号简介与主页表达", - "不变承诺", - ), - "选题与简介包装": ( - "选题组合", - "视频简介模式", - "封面包装", - "禁用方向", - ), - "内容形式与发布节奏": ( - "图文或视频比例", - "发布时间带", - "内容形式混合节奏", - "系列栏目", - ), - "高数据创意模式": ( - "高表现样本共性", - "可复用创意原型", - "触发条件", - "例外", - ), - "互动与系列": ( - "互动目标", - "引导方式", - "社交分享动机", - "系列与签名标记", - "必须做", - "避免", - ), - "制作交接与Pipeline": ( - "MainAgent交付物", - "ContentProducer交付物", - "Pipeline", - "素材与授权", - "风格边界", - ), - "口播文案DNA": ( - "是否启用", - "起承转合结构", - "语言与人称", - "声音倾向", - "必须做", - "避免", - ), -} def parse_template_fields(body: str) -> dict[str, str]: fields = {} for line in body.splitlines(): @@ -614,8 +739,8 @@ def parse_template_fields(body: str) -> dict[str, str]: return fields -def template_stage_from_heading(heading: str) -> str | None: - for stage in TEMPLATE_STAGES: +def template_stage_from_heading(heading: str, kind: str) -> str | None: + for stage in TEMPLATE_STAGES[kind]: if heading.startswith(f"[{stage}部分]"): return stage return None @@ -630,10 +755,10 @@ def template_segment(stage: str, values: dict[str, str] | None = None) -> str: return "\n".join(lines) -def stage_values_from_template(old_sections: dict[str, str]) -> dict[str, dict[str, str]]: - values = {stage: {} for stage in TEMPLATE_STAGES} - for heading in sorted(old_sections, key=template_order): - stage = template_stage_from_heading(heading) +def stage_values_from_template(old_sections: dict[str, str], kind: str) -> dict[str, dict[str, str]]: + values = {stage: {} for stage in TEMPLATE_STAGES[kind]} + for heading in sorted(old_sections, key=lambda item: template_order(item, kind)): + stage = template_stage_from_heading(heading, kind) if not stage: continue for field, value in parse_template_fields(old_sections[heading]).items(): @@ -643,32 +768,23 @@ def stage_values_from_template(old_sections: dict[str, str]) -> dict[str, dict[s def template_markdown( dna_id: str, + kind: str, source_dna: str, previous_template: str | None = None, ) -> str: - old_sections = extract_template_sections(previous_template) - stage_values = stage_values_from_template(old_sections) - segments = [template_segment(stage, stage_values[stage]) for stage in TEMPLATE_STAGES] + stages = TEMPLATE_STAGES[kind] + old_sections = extract_template_sections(previous_template, kind) + stage_values = stage_values_from_template(old_sections, kind) + segments = [template_segment(stage, stage_values[stage]) for stage in stages] section_defaults = [ - ( - "## 生产模板", - "\n\n".join(segments), - ), + ("## 生产模板", "\n\n".join(segments)), ( "## 用户输入转译后的执行规则", "- (来自用户输入:待 Agent 补齐来源。)\n" - f"- (影响维度:待 Agent 映射到 {len(DIMENSIONS)} 维 ID。)\n" - "- (执行规则:待 Agent 写成 Brief 或图文生产时可直接执行的要求。)", - ), - ( - "## 使用检查", - "- 是否只用一个 DNA,且与本次账号 / 内容任务匹配。\n" - "- 定位、选题、视频简介包装、核心传达是否来自 DNA 文档。\n" - "- 图文/视频比例、发布节奏和高数据创意是否尊重样本覆盖度;样本不足时是否标注未观测。\n" - "- 视频全案是否只输出 Brief,且 Brief 明确 Pipeline、素材授权、验收标准和交付边界。\n" - "- 口播文案 DNA 是否独立启用;未启用时是否避免规定逐句口播。\n" - "- 用户输入是否已转译为具体执行规则。", + f"- (影响维度:待 Agent 映射到 {len(dimensions_for(kind))} 维 ID。)\n" + "- (执行规则:待 Agent 写成生产时可直接执行的要求。)", ), + ("## 使用检查", TEMPLATE_CHECKLISTS[kind]), ] rendered_sections = [] for heading, default_body in section_defaults: @@ -686,17 +802,18 @@ def template_markdown( "---\n" f"dna-id: {yaml_value(dna_id)}\n" "type: dna-template\n" + f"kind: {yaml_value(kind)}\n" f"source_dna: {yaml_value(source_dna)}\n" f"generated_at: {yaml_value(generated_at())}\n" "---", - f"# {dna_id} DNA Template", - "本模板是 main agent 的账号级内容生产 / Brief 输入模板,必须由 DNA 文档推导;不得引入 DNA 文档未确认的规则,也不规定成片制作细节。", + f"# {dna_id} DNA Template({PLATFORM_LABEL} · {KIND_LABELS[kind]})", + TEMPLATE_INTROS[kind], *rendered_sections, ] ) + "\n" -def extract_template_sections(markdown: str | None) -> dict[str, str]: +def extract_template_sections(markdown: str | None, kind: str) -> dict[str, str]: if not markdown: return {} sections = {} @@ -723,10 +840,10 @@ def extract_template_sections(markdown: str | None) -> dict[str, str]: return sections -def template_order(heading: str) -> tuple[int, str]: - stage = template_stage_from_heading(heading) +def template_order(heading: str, kind: str) -> tuple[int, str]: + stage = template_stage_from_heading(heading, kind) if stage: - return (TEMPLATE_STAGES.index(stage) + 1, heading) + return (TEMPLATE_STAGES[kind].index(stage) + 1, heading) return (10_000, heading) @@ -735,11 +852,29 @@ def validate_id(value: str, label: str) -> None: raise SystemExit(f"{label} must be 2-64 chars: lowercase letters, digits, and hyphens") -def validate_focus(focus: list[str]) -> None: - valid = {dimension["id"] for dimension in DIMENSIONS} +def validate_kind(value: str | None) -> str | None: + if value is None: + return None + if value not in KINDS: + raise SystemExit(f"Unknown --kind: {value}({PLATFORM_LABEL}支持:{', '.join(KINDS)})") + return value + + +def validate_focus(focus: list[str], kind: str) -> None: + valid = {dimension["id"] for dimension in dimensions_for(kind)} unknown = sorted(set(focus) - valid) if unknown: - raise SystemExit(f"Unknown focus: {', '.join(unknown)}") + raise SystemExit(f"Unknown focus for kind '{kind}': {', '.join(unknown)}") + + +def validate_duration(value: str) -> float: + try: + duration = float(value) + except ValueError: + raise SystemExit("--duration must be a number of seconds") + if duration < 0 or not math.isfinite(duration): + raise SystemExit("--duration must be a non-negative finite number") + return duration def write_text(path: Path, content: str) -> None: @@ -756,32 +891,34 @@ def inputs_from_args(args: argparse.Namespace) -> list[str]: def report_command(args: argparse.Namespace) -> None: validate_id(args.dna_id, "--dna-id") validate_id(args.sample_id, "--sample-id") - validate_focus(args.focus) + kind = validate_kind(args.kind) or DEFAULT_KIND + validate_focus(args.focus, kind) weight = float(args.weight) if weight <= 0 or not math.isfinite(weight): raise SystemExit("--weight must be a positive finite number") + duration = validate_duration(args.duration) if args.duration else 0.0 + if kind != "video" and duration: + print(f"[warn] --duration 只对视频作品有意义,{KIND_LABELS[kind]} report 忽略该值。") + duration = 0.0 paths = collect_input_paths(inputs_from_args(args), {".md", ".txt"}) if len(paths) != 1: - raise SystemExit("report command accepts exactly one script/transcript; use build to aggregate reports") - document = document_metrics( - paths[0], paths[0].read_text(encoding="utf-8", errors="ignore") - ) + raise SystemExit("report command accepts exactly one transcript; use build to aggregate reports") + document = document_metrics(paths[0], paths[0].read_text(encoding="utf-8", errors="ignore"), duration) output_dir = Path(args.output_dir or f"{PLATFORM}/dna/{args.dna_id}/reports") cover_image = "" if args.cover_image: source_cover = validate_cover_image(args.cover_image) - cover_image = str( - persist_cover_image(source_cover, output_dir, args.sample_id).resolve() - ) + cover_image = str(persist_cover_image(source_cover, output_dir, args.sample_id).resolve()) output = output_dir / f"{args.sample_id}.report.md" write_text( output, report_markdown( - args.dna_id, args.sample_id, weight, args.focus, document, cover_image, - args.source_video or "", + args.dna_id, args.sample_id, kind, weight, args.focus, document, cover_image, + args.source_url or "", ), ) - print(f"Wrote single-video DNA report: {output}") + print(f"Wrote single-work DNA report ({KIND_LABELS[kind]}): {output}") + print(f"[next] Agent 补齐「样本观测」与 {len(dimensions_for(kind))} 维单篇结论后,跑 build 聚合。") def build_command(args: argparse.Namespace) -> None: @@ -794,22 +931,17 @@ def build_command(args: argparse.Namespace) -> None: reports = load_reports(report_paths) foreign_reports = [report["report_id"] for report in reports if report["dna_id"] != args.dna_id] if foreign_reports: - raise SystemExit( - f"Reports belong to another dna-id: {', '.join(foreign_reports)}" - ) - statistics = build_statistics(reports) + raise SystemExit(f"Reports belong to another dna-id: {', '.join(foreign_reports)}") + kind = enforce_single_kind(reports, validate_kind(args.kind)) + for report in reports: + validate_focus(report["focus"], kind) + statistics = build_statistics(reports, kind) output_dir = Path(args.output_dir or f"{PLATFORM}/dna/{args.dna_id}") dna_path = output_dir / f"{args.dna_id}.dna.md" template_path = output_dir / f"{args.dna_id}.template.md" - write_text( - dna_path, - dna_document_markdown(args.dna_id, reports, statistics, args.user_input), - ) - write_text( - template_path, - template_markdown(args.dna_id, dna_path.name, None), - ) - print(f"Wrote DNA document and template: {output_dir}") + write_text(dna_path, dna_document_markdown(args.dna_id, kind, reports, statistics, args.user_input)) + write_text(template_path, template_markdown(args.dna_id, kind, dna_path.name, None)) + print(f"Wrote DNA document and template ({KIND_LABELS[kind]}): {output_dir}") def update_command(args: argparse.Namespace) -> None: @@ -822,7 +954,8 @@ def update_command(args: argparse.Namespace) -> None: metadata = parse_frontmatter(previous_dna) dna_id = parse_quoted(metadata.get("dna-id", dna_path.name.removesuffix(".dna.md"))) validate_id(dna_id, "--dna-id") - validate_focus(args.focus) + declared_kind = parse_quoted(metadata.get("kind", "")) or None + kind = validate_kind(args.kind) or validate_kind(declared_kind) input_values = args.input or [] new_paths = collect_input_paths(input_values, {".md"}) if input_values else [] @@ -834,36 +967,29 @@ def update_command(args: argparse.Namespace) -> None: reports = load_reports(sorted(all_paths.values(), key=lambda path: str(path))) foreign_reports = [report["report_id"] for report in reports if report["dna_id"] != dna_id] if foreign_reports: - raise SystemExit( - f"Reports belong to another dna-id: {', '.join(foreign_reports)}" - ) - statistics = build_statistics(reports) + raise SystemExit(f"Reports belong to another dna-id: {', '.join(foreign_reports)}") + kind = enforce_single_kind(reports, kind) + for report in reports: + validate_focus(report["focus"], kind) + validate_focus(args.focus, kind) + statistics = build_statistics(reports, kind) user_inputs = list(args.user_input or []) - write_text( - dna_path, - dna_document_markdown( - dna_id, - reports, - statistics, - user_inputs, - previous_dna, - ), - ) - write_text( - template_path, - template_markdown(dna_id, dna_path.name, previous_template), - ) - print(f"Updated DNA document and template: {dna_path}") + write_text(dna_path, dna_document_markdown(dna_id, kind, reports, statistics, user_inputs, previous_dna)) + write_text(template_path, template_markdown(dna_id, kind, dna_path.name, previous_template)) + print(f"Updated DNA document and template ({KIND_LABELS[kind]}): {dna_path}") def build_parser() -> argparse.ArgumentParser: - parser = argparse.ArgumentParser(description="Build qualitative WeChat Channels (视频号) video DNA assets") + parser = argparse.ArgumentParser(description=f"Build qualitative {PLATFORM_LABEL} DNA assets per work kind") subparsers = parser.add_subparsers(dest="command", required=True) - report = subparsers.add_parser("report", help="Create one single-video DNA report") + kind_help = f"作品类型:{' / '.join(KINDS)}(默认 {DEFAULT_KIND})" + report = subparsers.add_parser("report", help="Create one single-work DNA report") report.add_argument("--input", action="append", required=True) - report.add_argument("--cover-image", help="Local cover image used by visual-model analysis") - report.add_argument("--source-video", help="Optional source video URL or local path, recorded in frontmatter") + report.add_argument("--kind", help=kind_help) + report.add_argument("--cover-image", help="Local cover / first-frame image used by visual-model analysis") + report.add_argument("--source-url", help="Original work URL kept as report evidence") + report.add_argument("--duration", help="Video duration in seconds (video kind only), used for speech-density statistics") report.add_argument("--dna-id", required=True) report.add_argument("--sample-id", required=True) report.add_argument("--weight", default="1") @@ -873,6 +999,7 @@ def build_parser() -> argparse.ArgumentParser: build = subparsers.add_parser("build", help="Aggregate DNA reports into DNA document and template") build.add_argument("--input", action="append") + build.add_argument("--kind", help=kind_help) build.add_argument("--dna-id", required=True) build.add_argument("--user-input", action="append", default=[]) build.add_argument("--output-dir") @@ -880,6 +1007,7 @@ def build_parser() -> argparse.ArgumentParser: update = subparsers.add_parser("update", help="Merge reports and translate user input") update.add_argument("--input", action="append") + update.add_argument("--kind", help=kind_help) update.add_argument("--dna", required=True) update.add_argument("--template", required=True) update.add_argument("--focus", action="append", default=[]) diff --git a/crews/main/skills/expert-wx-channel/workflows/account-benchmark.md b/crews/main/skills/expert-wx-channel/workflows/account-benchmark.md index 5bc2c82e..f3d11130 100644 --- a/crews/main/skills/expert-wx-channel/workflows/account-benchmark.md +++ b/crews/main/skills/expert-wx-channel/workflows/account-benchmark.md @@ -32,7 +32,7 @@ | 来源 | 处理 | | --- | --- | -| 自己账号的作品 | `wx-channel-engagement list` 拿视频简介与行内指标;完整口播仍需转写或用户提供 | +| 自己账号的作品 | `wx-channel-engagement list` 拿视频描述与行内指标;完整口播仍需转写或用户提供 | | 用户提供文字稿 / 视频文件 | 文字稿直接输入;视频文件先经顶层 `talking-head-cut` 转写 | | 用户提供的截图 / 口述数据 / 账号信息 | 作为数据、账号简介、发布时间与内容形式线索保留在 report 证据中,标注来源 | | 跨平台参考(抖音 / B站 / 小红书链接) | 顶层 `viral-chaser` 下载 + 转写 + 拆解;跨平台样本只借鉴结构,不照搬平台调性 | @@ -41,9 +41,10 @@ 1. 优先选择分享、评论信号强的视频;播放量只作次要参考(视频号分享权重高于点赞)。 2. 对标账号的播放、互动数据只能来自用户提供或截图;不得编造,不得把运营效果等同于内容质量。 -3. 账号级对标至少收集 10 条代表性视频样本;不足 10 条时提供全部并说明数量限制。 +3. 对标账号至少批量收集 10 条代表性视频样本(从账号发布列表提取);不足 10 条时提供全部并说明数量限制。 4. 每条样本尽量记录账号简介、发布时间、内容形式、素材来源与授权;缺失写「未观测」,不虚构。 -5. 单条视频可以形成单条观察,但不得当成账号级稳定 DNA;多个样本才分析覆盖率和共性。 +5. 单条视频可以形成单篇观察,但不得当成稳定结论;多个样本才分析覆盖率和共性。 +6. 批量账号样本必须填**账号运营子模块**:账号简介写法(`account-bio`)、发布习惯(`content-mix-cadence`,含发布时间段与节奏);只写进 DNA 文档,不进 template。 ### Step 2 - 建立对标 DNA @@ -70,7 +71,7 @@ wx_channel/dna/{benchmark-dna-id}/{benchmark-dna-id}.dna.md wx_channel/dna/{benchmark-dna-id}/{benchmark-dna-id}.template.md ``` -对标 DNA template 使用 v1 账号级语义段:定位与核心传达、选题与简介包装、内容形式与发布节奏、高数据创意模式、互动与系列、制作交接与 Pipeline、口播文案 DNA。每一部分都要能从对标 DNA 文档推导。 +对标 DNA template 用语义段与目标 DNA 一致:选题、短标题与视频描述、内容创意、视频形态与制作指向、制作规格、口播文案(可选)。每一部分都要能从对标 DNA 文档推导;账号运营子模块不进 template。 ### Step 3 - 选择比较基线 @@ -87,8 +88,8 @@ wx_channel/dna/{base-dna-id}/{base-dna-id}.template.md 比较必须覆盖两层: -1. **14 维 DNA 文档**:逐个比较定位、选题、简介包装、账号简介、内容形式、发布节奏、高数据创意、视觉/声音、口播文案 DNA、互动转化、社交分享闭环、系列签名与制作管线。 -2. **template 语义段**:逐项比较定位与核心传达、选题与简介包装、内容形式与发布节奏、高数据创意、互动与系列、制作交接、口播文案 DNA。 +1. **DNA 文档**:逐个比较选题与观看理由、短标题与视频描述、内容创意、视频形态与制作指向、制作规格、口播文案子模块,以及账号运营子模块(简介写法、发布习惯)。 +2. **template 语义段**:逐项比较选题、短标题与视频描述、内容创意、视频形态与制作指向、制作规格、口播文案。 每个维度和模板语义段都输出四类结论: @@ -96,7 +97,7 @@ wx_channel/dna/{base-dna-id}/{base-dna-id}.template.md | --- | --- | | 保持 | 基线已有优势,与目标观众和商业定位一致 | | 引入 | 对标更有效,且不冲突业务事实、合规边界和用户偏好 | -| 局部借鉴 | 只适合选题、包装、内容形式、发布节奏、社交分享或 Pipeline 等局部场景 | +| 局部借鉴 | 只适合选题、短标题与视频描述、内容创意、制作指向或发布习惯等局部场景 | | 不采纳 | 仅依赖孤例、冲突商业定位、风险高或难稳定执行 | 每项至少说明: @@ -117,7 +118,7 @@ wx_channel/dna/{base-dna-id}/{base-dna-id}.template.md 局部 DNA 融合流程: -1. 明确采纳范围:14 维中的维度、template 语义段,或两者组合。 +1. 明确采纳范围:维度、template 语义段,或两者组合。 2. 读取对标 DNA 文档 / template 的对应规则、适用条件和例外。 3. 整理为一条可转译输入,包含来源 `dna-id`、采纳范围和具体规则。 4. 在基线 DNA 上执行无新增样本的 update: diff --git a/crews/main/skills/expert-wx-channel/workflows/account-setup.md b/crews/main/skills/expert-wx-channel/workflows/account-setup.md index 38ffd7c0..8f299a38 100644 --- a/crews/main/skills/expert-wx-channel/workflows/account-setup.md +++ b/crews/main/skills/expert-wx-channel/workflows/account-setup.md @@ -93,7 +93,7 @@ 5. 把定位、选题、简介包装、账号简介、内容形式与比例、发布习惯、高数据创意、社交分享、口播 DNA 与制作管线发送用户确认。 6. 用户调整意见按 `style-dna.md` 的“用户输入转译”更新到 `dna-0`。 -用户没有明确权重时,所有 report 默认权重为 1。账号级初始参考建议收集 10 条视频样本;用户明确提供单条或少量样本时仍可建立 `dna-0`,但必须标注覆盖不足,不得称为账号级稳定 DNA。 +用户没有明确权重时,所有 report 默认权重为 1。对标账号初始参考建议批量收集 10 条视频样本;用户明确提供单条或少量样本时仍可建立 `dna-0`,但必须标注覆盖不足,不得称为稳定结论。批量账号样本才填账号运营子模块(简介写法、发布习惯)。 #### 路径 B:用户暂无参考,但业务信息完整 @@ -107,7 +107,7 @@ Agent 基于 `business_knowledge.md` 提炼搜索关键词,建议用户到视 #### 路径 C:用户拒绝外部参考或已有明确想法 1. 结合 `business_knowledge.md`、用户定位、目标观众、变现方式和用户思路,提炼 `dna-0`。 -2. Agent 直接按 14 维账号级框架编写: +2. Agent 直接按视频维度框架(`video-dna-framework.md`)编写: ```text wx_channel/dna/dna-0/dna-0.dna.md @@ -143,7 +143,7 @@ wx_channel/dna/dna-0/dna-0.template.md ### Step 1 - 获取老号样本 -1. 自己账号:用 `wx-channel-engagement list` 拿作品管理页最近 20 条的视频简介与行内指标(播放/点赞/评论/分享/收藏)作为数据线索。 +1. 自己账号:用 `wx-channel-engagement list` 拿作品管理页最近 20 条的视频描述与行内指标(播放/点赞/评论/分享/收藏)作为数据线索。 2. 完整口播文案仍需转写:用户提供视频文件或逐字稿;拿不到全部时,请用户至少提供最近 5-10 条的代表性文案。 3. 非自己账号:无后台权限,样本全部由用户提供(文字稿、视频文件、截图数据、账号简介、发布时间)。 4. 剔除重复、已删除和无完整文案的样本;保留账号简介、发布时间、内容形式、素材来源与授权信息,缺失写「未观测」。 @@ -179,4 +179,4 @@ wx_channel/dna/dna-0/dna-0.template.md | 成交少 | 承接路径断、信任状弱、促单模糊 | 强化信任状与限时促单,理顺挂车/直播路径 | | 记不住人 | 身份信号弱、人设张力弱 | 加入真实场景、短板、判断标准或系列任务 | -诊断结论按账号审计字段落盘 `wx_channel/calibration/`:当前定位、主页信号、内容垂直度、观看理由、视频简介/封面、钩子、人设张力、评论质量、承接路径、合集、合规风险、优先修复动作。 +诊断结论按账号审计字段落盘 `wx_channel/calibration/`:当前定位、主页信号、内容垂直度、观看理由、视频描述/封面、钩子、人设张力、评论质量、承接路径、合集、合规风险、优先修复动作。 diff --git a/crews/main/skills/expert-wx-channel/workflows/content-production.md b/crews/main/skills/expert-wx-channel/workflows/content-production.md index 3ba11fca..0d9bc228 100644 --- a/crews/main/skills/expert-wx-channel/workflows/content-production.md +++ b/crews/main/skills/expert-wx-channel/workflows/content-production.md @@ -2,7 +2,7 @@ 从选题、Brief 到发布的完整视频运营流程。用户说“帮我做一条视频”“出几条视频”“这条的主题我们也做一条”走这个。 -**分工硬边界**:main agent 只做已有视频素材的简单加工,并负责长文 / 图文内容;视频全案只产出 Brief,成片制作委托 `content-producer`。若口播文案 DNA 已启用,main agent 写口播终稿并随 Brief 交付;其余声画制作归 CP。 +**分工硬边界**:main agent 负责选题策划、按 DNA 出 Brief、拟定短标题与视频描述、准备素材(含简单加工)、监督 CP 进程、成片后的发布与运营;视频全案的成片制作委托 `content-producer`。口播类视频的口播文案由 main agent 写好随 Brief 交付(真人口播时向用户取得录音文件),其余声画制作归 CP。 ## Step 0 - 入口判断 @@ -45,7 +45,7 @@ wx_channel/dna/{dna-id}/{dna-id}.dna.md wx_channel/dna/{dna-id}/{dna-id}.template.md ``` -DNA template 是 main agent 的内容生产 / Brief 输入模板,覆盖定位与核心传达、选题与简介包装、内容形式与发布节奏、高数据创意、互动系列、制作交接与可选口播文案 DNA。它不规定镜头表、逐句台词、转场或编码细节。 +DNA template = **Brief 正文模板 +(可选)口播文案模板**,覆盖选题与观看理由、短标题与视频描述、内容创意、视频形态与制作指向、制作规格、口播文案。它不规定创作细节:逐句台词、镜头表、转场或编码参数归 Content Producer。账号运营子模块(简介写法、发布习惯)只留在 DNA 文档,用于发布节奏决策。 ### 2. 读取业务知识 @@ -64,7 +64,7 @@ DNA template 是 main agent 的内容生产 / Brief 输入模板,覆盖定位 | 项目 | 规则 | | --- | --- | -| 主题 / 方向 | 用户给了明确主题时不得另起炉灶,仅按 DNA template 细化选题和视频简介文案 | +| 主题 / 方向 | 用户给了明确主题时不得另起炉灶,仅按 DNA template 细化选题、短标题与视频描述 | | 素材 | 用户提供的文案、笔记、截图、链接、数据、案例、实拍素材必须优先使用 | | 目标观众 | 未指定时按 `business_knowledge.md` 和 DNA 受众关系推导(视频号主力人群 30-55 岁,偏真实、有深度、有温度) | | 时长 | 未指定时按 DNA template,再无要求默认 30-60 秒 | @@ -82,7 +82,7 @@ DNA template 是 main agent 的内容生产 / Brief 输入模板,覆盖定位 > Agent 的一般创作判断 ``` -DNA 约束的是账号定位、选题组合、简介包装、核心传达、内容形式、发布节奏、社交分享与制作边界;口播文案 DNA 只在明确启用时约束口播。DNA 不能覆盖用户指定的出镜人、事实、来源、CTA 和合规边界。 +DNA 约束的是选题与观看理由、短标题与视频描述写法、内容创意原型、视频形态与制作指向、制作规格;口播文案子模块只在明确启用时约束口播;账号运营子模块只用于发布节奏决策,不进 Brief。DNA 不能覆盖用户指定的出镜人、事实、来源、CTA 和合规边界。 ## Step 2 - 素材获取与整理 @@ -90,7 +90,7 @@ DNA 约束的是账号定位、选题组合、简介包装、核心传达、内 1. 按类型获取输入: - 抖音 / B站 / 小红书视频链接 -> 调顶层 `viral-chaser` 下载、转写与拆解,拆解报告与逐字稿进 `materials/`。 - - 视频号链接 -> 无公开下载路径:请用户提供视频文件或文字稿;自己账号的作品可用 `wx-channel-engagement list` 补视频简介与数据线索。 + - 视频号链接 -> 无公开下载路径:请用户提供视频文件或文字稿;自己账号的作品可用 `wx-channel-engagement list` 补视频描述与数据线索。 - 其他链接 -> 用浏览器工具读取。 - 本地 `.md` / `.txt` -> 直接读取;视频文件确认可读并记录格式(支持 `.mp4` / `.mov` / `.avi` / `.webm`)。 - PDF / Word -> 先提取正文文本再进入流程。 @@ -148,24 +148,24 @@ DNA 约束的是账号定位、选题组合、简介包装、核心传达、内 - 小调直接改。 - 换主题、换观众、换转化目标 -> 重做本步。 -## Step 4 - 视频简介文案 +## Step 4 - 短标题与视频描述 -视频号作品没有标题概念,只有**视频简介**;视频简介(≤300 字,含话题标签)就是作品展示文本。 +视频号发布页改版后可同时填 **视频描述** 与 **短标题**,官方明确「填写短标题会获得更多流量」,因此**两项都必须有、发布时都必须填**,且都由 main agent 拟定。 -1. **视频简介**(≤300 字):按 DNA template 的视频简介模式写,含 2-5 个话题标签,直接写在简介中。 -2. 给 2-3 个候选,分别说明各自的观看理由、核心传达与话题标签策略。 -3. 有参考视频或草稿时,必须参考其文案内容(如有),但不得照抄。 -4. 用户已指定简介或文案原则时,候选必须在该约束内生成,不得偷换方向。 +1. **短标题**:按 DNA template 的「短标题模式」写,6-16 字,承担身份信号与价值预告;给 2-3 个候选。 +2. **视频描述**(≤300 字,含 2-5 个话题标签,标签直接写在描述中):按 DNA template 的「视频描述结构」写;给 2-3 个候选,说明各自的观看理由与标签策略。 +3. 有参考视频或草稿时必须参考其文案内容(如有),但不得照抄。 +4. 用户已指定文案或原则时,候选必须在该约束内生成,不得偷换方向。 -> 视频简介后面要原样传给 `published-track record --title`(该参数是入库字段名,语义为完整视频简介),本步定稿后不再改动;确需改动时入库记录同步重跑。 +> **取数与入库只用视频描述**:作品管理页不展示短标题,`wx-channel-engagement` 抓取匹配与 `published-track record --title` 都用完整视频描述;**短标题不写库**,只记在作品目录的发布文案里,避免混淆。 -## 【确认】视频简介文案 +## 【确认】短标题与视频描述 -第二个必停节点。务必与用户就完整视频简介确认。用户如果提出修改意见,按用户意见修改,直至与用户达成一致。 +第二个必停节点。务必与用户就短标题与完整视频描述一并确认。用户如果提出修改意见,按用户意见修改,直至与用户达成一致。 ## Step 5 - 封面图 -1. 读取 DNA template 的简介包装与视觉语言,只取账号级风格边界。 +1. 读取 DNA template 的标题封面结论与制作规格,只取风格边界。 2. 结合本条核心传达、目标观众和封面文字承诺,确定本次封面的主体、色彩、光线、文字视觉(身份 + 痛点 + 解决方案)和负向约束。 3. 优先路径: - 有实拍 / 成片素材 -> 从素材中截取候选帧,交用户挑选。 @@ -179,35 +179,43 @@ DNA 约束的是账号定位、选题组合、简介包装、核心传达、内 ## Step 6 - 制作 Brief -每条需要 CP 全案制作的视频,main agent 只产出 `wx_channel/outputs//brief.md`。Brief 是 main / CP 的唯一分界点。 +每条需要 CP 全案制作的视频,main agent 只产出 `wx_channel/outputs//brief.md`。Brief 是 main / CP 的唯一交接物。 ### Brief 必填字段 ```markdown -# 视频制作 Brief - -- dna_id: -- pipeline:dna-ad-video-pipeline / video-producer:default / 未指定 -- MainAgent 交付物:Brief、视频简介、素材清单、(如启用)口播终稿 -- ContentProducer 交付物:成片、封面或候选帧、交付说明、自检结果 -- 账号定位与核心传达: -- 目标人群与观看理由: -- 选题与内容支柱: -- 用户痛点与信任状: -- 内容形式:口播 / 实拍拼接 / 创意转场 / 纯 AIGC 动画 / 混合 -- 素材清单、来源与授权: -- 视觉与声音边界:横竖屏、画面风格、音色/BGM 倾向(不写逐镜细节) -- 互动与分享目标:评论、关注、转发、私信、直播预约选一个主目标 -- 时长带与交付验收: -- 风险检查:事实、承诺、授权、平台合规 -- 发布后重点观察: +# 视频号视频制作 Brief + +- 视频名 / slug: +- platform:wx_channel +- workflow:reversal-ad / narration-video / collage-broll / 未指定(未指定时 CP 按通用阶段链自选档位) +- 选题与观看理由: +- 核心传达: +- 内容创意:创意原型 + 展开逻辑 + 记忆点(+ 反转设计,如为反转植入类) +- 封面要求:封面主文案(用短标题或核心传达)+ 视觉方向 +- 制作规格:横屏 / 竖屏、时长带、画面风格、配音音色与声音形态、BGM 与音效、字幕 +- 口播文案:`voiceover.md` 绝对路径(口播类必填)/ 真人口播录音绝对路径 / 不适用 +- 素材清单:逐条**绝对路径** + 来源 + 授权(无素材时写「无,由 CP 按 Brief 取材」) +- 互动与分享目标:评论 / 关注 / 转发 / 私信 / 直播预约,选一个主目标 +- 交付物与验收:`video.mp4` + `cover.jpg` + `final-deliver.md`,回报三者绝对路径;验收标准 +- 闸门:GATE A / GATE B 批准人(用户或 main 代理批准 + 批准范围) +- 禁止事项:事实与承诺边界、合规红线、禁用方向 ``` -### 口播终稿规则 +Brief 硬性规则: -- **口播文案 DNA 已启用**:main agent 按 `narration-dna` 写口播终稿,保存为 `voiceover.md` 并在 Brief 中引用;这等同写一篇长文,CP 不重写策略文案,只做声画制作。 -- **口播文案 DNA 未启用**:Brief 不规定逐句口播,CP 在 Pipeline 内完成脚本与声画方案。 -- 用户必用事实、案例、承诺和 CTA 必须进入 Brief 或口播终稿,不得为了形式删除关键事实。 +- **不写 DNA**:Brief 里不出现 dna-id、DNA 文档路径或 DNA 规则原文——CP 看不到 main 的 DNA,只按 Brief 制作。 +- **不写发布文案**:短标题与视频描述是 main 的发布职责(Step 4),不进 Brief;封面主文案除外。 +- **不建工作区**:main 不替 CP 建目录、不指定项目路径;CP 在自己的 workspace 下自建工作区。双方 T3 权限可互访取文件。 +- **素材给绝对路径**:main 负责素材准备,把绝对路径写进 Brief。 +- **甲乙方关系**:需求方向、品牌事实、发布文案归 main;制作方案、分镜、渲染参数归 CP。 + +### 口播文案规则 + +- **口播类视频的口播文案由 main agent 出**:按 DNA 文档的 `narration-script`(口播文案子模块)写终稿,保存为作品目录下的 `voiceover.md`,在 Brief 中给**绝对路径**。CP 不重写策略文案,只做声画实现。 +- **真人口播**:明确要用用户真人声音时,main agent 必须向用户取得录音文件,落到作品目录并在 Brief 中给绝对路径。 +- **口播子模块未启用**:Brief 写明「口播文案:不适用」或只给要点,由 CP 按其 workflow 组织旁白;main 不再规定逐句台词。 +- 用户必用的事实、案例、承诺和 CTA 必须进入 Brief 或口播终稿,不得为了形式删除关键事实。 ## 【确认】Brief @@ -215,22 +223,23 @@ DNA 约束的是账号定位、选题组合、简介包装、核心传达、内 ## Step 7 - 视频制作 -取得成片 `wx_channel/outputs//video.mp4`: +取得成片 `wx_channel/outputs//video.mp4`(CP 交付后按其回报的绝对路径取回,main 不替 CP 建工作区): | 情况 | 处理 | | --- | --- | | 用户直接提供成片 | 校验格式(`.mp4` / `.mov` / `.avi` / `.webm`)与时长,复制到作品目录 | | 用户有素材但需简单加工 | 口播轻剪辑(去口癖 / 剪集锦)走顶层 `talking-head-cut`;拼接、字幕、BGM、片头片尾走顶层 `video-edit` | -| 全案制作 | 委托 `content-producer`,只交付确认后的 Brief;若 Brief 指定 Pipeline,CP 必须采用,未指定则 CP 自由发挥 | +| 全案制作 | 委托 `content-producer`,只交付确认后的 Brief;若 Brief 指定 `workflow`,CP 必须采用,未指定则 CP 自由发挥 | -成片必须兑现 Brief 与口播终稿(如有);制作中发现 Brief 无法执行(素材缺失、时长超标)时回到【确认】Brief 节点处理,不擅自改策略。 +成片必须兑现 Brief 与口播终稿(如有);制作中发现 Brief 无法执行(素材缺失、时长超标)时回到【确认】Brief 节点处理,不擅自改策略。main 负责监督并推动 CP 的进度,卡住时追问而不是替它做。 ## Step 8 - 存文件 定稿作品目录 `wx_channel/outputs//`: - `brief.md`(定稿 Brief) -- `voiceover.md`(仅口播文案 DNA 启用时) +- `voiceover.md`(口播类:main 写的口播终稿) +- `publish-copy.md`(短标题 + 视频描述定稿,发布与入库都以它为准) - `video.mp4` 成片(或 `.mov` / `.avi` / `.webm`) - `cover.jpg` 封面图 - `materials/` 原始素材(Step 2 已放入) @@ -242,7 +251,7 @@ DNA 约束的是账号定位、选题组合、简介包装、核心传达、内 1. 打开创作者中心发布页,检查 `wechat-channel` session 登录态;失效时走该工具的无头截图扫码登录流,**Stop and wait** 等用户扫码确认。 2. 上传 `video.mp4`,等待上传与转码完成(最长 3 分钟)。 -3. 只填视频简介(Step 4 定稿,含话题标签);不要把视频简介拆成短标题,也不要另填标题。 +3. **视频描述与短标题都要填**(均取 Step 4 定稿):视频描述含话题标签,短标题按发布页字段单独填;两项都不得留空。 4. 点击“发表”;弹出原创声明弹窗时按工具说明处理。发布后用户会在手机端补充原创声明等事项。 5. 发布成功后在视频列表页等转码完成,通过分享面板复制视频链接(`https://weixin.qq.com/sph/xxxx`);审核中拿不到链接时留空,后续补充。 6. 遇到「session wechat-channel 正忙」(fail-first 队列)时等当前操作完成再重试,不盲试、不 close。 @@ -258,10 +267,11 @@ published-track record \ --platform wx_channel \ --source-folder wx_channel/outputs// \ --account <发布所用账号> \ - --title "" \ + --title "" \ --publish-url "" ``` -- `--title` 是数据库字段名,**必须传完整视频简介**(含 hashtag)——`wx-channel-engagement` 抓取按它匹配后台作品。 +- `--title` 是数据库字段名,**必须传完整视频描述**(含 hashtag)——`wx-channel-engagement` 抓取按它匹配后台作品。 +- **短标题不入库**:管理页不展示短标题,写库会造成匹配混淆;短标题只留在作品目录的 `publish-copy.md`。 - `dna_id` 自动从 `dna-meta.json` 读取。 - 发布时没拿到链接的,后续用户提供或抓取到链接后用相同 `--source-folder` 重跑 `published-track record` 补 `--publish-url`——upsert 语义升级记录,不重复插行。 diff --git a/crews/main/skills/expert-wx-channel/workflows/editing.md b/crews/main/skills/expert-wx-channel/workflows/editing.md index 8d6f42b8..28b189d6 100644 --- a/crews/main/skills/expert-wx-channel/workflows/editing.md +++ b/crews/main/skills/expert-wx-channel/workflows/editing.md @@ -9,11 +9,11 @@ | 用户的说法 | 改的层级 | 怎么走 | |-----------|---------|--------| | “润色一下” / “口播顺一顺” | 口播措辞层 | 轻改,直接上手 | -| “开头不行 / 换个钩子” | 口播层 | 口播文案 DNA 已启用时按 `narration-dna` 重做;未启用时按用户要求与内容形式重做,不虚构 DNA 规则 | -| “换个语气” / “更接地气” | 内容风格层 | 核对 `positioning-core`、`narration-dna` 与用户要求 -> 改稿 -> 自检 | +| “开头不行 / 换个钩子” | 口播层 | 口播文案子模块已启用时按 `narration-script` 重做;未启用时按用户要求与内容形式重做,不虚构 DNA 规则 | +| “换个语气” / “更接地气” | 内容风格层 | 核对 `topic-angle`、`content-idea`、`narration-script` 与用户要求 -> 改稿 -> 自检 | | “结构调一下” / “时间分配不对” | 结构层 | 先出调整方案(新分段 / 时间轴)-> 确认 -> 改 | | “方向不对,重做” | 方向层 | 回到 content-production.md 从选题重新走 | -| “换个封面” | 封面层 | 按 `description-packaging` 与 `visual-language` 重出候选 -> 确认 | +| “换个封面” | 封面层 | 按 `title-cover`(短标题与视频描述)与 `production-spec` 重出候选 -> 确认 | | “压缩到 X 秒” / “展开到 X 秒” | 时长层 | 先问清楚保留什么砍掉什么 | ## 轻改(口播措辞 / 润色 / 去 AI 腔) @@ -26,7 +26,7 @@ 只重做前 3 秒,不动正文: -1. 读目标 DNA 的 `narration-dna`;未启用时先记录「口播 DNA 未观测」,以用户要求和本条事实为准。 +1. 读目标 DNA 的 `narration-script`;未启用时先记录「口播文案子模块未启用」,以用户要求和本条事实为准。 2. 给 2-3 个候选,标明每个候选的冲突点、身份信号与核心传达。 3. 用户选定后同步改脚本对应段落与封面文案(如封面承载了旧钩子)。 @@ -48,7 +48,7 @@ ## 换封面 -1. 读目标 DNA 的 `description-packaging` 与 `visual-language`,保留封面三要素:身份 + 痛点 + 解决方案。 +1. 读目标 DNA 的 `title-cover` 与 `production-spec`,保留封面三要素:身份 + 痛点 + 解决方案。 2. 按新主题出 2-3 个候选:素材截帧优先,无合适素材走 `siliconflow-img-gen` 生成。 3. 用户确认后替换 `wx_channel/outputs//cover.jpg`。 @@ -71,4 +71,4 @@ 1. **成片同步**:脚本改动涉及画面或口播的,按 `content-production.md` Step 7 重新走视频制作(已有成片只需轻剪辑时走 `talking-head-cut` / `video-edit`)。 2. **存文件**:稿件与成片归位 `wx_channel/outputs//`,同步更新 `brief.md`、`voiceover.md`(如有)、`cover.jpg` 与 `dna-meta.json`。 -3. **发布 + 入库**:按 `wechat-channels-publish` 工具说明发布,再按 `content-production.md` Step 10 调 `published-track record` 入库——`--title` 仍传完整视频简介;视频简介改过的,用新文案重跑记录。 +3. **发布 + 入库**:按 `wechat-channels-publish` 工具说明发布,再按 `content-production.md` Step 10 调 `published-track record` 入库——`--title` 仍传完整视频描述;视频描述改过的,用新文案重跑记录。 diff --git a/crews/main/skills/expert-wx-channel/workflows/review.md b/crews/main/skills/expert-wx-channel/workflows/review.md index 840e79a2..bfe79044 100644 --- a/crews/main/skills/expert-wx-channel/workflows/review.md +++ b/crews/main/skills/expert-wx-channel/workflows/review.md @@ -29,20 +29,20 @@ 视频号助手作品管理页只给这 5 项行内指标。**完播率、社交推荐占比、观众来源构成**不在抓取范围内,需要用户提供创作者中心截图后才能作为证据;拿不到时相关漏斗层只能从相对趋势推断,并在报告中注明。 -### 互动漏斗 → template 语义段 → 14 维映射 +### 互动漏斗 → template 语义段 → DNA 维度映射(视频作品) | 漏斗卡点 | 先怀疑的 template 语义段 | 可回溯的 DNA 维度 | |---------|--------------------------|------------------| -| 播放低(曝光/推荐不足) | 定位与核心传达、选题与简介包装 | positioning-core、topic-portfolio、description-packaging | -| 完播低(需用户提供完播数据) | 内容形式与发布节奏、口播文案 DNA | content-form-mix、narration-dna、high-performer-patterns | -| 中段流失 | 高数据创意模式、内容形式与发布节奏 | high-performer-patterns、content-form-mix | -| 分享低 | 互动与系列 | social-share-loop、positioning-core、high-performer-patterns | -| 评论低 | 互动与系列 | engagement-conversion、social-share-loop | -| 收藏低 | 高数据创意模式 | high-performer-patterns | -| 关注少 | 定位与核心传达、互动与系列 | bio-profile、series-signature、engagement-conversion | -| 转化少(私信/成交) | 定位与核心传达、互动与系列 | positioning-core、engagement-conversion | +| 播放低(曝光/推荐不足) | 选题、短标题与视频描述 | topic-angle、title-cover | +| 完播低(需用户提供完播数据) | 内容创意、制作规格、口播文案 | content-idea、production-spec、narration-script | +| 中段流失 | 内容创意、视频形态与制作指向 | content-idea、video-form | +| 分享低 | 选题、内容创意 | topic-angle、content-idea | +| 评论低 | 内容创意(讨论点)、口播文案 | content-idea、narration-script | +| 收藏少 | 内容创意、制作规格 | content-idea、production-spec | +| 关注少 | 账号运营子模块、选题 | account-bio、content-mix-cadence、topic-angle | +| 转化少(私信/成交) | 选题、内容创意 | topic-angle、content-idea | -交叉判断:完播高、分享低 → 内容好看但缺社交价值;分享高、完播低 → 视频简介或定位承诺与内容兑现不足。 +交叉判断:完播高、分享低 → 内容好看但缺社交价值;分享高、完播低 → 短标题 / 视频描述的承诺与内容兑现不足。 ### 平台混杂因素(归因前必排) @@ -80,7 +80,7 @@ content-calibrator eval --platform wx_channel --dna-id # 指定 DNA 1. 判定只看比值与走向,绝对值只作上下文。 2. 逐条排除平台混杂因素,特别注意长尾效应——近 3-7 天发布的数据成熟度不足时降级为观察。 -3. 回读 `wx_channel/dna//.dna.md` / `.template.md` 与待评估作品(`source_folder` 内 `brief.md`、`voiceover.md` 或成片说明),把趋势变化落到 template 语义段与 14 维。 +3. 回读 `wx_channel/dna//.dna.md` / `.template.md` 与待评估作品(`source_folder` 内 `brief.md`、`voiceover.md` 或成片说明),把趋势变化落到 template 语义段与 DNA 维度。 4. 分享率异常(过高或过低)时优先核对转发动机设计是否命中,这是视频号区别于其他平台的第一归因点。 ### Step 3 - 报告与标记 diff --git a/crews/main/skills/expert-wx-channel/workflows/style-dna.md b/crews/main/skills/expert-wx-channel/workflows/style-dna.md index 83362b48..6ea6fd55 100644 --- a/crews/main/skills/expert-wx-channel/workflows/style-dna.md +++ b/crews/main/skills/expert-wx-channel/workflows/style-dna.md @@ -1,10 +1,10 @@ -# 视频号账号级 DNA 创建与更新 Workflow +# 视频号 DNA 创建与更新 Workflow -本 Workflow 负责 DNA report、DNA 文档与 DNA template 的创建与更新。维度框架以 `wx-channel-style-profiler` 的 `references/account-dna-framework.md` 为准(账号级 DNA v1)。 +本 Workflow 负责 DNA report、DNA 文档与 DNA template 的创建与更新。维度框架以 `wx-channel-style-profiler` 的 `references/video-dna-framework.md` 为准(DNA v2)。 ## 边界 -- DNA 是账号级运营框架,不是单条视频脚本或剪辑细节。 +- DNA 是从一批作品样本中提取、聚合出的内容生产规则集,不存在「平台级 DNA」或「账号级 DNA」的说法;样本可以来自多个账号,也可以来自用户指定的一个账号的发布列表批量提取。 - 账号初始化与默认 `dna-0` 建立走 `account-setup.md`;对标样本先走 `account-benchmark.md`。 - DNA 如何用于内容生产走 `content-production.md`;改稿走 `editing.md`;数据复盘走 `review.md`。 - DNA 指导 main agent 出内容或视频 Brief;全片制作委托 `content-producer`。 @@ -29,8 +29,9 @@ 1. 用户指定 `dna-id` 时使用该 DNA。 2. 未指定或说“默认 DNA”时使用 `dna-0`。 -3. 对标样本必须进入独立 DNA,不直接写入 `dna-0`;采纳后再通过局部融合更新。 -4. 目标 DNA 不存在时,先走 `account-setup.md` 或按用户明确指定的新 `dna-id` 初始化。 +3. **作品类型**:视频号只有视频作品,`--kind` 固定 `video`,无需分型建 DNA。 +4. 对标样本必须进入独立 DNA,不直接写入 `dna-0`;采纳后再通过局部融合更新。 +5. 目标 DNA 不存在时,先走 `account-setup.md` 或按用户明确指定的新 `dna-id` 初始化。 ## 存储结构 @@ -48,6 +49,8 @@ wx_channel/dna/{dna-id}/ ## 样本获取 +**先判作品类型**(视频 / 图文):它决定用哪套维度框架、`--kind` 取值与目标 dna-id;同一个 DNA 不混型。 + | 来源 | 处理 | | --- | --- | | 用户提供脚本 / 逐字稿 | 整理为 `.md` / `.txt`,保留描述、数据与账号线索 | @@ -69,11 +72,12 @@ wx_channel/dna/{dna-id}/ ### Step 1 - 准备样本 -1. 把脚本/逐字稿整理为 `.md`,首个一级标题写样本标签 / 视频简介摘要。 -2. 确定目标 `dna-id`、`sample-id`、样本权重和 focus。 -3. 整理账号观测与视频观测信息,供 Agent 补进 report。 +1. 判定作品类型(视频 / 图文),据此选框架与目标 `dna-id`。 +2. 把脚本/逐字稿整理为 `.md`,首个一级标题写样本标签 / 视频描述摘要。 +3. 确定目标 `dna-id`、`sample-id`、样本权重和 focus。 +4. 整理账号观测与视频观测信息,供 Agent 补进 report。 -### Step 2 - 生成单条 report +### Step 2 - 生成单篇 report ```bash wx-channel-style-profiler report \ @@ -87,10 +91,12 @@ wx-channel-style-profiler report \ 生成 scaffold 后必须: -1. 补齐「视频信息」与「样本与账号观测」。 -2. 回读脚本原文,补齐 14 维的单条结论、证据和可复用信号。 -3. 单条样本不得推导账号比例、发布节奏、社交分享闭环或高数据共性。 -4. 视觉语言必须有图片/关键帧证据;口播文案 DNA 样本不足时保持未启用。 +1. 补齐「样本观测」:作品类型、样本来源、账号与简介、发布时间、数据线索、横竖屏与时长、素材来源与授权(缺失写「未观测」)。 +2. 回读转录 / 脚本原文,补齐各维度的单篇结论、原文证据与可复用信号。 +3. 短标题与视频描述分开记:抓取只能拿到视频描述,短标题拿不到就写「未观测(管理页不展示)」,不得把描述当短标题。 +4. 视频内容形态必须给出**制作指向**(Content Producer `expert-video` 的某个 workflow,或 main 的 `video-edit` / `talking-head-cut` / `ui-demo`),只写真实存在的资源名。 +5. 视觉维度必须有封面 / 首帧 / 关键帧图片证据;口播文案子模块样本不足时写「未启用」。 +6. 账号运营子模块只在样本来自对标账号批量提取时填写,单篇样本写「未观测」。 ### Step 3 - 聚合 DNA @@ -101,10 +107,11 @@ wx-channel-style-profiler build --dna-id {dna-id} Agent 必须读取全部 report,按权重/focus 聚合: - 高频共性、高权重偏好、局部借鉴、孤例、例外分开写。 -- 标注样本覆盖度;单条/少量样本不得称为稳定账号 DNA。 -- 高数据内容要回读创意与内容形式,不能只归因播放量。 +- 标注样本覆盖度;单篇或少量样本不得称为稳定结论。 +- 视频形态必须聚合成明确的**制作指向**(Content Producer `expert-video` 的某个 workflow,或 main 的素材加工技能),供 Brief 的 `workflow` 字段直接引用。 +- 高数据内容要回读创意、形态与包装,不能只归因播放量。 - 为每个维度写聚合结论、报告依据和可执行规则。 -- 确保 DNA 文档能推导 template。 +- 确保 DNA 文档能推导 template;账号运营子模块的结论只留在 DNA 文档。 ## 更新已有 DNA @@ -123,7 +130,7 @@ wx-channel-style-profiler update \ ### 用户偏好 -用户偏好不直接入库。通过 `--user-input` 传入后,Agent 映射到具体维度,并把原话转译为账号级规则与 Brief 规则。 +用户偏好不直接入库。通过 `--user-input` 传入后,Agent 映射到具体维度,并把原话转译为具体维度的创作规则与 Brief / 图文生产规则。 ### 局部借鉴 @@ -131,22 +138,23 @@ wx-channel-style-profiler update \ ### 表现反馈 -复盘产生的建议先列证据与影响维度,经用户确认后写入 DNA;不得把一次数据波动直接升格为账号规则。 +复盘产生的建议先列证据与影响维度,经用户确认后写入 DNA;不得把一次数据波动直接升格为 DNA 规则。 ## DNA 使用接口 -- **内容生产**:读取 DNA 文档与 template,确定定位、选题、简介包装、内容形式、发布节奏和高数据创意。 -- **视频全案**:main agent 产出 Brief;Brief 写明 Pipeline、素材授权、验收标准、交付边界。未指定 Pipeline 时 CP 自由发挥,指定时必须采用。 -- **口播类视频**:若口播文案 DNA 已启用,main agent 写口播文案并随 Brief 交付;CP 不重写策略文案。 -- **图文/长文**:main agent 直接生产。 +- **视频全案**:读取 DNA 文档与 template,main agent 产出 **Brief**(+ 口播类的口播文案)。Brief 写明选题与观看理由、内容创意、`workflow`(视频形态的制作指向)、制作规格、素材清单与授权(绝对路径)、验收标准、闸门批准人。 +- **短标题与视频描述由 main agent 拟定**:视频号发布页两项都可填,官方称填短标题能获得更多流量,因此两项都必须有、发布时都必须填;但入库(`published-track`)与 `wx-channel-engagement` 匹配只用**视频描述**,短标题不写库。这两项属发布文案,不进 Brief 的制作要求。 +- **Brief 不含 DNA 信息**:Content Producer 看不到 main 的 DNA,只按 Brief 制作;也不要把 DNA 文档路径写进 Brief。 +- **口播类视频**:口播文案子模块启用时,口播终稿由 main agent 写好并随 Brief 交付;真人口播时由 main agent 向用户取得录音文件。CP 不重写策略文案。 +- **工作区**:main 不替 CP 建工作区,也不指定项目目录;CP 自建工作区,双方 T3 权限可互访取文件。 ## 对标接口 -对标样本进入独立 `dna-id`;比较时输出定位、选题、简介包装、内容形式、发布节奏、高数据创意、社交分享闭环与制作管线的差异。用户明确采纳后才融合进 `dna-0`。 +对标样本进入独立 `dna-id`;比较时输出选题、短标题与视频描述、内容创意、视频形态与制作指向、制作规格的差异,以及(账号批量样本才有的)账号运营子模块差异:简介写法、发布习惯。用户明确采纳后才通过局部融合写进目标 DNA。 ## 编排原则 -- 一个生产任务只使用一个 DNA;需要融合时先更新 DNA。 +- 一个生产任务只使用一个 DNA,且作品类型与任务一致;需要融合时先更新 DNA。 - 样本、用户输入、数据反馈必须可追溯。 -- 账号级结论必须有覆盖度;不足就写未观测。 -- Template 只写 main agent 可执行的输入/Brief 规则,不写成片制作细节。 +- 账号运营子模块(简介写法、内容形式比例、发布习惯)只在对标账号批量样本下填写,且只进 DNA 文档不进 template;覆盖度不足就写未观测。 +- Template 只写 main agent 可执行的规则:视频类 = Brief 正文模板 + 口播文案模板;图文类 = 图文写作模板。都不写成片制作细节。 diff --git a/crews/main/skills/expert-xhs/SKILL.md b/crews/main/skills/expert-xhs/SKILL.md index 69d4f40d..86f2e012 100644 --- a/crews/main/skills/expert-xhs/SKILL.md +++ b/crews/main/skills/expert-xhs/SKILL.md @@ -1,6 +1,6 @@ --- name: expert-xhs -description: 小红书账号运营专家。承接定位起号、账号级 DNA、搜索意图、图文笔记生产、已有素材轻加工、视频全案 Brief、发布与数据复盘;全片制作委托 content-producer。 +description: 小红书账号运营专家。承接定位起号、内容 DNA(图文 / 视频两套框架)、搜索意图与用户问题、图文笔记生产、已有素材轻加工、视频全案 Brief 与口播文案、发布与数据复盘;全片制作委托 content-producer。 metadata: openclaw: emoji: 📕 @@ -14,7 +14,7 @@ metadata: | 场景 | Workflow | 什么时候触发 | |------|----------|-------------| -| 内容 DNA 管理 | Style DNA | 建 / 更新内容 DNA(样本、偏好、局部借鉴、对标融合),决定样本落到哪个 DNA | +| 内容 DNA 管理 | Style DNA | 建 / 更新内容 DNA(样本、偏好、局部借鉴、对标融合):先判作品类型,再决定样本落到哪个 DNA | | 内容生产 | Content Production | 做一条 / 做几条小红书内容;图文由 main 直接生产,视频全案只产出并委托 Brief | | 起号与定位 | Account Setup | 新号起号、定位梳理、内容支柱搭建、老号接手与诊断 | | 账号对标 | Account Benchmark | 对标账号 / 对标笔记分析(关键词提取 + 低粉爆款搜索),并与默认或指定 DNA 逐项比较 | @@ -37,7 +37,7 @@ metadata: | 工具 | 用途 | 命令 | |------|------|------| -| `xhs-style-profiler` | 生成单篇笔记的账号级 DNA report,并聚合 DNA 文档、搜索意图地图与 Brief template | `xhs-style-profiler` | +| `xhs-style-profiler` | 生成单篇作品(图文 / 视频,`--kind`)的 DNA report,并聚合 DNA 文档、搜索意图地图与 template | `xhs-style-profiler` | | `xhs-content-ops` | 图文笔记下载(正文 / 图片 / 互动数据),对标与 DNA 采样的取数主力 | `xhs-content-ops` | | `xhs-publish` | 图文 / 视频笔记发布(creator COS 上传 + web_api,含登录态两步管理) | `xhs-publish` | | `xhs-engagement` | 创作者后台互动数抓取,写入 published-track 的 pub_xhs 表 | `xhs-engagement` | @@ -46,11 +46,11 @@ metadata: ## 风格与 DNA -账号级 DNA 存储目录是 `xhs/dna/`。未指定 DNA 时默认使用并更新 `dna-0`。生产前同时读取 DNA 文档与 DNA template;对标分析先建立独立对标 DNA,不默认写入 `dna-0`。 +DNA 存储目录是 `xhs/dna/`。未指定 DNA 时默认使用并更新 `dna-0`(图文);视频笔记另建 dna-id(如 `dna-0-video`)。生产前同时读取 DNA 文档与 DNA template;对标分析先建立独立对标 DNA,不默认写入 `dna-0`。 -DNA 是账号级框架:定位与核心传达、选题组合、标题包装、账号简介、图文/视频比例、发布习惯、高数据创意、视觉/声音倾向、口播文案 DNA、互动系列、搜索关键词与用户问题、制作管线。它指导 main agent 写图文或出视频 Brief,不规定成片制作细节。维度框架 v1 位于 `xhs-style-profiler` 的 `references/account-dna-framework.md`。 +DNA 是**从一批作品样本提取并聚合出的内容生产规则集**(不存在「平台级 / 账号级 DNA」):图文 9 维——选题与观看理由、标题与封面图组、内容创意、**匹配的用户问题**、正文表达与语气、图组视觉、互动引导与转化,加账号运营子模块(简介写法、内容形式比例、发布习惯);视频 9 维——前四项加视频内容形态与制作指向、制作规格与视听倾向、口播文案子模块与账号运营子模块。搜索维度是小红书必备:最大流量池来自搜索,关键词必须落到用户可能的提问原句。DNA 指导 main agent 写图文或出视频 Brief(+ 口播文案),不规定创作细节与成片制作。维度框架 v2 位于 `xhs-style-profiler` 的 `references/note-dna-framework.md` 与 `references/video-dna-framework.md`。 -**分工硬边界**:图文和长文由 main 直接生产;已有视频素材轻加工由 main 做;视频全案只产出 Brief 并委托 `content-producer`。口播类视频若 DNA 启用口播文案 DNA,口播文案由 main 写好并随 Brief 交付;Content Producer 只负责声画制作。Brief 指定 Pipeline 时 CP 必须采用,未指定时 CP 自由发挥。 +**分工硬边界**:图文和长文由 main 直接生产;已有视频素材轻加工由 main 做;视频全案由 main 出 **Brief**(+ 口播类的口播文案 / 录音)并委托 `content-producer`,CP 只做声画实现。Brief 指定 `workflow` 时 CP 必须采用,未指定时 CP 按通用阶段链自由发挥。Brief **不含 DNA 信息**;main 不替 CP 建工作区(双方 T3 权限可互访取文件),成片与封面按 CP 回报的绝对路径取回作品目录。 ## 数据与记录 diff --git a/crews/main/skills/expert-xhs/tools/xhs-style-profiler/SKILL.md b/crews/main/skills/expert-xhs/tools/xhs-style-profiler/SKILL.md index 66e62b06..29e36a34 100644 --- a/crews/main/skills/expert-xhs/tools/xhs-style-profiler/SKILL.md +++ b/crews/main/skills/expert-xhs/tools/xhs-style-profiler/SKILL.md @@ -1,6 +1,6 @@ --- name: xhs-style-profiler -description: 提取小红书账号级 DNA:单篇笔记生成 report,聚合定位、选题、标题、图文视频比例、发布节奏、高数据创意、搜索关键词与用户问题,推导 main agent 的图文生产输入或视频 Brief template。 +description: 提取小红书作品 DNA:单篇笔记(图文 / 视频)生成 report,按 dna-id 聚合选题、标题与封面、内容创意、匹配的用户问题(搜索意图)、视频形态与制作指向、正文表达与图组视觉、口播文案子模块与账号运营子模块,推导 main agent 的图文 / Brief 生产 template。 metadata: openclaw: emoji: 🧬 @@ -8,21 +8,34 @@ metadata: # xhs-style-profiler -小红书账号级 DNA 提取与聚合工具。输入是**笔记文本材料**(标题 + 正文 + 内联话题标签,由 Agent 先整理成 `.md` / `.txt`)与可选封面图;不接受笔记链接或图片文件作为直接输入。链接先用 `xhs-content-ops` 下载,图片作为视觉证据进入。 +小红书作品 DNA 提取与聚合工具。输入是**笔记文本**(标题 + 正文 + 话题标签)或**视频转录文本**(口播全文 + 标题 / 描述 / 时长 / 互动线索),由 Agent 先整理成 `.md` / `.txt`;可选封面 / 首帧 / 配图作为视觉证据。不接受视频文件或链接作为直接输入(取数走 `viral-chaser` 或平台工具)。 -DNA 的用途是指导 main agent 图文笔记生产、搜索意图覆盖和视频制作 Brief;不指导 main agent 直接产出全片。 +DNA 的用途是指导 main agent 选题、包装、出内容或出视频制作 Brief;**不指导成片制作**——创作细节、脚本结构、镜头与编码参数归 Content Producer。 + +## 作品类型(`--kind`) + +小红书有两套维度框架,用 `--kind` 选择;**一个 `dna-id` 只承载一种作品类型**: + +| kind | 作品 | 维度框架 | 默认 dna-id | +|------|------|----------|-------------| +| `video` | 视频 | `references/video-dna-framework.md` | `dna-0` | +| `note` | 图文 | `references/note-dna-framework.md` | `dna-0-note` | + +- 不传 `--kind` 时默认 `note`(小红书的主作品类型)。 +- `build` / `update` 会校验同一 DNA 目录下 report 的 kind 一致,混型直接报错——另一种类型请另建 dna-id。 +- 默认 dna-id 只是约定:用户可以指定任意 dna-id,脚本不强制命名。 ## 产物模型 ```text -单篇笔记 -> DNA report -同一 DNA 下的全部 report + 权重/focus + 用户输入 -> DNA 文档 -DNA 文档 -> 图文生产输入 / 视频制作 Brief template +单篇作品 -> DNA report +同一 dna-id 下全部 report + 权重 / focus + 用户输入转译 -> DNA 文档 +DNA 文档 -> DNA template ``` -- **DNA report**:单篇样本的账号级观测与 14 维提取结果。 -- **DNA 文档**:聚合后的账号级规则、搜索意图地图与样本覆盖度说明。 -- **DNA template**:main agent 生成图文或委托 Content Producer 制作视频时使用的输入模板。 +- **DNA report**:单篇作品的样本观测 + 维度提取结果(不是账号级结论,也不是模板)。 +- **DNA 文档**:聚合后的生产规则、样本覆盖度、子模块结论与用户输入转译区。 +- **DNA template**:main agent 的生产输入模板(视频 = Brief 正文 + 口播文案;图文 = 写作模板)。 ## 存储结构 @@ -36,34 +49,22 @@ xhs/dna/{dna-id}/ {dna-id}.template.md ``` -原始笔记文本可临时放在 `xhs/ref/{dna-id}/notes/`;生成后的 report 必须进入目标 DNA 的 `reports/` 目录。 - -## 样本文件约定 - -```text -# 笔记标题(首个一级标题行,≤20 字) -正文第 1-2 行(开头) -正文主体段落…… - -#话题1 #话题2 #话题3 -``` - -- 首个 `# ` 一级标题行识别为标题,其余内容为正文。 -- 正文保持纯文本,不使用 markdown 小标题。 -- 话题标签保持内联 `#话题` 形式。 +原始转录 / 正文文本可临时放在 `xhs/ref/{dna-id}/transcripts/`;生成后的 report 必须进入目标 DNA 的 `reports/` 目录。 ## 职责边界 -- 单篇样本只提供候选信号;账号比例、发布节奏、高数据共性、搜索意图地图必须由多样本或账号级数据聚合。 -- 搜索关键词必须尽量落到用户可能提问;不能只写平台标签。 -- 统计只做证据底座,不评分、不替代定性判断。 -- 视觉语言有图片证据时才由视觉模型分析;缺失写「未观测」。 -- 口播文案 DNA 独立聚合;样本不足时保持未启用。 +- **搜索优先**:`search-intent`(匹配的用户问题)是小红书必备维度——最大流量池来自搜索。关键词必须尽量落到用户可能的提问原句,不只写平台标签。 +- 单篇 report 只提供候选信号,不判断跨篇稳定性;共性、偏好、孤例由聚合阶段判断。 +- 统计只做证据底座,不评分、不判定风格是否合格。 +- 视觉维度必须有图片 / 关键帧证据,由视觉模型读取;缺失写「未提供」,不得凭文本想象补齐。 +- 口播文案子模块只在口播类作品启用;样本不足写「未启用」。 +- 账号运营子模块(简介写法、内容形式比例、发布习惯)只在样本来自对标账号批量提取时填写,且**不进 template**。 - 不输出合规结论、账号权重或风格评分。 -## Report - 单篇提取 +## Report — 单篇提取 ```bash +# 图文笔记(不传 --kind,默认 note) xhs-style-profiler report \ --input path/to/note.md \ --dna-id {dna-id} \ @@ -71,67 +72,56 @@ xhs-style-profiler report \ --cover-image path/to/cover.jpg \ --source-url "https://www.xiaohongshu.com/explore/..." \ --output-dir xhs/dna/{dna-id}/reports + +# 视频笔记 +xhs-style-profiler report \ + --input path/to/transcript.md \ + --kind video \ + --dna-id {dna-id}-video \ + --sample-id {sample-id} \ + --duration 45 \ + --cover-image path/to/cover.jpg \ + --output-dir xhs/dna/{dna-id}-video/reports ``` -- `--cover-image`:封面本地图片,用于视觉证据。 -- `--source-url`:原笔记链接;本地素材无链接时省略。 +- `--kind`:作品类型(见上);不传走默认。 +- `--cover-image`:封面 / 首帧 / 首图本地文件,作为视觉证据(自动拷进 `covers/`)。 +- `--source-url`:原作品链接;本地素材无链接时省略。 +- `--duration`:视频时长(秒),用于口播密度统计;图文忽略。 - `--weight`:样本权重,默认 1。 -- `--focus`:限制该样本只影响指定维度,可重复传入。 +- `--focus`:限制该样本只影响指定维度 ID,可重复传入。 Agent 生成 scaffold 后必须: -1. 补齐「样本与账号观测」:样本类型、账号与简介、发布时间、内容形式、数据线索、搜索关键词与用户可能提问、视频形态与授权信息。 -2. 回读笔记原文,补齐 14 维的单篇结论、原文证据和可复用信号。 -3. 单篇样本无法观测账号简介、图文/视频比例、发布节奏时写「未观测」。 -4. 高数据样本必须回读创意、关键词与内容形式,不得只凭阅读量下结论。 +1. 补齐「样本观测」:作品类型、样本来源、账号与简介、发布时间、数据线索、素材来源与授权。 +2. 回读原文,补齐各维度的单篇结论、原文证据与可复用信号。 +3. 视频形态必须给出**制作指向**(Content Producer `expert-video` 的某个 workflow,或 main 的某个素材加工技能),只写真实存在的资源名。 +4. 账号运营子模块无法从单篇观测时写「未观测」。 +5. 高数据样本必须回读创意与形态再归因,不得只凭播放 / 阅读量下结论。 -## Build - 聚合 DNA 文档与模板 +## Build — 聚合 DNA 文档与模板 ```bash -xhs-style-profiler build --dna-id {dna-id} -``` - -默认读取 `xhs/dna/{dna-id}/reports/`,输出: - -```text -xhs/dna/{dna-id}/{dna-id}.dna.md -xhs/dna/{dna-id}/{dna-id}.template.md +xhs-style-profiler build --dna-id {dna-id} # 图文 DNA(默认 kind=note) +xhs-style-profiler build --dna-id {dna-id}-video --kind video # 视频 DNA ``` -也可显式传入 report 文件/目录: +默认读取 `xhs/dna/{dna-id}/reports/`,输出 `{dna-id}.dna.md` 与 `{dna-id}.template.md`。也可显式传 report 文件 / 目录: ```bash -xhs-style-profiler build \ - --input path/to/reports \ - --dna-id {dna-id} +xhs-style-profiler build --input path/to/reports --dna-id {dna-id} ``` Agent 聚合时必须: 1. 读取全部 DNA report,不能只看统计表。 2. 按 `weight` 与 `focus` 判断影响范围。 -3. 区分高频共性、高权重偏好、局部借鉴、孤例和例外。 -4. 标注样本覆盖度;单篇/少量样本不得称为稳定账号 DNA。 -5. 聚合「关键词 → 用户问题 → 内容形式」的搜索意图地图。 -6. 为每个维度写聚合结论、报告依据和可执行规则。 -7. 确保 DNA 文档能完整推导 template。 - -## DNA Template - -Template 是 main agent 的图文生产输入 / 视频制作 Brief 模板,不是成片制作模板。固定语义段: - -1. 定位与核心传达 -2. 选题与标题包装 -3. 内容形式与发布节奏 -4. 高数据创意模式 -5. 互动与系列 -6. 搜索意图与用户问题 -7. 制作交接与 Pipeline -8. 口播文案 DNA(可选) +3. 区分高覆盖共性、高权重偏好、局部借鉴、孤例与例外。 +4. 标注样本覆盖度;少量样本不得称为稳定结论。 +5. 为每个维度写聚合结论、报告依据与可执行创作规则。 +6. 确保 DNA 文档能完整推导 template(template 不得引入 DNA 文档未确认的规则)。 -图文笔记由 main agent 直接生产;视频全案只输出 Brief。制作交接段必须写清 MainAgent 与 Content Producer 的交付物、Pipeline、素材授权和风格边界。Brief 未指定 Pipeline 时,Content Producer 可自由选择;指定 Pipeline 时必须直接采用。 - -## Update - 增量聚合 +## Update — 增量聚合 ```bash xhs-style-profiler update \ @@ -140,55 +130,95 @@ xhs-style-profiler update \ --template xhs/dna/{dna-id}/{dna-id}.template.md ``` -脚本合并历史 report 与新 report,重新计算统计并保留 Agent 已完成内容。Agent 仍需重新审视聚合结论,并同步修订 DNA 文档和 template。 +脚本合并历史 report 与新 report、重算统计并保留 Agent 已写内容;kind 从 DNA 文档 frontmatter 继承(也可用 `--kind` 显式指定,冲突时报错)。Agent 仍需重新审视聚合结论并同步修订 template。 `--input` 可省略,用于只融合用户输入或另一个 DNA 的局部规则;此时必须传 `--user-input`。 +## DNA Template + +**视频作品 template(= Brief 正文模板 + 口播文案模板)** + +1. 选题 +2. 标题与封面 +3. 内容创意 +4. 关键词与用户问题 +5. 视频形态与制作指向 +6. 制作规格 +7. 口播文案 + +**图文作品 template(= 图文写作模板)** + +1. 选题 +2. 标题与封面 +3. 关键词与用户问题 +4. 内容创意与结构 +5. 正文表达 +6. 图组 +7. 互动与标签 + +- 开头两段(**选题**、**标题与封面**)跨平台通用。 +- 视频 template 的各段直接对应 Brief 正文字段;**Brief 不含 DNA 信息**(Content Producer 看不到 main 的 DNA),素材清单与授权、验收标准、闸门批准人按平台 Content Production Workflow 填。 +- 账号运营子模块不进 template,只留在 DNA 文档。 + ## 用户输入转译 -用户输入是参考信息,不是可直接入库的 DNA 规则。 +用户输入是参考信息,不是可直接入库的 DNA 规则: ```bash ---user-input "搜索流量优先,标题必须覆盖真实提问" +--user-input "以后多做实拍拼接,少用纯动画" ``` -Agent 必须转译到具体维度,例如: +Agent 必须映射到具体维度并转译为可执行规则,例如: ```text -search-intent-map:标题优先覆盖长尾提问,不只堆品类词 -title-packaging:标题采用问题句式,并保留核心关键词 -topic-portfolio:优先选择能承接搜索意图的教程/避坑选题 +search-intent:主关键词补上「AI 客服」「评论区监控」,并落到用户提问原句 +title-cover:标题偏数字型 + 痛点型,封面用大字报版式 +content-idea:多用「实测对比」创意原型 ``` 处理要求: -1. 在 DNA 文档的「用户输入转译区」记录 affected dimensions、DNA 修改和 template 修改。 +1. 在 DNA 文档「用户输入转译区」记录 raw_input、affected dimensions、DNA 修改、template 修改与状态。 2. 原话必须转译为可执行规则,不得直接抄进 template。 3. 与样本证据冲突时保留冲突说明,由用户选择优先级。 ## Focus ID +### video(视频) + +| ID | 维度 | +|---|---| +| `topic-angle` | 选题与观看理由 | +| `title-cover` | 标题与封面 | +| `content-idea` | 内容创意 | +| `search-intent` | 匹配的用户问题 | +| `video-form` | 视频内容形态与制作指向 | +| `production-spec` | 制作规格与视听倾向 | +| `narration-script` | 口播文案子DNA | +| `account-bio` | 账号简介写法 | +| `content-mix-cadence` | 内容形式比例与发布习惯 | + +### note(图文) + | ID | 维度 | |---|---| -| `positioning-core` | 定位与核心传达 | -| `topic-portfolio` | 选题组合 | -| `title-packaging` | 标题与包装 | -| `bio-profile` | 账号简介与主页表达 | -| `content-form-mix` | 内容形式与比例 | -| `publish-cadence` | 发布习惯 | -| `high-performer-patterns` | 高数据创意模式 | -| `visual-language` | 视觉语言 | -| `audio-language` | 声音语言 | -| `narration-dna` | 口播文案 DNA | -| `engagement-conversion` | 互动与转化 | -| `series-signature` | 系列与签名 | -| `search-intent-map` | 搜索意图地图 | -| `production-pipeline` | 制作管线倾向 | +| `topic-angle` | 选题与观看理由 | +| `title-cover` | 标题与封面图组 | +| `content-idea` | 内容创意 | +| `search-intent` | 匹配的用户问题 | +| `body-voice` | 正文表达与语气 | +| `imageset-visual` | 图组视觉风格 | +| `interaction-cta` | 互动引导与转化 | +| `account-bio` | 账号简介写法 | +| `content-mix-cadence` | 内容形式比例与发布习惯 | + +`--focus` 按作品类型校验:视频 report 不接受图文维度 ID,反之亦然。 ## 统计与分词 -脚本统计标题字数、句段、行数、标点、人称、emoji、话题标签等指标;中文高频信号使用相邻二字组合,仅作为候选线索。Agent 必须回读原文确认口头禅或签名式表达。分词结果不能直接作为 DNA 结论。 +脚本统计句长、问句与人称密度、感叹号密度、口播密度(视频)/ 标题字数、正文行数、emoji 密度、话题标签数(图文)等指标作为聚合证据底座;中文高频信号使用相邻二字组合,仅作候选线索。口头禅与签名式表达必须由 Agent 回读原文确认,分词结果不能直接当 DNA 结论。 ## 参考资料 -- `references/account-dna-framework.md`(小红书账号级 DNA 框架 v1、搜索意图地图、Pipeline 映射与聚合边界) +- `references/video-dna-framework.md`(小红书视频作品 DNA 框架 v2:维度定义、制作指向映射、聚合边界与 Focus ID) +- `references/note-dna-framework.md`(小红书图文作品 DNA 框架 v2:维度定义、制作指向映射、聚合边界与 Focus ID) diff --git a/crews/main/skills/expert-xhs/tools/xhs-style-profiler/references/account-dna-framework.md b/crews/main/skills/expert-xhs/tools/xhs-style-profiler/references/account-dna-framework.md deleted file mode 100644 index b08eb5b7..00000000 --- a/crews/main/skills/expert-xhs/tools/xhs-style-profiler/references/account-dna-framework.md +++ /dev/null @@ -1,86 +0,0 @@ -# 小红书账号级 DNA 框架(v1) - -> 状态:v1。Focus ID、维度命名与 template 语义以本文件为准。调整维度必须升版本,并同步 `scripts/build_style_profile.py` 与工具 `SKILL.md`。 - -## 定位与边界 - -小红书 DNA 是**账号级运营框架**,不是单篇笔记的写作模板。它回答账号长期如何被识别、如何选题、如何包装、如何安排图文/视频与发布节奏,并指导 main agent 产出图文内容或视频制作 Brief。 - -- **必须进入 DNA**:定位与核心传达、选题组合、标题与包装、账号简介、图文/视频比例、发布习惯、高数据内容创意共性、视觉/声音倾向、口播文案 DNA、互动转化、系列标记、搜索关键词与用户问题、制作管线倾向。 -- **不进入主 DNA**:逐句正文、单图构图细节、emoji 数量、镜头表、转场参数。口播文案 DNA 是独立可选块;没有证据时写「未观测」。 -- **搜索是小红书一级流量入口**:每个 DNA 样本都应尽量记录主关键词、相关词、长尾搜索句和用户可能提问;聚合后形成「关键词 → 用户问题 → 内容形式」的搜索意图地图。 -- **MainAgent / Content Producer 分界**:main agent 负责图文笔记、长文内容、已有视频素材的简单加工,以及视频全案 Brief。除 Brief 之外的成片制作由 Content Producer 执行。口播类视频的口播文案若由 DNA 明确启用,则由 main agent 写好并随 Brief 交付;Content Producer 只做声画制作。 -- **样本可以是账号或单篇**:账号样本可提取简介与发布习惯;用户提供单篇或多篇时,仍按同维度统计聚合,但账号级字段必须标「样本不足 / 未观测」。 - -## 维度(14 维) - -### 一、定位与选题 - -| # | ID | 维度 | 观测内容 | -|---|----|------|----------| -| 1 | `positioning-core` | 定位与核心传达 | 一句话定位、目标人群、长期承诺、内容主张边界 | -| 2 | `topic-portfolio` | 选题组合 | 常见选题类型、内容支柱、选题入口、搜索/推荐/分享各自服务的选题 | -| 3 | `title-packaging` | 标题与包装 | 标题模式、封面承诺、话题标签策略;不规定具体字句 | -| 4 | `bio-profile` | 账号简介与主页表达 | 昵称、简介、置顶笔记、主页视觉与承诺;仅账号样本可得 | - -### 二、账号节奏 - -| # | ID | 维度 | 观测内容 | -|---|----|------|----------| -| 5 | `content-form-mix` | 内容形式与比例 | 图文、视频、口播、实拍拼接、创意转场、纯 AIGC 动画等形式;如混合,记录比例或倾向 | -| 6 | `publish-cadence` | 发布习惯 | 发布时间段、频率、图文/视频混合节奏(如三图文对一视频)、栏目化节奏 | -| 7 | `high-performer-patterns` | 高数据创意模式 | 高阅读/收藏/搜索样本的选题、包装、关键词、形式与创意共性;数据只是证据 | - -### 三、表达与风格 - -| # | ID | 维度 | 观测内容 | -|---|----|------|----------| -| 8 | `visual-language` | 视觉语言 | 稳定画面风格、横竖屏、图组/视频视觉记忆点;不规定单图细节 | -| 9 | `audio-language` | 声音语言 | 配音类型/音色倾向、口播密度、BGM/音效倾向;不规定 TTS 参数 | -| 10 | `narration-dna` | 口播文案 DNA | 独立可选块:开头钩子、起承转合、收束、人称与语气、签名表达 | - -### 四、互动、搜索与制作 - -| # | ID | 维度 | 观测内容 | -|---|----|------|----------| -| 11 | `engagement-conversion` | 互动与转化 | 评论、收藏、关注、进店、咨询等平台内行动与承接路径 | -| 12 | `series-signature` | 系列与签名 | 栏目名、固定开场/收尾、系列承诺、识别符号 | -| 13 | `search-intent-map` | 搜索意图地图 | 主关键词、相关词、长尾句、用户可能提问、搜索意图层级与内容形式匹配 | -| 14 | `production-pipeline` | 制作管线倾向 | 视频内容形态到 Content Producer Pipeline 的映射;未指定时交给 CP 自由发挥 | - -## Pipeline 映射 - -`production-pipeline` 只能写 Content Producer 已支持的管线,不得发明不存在的管线名。 - -| DNA 观测形态 | Brief 中的 Pipeline | -|--------------|---------------------| -| 影视解说 + 反转植入 | `dna-ad-video-pipeline` | -| 口播 / 真人讲述 | `video-producer:default`(narrative) | -| 实拍拼接 / 蒙太奇 | `video-producer:default`(montage) | -| 创意转场 / 动效 | `video-producer:default`(motion) | -| 纯 AIGC 动画 | `video-producer:default`(AIGC render) | - -Brief 未写 `pipeline` 时,Content Producer 按 `video-producer:default` 自由选择实现;Brief 写了 Pipeline 时必须直接采用,不得替换成自创流程。 - -## Report 与聚合规则 - -1. 单篇 report 先记录「样本与账号观测」:样本类型、账号与简介、发布时间、内容形式、数据线索、关键词与用户问题、视频形态、授权信息。缺失写「未观测」。 -2. 单篇样本不能推导账号比例、发布节奏或高数据共性;聚合时标注覆盖度。 -3. 搜索关键词必须尽量落到用户可能提问,不只写平台标签;单篇只记候选,聚合后才形成搜索意图地图。 -4. 高数据样本必须回读创意、关键词与内容形式,不能只凭阅读量归因。 -5. 脚本统计只提供标题字数、句长、标签、emoji 等证据;语义判断由 Agent 回读原文完成。 - -## Template 语义 - -DNA template 是 main agent 的 **Brief / 内容生产输入模板**,不是成片制作模板。固定语义段: - -1. 定位与核心传达 -2. 选题与标题包装 -3. 内容形式与发布节奏 -4. 高数据创意模式 -5. 互动与系列 -6. 搜索意图与用户问题 -7. 制作交接与 Pipeline -8. 口播文案 DNA(可选) - -图文笔记由 main agent 直接生产;视频全案只到 Brief 为止,制作选择由 Content Producer 在 Pipeline 内完成。模板不得规定逐句正文、单图构图、镜头表、转场参数或编码细节。 diff --git a/crews/main/skills/expert-xhs/tools/xhs-style-profiler/references/note-dna-framework.md b/crews/main/skills/expert-xhs/tools/xhs-style-profiler/references/note-dna-framework.md new file mode 100644 index 00000000..4777081a --- /dev/null +++ b/crews/main/skills/expert-xhs/tools/xhs-style-profiler/references/note-dna-framework.md @@ -0,0 +1,105 @@ +# 小红书图文作品 DNA 框架(v2) + +> 状态:v2(2026-09-10)。Focus ID、维度命名与 template 语义以本文件为准;调整维度必须升版本,并同步 `scripts/build_style_profile.py` 与工具 `SKILL.md`。 +> 小红书有两套框架:视频作品用 `video-dna-framework.md`,本文件是**图文作品**框架。 + +## 定位与边界 + +DNA 不是「平台级 DNA」,也不是账号人设说明书,而是**从一批作品样本中提取、聚合出的内容生产规则集**。生产方式与其他平台完全一致,仍是三层产物: + +```text +单篇作品 -> DNA report(本框架的维度逐项提取) +同一 dna-id 下全部 report + 权重 / focus + 用户输入转译 -> DNA 文档 +DNA 文档 -> DNA template +``` + +- 同一批样本可以来自多个不同账号,也可以来自用户指定的一个账号(从其发布列表批量提取);是否归入同一个 DNA 由用户指定,未指定入 `dna-0`。 +- 一个 `dna-id` 只承载一种作品类型:小红书默认 dna-0 为图文,视频样本请另建 dna-id(如 dna-0-video)。 +- **与其他平台的差别只在维度**:本平台作品的观测物是标题 + 正文 + 图组 + 标签,因此不照搬公众号 17 维里的表层 / 深层写作维度(用词习惯、句式节奏、修辞手法、论证逻辑等)。 + +### 进 DNA 与不进 DNA + +| 进 DNA | 不进 DNA | +|--------|----------| +| 选题与观看理由、标题与封面写法、内容创意原型、匹配的用户问题(搜索流量)、正文表达与语气、图组视觉、互动引导与转化、(对标账号)账号运营子模块 | 创作细节、脚本结构、逐句台词、镜头表、转场与编码参数——那些归 Content Producer | + +图文 DNA 的用途是指导 main agent 直接生产图文作品;template 就是图文写作模板。账号运营子模块(简介写法、内容形式比例、发布习惯)只写进 DNA 文档,**不进 template**。 + +## 维度(9 维) + +### 一、选题与包装 + +| # | ID | 维度 | 观测内容 | +|---|----|------|----------| +| 1 | `topic-angle` | 选题与观看理由 | 选题类型、选题入口(现象 / 问题 / 冲突 / 数据 / 热点 / 挑战 / 个人经历)、目标人群为什么要看完(理解 / 判断 / 行动 / 避坑 / 身份认同 / 情绪共鸣)、系列与内容支柱归属 | +| 2 | `title-cover` | 标题与封面图组 | 标题类型与原文、封面承诺(用户点进来的可见理由)、版式(大字报 / 实拍场景 / 对比图 / 清单卡片)、色彩与文字视觉、封面与图组风格一致性、AIGC 复现要素 | + +### 二、内容创意与搜索 + +| # | ID | 维度 | 观测内容 | +|---|----|------|----------| +| 3 | `content-idea` | 内容创意 | 一句话创意内核、创意类型、展开逻辑(悬念 / 反转 / 递进 / 对比 / 清单 / 实测)、记忆点;这个套路换主题还能怎么用 | +| 4 | `search-intent` | 匹配的用户问题 | 命中的关键词(核心词 / 痛点词 / 场景词 / 人群词)、用户可能的提问原句、搜索意图层级、标签承载的搜索意图 | + +### 三、正文与视觉 + +| # | ID | 维度 | 观测内容 | +|---|----|------|----------| +| 5 | `body-voice` | 正文表达与语气 | 开头钩子、正文组织方式(清单体 / 教程步骤 / 故事线 / 对比 / 观点输出)、分行与段落节奏、口语化程度与人称、emoji 与标点用法、签名式表达 | +| 6 | `imageset-visual` | 图组视觉风格 | 图片数量与顺序、构图类型(产品展示 / 场景 / 文字卡片 / 对比图 / 过程图)、图文信息分工、色调与质感、版式一致性、文字视觉 | + +### 四、互动与转化 + +| # | ID | 维度 | 观测内容 | +|---|----|------|----------| +| 7 | `interaction-cta` | 互动引导与转化 | 评论 / 收藏 / 关注 / 进店 / 咨询等平台内行动的引导方式与位置、每篇行动引导数量、话题标签承载的意图、合规边界 | + +### 五、账号运营子模块(对标账号样本才有) + +| # | ID | 维度 | 观测内容 | +|---|----|------|----------| +| 8 | `account-bio` | 账号简介写法 | 账号昵称、简介写法、主页与置顶表达、对外承诺(仅对标账号样本可得) | +| 9 | `content-mix-cadence` | 内容形式比例与发布习惯 | 图文 / 视频等内容形式比例、发布时间段与频率、内容形式混合节奏(如三篇图文对一篇视频)、栏目化节奏(仅对标账号批量样本可得) | + +## Report 与聚合规则 + +1. 单篇 report 先填「样本观测」:作品类型、样本来源、账号与简介、发布时间、数据线索、图片数量与来源、关键词与标签。缺失一律写「未观测」,不得编造。 +2. 单篇 report 不判断跨篇稳定性;聚合时区分高覆盖共性、高权重样本偏好、局部借鉴(focus)、孤例与例外,并标注样本覆盖度。 +3. 视觉维度(封面 / 首帧 / 图组)必须有图片证据,由视觉模型读取本地图片后反推 AIGC 复现要素;没有图片写「未提供」。 +4. 正文表达维度里的高频词、口头禅与签名表达只是候选线索,必须回读原文确认后才能进 DNA 文档。 +5. 账号运营子模块(`account-bio`、`content-mix-cadence`)只在样本来自对标账号批量提取时填写;用户只提供单篇时写「未观测」。 +6. 脚本统计只作证据底座(标题字数、正文行数、句长、问句密度、emoji 密度、话题标签数),不生成总分、不判定风格是否合格。 +7. 关键词必须尽量落到**用户可能的提问原句**,不只写平台标签;单篇只记候选,聚合后形成「关键词 → 用户问题 → 内容形式」的搜索意图地图(小红书最大流量池来自搜索)。 +8. 高数据样本必须回读创意、结构与关键词再归因,不得把高阅读直接等同于风格好。 + +## Template 语义 + +DNA template = **图文写作模板**,固定语义段如下(脚本 `build` / `update` 自动生成骨架,字段由 Agent 从 DNA 文档推导填写): + +1. **[选题部分]** — 选题角度推荐、选题需考虑的受众关联角度、内容支柱与系列关系、避免 +2. **[标题与封面部分]** — 标题类型、参考标题、封面或首帧风格、封面 AIGC 提示词要素、话题标签策略 +3. **[关键词与用户问题部分]** — 主关键词、相关词与长尾句、用户可能的提问、搜索意图与内容形式匹配、标签策略 +4. **[内容创意与结构部分]** — 创意原型、正文组织方式、信息密度、记忆点、避免 +5. **[正文表达部分]** — 开头钩子、推进方式、段落与分行节奏、人称与语气、emoji 与标点、签名式表达、必须做、避免 +6. **[图组部分]** — 图片数量与顺序、构图类型、色调与质感、版式一致性、文字视觉、AIGC 提示词要素 +7. **[互动与标签部分]** — 互动目标、引导方式、话题标签策略、合规边界 + +- 开头两段(**选题**、**标题与封面**)跨平台通用:任何生产都先解决「做什么」和「怎么命名、怎么呈现封面」。 +- `[图组部分]` 与 `[正文表达部分]` 是两条并行的生产轨道:图组规则管视觉,正文规则管文字。 +- 账号运营子模块不进 template:`account-bio` 与 `content-mix-cadence` 的结论只留在 DNA 文档,供起号、对标与发布节奏决策使用。 +- template 必须能从 DNA 文档推导,不得引入 DNA 文档未确认的规则;用户输入必须先映射到维度再转译为执行规则。 +- 分段数量由 DNA 文档的结构结论决定,不固定为三段或四段;固定的是语义部分,不是物理段落数量。 + +## Focus ID 表 + +`--focus` 只接受本框架的维度 ID;限制某篇样本只影响这些维度。 + +| 分组 | Focus ID | +|------|----------| +| 选题与包装 | `topic-angle` `title-cover` | +| 内容创意与搜索 | `content-idea` `search-intent` | +| 正文与视觉 | `body-voice` `imageset-visual` | +| 互动与转化 | `interaction-cta` | +| 账号运营子模块(对标账号样本才有) | `account-bio` `content-mix-cadence` | + +focus 校验按作品类型执行:视频 report 不接受图文维度 ID,反之亦然。 diff --git a/crews/main/skills/expert-xhs/tools/xhs-style-profiler/references/video-dna-framework.md b/crews/main/skills/expert-xhs/tools/xhs-style-profiler/references/video-dna-framework.md new file mode 100644 index 00000000..01fa489f --- /dev/null +++ b/crews/main/skills/expert-xhs/tools/xhs-style-profiler/references/video-dna-framework.md @@ -0,0 +1,120 @@ +# 小红书视频作品 DNA 框架(v2) + +> 状态:v2(2026-09-10)。Focus ID、维度命名与 template 语义以本文件为准;调整维度必须升版本,并同步 `scripts/build_style_profile.py` 与工具 `SKILL.md`。 +> 小红书有两套框架:图文作品用 `note-dna-framework.md`,本文件是**视频作品**框架。 + +## 定位与边界 + +DNA 不是「平台级 DNA」,也不是账号人设说明书,而是**从一批作品样本中提取、聚合出的内容生产规则集**。生产方式与其他平台完全一致,仍是三层产物: + +```text +单篇作品 -> DNA report(本框架的维度逐项提取) +同一 dna-id 下全部 report + 权重 / focus + 用户输入转译 -> DNA 文档 +DNA 文档 -> DNA template +``` + +- 同一批样本可以来自多个不同账号,也可以来自用户指定的一个账号(从其发布列表批量提取);是否归入同一个 DNA 由用户指定,未指定入 `dna-0`。 +- 一个 `dna-id` 只承载一种作品类型:小红书默认 dna-0 为图文,视频样本请另建 dna-id(如 dna-0-video)。 +- **与其他平台的差别只在维度**:本平台作品的观测物是口播转录文本 + 画面 + 声音 + 结构节奏,因此不照搬公众号 17 维里的表层 / 深层写作维度(用词习惯、句式节奏、修辞手法、论证逻辑等)。 + +### 进 DNA 与不进 DNA + +| 进 DNA | 不进 DNA | +|--------|----------| +| 选题与观看理由、标题与封面写法、内容创意原型、匹配的用户问题(搜索流量)、视频内容形态与制作指向、制作规格与视听倾向、(口播类)口播文案子DNA、(对标账号)账号运营子模块 | 创作细节、脚本结构、逐句台词、镜头表、转场与编码参数——那些归 Content Producer | + +视频 DNA 的用途是指导 main agent 出具 **Brief.md** 与(口播类的)**口播文案**。template 就是 Brief 正文模板 + 口播文案模板(可选),不是成片制作模板。账号运营子模块(简介写法、内容形式比例、发布习惯)只写进 DNA 文档,**不进 template**。 + +## 维度(9 维) + +### 一、选题与包装 + +| # | ID | 维度 | 观测内容 | +|---|----|------|----------| +| 1 | `topic-angle` | 选题与观看理由 | 选题类型、选题入口(现象 / 问题 / 冲突 / 数据 / 热点 / 挑战 / 个人经历)、目标人群为什么要看完(理解 / 判断 / 行动 / 避坑 / 身份认同 / 情绪共鸣)、系列与内容支柱归属 | +| 2 | `title-cover` | 标题与封面 | 标题类型(痛点 / 数字 / 反差 / 悬念 / 身份点名 / 搜索长尾)、标题与描述原文、话题标签策略;封面或首帧的视觉特征与 AIGC 复现要素 | + +### 二、内容创意与搜索 + +| # | ID | 维度 | 观测内容 | +|---|----|------|----------| +| 3 | `content-idea` | 内容创意 | 一句话创意内核、创意类型、展开逻辑(悬念 / 反转 / 递进 / 对比 / 清单 / 实测)、记忆点;这个套路换主题还能怎么用 | +| 4 | `search-intent` | 匹配的用户问题 | 命中的关键词(核心词 / 痛点词 / 场景词 / 人群词)、用户可能的提问原句、搜索意图层级、标签承载的搜索意图 | + +### 三、形态与规格 + +| # | ID | 维度 | 观测内容 | +|---|----|------|----------| +| 5 | `video-form` | 视频内容形态与制作指向 | 视频内容形态(口播 / 实拍拼接 / 影视解说+反转植入 / 纯 AIGC 动画 / 创意转场动效 / 录屏演示 / 图文卡片视频 / 混合)+ 判定依据 + **制作指向**(见下方映射表) | +| 6 | `production-spec` | 制作规格与视听倾向 | 横屏或竖屏、时长带、画面风格(色调、质感、字幕样式倾向、信息密度)、配音音色与声音形态(原声口播 / TTS / 旁白 / 纯画面字幕)、BGM 与音效倾向、封面规格 | + +### 四、口播文案子模块(可选,仅口播类启用) + +| # | ID | 维度 | 观测内容 | +|---|----|------|----------| +| 7 | `narration-script` | 口播文案子DNA | 起(从什么起步)、承(靠什么推进)、转(转折触发)、合(收束与 CTA)、人称与语气、句长与语速、签名式表达——用于指导 main agent 写同类型视频的口播文案 | + +### 五、账号运营子模块(对标账号样本才有) + +| # | ID | 维度 | 观测内容 | +|---|----|------|----------| +| 8 | `account-bio` | 账号简介写法 | 账号昵称、简介写法、主页与置顶表达、对外承诺(仅对标账号样本可得) | +| 9 | `content-mix-cadence` | 内容形式比例与发布习惯 | 图文 / 视频等内容形式比例、发布时间段与频率、内容形式混合节奏(如三篇图文对一篇视频)、栏目化节奏(仅对标账号批量样本可得) | + +## Report 与聚合规则 + +1. 单篇 report 先填「样本观测」:作品类型、样本来源、账号与简介、发布时间、数据线索、横竖屏与时长、素材来源与授权、转录与关键帧来源。缺失一律写「未观测」,不得编造。 +2. 单篇 report 不判断跨篇稳定性;聚合时区分高覆盖共性、高权重样本偏好、局部借鉴(focus)、孤例与例外,并标注样本覆盖度。 +3. 视觉维度(封面 / 首帧 / 图组)必须有图片证据,由视觉模型读取本地图片后反推 AIGC 复现要素;没有图片写「未提供」。 +4. 口播文案子模块只在口播类作品启用;非口播类或样本不足写「未启用 / 未观测」,不得把单篇句式上升为规则。 +5. 账号运营子模块(`account-bio`、`content-mix-cadence`)只在样本来自对标账号批量提取时填写;用户只提供单篇时写「未观测」。 +6. 脚本统计只作证据底座(句长、问句密度、人称密度、感叹号密度、口播密度),不生成总分、不判定风格是否合格。 +7. 关键词必须尽量落到**用户可能的提问原句**,不只写平台标签;单篇只记候选,聚合后形成「关键词 → 用户问题 → 内容形式」的搜索意图地图(小红书最大流量池来自搜索)。 +8. 高数据样本必须回读创意、形态与包装再归因,不得把高播放直接等同于风格好。 + +## Template 语义 + +DNA template = **Brief.md 正文模板 + 口播文案模板(可选)**,固定语义段如下(脚本 `build` / `update` 自动生成骨架,字段由 Agent 从 DNA 文档推导填写): + +1. **[选题部分]** — 选题角度推荐、选题需考虑的受众关联角度、内容支柱与系列关系、避免 +2. **[标题与封面部分]** — 标题类型、参考标题、封面或首帧风格、封面 AIGC 提示词要素、话题标签策略 +3. **[内容创意部分]** — 创意原型、展开逻辑、记忆点与反转设计、触发条件、避免 +4. **[关键词与用户问题部分]** — 主关键词、相关词与长尾句、用户可能的提问、搜索意图与内容形式匹配、标签策略 +5. **[视频形态与制作指向部分]** — 视频内容形态、制作指向、委托边界、未指定形态时 +6. **[制作规格部分]** — 横屏或竖屏、时长带、画面风格、配音音色与声音形态、BGM 与音效、字幕 +7. **[口播文案部分]** — 是否启用、起、承、转、合、人称与语气、句长与语速、签名式表达、必须做、避免 + +- 开头两段(**选题**、**标题与封面**)跨平台通用:任何生产都先解决「做什么」和「怎么命名、怎么呈现封面」。 +- `[视频形态与制作指向部分]` 的「制作指向」必须写下方映射表里的真实资源名,Brief 的 `workflow` 字段据此填写。 +- 账号运营子模块不进 template:`account-bio` 与 `content-mix-cadence` 的结论只留在 DNA 文档,供起号、对标与发布节奏决策使用。 +- template 必须能从 DNA 文档推导,不得引入 DNA 文档未确认的规则;用户输入必须先映射到维度再转译为执行规则。 + +## 制作指向映射 + +`video-form` 维度提炼出的内容形态,必须能明确指向后续用什么技能制作: + +| DNA 观测到的内容形态 | 制作指向(只能写真实存在的资源名) | Brief `workflow` 字段值 | +|---------------------|-----------------------------------|------------------------| +| 影视解说 / 剧情解说 + 反转植入(「万万没想到」式) | Content Producer `expert-video` → **Reversal Ad** workflow | `reversal-ad` | +| 口播类(真人口播出镜,或旁白 + 画面) | Content Producer `expert-video` → **Narration Video** workflow | `narration-video` | +| 一句文稿转视觉隐喻的纸拼贴动画 | Content Producer `expert-video` → **Collage B-roll** workflow | `collage-broll` | +| 纯 AIGC 动画 / 剧情短片 / 蒙太奇拼接(需从零出脚本分镜) | Content Producer `expert-video` → 通用阶段链(narrative / motion / montage 档) | 省略(由 CP 定档位) | +| 已有素材简单拼接、加旁白、烧字幕 | main `video-edit`(不委托 CP) | — | +| 已有真人口播素材去口气词、剪高光 | main `talking-head-cut`(不委托 CP) | — | +| 产品操作录屏 | main `ui-demo`(不委托 CP) | — | + +Brief 写了 `workflow` 时 Content Producer 必须直接采用,不得替换成自创流程;未写时按通用阶段链自由选择实现。 + +## Focus ID 表 + +`--focus` 只接受本框架的维度 ID;限制某篇样本只影响这些维度。 + +| 分组 | Focus ID | +|------|----------| +| 选题与包装 | `topic-angle` `title-cover` | +| 内容创意与搜索 | `content-idea` `search-intent` | +| 形态与规格 | `video-form` `production-spec` | +| 口播文案子模块(可选,仅口播类启用) | `narration-script` | +| 账号运营子模块(对标账号样本才有) | `account-bio` `content-mix-cadence` | + +focus 校验按作品类型执行:视频 report 不接受图文维度 ID,反之亦然。 diff --git a/crews/main/skills/expert-xhs/tools/xhs-style-profiler/scripts/build_style_profile.py b/crews/main/skills/expert-xhs/tools/xhs-style-profiler/scripts/build_style_profile.py index f93cb203..3b4c6170 100755 --- a/crews/main/skills/expert-xhs/tools/xhs-style-profiler/scripts/build_style_profile.py +++ b/crews/main/skills/expert-xhs/tools/xhs-style-profiler/scripts/build_style_profile.py @@ -1,4 +1,14 @@ #!/usr/bin/env python3 +"""xhs-style-profiler:按作品类型(video / note)生成定性 DNA 资产。 + +三层产物(与 docs/expert-pack-dna-architecture.md 4.6 一致): + 单篇作品 -> DNA report + 同一 DNA 目录下全部 report + 权重/focus + 用户输入转译 -> DNA 文档 + DNA 文档 -> DNA template + +维度框架见本工具 references/ 下的 FRAMEWORK 文档(视频 / 图文各一份)。 +脚本只做 scaffold 与统计证据底座:不评分、不判定风格合格,定性结论由 Agent 回读原文补齐。 +""" import argparse, json, math, re, shutil from collections import Counter, defaultdict from datetime import datetime, timezone @@ -7,6 +17,7 @@ SENTENCE_SPLIT = re.compile(r"[。!?!?]+") +PARAGRAPH_SPLIT = re.compile(r"\n\s*\n") TOKEN_RE = re.compile(r"[一-鿿A-Za-z0-9_]+") ENGLISH_WORD_RE = re.compile(r"[A-Za-z0-9_]+") SECOND_PERSON_RE = re.compile(r"你们?|you", re.IGNORECASE) @@ -16,7 +27,7 @@ ID_RE = re.compile(r"^[a-z0-9][a-z0-9-]{1,63}$") SOURCE_BLOCK_RE = re.compile(r"", re.DOTALL) REPORT_BLOCK_RE = re.compile(r"", re.DOTALL) -# 小红书正文内联话题标签:#话题 (样本文件约定正文为纯文本,行首 # 只出现在标题行) +# 图文正文内联话题标签:#话题(样本文件约定正文为纯文本,行首 # 只出现在标题行) TAG_RE = re.compile(r"(?m)(?:^|(?<=\s))#[^\s#]\S*") EMOJI_RE = re.compile( "[" @@ -37,70 +48,185 @@ "]" ) -STATISTICS_METRICS = [ +# ── CONFIG-BEGIN ── +PLATFORM = "xhs" + +PLATFORM_LABEL = "小红书" + +PLATFORM_DESC = "小红书图文笔记与视频笔记" + +KINDS = ("video", "note") + +DEFAULT_KIND = "note" + +KIND_LABELS = {"video": "视频作品", "note": "图文作品"} + +KIND_SUFFIX_HINT = "小红书默认 dna-0 为图文,视频样本请另建 dna-id(如 dna-0-video)" + +FRAMEWORK_DOCS = {"video": "video-dna-framework.md", "note": "note-dna-framework.md"} + +# DNA 维度 v2(按作品类型分框架)。调整维度必须升版本,并同步 references/ 下的框架文档与工具 SKILL.md 的 Focus ID 表。 +DIMENSION_GROUPS = { + "video": { + "选题与包装": [("topic-angle", "选题与观看理由"), ("title-cover", "标题与封面")], + "内容创意与搜索": [("content-idea", "内容创意"), ("search-intent", "匹配的用户问题")], + "形态与规格": [("video-form", "视频内容形态与制作指向"), ("production-spec", "制作规格与视听倾向")], + "口播文案子模块(可选,仅口播类启用)": [("narration-script", "口播文案子DNA")], + "账号运营子模块(对标账号样本才有)": [("account-bio", "账号简介写法"), ("content-mix-cadence", "内容形式比例与发布习惯")], + }, + "note": { + "选题与包装": [("topic-angle", "选题与观看理由"), ("title-cover", "标题与封面图组")], + "内容创意与搜索": [("content-idea", "内容创意"), ("search-intent", "匹配的用户问题")], + "正文与视觉": [("body-voice", "正文表达与语气"), ("imageset-visual", "图组视觉风格")], + "互动与转化": [("interaction-cta", "互动引导与转化")], + "账号运营子模块(对标账号样本才有)": [("account-bio", "账号简介写法"), ("content-mix-cadence", "内容形式比例与发布习惯")], + }, +} + +STATISTICS_METRICS = { + "video": [ + "avg_sentence_tokens", + "question_density_per_100_sentences", + "second_person_density_per_100_sentences", + "exclamation_density_per_1000_characters", + "speech_chars_per_minute", + ], + "note": [ + "title_chars", + "line_count", + "avg_sentence_tokens", + "question_density_per_100_sentences", + "second_person_density_per_100_sentences", + "emoji_density_per_100_characters", + "tag_count", + ], +} + +REPORT_DIMENSION_PROMPTS = { + "topic-angle": "- 单篇观测:选题类型、选题入口(现象 / 问题 / 冲突 / 数据 / 热点 / 挑战 / 个人经历)、目标人群为什么要看完(理解 / 判断 / 行动 / 避坑 / 身份认同 / 情绪共鸣)。\n- 边界:只记本篇,不判断跨篇稳定性;不评价选题好坏。", + "title-cover": "- 单篇观测:标题类型(痛点 / 数字 / 反差 / 悬念 / 身份点名 / 搜索长尾)、标题与描述原文、话题标签策略。\n- 视觉证据:封面或首帧必须由视觉模型读取图片,至少提取画面主体与场景、构图与画幅、色彩体系、光线与质感、风格与媒介、文字视觉与图文关系、品牌识别元素、避免项,并反推为可执行的 AIGC 提示词要素;无图片写「未提供」,不得凭正文或标题想象补齐。", + "content-idea": "- 单篇观测:一句话创意内核、创意类型、展开逻辑(悬念 / 反转 / 递进 / 对比 / 清单 / 实测)、记忆点。\n- 可复用信号:这个创意套路换成别的主题还能怎么用。\n- 边界:只记创意层,不记创作细节(逐句台词、镜头表、脚本结构、转场与编码参数)。", + "search-intent": "- 单篇观测:本篇命中的关键词(核心词 / 痛点词 / 场景词 / 人群词)、用户可能的提问原句、搜索意图层级、标签承载的搜索意图。\n- 边界:单篇只记候选;聚合后才形成「关键词 → 用户问题 → 内容形式」的搜索意图地图。", + "video-form": "- 单篇观测:视频内容形态(口播 / 实拍拼接 / 影视解说+反转植入 / 纯 AIGC 动画 / 创意转场动效 / 录屏演示 / 图文卡片视频 / 混合)与判定依据(画面证据、口播占比、素材来源)。\n- 制作指向:必须落到真实存在的资源名——Content Producer `expert-video` 的某个 workflow(Reversal Ad / Narration Video / Collage B-roll / 通用阶段链),或 main 的素材加工技能(`video-edit` / `talking-head-cut` / `ui-demo`);不得发明不存在的名字。", + "production-spec": "- 单篇观测:横屏或竖屏、时长带、画面风格(色调、质感、字幕样式倾向、信息密度)、配音音色与声音形态(原声口播 / TTS / 旁白 / 纯画面字幕)、BGM 与音效倾向、封面规格。\n- 边界:只记规格与倾向,不规定镜头参数、逐镜设计、转场与编码细节——那些归 Content Producer。", + "narration-script": "- 子模块(仅口播类作品启用):起(从什么起步)、承(靠什么推进)、转(转折触发)、合(收束与 CTA),以及人称与语气、句长与语速、签名式表达。\n- 边界:非口播类或证据不足时写「未启用 / 未观测」;不得把单篇句式直接上升为规则。", + "body-voice": "- 单篇观测:开头钩子(原文摘录)、正文组织方式(清单体 / 教程步骤 / 故事线 / 对比 / 观点输出)、分行与段落节奏、口语化程度与人称、emoji 与标点用法、签名式表达。\n- 证据边界:脚本统计只给句长 / 行数 / emoji / 标签等线索;口头禅与签名表达必须回读原文确认。", + "imageset-visual": "- 单篇观测:图片数量与顺序、构图类型(产品展示 / 场景 / 文字卡片 / 对比图 / 过程图)、图文信息分工、色调与质感、版式一致性、文字视觉。\n- 视觉证据:必须由视觉模型读取本地图片并反推 AIGC 复现要素;无图片写「未提供」,不得凭正文想象补齐。", + "interaction-cta": "- 单篇观测:评论 / 收藏 / 关注 / 进店 / 咨询等平台内行动的引导方式与位置、每篇行动引导数量、话题标签承载的意图。\n- 合规边界:不隐藏站外联系方式、不绕平台检测、不以利益换互动,记为必须避免项。", + "account-bio": "- 子模块(仅对标账号样本可得):账号昵称、简介写法、主页与置顶表达、对外承诺。\n- 边界:用户提供的单篇样本无法观测时写「未观测」,不得推导。", + "content-mix-cadence": "- 子模块(仅对标账号批量样本可得):图文 / 视频等内容形式比例、发布时间段与频率、内容形式混合节奏(如三篇图文对一篇视频)、栏目化节奏。\n- 边界:必须由账号发布列表的批量样本推导;单篇样本只记本篇发布时间。", +} + +REPORT_OBSERVATION_PROMPTS = { + "video": "- 作品类型:视频(本框架只用于视频作品;图文样本走 note-dna-framework)。\n- 样本来源:待 Agent 补齐(对标账号批量作品 / 用户提供的单篇或多篇 / 用户想法转译)。\n- 账号与简介:待 Agent 补齐;非账号样本写未观测。\n- 发布时间与时间段:待 Agent 补齐;单篇只记本篇时间,不推导账号节奏。\n- 数据线索:待 Agent 补齐(播放 / 点赞 / 评论 / 分享 / 收藏);只作证据,不直接判风格好坏。\n- 横竖屏与时长:待 Agent 补齐。\n- 素材来源与授权:待 Agent 补齐(实拍 / 影视或开源片源 / AIGC / 录屏 / 混合)。\n- 转录与关键帧来源:待 Agent 补齐(如 main 的 viral-chaser 产物路径);缺失写未提供,不得编造。", + "note": "- 作品类型:图文(本框架只用于图文作品;视频样本走 video-dna-framework)。\n- 样本来源:待 Agent 补齐(对标账号批量作品 / 用户提供的单篇或多篇 / 用户想法转译)。\n- 账号与简介:待 Agent 补齐;非账号样本写未观测。\n- 发布时间与时间段:待 Agent 补齐;单篇只记本篇时间,不推导账号节奏。\n- 数据线索:待 Agent 补齐(阅读 / 点赞 / 收藏 / 评论 / 分享);只作证据,不直接判风格好坏。\n- 图片数量与来源:待 Agent 补齐(封面 + 配图张数、实拍 / 截图 / AIGC)。\n- 关键词与标签:待 Agent 补齐(标题、正文与标签区原样记录)。", +} + +TEMPLATE_STAGES = { + "video": ("选题", "标题与封面", "内容创意", "关键词与用户问题", "视频形态与制作指向", "制作规格", "口播文案"), + "note": ("选题", "标题与封面", "关键词与用户问题", "内容创意与结构", "正文表达", "图组", "互动与标签"), +} + +TEMPLATE_STAGE_FIELDS = { + "选题": ("选题角度推荐", "选题需考虑的受众关联角度", "内容支柱与系列关系", "避免"), + "标题与封面": ("标题类型", "参考标题", "封面或首帧风格", "封面 AIGC 提示词要素", "话题标签策略"), + "内容创意": ("创意原型", "展开逻辑", "记忆点与反转设计", "触发条件", "避免"), + "关键词与用户问题": ("主关键词", "相关词与长尾句", "用户可能的提问", "搜索意图与内容形式匹配", "标签策略"), + "视频形态与制作指向": ("视频内容形态", "制作指向", "委托边界", "未指定形态时"), + "制作规格": ("横屏或竖屏", "时长带", "画面风格", "配音音色与声音形态", "BGM 与音效", "字幕"), + "口播文案": ("是否启用", "起", "承", "转", "合", "人称与语气", "句长与语速", "签名式表达", "必须做", "避免"), + "内容创意与结构": ("创意原型", "正文组织方式", "信息密度", "记忆点", "避免"), + "正文表达": ("开头钩子", "推进方式", "段落与分行节奏", "人称与语气", "emoji 与标点", "签名式表达", "必须做", "避免"), + "图组": ("图片数量与顺序", "构图类型", "色调与质感", "版式一致性", "文字视觉", "AIGC 提示词要素"), + "互动与标签": ("互动目标", "引导方式", "话题标签策略", "合规边界"), +} + +TEMPLATE_INTROS = { + "video": "本模板是 main agent 出具**视频制作 Brief(brief.md)**与(口播类)**口播文案**的输入模板,必须由 DNA 文档推导,不得引入 DNA 文档未确认的规则。\n\n- 下列各段直接对应 Brief 的正文字段;Brief 的其余字段(素材清单与授权、验收标准、闸门批准人)由平台 Content Production Workflow 规定。\n- Brief **不含 DNA 信息**:Content Producer 看不到 main 的 DNA,只按 Brief 制作。\n- 模板不规定创作细节:逐句台词、镜头表、转场与编码参数归 Content Producer。\n- 账号运营子模块(简介写法、内容形式比例、发布习惯)写在 DNA 文档,不进本模板。", + "note": "本模板是 main agent 直接生产**图文作品**的写作模板,必须由 DNA 文档推导,不得引入 DNA 文档未确认的规则。\n\n- 开头两段(选题、标题与封面)跨平台通用。\n- 固定的是语义部分,不是物理段落数量:任一部分可对应一个或多个自然段,也可略过。\n- 每条规则必须能从 DNA 文档的聚合结论推导,避免「专业」「亲切」这类空泛形容词。\n- 账号运营子模块写在 DNA 文档,不进本模板。", +} + +TEMPLATE_CHECKLISTS = { + "video": "- 是否只用一个 DNA,且作品类型与该 DNA 的 `kind` 一致。\n- 选题、标题 / 描述、封面是否来自 DNA 文档。\n- 内容创意是否落到可复用的创意原型,而不是照抄样本主题。\n- 视频形态是否明确指向 Content Producer `expert-video` 的某个 workflow,或 main 的某个素材加工技能。\n- Brief 是否只含制作所需信息(不含 DNA 内容),素材是否给了绝对路径与授权说明。\n- 口播类是否附口播文案(或真人口播录音路径);口播子模块未启用时是否避免规定逐句口播。\n- 制作规格(横竖屏、时长带、画面风格、配音音色)是否尊重样本覆盖度;样本不足时是否标注未观测。\n- 关键词是否落到用户可能的提问原句(不只平台标签),并与内容形式匹配。\n- 用户输入是否已转译为具体执行规则。", + "note": "- 是否只用一个 DNA,且作品类型与该 DNA 的 `kind` 一致。\n- 选题、标题与封面图组是否来自 DNA 文档。\n- 正文表达的每条规则是否可从 DNA 文档推导,未使用空泛形容词。\n- 图组数量、构图与视觉风格是否与 DNA 一致;视觉结论是否有图片证据。\n- 互动引导是否每篇只放一个行动,且不越合规红线。\n- 关键词是否落到用户可能的提问原句(不只平台标签),并与内容形式匹配。\n- 用户输入是否已转译为具体执行规则。", +} + +DNA_SUBMODULE_NOTE = "- **口播文案子模块**(`narration-script`):仅口播类视频启用,用于指导 main agent 写同类型视频的口播文案;它不是独立 DNA,未启用时写「未启用」。\n- **账号运营子模块**(`account-bio`、`content-mix-cadence`):只在样本来自用户提供的对标账号(可从账号发布列表批量提取)时填写;结论只写进本 DNA 文档,不进 template;样本不足写「未观测」。" +# ── CONFIG-END ── + +STATISTICS_METRICS_ALL = [ "title_chars", - "avg_sentence_tokens", "line_count", + "paragraphs", + "avg_paragraph_tokens", + "avg_sentence_tokens", "question_density_per_100_sentences", "second_person_density_per_100_sentences", "first_person_density_per_100_sentences", "exclamation_density_per_1000_characters", - "emoji_count", + "speech_chars_per_minute", "emoji_density_per_100_characters", "tag_count", ] +METRIC_LABELS = { + "title_chars": "标题字数", + "line_count": "正文行数", + "paragraphs": "段落数", + "avg_paragraph_tokens": "平均每段长度(token)", + "avg_sentence_tokens": "平均句长(token)", + "question_density_per_100_sentences": "问句密度 / 百句", + "second_person_density_per_100_sentences": "第二人称密度 / 百句", + "first_person_density_per_100_sentences": "第一人称密度 / 百句", + "exclamation_density_per_1000_characters": "感叹号密度 / 千字", + "speech_chars_per_minute": "口播密度(字/分钟)", + "emoji_density_per_100_characters": "emoji 密度 / 百字", + "tag_count": "话题标签数", +} + STOP_TERMS = { "一个", "我们", "你们", "这里", "不会", "这个", "那个", "什么", "可以", "因为", "但是", "所以", "还是", "以及", "如果", "他们", "自己", "的时候", "to", "the", "a", "an", "is", "are", "and", "or", "of", "in", "for", "on", "with", "you", "we", } -DIMENSION_GROUPS = { - "定位与选题": [ - ("positioning-core", "定位与核心传达"), - ("topic-portfolio", "选题组合"), - ("title-packaging", "标题与包装"), - ("bio-profile", "账号简介与主页表达"), - ], - "账号节奏": [ - ("content-form-mix", "内容形式与比例"), - ("publish-cadence", "发布习惯"), - ("high-performer-patterns", "高数据创意模式"), - ], - "表达与风格": [ - ("visual-language", "视觉语言"), - ("audio-language", "声音语言"), - ("narration-dna", "口播文案 DNA"), - ], - "互动、搜索与制作": [ - ("engagement-conversion", "互动与转化"), - ("series-signature", "系列与签名"), - ("search-intent-map", "搜索意图地图"), - ("production-pipeline", "制作管线倾向"), - ], -} -DIMENSIONS = [] -number = 1 -for group, dimensions in DIMENSION_GROUPS.items(): - for dimension_id, name in dimensions: - DIMENSIONS.append( - {"id": dimension_id, "number": number, "name": name, "group": group} - ) - number += 1 + +def dimensions_for(kind: str) -> list[dict]: + """把某个作品类型的分组配置摊平成带序号的维度列表。""" + dimensions = [] + number = 1 + for group, items in DIMENSION_GROUPS[kind].items(): + for dimension_id, name in items: + dimensions.append({"id": dimension_id, "number": number, "name": name, "group": group}) + number += 1 + return dimensions + + +def statistics_metrics(kind: str) -> list[str]: + wanted = STATISTICS_METRICS[kind] + return [metric for metric in STATISTICS_METRICS_ALL if metric in wanted] + def split_sentences(text: str) -> list[str]: return [item.strip() for item in SENTENCE_SPLIT.split(text) if item.strip()] + +def split_paragraphs(text: str) -> list[str]: + return [item.strip() for item in PARAGRAPH_SPLIT.split(text) if item.strip()] + + def tokenize(text: str) -> list[str]: return TOKEN_RE.findall(text) + def rounded(value: float) -> float: return round(value, 4) -def safe_ratio(numerator: int, denominator: int, multiplier: float = 1) -> float: + +def safe_ratio(numerator: int, denominator: float, multiplier: float = 1) -> float: return rounded((numerator / denominator) * multiplier) if denominator else 0.0 + def average(values: list[float]) -> float: return rounded(sum(values) / len(values)) if values else 0.0 @@ -124,7 +250,7 @@ def extract_terms(text: str) -> Counter: def split_title_body(text: str) -> tuple[str, str]: - """样本文件约定:首个一级标题行为笔记标题,其余为正文(含内联 #话题)。""" + """样本文件约定:首个一级标题行为作品标题,其余为正文(图文正文可含内联 #话题)。""" title = "" body_lines = [] for line in text.splitlines(): @@ -133,8 +259,7 @@ def split_title_body(text: str) -> tuple[str, str]: title = stripped[2:].strip() continue body_lines.append(line) - body = "\n".join(body_lines).strip() - return title, body + return title, "\n".join(body_lines).strip() def title_candidates(path: Path, text: str) -> list[str]: @@ -145,41 +270,38 @@ def title_candidates(path: Path, text: str) -> list[str]: return candidates -def document_metrics(path: Path, text: str) -> dict: +def document_metrics(path: Path, text: str, duration: float = 0.0) -> dict: + """统计证据底座:视频与图文共用一套超集,展示哪些指标由 STATISTICS_METRICS[kind] 决定。""" title, body = split_title_body(text) sentences = split_sentences(text) sentence_lengths = [len(tokenize(sentence)) for sentence in sentences] sentence_count = len(sentences) character_count = len(text) body_lines = [line for line in body.splitlines() if line.strip()] + paragraphs = split_paragraphs(body) + paragraph_lengths = [len(tokenize(paragraph)) for paragraph in paragraphs] emoji_count = len(EMOJI_RE.findall(body)) - tag_count = len(TAG_RE.findall(body)) + speech_chars_per_minute = rounded(character_count / duration * 60) if duration > 0 else 0.0 return { "source_transcript": str(path.resolve()), "title_candidates": title_candidates(path, text), "characters": character_count, "sentences": sentence_count, + "duration": duration, "title_chars": len(title), "line_count": len(body_lines), + "paragraphs": len(paragraphs), + "avg_paragraph_tokens": average([float(value) for value in paragraph_lengths]), "avg_sentence_tokens": average([float(value) for value in sentence_lengths]), - "question_density_per_100_sentences": safe_ratio( - len(QUESTION_RE.findall(text)), sentence_count, 100 - ), - "second_person_density_per_100_sentences": safe_ratio( - len(SECOND_PERSON_RE.findall(text)), sentence_count, 100 - ), - "first_person_density_per_100_sentences": safe_ratio( - len(FIRST_PERSON_RE.findall(text)), sentence_count, 100 - ), - "exclamation_density_per_1000_characters": safe_ratio( - len(EXCLAMATION_RE.findall(text)), character_count, 1000 - ), + "question_density_per_100_sentences": safe_ratio(len(QUESTION_RE.findall(text)), sentence_count, 100), + "second_person_density_per_100_sentences": safe_ratio(len(SECOND_PERSON_RE.findall(text)), sentence_count, 100), + "first_person_density_per_100_sentences": safe_ratio(len(FIRST_PERSON_RE.findall(text)), sentence_count, 100), + "exclamation_density_per_1000_characters": safe_ratio(len(EXCLAMATION_RE.findall(text)), character_count, 1000), + "speech_chars_per_minute": speech_chars_per_minute, "emoji_count": emoji_count, - "emoji_density_per_100_characters": safe_ratio( - emoji_count, len(body), 100 - ), - "tag_count": tag_count, + "emoji_density_per_100_characters": safe_ratio(emoji_count, character_count, 100), + "tag_count": len(TAG_RE.findall(body)), "terms": dict(extract_terms(text)), } @@ -230,19 +352,7 @@ def generated_at() -> str: def metric_label(metric_name: str) -> str: - labels = { - "title_chars": "标题字数", - "avg_sentence_tokens": "平均句长(token)", - "line_count": "正文行数(非空行)", - "question_density_per_100_sentences": "问句密度 / 百句", - "second_person_density_per_100_sentences": "第二人称密度 / 百句", - "first_person_density_per_100_sentences": "第一人称密度 / 百句", - "exclamation_density_per_1000_characters": "感叹号密度 / 千字", - "emoji_count": "emoji 数量 / 篇", - "emoji_density_per_100_characters": "emoji 密度 / 百字(正文)", - "tag_count": "话题标签数 / 篇", - } - return labels.get(metric_name, metric_name) + return METRIC_LABELS.get(metric_name, metric_name) def weighted_median(pairs: list[tuple[float, float]]) -> float: @@ -265,10 +375,10 @@ def weighted_mad(pairs: list[tuple[float, float]], center: float) -> float: return weighted_median(deviations) -def build_statistics(reports: list[dict]) -> dict: +def build_statistics(reports: list[dict], kind: str) -> dict: total_weight = rounded(sum(report["weight"] for report in reports)) numeric_metrics = {} - for metric_name in STATISTICS_METRICS: + for metric_name in statistics_metrics(kind): pairs = [ (float(report["document"][metric_name]), float(report["weight"])) for report in reports @@ -324,7 +434,7 @@ def build_statistics(reports: list[dict]) -> dict: } -def statistics_markdown(statistics: dict) -> str: +def statistics_markdown(statistics: dict, kind: str) -> str: lines = [ "| 指标 | 加权中位数 | 加权 MAD | 最小值 | 最大值 |", "|---|---:|---:|---:|---:|", @@ -336,10 +446,16 @@ def statistics_markdown(statistics: dict) -> str: lines.extend( [ "", - f"样本覆盖度:`{statistics['report_count']}` 条 report;单条或少量样本不能推导账号比例、发布节奏或高数据共性。", + f"样本覆盖度:`{statistics['report_count']}` 条 {KIND_LABELS[kind]} report。", f"权重模式:`{statistics['weighting']}`;总权重:`{statistics['total_weight']}`。", - "话题标签数按正文内联 `#话题` 统计;emoji 统计只作证据底座,不构成「必须凑满几个」的规则。", - "统计只用于辅助聚合,不生成评分;定性判断必须回到各篇 DNA report。", + ] + ) + if kind == "video": + lines.append("口播密度(字/分钟)仅在 report 提供 `duration` 时有意义;未提供时该行只是 0 值占位。") + lines.extend( + [ + "「账号运营子模块」维度(简介写法、内容形式比例、发布习惯)不能由单篇样本推导:样本非对标账号批量时写未观测。", + "统计只用于辅助聚合,不生成评分;定性判断必须回到各篇 DNA report 与原文。", ] ) return "\n".join(lines) @@ -372,6 +488,14 @@ def parse_json_list(value: str) -> list: return [] +def parse_duration(value: str) -> float: + try: + duration = float(value) + except ValueError: + return 0.0 + return duration if math.isfinite(duration) and duration > 0 else 0.0 + + def hidden_path(block_match, path_key: str = "paths") -> list[Path]: if not block_match: return [] @@ -388,15 +512,18 @@ def load_reports(paths: list[Path]) -> list[dict]: source_path = source_paths[0] if source_paths else Path(parse_quoted(metadata.get("source-transcript", ""))) if not source_path.is_file(): raise SystemExit(f"DNA report source transcript does not exist: {source_path}") + duration = parse_duration(metadata.get("duration", "0")) document = document_metrics( source_path, source_path.read_text(encoding="utf-8", errors="ignore"), + duration, ) reports.append( { "report_path": str(path.resolve()), "dna_id": parse_quoted(metadata.get("dna-id", "")), "report_id": parse_quoted(metadata.get("report-id", path.name.removesuffix(".report.md"))), + "kind": parse_quoted(metadata.get("kind", DEFAULT_KIND)), "title": parse_quoted(metadata.get("title", document["title_candidates"][-1])), "weight": float(metadata.get("weight", "1")), "focus": parse_json_list(metadata.get("focus", "[]")), @@ -406,87 +533,116 @@ def load_reports(paths: list[Path]) -> list[dict]: return reports -REPORT_DIMENSION_PROMPTS = { - 'positioning-core': '- 单条观测:本条暴露出的定位、目标人群与核心传达。\n- 账号级判断:单条样本只给候选,不直接判定账号稳定性。', - 'topic-portfolio': '- 单条观测:本条选题类型、入口和系列关系。', - 'title-packaging': '- 单条观测:标题 / 描述 / 封面包装模式;只记模式,不抄成固定字句。', - 'bio-profile': '- 账号级观测:账号简介、主页与置顶表达;单条样本无法观测时写未观测。', - 'content-form-mix': '- 单条观测:本条内容形式。\n- 聚合边界:图文/视频比例与混合节奏必须由多样本或账号级数据推导。', - 'publish-cadence': '- 单条观测:本条发布时间。\n- 聚合边界:时间段、频率和三图文对一视频等节奏必须由多样本或账号级数据推导。', - 'high-performer-patterns': '- 数据线索:记录本条互动/播放/阅读线索。\n- 创意判断:回读选题、包装、形式与创意,不得把高数据直接等同于风格好。', - 'visual-language': '- 视觉证据:有图片或关键帧时由视觉模型读取;无证据写未观测。\n- 边界:只记录账号级稳定视觉语言,不规定逐镜设计。', - 'audio-language': '- 声音证据:来自口播稿、原视频信息或用户说明;无证据写未观测。\n- 边界:只记录音色/语速/声音气质倾向,不规定 TTS 参数。', - 'narration-dna': '- 独立块:记录开头、起承转合、收束、人称与签名表达。\n- 聚合边界:样本不足时保持未启用,不把单条句式上升为账号 DNA。', - 'engagement-conversion': '- 单条观测:平台内行动引导与承接路径。', - 'series-signature': '- 单条观测:栏目、固定表达或识别符号;高频词必须回读原文确认。', - 'search-intent-map': '- 搜索证据:主关键词、相关词、长尾句和用户可能提问。\n- 聚合边界:单篇只给候选;账号级搜索意图地图需多样本聚合。', - 'production-pipeline': '- 管线映射:只写 Content Producer 已支持的 Pipeline;未确定时写待定。\n- 分界:DNA 指导 main agent 出 Brief,不规定成片制作细节。', -} +def enforce_single_kind(reports: list[dict], kind: str | None) -> str: + """一个 DNA 只承载一种作品类型:返回该 DNA 的 kind,混型直接报错。""" + kinds = {report["kind"] for report in reports} + if len(kinds) > 1: + raise SystemExit( + "Reports mix work kinds: " + + ", ".join(sorted(kinds)) + + f"。一个 dna-id 只能承载一种作品类型:{KIND_SUFFIX_HINT}。" + ) + resolved = kinds.pop() if kinds else (kind or DEFAULT_KIND) + if kind and resolved != kind: + raise SystemExit( + f"--kind {kind} 与 report 内的 kind {resolved} 不一致;一个 dna-id 只能承载一种作品类型。" + ) + if resolved not in KINDS: + raise SystemExit(f"Unknown kind: {resolved}({PLATFORM_LABEL}支持:{', '.join(KINDS)})") + return resolved + def report_dimension_markdown(dimension: dict) -> str: heading = f"### {dimension['number']}. {dimension['name']}" - prompt = REPORT_DIMENSION_PROMPTS.get(dimension["id"], "- 单条观测:待 Agent 补齐。") + prompt = REPORT_DIMENSION_PROMPTS.get(dimension["id"], "- 单篇观测:待 Agent 补齐。") return ( f"{heading}\n\n" f"{prompt}\n\n" - "**单条结论:**待 Agent 补齐。\n\n" - "**原文证据:**待 Agent 补齐(逐字引用、账号信息、发布信息、画面/声音描述或数据线索;注明来源)。\n\n" - "**可复用信号:**待 Agent 补齐;样本不足时写未观测,不推导账号级稳定性。" + "**单篇结论:**待 Agent 补齐。\n\n" + "**原文证据:**待 Agent 补齐(逐字引用、账号/发布信息、画面或声音描述、数据线索;注明来源)。\n\n" + "**可复用信号:**待 Agent 补齐;证据不足时写未观测,不推导跨篇稳定性。" ) def report_markdown( dna_id: str, report_id: str, + kind: str, weight: float, focus: list[str], document: dict, cover_image: str, source_url: str, ) -> str: - dimensions = [] - for dimension in DIMENSIONS: - dimensions.append(report_dimension_markdown(dimension)) + dimensions = dimensions_for(kind) + dimension_blocks = [report_dimension_markdown(dimension) for dimension in dimensions] + duration_line = f"duration: {document['duration']}" + statistics_lines = [ + f"- 转录/正文字符:{document['characters']}", + f"- 句子数:{document['sentences']}", + ] + if kind == "video": + statistics_lines.append(f"- 视频时长:{document['duration'] or '未提供'}") + else: + statistics_lines.extend( + [ + f"- 标题字数:{document['title_chars']}", + f"- 正文行数:{document['line_count']}", + f"- 话题标签数:{document['tag_count']}", + ] + ) + statistics_lines.extend( + [ + f"- 标题候选:{' / '.join(document['title_candidates'])}", + f"- 来源链接:{source_url or '未提供'}", + f"- 封面 / 首帧图:{cover_image or '未提供'}", + ] + ) return "\n\n".join( [ "---\n" f"dna-id: {yaml_value(dna_id)}\n" f"report-id: {yaml_value(report_id)}\n" "type: dna-report\n" + f"kind: {yaml_value(kind)}\n" f"title: {yaml_value(document['title_candidates'][-1])}\n" f"source-transcript: {yaml_value(document['source_transcript'])}\n" f"source-url: {yaml_value(source_url)}\n" f"cover-image: {yaml_value(cover_image)}\n" + f"{duration_line}\n" f"weight: {weight}\n" f"focus: {yaml_value(focus)}\n" "sample_count: 1\n" f"generated_at: {yaml_value(generated_at())}\n" "---", - f"# {document['title_candidates'][-1]} 单篇 DNA Report", - "本文件只描述这一篇笔记。它不是聚合后的 DNA 文档,也不直接作为生产模板。", + f"# {document['title_candidates'][-1]} 单篇 DNA Report({KIND_LABELS[kind]})", + "本文件只描述这一篇作品。它不是聚合后的 DNA 文档,也不直接作为生产模板。", "## 单篇统计", - f"- 总字符:{document['characters']}\n- 标题字数:{document['title_chars']}\n- 句子数:{document['sentences']}\n- 正文行数:{document['line_count']}\n- emoji 数:{document['emoji_count']}\n- 话题标签数:{document['tag_count']}\n- 标题候选:{' / '.join(document['title_candidates'])}\n- 来源链接:{source_url or '未提供'}\n- 封面图:{cover_image or '未提供'}", - "## 样本与账号观测", - '- 样本类型:待 Agent 补齐(账号作品 / 用户提供单篇)。\n- 账号与简介:待 Agent 补齐;单篇样本无法观测时写未观测。\n- 发布时间与时间段:待 Agent 补齐;单篇样本只记录本篇时间,不推导账号节奏。\n- 内容形式:待 Agent 补齐(图文 / 视频 / 口播 / 实拍拼接 / 创意转场 / 纯 AIGC 动画 / 混合)。\n- 数据表现线索:待 Agent 补齐;只作证据,不直接判风格好坏。\n- 搜索关键词与用户可能提问:待 Agent 补齐;只记录本篇候选,不直接判定账号级搜索意图。\n- 视频形态与授权信息:待 Agent 补齐(横竖屏、时长、素材来源、授权边界)。', - f"## {len(DIMENSIONS)} 维单篇分析", - "\n\n".join(dimensions), + "\n".join(statistics_lines), + "## 样本观测", + REPORT_OBSERVATION_PROMPTS[kind], + f"## {len(dimensions)} 维单篇分析", + "\n\n".join(dimension_blocks), "## 单篇边界", - "- 这里记录本篇笔记的可复用信号,不判断跨篇稳定性。\n- 聚合时由 Agent 根据 DNA report、权重和 focus 判断共性、偏好和例外。", + "- 这里记录本篇作品的可复用信号,不判断跨篇稳定性。\n" + "- 聚合时由 Agent 根据全部 DNA report、权重和 focus 判断共性、偏好和例外。\n" + f"- 维度定义与边界见本工具 `references/{FRAMEWORK_DOCS[kind]}`。", f"", - *( [f""] if cover_image else [] ), + *([f""] if cover_image else []), ] ) + "\n" -def user_input_markdown(user_inputs: list[str], existing_body: str | None = None) -> str: +def user_input_markdown(user_inputs: list[str], kind: str, existing_body: str | None = None) -> str: if not user_inputs: return existing_body or "暂无待转译输入。" + dimension_count = len(dimensions_for(kind)) entries = [] for index, user_input in enumerate(user_inputs, start=1): entries.append( f"### 输入 {index}\n" f"- raw_input: {yaml_value(user_input)}\n" - f"- affected_dimensions: 待 Agent 映射到 {len(DIMENSIONS)} 个维度 ID\n" + f"- affected_dimensions: 待 Agent 映射到 {dimension_count} 个维度 ID\n" "- dna_document_change: 待 Agent 转译为聚合结论 / 报告依据 / 创作规则\n" "- template_change: 待 Agent 转译为具体执行规则\n" "- status: pending" @@ -498,20 +654,22 @@ def user_input_markdown(user_inputs: list[str], existing_body: str | None = None def dna_document_markdown( dna_id: str, + kind: str, reports: list[dict], statistics: dict, user_inputs: list[str] | None = None, previous_dna: str | None = None, ) -> str: + dimensions = dimensions_for(kind) old_sections = extract_markdown_sections(previous_dna, "### ") - dimensions = [] - for dimension in DIMENSIONS: + dimension_blocks = [] + for dimension in dimensions: heading = f"### {dimension['number']}. {dimension['name']}" body = old_sections.get(heading) if body: - dimensions.append(f"{heading}\n\n{body}") + dimension_blocks.append(f"{heading}\n\n{body}") else: - dimensions.append( + dimension_blocks.append( f"{heading}\n\n**聚合结论:**待 Agent 补齐。\n\n" "**报告依据:**待 Agent 列出使用的 DNA report、权重和 focus。\n\n" "**创作规则:**待 Agent 补齐。" @@ -525,25 +683,33 @@ def dna_document_markdown( "---\n" f"dna-id: {yaml_value(dna_id)}\n" "type: dna-document\n" + f"kind: {yaml_value(kind)}\n" + f"platform: {yaml_value(PLATFORM)}\n" f"report_count: {statistics['report_count']}\n" f"total_weight: {statistics['total_weight']}\n" f"weighting: {statistics['weighting']}\n" f"generated_at: {yaml_value(generated_at())}\n" "---", - f"# {dna_id} DNA 文档", - "本文件聚合历史 DNA report。它是账号当前采用的账号级运营规则,也必须能推导出 main agent 的图文生产输入或视频制作 Brief。", + f"# {dna_id} DNA 文档({PLATFORM_LABEL} · {KIND_LABELS[kind]})", + "本文件聚合该 DNA 目录下的全部 DNA report,形成当前采用的内容生产规则,并且必须能推导出 DNA template。" + "样本可以来自多个账号,也可以来自用户指定的一个账号的批量作品。", "## 报告与权重", "\n".join( f"- `{report['report_path']}`:weight `{report['weight']}`,focus `{', '.join(report['focus']) or 'all'}`" for report in reports ), - statistics_markdown(statistics), - f"## {len(DIMENSIONS)} 维聚合", - "\n\n".join(dimensions), + statistics_markdown(statistics, kind), + f"## {len(dimensions)} 维聚合", + "\n\n".join(dimension_blocks), + "## 子模块说明", + DNA_SUBMODULE_NOTE, "## 用户输入转译区", - user_input_markdown(user_inputs or [], existing_user_inputs), + user_input_markdown(user_inputs or [], kind, existing_user_inputs), "## 推导规则", - "- 聚合结论必须能追溯到 DNA report。\n- 用户输入必须先映射到具体维度,再修改聚合结论和创作规则;不得把原话直接当成 DNA 规则。\n- 模板必须由本文件推导,不能引入本文件未确认的规则。", + "- 聚合结论必须能追溯到 DNA report;区分高覆盖共性、高权重偏好、局部借鉴、孤例与例外。\n" + "- 用户输入必须先映射到具体维度,再修改聚合结论和创作规则;不得把原话直接当成 DNA 规则。\n" + "- template 必须由本文件推导,不能引入本文件未确认的规则。\n" + "- 账号运营子模块(简介写法、内容形式比例、发布习惯)只写进本文件,不进 template。", f"", ] ) + "\n" @@ -593,64 +759,6 @@ def extract_named_section(markdown: str, heading: str) -> str: return "\n".join(body).strip() -TEMPLATE_STAGES = ("定位与核心传达", "选题与标题包装", "内容形式与发布节奏", "高数据创意模式", "互动与系列", "搜索意图与用户问题", "制作交接与Pipeline", "口播文案DNA") - -TEMPLATE_STAGE_FIELDS = { - "定位与核心传达": ( - "一句话定位", - "目标人群", - "核心传达", - "账号简介与主页表达", - "不变承诺", - ), - "选题与标题包装": ( - "选题组合", - "标题模式", - "封面包装", - "禁用方向", - ), - "内容形式与发布节奏": ( - "图文与视频比例", - "发布时间带", - "内容形式混合节奏", - "系列栏目", - ), - "高数据创意模式": ( - "高表现样本共性", - "可复用创意原型", - "触发条件", - "例外", - ), - "互动与系列": ( - "互动目标", - "引导方式", - "系列与签名标记", - "必须做", - "避免", - ), - "搜索意图与用户问题": ( - "主关键词", - "相关词与长尾句", - "用户可能提问", - "搜索意图分层", - "标签与正文覆盖策略", - ), - "制作交接与Pipeline": ( - "MainAgent交付物", - "ContentProducer交付物", - "Pipeline", - "素材与授权", - "风格边界", - ), - "口播文案DNA": ( - "是否启用", - "起承转合结构", - "语言与人称", - "声音倾向", - "必须做", - "避免", - ), -} def parse_template_fields(body: str) -> dict[str, str]: fields = {} for line in body.splitlines(): @@ -660,8 +768,8 @@ def parse_template_fields(body: str) -> dict[str, str]: return fields -def template_stage_from_heading(heading: str) -> str | None: - for stage in TEMPLATE_STAGES: +def template_stage_from_heading(heading: str, kind: str) -> str | None: + for stage in TEMPLATE_STAGES[kind]: if heading.startswith(f"[{stage}部分]"): return stage return None @@ -676,10 +784,10 @@ def template_segment(stage: str, values: dict[str, str] | None = None) -> str: return "\n".join(lines) -def stage_values_from_template(old_sections: dict[str, str]) -> dict[str, dict[str, str]]: - values = {stage: {} for stage in TEMPLATE_STAGES} - for heading in sorted(old_sections, key=template_order): - stage = template_stage_from_heading(heading) +def stage_values_from_template(old_sections: dict[str, str], kind: str) -> dict[str, dict[str, str]]: + values = {stage: {} for stage in TEMPLATE_STAGES[kind]} + for heading in sorted(old_sections, key=lambda item: template_order(item, kind)): + stage = template_stage_from_heading(heading, kind) if not stage: continue for field, value in parse_template_fields(old_sections[heading]).items(): @@ -689,32 +797,23 @@ def stage_values_from_template(old_sections: dict[str, str]) -> dict[str, dict[s def template_markdown( dna_id: str, + kind: str, source_dna: str, previous_template: str | None = None, ) -> str: - old_sections = extract_template_sections(previous_template) - stage_values = stage_values_from_template(old_sections) - segments = [template_segment(stage, stage_values[stage]) for stage in TEMPLATE_STAGES] + stages = TEMPLATE_STAGES[kind] + old_sections = extract_template_sections(previous_template, kind) + stage_values = stage_values_from_template(old_sections, kind) + segments = [template_segment(stage, stage_values[stage]) for stage in stages] section_defaults = [ - ( - "## 生产模板", - "\n\n".join(segments), - ), + ("## 生产模板", "\n\n".join(segments)), ( "## 用户输入转译后的执行规则", "- (来自用户输入:待 Agent 补齐来源。)\n" - f"- (影响维度:待 Agent 映射到 {len(DIMENSIONS)} 维 ID。)\n" - "- (执行规则:待 Agent 写成 Brief 或图文生产时可直接执行的要求。)", - ), - ( - "## 使用检查", - "- 是否只用一个 DNA,且与本次账号 / 内容任务匹配。\n" - "- 定位、选题、标题包装、核心传达是否来自 DNA 文档。\n" - "- 图文/视频比例、发布节奏和高数据创意是否尊重样本覆盖度;样本不足时是否标注未观测。\n" - "- 视频全案是否只输出 Brief,且 Brief 明确 Pipeline、素材授权、验收标准和交付边界。\n" - "- 口播文案 DNA 是否独立启用;未启用时是否避免规定逐句口播。\n" - "- 用户输入是否已转译为具体执行规则。", + f"- (影响维度:待 Agent 映射到 {len(dimensions_for(kind))} 维 ID。)\n" + "- (执行规则:待 Agent 写成生产时可直接执行的要求。)", ), + ("## 使用检查", TEMPLATE_CHECKLISTS[kind]), ] rendered_sections = [] for heading, default_body in section_defaults: @@ -732,17 +831,18 @@ def template_markdown( "---\n" f"dna-id: {yaml_value(dna_id)}\n" "type: dna-template\n" + f"kind: {yaml_value(kind)}\n" f"source_dna: {yaml_value(source_dna)}\n" f"generated_at: {yaml_value(generated_at())}\n" "---", - f"# {dna_id} DNA Template", - "本模板是 main agent 的账号级内容生产 / Brief 输入模板,必须由 DNA 文档推导;不得引入 DNA 文档未确认的规则,也不规定成片制作细节。", + f"# {dna_id} DNA Template({PLATFORM_LABEL} · {KIND_LABELS[kind]})", + TEMPLATE_INTROS[kind], *rendered_sections, ] ) + "\n" -def extract_template_sections(markdown: str | None) -> dict[str, str]: +def extract_template_sections(markdown: str | None, kind: str) -> dict[str, str]: if not markdown: return {} sections = {} @@ -769,10 +869,10 @@ def extract_template_sections(markdown: str | None) -> dict[str, str]: return sections -def template_order(heading: str) -> tuple[int, str]: - stage = template_stage_from_heading(heading) +def template_order(heading: str, kind: str) -> tuple[int, str]: + stage = template_stage_from_heading(heading, kind) if stage: - return (TEMPLATE_STAGES.index(stage) + 1, heading) + return (TEMPLATE_STAGES[kind].index(stage) + 1, heading) return (10_000, heading) @@ -781,11 +881,29 @@ def validate_id(value: str, label: str) -> None: raise SystemExit(f"{label} must be 2-64 chars: lowercase letters, digits, and hyphens") -def validate_focus(focus: list[str]) -> None: - valid = {dimension["id"] for dimension in DIMENSIONS} +def validate_kind(value: str | None) -> str | None: + if value is None: + return None + if value not in KINDS: + raise SystemExit(f"Unknown --kind: {value}({PLATFORM_LABEL}支持:{', '.join(KINDS)})") + return value + + +def validate_focus(focus: list[str], kind: str) -> None: + valid = {dimension["id"] for dimension in dimensions_for(kind)} unknown = sorted(set(focus) - valid) if unknown: - raise SystemExit(f"Unknown focus: {', '.join(unknown)}") + raise SystemExit(f"Unknown focus for kind '{kind}': {', '.join(unknown)}") + + +def validate_duration(value: str) -> float: + try: + duration = float(value) + except ValueError: + raise SystemExit("--duration must be a number of seconds") + if duration < 0 or not math.isfinite(duration): + raise SystemExit("--duration must be a non-negative finite number") + return duration def write_text(path: Path, content: str) -> None: @@ -802,37 +920,39 @@ def inputs_from_args(args: argparse.Namespace) -> list[str]: def report_command(args: argparse.Namespace) -> None: validate_id(args.dna_id, "--dna-id") validate_id(args.sample_id, "--sample-id") - validate_focus(args.focus) + kind = validate_kind(args.kind) or DEFAULT_KIND + validate_focus(args.focus, kind) weight = float(args.weight) if weight <= 0 or not math.isfinite(weight): raise SystemExit("--weight must be a positive finite number") + duration = validate_duration(args.duration) if args.duration else 0.0 + if kind != "video" and duration: + print(f"[warn] --duration 只对视频作品有意义,{KIND_LABELS[kind]} report 忽略该值。") + duration = 0.0 paths = collect_input_paths(inputs_from_args(args), {".md", ".txt"}) if len(paths) != 1: - raise SystemExit("report command accepts exactly one note text; use build to aggregate reports") - document = document_metrics( - paths[0], paths[0].read_text(encoding="utf-8", errors="ignore") - ) - output_dir = Path(args.output_dir or f"xhs/dna/{args.dna_id}/reports") + raise SystemExit("report command accepts exactly one transcript; use build to aggregate reports") + document = document_metrics(paths[0], paths[0].read_text(encoding="utf-8", errors="ignore"), duration) + output_dir = Path(args.output_dir or f"{PLATFORM}/dna/{args.dna_id}/reports") cover_image = "" if args.cover_image: source_cover = validate_cover_image(args.cover_image) - cover_image = str( - persist_cover_image(source_cover, output_dir, args.sample_id).resolve() - ) + cover_image = str(persist_cover_image(source_cover, output_dir, args.sample_id).resolve()) output = output_dir / f"{args.sample_id}.report.md" write_text( output, report_markdown( - args.dna_id, args.sample_id, weight, args.focus, document, cover_image, + args.dna_id, args.sample_id, kind, weight, args.focus, document, cover_image, args.source_url or "", ), ) - print(f"Wrote single-note DNA report: {output}") + print(f"Wrote single-work DNA report ({KIND_LABELS[kind]}): {output}") + print(f"[next] Agent 补齐「样本观测」与 {len(dimensions_for(kind))} 维单篇结论后,跑 build 聚合。") def build_command(args: argparse.Namespace) -> None: validate_id(args.dna_id, "--dna-id") - input_values = args.input or [f"xhs/dna/{args.dna_id}/reports"] + input_values = args.input or [f"{PLATFORM}/dna/{args.dna_id}/reports"] paths = collect_input_paths(input_values, {".md"}) report_paths = [path for path in paths if path.name.endswith(".report.md")] if not report_paths: @@ -840,22 +960,17 @@ def build_command(args: argparse.Namespace) -> None: reports = load_reports(report_paths) foreign_reports = [report["report_id"] for report in reports if report["dna_id"] != args.dna_id] if foreign_reports: - raise SystemExit( - f"Reports belong to another dna-id: {', '.join(foreign_reports)}" - ) - statistics = build_statistics(reports) - output_dir = Path(args.output_dir or f"xhs/dna/{args.dna_id}") + raise SystemExit(f"Reports belong to another dna-id: {', '.join(foreign_reports)}") + kind = enforce_single_kind(reports, validate_kind(args.kind)) + for report in reports: + validate_focus(report["focus"], kind) + statistics = build_statistics(reports, kind) + output_dir = Path(args.output_dir or f"{PLATFORM}/dna/{args.dna_id}") dna_path = output_dir / f"{args.dna_id}.dna.md" template_path = output_dir / f"{args.dna_id}.template.md" - write_text( - dna_path, - dna_document_markdown(args.dna_id, reports, statistics, args.user_input), - ) - write_text( - template_path, - template_markdown(args.dna_id, dna_path.name, None), - ) - print(f"Wrote DNA document and template: {output_dir}") + write_text(dna_path, dna_document_markdown(args.dna_id, kind, reports, statistics, args.user_input)) + write_text(template_path, template_markdown(args.dna_id, kind, dna_path.name, None)) + print(f"Wrote DNA document and template ({KIND_LABELS[kind]}): {output_dir}") def update_command(args: argparse.Namespace) -> None: @@ -868,7 +983,8 @@ def update_command(args: argparse.Namespace) -> None: metadata = parse_frontmatter(previous_dna) dna_id = parse_quoted(metadata.get("dna-id", dna_path.name.removesuffix(".dna.md"))) validate_id(dna_id, "--dna-id") - validate_focus(args.focus) + declared_kind = parse_quoted(metadata.get("kind", "")) or None + kind = validate_kind(args.kind) or validate_kind(declared_kind) input_values = args.input or [] new_paths = collect_input_paths(input_values, {".md"}) if input_values else [] @@ -880,36 +996,29 @@ def update_command(args: argparse.Namespace) -> None: reports = load_reports(sorted(all_paths.values(), key=lambda path: str(path))) foreign_reports = [report["report_id"] for report in reports if report["dna_id"] != dna_id] if foreign_reports: - raise SystemExit( - f"Reports belong to another dna-id: {', '.join(foreign_reports)}" - ) - statistics = build_statistics(reports) + raise SystemExit(f"Reports belong to another dna-id: {', '.join(foreign_reports)}") + kind = enforce_single_kind(reports, kind) + for report in reports: + validate_focus(report["focus"], kind) + validate_focus(args.focus, kind) + statistics = build_statistics(reports, kind) user_inputs = list(args.user_input or []) - write_text( - dna_path, - dna_document_markdown( - dna_id, - reports, - statistics, - user_inputs, - previous_dna, - ), - ) - write_text( - template_path, - template_markdown(dna_id, dna_path.name, previous_template), - ) - print(f"Updated DNA document and template: {dna_path}") + write_text(dna_path, dna_document_markdown(dna_id, kind, reports, statistics, user_inputs, previous_dna)) + write_text(template_path, template_markdown(dna_id, kind, dna_path.name, previous_template)) + print(f"Updated DNA document and template ({KIND_LABELS[kind]}): {dna_path}") def build_parser() -> argparse.ArgumentParser: - parser = argparse.ArgumentParser(description="Build qualitative Xiaohongshu note DNA assets") + parser = argparse.ArgumentParser(description=f"Build qualitative {PLATFORM_LABEL} DNA assets per work kind") subparsers = parser.add_subparsers(dest="command", required=True) - report = subparsers.add_parser("report", help="Create one single-note DNA report") + kind_help = f"作品类型:{' / '.join(KINDS)}(默认 {DEFAULT_KIND})" + report = subparsers.add_parser("report", help="Create one single-work DNA report") report.add_argument("--input", action="append", required=True) - report.add_argument("--cover-image", help="Local cover image used by visual-model analysis") - report.add_argument("--source-url", help="Original note URL kept as report evidence") + report.add_argument("--kind", help=kind_help) + report.add_argument("--cover-image", help="Local cover / first-frame image used by visual-model analysis") + report.add_argument("--source-url", help="Original work URL kept as report evidence") + report.add_argument("--duration", help="Video duration in seconds (video kind only), used for speech-density statistics") report.add_argument("--dna-id", required=True) report.add_argument("--sample-id", required=True) report.add_argument("--weight", default="1") @@ -919,6 +1028,7 @@ def build_parser() -> argparse.ArgumentParser: build = subparsers.add_parser("build", help="Aggregate DNA reports into DNA document and template") build.add_argument("--input", action="append") + build.add_argument("--kind", help=kind_help) build.add_argument("--dna-id", required=True) build.add_argument("--user-input", action="append", default=[]) build.add_argument("--output-dir") @@ -926,6 +1036,7 @@ def build_parser() -> argparse.ArgumentParser: update = subparsers.add_parser("update", help="Merge reports and translate user input") update.add_argument("--input", action="append") + update.add_argument("--kind", help=kind_help) update.add_argument("--dna", required=True) update.add_argument("--template", required=True) update.add_argument("--focus", action="append", default=[]) diff --git a/crews/main/skills/expert-xhs/workflows/account-benchmark.md b/crews/main/skills/expert-xhs/workflows/account-benchmark.md index 9e52e96f..61637bc4 100644 --- a/crews/main/skills/expert-xhs/workflows/account-benchmark.md +++ b/crews/main/skills/expert-xhs/workflows/account-benchmark.md @@ -76,7 +76,8 @@ camoufox-cli --session xhs-browse --persistent --json open "https://www.xiaohong - 搜索翻页间隔 3-5 秒,下载间隔 5-10 秒;串行执行,不并发多个搜索。 - 发现登录失效 -> 走 `login-manager` 有头重登流程(复用 `xhs-browse` session),重登后重试一次。 -- 每个关键词选取 3-5 篇代表性图文笔记;账号级对标至少收集 10 篇。 +- 每个关键词选取 3-5 篇代表性图文笔记;对标账号至少批量收集 10 篇(从账号发布列表提取,图文与视频分开建 DNA)。 +- 批量账号样本必须填**账号运营子模块**:账号简介写法(`account-bio`)、内容形式比例与发布习惯(`content-mix-cadence`,含发布时间段与混合节奏,如三篇图文对一篇视频);只写进 DNA 文档,不进 template。 - 遇到视频笔记:该篇转 `viral-chaser` 拆解,或跳过只保留图文样本(看对标目的)。 ## Step 3 - 下载样本 @@ -122,7 +123,7 @@ xhs/dna/{benchmark-dna-id}/{benchmark-dna-id}.dna.md xhs/dna/{benchmark-dna-id}/{benchmark-dna-id}.template.md ``` -对标 DNA template 使用 v1 账号级语义段:定位与核心传达、选题与标题包装、内容形式与发布节奏、高数据创意模式、互动与系列、搜索意图与用户问题、制作交接与 Pipeline、口播文案 DNA。每一部分都要能从对标 DNA 文档推导。 +对标 DNA template 用语义段与目标 DNA 一致:图文 = 选题、标题与封面、关键词与用户问题、内容创意与结构、正文表达、图组、互动与标签;视频 = 选题、标题与封面、内容创意、关键词与用户问题、视频形态与制作指向、制作规格、口播文案(可选)。每一部分都要能从对标 DNA 文档推导;账号运营子模块不进 template。 ## Step 5 - 模式分析与差异化(agent 推理) @@ -168,8 +169,8 @@ xhs/dna/{base-dna-id}/{base-dna-id}.template.md 比较必须覆盖两层: -1. **14 维 DNA 文档**:逐个比较定位、选题、标题包装、账号简介、图文/视频比例、发布节奏、高数据创意、视觉/声音、口播文案 DNA、互动系列、搜索意图地图与制作管线。 -2. **template 语义段**:逐项比较定位与核心传达、选题与标题包装、内容形式与发布节奏、高数据创意、互动与系列、搜索意图与用户问题、制作交接、口播文案 DNA。 +1. **DNA 文档**:逐个比较选题与观看理由、标题与封面、内容创意、匹配的用户问题、正文表达与语气、图组视觉、互动引导与转化,以及账号运营子模块(简介写法、内容形式比例、发布习惯);视频对标另比视频形态与制作指向、制作规格、口播文案子模块。 +2. **template 语义段**:按作品类型逐项比较(图看看写作段与图组,视频看 Brief 相关段)。 每个维度和模板语义段都输出四类结论: @@ -177,7 +178,7 @@ xhs/dna/{base-dna-id}/{base-dna-id}.template.md | --- | --- | | 保持 | 基线已有优势,与目标读者和商业定位一致 | | 引入 | 对标更有效,且不冲突业务事实、合规边界和用户偏好 | -| 局部借鉴 | 只适合选题、标题包装、搜索意图、图文/视频比例、发布节奏或 Pipeline 等局部场景 | +| 局部借鉴 | 只适合选题、标题与封面、关键词与用户问题、内容形式比例、发布习惯或制作指向等局部场景 | | 不采纳 | 仅依赖孤例、冲突商业定位、风险高或难稳定执行 | 每项至少说明: @@ -198,7 +199,7 @@ xhs/dna/{base-dna-id}/{base-dna-id}.template.md 局部 DNA 融合流程: -1. 明确采纳范围:14 维中的维度、template 语义段,或两者组合。 +1. 明确采纳范围:维度、template 语义段,或两者组合。 2. 读取对标 DNA 文档 / template 的对应规则、适用条件和例外。 3. 整理为一条可转译输入,包含来源 `dna-id`、采纳范围和具体规则。 4. 在基线 DNA 上执行无新增样本的 update: diff --git a/crews/main/skills/expert-xhs/workflows/account-setup.md b/crews/main/skills/expert-xhs/workflows/account-setup.md index 9732dcec..dbd39d57 100644 --- a/crews/main/skills/expert-xhs/workflows/account-setup.md +++ b/crews/main/skills/expert-xhs/workflows/account-setup.md @@ -167,14 +167,14 @@ Agent 基于 `business_knowledge.md` 和关键词宇宙提炼搜索关键词, 1. 优先找受众相近、承诺清晰、近期仍活跃的账号;优先低粉爆款账号(粉丝与自己体量近似但互动明显更高,内容形式可学习)。 2. 优先选择点赞、收藏、评论信号强的笔记;单一互动数只作参考之一。 -3. 账号级初始参考建议收集 10 篇笔记;用户明确提供单篇或少量样本时仍可建立 `dna-0`,但必须标注覆盖不足,不得称为账号级稳定 DNA。 +3. 对标账号初始参考建议批量收集 10 篇笔记(图文进 `dna-0`,视频进 `dna-0-video`);用户明确提供单篇或少量样本时仍可建立 DNA,但必须标注覆盖不足,不得称为稳定结论。批量账号样本才填账号运营子模块(简介写法、内容形式比例、发布习惯)。 用户补回账号名或笔记链接后,回到路径 A。 #### 路径 C:用户拒绝外部参考或已有明确想法 1. 结合 `business_knowledge.md`、用户定位、目标读者、变现方式和用户思路,提炼 `dna-0`。 -2. Agent 直接按 14 维账号级框架编写: +2. Agent 直接按维度框架编写(图文用 `note-dna-framework.md`,视频用 `video-dna-framework.md`): ```text xhs/dna/dna-0/dna-0.dna.md @@ -183,7 +183,7 @@ xhs/dna/dna-0/dna-0.template.md 3. DNA 文档必须标明来源是"业务信息 + 用户指定方向",没有笔记样本统计。 4. 能确定的规则写为执行要求;未确定维度写"待校准",不得虚构单篇证据。 -5. Template 仍必须给出可执行的图文输入 / 视频 Brief 语义段;不能只有抽象定位词。 +5. Template 仍必须给出可执行语义段:图文 = 图文写作模板(含关键词与用户问题);视频 = Brief 正文 + 口播文案模板。不能只有抽象定位词。 6. 将 DNA 摘要和待校准点发给用户确认,再按用户意见转译修订。 后续获得真实笔记样本时,按 `style-dna.md` 生成 report 并更新 `dna-0`。 diff --git a/crews/main/skills/expert-xhs/workflows/content-production.md b/crews/main/skills/expert-xhs/workflows/content-production.md index dbc99607..2d0f7997 100644 --- a/crews/main/skills/expert-xhs/workflows/content-production.md +++ b/crews/main/skills/expert-xhs/workflows/content-production.md @@ -4,10 +4,10 @@ **分工硬边界**: -- 图文笔记、长文内容:main agent 直接生产。 -- 已有视频素材的简单加工:main agent 直接做。 -- 视频全案:main agent 只产出 **Brief**,成片制作委托 `content-producer`。若口播文案 DNA 已启用,口播终稿由 main 写好并随 Brief 交付;CP 只做声画制作。 -- Brief 指定 Pipeline 时,CP 必须采用;未指定时 CP 自由发挥。 +- 图文笔记、长文内容:main agent 直接生产(含标题、正文、图组、话题标签与发布)。 +- 已有视频素材的简单加工:main agent 直接做(`video-edit` / `talking-head-cut`)。 +- 视频全案:main agent 只产出 **Brief**(+ 口播类的口播文案 / 录音),成片制作委托 `content-producer`;CP 只做声画实现。 +- Brief 指定 `workflow` 时 CP 必须采用;未指定时 CP 按通用阶段链自由发挥。 ## Step 0 - 入口判断 @@ -57,7 +57,7 @@ xhs/dna/{dna-id}/{dna-id}.dna.md xhs/dna/{dna-id}/{dna-id}.template.md ``` -DNA template 是 main agent 的图文生产 / 视频 Brief 输入模板,覆盖定位与核心传达、选题与标题包装、内容形式与发布节奏、高数据创意、互动系列、搜索意图与用户问题、制作交接与可选口播文案 DNA。它不规定逐句正文、单图构图、镜头表、转场或编码细节。 +DNA template 是 main agent 的生产输入模板:**图文 DNA 的 template = 图文写作模板**(选题、标题与封面、关键词与用户问题、内容创意与结构、正文表达、图组、互动与标签);**视频 DNA 的 template = Brief 正文模板 +(可选)口播文案模板**。都不规定创作细节:逐句正文之外的排版参数、镜头表、转场或编码细节归执行方。 ### 2. 读取业务知识 @@ -82,7 +82,7 @@ DNA template 是 main agent 的图文生产 / 视频 Brief 输入模板,覆盖 | 标题 | 硬限制 ≤ 20 字;覆盖主关键词或用户问题 | | 图组 | 硬限制 ≤ 18 张;用户提供优先 | | 行动引导 | 只放一个平台内动作 | -| Pipeline | 视频全案已确定时写 CP 支持的 Pipeline;未确定则省略 | +| workflow | 视频全案已确定形态时写 CP `expert-video` 支持的 workflow(reversal-ad / narration-video / collage-broll);未确定则省略 | 优先级: @@ -90,7 +90,7 @@ DNA template 是 main agent 的图文生产 / 视频 Brief 输入模板,覆盖 用户明确交付要求 + 业务事实 / 红线 > 用户提供的素材 > business_knowledge.md -> DNA 的账号级规则 +> DNA template 的生产规则 > Agent 的一般内容判断 ``` @@ -161,22 +161,36 @@ DNA template 是 main agent 的图文生产 / 视频 Brief 输入模板,覆盖 ```markdown # 小红书视频制作 Brief -- dna_id: -- pipeline:dna-ad-video-pipeline / video-producer:default / 未指定 -- MainAgent 交付物:Brief、标题与正文、素材清单、(如启用)口播终稿 -- ContentProducer 交付物:成片、封面候选、交付说明、自检结果 -- 账号定位与核心传达: -- 目标人群与观看理由: -- 选题与搜索意图:主关键词、相关词、用户可能提问 -- 内容形式:口播 / 实拍拼接 / 创意转场 / 纯 AIGC 动画 / 混合 -- 素材清单、来源与授权: -- 视觉与声音边界:横竖屏、画面风格、音色/BGM 倾向(不写逐镜细节) -- 互动与转化目标: -- 时长带与验收标准: -- 风险检查:事实、承诺、授权、平台合规 +- 视频名 / slug: +- platform:xhs +- workflow:reversal-ad / narration-video / collage-broll / 未指定(未指定时 CP 按通用阶段链自选档位) +- 选题与观看理由: +- 核心传达: +- 内容创意:创意原型 + 展开逻辑 + 记忆点 +- 关键词与用户问题:主关键词、相关词与长尾句、用户可能的提问(供 CP 理解内容指向,不做 SEO 操作) +- 标题与简介:笔记标题、发布正文、话题标签(main 定稿) +- 封面要求:封面主文案 + 视觉方向 +- 制作规格:横屏 / 竖屏、时长带、画面风格、配音音色与声音形态、BGM 与音效、字幕 +- 口播文案:`voiceover.md` 绝对路径(口播类必填)/ 真人口播录音绝对路径 / 不适用 +- 素材清单:逐条**绝对路径** + 来源 + 授权(无素材时写「无,由 CP 按 Brief 取材」) +- 交付物与验收:`video.mp4` + `cover.jpg` + `final-deliver.md`,回报三者绝对路径;验收标准 +- 闸门:GATE A / GATE B 批准人(用户或 main 代理批准 + 批准范围) +- 禁止事项:事实与承诺边界、合规红线、禁用方向 ``` -口播文案 DNA 已启用时,main 按 `narration-dna` 写 `voiceover.md`,CP 不重写策略文案;未启用时,CP 在 Pipeline 内完成脚本与声画方案。 +Brief 硬性规则: + +- **不写 DNA**:Brief 里不出现 dna-id、DNA 文档路径或 DNA 规则原文——CP 看不到 main 的 DNA,只按 Brief 制作。 +- **不建工作区**:main 不替 CP 建目录、不指定项目路径;CP 在自己的 workspace 下自建工作区。双方 T3 权限可互访取文件。 +- **素材给绝对路径**:main 负责素材准备,把绝对路径写进 Brief。 +- **甲乙方关系**:需求方向、品牌事实、发布文案归 main;制作方案、分镜、渲染参数归 CP。 + +### 口播文案规则 + +- **口播类视频的口播文案由 main agent 出**:按 DNA 文档的 `narration-script`(口播文案子模块)写终稿,保存为作品目录下的 `voiceover.md`,在 Brief 中给**绝对路径**。CP 不重写策略文案,只做声画实现。 +- **真人口播**:明确要用用户真人声音时,main agent 必须向用户取得录音文件,落到作品目录并在 Brief 中给绝对路径。 +- **口播子模块未启用**:Brief 写明「口播文案:不适用」或只给要点,由 CP 按其 workflow 组织旁白;main 不再规定逐句台词。 +- 用户必用的事实、案例、承诺和 CTA 必须进入 Brief 或口播终稿,不得为了形式删除关键事实。 ## 【确认】正文 / Brief @@ -190,7 +204,7 @@ DNA template 是 main agent 的图文生产 / 视频 Brief 输入模板,覆盖 ### 图文图组 -1. 读取 DNA 的视觉语言与标题包装,只取账号级风格边界。 +1. 读取 DNA 的图组视觉与标题封面结论,只取风格边界。 2. 图片来源优先级:用户素材 → `campaign_assets/` → `siliconflow-img-gen` → `pexels-footage` / `pixabay-footage`。 3. 封面必须存在,兑现标题承诺;生成图发用户确认。 4. 图文建议 3:4 竖版,图片 ≤ 18 张,顺序按信息推进。 @@ -202,7 +216,7 @@ DNA template 是 main agent 的图文生产 / 视频 Brief 输入模板,覆盖 | --- | --- | | 用户直接提供成片 | 校验格式与时长,复制到作品目录 | | 已有素材需简单加工 | main 用 `video-edit` / `talking-head-cut` 处理 | -| 全案制作 | 委托 `content-producer`,只交付确认后的 Brief;指定 Pipeline 必须采用,未指定由 CP 自由发挥 | +| 全案制作 | 委托 `content-producer`:只交 Brief + 素材绝对路径 + 口播文案 / 录音,不指定 CP 工作区;指定 `workflow` 必须采用,未指定由 CP 自由发挥。成片与封面按 CP 回报的绝对路径取回作品目录 | 视频封面优先从成片选帧;需要更强视觉冲击时用 `siliconflow-img-gen`。 diff --git a/crews/main/skills/expert-xhs/workflows/editing.md b/crews/main/skills/expert-xhs/workflows/editing.md index 08465f26..74dffd76 100644 --- a/crews/main/skills/expert-xhs/workflows/editing.md +++ b/crews/main/skills/expert-xhs/workflows/editing.md @@ -10,27 +10,27 @@ | 用户的说法 | 改的层级 | 怎么走 | |-----------|---------|--------| -| "改下标题" | 标题层 | 按 `title-packaging` 与 `search-intent-map` 重写,给 2-3 个候选 | -| "正文改改 / 精简 / 扩写 / 换开头" | 正文层 | 按 `positioning-core`、`search-intent-map` 与互动目标局部调整 | +| "改下标题" | 标题层 | 按 `title-cover` 与 `search-intent` 重写,给 2-3 个候选 | +| "正文改改 / 精简 / 扩写 / 换开头" | 正文层 | 按 `topic-angle`、`content-idea`、`search-intent` 与互动目标局部调整 | | "标签换一下 / 加标签" | 标签层 | 按 template 话题标签策略重组(≤10 个硬限制) | -| "换封面 / 换图 / 加图" | 图组层 | 用户指定 / `siliconflow-img-gen` 按 `visual-language` 重做 | +| "换封面 / 换图 / 加图" | 图组层 | 用户指定 / `siliconflow-img-gen` 按 `imageset-visual` 重做 | | "结构调一下 / 改成清单体" | 结构层 | 先出调整方案(新的段落组织与信息顺序)-> 确认 -> 重写正文 | | "换个风格 / 用另一个 DNA 写" | 方向层 | 明确目标 `dna-id`,按目标 template 重写;等于一次小型再生产 | | "方向不对 / 选题要换" | 选题层 | 回到 `content-production.md` 从选题重新走 | ## 标题层 -1. 目标 DNA 不确定时先确认(用户指定或默认 `dna-0`),读取 `title-packaging` 与 `search-intent-map`。 +1. 目标 DNA 不确定时先确认(用户指定或默认 `dna-0`),读取 `title-cover` 与 `search-intent`。 2. 硬限制 ≤ 20 字;有参考标题时参考但不照抄。 3. 给 2-3 个候选并说明各自侧重的标题类型,用户选定后更新 `xhs/outputs//note.md` 的标题行。 ## 正文层 -按目标 DNA 的账号级约束修改: +按目标 DNA 的约束修改: - 改开头:确保兑现标题与封面承诺,并优先回答用户问题或给出核心结论。 - 改主体:围绕核心传达与搜索意图调整信息顺序;必用素材与业务事实不得丢失。 -- 改结尾与 CTA:只保留一个平台内行动引导,与 `engagement-conversion` 一致。 +- 改结尾与 CTA:只保留一个平台内行动引导,与 `interaction-cta` 一致。 - emoji 与标点按小红书语感和用户偏好调整,不作为 DNA 维度机械规定。 - 正文硬限制 ≤ 1000 字;改完同步更新 `note.md`。 @@ -45,7 +45,7 @@ ## 图组层 1. 用户直接指定图片 -> 直接使用,复制进笔记目录。 -2. 用户说"重做封面" -> 按 DNA `visual-language`、标题承诺与核心收益,用 `siliconflow-img-gen` 生成,用户确认后替换。 +2. 用户说"重做封面" -> 按 DNA `imageset-visual`、标题承诺与核心收益,用 `siliconflow-img-gen` 生成,用户确认后替换。 3. 增删配图:总数 ≤ 18 张;顺序按正文信息推进重排;替换后更新 `note.md` 同目录图片与引用。 4. 图文建议 3:4 竖版;封面(首图)必须存在。 diff --git a/crews/main/skills/expert-xhs/workflows/review.md b/crews/main/skills/expert-xhs/workflows/review.md index 223b8cb8..c1ac24b9 100644 --- a/crews/main/skills/expert-xhs/workflows/review.md +++ b/crews/main/skills/expert-xhs/workflows/review.md @@ -35,17 +35,29 @@ xhs 互动数据来自创作者后台笔记管理页 5 列(`xhs-engagement` - 精确的传播系数(每次分享带来多少新观众)不可得;只能用 分享/(点赞+评论) 作为传播效率的代理估算。 - 用户可提供创作者中心后台截图(流量来源、粉丝画像、搜索词),作为更高置信度的证据;没有就用库内指标。 -### 互动漏斗 → template 语义段 → 14 维映射 +### 互动漏斗 → template 语义段 → DNA 维度映射 + +**图文作品(默认 `dna-0`)** + +| 漏斗卡点 | 先怀疑的 template 语义段 | 可回溯的 DNA 维度 | +|---------|--------------------------|------------------| +| 阅读低(推荐/点击瓶颈) | 选题、标题与封面、关键词与用户问题 | topic-angle、title-cover、search-intent | +| 阅读正常但收藏低 | 内容创意与结构、正文表达 | content-idea、body-voice | +| 阅读正常但评论低 | 互动与标签、内容创意 | interaction-cta、content-idea | +| 点赞低 | 内容创意、正文表达 | content-idea、body-voice | +| 分享低 | 选题、正文表达 | topic-angle、body-voice | +| 关注转化低 | 账号运营子模块、互动与标签 | account-bio、content-mix-cadence、interaction-cta | +| 搜索流量弱 | 关键词与用户问题、标题与封面 | search-intent、title-cover | +| 图组跳失 / 图片被吐槽 | 图组、标题与封面 | imageset-visual、title-cover | + +**视频作品(`dna-*-video`)** | 漏斗卡点 | 先怀疑的 template 语义段 | 可回溯的 DNA 维度 | |---------|--------------------------|------------------| -| 阅读低(推荐/点击瓶颈) | 定位与核心传达、选题与标题包装、搜索意图 | positioning-core、topic-portfolio、title-packaging、search-intent-map | -| 阅读正常但收藏低 | 搜索意图与用户问题、高数据创意模式 | search-intent-map、high-performer-patterns、positioning-core | -| 阅读正常但评论低 | 互动与系列 | engagement-conversion、search-intent-map | -| 点赞低 | 高数据创意模式、定位与核心传达 | high-performer-patterns、positioning-core | -| 分享低 | 选题与标题包装、互动与系列 | topic-portfolio、engagement-conversion | -| 关注转化低 | 定位与核心传达、互动与系列 | bio-profile、series-signature、engagement-conversion | -| 搜索流量弱 | 搜索意图与用户问题、选题与标题包装 | search-intent-map、title-packaging、content-form-mix | +| 播放低 | 选题、标题与封面、关键词与用户问题 | topic-angle、title-cover、search-intent | +| 完播估算低 | 内容创意、制作规格、口播文案 | content-idea、production-spec、narration-script | +| 搜索流量弱 | 关键词与用户问题 | search-intent | +| 关注转化低 | 账号运营子模块 | account-bio、content-mix-cadence | ### 平台混杂因素(归因前必排) @@ -86,7 +98,7 @@ content-calibrator eval --platform xhs --dna-id # 指定 DNA 1. 判定只看比值与走向,绝对值只作上下文。 2. 逐条排除平台混杂因素,输出替代假设检验结果。 -3. 回读 `xhs/dna//.dna.md` / `.template.md` 与待评估作品(`source_folder` 下的 `note.md`、`brief.md`、`voiceover.md` 与图片/成片线索),把趋势变化落到 template 语义段与 14 维。 +3. 回读 `xhs/dna//.dna.md` / `.template.md` 与待评估作品(`source_folder` 下的 `note.md`、`brief.md`、`voiceover.md` 与图片/成片线索),把趋势变化落到 template 语义段与 DNA 维度。 ### Step 3 - 报告与标记 diff --git a/crews/main/skills/expert-xhs/workflows/style-dna.md b/crews/main/skills/expert-xhs/workflows/style-dna.md index 6bc5779d..ed25b588 100644 --- a/crews/main/skills/expert-xhs/workflows/style-dna.md +++ b/crews/main/skills/expert-xhs/workflows/style-dna.md @@ -1,13 +1,13 @@ -# 小红书账号级 DNA 创建与更新 Workflow +# 小红书 DNA 创建与更新 Workflow -本 Workflow 负责 DNA report、DNA 文档、搜索意图地图与 DNA template 的创建与更新。维度框架以 `xhs-style-profiler` 的 `references/account-dna-framework.md` 为准(账号级 DNA v1)。 +本 Workflow 负责 DNA report、DNA 文档、搜索意图地图与 DNA template 的创建与更新。维度框架以 `xhs-style-profiler` 的 `references/note-dna-framework.md`(图文)与 `references/video-dna-framework.md`(视频)为准(DNA v2)。 ## 边界 -- DNA 是账号级运营框架,不是单篇笔记写作模板。 +- DNA 是从一批作品样本中提取、聚合出的内容生产规则集,不存在「平台级 DNA」或「账号级 DNA」的说法;样本可以来自多个账号,也可以来自用户指定的一个账号的发布列表批量提取。 - 账号初始化与默认 `dna-0` 建立走 `account-setup.md`;对标样本先走 `account-benchmark.md`。 - DNA 如何用于内容生产走 `content-production.md`;改稿走 `editing.md`;数据复盘走 `review.md`。 -- 图文由 main agent 直接生产;视频全案只到 Brief,制作委托 `content-producer`。 +- 图文由 main agent 直接生产;视频全案只到 Brief(+ 口播文案),制作委托 `content-producer`。 ## 入口判断 @@ -30,8 +30,9 @@ 1. 用户指定 `dna-id` 时使用该 DNA。 2. 未指定或说“默认 DNA”时使用 `dna-0`。 -3. 对标样本必须进入独立 DNA,不直接写入 `dna-0`;采纳后再通过局部融合更新。 -4. 目标 DNA 不存在时,先走 `account-setup.md` 或按用户明确指定的新 `dna-id` 初始化。 +3. **作品类型分流**:一个 `dna-id` 只承载一种作品类型。小红书默认 `dna-0` 是图文(`--kind note`),视频笔记另建 dna-id(如 `dna-0-video`);混型 `build` 会直接报错。 +4. 对标样本必须进入独立 DNA,不直接写入 `dna-0`;采纳后再通过局部融合更新。 +5. 目标 DNA 不存在时,先走 `account-setup.md` 或按用户明确指定的新 `dna-id` 初始化。 ## 存储结构 @@ -49,6 +50,8 @@ xhs/dna/{dna-id}/ ## 样本获取 +**先判作品类型**(视频 / 图文):它决定用哪套维度框架、`--kind` 取值与目标 dna-id;同一个 DNA 不混型。 + | 来源 | 处理 | | --- | --- | | 图文笔记链接 | `xhs-content-ops` 下载正文、图片与互动数据 | @@ -71,13 +74,15 @@ xhs/dna/{dna-id}/ ### Step 1 - 准备样本 -1. 把笔记整理为 `.md`:首个一级标题为标题,正文保留换行与内联话题标签。 -2. 确定目标 `dna-id`、`sample-id`、样本权重和 focus。 -3. 整理账号观测、搜索关键词与用户问题,供 Agent 补进 report。 +1. 判定作品类型(视频 / 图文),据此选框架与目标 `dna-id`。 +2. 把笔记整理为 `.md`:首个一级标题为标题,正文保留换行与内联话题标签。 +3. 确定目标 `dna-id`、`sample-id`、样本权重和 focus。 +4. 整理账号观测、搜索关键词与用户问题,供 Agent 补进 report。 ### Step 2 - 生成单篇 report ```bash +# 图文笔记(默认 kind=note) xhs-style-profiler report \ --input path/to/note.md \ --dna-id {dna-id} \ @@ -89,26 +94,28 @@ xhs-style-profiler report \ 生成 scaffold 后必须: -1. 补齐「样本与账号观测」。 -2. 回读笔记原文,补齐 14 维的单篇结论、证据和可复用信号。 -3. 搜索关键词必须落到用户可能提问;单篇只记候选,不直接判定账号级搜索意图。 -4. 单篇样本不得推导账号简介、图文/视频比例、发布节奏或高数据共性。 -5. 视觉语言必须有图片证据;口播文案 DNA 样本不足时保持未启用。 +1. 补齐「样本观测」:作品类型、样本来源、账号与简介、发布时间、数据线索、图片数量与来源、关键词与标签(缺失写「未观测」)。 +2. 回读原文,补齐各维度的单篇结论、原文证据与可复用信号。 +3. `search-intent` 必须落到用户可能的提问原句,不只抄平台标签;单篇只记候选。 +4. 图组视觉必须有图片证据;视频样本另给视频内容形态与制作指向(`expert-video` 的某个 workflow,或 main 的素材加工技能)。 +5. 账号运营子模块(简介写法、内容形式比例、发布习惯)只在样本来自对标账号批量提取时填写,单篇样本写「未观测」。 ### Step 3 - 聚合 DNA ```bash -xhs-style-profiler build --dna-id {dna-id} +xhs-style-profiler build --dna-id {dna-id} # 图文 DNA(默认 kind=note) +xhs-style-profiler build --dna-id {dna-id}-video --kind video # 视频 DNA ``` Agent 必须读取全部 report,按权重/focus 聚合: - 高频共性、高权重偏好、局部借鉴、孤例、例外分开写。 -- 标注样本覆盖度;单篇/少量样本不得称为稳定账号 DNA。 +- 标注样本覆盖度;单篇或少量样本不得称为稳定结论。 - 聚合「关键词 → 用户问题 → 内容形式」的搜索意图地图。 -- 高数据内容要回读创意、关键词与内容形式,不能只归因阅读量。 +- 视频样本的形态结论必须聚合成明确的**制作指向**,供 Brief 的 `workflow` 字段直接引用。 +- 高数据内容要回读创意、形态与包装,不能只归因播放量 / 阅读量。 - 为每个维度写聚合结论、报告依据和可执行规则。 -- 确保 DNA 文档能推导 template。 +- 确保 DNA 文档能推导 template;账号运营子模块的结论只留在 DNA 文档。 ## 更新已有 DNA @@ -127,7 +134,7 @@ xhs-style-profiler update \ ### 用户偏好 -用户偏好不直接入库。通过 `--user-input` 传入后,Agent 映射到具体维度,并把原话转译为账号级规则与 Brief/图文生产规则。 +用户偏好不直接入库。通过 `--user-input` 传入后,Agent 映射到具体维度,并把原话转译为具体维度的创作规则与 Brief / 图文生产规则。 ### 局部借鉴 @@ -135,22 +142,23 @@ xhs-style-profiler update \ ### 表现反馈 -复盘产生的建议先列证据与影响维度,经用户确认后写入 DNA;不得把一次数据波动直接升格为账号规则。 +复盘产生的建议先列证据与影响维度,经用户确认后写入 DNA;不得把一次数据波动直接升格为 DNA 规则。 ## DNA 使用接口 -- **图文生产**:读取 DNA 文档与 template,确定定位、选题、标题包装、搜索意图、内容形式与发布节奏,由 main agent 直接写图文。 -- **视频全案**:main agent 产出 Brief;Brief 写明 Pipeline、素材授权、验收标准、交付边界。未指定 Pipeline 时 CP 自由发挥,指定时必须采用。 -- **口播类视频**:若口播文案 DNA 已启用,main agent 写口播文案并随 Brief 交付;CP 不重写策略文案。 -- **搜索优化**:选题和标题必须覆盖主关键词、长尾句与用户可能提问,不堆砌标签。 +- **图文笔记**:读取图文 DNA 文档与 template(含关键词与用户问题段),main agent 直接生产。 +- **视频全案**:读取视频 DNA 文档与 template,main agent 产出 **Brief**(+ 口播类的口播文案)。Brief 写明选题与观看理由、标题与简介、内容创意、关键词与用户问题、`workflow`(视频形态的制作指向)、制作规格、素材清单与授权(绝对路径)、验收标准、闸门批准人。 +- **Brief 不含 DNA 信息**:Content Producer 看不到 main 的 DNA,只按 Brief 制作;也不要把 DNA 文档路径写进 Brief。 +- **口播类视频**:口播文案子模块启用时,口播终稿由 main agent 写好并随 Brief 交付;真人口播时由 main agent 向用户取得录音文件。CP 不重写策略文案。 +- **工作区**:main 不替 CP 建工作区,也不指定项目目录;CP 自建工作区,双方 T3 权限可互访取文件。 ## 对标接口 -对标样本进入独立 `dna-id`;比较时输出定位、选题、标题包装、内容形式、发布节奏、高数据创意、搜索意图与制作管线的差异。用户明确采纳后才融合进 `dna-0`。 +对标样本进入独立 `dna-id`(同样按作品类型分流);比较时输出选题、标题与封面、内容创意、关键词与用户问题、正文表达与图组视觉的差异,以及(账号批量样本才有的)账号运营子模块差异:简介写法、内容形式比例、发布习惯。用户明确采纳后才通过局部融合写进目标 DNA。 ## 编排原则 -- 一个生产任务只使用一个 DNA;需要融合时先更新 DNA。 +- 一个生产任务只使用一个 DNA,且作品类型与任务一致;需要融合时先更新 DNA。 - 样本、用户输入、数据反馈必须可追溯。 -- 账号级结论必须有覆盖度;不足就写未观测。 -- Template 只写 main agent 可执行的图文输入/Brief 规则,不写成片制作细节。 +- 账号运营子模块(简介写法、内容形式比例、发布习惯)只在对标账号批量样本下填写,且只进 DNA 文档不进 template;覆盖度不足就写未观测。 +- Template 只写 main agent 可执行的规则:视频类 = Brief 正文模板 + 口播文案模板;图文类 = 图文写作模板。都不写成片制作细节。 diff --git a/crews/main/skills/published-track/SKILL.md b/crews/main/skills/published-track/SKILL.md index c8764002..d800ab53 100644 --- a/crews/main/skills/published-track/SKILL.md +++ b/crews/main/skills/published-track/SKILL.md @@ -51,7 +51,7 @@ published-track init-db 每张表共享通用字段:`id`(自增主键)、`title`、`content_type`(article/video/post)、`source_folder`(原始文件夹,如 `wx_mp/outputs/xxx`,**不做唯一约束,同内容可同平台多次发布**)、`publish_url`、`publish_date`(YYYY-MM-DD)、`distribute_status`(0=待分发,1=无需分发,2=已分发)、`notes`、`created_at`、`updated_at`。各平台特有互动指标默认 0,另有 `top_comment`(主要留言摘要)。 -> **视频号(`pub_wx_channel`)特例**:视频号作品没有「标题」概念,只有视频简介——`title` 列存的是**完整视频简介**(含 hashtag,最长约 300 字),即 `wechat-channels-publish` Step 5 填的视频简介。`wx-channel-engagement` 抓取按它匹配后台作品管理页。调用方调 `record.sh --platform wx_channel --title` 必须传完整视频简介,不要拆短标题。 +> **视频号(`pub_wx_channel`)特例**:`title` 列存的是**完整视频描述**(含 hashtag,最长约 300 字),即 `wechat-channels-publish` Step 5 填的视频描述;`wx-channel-engagement` 抓取按它匹配后台作品管理页。发布页改版后虽可另填**短标题**(官方称填了有更多流量),但作品管理页不展示短标题,因此**短标题不入库**——调用方调 `record.sh --platform wx_channel --title` 必须传完整视频描述,不要传短标题、也不要把两者拼接。 ### DNA 关联字段(v3 schema) diff --git a/crews/main/skills/viral-chaser/SKILL.md b/crews/main/skills/viral-chaser/SKILL.md index c72374ee..70b206d1 100644 --- a/crews/main/skills/viral-chaser/SKILL.md +++ b/crews/main/skills/viral-chaser/SKILL.md @@ -1,6 +1,6 @@ --- name: viral-chaser -description: 用户转发/分享抖音、B站、小红书视频链接(v.douyin.com / b23.tv / xhslink.com / xhslink.cn),要求拆解分析时使用(俗称「追爆」):下载视频、ASR 转写、结构化拆解,产出拆解分析报告。仅产出报告,视频生产需另外委托 content-producer。 +description: 用户转发/分享抖音、B站、小红书视频链接(v.douyin.com / b23.tv / xhslink.com / xhslink.cn),要求拆解分析时使用(俗称「追爆」):下载视频、ASR 转写、全片关键帧抽取、结构化拆解,产出拆解分析报告(含视频 meta、逐帧画面分析、结构占比、内容形态与制作指向)。也是各平台 DNA 采样的取数主力:可按 DNA 样本格式归档转录稿。抖音图集(图文作品)只下载图片与文本,不做转写。仅产出报告,视频生产需另外委托 content-producer。 metadata: openclaw: emoji: 🎯 @@ -49,7 +49,7 @@ Use this skill when: **本技能仅产出追爆报告**,不生成脚本,不制作视频。如需据此生成视频,需另行委托 `content-producer` (spawn subagent)执行。 -**Supported platforms:** 抖音(Douyin)、B 站(Bilibili)、小红书(XHS — 仅视频笔记, 如果是图文的话则转向执行 `xhs-content-ops` 技能。) +**Supported platforms:** 抖音(Douyin — 视频作品 + 图集图文作品)、B 站(Bilibili)、小红书(XHS — 仅视频笔记, 如果是图文的话则转向执行 `xhs-content-ops` 技能。) **Not supported:** 微信视频号、TikTok @@ -102,14 +102,23 @@ The script outputs a **JSON object to stdout**. Read it and proceed with analysi { "ok": true, "platform": "douyin", + "kind": "video", "metadata": { "contentId": "...", "title": "...", "desc": "...", "author": "...", - "durationSeconds": 89, + "authorSignature": "账号简介(对标账号样本的 account-bio 维度用)", + "authorUid": "...", + "durationSeconds": 36, + "width": 1080, + "height": 1920, + "orientation": "vertical", + "ratio": "vertical", + "publishTime": "2026-09-04T10:12:00.000Z", + "hashtags": ["话题1", "话题2"], "coverUrl": "...", - "stats": { "playCount": 0, "likeCount": 0, "commentCount": 0 } + "stats": { "playCount": 0, "likeCount": 12, "commentCount": 0, "shareCount": 0, "collectCount": 0 } }, "transcript": { "text": "全文转录...", @@ -125,6 +134,11 @@ The script outputs a **JSON object to stdout**. Read it and proceed with analysi } ``` +- `kind`: `video`(正常视频作品)或 `note`(抖音图集图文作品)。`note` 时 `transcript` 为 `null`、`frames` 为空,另给 `images: [".../image_00.jpg", ...]`(最多 20 张)与 `metadata.imageCount`;图文样本的视觉证据就是这些图片。 +- `metadata.orientation`: 由 width/height 推出的 `vertical` / `horizontal` / `square`;平台拿不到宽高时为空串,必须自己 `ffprobe` 本地成片补齐,不得猜。 +- 各字段平台支持度不同:抖音给全套;小红书 HTML 路线给 `hashtags` 与互动计数(无播放数、无发布时间);B 站给时长与三项互动。缺失一律在报告里写「接口未返回」,不编造。 +- `frames`: 最多 12 张,覆盖开场(0s / 3s)、各口播段中点与全片比例点(25% / 50% / 63% / 75% / 90%)——**反转植入类作品的反转点通常在 55%-76%,只抽前几秒会完全错过**。 + - `transcript.estimated`: `false` 表示 `segments` 是火山 ASR 返回的**真实时间戳**(utterance 级,毫秒精度转秒);`true` 仅在接口异常未返回 utterances 时出现,此时按句切分全文并按字数比例在音频时长上估算分段,时间区间为近似值。正常情况下始终为 `false`。 **Exit codes:** @@ -146,29 +160,98 @@ Read: /ref//references/frames/frame_01_3s.jpg ## Analysis Framework -After receiving the JSON output and reading the frames, generate a **追爆报告** in Markdown and save it to `/ref//raw_article.md`. +读完 JSON 与全部关键帧后,产出**追爆报告**,保存到 `/ref//raw_article.md`。报告既要能给人看,也要能直接喂 DNA 采样(见最后一节)。 + +### 1. 作品 meta 信息 + +| 项 | 内容 | +|----|------| +| 作品类型 | 视频 / 图文(`kind`) | +| 时长 | xx s(`durationSeconds`) | +| 画幅 | 竖屏 9:16 / 横屏 16:9(`orientation` + `width`×`height`) | +| 发布时间 | `publishTime`(接口未返回时写「未返回」) | +| 作者与简介 | `author` + `authorSignature`(对标账号样本必记) | +| 话题标签 | `hashtags` | +| 互动数据 | 播放 / 点赞 / 评论 / 分享 / 收藏(逐项写;接口未返回的项注明) | +| 来源 | 原视频 URL + 内容 ID + 抓取日期 | + +### 2. 内容摘要 + +1–2 句:这条作品给观众的核心价值或核心情绪是什么。 + +### 3. 开头钩子分析(前 0–10 秒) + +基于 `transcript.segments` 中 `start < 10` 的段: + +- **钩子类型**:提问型 / 冲突型 / 反转型 / 数字型 / 悬念型 / 痛点型 / 利益型 +- **具体文案**:逐字摘录开场句 +- **声画是否同步**:画面、字幕、口播是否在同一秒传递同一个重点 +- **效果评估**:这个钩子为什么留人(或为什么不留) + +### 4. 结构拆解(按时间占比) + +按功能把全片切段,**每段给时间区间与占全片百分比**: + +| 段落 | 时间区间 | 占比 | 功能 | 核心内容 | +|------|---------|------|------|---------| +| 开场 | 0–Xs | xx% | 钩子/引入 | ... | +| 主体一 | X–Ys | xx% | 信息/剧情推进 | ... | +| 转折 | Y–Zs | xx% | 反转/揭示 | ... | +| 收尾 | Z–结束 | xx% | CTA/情绪收尾 | ... | + +必须额外标注: + +- **反转点位置**(若有):占总时长的百分比,以及反转是靠什么衔接的(口播因果句 / 意象复用 / 身份彩蛋 / 戏中戏)。 +- **植入或转化段占比**(若有):产品/服务出现在哪一段、占比多少、是否集中。 +- **主悬念**:一句话复述贯穿全片的悬念;说明它在哪一秒被回答。 + +### 5. 关键帧逐帧分析 + +对 `frames` 里每一张都用视觉模型读取,逐帧一行(不要只看首帧): + +| 帧 | 时间码 / 占比 | 画面内容 | 字幕或贴图文字 | 景别与构图 | 色调与质感 | 品牌/产品是否出现 | 可否作封面 | +|----|--------------|----------|---------------|-----------|-----------|------------------|-----------| +| frame_00 | 0s / 0% | ... | ...(逐字抄) | 近景/中景/远景、主体位置 | 冷暖、饱和、颗粒 | 是/否 | 是/否 | -### 1. 内容摘要 -1–2 sentences: what core value does this video deliver to viewers? +`--no-frames` 或 frames 为空时注明:「(跳过视觉分析,请重新运行不带 --no-frames 参数)」。 -### 2. 开头钩子分析(前 0–10 秒) -Based on `transcript.segments` where `start < 10`: -- **钩子类型**: 提问型 / 冲突型 / 反转型 / 数字型 / 悬念型 / 痛点型 / 利益型 -- **具体文案**: quote the exact opening line(s) -- **效果评估**: why this hook works (or doesn't) +### 6. 视觉与声音风格汇总 -### 3. 内容结构拆解 -Based on transcript segments, divide into logical sections: +基于逐帧结果与转录: -| 段落 | 时间区间 | 功能 | 核心内容 | -|------|---------|------|---------| -| 开场 | 0–Xs | 钩子/引入 | ... | -| 主体一 | X–Ys | 价值/信息传递 | ... | -| 主体二 | Y–Zs | 深化/转折 | ... | -| 收尾 | Z–结束 | CTA/情绪收尾 | ... | +- **色调风格**:暖/冷、高饱和/低饱和、黑白;是否有明显的两段式对比(如解说段冷暗、植入段明亮) +- **画面形态**:实拍 / 影视或开源片源 / 录屏 / 图文卡片 / AIGC / 混剪 +- **字幕与贴图**:字体粗细、位置、背景框、箭头标注、重点字变色放大 +- **声音形态**:原声口播 / TTS 旁白 / 纯画面 + 字幕;BGM 类型与主从关系;音效使用(如反转处 whoosh) +- **整体视觉标签**:3–5 个关键词 -### 4. 爆款元素评估 -Rate each element as **强 / 中 / 弱** with a one-line explanation: +### 7. 内容形态判定与制作指向 + +判定这条作品属于哪种视频内容形态,并给出**制作指向**——只能写真实存在的资源名: + +| 观测到的形态 | 制作指向 | +|-------------|----------| +| 影视解说 / 剧情解说 + 反转植入(「万万没想到」式) | Content Producer `expert-video` → Reversal Ad workflow | +| 口播类(真人口播出镜,或旁白 + 画面) | Content Producer `expert-video` → Narration Video workflow | +| 一句文稿转视觉隐喻的纸拼贴动画 | Content Producer `expert-video` → Collage B-roll workflow | +| 纯 AIGC 动画 / 剧情短片 / 蒙太奇(需从零出脚本分镜) | Content Producer `expert-video` → 通用阶段链(narrative / motion / montage) | +| 已有素材简单拼接、加旁白、烧字幕 | main `video-edit` | +| 已有真人口播素材去口气词、剪高光 | main `talking-head-cut` | +| 产品操作录屏 | main `ui-demo` | +| 图文笔记 | main 直接生产(图文不走 CP) | + +判定要写依据:口播占比、素材来源、画面是否连续叙事、有无产品段。 + +### 8. 内容创意 + +- **创意内核**:一句话说清这条作品的创意是什么 +- **展开逻辑**:悬念 / 反转 / 递进 / 对比 / 清单 / 实测 +- **记忆点**:观众会记住或复述的那一个点 +- **可复用套路**:换成别的主题还能怎么用(这是 DNA 的 `content-idea` 维度要的) + +### 9. 爆款元素评估 + +每项评 **强 / 中 / 弱** + 一句说明: | 元素 | 评级 | 说明 | |------|:----:|------| @@ -181,22 +264,43 @@ Rate each element as **强 / 中 / 弱** with a one-line explanation: | 视觉冲击(基于关键帧) | | | | 节奏把控 | | | -### 5. 视觉风格分析(基于关键帧图片) -After reading the frame images: -- **色调风格**: 暖色系/冷色系/高饱和/低饱和/黑白 -- **构图类型**: 人脸近景 / 产品展示 / 场景空镜 / 文字卡片 / 混合 -- **字幕/文字覆盖**: 字体粗细、位置、是否有背景框、动画感 -- **整体视觉标签**: 3–5 个关键词(如:「真实感」「强对比」「高信息密度」) +### 10. 可借鉴点与目标受众 -If `--no-frames` was used or frames is empty, note: "(跳过视觉分析,请重新运行不带 --no-frames 参数)" +- **可借鉴点**:3–5 条,每条一句、可直接执行。 +- **目标受众**:一句话人群画像。 +- **ASR 校正注记**:转写与画面字幕不一致时(谐音、专有名词、案名),逐条列出原文与校正依据(哪一帧的字幕)。 -### 6. 可借鉴点 -3–5 concise, directly actionable techniques. One sentence each. +### 11. DNA 样本归档(喂 style-profiler 用) -### 7. 目标受众 -One sentence describing the primary audience persona. +这条作品要进 DNA 时,把报告转成一份**样本文字稿**,落 `/ref//transcripts/.md`,格式如下(`-style-profiler report --input` 直接吃这个文件:首个一级标题即作品标题): ---- +```markdown +# 样本文字稿:<作品标题>(sample-id: ) + +> 来源:<对标账号名 + 账号 ID / 用户提供> +> 原视频 URL:(内容 ID ) +> 时长:xxs | 画幅:竖屏 9:16 | 点赞 x | 评论 x | 播放量:<数值或「接口未返回」> +> 发布时间: | 抓取日期:YYYY-MM-DD | 拆解报告:/ref//raw_article.md +> ASR 说明:火山引擎极速版,真实时间戳(estimated=false) +> ⚠️ ASR 谐音校正:<逐条列出,注明依据的画面字幕帧;无则写「无」> +> 转录约定:逐字引语为原话引用(含时间戳);标注 [剧情概括] 的段为报告中段转述,非逐字。 + +## 口播全文(含时间戳) + +- 0.04–5.32s:<逐字> +- 5.36–8.84s:<逐字> +- 8.9–14.3s:[剧情概括] <转述> + +## 结构标注(取自拆解报告) + +- 0–24.6s(≈71%):<段功能与内容> +- 24.7–25.7s(≈3%):<反转过渡> +- 25.7–35.3s(≈26.5%):<植入段> +``` + +- 图文作品(`kind: "note"`)没有口播全文:把正文原样抄进「## 正文全文」,图片路径列在「## 图组」下,其余元信息与结构标注照写。 +- 逐字引语与转述必须分开标注,不得把概括写成原话。 +- 拿不到的字段写「接口未返回」或「未观测」,不编造。 ## Notes diff --git a/crews/main/skills/viral-chaser/scripts/platforms/douyin.ts b/crews/main/skills/viral-chaser/scripts/platforms/douyin.ts index 0cc07e92..a503e521 100644 --- a/crews/main/skills/viral-chaser/scripts/platforms/douyin.ts +++ b/crews/main/skills/viral-chaser/scripts/platforms/douyin.ts @@ -19,7 +19,20 @@ export interface VideoInfo { coverUrl: string durationMs: number author: string - stats: { playCount: number; likeCount: number; commentCount: number } + stats: { playCount: number; likeCount: number; commentCount: number; shareCount: number; collectCount: number } + /** 画面宽高与平台标注的画幅比(vertical / horizontal)——DNA 的制作规格维度要用 */ + width: number + height: number + ratio: string + /** 发布时间(unix 秒)——账号运营子模块(发布习惯)要用 */ + createTime: number + /** 作者简介与 UID——对标账号样本的 account-bio 维度要用 */ + authorSignature: string + authorUid: string + /** 话题标签名(text_extra.hashtag_name) */ + hashtags: string[] + /** 图文作品的图片地址列表;视频作品为空数组 */ + imageUrls: string[] } // ── Signed GET request(a_bogus + COMMON_PARAMS 走 _shared)──────────────── @@ -65,6 +78,21 @@ export async function getDouyinVideo(awemeId: string, session: SessionData): Pro const stats = detail.statistics ?? {} + // 话题标签:text_extra 里 type=1 的项带 hashtag_name + const textExtra: any[] = Array.isArray(detail.text_extra) ? detail.text_extra : [] + const hashtags: string[] = textExtra + .map((t: any) => t?.hashtag_name ?? "") + .filter(Boolean) + + // 图文作品(图集笔记):images[] 有内容且无播放地址 + const imageArr: any[] = Array.isArray(detail.images) ? detail.images : [] + const imageUrls: string[] = imageArr + .map((img: any) => { + const list: string[] = img?.url_list ?? [] + return list[1] ?? list[0] ?? "" + }) + .filter(Boolean) + return { contentId: awemeId, title: detail.desc ?? "", @@ -77,6 +105,16 @@ export async function getDouyinVideo(awemeId: string, session: SessionData): Pro playCount: stats.play_count ?? 0, likeCount: stats.digg_count ?? 0, commentCount: stats.comment_count ?? 0, + shareCount: stats.share_count ?? 0, + collectCount: stats.collect_count ?? 0, }, + width: video.width ?? 0, + height: video.height ?? 0, + ratio: video.ratio ?? "", + createTime: detail.create_time ?? 0, + authorSignature: detail.author?.signature ?? "", + authorUid: detail.author?.uid_str ?? detail.author?.unique_id ?? "", + hashtags, + imageUrls, } } diff --git a/crews/main/skills/viral-chaser/scripts/platforms/xhs.ts b/crews/main/skills/viral-chaser/scripts/platforms/xhs.ts index 75305ba5..e4dc9023 100644 --- a/crews/main/skills/viral-chaser/scripts/platforms/xhs.ts +++ b/crews/main/skills/viral-chaser/scripts/platforms/xhs.ts @@ -32,6 +32,8 @@ export interface VideoInfo { durationMs: number author: string stats: { playCount: number; likeCount: number; commentCount: number; collectCount: number; shareCount: number } + /** 话题标签名(HTML 路线的 tagList) */ + hashtags: string[] mediaFormat?: string } @@ -121,5 +123,6 @@ export async function getXhsVideo( collectCount: note.stats.collectCount, shareCount: note.stats.shareCount, }, + hashtags: note.tags ?? [], } } diff --git a/crews/main/skills/viral-chaser/scripts/viral_chaser.ts b/crews/main/skills/viral-chaser/scripts/viral_chaser.ts index fee2585d..7aafb9bd 100644 --- a/crews/main/skills/viral-chaser/scripts/viral_chaser.ts +++ b/crews/main/skills/viral-chaser/scripts/viral_chaser.ts @@ -40,6 +40,11 @@ function errExit(msg: string, code = 1): never { process.exit(code) } +function unixToIso(seconds?: number): string { + if (!seconds || seconds <= 0) return "" + return new Date(seconds * 1000).toISOString() +} + function getTmpDir(contentId: string): string { // Honor OUTPUT_DIR env var (SKILL.md sets it to /ref//references). // Fall back to a per-id tmp dir when unset. @@ -56,26 +61,33 @@ async function extractKeyFrames( outputDir: string, segments: Array<{ start: number; end: number; text: string }>, noFrames: boolean, + durationSeconds = 0, ): Promise { if (noFrames) return [] const framesDir = join(outputDir, "frames") mkdirSync(framesDir, { recursive: true }) - // Build list of timestamps: 0s, 3s, segment midpoints. - // Intentionally front-loaded (slice(0, 8) keeps the earliest 8): for short - // videos the opening is what matters most — the goal is to not let viewers - // scroll past the first few seconds. + // 采样点:开场(0s / 3s,看钩子与首帧包装)+ 各口播段中点(看画面与口播的对应) + // + 全片比例点 25% / 50% / 63% / 75% / 90%(看结构转折——反转植入类作品的反转点 + // 通常落在 55%-76%,只采前几秒会完全错过)。去重后按时间排序,最多 12 张。 const timestamps: number[] = [0, 3] for (const seg of segments) { const mid = Math.floor((seg.start + seg.end) / 2) if (!timestamps.includes(mid)) timestamps.push(mid) } + if (durationSeconds > 0) { + for (const ratio of [0.25, 0.5, 0.63, 0.75, 0.9]) { + const ts = Math.min(Math.floor(durationSeconds * ratio), Math.max(durationSeconds - 1, 0)) + if (!timestamps.includes(ts)) timestamps.push(ts) + } + } + timestamps.sort((a, b) => a - b) const framePaths: string[] = [] let frameIdx = 0 - for (const ts of timestamps.slice(0, 8)) { // max 8 frames, front-loaded + for (const ts of timestamps.slice(0, 12)) { // max 12 frames, 覆盖全片 const timeStr = new Date(ts * 1000).toISOString().substring(11, 19) const outPath = join(framesDir, `frame_${String(frameIdx).padStart(2, "0")}_${ts}s.jpg`) try { @@ -154,6 +166,10 @@ async function main(): Promise { coverUrl: string; durationMs?: number; durationSeconds?: number author: string; stats: Record contentId: string; mediaFormat?: string + // DNA 采样补充字段(平台能给则给,缺失为 0 / 空) + width?: number; height?: number; ratio?: string + createTime?: number; authorSignature?: string; authorUid?: string + hashtags?: string[]; imageUrls?: string[] } try { @@ -181,13 +197,55 @@ async function main(): Promise { errExit(`获取视频信息失败: ${msg}`) } + const tmpDir = getTmpDir(contentId) + mkdirSync(tmpDir, { recursive: true }) + + // 4b. 图文作品分支(抖音图集笔记):无播放地址但有图片列表时,只下载图片 + + // 输出文本与 meta,不做音频提取 / ASR / 抽帧。这类样本喂图文 DNA(note 框架), + // 视觉证据就是下载到的图片。 + const imageUrls = videoInfo!.imageUrls ?? [] + if (!videoInfo!.videoUrl && imageUrls.length) { + process.stderr.write(`[viral-chaser] 图文作品:下载 ${imageUrls.length} 张图片...\n`) + const imagePaths: string[] = [] + for (let i = 0; i < imageUrls.length && i < 20; i++) { + const outName = `image_${String(i).padStart(2, "0")}.jpg` + try { + const r = await downloadVideo(imageUrls[i], tmpDir, outName, readUserAgent(sessionPlatform) || "") + if (r?.filePath) imagePaths.push(r.filePath) + } catch { + // 单张失败不致命:跳过继续 + } + } + printJson({ + ok: true, + platform, + kind: "note", + metadata: { + contentId, + title: videoInfo!.title, + desc: videoInfo!.desc, + author: videoInfo!.author, + authorSignature: videoInfo!.authorSignature ?? "", + publishTime: unixToIso(videoInfo!.createTime), + hashtags: videoInfo!.hashtags ?? [], + coverUrl: videoInfo!.coverUrl, + stats: videoInfo!.stats, + imageCount: imagePaths.length, + }, + transcript: null, + frames: [], + images: imagePaths, + localPaths: { tmpDir }, + }) + process.stderr.write(`[viral-chaser] 完成(图文)。图片: ${imagePaths.length} 张\n`) + return + } + if (!videoInfo!.videoUrl) { errExit("未能获取视频下载地址(可能需要登录或视频已删除)") } // 5. Download video - const tmpDir = getTmpDir(contentId) - mkdirSync(tmpDir, { recursive: true }) process.stderr.write(`[viral-chaser] 开始下载视频...\n`) // UA 走独立 .ua.json 文件(原则 4:cookie + UA 同指纹同源)。 @@ -223,11 +281,15 @@ async function main(): Promise { // 8. Extract key frames process.stderr.write(`[viral-chaser] 提取关键帧...\n`) + const durationForFrames = + videoInfo!.durationSeconds ?? + (videoInfo!.durationMs ? Math.round(videoInfo!.durationMs / 1000) : audioResult!.durationSeconds) const framePaths = await extractKeyFrames( downloadResult!.filePath, tmpDir, transcript!.segments, noFrames, + durationForFrames, ) // 9. Output result JSON to stdout @@ -235,15 +297,29 @@ async function main(): Promise { videoInfo!.durationSeconds ?? (videoInfo!.durationMs ? Math.round(videoInfo!.durationMs / 1000) : audioResult!.durationSeconds) + const width = videoInfo!.width ?? 0 + const height = videoInfo!.height ?? 0 + const orientation = + width && height ? (height > width ? "vertical" : height < width ? "horizontal" : "square") : "" + const result = { ok: true, platform, + kind: "video", metadata: { contentId, title: videoInfo!.title, desc: videoInfo!.desc, author: videoInfo!.author, + authorSignature: videoInfo!.authorSignature ?? "", + authorUid: videoInfo!.authorUid ?? "", durationSeconds, + width, + height, + orientation, // vertical / horizontal / square —— DNA 制作规格维度用 + ratio: videoInfo!.ratio ?? "", + publishTime: unixToIso(videoInfo!.createTime), + hashtags: videoInfo!.hashtags ?? [], coverUrl: videoInfo!.coverUrl, stats: videoInfo!.stats, }, diff --git a/docs/d21-symlink-skill.md b/docs/d21-symlink-skill.md index e02c70a5..8ecd20a9 100644 --- a/docs/d21-symlink-skill.md +++ b/docs/d21-symlink-skill.md @@ -138,7 +138,7 @@ login-manager check douyin # wrapper 在 PATH 中 | bilibili-publish | scripts/publish_bilibili.py | wrapper → py | | design-system-picker | scripts/pick.sh | wrapper → sh | | init-workspace | scripts/init.sh | wrapper → sh | -| manim-explainer | scripts/render-manim.sh | wrapper → sh | +| ~~manim-explainer~~ | scripts/render-manim.sh | wrapper → sh(**2026-09-10 技能删除**,wrapper 与 bin 软链由 `expose_skill_wrappers` 的悬挂清理回收) | | siliconflow-tts | scripts/tts.py | wrapper → py | | siliconflow-video-gen | scripts/gen.py | wrapper → py | | awada-channel-setup | scripts/apply-awada-config.py | wrapper → py | @@ -217,6 +217,8 @@ dev plan §Phase 7 续 写"验收": ## 六、变更历史 +- **2026-09-10**:`content-producer` 引入专家包,四个技能整合为两个包——`video-producer` / `collage-broll` 收纳进 `expert-video/tools/`(`pipelines/` 改造为 `expert-video/workflows/`,`dna-ad-video-pipeline.md` 重构为 `reversal-ad.md`),`design-full` 收纳进 `expert-design/tools/`,`manim-explainer` 删除。**PATH wrapper 名与子命令全部不变**(`video-producer <子命令>` / `design-full `),靠 `expose_skill_wrappers` 的 `*/tools/*/` 扫描层暴露;新增 `collage-broll ` wrapper(原先 agent 直接拼 `scripts/run_gate3.py` 路径)。同时给 `expose_skill_wrappers` 与 `sync_crew_skills` 加**悬挂软链清理**(技能改名 / 收纳后,`~/.openclaw/bin` 与 `workspace-*/skills/` 里的旧软链会指向不存在的仓路径),并让 `collect_skill_script_commands` 扫 `skills//tools/*/scripts/`(否则包内 `.sh` 脚本会从 ALLOWED_COMMANDS 掉出去)。 + - **2026-08-27**:`swcr-register` 加子命令分发器 wrapper(`swcr-register `,-> scripts 下三个 Python 脚本),移出 C 类清单。同日随 expert-bd 专家包落地时曾折入 `expert-bd/tools/`,当天移出回归顶层 `crews/main/skills/swcr-register/`(软著属项目申报配套,非 BD 领域专属,跨包复用保持顶层)。wrapper 为 `SCRIPT_DIR` 自定位写法,与所在层级无关,移入移出均无需改动;`expert-bd/SKILL.md` 工具清单与 AGENTS.md 路由同步去除包内引用。同日 sales-cs-enablement / sales-cs-review 随 `sales-cs-manager` 专家包收纳至 tools/ 层(见 §4.2 表注与 §8.2 条目 21),sales-cs-enablement 转子命令分发器。 - **2026-08-21**:数据直连 DNA 改造连带 wrapper 补强:`published-track`(C 类转分发器 wrapper:`published-track `)与 `content-calibrator`(`content-calibrator `)落顶层分发器 wrapper,SKILL.md / HEARTBEAT.md / AGENTS.md / expert-wx-mp workflows 全部 PATH 化,agent 零路径拼接。继 video-edit 之后第 2、3 个分发器 wrapper。 diff --git a/docs/expert-pack-dna-architecture.md b/docs/expert-pack-dna-architecture.md index 67f5af97..082035eb 100644 --- a/docs/expert-pack-dna-architecture.md +++ b/docs/expert-pack-dna-architecture.md @@ -1,6 +1,6 @@ # 专家包(Expert Pack)+ DNA 架构规划 -> 日期:2026-08-14;2026-08-18 更新 DNA 生产模式与跨平台 profiler 规范;2026-08-20 明确跨平台 DNA 维度边界与 template 通用开头;2026-08-20 废除 rubric、数据直连 DNA(见第 11 节);2026-08-29 新增平台运营文件夹规范(见 4.2 节);2026-08-29 `dna/` 与 `calibration/` 由集中目录下沉进各平台运营文件夹(见 4.2 节);2026-09-08 抖音 / 视频号 / 小红书升级为账号级 DNA v1(见 4.7 节) +> 日期:2026-08-14;2026-08-18 更新 DNA 生产模式与跨平台 profiler 规范;2026-08-20 明确跨平台 DNA 维度边界与 template 通用开头;2026-08-20 废除 rubric、数据直连 DNA(见第 11 节);2026-08-29 新增平台运营文件夹规范(见 4.2 节);2026-08-29 `dna/` 与 `calibration/` 由集中目录下沉进各平台运营文件夹(见 4.2 节);2026-09-08 抖音 / 视频号 / 小红书 DNA 维度重做(v1,已废止);2026-09-10 三平台 DNA 回到「单篇作品提取 → 批次聚合」范式并按作品类型分框架(v2,见 4.7 节);2026-09-10 明确 main / Content Producer 的 Brief 交接契约(见 4.7 节);2026-09-10 content-producer 引入专家包 expert-video / expert-design(见 4.9 节) > 首个改造对象:`crews/main`(小贝 / main agent) --- @@ -149,6 +149,7 @@ crews/main/ - 专家包内不使用 `AGENTS.md` 作为文件名(避免与 workspace bootstrap 文件混淆,也避免被误解为会被自动注入)。 - 专家包内不保存可变 DNA。运行期生成的 DNA report、DNA 文档和 DNA template 一律写入 Workspace 的 `/dna//`;专家包只保留方法论、框架和工具。 - 非内容平台专家包(`expert-bd` / `expert-ir`,2026-08-27 落地)没有 DNA 与数据复盘概念:不配 style-profiler,也不要求 4.7 的 6 类 workflow 基线集;workflow 按业务场景组织(如 Lead Hunting / Investor Pipeline),运行期数据只有 Workspace `db/` 下的 SQLite 库。其余分层原则同样适用:薄根 `SKILL.md`(入口 + 路由)+ `workflows/*.md`(场景编排)+ `tools/`(原子技能收纳,SKILL.md 瘦身为工具说明书)+ 顶层 `.sh` wrapper 暴露到 PATH(`skill-wrappers.sh` 的 `*/tools/*/` 扫描层)。 +- 专家包不限于 main crew:`content-producer` 也已按同一分层原则改造为 `expert-video` + `expert-design`两个非平台运营专家包(无 DNA / 无 style-profiler / 无复盘 workflow),详见 4.9 节。 - 既有非内容专家包命名统一 `expert-` 前缀;`sales-cs-manager`(2026-08-27 落地)是特例:crew 管理型专家包,同样无 DNA / style-profiler / workflow 基线集要求,只覆盖 sales-cs crew 的启用(Enablement)与复盘升级(Review)两个 workflow,且自身不落任何运行期数据(无 `db/`、无 Workspace 数据目录),运行期产物都在 sales-cs workspace(feedback/ 只读、business_knowledge 软链)。 ### 4.2 专家包与运行时资产分层原则 @@ -393,11 +394,42 @@ crews//skills/expert-/tools/-style-profiler/ 5. **用户输入**:进入转译区,由 Agent 映射到平台维度后同步 DNA 文档与 template。 6. **统计边界**:脚本只做证据底座,不评分、不替代定性判断。 7. **更新模式**:合并历史 report 与新 report,保留 Agent 已完成结论和自定义段落。 -8. **template 开头两项**:微信及未升级平台固定为选题、标题(含封面图);第三项起平台自定义。抖音 / 视频号 / 小红书 v1 按下方账号级例外执行。 +8. **template 开头两项**:所有平台固定为选题、标题(含封面图);第三项起平台自定义。抖音 / 视频号 / 小红书 v2 的第三项起按下方作品类型框架执行。 -**(抖音 / 视频号 / 小红书,2026-09-08)**:这三个平台的 DNA 是账号级运营框架,不再采用单篇成片生产模板。主 DNA 覆盖定位与核心传达、选题组合、包装文案、账号简介、内容形式与比例、发布习惯、高数据创意模式、视觉/声音倾向、互动与系列、制作管线;口播文案 DNA 是独立可选块。小红书额外必须有搜索关键词与用户问题地图;DNA template 是 main agent 的图文生产输入 / 视频制作 Brief 输入,不规定逐句文案、镜头表、转场或编码细节。用户提供的单篇 / 多篇样本仍按同维度聚合,但账号简介、比例、节奏等字段必须标注样本不足或未观测。 +**(抖音 / 视频号 / 小红书,v2,2026-09-10)**:这三个平台**不存在「平台级 DNA」或「账号级 DNA」**——DNA 的生产范式与微信完全一致:按框架从**单篇作品**提取 report,再从**一批样本**聚合出 DNA 文档与 template;同一批样本可以来自多个不同账号,也可以来自用户指定的一个账号(从其发布列表批量提取)。2026-09-08 的 v1 把这三个平台的 DNA 写成「账号级运营框架」是对需求的过度解读,已废止。 + +与微信的差别只在**维度**:这三个平台的观测物是短视频 / 图文笔记,不是长文,因此不照搬 17 维里的表层与深层写作维度。v2 维度按**作品类型**分框架: + +| 平台 | 框架文件 | 维度 | +|------|----------|------| +| 抖音 | `video-dna-framework.md`(8 维)+ `note-dna-framework.md`(8 维) | 视频:选题与观看理由、标题与封面、内容创意、视频内容形态与制作指向、制作规格与视听倾向、口播文案子模块(可选)、账号运营子模块(简介写法、内容形式比例与发布习惯);图文:选题、标题与封面图组、内容创意、正文表达与语气、图组视觉、互动引导与转化、账号运营子模块 | +| 小红书 | `note-dna-framework.md`(9 维,默认)+ `video-dna-framework.md`(9 维) | 同上,另加**匹配的用户问题**(`search-intent`)——小红书最大流量池来自搜索,关键词必须落到用户可能的提问原句 | +| 视频号 | `video-dna-framework.md`(8 维,只有视频) | 同抖音视频,其中标题维度是**短标题 + 视频描述**两项(发布页都可填、官方称填短标题有更多流量;管理页不展示短标题,取数与入库只用视频描述) | + +三条硬规则: + +1. **一个 `dna-id` 只承载一种作品类型**(`--kind video|note`),混型 `build` 直接报错;抖音默认 `dna-0` 为视频、图文另建(如 `dna-0-note`),小红书默认 `dna-0` 为图文、视频另建(如 `dna-0-video`)。 +2. **子模块不是独立 DNA**:口播文案子模块(`narration-script`,参考微信的起承转合)只在口播类作品启用,用于指导 main 写同类型视频的口播文案;账号运营子模块(`account-bio`、`content-mix-cadence`)只在样本来自对标账号批量提取时填写,**只写进 DNA 文档、不进 template**。 +3. **视频 DNA 不含创作细节**:不写脚本结构、逐句台词、镜头表、转场与编码参数。视频类 template = **Brief.md 正文模板 + 口播文案模板(可选)**;图文类 template = 图文写作模板。 + +`video-form`(视频内容形态:口播 / 实拍拼接 / 影视解说+反转植入 / 纯 AIGC 动画 / 创意转场 / 录屏 / 图文卡片)必须聚合成明确的**制作指向**,且只能写真实存在的资源名:Content Producer `expert-video` 的某个 workflow(Reversal Ad / Narration Video / Collage B-roll / 通用阶段链),或 main 的素材加工技能(`video-edit` / `talking-head-cut` / `ui-demo`)。Brief 的 `workflow` 字段据此填写。 + +采样侧配套:`viral-chaser` 的输出必须够喂这套框架——视频 meta(时长、宽高与横竖屏、发布时间、作者与简介、话题标签、互动数据)、覆盖全片的关键帧(含 25%/50%/63%/75%/90% 比例点,反转点通常在 55%-76%)、按时间占比的结构拆解与反转点位置、内容形态判定与制作指向,以及可直接喂 profiler 的 DNA 样本文字稿格式。 + +在全案视频制作(非已有素材简单剪辑)上,main agent 与 Content Producer 的分工分界点是 **Brief**,两者始终是甲方 / 乙方关系: + +| 角色 | 职责 | +|------|------| +| main agent(甲方) | 选题策划;按 DNA 出 `brief.md`;拟定标题 / 短标题 / 简介等发布文案;素材准备(用户素材的简单预处理、按要求用 `ui-demo` 录屏、从 `campaign_assets/` 挑选),把素材**绝对路径**写进 Brief;口播类视频出口播文案(真人口播时向用户取得录音文件);监督并推动作为 subagent 的 CP;成片后执行发布与运营 | +| Content Producer(乙方) | 接 Brief + 已有素材,按自己技能包的 workflow 制作成片(含封面),交付成片 + 封面 + 交付说明 | + +交接契约: + +1. **交接物**:甲方给「已有素材(绝对路径)+ `brief.md` + 口播文案 / 口播录音(如有)」;乙方回「成片 + 封面图 + 交付说明」,并回报三者的绝对路径。 +2. **Brief 不含 DNA**:CP 查不到 main 的 DNA,Brief 里不写 dna-id、DNA 文档路径或 DNA 规则原文;DNA 结论由 main 消化成 Brief 的具体要求。 +3. **各自建各自的工作区**:main 不指定、也不替 CP 建工作区;CP 按自己技能包的工作区约定自建(`output_videos//`、`design_assets/YYYY-MM-DD-<任务名>/`)。双方都是 T3 权限,可互访对方工作区取文件。 +4. **不越界**:需求方向、品牌事实、卖点承诺、发布文案归 main;制作方案、分镜、素材实现、渲染参数归 CP。CP 不发布、不私信用户、不做平台运营。 -在全案视频制作(非已有素材简单剪辑),MainAgent 与 Content Producer 的分工分界点是 **Brief**:main 负责已有视频素材的简单加工、长文 / 图文内容和出具视频全案 Brief;全片制作由 Content Producer 执行。口播类视频若口播文案 DNA 已启用,口播终稿由 main 写好并随 Brief 交付。 ### 4.8 Markdown 引用与运行时数据边界 @@ -415,6 +447,42 @@ crews//skills/expert-/tools/-style-profiler/ - 不得写入专家包的 `dna/`、`references/`、`tools/` 或其他包内目录。专家包是可替换、可重建、可软链的代码与规则资产;运行期写入会造成实例状态和源仓状态耦合, reinstall / 重建 / 升级时也容易丢失。 - 专家包只发布方法论、框架和工具;DNA 生成结果保存在 Workspace `/dna//`,不通过索引登记。 +### 4.9 非 main crew 的专家包:content-producer(2026-09-10) + +专家包不只适用于 main。`content-producer` 按同一套分层原则改造,但属于**非平台运营专家包**(同 `expert-bd` / `expert-ir`):没有 DNA、没有 style-profiler、没有数据复盘,也不要求平台包的 6 类 workflow 基线集。 + +```text +crews/content-producer/ + AGENTS.md # 薄:两种工作模式 + 专家包路由 + 交接契约 + 通用约定 + skills/ + expert-video/ + SKILL.md # 乙方角色 + 两种工作模式 + workflow 清单 + 工具清单 + 交接契约 + # + 工作区约定 + 通用制作流程(阶段链 Stage 0→14,两闸门)+ 护栏 + workflows/ # workflow = 某一类型视频怎么做 + reversal-ad.md 「万万没想到」式:影视 / 剧情解说 + 突然反转植入品宣 + narration-video.md 口播类:甲方交付口播文案或真人录音,做声画实现 + collage-broll.md 纸拼贴 B-roll:隐喻 → 静帧 → i2v 三道闸门 + tools/ + video-producer/ # 原子能力(24 个子命令)+ wrapper,PATH 名不变 + collage-broll/ # check-setup / gate3 + wrapper + expert-design/ + SKILL.md # 通用骨架(七步两闸门)+ workflow 清单 + 工具清单 + workflows/ # workflow = 某一类设计任务怎么做 + web-page.md / app-ui.md / brand-visual.md + tools/design-full/ # init / pick + 设计系统库 + wrapper,PATH 名不变 +``` + +要点: + +- **workflow 的语义随 crew 变**:平台运营包里 workflow = 业务场景(起号 / 生产 / 复盘);content-producer 里 workflow = **某一类型视频 / 某一类设计任务怎么做**。制作流程本身高度程式化、跨类型一致,写在包根 `SKILL.md`(阶段链、两闸门、护栏、Stage 12 工具箱);类型差异(叙事套路、素材来源、声画组织方式)才写进 workflow。 +- **原技能整体降级为 tool**:`video-producer` / `collage-broll` / `design-full` 从路由面消失,成为包内工具;wrapper 名与子命令不变(靠 `skill-wrappers.sh` 的 `*/tools/*/` 扫描层暴露),调用方零改动。 +- **`manim-explainer` 删除**:能力已被 `expert-video` 的通用阶段链与 AIGC 动画路径覆盖。 +- **原「Pipeline 机制」废除**:Brief 字段从 `pipeline` 改为 `workflow`;不存在「viral-chaser 报告输入」阶段(追爆拆解是 main 的采样动作,CP 只吃 Brief),Stage 1 由 `intent-router` 承担定档位,`reference-concepts` 降为可选工具(仅直接对接用户模式下用户给了参考拆解报告时用)。 +- **两种工作模式**:A 作为 main 的 subagent(甲方 = main,Brief 已确认,不重开需求讨论);B 直接对接用户(甲方 = 用户,可能不专业,先引导确认 Brief、落实素材位置与存在性,口播文案代拟需用户确认)。两种模式都坚持乙方角色、都自建工作区。 +- **运行期数据只写 CP 自己的 Workspace**:`output_videos//`(视频)与 `design_assets/YYYY-MM-DD-<任务名>/`(设计);不写进 main 的平台目录,也不让 main 代建。 + +--- + --- ## 5. 与现有机制的整合 @@ -476,7 +544,7 @@ crews//skills/expert-/tools/-style-profiler/ ### Phase 4:DNA 工具链 + 多平台扩展(后续) - 每个平台专家包先落地同构 `-style-profiler`:统一 report / build / update 命令、DNA ID 存储和用户输入转译机制。 -- 平台架构完成后,与用户确认该平台独有的 DNA 提取与分析维度;维度可以与微信截然不同、不必对齐 17 维。微信保持 template 开头两项(选题、标题(含封面图))通用;抖音 / 视频号 / 小红书 v1 使用账号级 Brief 语义段。 +- 平台架构完成后,与用户确认该平台独有的 DNA 提取与分析维度;维度可以与微信截然不同、不必对齐 17 维。所有平台保持 template 开头两项(选题、标题(含封面图))通用;抖音 / 视频号 / 小红书 v2 按作品类型分框架,视频类 template 用 Brief 语义段(+ 可选口播文案段),图文类用写作语义段。 - 维度确认后再接入内容生产、改稿等下游 workflow,并配齐 4.7 要求的 6 类 workflow 基线集。 - 逐步沉淀更多 DNA:每个 DNA 持续追加 report,并通过 update 重聚合同步 DNA 文档与 template。 diff --git a/docs/video-capability-replanning-2026-07-25.md b/docs/video-capability-replanning-2026-07-25.md index b8867b47..5b235715 100644 --- a/docs/video-capability-replanning-2026-07-25.md +++ b/docs/video-capability-replanning-2026-07-25.md @@ -1,7 +1,8 @@ # 小贝系统视频能力重新规划 — 调研与开发计划 > 起草日期:2026-07-25(周六) -> 状态:调研期(未进入开发) +> 状态:调研期历史文档(未进入开发),**不作为当前实现依据** +> 2026-09-10 注:本文提到的 `video-producer` / `collage-broll` / `manim-explainer` / `design-full` 四个技能已整合为 `crews/content-producer/skills/expert-video`(含 `video-producer` / `collage-broll` 两个 tool 与 reversal-ad / narration-video / collage-broll 三个 workflow)与 `expert-design`(含 `design-full` tool 与 web-page / app-ui / brand-visual 三个 workflow),`manim-explainer` 已删除;main 与 content-producer 的分工与 Brief 交接契约以 `docs/expert-pack-dna-architecture.md` 4.7 / 4.9 节为准。 > 用途:本文件用于**沉淀调研结果**与**规划出发点**,最终据此生成开发计划。开发计划不在本文件撰写,等调研结束另起一份。 --- diff --git a/scripts/lib/agent-skills.sh b/scripts/lib/agent-skills.sh index 4c56c7a5..23a1cb9e 100644 --- a/scripts/lib/agent-skills.sh +++ b/scripts/lib/agent-skills.sh @@ -376,6 +376,26 @@ for (const b of bins) console.log("+" + b); done < <(find "$ws_scripts_dir" -type f -print0 2>/dev/null) fi + # ── 专家包收纳层:skills//tools//scripts/ ────────── + # 技能整体迁入专家包 tools/ 后(如 expert-design/tools/design-full/scripts/init.sh), + # 脚本路径多一层;不扫这层会让改名后的包内脚本从 ALLOWED_COMMANDS 掉出去。 + local tool_scripts_dir="" + for tool_scripts_dir in "$workspace_dir/skills/$skill"/tools/*/scripts; do + [ -d "$tool_scripts_dir" ] || continue + while IFS= read -r -d '' f; do + local tfname + tfname="$(basename "$f")" + case "$tfname" in + *.py|*.mjs|*.ts|*.js|*.json|*.txt|*.md|*.yaml|*.yml) continue ;; + esac + [ -x "$f" ] || continue + local tool_name + tool_name="$(basename "$(dirname "$tool_scripts_dir")")" + local trelpath="${f#$tool_scripts_dir/}" + printf '+./skills/%s/tools/%s/scripts/%s\n' "$skill" "$tool_name" "$trelpath" + done < <(find "$tool_scripts_dir" -type f -print0 2>/dev/null) + done + # ── 全局 skill(~/.openclaw/skills/,由 apply-addons.sh 同步)── local global_scripts_dir="$openclaw_home/skills/$skill/scripts" if [ -d "$global_scripts_dir" ]; then diff --git a/scripts/lib/crew-workspaces.sh b/scripts/lib/crew-workspaces.sh index 920916a2..4fcc6b47 100755 --- a/scripts/lib/crew-workspaces.sh +++ b/scripts/lib/crew-workspaces.sh @@ -74,6 +74,21 @@ sync_crew_skills() { [ "$synced" -gt 0 ] && echo " ✅ synced $synced crew skill(s) → $(basename "$dest_ws")" + # 清理悬挂软链:技能在仓里改名 / 收纳进专家包后(如 video-producer → expert-video/tools/video-producer), + # dest_skills 下的旧软链会指向不存在的仓路径。只删「软链且目标不存在」的条目, + # 真目录(部署实例自建技能)与有效软链一律不动。 + local pruned=0 + local link="" + for link in "$dest_skills"/*; do + [ -L "$link" ] || continue + if [ ! -e "$link" ]; then + if rm -f "$link" 2>/dev/null; then + pruned=$((pruned + 1)) + fi + fi + done + [ "$pruned" -gt 0 ] && echo " 🧹 pruned $pruned dangling skill symlink(s) in $(basename "$dest_ws")/skills" + # D21 wrapper 暴露:把 dest_ws/skills 下顶层 wrapper 暴露到 ~/.openclaw/bin/。 # 必须扫 dest_ws 而非 src:wrapper 软链目标需指向 workspace 字面路径——子脚本用 # dirname $0/../../.. 推导 workspace 根(ROOT/db/…),若指向 src(仓库模板), @@ -87,6 +102,73 @@ sync_crew_skills() { type expose_skill_wrappers &>/dev/null && expose_skill_wrappers "$dest_skills" } +# 同步仓库声明的 skill 列表文件(BUILTIN_SKILLS / DENIED_SKILLS)到已部署 workspace。 +# $1 src_crew 仓库 crews// +# $2 dest_ws ~/.openclaw/workspace-/ +# $3 project_root 仓库根 +# $4 openclaw_home ~/.openclaw +# 为什么需要:这两个文件驱动 openclaw.json 里该 agent 的 skills allowlist。技能改名或 +# 收纳进专家包后(如 video-producer → expert-video/tools/video-producer),已部署 workspace +# 里的旧文件名会让 allowlist 指向不存在的技能,crew 直接失去这批能力。 +# 安全边界:只在「已部署文件里存在解析不到的陈旧条目」时才重写;重写取 +# 仓库声明 ∪ 已部署文件中仍能解析的条目,实例自装技能不会被抹掉。 +sync_skill_declaration_files() { + local src_crew="$1" + local dest_ws="$2" + local project_root="$3" + local openclaw_home="${4:-$HOME/.openclaw}" + local fname="" + + _skill_name_resolves() { + local name="$1" + [ -d "$dest_ws/skills/$name" ] && return 0 + [ -d "$src_crew/skills/$name" ] && return 0 + [ -d "$project_root/skills/$name" ] && return 0 + [ -d "$openclaw_home/skills/$name" ] && return 0 + [ -d "$project_root/openclaw/skills/$name" ] && return 0 + return 1 + } + + for fname in BUILTIN_SKILLS DENIED_SKILLS; do + local src="$src_crew/$fname" + local dest="$dest_ws/$fname" + [ -f "$src" ] || continue + if [ ! -f "$dest" ]; then + cp "$src" "$dest" 2>/dev/null || true + echo " ✅ $fname installed → $(basename "$dest_ws")" + continue + fi + + local stale=0 + local line="" + while IFS= read -r line || [ -n "$line" ]; do + line="${line#"${line%%[![:space:]]*}"}" # ltrim + line="${line%"${line##*[![:space:]]}"}" # rtrim + [ -n "$line" ] || continue + case "$line" in \#*) continue ;; esac + _skill_name_resolves "$line" || stale=1 + done < "$dest" + + [ "$stale" = "1" ] || continue + + # 重写:仓库声明在前,已部署文件中仍能解析的自定义条目追加在后(去重) + local merged="" + merged="$(cat "$src")" + while IFS= read -r line || [ -n "$line" ]; do + line="${line#"${line%%[![:space:]]*}"}" + line="${line%"${line##*[![:space:]]}"}" + [ -n "$line" ] || continue + case "$line" in \#*) continue ;; esac + _skill_name_resolves "$line" || continue + if ! printf '%s\n' "$merged" | grep -qxF "$line"; then + merged="$merged"$'\n'"$line" + fi + done < "$dest" + printf '%s\n' "$merged" > "$dest" + echo " 🧹 $fname refreshed (stale skill entries) → $(basename "$dest_ws")" + done +} + ensure_soul_crew_type() { local soul_file="$1" local crew_type="$2" diff --git a/scripts/lib/skill-wrappers.sh b/scripts/lib/skill-wrappers.sh index 12d2ff5c..63e6e434 100755 --- a/scripts/lib/skill-wrappers.sh +++ b/scripts/lib/skill-wrappers.sh @@ -63,6 +63,20 @@ expose_skill_wrappers() { exposed=$((exposed + 1)) done + # 清理悬挂 wrapper:技能改名 / 收纳进专家包 tools/ 后,旧 bin 软链会指向不存在的路径。 + # 只删「软链且目标不存在」的条目;真文件与有效软链不动。 + local bin_link="" + local pruned=0 + for bin_link in "$OPENCLAW_BIN_DIR"/*; do + [ -L "$bin_link" ] || continue + if [ ! -e "$bin_link" ]; then + if rm -f "$bin_link" 2>/dev/null; then + pruned=$((pruned + 1)) + fi + fi + done + [ "$pruned" -gt 0 ] && echo " 🧹 pruned $pruned dangling wrapper symlink(s) in ~/.openclaw/bin" + [ "$exposed" -gt 0 ] && echo " ✅ exposed $exposed wrapper(s) → ~/.openclaw/bin (from $(basename "$skills_root"))" } diff --git a/scripts/setup-crew.sh b/scripts/setup-crew.sh index 5c270ea8..3add90fa 100755 --- a/scripts/setup-crew.sh +++ b/scripts/setup-crew.sh @@ -283,6 +283,9 @@ for agent_dir in "$CREWS_DIR"/*/; do # §2.3:已部署 workspace 仍同步 crew 专属 skill(覆盖),但不碰 AGENTS.md/TOOLS.md/Memory # 及部署实例自定义 skill(sync_crew_skills 只覆盖仓库里同名的 skill) sync_crew_skills "$agent_dir" "$dest" + # skill 声明文件(BUILTIN_SKILLS / DENIED_SKILLS)跟仓库走:陈旧条目会让 openclaw.json + # 的 skills allowlist 指向已改名 / 已收纳的技能,crew 直接失去这批能力。 + sync_skill_declaration_files "$agent_dir" "$dest" "$PROJECT_ROOT" "$OPENCLAW_HOME" # 仅做幂等注入(有标记则跳过,不覆盖用户编辑的内容) inject_file_edit_guide "$dest/TOOLS.md" inject_exec_guide "$dest/TOOLS.md" "$dest" From d5e112c4d25cfe078eef4377fc1b5e53e8465f8e Mon Sep 17 00:00:00 2001 From: codes-factory-of-bg Date: Thu, 10 Sep 2026 07:59:56 +0800 Subject: [PATCH 09/34] chore(deps): bump openclaw-weixin 2.4.6 -> 2.4.8, wecom-openclaw-cli 1.1.0 -> 1.1.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit openclaw 基座保持 2026.7.1-2 不变,只升第三方插件 pin。 - openclaw-weixin 2.4.8:上游唯一实质改动是 createTypingCallbacks 的 import 从 openclaw/plugin-sdk/channel-runtime(8.1 已删)改到 channel-message。 已对 7.1-2 宿主实测兼容:11 个 plugin-sdk 子路径在 exports 中均存在, 12 个 value import 逐个对已 build 的 dist/ 做运行时校验全部命中 (含 channel-message::createTypingCallbacks),6 个 type-only import 在 7.1-2 的 .d.ts 中也存在。integrity 用本地 npm pack tarball 的 sha512 与 registry dist.integrity 对账一致。 - wecom-openclaw-cli 1.1.1:plugins install 支持透传 --force(7.1-2 已有该 flag)、多源 failover 不再删插件目录、channels.wecom 配置备份/描述更宽容。 - openclaw-weixin-cli 2.1.4 不变(仍为 npm latest,integrity 已复核)。 - 同步三处兜底版本 2.4.6 -> 2.4.8:scripts/install.sh、 scripts/install-atomgit.sh、docker/docker-bootstrap.sh。 未触碰本机部署实例(~/.openclaw 只读)。 --- CHANGELOG.md | 15 +++++++++++++++ docker/docker-bootstrap.sh | 2 +- openclaw-weixin.version.json | 8 ++++---- scripts/install-atomgit.sh | 2 +- scripts/install.sh | 2 +- 5 files changed, 22 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 663666d4..5a9f4aba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,18 @@ +# v5.7.1 (2026-09-10) + +### 第三方插件 pin 升级(openclaw-weixin 2.4.8 / wecom-openclaw-cli 1.1.1) + +> openclaw 基座保持 `2026.7.1-2`(`0790d9f593`)不变,8.x/9.x 迁移另案评估。本轮只动第三方插件 pin,**不触碰本机部署实例**。 + +- **`@tencent-weixin/openclaw-weixin` 2.4.6 → 2.4.8**:上游 2.4.7/2.4.8 的唯一实质改动是一行 import——`createTypingCallbacks` 从已被 OpenClaw 2026.8.1 删除的兼容子路径 `openclaw/plugin-sdk/channel-runtime` 改到 `openclaw/plugin-sdk/channel-message`(上游声明新路径仍兼容最低宿主 2026.5.12)。其余为版本号与 CHANGELOG。 + - **对 7.1-2 宿主已实测兼容**(不是照抄上游声明):2.4.8 用到的 11 个 `openclaw/plugin-sdk/*` 子路径在 7.1-2 的 `package.json` exports 里全部存在;18 个具名导入逐个对 7.1-2 已 build 的 `dist/` 做运行时校验,12 个 value import 全部命中(含关键的 `channel-message :: createTypingCallbacks`),6 个 type-only import 在 7.1-2 的 `.d.ts` 中也都在。 + - pin 的 `integrity` 用本地 `npm pack` 下来的 tarball 算 sha512 与 npm `dist.integrity` 对账一致(`update.sh` / `install-wecom-channel.sh` 都是按 tarball sha512 校验)。 +- **`@wecom/wecom-openclaw-cli` 1.1.0 → 1.1.1**:安装流程更稳健——`openclaw plugins install` 支持透传 `--force`(7.1-2 的 `plugins-cli` 已有该 flag:Overwrite an existing installed plugin)、多 npm 源 failover 时不再顺手删插件目录、`channels.wecom` 配置备份与描述对缺失 `botId` 更宽容、移除废弃的 `getNpmPluginPath` / `hasValidChannelConfig`。 +- **`@tencent-weixin/openclaw-weixin-cli` 2.1.4 不变**:仍是 npm latest,integrity 已复核与 registry 一致。 +- 同步把 `scripts/install.sh` / `scripts/install-atomgit.sh` / `docker/docker-bootstrap.sh` 中「pin 文件缺失时」的兜底版本 `2.4.6 → 2.4.8`(三处,避免兜底路径装到旧版)。 + +--- + # v5.7.0 (2026-08-31) ### 专家包(Expert Pack)架构 diff --git a/docker/docker-bootstrap.sh b/docker/docker-bootstrap.sh index f8d39fd0..0022f655 100755 --- a/docker/docker-bootstrap.sh +++ b/docker/docker-bootstrap.sh @@ -108,7 +108,7 @@ install_weixin_plugin() { ver=$(python3 -c "import json;print(json.load(open('$pin_file'))['openclaw-weixin']['version'])" 2>/dev/null || true) fi pkg="${pkg:-@tencent-weixin/openclaw-weixin}" - ver="${ver:-2.4.6}" + ver="${ver:-2.4.8}" # 幂等检查 if (cd "$PROJECT_ROOT/openclaw" && pnpm openclaw plugins list 2>/dev/null | grep -q "openclaw-weixin"); then log "openclaw-weixin plugin already installed" diff --git a/openclaw-weixin.version.json b/openclaw-weixin.version.json index 34302061..6cc6e33c 100644 --- a/openclaw-weixin.version.json +++ b/openclaw-weixin.version.json @@ -1,8 +1,8 @@ { "openclaw-weixin": { "package": "@tencent-weixin/openclaw-weixin", - "version": "2.4.6", - "integrity": "sha512-qw9k3PLTiMWGNjjsknHgcTManH1w4j+Ji1ArWIaYLKCq3aFRsVwcqnPi127bvOoVMJGW4dbyJ8NECEMgoO+iRw==" + "version": "2.4.8", + "integrity": "sha512-hhO9prUQwzfSpIL6XGWazRsxNs89K+Mis3iQW6a8eum4AIDxOUiTFMg4nlNVD/au0SjAx5CcJjo3KYWZqGpgQA==" }, "openclaw-weixin-cli": { "package": "@tencent-weixin/openclaw-weixin-cli", @@ -11,7 +11,7 @@ }, "wecom-openclaw-cli": { "package": "@wecom/wecom-openclaw-cli", - "version": "1.1.0", - "integrity": "sha512-qnD0JXaHtgSsKh0REm5r17Kv/PyqZY+uavBxfLdqeoptfZS08aftJXUtIiFbuASdxGKpEjDxto2fiwf/3yAgew==" + "version": "1.1.1", + "integrity": "sha512-b+TT7KodLzu9+DA6CFkpHsj9xg1dZktagbigF3BEI5hrNai+C+djyjS/rRPvy0Ix+AGg9EepQ9kfMFs+Hov3og==" } } diff --git a/scripts/install-atomgit.sh b/scripts/install-atomgit.sh index b519508f..8be9fdc4 100755 --- a/scripts/install-atomgit.sh +++ b/scripts/install-atomgit.sh @@ -772,7 +772,7 @@ install_weixin_plugin() { ver=$(python3 -c "import json;print(json.load(open('$pin_file'))['openclaw-weixin']['version'])" 2>/dev/null || true) fi pkg="${pkg:-@tencent-weixin/openclaw-weixin}" - ver="${ver:-2.4.6}" + ver="${ver:-2.4.8}" # 幂等检查:plugins list 已含则跳过 if "$claw_cmd" plugins list 2>/dev/null | grep -q "openclaw-weixin"; then ui_success "openclaw-weixin plugin already installed" diff --git a/scripts/install.sh b/scripts/install.sh index ab03dcd3..f5f8c44e 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -1084,7 +1084,7 @@ install_weixin_plugin() { ver=$(python3 -c "import json;print(json.load(open('$pin_file'))['openclaw-weixin']['version'])" 2>/dev/null || true) fi pkg="${pkg:-@tencent-weixin/openclaw-weixin}" - ver="${ver:-2.4.6}" + ver="${ver:-2.4.8}" # 幂等检查:plugins list 已含则跳过 if "$claw_cmd" plugins list 2>/dev/null | grep -q "openclaw-weixin"; then ui_success "openclaw-weixin plugin already installed" From 8d666180b25310a9179d9f8bd30155107c3a0585 Mon Sep 17 00:00:00 2001 From: bigbrother666sh Date: Thu, 10 Sep 2026 22:13:34 +0800 Subject: [PATCH 10/34] adjust: dna v2 --- crews/content-producer/AGENTS.md | 2 +- crews/content-producer/IDENTITY.md | 2 +- crews/main/AGENTS.md | 15 +++------ crews/main/skills/expert-douyin/SKILL.md | 8 ++--- .../workflows/account-benchmark.md | 2 +- .../expert-douyin/workflows/account-setup.md | 4 +-- .../workflows/content-production.md | 33 ++++++------------- .../skills/expert-douyin/workflows/editing.md | 2 +- .../expert-douyin/workflows/style-dna.md | 8 ++--- crews/main/skills/expert-wx-channel/SKILL.md | 10 +++--- .../workflows/content-production.md | 22 +++---------- .../expert-wx-channel/workflows/style-dna.md | 2 +- crews/main/skills/expert-xhs/SKILL.md | 6 ++-- .../references/note-dna-framework.md | 2 +- .../workflows/content-production.md | 5 ++- .../skills/expert-xhs/workflows/style-dna.md | 2 +- crews/sales-cs/AGENTS.md | 4 +-- 17 files changed, 47 insertions(+), 82 deletions(-) diff --git a/crews/content-producer/AGENTS.md b/crews/content-producer/AGENTS.md index cf7dcef0..28938e3c 100644 --- a/crews/content-producer/AGENTS.md +++ b/crews/content-producer/AGENTS.md @@ -29,7 +29,7 @@ ## 交接契约(硬边界) -- **甲方给我**:Brief(绝对路径)+ 已有素材(绝对路径 + 来源授权)+ 口播文案 / 口播录音(如有)。Brief 里不含甲方的 DNA 等内部资产,我也不去翻甲方工作区找规则。 +- **甲方给我**:Brief(绝对路径)+ 已有素材(绝对路径 + 来源授权)+ 口播文案 / 口播录音(如有)。 - **我给甲方**:成片 + 封面 + 交付说明(`final-deliver.md`),回报三者的**绝对路径**。 - **甲方不替我建工作区,我也不替甲方建**:同一个活儿各自建各自的工作区。双方都是 T3 权限,需要时可直接读对方工作区取文件。 - **甲乙方关系不破**:需求方向、品牌事实、卖点承诺、发布文案归甲方;工作区、制作方案、分镜、素材实现、渲染参数归我。缺信息就问甲方,不自行脑补,也不反过来指挥甲方。 diff --git a/crews/content-producer/IDENTITY.md b/crews/content-producer/IDENTITY.md index 4e8f5c4d..70f615d7 100644 --- a/crews/content-producer/IDENTITY.md +++ b/crews/content-producer/IDENTITY.md @@ -13,5 +13,5 @@ content-producer(内容制作者) 🎬 ## Role -专业内容制作者,乙方。两个专家包:`expert-video`(视频制作)+ `expert-design`(平面设计)。 +专业内容制作者,乙方定位。 既接受 main agent 派单,也接受用户直接对话。 diff --git a/crews/main/AGENTS.md b/crews/main/AGENTS.md index ca4e4b08..33c28286 100644 --- a/crews/main/AGENTS.md +++ b/crews/main/AGENTS.md @@ -77,17 +77,10 @@ index.md 格式为: ### content-producer(对内 crew) -- 用途:专业内容制作者(视频/视觉),它既可以被你spawn为subagent支持你的工作,也可以直接受命于用户。它有两个专家包:`expert-video`(视频制作)与 `expert-design`(平面设计)。 -- **甲乙方分工**:你(甲方)负责选题策划、按 DNA 出 `brief.md`、拟定标题/短标题/简介、准备素材(简单预处理、`ui-demo` 录屏、从 `campaign_assets/` 挑选)并把**绝对路径**写进 Brief、口播类的口播文案(真人口播时向用户取录音文件)、监督推动 CP 进度、成片后的发布与运营;CP(乙方)只按 Brief 制作成片与封面。 -- **交接物**:你给「Brief + 已有素材绝对路径 + 口播文案/录音(如有)」,CP 回「成片 + 封面 + 交付说明」的绝对路径,你取回作品目录后再发布。 -- **Brief 不写 DNA**:CP 查不到你的 DNA,Brief 里不出现 dna-id、DNA 文档路径或 DNA 规则原文;DNA 结论由你消化成 Brief 的具体要求(选题、创意、形态与制作指向、规格、封面主文案)。 -- **不替 CP 建工作区**:CP 在自己的 workspace 下自建工作区;双方都是 T3,可互访对方工作区取文件。 -- Brief 的 `workflow` 字段写 CP 支持的视频类型(`reversal-ad` 影视解说+反转植入 / `narration-video` 口播类 / `collage-broll` 纸拼贴),形态未确定就省略,由 CP 自选。 -- 启用流程: - 1. **先判断** `openclaw.json` 的 `channels` 段是否已配置飞书 channel 或企业微信 channel。 - 2. **若都没有** → 提醒用户:content-producer 是对内 crew,需绑定一个独立工作 channel(飞书或企业微信二选一)才能接收任务派发;等用户确认选哪个。 - 3. 用户确认后 → spawn IT engineer → 跑 `work-channel-binding` 配 channel + 把 `workspace-content-producer/openclaw_sample.json` 并入 `openclaw.json`(加入 `agents.list` + 绑该工作 channel)。 -- 若已有飞书或企业微信 channel → 跳过提醒,直接 spawn IT engineer 合入 openclaw_sample.json。 +- 用途:专业内容制作者(视频/视觉),它既可以被你spawn为subagent支持你的工作,也可以直接受命于用户(需要先启用并给它配置独立的工作channel)。 +它有两个专家包:`expert-video`(视频制作)与 `expert-design`(平面设计)。涉及到视频全案制作或者设计全案制作时应该将任务委托给它,这种情况下你们的分工约定如下: +>1. **甲乙方分工**:你(甲方)负责选题策划、按 DNA 出 `brief.md`、拟定标题/短标题/简介、准备素材(简单预处理、`ui-demo` 录屏、从 `campaign_assets/` 挑选)并把**绝对路径**写进 Brief、口播类的口播文案(真人口播时指导用户按口播稿录音并向用户取录音文件)、监督推动 CP 进度、成片后的发布与运营;CP(乙方)只按 Brief 制作成片与封面。 +>2. **交接物**:你给「Brief + 已有素材绝对路径 + 口播文案/录音(如有)」,CP 回「成片 + 封面 + 交付说明」的绝对路径,你取回作品目录后再发布。 ### 通用约束 diff --git a/crews/main/skills/expert-douyin/SKILL.md b/crews/main/skills/expert-douyin/SKILL.md index f14e33fd..95681d3f 100644 --- a/crews/main/skills/expert-douyin/SKILL.md +++ b/crews/main/skills/expert-douyin/SKILL.md @@ -15,7 +15,7 @@ metadata: | 场景 | Workflow | 什么时候触发 | |------|----------|-------------| | 内容 DNA 管理 | Style DNA | 建 / 更新内容 DNA(样本、偏好、局部借鉴、对标融合):先判作品类型,再决定样本落到哪个 DNA | -| 内容生产 | Content Production | 做一条 / 做几条抖音内容;main 直接做已有素材轻加工,视频全案只产出并委托 Brief | +| 内容生产 | Content Production | 做一条 / 做几条抖音内容;main 直接做已有素材轻加工,视频全案只产出Brief并委托content-producer | | 起号与定位 | Account Setup | 新号起号、定位梳理、内容支柱搭建、老号接手与诊断 | | 账号对标 | Account Benchmark | 对标账号 / 对标视频分析,并与默认或指定 DNA 逐项比较 | | 改片与调整 | Editing | 改文案、重剪、换封面、调结构、换风格 | @@ -43,15 +43,15 @@ metadata: 跨领域通用技能:`viral-chaser`(抖音 / B站 / 小红书视频下载拆解,DNA 采样与仿写参考的取数主力)、`smart-search`(跨平台搜索,选题调研优先走社交平台,不用通用搜索引擎)、`content-calibrator`(DNA 表现评估)、`published-track`(发布记录与指标库)、`login-manager`(抖音登录态维护)。 -制作链相关技能(边界见 Content Production Workflow):`video-edit`(素材加工拼接)、`talking-head-cut`(口播轻剪辑)、`ui-demo`(产品操作录屏)、`video-review`(成片质检闸门)、`siliconflow-img-gen`(封面图)、`pexels-footage` / `pixabay-footage`(免版权素材)。 +素材加工相关技能:`video-edit`(素材加工拼接)、`talking-head-cut`(口播轻剪辑)、`ui-demo`(产品操作录屏)、`video-review`(成片质检闸门)、`siliconflow-img-gen`(封面图)、`pexels-footage` / `pixabay-footage`(免版权素材)。 -**分工硬边界**:main 负责选题策划、按 DNA 出 **Brief**、拟定标题与简介、准备素材(用户素材预处理 / `ui-demo` 录屏 / 从 `campaign_assets/` 挑选,绝对路径写进 Brief)、监督推动 CP、成片后的发布与运营,也直接做图文内容与已有素材轻加工;视频全案的成片制作委托 `content-producer`。口播类视频的口播文案由 main 按 `narration-script` 子模块写好并随 Brief 交付(真人口播时向用户取得录音文件),CP 不重写策略文案。Brief 指定 `workflow` 时 CP 必须采用,未指定时 CP 按通用阶段链自由发挥。Brief **不含 DNA 信息**,main 也不替 CP 建工作区(双方 T3 权限可互访取文件)。 +**视频全案分工硬边界**:main 负责选题策划、按 DNA 出 **Brief**、拟定标题与简介、准备素材(用户素材预处理 / `ui-demo` 录屏 / 从 `campaign_assets/` 挑选,绝对路径写进 Brief)、监督推动 CP、成片后的发布与运营,也直接做图文内容与已有素材轻加工;视频全案的成片制作委托 `content-producer`。口播类视频的口播文案由 main 按 `narration-script` 子模块写好并随 Brief 交付(真人口播时,指导用户录音并取得录音文件),CP 不重写策略文案。Brief 指定 `workflow` 时 CP 必须采用,未指定时 CP 按通用阶段链自由发挥。Brief **不含 DNA 信息**,main 也不替 CP 建工作区(双方 T3 权限可互访取文件)。 ## 风格与 DNA DNA 存储目录是 `douyin/dna/`。未指定 DNA 时默认使用并更新 `dna-0`(视频);图文另建 dna-id(如 `dna-0-note`)。生产前同时读取 DNA 文档与 DNA template;对标分析先建立独立对标 DNA,不默认写入 `dna-0`。 -DNA 是**从一批作品样本提取并聚合出的内容生产规则集**(不存在「平台级 / 账号级 DNA」):视频 8 维——选题与观看理由、标题与封面、内容创意、视频内容形态与制作指向、制作规格与视听倾向,加可选的口播文案子模块与账号运营子模块(简介写法、内容形式比例、发布习惯);图文 8 维——选题、标题与封面图组、内容创意、正文表达与语气、图组视觉、互动引导与转化,加账号运营子模块。它指导 main agent 出图文内容或视频 Brief(+ 口播文案),不规定创作细节与成片制作。维度框架 v2 位于 `douyin-style-profiler` 的 `references/video-dna-framework.md` 与 `references/note-dna-framework.md`。 +DNA 是**从一批作品样本提取并聚合出的内容生产规则集**:视频 8 维——选题与观看理由、标题与封面、内容创意、视频内容形态与制作指向、制作规格与视听倾向,加可选的口播文案子模块与账号运营子模块(简介写法、内容形式比例、发布习惯);图文 8 维——选题、标题与封面图组、内容创意、正文表达与语气、图组视觉、互动引导与转化,加账号运营子模块。它指导 main agent 出图文内容或视频 Brief(+ 口播文案),不规定创作细节与成片制作。维度框架 v2 位于 `douyin-style-profiler` 的 `references/video-dna-framework.md` 与 `references/note-dna-framework.md`。 ## 数据与记录 diff --git a/crews/main/skills/expert-douyin/workflows/account-benchmark.md b/crews/main/skills/expert-douyin/workflows/account-benchmark.md index b376ce0f..fc3d5815 100644 --- a/crews/main/skills/expert-douyin/workflows/account-benchmark.md +++ b/crews/main/skills/expert-douyin/workflows/account-benchmark.md @@ -37,7 +37,7 @@ 选择建议: -1. 优先选择点赞、评论、分享信号强的视频;播放量只作参考之一。优先低粉高播账号的作品(内容形式更可学习)。 +1. 优先选择点赞、评论、分享信号强的视频;播放量只作参考之一。优先选择粉丝量少但互动(赞、评)高的账号的作品(内容形式更可学习)。 2. 抖音没有账号作品列表抓取工具;请用户提供对标账号的代表性视频链接,或由 `smart-search` 辅助发现候选账号后请用户确认。 3. 对标账号至少批量收集 10 条代表性作品(从账号发布列表提取,视频与图文分开建 DNA);可获取作品不足 10 条时,提供全部并说明数量限制。 4. 单篇作品可以形成单篇观察,但不得当成稳定结论;多个样本才分析覆盖率和共性。 diff --git a/crews/main/skills/expert-douyin/workflows/account-setup.md b/crews/main/skills/expert-douyin/workflows/account-setup.md index e8ba4d55..532fbbef 100644 --- a/crews/main/skills/expert-douyin/workflows/account-setup.md +++ b/crews/main/skills/expert-douyin/workflows/account-setup.md @@ -2,7 +2,7 @@ 新号起号、账号定位梳理、内容支柱搭建、默认 `dna-0` 初始化、老号接手与诊断走这个 Workflow。独立账号对标走 `account-benchmark.md`。 -起号拆成六件事:定位清楚、观看理由成立、标签稳定、内容有用、互动真实、复盘持续。方法库里的技巧和案例都是启发,不要把具体数字当成保证;平台功能入口、算法权重、处罚规则按待确认信息处理。产出要能直接执行:表格、清单、脚本简报、30 天节奏或复盘动作,少写空泛建议,多给"下一条视频该怎么做"。 +起号拆成六件事:定位清楚、观看理由成立、标签稳定、内容有用、互动真实、复盘持续;平台功能入口、算法权重、处罚规则按待确认信息处理。产出要能直接执行:表格、清单、脚本简报、选题池或复盘动作,少写空泛建议,多给"下一条视频该怎么做"。 ## 入口判断 @@ -59,7 +59,7 @@ 2. **观看理由**:陌生人为什么要看完这条而不是划走;看完能记住账号的哪个身份、冲突、热情或承诺。每条视频都要能回答这两个问题,回答不了的选题不做。 3. **内容支柱**:3-5 个,分别承接搜索、信任、故事、证明、转化或留存。 4. **关键词地图**(标签反推 + 搜索预埋): - - 选 5-10 个对标账号,优先低粉高播、近期更新、评论真实、内容形式可学习的账号;请用户提供账号名或视频链接,Agent 用 `viral-chaser` 逐个拆解,并用 `douyin-comments` 直接抓取对标视频评论(不依赖用户提供截图),记录主标签、细分人群、核心场景、常见痛点、标题高频词、评论高频问题。 + - 选 5-10 个对标账号,优先低粉高互动、近期更新、评论真实、内容形式可学习的账号;请用户提供账号名或视频链接,Agent 用 `viral-chaser` 逐个拆解,并用 `douyin-comments` 直接抓取对标视频评论(不依赖用户提供截图),记录主标签、细分人群、核心场景、常见痛点、标题高频词、评论高频问题。 - 汇总成 `1 个主标签 + 2-4 个场景词 + 3-5 个人群痛点词`,并按关键词层级展开:核心词(赛道本体)、长尾痛点词(用户真实搜索句)、场景词(时间/人群/预算/身份)、转化词(清单/步骤/避坑/对比/测评/教程)。 - 这些词写进昵称、简介、置顶视频、前 10 条标题、口播首句、合集名称和结尾关注理由。 5. **主页文案**:昵称、简介、置顶视频规划。 diff --git a/crews/main/skills/expert-douyin/workflows/content-production.md b/crews/main/skills/expert-douyin/workflows/content-production.md index b4c76333..339aaac3 100644 --- a/crews/main/skills/expert-douyin/workflows/content-production.md +++ b/crews/main/skills/expert-douyin/workflows/content-production.md @@ -2,7 +2,7 @@ 从选题到发布的完整内容生产。用户说"帮我做条抖音视频""发个抖音""这条照着做一条""这个选题我们也做一条"走这个。 -**分工硬边界**:main agent 负责选题策划、按 DNA 出 **Brief**、拟定标题与简介、准备素材(用户素材预处理 / `ui-demo` 录屏 / 从 `campaign_assets/` 挑选,绝对路径写进 Brief)、监督并推动作为 subagent 的 `content-producer`、成片后的发布与运营;也直接做图文内容与**已有视频素材的简单加工**(`video-edit` / `talking-head-cut`)。视频全案的成片制作一律委托 `content-producer`。本 workflow 的价值在于:用 DNA 锁定选题、包装、内容创意、视频形态与制作规格,编排输入分支与确认节点,衔接制作并把关发布记录。 +**视频作品分工硬边界**:main agent 负责选题策划、按 DNA 出 **Brief**、拟定标题与简介、准备素材(用户素材预处理 / `ui-demo` 录屏 / 从 `campaign_assets/` 挑选,绝对路径写进 Brief)、监督并推动作为 subagent 的 `content-producer`、成片后的发布与运营;也直接做图文内容与**已有视频素材的简单加工**(`video-edit` / `talking-head-cut`)。视频全案的成片制作一律委托 `content-producer`。本 workflow 的价值在于:用 DNA 锁定选题、包装、内容创意、视频形态与制作规格,编排输入分支与确认节点,衔接制作并把关发布记录。 ## Step 0 - 入口判断 @@ -21,7 +21,7 @@ | --- | --- | --- | | 素材组装 / 轻剪辑 | 用户手里有可用素材 | main 直接做:`video-edit` / `talking-head-cut` / `ui-demo` | | 从零制作 | 没有素材,需要出脚本、拍摄/生成画面 | main 出制作简报,委托 `content-producer` | -| 脚本制作 | 用户已有脚本 | 脚本交 `content-producer` 制作 | +| 脚本制作 | 用户已有脚本 | 根据dna对脚本做必要修改,提交用户确认后,脚本交 `content-producer` 制作 | ### 3. 抖音链接的意图判断 @@ -54,7 +54,10 @@ douyin/dna/{dna-id}/{dna-id}.dna.md douyin/dna/{dna-id}/{dna-id}.template.md ``` -DNA template 是 main agent 的生产输入模板:**视频 DNA 的 template = Brief 正文模板 +(可选)口播文案模板**,覆盖选题与观看理由、标题与封面、内容创意、视频形态与制作指向、制作规格、口播文案;**图文 DNA 的 template = 图文写作模板**。它不规定创作细节:逐句台词、镜头表、转场或编码参数归 Content Producer。 +DNA template 是 main agent 的生产输入模板: + +- **视频 DNA 的 template = Brief 正文模板 +(可选)口播文案模板**,覆盖选题与观看理由、标题与封面、内容创意、视频形态与制作指向、制作规格、口播文案(如果是口播类视频创作),它不规定创作细节:逐句台词、镜头表、转场或编码参数,这些归 Content Producer决定; +- **图文 DNA 的 template = 图文写作模板**。 ### 2. 读取业务知识 @@ -174,7 +177,7 @@ DNA 约束的是选题与观看理由、标题与封面写法、内容创意原 只处理已有素材的简单加工,不升格为全案制作: -1. 按 DNA 的核心传达、内容形式与互动目标整理剪辑顺序清单;若口播文案子模块已启用且需要新口播,由 main 先写口播稿。 +1. 按 DNA 的核心传达、内容形式与互动目标整理剪辑顺序清单;若创作口播类视频,由 main 先写口播稿。 2. 口播类素材去口气词、剪高光 -> `talking-head-cut`。 3. 抽段拼接、加旁白 / BGM、烧字幕、编号合成 -> `video-edit`。 4. 素材缺口经 AIGC 片段(`aigc-video-gen`)或免费素材库(`pexels-footage` / `pixabay-footage`)补充,补充素材在清单中标注来源。 @@ -211,33 +214,17 @@ Brief 硬性规则: - **素材给绝对路径**:main 负责素材准备(用户素材预处理、`ui-demo` 录屏、从 `campaign_assets/` 挑选),把绝对路径写进 Brief。 - **甲乙方关系**:需求方向、品牌事实、发布文案归 main;制作方案、分镜、渲染参数归 CP。 -2. 口播类视频:按 DNA 的 `narration-script` 子模块写口播终稿 `douyin/outputs//voiceover.md`,Brief 里给绝对路径;真人口播时先向用户取得录音文件。 +2. 口播类视频:按 DNA 的 `narration-script` 子模块写口播终稿 `douyin/outputs//voiceover.md`,Brief 里给绝对路径;真人口播时指导用户按口播稿录音,完成后向用户取得录音文件。 3. 参考模式下,把选题与创意结论写进 Brief 的「内容创意」段即可;`viral-chaser` 拆解报告是 main 的采样材料,**不作为 Brief 附件交给 CP**。 4. spawn `content-producer` 委托制作:只交 Brief + 素材绝对路径 + 口播文案 / 录音;不指定 CP 的工作区与制作方案。 5. Brief 变更时更新版本并推送变更要点;已开工中间产物按新版取舍,弃用部分记入交付说明。 6. CP 交付后,按其回报的绝对路径把成片与封面取回 `douyin/outputs//`(`video.mp4` / `cover.jpg`),并把交付说明要点记入作品目录。 -## Step 6 - 成片自检 - -成品必须过 `video-review` 闸门(ffprobe 校验 + 黑帧扫描 + 音频电平 + 时长/分辨率一致性)。 - -- verdict = pass -> 继续。 -- verdict = warn -> 向用户说明警告项,由用户决定是否修正。 -- verdict = fail -> 回到 Step 5 修正后重跑,不带病交付。 - -## Step 7 - 封面 - -1. 读取 DNA template 的标题与封面段,只取风格边界,不搬逐镜制作细节。 -2. 结合最终标题、目标观众和本条核心收益,确定本次封面的主体、色彩、文字视觉和负向约束。 -3. 优先从成片中选帧作为封面(与内容一致);需要更强视觉冲击时用 `siliconflow-img-gen` 生成。 -4. 用户已提供封面时直接使用;用户提供素材不足时作为生成参考。 -5. 封面图发用户确认,确认后保存为 `douyin/outputs//cover.jpg`。 - ## 【确认】成片与封面 第三个必停节点。成片与封面确认后才可发布。用户有意见按意见修改,直至确认。 -## Step 8 - 发布 +## Step 6 - 发布 发布走 `douyin-publish`(工具说明见包内 `douyin-publish` 文档): @@ -256,7 +243,7 @@ douyin-publish run --video douyin/outputs//<成片文件> --title " - 同一时间只能有一个 `douyin-publish` 发布任务在跑(浏览器 session 竞态),多平台分发时抖音这条必须串行。 - 限频:单抖音号每 24h ≤ 5 条;触发风控立即降级,30 分钟内不重试。 -## Step 9 - 记录 +## Step 7 - 记录 发布成功(拿到视频链接)后入库: diff --git a/crews/main/skills/expert-douyin/workflows/editing.md b/crews/main/skills/expert-douyin/workflows/editing.md index 73cc0ba1..312f1395 100644 --- a/crews/main/skills/expert-douyin/workflows/editing.md +++ b/crews/main/skills/expert-douyin/workflows/editing.md @@ -54,7 +54,7 @@ ## 改后发布 -用户要求改完后发布 / 重新发布时,走 `content-production.md` 的发布与记录流程(Step 8-9): +用户要求改完后发布 / 重新发布时,走 `content-production.md` 的发布与记录流程(Step 6-7): 1. **发布**:`douyin-publish open-page` + 登录态判定后 `run`;标题与文案用改后版本。 2. **记录**:`published-track record` 重新入库为新记录(新链接、新发布日期),`dna-meta.json` 沿用该视频目录的 DNA 绑定。 diff --git a/crews/main/skills/expert-douyin/workflows/style-dna.md b/crews/main/skills/expert-douyin/workflows/style-dna.md index 1a814a9a..ccca1c66 100644 --- a/crews/main/skills/expert-douyin/workflows/style-dna.md +++ b/crews/main/skills/expert-douyin/workflows/style-dna.md @@ -4,7 +4,7 @@ ## 边界 -- DNA 是从一批作品样本中提取、聚合出的内容生产规则集,不存在「平台级 DNA」或「账号级 DNA」的说法;样本可以来自多个账号,也可以来自用户指定的一个账号的发布列表批量提取。 +- DNA 是从一批作品样本中提取、聚合出的内容生产规则集,样本可以来自多个账号,也可以来自用户指定的一个账号的发布列表批量提取。 - 账号初始化与默认 `dna-0` 建立走 `account-setup.md`;对标样本先走 `account-benchmark.md`。 - DNA 如何用于内容生产走 `content-production.md`;改片走 `editing.md`;数据复盘走 `review.md`。 - DNA 指导 main agent 出图文内容、视频 Brief 与(口播类的)口播文案;全片制作委托 `content-producer`。 @@ -120,9 +120,9 @@ Agent 必须读取全部 report,按权重/focus 聚合: - 高频共性、高权重偏好、局部借鉴、孤例、例外分开写。 - 标注样本覆盖度;单篇或少量样本不得称为稳定结论。 - 视频形态必须聚合成明确的**制作指向**(Content Producer `expert-video` 的某个 workflow,或 main 的素材加工技能),供 Brief 的 `workflow` 字段直接引用。 -- 高数据内容要回读创意、形态与包装,不能只归因播放量 / 阅读量。 +- 高数据内容要回读创意、形态与包装,不能只归因。 - 为每个维度写聚合结论、报告依据和可执行规则。 -- 确保 DNA 文档能推导 template;账号运营子模块的结论只留在 DNA 文档。 +- 确保 DNA 文档能推导 template;但账号运营子模块的结论只留在 DNA 文档。 ## 更新已有 DNA @@ -156,7 +156,7 @@ douyin-style-profiler update \ - **图文内容**:读取图文 DNA 文档与 template,main agent 直接生产。 - **视频全案**:读取视频 DNA 文档与 template,main agent 产出 **Brief**(+ 口播类的口播文案)。Brief 写明选题与观看理由、标题与简介、内容创意、`workflow`(视频形态的制作指向)、制作规格(横竖屏 / 时长带 / 画面风格 / 配音音色)、素材清单与授权(绝对路径)、验收标准、闸门批准人。 - **Brief 不含 DNA 信息**:Content Producer 看不到 main 的 DNA,只按 Brief 制作;也不要把 DNA 文档路径写进 Brief。 -- **口播类视频**:口播文案子模块启用时,口播终稿由 main agent 写好并随 Brief 交付;真人口播时由 main agent 向用户取得录音文件。CP 不重写策略文案。 +- **口播类视频**:口播文案子模块启用时,口播终稿由 main agent 写好并随 Brief 交付;真人口播时由 main agent 指导用户录音并向用户取得录音文件。CP 不重写策略文案。 - **工作区**:main 不替 CP 建工作区,也不指定项目目录;CP 自建工作区,双方 T3 权限可互访取文件。 ## 对标接口 diff --git a/crews/main/skills/expert-wx-channel/SKILL.md b/crews/main/skills/expert-wx-channel/SKILL.md index db4f4842..73992e35 100644 --- a/crews/main/skills/expert-wx-channel/SKILL.md +++ b/crews/main/skills/expert-wx-channel/SKILL.md @@ -15,7 +15,7 @@ metadata: | 场景 | Workflow | 什么时候触发 | |------|----------|-------------| | 内容 DNA 管理 | Style DNA | 建 / 更新内容 DNA(样本、偏好、局部借鉴、对标融合):先判作品类型,再决定样本落到哪个 DNA | -| 内容生产 | Content Production | 做一条 / 做几条视频号内容;main 直接做已有素材轻加工,视频全案只产出并委托 Brief | +| 内容生产 | Content Production | 做一条 / 做几条视频号内容;main 直接做已有素材轻加工,视频全案只产出Brief并委托content-producer | | 起号与定位 | Account Setup | 新号起号、定位梳理、内容支柱搭建、冷启动方案、老号接手与诊断 | | 账号对标 | Account Benchmark | 对标账号 / 对标视频分析,并与默认或指定 DNA 逐项比较 | | 改稿与调整 | Editing | 改脚本、润色口播、换钩子、换风格、换封面、压缩时长 | @@ -43,16 +43,16 @@ metadata: 跨领域通用技能:`published-track`(发布记录与指标库)、`content-calibrator`(DNA 表现评估)、`smart-search`(跨平台搜索,选题调研优先社交平台)、`council`(定位决策辅助)、`siliconflow-img-gen`(封面图生成)。 -视频制作链路(内容生产时按需编排,不属于本专家包):`content-producer` subagent(吃 Brief 制作成片)、`video-edit`(已有素材加工拼接)、`talking-head-cut`(口播轻剪辑)、`viral-chaser`(抖音/B站/小红书视频追爆拆解)。 +素材加工相关技能:`video-edit`(素材加工拼接)、`talking-head-cut`(口播轻剪辑)、`ui-demo`(产品操作录屏)、`video-review`(成片质检闸门)、`siliconflow-img-gen`(封面图)、`pexels-footage` / `pixabay-footage`(免版权素材)。 -**分工硬边界**:main 负责选题策划、按 DNA 出 **Brief**、拟定短标题与视频描述、准备素材(绝对路径写进 Brief)、监督推动 CP、成片后的发布与运营,也直接做已有素材轻加工;视频全案的成片制作委托 `content-producer`。口播类视频的口播文案由 main 按 `narration-script` 子模块写好并随 Brief 交付(真人口播时向用户取得录音文件),CP 不重写策略文案。Brief 指定 `workflow` 时 CP 必须采用,未指定时 CP 按通用阶段链自由发挥。Brief **不含 DNA 信息**,main 也不替 CP 建工作区(双方 T3 权限可互访取文件)。 +**视频全案分工硬边界**:main 负责选题策划、按 DNA 出 **Brief**、拟定标题与简介、准备素材(用户素材预处理 / `ui-demo` 录屏 / 从 `campaign_assets/` 挑选,绝对路径写进 Brief)、监督推动 CP、成片后的发布与运营,也直接做图文内容与已有素材轻加工;视频全案的成片制作委托 `content-producer`。口播类视频的口播文案由 main 按 `narration-script` 子模块写好并随 Brief 交付(真人口播时,指导用户录音并取得录音文件),CP 不重写策略文案。Brief 指定 `workflow` 时 CP 必须采用,未指定时 CP 按通用阶段链自由发挥。Brief **不含 DNA 信息**,main 也不替 CP 建工作区(双方 T3 权限可互访取文件)。 ## 平台速查 - 视频号核心引擎是**社交推荐 > 算法推荐**:分享(转发朋友圈/群聊)权重高于点赞;判断内容健康度交叉看「完播 × 分享」。 - 视频号发布页可同时填 **视频描述**(≤300 字,含 hashtag)与 **短标题**,官方称填短标题能获得更多流量:**两项都必须有、发布时都必须填**,都由 main agent 拟定。但作品管理页不展示短标题,所以取数、`wx-channel-engagement` 匹配与 `published-track record --platform wx_channel --title` 一律只用完整视频描述(`--title` 只是数据库字段名),**短标题不入库**。 - 发布与取数共用持久化 session `wechat-channel`(fail-first 队列):读到「session 正忙」就等当前操作完成再重试,不自动 close。 -- 前 3 秒决定去留:封面三要素(身份 + 痛点 + 解决方案),前 2 秒抛冲突,第 3 秒预告价值。 +- 前 3 秒决定去留:身份 + 痛点 + 解决方案,前 2 秒抛冲突,第 3 秒预告价值。 - 真人出镜占比建议 ≥ 60%;起号期前 5 条必须垂直打透一个定位,周更 3-5 条。 - 冷启动只发动真实私域(点赞-评论-转发三连),禁止买量、互刷、群控、诱导互动(「点赞关注才发」类话术)。 - 除自己账号外没有公开抓取路径:对标样本的文案与数据必须用户提供,不得编造。 @@ -62,7 +62,7 @@ metadata: DNA 存储目录是 `wx_channel/dna/`。未指定 DNA 时默认使用并更新 `dna-0`。生产前同时读取 DNA 文档与 DNA template;对标分析先建立独立对标 DNA,不默认写入 `dna-0`。 -DNA 是**从一批作品样本提取并聚合出的内容生产规则集**(不存在「平台级 / 账号级 DNA」):8 维——选题与观看理由、短标题与视频描述与封面、内容创意、视频内容形态与制作指向、制作规格与视听倾向,加可选的口播文案子模块与账号运营子模块(简介写法、发布习惯)。它指导 main agent 出 Brief(+ 口播文案)与发布文案,不规定创作细节与成片制作。维度框架 v2 位于 `wx-channel-style-profiler` 的 `references/video-dna-framework.md`。 +DNA 是**从一批作品样本提取并聚合出的内容生产规则集**:8 维——选题与观看理由、短标题与视频描述与封面、内容创意、视频内容形态与制作指向、制作规格与视听倾向,加可选的口播文案子模块与账号运营子模块(简介写法、发布习惯)。它指导 main agent 出 Brief(+ 口播文案)与发布文案,不规定创作细节与成片制作。维度框架 v2 位于 `wx-channel-style-profiler` 的 `references/video-dna-framework.md`。 ## 数据与记录 diff --git a/crews/main/skills/expert-wx-channel/workflows/content-production.md b/crews/main/skills/expert-wx-channel/workflows/content-production.md index 0d9bc228..472243fd 100644 --- a/crews/main/skills/expert-wx-channel/workflows/content-production.md +++ b/crews/main/skills/expert-wx-channel/workflows/content-production.md @@ -2,7 +2,7 @@ 从选题、Brief 到发布的完整视频运营流程。用户说“帮我做一条视频”“出几条视频”“这条的主题我们也做一条”走这个。 -**分工硬边界**:main agent 负责选题策划、按 DNA 出 Brief、拟定短标题与视频描述、准备素材(含简单加工)、监督 CP 进程、成片后的发布与运营;视频全案的成片制作委托 `content-producer`。口播类视频的口播文案由 main agent 写好随 Brief 交付(真人口播时向用户取得录音文件),其余声画制作归 CP。 +**分工硬边界**:main agent 负责选题策划、按 DNA 出 Brief、拟定短标题与视频描述、准备素材(含简单加工)、监督 CP 进程、成片后的发布与运营;视频全案的成片制作委托 `content-producer`。口播类视频的口播文案由 main agent 写好随 Brief 交付(真人口播时指导用户按口播稿录音,完成后向用户取得录音文件),其余声画制作归 CP。 ## Step 0 - 入口判断 @@ -163,21 +163,7 @@ DNA 约束的是选题与观看理由、短标题与视频描述写法、内容 第二个必停节点。务必与用户就短标题与完整视频描述一并确认。用户如果提出修改意见,按用户意见修改,直至与用户达成一致。 -## Step 5 - 封面图 - -1. 读取 DNA template 的标题封面结论与制作规格,只取风格边界。 -2. 结合本条核心传达、目标观众和封面文字承诺,确定本次封面的主体、色彩、光线、文字视觉(身份 + 痛点 + 解决方案)和负向约束。 -3. 优先路径: - - 有实拍 / 成片素材 -> 从素材中截取候选帧,交用户挑选。 - - 无合适素材 -> 使用 `siliconflow-img-gen` 生成。 -4. 用户已提供封面或封面素材时,可基于其做编辑补充必要要素;不足以成为封面(尺寸偏小、元素不全)时作为生成参考;用户明确说就按他提供的,直接使用。 -5. 生成的封面图发给用户确认,确认后保存到 `wx_channel/outputs//cover.jpg`。 - -## 【确认】封面 - -第三个必停节点。封面图确认,如果用户有意见,按照他的意见修改,直至确认。 - -## Step 6 - 制作 Brief +## Step 5 - 制作 Brief 每条需要 CP 全案制作的视频,main agent 只产出 `wx_channel/outputs//brief.md`。Brief 是 main / CP 的唯一交接物。 @@ -213,7 +199,7 @@ Brief 硬性规则: ### 口播文案规则 - **口播类视频的口播文案由 main agent 出**:按 DNA 文档的 `narration-script`(口播文案子模块)写终稿,保存为作品目录下的 `voiceover.md`,在 Brief 中给**绝对路径**。CP 不重写策略文案,只做声画实现。 -- **真人口播**:明确要用用户真人声音时,main agent 必须向用户取得录音文件,落到作品目录并在 Brief 中给绝对路径。 +- **真人口播**:明确要用用户真人声音时,指导用户按口播稿录音,完成后向用户取得录音文件。 - **口播子模块未启用**:Brief 写明「口播文案:不适用」或只给要点,由 CP 按其 workflow 组织旁白;main 不再规定逐句台词。 - 用户必用的事实、案例、承诺和 CTA 必须进入 Brief 或口播终稿,不得为了形式删除关键事实。 @@ -221,7 +207,7 @@ Brief 硬性规则: 第四个必停节点。用户确认 Brief(及已启用的口播终稿)后才委托 CP。Brief 变更时更新版本并推送变更要点;已开工中间产物按新版取舍,弃用部分记入交付说明。 -## Step 7 - 视频制作 +## Step 6 - 视频制作 取得成片 `wx_channel/outputs//video.mp4`(CP 交付后按其回报的绝对路径取回,main 不替 CP 建工作区): diff --git a/crews/main/skills/expert-wx-channel/workflows/style-dna.md b/crews/main/skills/expert-wx-channel/workflows/style-dna.md index 6ea6fd55..6d0a8a1e 100644 --- a/crews/main/skills/expert-wx-channel/workflows/style-dna.md +++ b/crews/main/skills/expert-wx-channel/workflows/style-dna.md @@ -145,7 +145,7 @@ wx-channel-style-profiler update \ - **视频全案**:读取 DNA 文档与 template,main agent 产出 **Brief**(+ 口播类的口播文案)。Brief 写明选题与观看理由、内容创意、`workflow`(视频形态的制作指向)、制作规格、素材清单与授权(绝对路径)、验收标准、闸门批准人。 - **短标题与视频描述由 main agent 拟定**:视频号发布页两项都可填,官方称填短标题能获得更多流量,因此两项都必须有、发布时都必须填;但入库(`published-track`)与 `wx-channel-engagement` 匹配只用**视频描述**,短标题不写库。这两项属发布文案,不进 Brief 的制作要求。 - **Brief 不含 DNA 信息**:Content Producer 看不到 main 的 DNA,只按 Brief 制作;也不要把 DNA 文档路径写进 Brief。 -- **口播类视频**:口播文案子模块启用时,口播终稿由 main agent 写好并随 Brief 交付;真人口播时由 main agent 向用户取得录音文件。CP 不重写策略文案。 +- **口播类视频**:口播文案子模块启用时,口播终稿由 main agent 写好并随 Brief 交付;真人口播时由 main agent 指导用户录音并向用户取得录音文件。CP 不重写策略文案。 - **工作区**:main 不替 CP 建工作区,也不指定项目目录;CP 自建工作区,双方 T3 权限可互访取文件。 ## 对标接口 diff --git a/crews/main/skills/expert-xhs/SKILL.md b/crews/main/skills/expert-xhs/SKILL.md index 86f2e012..8fe8d351 100644 --- a/crews/main/skills/expert-xhs/SKILL.md +++ b/crews/main/skills/expert-xhs/SKILL.md @@ -15,7 +15,7 @@ metadata: | 场景 | Workflow | 什么时候触发 | |------|----------|-------------| | 内容 DNA 管理 | Style DNA | 建 / 更新内容 DNA(样本、偏好、局部借鉴、对标融合):先判作品类型,再决定样本落到哪个 DNA | -| 内容生产 | Content Production | 做一条 / 做几条小红书内容;图文由 main 直接生产,视频全案只产出并委托 Brief | +| 内容生产 | Content Production | 做一条 / 做几条小红书内容;图文由 main 直接生产,视频全案只产出Brief并委托content-producer | | 起号与定位 | Account Setup | 新号起号、定位梳理、内容支柱搭建、老号接手与诊断 | | 账号对标 | Account Benchmark | 对标账号 / 对标笔记分析(关键词提取 + 低粉爆款搜索),并与默认或指定 DNA 逐项比较 | | 改稿与调整 | Editing | 改标题、改正文、换封面、换标签、换风格 | @@ -48,9 +48,9 @@ metadata: DNA 存储目录是 `xhs/dna/`。未指定 DNA 时默认使用并更新 `dna-0`(图文);视频笔记另建 dna-id(如 `dna-0-video`)。生产前同时读取 DNA 文档与 DNA template;对标分析先建立独立对标 DNA,不默认写入 `dna-0`。 -DNA 是**从一批作品样本提取并聚合出的内容生产规则集**(不存在「平台级 / 账号级 DNA」):图文 9 维——选题与观看理由、标题与封面图组、内容创意、**匹配的用户问题**、正文表达与语气、图组视觉、互动引导与转化,加账号运营子模块(简介写法、内容形式比例、发布习惯);视频 9 维——前四项加视频内容形态与制作指向、制作规格与视听倾向、口播文案子模块与账号运营子模块。搜索维度是小红书必备:最大流量池来自搜索,关键词必须落到用户可能的提问原句。DNA 指导 main agent 写图文或出视频 Brief(+ 口播文案),不规定创作细节与成片制作。维度框架 v2 位于 `xhs-style-profiler` 的 `references/note-dna-framework.md` 与 `references/video-dna-framework.md`。 +DNA 是**从一批作品样本提取并聚合出的内容生产规则集**:图文 9 维——选题与观看理由、标题与封面图组、内容创意、**匹配的用户问题**、正文表达与语气、图组视觉、互动引导与转化,加账号运营子模块(简介写法、内容形式比例、发布习惯);视频 9 维——前四项加视频内容形态与制作指向、制作规格与视听倾向、口播文案子模块与账号运营子模块。搜索维度是小红书必备:最大流量池来自搜索,关键词必须落到用户可能的提问原句。DNA 指导 main agent 写图文或出视频 Brief(+ 口播文案),不规定创作细节与成片制作。维度框架 v2 位于 `xhs-style-profiler` 的 `references/note-dna-framework.md` 与 `references/video-dna-framework.md`。 -**分工硬边界**:图文和长文由 main 直接生产;已有视频素材轻加工由 main 做;视频全案由 main 出 **Brief**(+ 口播类的口播文案 / 录音)并委托 `content-producer`,CP 只做声画实现。Brief 指定 `workflow` 时 CP 必须采用,未指定时 CP 按通用阶段链自由发挥。Brief **不含 DNA 信息**;main 不替 CP 建工作区(双方 T3 权限可互访取文件),成片与封面按 CP 回报的绝对路径取回作品目录。 +**视频全案分工硬边界**:main 负责选题策划、按 DNA 出 **Brief**、拟定标题与简介、准备素材(用户素材预处理 / `ui-demo` 录屏 / 从 `campaign_assets/` 挑选,绝对路径写进 Brief)、监督推动 CP、成片后的发布与运营,也直接做图文内容与已有素材轻加工;视频全案的成片制作委托 `content-producer`。口播类视频的口播文案由 main 按 `narration-script` 子模块写好并随 Brief 交付(真人口播时,指导用户录音并取得录音文件),CP 不重写策略文案。Brief 指定 `workflow` 时 CP 必须采用,未指定时 CP 按通用阶段链自由发挥。Brief **不含 DNA 信息**,main 也不替 CP 建工作区(双方 T3 权限可互访取文件)。 ## 数据与记录 diff --git a/crews/main/skills/expert-xhs/tools/xhs-style-profiler/references/note-dna-framework.md b/crews/main/skills/expert-xhs/tools/xhs-style-profiler/references/note-dna-framework.md index 4777081a..b69e84fb 100644 --- a/crews/main/skills/expert-xhs/tools/xhs-style-profiler/references/note-dna-framework.md +++ b/crews/main/skills/expert-xhs/tools/xhs-style-profiler/references/note-dna-framework.md @@ -5,7 +5,7 @@ ## 定位与边界 -DNA 不是「平台级 DNA」,也不是账号人设说明书,而是**从一批作品样本中提取、聚合出的内容生产规则集**。生产方式与其他平台完全一致,仍是三层产物: +DNA 不是「平台级 DNA」,也不是账号人设说明书,而是**从一批作品样本中提取、聚合出的内容生产规则集**。生产方式为三层产物: ```text 单篇作品 -> DNA report(本框架的维度逐项提取) diff --git a/crews/main/skills/expert-xhs/workflows/content-production.md b/crews/main/skills/expert-xhs/workflows/content-production.md index 2d0f7997..459dc02b 100644 --- a/crews/main/skills/expert-xhs/workflows/content-production.md +++ b/crews/main/skills/expert-xhs/workflows/content-production.md @@ -6,8 +6,7 @@ - 图文笔记、长文内容:main agent 直接生产(含标题、正文、图组、话题标签与发布)。 - 已有视频素材的简单加工:main agent 直接做(`video-edit` / `talking-head-cut`)。 -- 视频全案:main agent 只产出 **Brief**(+ 口播类的口播文案 / 录音),成片制作委托 `content-producer`;CP 只做声画实现。 -- Brief 指定 `workflow` 时 CP 必须采用;未指定时 CP 按通用阶段链自由发挥。 +- 视频全案:main agent 负责选题策划、按 DNA 出 Brief、拟定短标题与视频描述、准备素材(含简单加工)、监督 CP 进程、成片后的发布与运营;视频全案的成片制作委托 `content-producer`。口播类视频的口播文案由 main agent 写好随 Brief 交付(真人口播时指导用户按口播稿录音,完成后向用户取得录音文件),其余声画制作归 CP。 ## Step 0 - 入口判断 @@ -188,7 +187,7 @@ Brief 硬性规则: ### 口播文案规则 - **口播类视频的口播文案由 main agent 出**:按 DNA 文档的 `narration-script`(口播文案子模块)写终稿,保存为作品目录下的 `voiceover.md`,在 Brief 中给**绝对路径**。CP 不重写策略文案,只做声画实现。 -- **真人口播**:明确要用用户真人声音时,main agent 必须向用户取得录音文件,落到作品目录并在 Brief 中给绝对路径。 +- **真人口播**:明确要用用户真人声音时,指导用户按口播稿录音,完成后向用户取得录音文件,落到作品目录并在 Brief 中给绝对路径。 - **口播子模块未启用**:Brief 写明「口播文案:不适用」或只给要点,由 CP 按其 workflow 组织旁白;main 不再规定逐句台词。 - 用户必用的事实、案例、承诺和 CTA 必须进入 Brief 或口播终稿,不得为了形式删除关键事实。 diff --git a/crews/main/skills/expert-xhs/workflows/style-dna.md b/crews/main/skills/expert-xhs/workflows/style-dna.md index ed25b588..a48db684 100644 --- a/crews/main/skills/expert-xhs/workflows/style-dna.md +++ b/crews/main/skills/expert-xhs/workflows/style-dna.md @@ -4,7 +4,7 @@ ## 边界 -- DNA 是从一批作品样本中提取、聚合出的内容生产规则集,不存在「平台级 DNA」或「账号级 DNA」的说法;样本可以来自多个账号,也可以来自用户指定的一个账号的发布列表批量提取。 +- DNA 是从一批作品样本中提取、聚合出的内容生产规则集,样本可以来自多个账号,也可以来自用户指定的一个账号的发布列表批量提取。 - 账号初始化与默认 `dna-0` 建立走 `account-setup.md`;对标样本先走 `account-benchmark.md`。 - DNA 如何用于内容生产走 `content-production.md`;改稿走 `editing.md`;数据复盘走 `review.md`。 - 图文由 main agent 直接生产;视频全案只到 Brief(+ 口播文案),制作委托 `content-producer`。 diff --git a/crews/sales-cs/AGENTS.md b/crews/sales-cs/AGENTS.md index e2d74565..462f284a 100644 --- a/crews/sales-cs/AGENTS.md +++ b/crews/sales-cs/AGENTS.md @@ -271,8 +271,8 @@ --- ### awada 回复发送规则(强制) -- 在 awada 会话中,常规回复必须直接输出 assistant 文本,不要调用 `message` 工具二次发送。 -- `message` 工具仅用于明确的主动外呼场景;当前会话应答禁止使用。 +- 在 awada 会话中,常规回复必须直接输出 assistant 文本,不要用 message 工具二次发送相同的文本内容(这会导致客户收到重复消息)。 +- message 工具用于主动外呼,以及技能明确要求在当前会话发送预置云文件的场景(如 payment-send 用 sendAttachment 发付款码、demo-send 发视频)。 - 若工具调用报错(如 Unknown target / send failed),不得把报错文本透传给客户,必须改为正常人工话术重答。 --- From 53d4d19e1252ec7117323ed5fb3276455fad0371 Mon Sep 17 00:00:00 2001 From: codes-factory-of-bg Date: Thu, 10 Sep 2026 22:49:24 +0800 Subject: [PATCH 11/34] =?UTF-8?q?fix(dna+expert-pack):=20=E5=AE=A1?= =?UTF-8?q?=E6=A0=B8=E5=8F=8D=E9=A6=88=E4=BF=AE=E5=A4=8D=E2=80=94=E2=80=94?= =?UTF-8?q?=E8=A1=A5=E4=B8=9A=E5=8A=A1=E6=A4=8D=E5=85=A5=20/=20CTA=20?= =?UTF-8?q?=E4=B8=A4=E7=BB=B4=E3=80=81=E4=BF=AE=20xhs=20=E4=BD=9C=E5=93=81?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B=E8=A1=A8=E3=80=81=E9=87=8D=E5=86=99=E8=A7=86?= =?UTF-8?q?=E9=A2=91=E5=8F=B7=20content-production?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 一、DNA 框架补两维(抖音 / 小红书 / 视频号,共 5 份框架) - 新增 `biz-implant` 业务植入套路:是否植入、植入位置与时机、植入载体、方式原型 (反转植入 / 痛点→方案 / 场景带入 / 实测对比 / 清单第 N 项 / 身份认同 / 口碑故事 / 教程内嵌 / 硬广直给)、内容与业务的衔接句、植入密度与占比、品牌词出现方式。 - 新增 `interaction-cta` 互动引导与 CTA 套路(图文框架由原「互动引导与转化」升级而来): 行动目标与本篇主目标、CTA 位置与时机、句式与原文摘录、行动数量、诱因设计、 与转化目标的对应、平台组件与合规边界。 - 与 `narration-script` 划界:口播子模块的「合」只记收束方式,CTA 目标与句式归 `interaction-cta`;与 `video-form` 划界:形态层面的「影视解说 + 反转植入」记形态与 制作指向,本维度记植入怎么设计。 - 聚合规则补硬要求:两维必须给出「位置 + 载体 + 原文摘录」三样证据,只写「自然植入」 「引导关注」不算提取完成;无植入写「无植入(纯内容)」;平台红线记为必须避免项。 - 维度数:抖音 视频 10 / 图文 9,小红书 视频 11 / 图文 10,视频号 10。三份 build_style_profile.py 的 DIMENSION_GROUPS / REPORT_DIMENSION_PROMPTS / TEMPLATE_STAGES / TEMPLATE_STAGE_FIELDS / TEMPLATE_CHECKLISTS 同步;template 新增 「[业务植入与 CTA部分]」(图文侧取代原「互动与标签」段,话题标签策略仍在标题与封面段)。 二、删「统计与分词」 - 三个 style-profiler SKILL.md 的「## 统计与分词」段删除:分词(相邻二字组合)在脚本里 算完从未渲染进 report 或 DNA 文档,属死代码;统计口径合并进「职责边界」一行。 - 脚本移除 extract_terms / terms / stable_terms / STOP_TERMS / ENGLISH_WORD_RE 及未使用的 Counter、defaultdict、median 导入;同时删掉同样从未被读取的 weighted_coverage 聚合项。 - 框架文档里「高频词只是候选线索」相应改为「口头禅与签名表达必须回读原文确认」。 三、xhs-style-profiler 作品类型表纠正 - 默认 `--kind` 是 note,但表里把 `dna-0` 挂在 video、`dna-0-note` 挂在 note,与脚本 KIND_SUFFIX_HINT、框架文档、style-dna.md、专家包 SKILL.md 全部相反。改为 note(默认) → `dna-0`、video → `dna-0-video`,并把默认 kind 排在表格首行。 四、视频号 content-production.md 按抖音版重写 - 补 Step 0「识别制作路线」(素材组装 / 从零制作 / 脚本制作)与素材模式、脚本模式入口。 - Step 编号连续(0-7):原文件 Step 6 之后直接跳到 Step 8/9/10;制作合并为 Step 5 (路线 A main 直接做轻加工 / 路线 B、C 出 Brief 委托 CP),存文件与入库合并为 Step 7。 - 确认节点回到三个:选题 / 短标题与视频描述 / 成片与封面(原「第四个必停节点」编号错, 且缺成片确认)。Brief 增「业务植入与 CTA」字段。 - 视频号特有约束全部保留:短标题 + 视频描述双字段、取数与入库只用视频描述、短标题不写库、 无公开下载路径、30-55 人群与真人出镜占比、转发动机、session 正忙与扫码登录、sph 链接。 - editing.md 的 Step 引用同步(Step 7-10 → Step 5-7)。 五、措辞与下游同步 - 清掉写给开发者的元叙述:「DNA 不是『平台级 DNA』」「不存在『平台级 DNA』或『账号级 DNA』 的说法」「不是账号级结论」在 5 份框架、style-dna.md、三份工具 SKILL.md 中改为正向表述。 - Brief 新字段同步到抖音 / 小红书 content-production 与 CP 侧交接契约 (expert-video/SKILL.md 输入表、reversal-ad.md Brief 必备字段 implant_cta)。 - review.md 归因表补植入 / CTA 卡点行(组件点击、私信成交、广告感重),account-benchmark.md 与 style-dna.md 的语义段 / 逐项比较清单补两维;旧的「互动与标签」「互动引导与转化」 名称全仓清理干净。 - docs/expert-pack-dna-architecture.md:4.7 维度表更新为 10/9、11/10、10 维,硬规则补 「业务植入与 CTA 是三平台共有维度」。 自测:三平台 report → build → update 全链路跑通(维度数与 template 段与框架文档一致), focus 新 ID 可用、跨 kind 仍报错、混型 build 仍报错;另跑交叉校验脚本比对 脚本维度 == 框架维度表 == 框架 Focus 表 == SKILL.md Focus 表 == template 段顺序,全通过。 未触碰本机部署实例(~/.openclaw 只读);expert-wx-mp 不在本次范围,其 SKILL.md 仍有 同样的「统计与分词」段与死代码,待确认后另行处理。 --- .../skills/expert-video/SKILL.md | 2 +- .../expert-video/workflows/reversal-ad.md | 1 + crews/main/skills/expert-douyin/SKILL.md | 2 +- .../tools/douyin-style-profiler/SKILL.md | 26 +-- .../references/note-dna-framework.md | 26 +-- .../references/video-dna-framework.md | 36 ++-- .../scripts/build_style_profile.py | 80 ++------- .../workflows/account-benchmark.md | 6 +- .../workflows/content-production.md | 6 +- .../skills/expert-douyin/workflows/review.md | 6 +- .../expert-douyin/workflows/style-dna.md | 2 +- crews/main/skills/expert-wx-channel/SKILL.md | 2 +- .../tools/wx-channel-style-profiler/SKILL.md | 34 ++-- .../references/video-dna-framework.md | 36 ++-- .../scripts/build_style_profile.py | 72 +------- .../workflows/account-benchmark.md | 6 +- .../workflows/content-production.md | 168 ++++++++++-------- .../expert-wx-channel/workflows/editing.md | 6 +- .../expert-wx-channel/workflows/review.md | 5 +- .../expert-wx-channel/workflows/style-dna.md | 4 +- crews/main/skills/expert-xhs/SKILL.md | 2 +- .../tools/xhs-style-profiler/SKILL.md | 32 ++-- .../references/note-dna-framework.md | 26 +-- .../references/video-dna-framework.md | 36 ++-- .../scripts/build_style_profile.py | 80 ++------- .../expert-xhs/workflows/account-benchmark.md | 4 +- .../workflows/content-production.md | 5 +- .../skills/expert-xhs/workflows/review.md | 9 +- .../skills/expert-xhs/workflows/style-dna.md | 2 +- docs/expert-pack-dna-architecture.md | 13 +- 30 files changed, 309 insertions(+), 426 deletions(-) diff --git a/crews/content-producer/skills/expert-video/SKILL.md b/crews/content-producer/skills/expert-video/SKILL.md index 7990c9c8..12a9c6f4 100644 --- a/crews/content-producer/skills/expert-video/SKILL.md +++ b/crews/content-producer/skills/expert-video/SKILL.md @@ -80,7 +80,7 @@ Workflow 文档在包内 `workflows/<字段值>.md`。Brief 指定了 `workflow` | 项 | 要求 | |----|------| -| `brief.md` | 绝对路径。含视频类型/workflow、主题与观看理由、核心传达、时长与横竖屏、素材清单、封面要求、交付与验收、闸门批准人。**不含 DNA 信息**(甲方内部资产,本包不读也不用) | +| `brief.md` | 绝对路径。含视频类型/workflow、主题与观看理由、核心传达、业务植入与 CTA(植入位置与方式、内容与业务的衔接句要求、CTA 主目标与句式)、时长与横竖屏、素材清单、封面要求、交付与验收、闸门批准人。**不含 DNA 信息**(甲方内部资产,本包不读也不用) | | 已有素材 | 绝对路径逐条列出,含来源与授权说明;本包只做入库校验与技术处理 | | 口播文案 | 有口播时由甲方出具(`voiceover.md`,绝对路径)。本包不重写策略文案,只做声画实现 | | 口播录音 | 明确真人口播时,甲方提供录音文件绝对路径 | diff --git a/crews/content-producer/skills/expert-video/workflows/reversal-ad.md b/crews/content-producer/skills/expert-video/workflows/reversal-ad.md index 26be4be6..9c032243 100644 --- a/crews/content-producer/skills/expert-video/workflows/reversal-ad.md +++ b/crews/content-producer/skills/expert-video/workflows/reversal-ad.md @@ -67,6 +67,7 @@ Brief 里写 `workflow: reversal-ad` 时使用。本 workflow 只写这一类型 | platform | douyin / wx_channel / xhs 等,用于画幅、时长带与合规边界 | | core_message | 本条必须传达的核心信息 | | product_points | 产品/服务事实、允许讲的能力、禁用承诺(只以 Brief 为准,不内置品牌事实) | +| implant_cta | Brief「业务植入与 CTA」字段:植入位置与方式、内容与业务的衔接句要求、CTA 主目标与句式;未给时按本 workflow 默认(反转点 55%–76%、植入段单点深打、片尾一个主行动),并在 GATE A 说明 | | twist_variant | 任务指引式 / 双关置换式 / 身份彩蛋式 / 戏中戏式;未指定时由本包据素材与故事选定,并在 GATE A 说明理由 | | story_source | 解说正文的故事来源(开源片名 / 用户素材 / AIGC 生成) | | voiceover | 甲方交付口播终稿路径;未交付时写明由本包起草 | diff --git a/crews/main/skills/expert-douyin/SKILL.md b/crews/main/skills/expert-douyin/SKILL.md index 95681d3f..3dfb2a75 100644 --- a/crews/main/skills/expert-douyin/SKILL.md +++ b/crews/main/skills/expert-douyin/SKILL.md @@ -51,7 +51,7 @@ metadata: DNA 存储目录是 `douyin/dna/`。未指定 DNA 时默认使用并更新 `dna-0`(视频);图文另建 dna-id(如 `dna-0-note`)。生产前同时读取 DNA 文档与 DNA template;对标分析先建立独立对标 DNA,不默认写入 `dna-0`。 -DNA 是**从一批作品样本提取并聚合出的内容生产规则集**:视频 8 维——选题与观看理由、标题与封面、内容创意、视频内容形态与制作指向、制作规格与视听倾向,加可选的口播文案子模块与账号运营子模块(简介写法、内容形式比例、发布习惯);图文 8 维——选题、标题与封面图组、内容创意、正文表达与语气、图组视觉、互动引导与转化,加账号运营子模块。它指导 main agent 出图文内容或视频 Brief(+ 口播文案),不规定创作细节与成片制作。维度框架 v2 位于 `douyin-style-profiler` 的 `references/video-dna-framework.md` 与 `references/note-dna-framework.md`。 +DNA 是**从一批作品样本提取并聚合出的内容生产规则集**:视频 10 维——选题与观看理由、标题与封面、内容创意、**业务植入套路**、**互动引导与 CTA 套路**、视频内容形态与制作指向、制作规格与视听倾向,加可选的口播文案子模块与账号运营子模块(简介写法、内容形式比例、发布习惯);图文 9 维——选题、标题与封面图组、内容创意、正文表达与语气、图组视觉、**业务植入套路**、**互动引导与 CTA 套路**,加账号运营子模块。它指导 main agent 出图文内容或视频 Brief(+ 口播文案),不规定创作细节与成片制作。维度框架 v2 位于 `douyin-style-profiler` 的 `references/video-dna-framework.md` 与 `references/note-dna-framework.md`。 ## 数据与记录 diff --git a/crews/main/skills/expert-douyin/tools/douyin-style-profiler/SKILL.md b/crews/main/skills/expert-douyin/tools/douyin-style-profiler/SKILL.md index 9d6e5cf2..7acc625d 100644 --- a/crews/main/skills/expert-douyin/tools/douyin-style-profiler/SKILL.md +++ b/crews/main/skills/expert-douyin/tools/douyin-style-profiler/SKILL.md @@ -1,6 +1,6 @@ --- name: douyin-style-profiler -description: 提取抖音作品 DNA:单篇作品(视频 / 图文)生成 report,按 dna-id 聚合选题、标题与封面、内容创意、视频形态与制作指向、制作规格、口播文案子模块与账号运营子模块,推导 main agent 的 Brief / 图文生产 template。 +description: 提取抖音作品 DNA:单篇作品(视频 / 图文)生成 report,按 dna-id 聚合选题、标题与封面、内容创意、业务植入套路、互动引导与 CTA 套路、视频形态与制作指向、制作规格、口播文案子模块与账号运营子模块,推导 main agent 的 Brief / 图文生产 template。 metadata: openclaw: emoji: 🧬 @@ -33,7 +33,7 @@ DNA 的用途是指导 main agent 选题、包装、出内容或出视频制作 DNA 文档 -> DNA template ``` -- **DNA report**:单篇作品的样本观测 + 维度提取结果(不是账号级结论,也不是模板)。 +- **DNA report**:单篇作品的样本观测 + 维度提取结果;跨篇共性与生产规则由聚合阶段给出,report 本身不是模板。 - **DNA 文档**:聚合后的生产规则、样本覆盖度、子模块结论与用户输入转译区。 - **DNA template**:main agent 的生产输入模板(视频 = Brief 正文 + 口播文案;图文 = 写作模板)。 @@ -55,11 +55,11 @@ douyin/dna/{dna-id}/ - 抖音视频与图文都常见:先判作品类型再选框架,不要把图文笔记塞进视频 DNA。 - 单篇 report 只提供候选信号,不判断跨篇稳定性;共性、偏好、孤例由聚合阶段判断。 -- 统计只做证据底座,不评分、不判定风格是否合格。 +- 脚本统计(句长、问句与人称密度、感叹号密度、口播密度;图文另有标题字数、正文行数、emoji 密度、话题标签数)只做证据底座,不评分、不判定风格是否合格;口头禅与签名式表达必须由 Agent 回读原文确认,不能凭统计直接下 DNA 结论。 - 视觉维度必须有图片 / 关键帧证据,由视觉模型读取;缺失写「未提供」,不得凭文本想象补齐。 - 口播文案子模块只在口播类作品启用;样本不足写「未启用」。 - 账号运营子模块(简介写法、内容形式比例、发布习惯)只在样本来自对标账号批量提取时填写,且**不进 template**。 -- 不输出合规结论、账号权重或风格评分。 +- 不输出风格评分或账号权重;合规只记「必须避免项」(虚假承诺、利益诱导互动、隐藏站外联系方式、谐音绕检测),不出具合规审查结论。 ## Report — 单篇提取 @@ -141,9 +141,10 @@ douyin-style-profiler update \ 1. 选题 2. 标题与封面 3. 内容创意 -4. 视频形态与制作指向 -5. 制作规格 -6. 口播文案 +4. 业务植入与 CTA +5. 视频形态与制作指向 +6. 制作规格 +7. 口播文案 **图文作品 template(= 图文写作模板)** @@ -152,7 +153,7 @@ douyin-style-profiler update \ 3. 内容创意与结构 4. 正文表达 5. 图组 -6. 互动与标签 +6. 业务植入与 CTA - 开头两段(**选题**、**标题与封面**)跨平台通用。 - 视频 template 的各段直接对应 Brief 正文字段;**Brief 不含 DNA 信息**(Content Producer 看不到 main 的 DNA),素材清单与授权、验收标准、闸门批准人按平台 Content Production Workflow 填。 @@ -191,6 +192,8 @@ narration-script:口播保持第三人称解说体,句长 10-15 字 | `content-idea` | 内容创意 | | `video-form` | 视频内容形态与制作指向 | | `production-spec` | 制作规格与视听倾向 | +| `biz-implant` | 业务植入套路 | +| `interaction-cta` | 互动引导与 CTA 套路 | | `narration-script` | 口播文案子DNA | | `account-bio` | 账号简介写法 | | `content-mix-cadence` | 内容形式比例与发布习惯 | @@ -204,16 +207,13 @@ narration-script:口播保持第三人称解说体,句长 10-15 字 | `content-idea` | 内容创意 | | `body-voice` | 正文表达与语气 | | `imageset-visual` | 图组视觉风格 | -| `interaction-cta` | 互动引导与转化 | +| `biz-implant` | 业务植入套路 | +| `interaction-cta` | 互动引导与 CTA 套路 | | `account-bio` | 账号简介写法 | | `content-mix-cadence` | 内容形式比例与发布习惯 | `--focus` 按作品类型校验:视频 report 不接受图文维度 ID,反之亦然。 -## 统计与分词 - -脚本统计句长、问句与人称密度、感叹号密度、口播密度(视频)/ 标题字数、正文行数、emoji 密度、话题标签数(图文)等指标作为聚合证据底座;中文高频信号使用相邻二字组合,仅作候选线索。口头禅与签名式表达必须由 Agent 回读原文确认,分词结果不能直接当 DNA 结论。 - ## 参考资料 - `references/video-dna-framework.md`(抖音视频作品 DNA 框架 v2:维度定义、制作指向映射、聚合边界与 Focus ID) diff --git a/crews/main/skills/expert-douyin/tools/douyin-style-profiler/references/note-dna-framework.md b/crews/main/skills/expert-douyin/tools/douyin-style-profiler/references/note-dna-framework.md index 83af0eb8..f600658e 100644 --- a/crews/main/skills/expert-douyin/tools/douyin-style-profiler/references/note-dna-framework.md +++ b/crews/main/skills/expert-douyin/tools/douyin-style-profiler/references/note-dna-framework.md @@ -1,11 +1,11 @@ # 抖音图文作品 DNA 框架(v2) -> 状态:v2(2026-09-10)。Focus ID、维度命名与 template 语义以本文件为准;调整维度必须升版本,并同步 `scripts/build_style_profile.py` 与工具 `SKILL.md`。 +> 状态:v2(2026-09-10 修订:补业务植入套路 `biz-implant` 与互动引导 / CTA 套路 `interaction-cta` 两维)。Focus ID、维度命名与 template 语义以本文件为准;调整维度必须升版本,并同步 `scripts/build_style_profile.py` 与工具 `SKILL.md`。 > 抖音有两套框架:视频作品用 `video-dna-framework.md`,本文件是**图文作品**框架。 ## 定位与边界 -DNA 不是「平台级 DNA」,也不是账号人设说明书,而是**从一批作品样本中提取、聚合出的内容生产规则集**。生产方式与其他平台完全一致,仍是三层产物: +DNA 是**从一批作品样本中提取、聚合出的内容生产规则集**:它回答「这类作品怎么选、怎么包装、业务怎么植入、怎么引导行动」,不是账号人设说明书。生产方式与其他平台完全一致,仍是三层产物: ```text 单篇作品 -> DNA report(本框架的维度逐项提取) @@ -21,11 +21,11 @@ DNA 文档 -> DNA template | 进 DNA | 不进 DNA | |--------|----------| -| 选题与观看理由、标题与封面写法、内容创意原型、正文表达与语气、图组视觉、互动引导与转化、(对标账号)账号运营子模块 | 创作细节、脚本结构、逐句台词、镜头表、转场与编码参数——那些归 Content Producer | +| 选题与观看理由、标题与封面写法、内容创意原型、正文表达与语气、图组视觉、业务植入套路、互动引导与 CTA 套路、(对标账号)账号运营子模块 | 创作细节、脚本结构、逐句台词、镜头表、转场与编码参数——那些归 Content Producer | 图文 DNA 的用途是指导 main agent 直接生产图文作品;template 就是图文写作模板。账号运营子模块(简介写法、内容形式比例、发布习惯)只写进 DNA 文档,**不进 template**。 -## 维度(8 维) +## 维度(9 维) ### 一、选题与包装 @@ -47,28 +47,30 @@ DNA 文档 -> DNA template | 4 | `body-voice` | 正文表达与语气 | 开头钩子、正文组织方式(清单体 / 教程步骤 / 故事线 / 对比 / 观点输出)、分行与段落节奏、口语化程度与人称、emoji 与标点用法、签名式表达 | | 5 | `imageset-visual` | 图组视觉风格 | 图片数量与顺序、构图类型(产品展示 / 场景 / 文字卡片 / 对比图 / 过程图)、图文信息分工、色调与质感、版式一致性、文字视觉 | -### 四、互动与转化 +### 四、业务植入与转化 | # | ID | 维度 | 观测内容 | |---|----|------|----------| -| 6 | `interaction-cta` | 互动引导与转化 | 评论 / 收藏 / 关注 / 进店 / 咨询等平台内行动的引导方式与位置、每篇行动引导数量、话题标签承载的意图、合规边界 | +| 6 | `biz-implant` | 业务植入套路 | 是否有业务植入(纯内容 / 软植入 / 硬广直给)、植入位置(开头 / 中段某一步 / 结尾 / 图组某一图 / 评论区自评)、植入载体(案例与数据、清单第 N 项、教程步骤内嵌、痛点故事转折、对比实测、产品截图、体验记录)、植入方式原型(痛点→方案 / 场景带入 / 实测对比 / 清单第 N 项 / 身份认同 / 口碑故事 / 教程内嵌 / 硬广直给)、内容与业务的衔接句(转折句原文)、植入密度与占比(正文字数占比、品牌词与产品名出现次数与方式) | +| 7 | `interaction-cta` | 互动引导与 CTA 套路 | 行动目标(评论 / 收藏 / 关注 / 进店 / 咨询 / 搜索品牌词)与本篇主目标、CTA 位置与时机(首段钩子后 / 正文中段 / 结尾 / 图组末图 / 评论区自评)、CTA 句式与原文摘录(命令式 / 提问式 / 利益式 / 身份式)、一篇放几个行动、诱因设计(利益点 / 情绪 / 身份认同 / 稀缺)、话题标签承载的意图、与业务转化目标的对应、合规边界 | ### 五、账号运营子模块(对标账号样本才有) | # | ID | 维度 | 观测内容 | |---|----|------|----------| -| 7 | `account-bio` | 账号简介写法 | 账号昵称、简介写法、主页与置顶表达、对外承诺(仅对标账号样本可得) | -| 8 | `content-mix-cadence` | 内容形式比例与发布习惯 | 图文 / 视频等内容形式比例、发布时间段与频率、内容形式混合节奏(如三篇图文对一篇视频)、栏目化节奏(仅对标账号批量样本可得) | +| 8 | `account-bio` | 账号简介写法 | 账号昵称、简介写法、主页与置顶表达、对外承诺(仅对标账号样本可得) | +| 9 | `content-mix-cadence` | 内容形式比例与发布习惯 | 图文 / 视频等内容形式比例、发布时间段与频率、内容形式混合节奏(如三篇图文对一篇视频)、栏目化节奏(仅对标账号批量样本可得) | ## Report 与聚合规则 1. 单篇 report 先填「样本观测」:作品类型、样本来源、账号与简介、发布时间、数据线索、图片数量与来源、关键词与标签。缺失一律写「未观测」,不得编造。 2. 单篇 report 不判断跨篇稳定性;聚合时区分高覆盖共性、高权重样本偏好、局部借鉴(focus)、孤例与例外,并标注样本覆盖度。 3. 视觉维度(封面 / 首帧 / 图组)必须有图片证据,由视觉模型读取本地图片后反推 AIGC 复现要素;没有图片写「未提供」。 -4. 正文表达维度里的高频词、口头禅与签名表达只是候选线索,必须回读原文确认后才能进 DNA 文档。 +4. 正文表达维度里的口头禅与签名表达只是候选线索,必须回读原文确认后才能进 DNA 文档。 5. 账号运营子模块(`account-bio`、`content-mix-cadence`)只在样本来自对标账号批量提取时填写;用户只提供单篇时写「未观测」。 6. 脚本统计只作证据底座(标题字数、正文行数、句长、问句密度、emoji 密度、话题标签数),不生成总分、不判定风格是否合格。 -7. 高数据样本必须回读创意、结构与关键词再归因,不得把高阅读直接等同于风格好。 +7. 业务植入与 CTA 两个维度必须给出**位置 + 载体 + 原文摘录**三样证据;只写「自然植入」「引导关注」这类空泛结论不算提取完成。本篇没有业务植入时明确写「无植入(纯内容)」,不得留空;平台红线(虚假承诺、利益诱导互动、隐藏站外联系方式、谐音绕检测)一律记为必须避免项。 +8. 高数据样本必须回读创意、结构与关键词再归因,不得把高阅读直接等同于风格好。 ## Template 语义 @@ -79,7 +81,7 @@ DNA template = **图文写作模板**,固定语义段如下(脚本 `build` / 3. **[内容创意与结构部分]** — 创意原型、正文组织方式、信息密度、记忆点、避免 4. **[正文表达部分]** — 开头钩子、推进方式、段落与分行节奏、人称与语气、emoji 与标点、签名式表达、必须做、避免 5. **[图组部分]** — 图片数量与顺序、构图类型、色调与质感、版式一致性、文字视觉、AIGC 提示词要素 -6. **[互动与标签部分]** — 互动目标、引导方式、话题标签策略、合规边界 +6. **[业务植入与 CTA部分]** — 植入位置与时机、植入载体与方式原型、内容与业务的衔接句、植入密度与占比、CTA 主目标、CTA 位置与句式、诱因与合规红线、避免 - 开头两段(**选题**、**标题与封面**)跨平台通用:任何生产都先解决「做什么」和「怎么命名、怎么呈现封面」。 - `[图组部分]` 与 `[正文表达部分]` 是两条并行的生产轨道:图组规则管视觉,正文规则管文字。 @@ -96,7 +98,7 @@ DNA template = **图文写作模板**,固定语义段如下(脚本 `build` / | 选题与包装 | `topic-angle` `title-cover` | | 内容创意 | `content-idea` | | 正文与视觉 | `body-voice` `imageset-visual` | -| 互动与转化 | `interaction-cta` | +| 业务植入与转化 | `biz-implant` `interaction-cta` | | 账号运营子模块(对标账号样本才有) | `account-bio` `content-mix-cadence` | focus 校验按作品类型执行:视频 report 不接受图文维度 ID,反之亦然。 diff --git a/crews/main/skills/expert-douyin/tools/douyin-style-profiler/references/video-dna-framework.md b/crews/main/skills/expert-douyin/tools/douyin-style-profiler/references/video-dna-framework.md index 360651c6..70949299 100644 --- a/crews/main/skills/expert-douyin/tools/douyin-style-profiler/references/video-dna-framework.md +++ b/crews/main/skills/expert-douyin/tools/douyin-style-profiler/references/video-dna-framework.md @@ -1,11 +1,11 @@ # 抖音视频作品 DNA 框架(v2) -> 状态:v2(2026-09-10)。Focus ID、维度命名与 template 语义以本文件为准;调整维度必须升版本,并同步 `scripts/build_style_profile.py` 与工具 `SKILL.md`。 +> 状态:v2(2026-09-10 修订:补业务植入套路 `biz-implant` 与互动引导 / CTA 套路 `interaction-cta` 两维)。Focus ID、维度命名与 template 语义以本文件为准;调整维度必须升版本,并同步 `scripts/build_style_profile.py` 与工具 `SKILL.md`。 > 抖音有两套框架:图文作品用 `note-dna-framework.md`,本文件是**视频作品**框架。 ## 定位与边界 -DNA 不是「平台级 DNA」,也不是账号人设说明书,而是**从一批作品样本中提取、聚合出的内容生产规则集**。生产方式与其他平台完全一致,仍是三层产物: +DNA 是**从一批作品样本中提取、聚合出的内容生产规则集**:它回答「这类作品怎么选、怎么包装、业务怎么植入、怎么引导行动」,不是账号人设说明书。生产方式与其他平台完全一致,仍是三层产物: ```text 单篇作品 -> DNA report(本框架的维度逐项提取) @@ -21,11 +21,11 @@ DNA 文档 -> DNA template | 进 DNA | 不进 DNA | |--------|----------| -| 选题与观看理由、标题与封面写法、内容创意原型、视频内容形态与制作指向、制作规格与视听倾向、(口播类)口播文案子DNA、(对标账号)账号运营子模块 | 创作细节、脚本结构、逐句台词、镜头表、转场与编码参数——那些归 Content Producer | +| 选题与观看理由、标题与封面写法、内容创意原型、业务植入套路、互动引导与 CTA 套路、视频内容形态与制作指向、制作规格与视听倾向、(口播类)口播文案子DNA、(对标账号)账号运营子模块 | 创作细节、脚本结构、逐句台词、镜头表、转场与编码参数——那些归 Content Producer | 视频 DNA 的用途是指导 main agent 出具 **Brief.md** 与(口播类的)**口播文案**。template 就是 Brief 正文模板 + 口播文案模板(可选),不是成片制作模板。账号运营子模块(简介写法、内容形式比例、发布习惯)只写进 DNA 文档,**不进 template**。 -## 维度(8 维) +## 维度(10 维) ### 一、选题与包装 @@ -47,18 +47,25 @@ DNA 文档 -> DNA template | 4 | `video-form` | 视频内容形态与制作指向 | 视频内容形态(口播 / 实拍拼接 / 影视解说+反转植入 / 纯 AIGC 动画 / 创意转场动效 / 录屏演示 / 图文卡片视频 / 混合)+ 判定依据 + **制作指向**(见下方映射表) | | 5 | `production-spec` | 制作规格与视听倾向 | 横屏或竖屏、时长带、画面风格(色调、质感、字幕样式倾向、信息密度)、配音音色与声音形态(原声口播 / TTS / 旁白 / 纯画面字幕)、BGM 与音效倾向、封面规格 | -### 四、口播文案子模块(可选,仅口播类启用) +### 四、业务植入与转化 | # | ID | 维度 | 观测内容 | |---|----|------|----------| -| 6 | `narration-script` | 口播文案子DNA | 起(从什么起步)、承(靠什么推进)、转(转折触发)、合(收束与 CTA)、人称与语气、句长与语速、签名式表达——用于指导 main agent 写同类型视频的口播文案 | +| 6 | `biz-implant` | 业务植入套路 | 是否有业务植入(纯内容 / 软植入 / 硬广直给)、植入位置与时机(前 3 秒 / 中段转折 / 结尾收束 / 反转点 / 评论区置顶 / 主页与组件)、植入载体(剧情道具、口播一句话、字幕卡片、场景背景、案例与数据、测评对象、购物车或留资组件、私信与主页引导)、植入方式原型(反转植入 / 痛点→方案 / 场景带入 / 实测对比 / 清单第 N 项 / 身份认同 / 口碑故事 / 教程内嵌 / 硬广直给)、内容与业务的衔接句(转折触发词原文)、植入密度与占比(时长占比、品牌词与产品名出现次数与方式);形态层面的「影视解说 + 反转植入」在 `video-form` 记形态与制作指向,本维度记植入怎么设计 | +| 7 | `interaction-cta` | 互动引导与 CTA 套路 | 行动目标(关注 / 评论关键词 / 私信 / 主页点击 / 搜索品牌词 / 购物车 / 直播预约 / 转发)与本篇主目标、CTA 出现位置与时机(口播收尾句 / 字幕卡 / 片尾贴片 / 描述区 / 评论区置顶)、CTA 句式与原文摘录(命令式 / 提问式 / 利益式 / 身份式 / 悬念式)、一篇放几个行动、诱因设计(利益点 / 情绪 / 身份认同 / 稀缺)、与业务转化目标的对应、平台组件挂载与合规边界 | -### 五、账号运营子模块(对标账号样本才有) +### 五、口播文案子模块(可选,仅口播类启用) | # | ID | 维度 | 观测内容 | |---|----|------|----------| -| 7 | `account-bio` | 账号简介写法 | 账号昵称、简介写法、主页与置顶表达、对外承诺(仅对标账号样本可得) | -| 8 | `content-mix-cadence` | 内容形式比例与发布习惯 | 图文 / 视频等内容形式比例、发布时间段与频率、内容形式混合节奏(如三篇图文对一篇视频)、栏目化节奏(仅对标账号批量样本可得) | +| 8 | `narration-script` | 口播文案子DNA | 起(从什么起步)、承(靠什么推进)、转(转折触发)、合(收束方式;CTA 的目标、位置与句式记在 `interaction-cta`)、人称与语气、句长与语速、签名式表达——用于指导 main agent 写同类型视频的口播文案 | + +### 六、账号运营子模块(对标账号样本才有) + +| # | ID | 维度 | 观测内容 | +|---|----|------|----------| +| 9 | `account-bio` | 账号简介写法 | 账号昵称、简介写法、主页与置顶表达、对外承诺(仅对标账号样本可得) | +| 10 | `content-mix-cadence` | 内容形式比例与发布习惯 | 图文 / 视频等内容形式比例、发布时间段与频率、内容形式混合节奏(如三篇图文对一篇视频)、栏目化节奏(仅对标账号批量样本可得) | ## Report 与聚合规则 @@ -68,7 +75,8 @@ DNA 文档 -> DNA template 4. 口播文案子模块只在口播类作品启用;非口播类或样本不足写「未启用 / 未观测」,不得把单篇句式上升为规则。 5. 账号运营子模块(`account-bio`、`content-mix-cadence`)只在样本来自对标账号批量提取时填写;用户只提供单篇时写「未观测」。 6. 脚本统计只作证据底座(句长、问句密度、人称密度、感叹号密度、口播密度),不生成总分、不判定风格是否合格。 -7. 高数据样本必须回读创意、形态与包装再归因,不得把高播放直接等同于风格好。 +7. 业务植入与 CTA 两个维度必须给出**位置 + 载体 + 原文摘录**三样证据;只写「自然植入」「引导关注」这类空泛结论不算提取完成。本篇没有业务植入时明确写「无植入(纯内容)」,不得留空;平台红线(虚假承诺、利益诱导互动、隐藏站外联系方式、谐音绕检测)一律记为必须避免项。 +8. 高数据样本必须回读创意、形态与包装再归因,不得把高播放直接等同于风格好。 ## Template 语义 @@ -77,9 +85,10 @@ DNA template = **Brief.md 正文模板 + 口播文案模板(可选)**,固 1. **[选题部分]** — 选题角度推荐、选题需考虑的受众关联角度、内容支柱与系列关系、避免 2. **[标题与封面部分]** — 标题类型、参考标题、封面或首帧风格、封面 AIGC 提示词要素、话题标签策略 3. **[内容创意部分]** — 创意原型、展开逻辑、记忆点与反转设计、触发条件、避免 -4. **[视频形态与制作指向部分]** — 视频内容形态、制作指向、委托边界、未指定形态时 -5. **[制作规格部分]** — 横屏或竖屏、时长带、画面风格、配音音色与声音形态、BGM 与音效、字幕 -6. **[口播文案部分]** — 是否启用、起、承、转、合、人称与语气、句长与语速、签名式表达、必须做、避免 +4. **[业务植入与 CTA部分]** — 植入位置与时机、植入载体与方式原型、内容与业务的衔接句、植入密度与占比、CTA 主目标、CTA 位置与句式、诱因与合规红线、避免 +5. **[视频形态与制作指向部分]** — 视频内容形态、制作指向、委托边界、未指定形态时 +6. **[制作规格部分]** — 横屏或竖屏、时长带、画面风格、配音音色与声音形态、BGM 与音效、字幕 +7. **[口播文案部分]** — 是否启用、起、承、转、合、人称与语气、句长与语速、签名式表达、必须做、避免 - 开头两段(**选题**、**标题与封面**)跨平台通用:任何生产都先解决「做什么」和「怎么命名、怎么呈现封面」。 - `[视频形态与制作指向部分]` 的「制作指向」必须写下方映射表里的真实资源名,Brief 的 `workflow` 字段据此填写。 @@ -111,6 +120,7 @@ Brief 写了 `workflow` 时 Content Producer 必须直接采用,不得替换 | 选题与包装 | `topic-angle` `title-cover` | | 内容创意 | `content-idea` | | 形态与规格 | `video-form` `production-spec` | +| 业务植入与转化 | `biz-implant` `interaction-cta` | | 口播文案子模块(可选,仅口播类启用) | `narration-script` | | 账号运营子模块(对标账号样本才有) | `account-bio` `content-mix-cadence` | diff --git a/crews/main/skills/expert-douyin/tools/douyin-style-profiler/scripts/build_style_profile.py b/crews/main/skills/expert-douyin/tools/douyin-style-profiler/scripts/build_style_profile.py index 7f1dc717..c026ed3f 100644 --- a/crews/main/skills/expert-douyin/tools/douyin-style-profiler/scripts/build_style_profile.py +++ b/crews/main/skills/expert-douyin/tools/douyin-style-profiler/scripts/build_style_profile.py @@ -10,16 +10,13 @@ 脚本只做 scaffold 与统计证据底座:不评分、不判定风格合格,定性结论由 Agent 回读原文补齐。 """ import argparse, json, math, re, shutil -from collections import Counter, defaultdict from datetime import datetime, timezone from pathlib import Path -from statistics import median SENTENCE_SPLIT = re.compile(r"[。!?!?]+") PARAGRAPH_SPLIT = re.compile(r"\n\s*\n") TOKEN_RE = re.compile(r"[一-鿿A-Za-z0-9_]+") -ENGLISH_WORD_RE = re.compile(r"[A-Za-z0-9_]+") SECOND_PERSON_RE = re.compile(r"你们?|you", re.IGNORECASE) FIRST_PERSON_RE = re.compile(r"我们?|I|we", re.IGNORECASE) QUESTION_RE = re.compile(r"[??]") @@ -71,6 +68,7 @@ "选题与包装": [("topic-angle", "选题与观看理由"), ("title-cover", "标题与封面")], "内容创意": [("content-idea", "内容创意")], "形态与规格": [("video-form", "视频内容形态与制作指向"), ("production-spec", "制作规格与视听倾向")], + "业务植入与转化": [("biz-implant", "业务植入套路"), ("interaction-cta", "互动引导与 CTA 套路")], "口播文案子模块(可选,仅口播类启用)": [("narration-script", "口播文案子DNA")], "账号运营子模块(对标账号样本才有)": [("account-bio", "账号简介写法"), ("content-mix-cadence", "内容形式比例与发布习惯")], }, @@ -78,7 +76,7 @@ "选题与包装": [("topic-angle", "选题与观看理由"), ("title-cover", "标题与封面图组")], "内容创意": [("content-idea", "内容创意")], "正文与视觉": [("body-voice", "正文表达与语气"), ("imageset-visual", "图组视觉风格")], - "互动与转化": [("interaction-cta", "互动引导与转化")], + "业务植入与转化": [("biz-implant", "业务植入套路"), ("interaction-cta", "互动引导与 CTA 套路")], "账号运营子模块(对标账号样本才有)": [("account-bio", "账号简介写法"), ("content-mix-cadence", "内容形式比例与发布习惯")], }, } @@ -108,10 +106,11 @@ "content-idea": "- 单篇观测:一句话创意内核、创意类型、展开逻辑(悬念 / 反转 / 递进 / 对比 / 清单 / 实测)、记忆点。\n- 可复用信号:这个创意套路换成别的主题还能怎么用。\n- 边界:只记创意层,不记创作细节(逐句台词、镜头表、脚本结构、转场与编码参数)。", "video-form": "- 单篇观测:视频内容形态(口播 / 实拍拼接 / 影视解说+反转植入 / 纯 AIGC 动画 / 创意转场动效 / 录屏演示 / 图文卡片视频 / 混合)与判定依据(画面证据、口播占比、素材来源)。\n- 制作指向:必须落到真实存在的资源名——Content Producer `expert-video` 的某个 workflow(Reversal Ad / Narration Video / Collage B-roll / 通用阶段链),或 main 的素材加工技能(`video-edit` / `talking-head-cut` / `ui-demo`);不得发明不存在的名字。", "production-spec": "- 单篇观测:横屏或竖屏、时长带、画面风格(色调、质感、字幕样式倾向、信息密度)、配音音色与声音形态(原声口播 / TTS / 旁白 / 纯画面字幕)、BGM 与音效倾向、封面规格。\n- 边界:只记规格与倾向,不规定镜头参数、逐镜设计、转场与编码细节——那些归 Content Producer。", - "narration-script": "- 子模块(仅口播类作品启用):起(从什么起步)、承(靠什么推进)、转(转折触发)、合(收束与 CTA),以及人称与语气、句长与语速、签名式表达。\n- 边界:非口播类或证据不足时写「未启用 / 未观测」;不得把单篇句式直接上升为规则。", + "narration-script": "- 子模块(仅口播类作品启用):起(从什么起步)、承(靠什么推进)、转(转折触发)、合(收束方式;CTA 的目标、位置与句式记在 `interaction-cta`),以及人称与语气、句长与语速、签名式表达。\n- 边界:非口播类或证据不足时写「未启用 / 未观测」;不得把单篇句式直接上升为规则。", "body-voice": "- 单篇观测:开头钩子(原文摘录)、正文组织方式(清单体 / 教程步骤 / 故事线 / 对比 / 观点输出)、分行与段落节奏、口语化程度与人称、emoji 与标点用法、签名式表达。\n- 证据边界:脚本统计只给句长 / 行数 / emoji / 标签等线索;口头禅与签名表达必须回读原文确认。", "imageset-visual": "- 单篇观测:图片数量与顺序、构图类型(产品展示 / 场景 / 文字卡片 / 对比图 / 过程图)、图文信息分工、色调与质感、版式一致性、文字视觉。\n- 视觉证据:必须由视觉模型读取本地图片并反推 AIGC 复现要素;无图片写「未提供」,不得凭正文想象补齐。", - "interaction-cta": "- 单篇观测:评论 / 收藏 / 关注 / 进店 / 咨询等平台内行动的引导方式与位置、每篇行动引导数量、话题标签承载的意图。\n- 合规边界:不隐藏站外联系方式、不绕平台检测、不以利益换互动,记为必须避免项。", + "biz-implant": "- 单篇观测:是否有业务植入(纯内容 / 软植入 / 硬广直给)、植入位置与时机、植入载体(剧情道具 / 口播一句话 / 字幕卡片 / 场景背景 / 案例与数据 / 清单第 N 项 / 教程步骤内嵌 / 产品截图 / 购物车或留资组件 / 主页与私信引导)、植入方式原型(反转植入 / 痛点→方案 / 场景带入 / 实测对比 / 身份认同 / 口碑故事 / 教程内嵌 / 硬广直给)、内容与业务的衔接句(原文摘录)、植入密度与占比、品牌词与产品名出现方式与频次。\n- 证据要求:位置 + 载体 + 原文摘录三样齐全;本篇无植入时写「无植入(纯内容)」,不得留空。\n- 边界:只记植入套路,不写逐句广告文案;形态层面的「影视解说 + 反转植入」由 `video-form` 记形态与制作指向。", + "interaction-cta": "- 单篇观测:行动目标(关注 / 评论 / 收藏 / 转发 / 私信 / 主页点击 / 进店 / 咨询 / 搜索品牌词 / 购物车 / 直播预约)与本篇主目标、CTA 出现位置与时机(口播收尾句 / 字幕卡 / 片尾贴片 / 描述区 / 正文结尾 / 图组末图 / 评论区)、CTA 句式与原文摘录(命令式 / 提问式 / 利益式 / 身份式 / 悬念式)、一篇放几个行动、诱因设计(利益点 / 情绪 / 身份认同 / 稀缺)、与业务转化目标的对应。\n- 合规边界:不隐藏站外联系方式、不绕平台检测、不以利益换互动,记为必须避免项。", "account-bio": "- 子模块(仅对标账号样本可得):账号昵称、简介写法、主页与置顶表达、对外承诺。\n- 边界:用户提供的单篇样本无法观测时写「未观测」,不得推导。", "content-mix-cadence": "- 子模块(仅对标账号批量样本可得):图文 / 视频等内容形式比例、发布时间段与频率、内容形式混合节奏(如三篇图文对一篇视频)、栏目化节奏。\n- 边界:必须由账号发布列表的批量样本推导;单篇样本只记本篇发布时间。", } @@ -122,8 +121,8 @@ } TEMPLATE_STAGES = { - "video": ("选题", "标题与封面", "内容创意", "视频形态与制作指向", "制作规格", "口播文案"), - "note": ("选题", "标题与封面", "内容创意与结构", "正文表达", "图组", "互动与标签"), + "video": ("选题", "标题与封面", "内容创意", "业务植入与 CTA", "视频形态与制作指向", "制作规格", "口播文案"), + "note": ("选题", "标题与封面", "内容创意与结构", "正文表达", "图组", "业务植入与 CTA"), } TEMPLATE_STAGE_FIELDS = { @@ -133,10 +132,10 @@ "视频形态与制作指向": ("视频内容形态", "制作指向", "委托边界", "未指定形态时"), "制作规格": ("横屏或竖屏", "时长带", "画面风格", "配音音色与声音形态", "BGM 与音效", "字幕"), "口播文案": ("是否启用", "起", "承", "转", "合", "人称与语气", "句长与语速", "签名式表达", "必须做", "避免"), + "业务植入与 CTA": ("植入位置与时机", "植入载体与方式原型", "内容与业务的衔接句", "植入密度与占比", "CTA 主目标", "CTA 位置与句式", "诱因与合规红线", "避免"), "内容创意与结构": ("创意原型", "正文组织方式", "信息密度", "记忆点", "避免"), "正文表达": ("开头钩子", "推进方式", "段落与分行节奏", "人称与语气", "emoji 与标点", "签名式表达", "必须做", "避免"), "图组": ("图片数量与顺序", "构图类型", "色调与质感", "版式一致性", "文字视觉", "AIGC 提示词要素"), - "互动与标签": ("互动目标", "引导方式", "话题标签策略", "合规边界"), } TEMPLATE_INTROS = { @@ -145,8 +144,8 @@ } TEMPLATE_CHECKLISTS = { - "video": "- 是否只用一个 DNA,且作品类型与该 DNA 的 `kind` 一致。\n- 选题、标题 / 描述、封面是否来自 DNA 文档。\n- 内容创意是否落到可复用的创意原型,而不是照抄样本主题。\n- 视频形态是否明确指向 Content Producer `expert-video` 的某个 workflow,或 main 的某个素材加工技能。\n- Brief 是否只含制作所需信息(不含 DNA 内容),素材是否给了绝对路径与授权说明。\n- 口播类是否附口播文案(或真人口播录音路径);口播子模块未启用时是否避免规定逐句口播。\n- 制作规格(横竖屏、时长带、画面风格、配音音色)是否尊重样本覆盖度;样本不足时是否标注未观测。\n- 用户输入是否已转译为具体执行规则。", - "note": "- 是否只用一个 DNA,且作品类型与该 DNA 的 `kind` 一致。\n- 选题、标题与封面图组是否来自 DNA 文档。\n- 正文表达的每条规则是否可从 DNA 文档推导,未使用空泛形容词。\n- 图组数量、构图与视觉风格是否与 DNA 一致;视觉结论是否有图片证据。\n- 互动引导是否每篇只放一个行动,且不越合规红线。\n- 用户输入是否已转译为具体执行规则。", + "video": "- 是否只用一个 DNA,且作品类型与该 DNA 的 `kind` 一致。\n- 选题、标题 / 描述、封面是否来自 DNA 文档。\n- 内容创意是否落到可复用的创意原型,而不是照抄样本主题。\n- 视频形态是否明确指向 Content Producer `expert-video` 的某个 workflow,或 main 的某个素材加工技能。\n- Brief 是否只含制作所需信息(不含 DNA 内容),素材是否给了绝对路径与授权说明。\n- 口播类是否附口播文案(或真人口播录音路径);口播子模块未启用时是否避免规定逐句口播。\n- 制作规格(横竖屏、时长带、画面风格、配音音色)是否尊重样本覆盖度;样本不足时是否标注未观测。\n- 业务植入是否落到位置 + 载体 + 衔接句(而不是只写「自然植入」),CTA 是否只有一个主行动、句式可执行且未越合规红线。\n- 用户输入是否已转译为具体执行规则。", + "note": "- 是否只用一个 DNA,且作品类型与该 DNA 的 `kind` 一致。\n- 选题、标题与封面图组是否来自 DNA 文档。\n- 正文表达的每条规则是否可从 DNA 文档推导,未使用空泛形容词。\n- 图组数量、构图与视觉风格是否与 DNA 一致;视觉结论是否有图片证据。\n- 业务植入是否落到位置 + 载体 + 衔接句(而不是只写「软性推荐」),CTA 是否每篇只放一个主行动、句式可执行且未越合规红线。\n- 用户输入是否已转译为具体执行规则。", } DNA_SUBMODULE_NOTE = "- **口播文案子模块**(`narration-script`):仅口播类视频启用,用于指导 main agent 写同类型视频的口播文案;它不是独立 DNA,未启用时写「未启用」。\n- **账号运营子模块**(`account-bio`、`content-mix-cadence`):只在样本来自用户提供的对标账号(可从账号发布列表批量提取)时填写;结论只写进本 DNA 文档,不进 template;样本不足写「未观测」。" @@ -182,12 +181,6 @@ "tag_count": "话题标签数", } -STOP_TERMS = { - "一个", "我们", "你们", "这里", "不会", "这个", "那个", "什么", "可以", "因为", - "但是", "所以", "还是", "以及", "如果", "他们", "自己", "的时候", "to", "the", - "a", "an", "is", "are", "and", "or", "of", "in", "for", "on", "with", "you", "we", -} - def dimensions_for(kind: str) -> list[dict]: """把某个作品类型的分组配置摊平成带序号的维度列表。""" @@ -229,24 +222,6 @@ def average(values: list[float]) -> float: return rounded(sum(values) / len(values)) if values else 0.0 -def extract_terms(text: str) -> Counter: - terms: Counter = Counter() - for token in tokenize(text): - if re.fullmatch(r"[A-Za-z0-9_]+", token): - term = token.lower() - if term not in STOP_TERMS and len(term) > 1: - terms[term] += 1 - continue - cleaned = "".join(ENGLISH_WORD_RE.sub("", token).split()) - if len(cleaned) == 1: - continue - for start in range(len(cleaned) - 1): - term = cleaned[start : start + 2] - if term not in STOP_TERMS: - terms[term] += 1 - return terms - - def split_title_body(text: str) -> tuple[str, str]: """样本文件约定:首个一级标题行为作品标题,其余为正文(图文正文可含内联 #话题)。""" title = "" @@ -300,7 +275,6 @@ def document_metrics(path: Path, text: str, duration: float = 0.0) -> dict: "emoji_count": emoji_count, "emoji_density_per_100_characters": safe_ratio(emoji_count, character_count, 100), "tag_count": len(TAG_RE.findall(body)), - "terms": dict(extract_terms(text)), } @@ -389,46 +363,12 @@ def build_statistics(reports: list[dict], kind: str) -> dict: "max": rounded(max(value for value, _ in pairs)) if pairs else 0.0, } - term_weights: dict[str, float] = defaultdict(float) - term_counts: dict[str, list[int]] = defaultdict(list) - for report in reports: - for term, count in report["document"]["terms"].items(): - term_weights[term] += report["weight"] - term_counts[term].append(count) - stable_terms = [] - for term, coverage_weight in term_weights.items(): - stable_terms.append( - { - "term": term, - "weighted_coverage": rounded(coverage_weight / total_weight), - "report_count": len(term_counts[term]), - "median_count_per_report": rounded(median(term_counts[term])), - } - ) - stable_terms.sort( - key=lambda item: (item["weighted_coverage"], item["report_count"], item["term"]), - reverse=True, - ) - weights = [report["weight"] for report in reports] return { "report_count": len(reports), "total_weight": total_weight, "weighting": "user-specified" if any(abs(weight - 1) > 1e-9 for weight in weights) else "uniform", "numeric_metrics": numeric_metrics, - "stable_terms": stable_terms[:30], - "weighted_coverage": sorted( - ( - { - "report_id": report["report_id"], - "weight": report["weight"], - "focus": report["focus"], - } - for report in reports - ), - key=lambda item: item["weight"], - reverse=True, - ), } diff --git a/crews/main/skills/expert-douyin/workflows/account-benchmark.md b/crews/main/skills/expert-douyin/workflows/account-benchmark.md index fc3d5815..885e1bfb 100644 --- a/crews/main/skills/expert-douyin/workflows/account-benchmark.md +++ b/crews/main/skills/expert-douyin/workflows/account-benchmark.md @@ -73,13 +73,13 @@ douyin/dna/{benchmark-dna-id}/{benchmark-dna-id}.dna.md douyin/dna/{benchmark-dna-id}/{benchmark-dna-id}.template.md ``` -对标 DNA template 用语义段与目标 DNA 一致:视频 = 选题、标题与封面、内容创意、视频形态与制作指向、制作规格、口播文案(可选);图文 = 选题、标题与封面、内容创意与结构、正文表达、图组、互动与标签。每一部分都要能从对标 DNA 文档推导;账号运营子模块不进 template。 +对标 DNA template 用语义段与目标 DNA 一致:视频 = 选题、标题与封面、内容创意、业务植入与 CTA、视频形态与制作指向、制作规格、口播文案(可选);图文 = 选题、标题与封面、内容创意与结构、正文表达、图组、业务植入与 CTA。每一部分都要能从对标 DNA 文档推导;账号运营子模块不进 template。 ### Step 3 - 模式分析与差异化(agent 推理) 基于对标 DNA 与样本数据,回答三个问题(不下没有证据的结论): -1. **它为什么有效**:对标账号的高表现内容在选题、标题包装、内容形式、发布节奏、高数据创意与互动设计上有什么共性?哪些信号在多条视频中稳定出现? +1. **它为什么有效**:对标账号的高表现内容在选题、标题包装、内容形式、业务植入与 CTA、发布节奏、高数据创意与互动设计上有什么共性?哪些信号在多条视频中稳定出现? 2. **相对表现**:同一账号内部,哪类内容明显高于其他条(用用户提供的互动数据判断;无数据时只做内容面分析,不编数据)。 3. **差异化切入点**:我们的账号比对标强在哪、弱在哪?有哪些内容空白或人群空白可以切入?每个切入点说明依据和建议的验证方式(一条视频验证一个变量)。 @@ -100,7 +100,7 @@ douyin/dna/{base-dna-id}/{base-dna-id}.template.md 比较必须覆盖两层: -1. **DNA 文档**:逐个比较选题与观看理由、标题与封面、内容创意、视频形态与制作指向、制作规格、口播文案子模块,以及账号运营子模块(简介写法、内容形式比例、发布习惯)。 +1. **DNA 文档**:逐个比较选题与观看理由、标题与封面、内容创意、业务植入套路、互动引导与 CTA 套路、视频形态与制作指向、制作规格、口播文案子模块,以及账号运营子模块(简介写法、内容形式比例、发布习惯)。 2. **template 语义段**:按作品类型逐项比较(视频看 Brief 相关段,图文看写作段)。 每个维度和模板语义段都输出四类结论: diff --git a/crews/main/skills/expert-douyin/workflows/content-production.md b/crews/main/skills/expert-douyin/workflows/content-production.md index 339aaac3..4b1e790e 100644 --- a/crews/main/skills/expert-douyin/workflows/content-production.md +++ b/crews/main/skills/expert-douyin/workflows/content-production.md @@ -56,7 +56,7 @@ douyin/dna/{dna-id}/{dna-id}.template.md DNA template 是 main agent 的生产输入模板: -- **视频 DNA 的 template = Brief 正文模板 +(可选)口播文案模板**,覆盖选题与观看理由、标题与封面、内容创意、视频形态与制作指向、制作规格、口播文案(如果是口播类视频创作),它不规定创作细节:逐句台词、镜头表、转场或编码参数,这些归 Content Producer决定; +- **视频 DNA 的 template = Brief 正文模板 +(可选)口播文案模板**,覆盖选题与观看理由、标题与封面、内容创意、业务植入与 CTA、视频形态与制作指向、制作规格、口播文案(如果是口播类视频创作),它不规定创作细节:逐句台词、镜头表、转场或编码参数,这些归 Content Producer决定; - **图文 DNA 的 template = 图文写作模板**。 ### 2. 读取业务知识 @@ -94,7 +94,7 @@ DNA template 是 main agent 的生产输入模板: > Agent 的一般内容判断 ``` -DNA 约束的是选题与观看理由、标题与封面写法、内容创意原型、视频形态与制作指向、制作规格;口播文案子模块只在明确启用时约束口播;账号运营子模块(简介写法、内容形式比例、发布习惯)只用于起号、对标与发布节奏决策,不进 Brief。DNA 不能覆盖用户指定素材、事实、合规边界和转化要求。 +DNA 约束的是选题与观看理由、标题与封面写法、内容创意原型、业务植入套路、互动引导与 CTA 套路、视频形态与制作指向、制作规格;口播文案子模块只在明确启用时约束口播;账号运营子模块(简介写法、内容形式比例、发布习惯)只用于起号、对标与发布节奏决策,不进 Brief。DNA 不能覆盖用户指定素材、事实、合规边界和转化要求。 ## Step 2 - 素材获取与整理 @@ -164,6 +164,7 @@ DNA 约束的是选题与观看理由、标题与封面写法、内容创意原 - 标题硬限制:不超过 30 字(抖音创作者平台上限)。 - 简介提及产品或业务,但不放明显引流信息;禁止二维码、联系方式;可引导用户主动搜索或点头像看主页。 - 话题标签按 DNA template 的策略组织:主标签 + 场景词 + 痛点词,不堆砌。 +- 业务植入与 CTA 按 DNA 的 `biz-implant` / `interaction-cta` 落位:植入位置、载体与衔接句写清楚,一条只放一个主行动,不堆叠 CTA。 ## 【确认】标题与文案 @@ -197,6 +198,7 @@ DNA 约束的是选题与观看理由、标题与封面写法、内容创意原 - 选题与观看理由: - 核心传达: - 内容创意:创意原型 + 展开逻辑 + 记忆点(+ 反转设计,如为反转植入类) +- 业务植入与 CTA:植入位置与方式原型 + 内容与业务的衔接句要求 + CTA 主目标与句式(只写要求,不写 DNA 规则原文) - 标题与简介:发布标题、简介文案、话题标签(main 定稿) - 封面要求:封面主文案 + 视觉方向 - 制作规格:横屏 / 竖屏、时长带、画面风格、配音音色与声音形态、BGM 与音效、字幕 diff --git a/crews/main/skills/expert-douyin/workflows/review.md b/crews/main/skills/expert-douyin/workflows/review.md index ca857c64..a5043f29 100644 --- a/crews/main/skills/expert-douyin/workflows/review.md +++ b/crews/main/skills/expert-douyin/workflows/review.md @@ -44,10 +44,12 @@ | 评论低 | 内容创意(讨论点)、口播文案 | content-idea、narration-script | | 分享低 | 选题、内容创意 | topic-angle、content-idea | | 收藏低 | 内容创意、制作规格 | content-idea、production-spec | -| 关注转化低 | 账号运营子模块、内容创意 | account-bio、content-mix-cadence、content-idea | +| 关注转化低 | 账号运营子模块、业务植入与 CTA | account-bio、content-mix-cadence、interaction-cta | +| 组件点击 / 私信 / 成交转化低 | 业务植入与 CTA | biz-implant、interaction-cta | +| 广告感重(负面评论、掉粉、植入点完播跳水) | 业务植入与 CTA、选题 | biz-implant、interaction-cta、topic-angle | | 画面/时长/音色被吐槽 | 制作规格、视频形态与制作指向 | production-spec、video-form | -图文作品(`dna-*-note`)改按图文 template 语义段归因:选题、标题与封面、内容创意与结构、正文表达、图组、互动与标签,对应维度 `topic-angle`、`title-cover`、`content-idea`、`body-voice`、`imageset-visual`、`interaction-cta`。 +图文作品(`dna-*-note`)改按图文 template 语义段归因:选题、标题与封面、内容创意与结构、正文表达、图组、业务植入与 CTA,对应维度 `topic-angle`、`title-cover`、`content-idea`、`body-voice`、`imageset-visual`、`biz-implant`、`interaction-cta`。 ### 平台混杂因素(归因前必排) diff --git a/crews/main/skills/expert-douyin/workflows/style-dna.md b/crews/main/skills/expert-douyin/workflows/style-dna.md index ccca1c66..99dbf19a 100644 --- a/crews/main/skills/expert-douyin/workflows/style-dna.md +++ b/crews/main/skills/expert-douyin/workflows/style-dna.md @@ -161,7 +161,7 @@ douyin-style-profiler update \ ## 对标接口 -对标样本进入独立 `dna-id`(同样按作品类型分流);比较时输出选题、标题与封面、内容创意、视频形态与制作指向、制作规格的差异,以及(账号批量样本才有的)账号运营子模块差异:简介写法、内容形式比例、发布习惯。用户明确采纳后才通过局部融合写进目标 DNA。 +对标样本进入独立 `dna-id`(同样按作品类型分流);比较时输出选题、标题与封面、内容创意、业务植入与 CTA、视频形态与制作指向、制作规格的差异,以及(账号批量样本才有的)账号运营子模块差异:简介写法、内容形式比例、发布习惯。用户明确采纳后才通过局部融合写进目标 DNA。 ## 编排原则 diff --git a/crews/main/skills/expert-wx-channel/SKILL.md b/crews/main/skills/expert-wx-channel/SKILL.md index 73992e35..a5142099 100644 --- a/crews/main/skills/expert-wx-channel/SKILL.md +++ b/crews/main/skills/expert-wx-channel/SKILL.md @@ -62,7 +62,7 @@ metadata: DNA 存储目录是 `wx_channel/dna/`。未指定 DNA 时默认使用并更新 `dna-0`。生产前同时读取 DNA 文档与 DNA template;对标分析先建立独立对标 DNA,不默认写入 `dna-0`。 -DNA 是**从一批作品样本提取并聚合出的内容生产规则集**:8 维——选题与观看理由、短标题与视频描述与封面、内容创意、视频内容形态与制作指向、制作规格与视听倾向,加可选的口播文案子模块与账号运营子模块(简介写法、发布习惯)。它指导 main agent 出 Brief(+ 口播文案)与发布文案,不规定创作细节与成片制作。维度框架 v2 位于 `wx-channel-style-profiler` 的 `references/video-dna-framework.md`。 +DNA 是**从一批作品样本提取并聚合出的内容生产规则集**:10 维——选题与观看理由、短标题与视频描述与封面、内容创意、**业务植入套路**、**互动引导与 CTA 套路**、视频内容形态与制作指向、制作规格与视听倾向,加可选的口播文案子模块与账号运营子模块(简介写法、发布习惯)。它指导 main agent 出 Brief(+ 口播文案)与发布文案,不规定创作细节与成片制作。维度框架 v2 位于 `wx-channel-style-profiler` 的 `references/video-dna-framework.md`。 ## 数据与记录 diff --git a/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/SKILL.md b/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/SKILL.md index cb759ef3..063ac190 100644 --- a/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/SKILL.md +++ b/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/SKILL.md @@ -1,6 +1,6 @@ --- name: wx-channel-style-profiler -description: 提取微信视频号作品 DNA:单条视频生成 report,按 dna-id 聚合选题、短标题与视频描述、内容创意、视频形态与制作指向、制作规格、口播文案子模块与账号运营子模块,推导 main agent 的视频 Brief template。 +description: 提取微信视频号作品 DNA:单条视频生成 report,按 dna-id 聚合选题、短标题与视频描述、内容创意、业务植入套路、互动引导与 CTA 套路、视频形态与制作指向、制作规格、口播文案子模块与账号运营子模块,推导 main agent 的视频 Brief template。 metadata: openclaw: emoji: 🧬 @@ -24,9 +24,9 @@ DNA 的用途是指导 main agent 选题、包装、出内容或出视频制作 DNA 文档 -> DNA template ``` -- **DNA report**:单篇作品的样本观测 + 维度提取结果(不是账号级结论,也不是模板)。 +- **DNA report**:单篇作品的样本观测 + 维度提取结果;跨篇共性与生产规则由聚合阶段给出,report 本身不是模板。 - **DNA 文档**:聚合后的生产规则、样本覆盖度、子模块结论与用户输入转译区。 -- **DNA template**:main agent 的生产输入模板(视频 = Brief 正文 + 口播文案;图文 = 写作模板)。 +- **DNA template**:main agent 的生产输入模板(Brief 正文 + 可选口播文案)。 ## 存储结构 @@ -46,11 +46,11 @@ wx_channel/dna/{dna-id}/ - **短标题与视频描述是两项独立内容**:发布页两项都可填,官方称填短标题能获得更多流量;但作品管理页不展示短标题,取数时只能拿到视频描述。report 里短标题拿不到就写「未观测(管理页不展示)」,不得把视频描述当短标题;两者都由 main agent 拟定,发布时都必须填。 - 单篇 report 只提供候选信号,不判断跨篇稳定性;共性、偏好、孤例由聚合阶段判断。 -- 统计只做证据底座,不评分、不判定风格是否合格。 +- 脚本统计(句长、问句与人称密度、感叹号密度、口播密度)只做证据底座,不评分、不判定风格是否合格;口头禅与签名式表达必须由 Agent 回读原文确认,不能凭统计直接下 DNA 结论。 - 视觉维度必须有图片 / 关键帧证据,由视觉模型读取;缺失写「未提供」,不得凭文本想象补齐。 - 口播文案子模块只在口播类作品启用;样本不足写「未启用」。 - 账号运营子模块(简介写法、内容形式比例、发布习惯)只在样本来自对标账号批量提取时填写,且**不进 template**。 -- 不输出合规结论、账号权重或风格评分。 +- 不输出风格评分或账号权重;合规只记「必须避免项」(虚假承诺、利益诱导互动、隐藏站外联系方式、谐音绕检测),不出具合规审查结论。 ## Report — 单篇提取 @@ -65,10 +65,10 @@ wx-channel-style-profiler report \ --output-dir wx_channel/dna/{dna-id}/reports ``` -- `--kind`:作品类型(见上);不传走默认。 +- `--kind`:只接受 `video`(即默认值),通常不用传。 - `--cover-image`:封面 / 首帧 / 首图本地文件,作为视觉证据(自动拷进 `covers/`)。 - `--source-url`:原作品链接;本地素材无链接时省略。 -- `--duration`:视频时长(秒),用于口播密度统计;图文忽略。 +- `--duration`:视频时长(秒),用于口播密度统计。 - `--weight`:样本权重,默认 1。 - `--focus`:限制该样本只影响指定维度 ID,可重复传入。 @@ -84,7 +84,6 @@ Agent 生成 scaffold 后必须: ```bash wx-channel-style-profiler build --dna-id {dna-id} - ``` 默认读取 `wx_channel/dna/{dna-id}/reports/`,输出 `{dna-id}.dna.md` 与 `{dna-id}.template.md`。也可显式传 report 文件 / 目录: @@ -122,11 +121,12 @@ wx-channel-style-profiler update \ 1. 选题 2. 短标题与视频描述 3. 内容创意 -4. 视频形态与制作指向 -5. 制作规格 -6. 口播文案 +4. 业务植入与 CTA +5. 视频形态与制作指向 +6. 制作规格 +7. 口播文案 -- 开头两段(**选题**、**标题与封面**)跨平台通用。 +- 开头两段(**选题**、**短标题与视频描述**)跨平台通用。 - 视频 template 的各段直接对应 Brief 正文字段;**Brief 不含 DNA 信息**(Content Producer 看不到 main 的 DNA),素材清单与授权、验收标准、闸门批准人按平台 Content Production Workflow 填。 - 账号运营子模块不进 template,只留在 DNA 文档。 @@ -154,8 +154,6 @@ narration-script:口播保持第三人称解说体,句长 10-15 字 ## Focus ID -### Focus ID - | ID | 维度 | |---|---| | `topic-angle` | 选题与观看理由 | @@ -163,15 +161,13 @@ narration-script:口播保持第三人称解说体,句长 10-15 字 | `content-idea` | 内容创意 | | `video-form` | 视频内容形态与制作指向 | | `production-spec` | 制作规格与视听倾向 | +| `biz-implant` | 业务植入套路 | +| `interaction-cta` | 互动引导与 CTA 套路 | | `narration-script` | 口播文案子DNA | | `account-bio` | 账号简介写法 | | `content-mix-cadence` | 内容形式比例与发布习惯 | -`--focus` 按作品类型校验:视频 report 不接受图文维度 ID,反之亦然。 - -## 统计与分词 - -脚本统计句长、问句与人称密度、感叹号密度、口播密度等指标作为聚合证据底座;中文高频信号使用相邻二字组合,仅作候选线索。口头禅与签名式表达必须由 Agent 回读原文确认,分词结果不能直接当 DNA 结论。 +`--focus` 只接受上表维度 ID;填入其他值直接报错。 ## 参考资料 diff --git a/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/references/video-dna-framework.md b/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/references/video-dna-framework.md index ff9adc50..b53429a0 100644 --- a/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/references/video-dna-framework.md +++ b/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/references/video-dna-framework.md @@ -1,11 +1,11 @@ # 微信视频号视频作品 DNA 框架(v2) -> 状态:v2(2026-09-10)。Focus ID、维度命名与 template 语义以本文件为准;调整维度必须升版本,并同步 `scripts/build_style_profile.py` 与工具 `SKILL.md`。 +> 状态:v2(2026-09-10 修订:补业务植入套路 `biz-implant` 与互动引导 / CTA 套路 `interaction-cta` 两维)。Focus ID、维度命名与 template 语义以本文件为准;调整维度必须升版本,并同步 `scripts/build_style_profile.py` 与工具 `SKILL.md`。 > 微信视频号只有视频作品,本文件即该平台的全部维度框架。 ## 定位与边界 -DNA 不是「平台级 DNA」,也不是账号人设说明书,而是**从一批作品样本中提取、聚合出的内容生产规则集**。生产方式与其他平台完全一致,仍是三层产物: +DNA 是**从一批作品样本中提取、聚合出的内容生产规则集**:它回答「这类作品怎么选、怎么包装、业务怎么植入、怎么引导行动」,不是账号人设说明书。生产方式与其他平台完全一致,仍是三层产物: ```text 单篇作品 -> DNA report(本框架的维度逐项提取) @@ -21,11 +21,11 @@ DNA 文档 -> DNA template | 进 DNA | 不进 DNA | |--------|----------| -| 选题与观看理由、标题与封面写法、内容创意原型、视频内容形态与制作指向、制作规格与视听倾向、(口播类)口播文案子DNA、(对标账号)账号运营子模块 | 创作细节、脚本结构、逐句台词、镜头表、转场与编码参数——那些归 Content Producer | +| 选题与观看理由、标题与封面写法、内容创意原型、业务植入套路、互动引导与 CTA 套路、视频内容形态与制作指向、制作规格与视听倾向、(口播类)口播文案子DNA、(对标账号)账号运营子模块 | 创作细节、脚本结构、逐句台词、镜头表、转场与编码参数——那些归 Content Producer | 视频 DNA 的用途是指导 main agent 出具 **Brief.md** 与(口播类的)**口播文案**。template 就是 Brief 正文模板 + 口播文案模板(可选),不是成片制作模板。账号运营子模块(简介写法、内容形式比例、发布习惯)只写进 DNA 文档,**不进 template**。 -## 维度(8 维) +## 维度(10 维) ### 一、选题与包装 @@ -47,18 +47,25 @@ DNA 文档 -> DNA template | 4 | `video-form` | 视频内容形态与制作指向 | 视频内容形态(口播 / 实拍拼接 / 影视解说+反转植入 / 纯 AIGC 动画 / 创意转场动效 / 录屏演示 / 图文卡片视频 / 混合)+ 判定依据 + **制作指向**(见下方映射表) | | 5 | `production-spec` | 制作规格与视听倾向 | 横屏或竖屏、时长带、画面风格(色调、质感、字幕样式倾向、信息密度)、配音音色与声音形态(原声口播 / TTS / 旁白 / 纯画面字幕)、BGM 与音效倾向、封面规格 | -### 四、口播文案子模块(可选,仅口播类启用) +### 四、业务植入与转化 | # | ID | 维度 | 观测内容 | |---|----|------|----------| -| 6 | `narration-script` | 口播文案子DNA | 起(从什么起步)、承(靠什么推进)、转(转折触发)、合(收束与 CTA)、人称与语气、句长与语速、签名式表达——用于指导 main agent 写同类型视频的口播文案 | +| 6 | `biz-implant` | 业务植入套路 | 是否有业务植入(纯内容 / 软植入 / 硬广直给)、植入位置与时机(前 3 秒 / 中段转折 / 结尾收束 / 反转点 / 评论区置顶 / 主页与组件)、植入载体(剧情道具、口播一句话、字幕卡片、场景背景、案例与数据、测评对象、购物车或留资组件、私信与主页引导)、植入方式原型(反转植入 / 痛点→方案 / 场景带入 / 实测对比 / 清单第 N 项 / 身份认同 / 口碑故事 / 教程内嵌 / 硬广直给)、内容与业务的衔接句(转折触发词原文)、植入密度与占比(时长占比、品牌词与产品名出现次数与方式);形态层面的「影视解说 + 反转植入」在 `video-form` 记形态与制作指向,本维度记植入怎么设计 | +| 7 | `interaction-cta` | 互动引导与 CTA 套路 | 行动目标(关注 / 评论关键词 / 私信 / 主页点击 / 搜索品牌词 / 购物车 / 直播预约 / 转发)与本篇主目标、CTA 出现位置与时机(口播收尾句 / 字幕卡 / 片尾贴片 / 描述区 / 评论区置顶)、CTA 句式与原文摘录(命令式 / 提问式 / 利益式 / 身份式 / 悬念式)、一篇放几个行动、诱因设计(利益点 / 情绪 / 身份认同 / 稀缺)、与业务转化目标的对应、平台组件挂载与合规边界 | -### 五、账号运营子模块(对标账号样本才有) +### 五、口播文案子模块(可选,仅口播类启用) | # | ID | 维度 | 观测内容 | |---|----|------|----------| -| 7 | `account-bio` | 账号简介写法 | 账号昵称、简介写法、主页与置顶表达、对外承诺(仅对标账号样本可得) | -| 8 | `content-mix-cadence` | 内容形式比例与发布习惯 | 图文 / 视频等内容形式比例、发布时间段与频率、内容形式混合节奏(如三篇图文对一篇视频)、栏目化节奏(仅对标账号批量样本可得) | +| 8 | `narration-script` | 口播文案子DNA | 起(从什么起步)、承(靠什么推进)、转(转折触发)、合(收束方式;CTA 的目标、位置与句式记在 `interaction-cta`)、人称与语气、句长与语速、签名式表达——用于指导 main agent 写同类型视频的口播文案 | + +### 六、账号运营子模块(对标账号样本才有) + +| # | ID | 维度 | 观测内容 | +|---|----|------|----------| +| 9 | `account-bio` | 账号简介写法 | 账号昵称、简介写法、主页与置顶表达、对外承诺(仅对标账号样本可得) | +| 10 | `content-mix-cadence` | 内容形式比例与发布习惯 | 图文 / 视频等内容形式比例、发布时间段与频率、内容形式混合节奏(如三篇图文对一篇视频)、栏目化节奏(仅对标账号批量样本可得) | ## Report 与聚合规则 @@ -68,7 +75,8 @@ DNA 文档 -> DNA template 4. 口播文案子模块只在口播类作品启用;非口播类或样本不足写「未启用 / 未观测」,不得把单篇句式上升为规则。 5. 账号运营子模块(`account-bio`、`content-mix-cadence`)只在样本来自对标账号批量提取时填写;用户只提供单篇时写「未观测」。 6. 脚本统计只作证据底座(句长、问句密度、人称密度、感叹号密度、口播密度),不生成总分、不判定风格是否合格。 -7. 高数据样本必须回读创意、形态与包装再归因,不得把高播放直接等同于风格好。 +7. 业务植入与 CTA 两个维度必须给出**位置 + 载体 + 原文摘录**三样证据;只写「自然植入」「引导关注」这类空泛结论不算提取完成。本篇没有业务植入时明确写「无植入(纯内容)」,不得留空;平台红线(虚假承诺、利益诱导互动、隐藏站外联系方式、谐音绕检测)一律记为必须避免项。 +8. 高数据样本必须回读创意、形态与包装再归因,不得把高播放直接等同于风格好。 ## Template 语义 @@ -77,9 +85,10 @@ DNA template = **Brief.md 正文模板 + 口播文案模板(可选)**,固 1. **[选题部分]** — 选题角度推荐、选题需考虑的受众关联角度、内容支柱与系列关系、避免 2. **[短标题与视频描述部分]** — 短标题模式、参考短标题、视频描述结构、参考视频描述、话题标签策略、封面风格与 AIGC 要素 3. **[内容创意部分]** — 创意原型、展开逻辑、记忆点与反转设计、触发条件、避免 -4. **[视频形态与制作指向部分]** — 视频内容形态、制作指向、委托边界、未指定形态时 -5. **[制作规格部分]** — 横屏或竖屏、时长带、画面风格、配音音色与声音形态、BGM 与音效、字幕 -6. **[口播文案部分]** — 是否启用、起、承、转、合、人称与语气、句长与语速、签名式表达、必须做、避免 +4. **[业务植入与 CTA部分]** — 植入位置与时机、植入载体与方式原型、内容与业务的衔接句、植入密度与占比、CTA 主目标、CTA 位置与句式、诱因与合规红线、避免 +5. **[视频形态与制作指向部分]** — 视频内容形态、制作指向、委托边界、未指定形态时 +6. **[制作规格部分]** — 横屏或竖屏、时长带、画面风格、配音音色与声音形态、BGM 与音效、字幕 +7. **[口播文案部分]** — 是否启用、起、承、转、合、人称与语气、句长与语速、签名式表达、必须做、避免 - 开头两段(**选题**、**标题与封面**)跨平台通用:任何生产都先解决「做什么」和「怎么命名、怎么呈现封面」。 - `[视频形态与制作指向部分]` 的「制作指向」必须写下方映射表里的真实资源名,Brief 的 `workflow` 字段据此填写。 @@ -111,6 +120,7 @@ Brief 写了 `workflow` 时 Content Producer 必须直接采用,不得替换 | 选题与包装 | `topic-angle` `title-cover` | | 内容创意 | `content-idea` | | 形态与规格 | `video-form` `production-spec` | +| 业务植入与转化 | `biz-implant` `interaction-cta` | | 口播文案子模块(可选,仅口播类启用) | `narration-script` | | 账号运营子模块(对标账号样本才有) | `account-bio` `content-mix-cadence` | diff --git a/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/scripts/build_style_profile.py b/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/scripts/build_style_profile.py index 1fcd2021..927d558f 100644 --- a/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/scripts/build_style_profile.py +++ b/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/scripts/build_style_profile.py @@ -10,16 +10,13 @@ 脚本只做 scaffold 与统计证据底座:不评分、不判定风格合格,定性结论由 Agent 回读原文补齐。 """ import argparse, json, math, re, shutil -from collections import Counter, defaultdict from datetime import datetime, timezone from pathlib import Path -from statistics import median SENTENCE_SPLIT = re.compile(r"[。!?!?]+") PARAGRAPH_SPLIT = re.compile(r"\n\s*\n") TOKEN_RE = re.compile(r"[一-鿿A-Za-z0-9_]+") -ENGLISH_WORD_RE = re.compile(r"[A-Za-z0-9_]+") SECOND_PERSON_RE = re.compile(r"你们?|you", re.IGNORECASE) FIRST_PERSON_RE = re.compile(r"我们?|I|we", re.IGNORECASE) QUESTION_RE = re.compile(r"[??]") @@ -71,6 +68,7 @@ "选题与包装": [("topic-angle", "选题与观看理由"), ("title-cover", "短标题、视频描述与封面")], "内容创意": [("content-idea", "内容创意")], "形态与规格": [("video-form", "视频内容形态与制作指向"), ("production-spec", "制作规格与视听倾向")], + "业务植入与转化": [("biz-implant", "业务植入套路"), ("interaction-cta", "互动引导与 CTA 套路")], "口播文案子模块(可选,仅口播类启用)": [("narration-script", "口播文案子DNA")], "账号运营子模块(对标账号样本才有)": [("account-bio", "账号简介写法"), ("content-mix-cadence", "内容形式比例与发布习惯")], }, @@ -92,7 +90,9 @@ "content-idea": "- 单篇观测:一句话创意内核、创意类型、展开逻辑(悬念 / 反转 / 递进 / 对比 / 清单 / 实测)、记忆点。\n- 可复用信号:这个创意套路换成别的主题还能怎么用。\n- 边界:只记创意层,不记创作细节(逐句台词、镜头表、脚本结构、转场与编码参数)。", "video-form": "- 单篇观测:视频内容形态(口播 / 实拍拼接 / 影视解说+反转植入 / 纯 AIGC 动画 / 创意转场动效 / 录屏演示 / 图文卡片视频 / 混合)与判定依据(画面证据、口播占比、素材来源)。\n- 制作指向:必须落到真实存在的资源名——Content Producer `expert-video` 的某个 workflow(Reversal Ad / Narration Video / Collage B-roll / 通用阶段链),或 main 的素材加工技能(`video-edit` / `talking-head-cut` / `ui-demo`);不得发明不存在的名字。", "production-spec": "- 单篇观测:横屏或竖屏、时长带、画面风格(色调、质感、字幕样式倾向、信息密度)、配音音色与声音形态(原声口播 / TTS / 旁白 / 纯画面字幕)、BGM 与音效倾向、封面规格。\n- 边界:只记规格与倾向,不规定镜头参数、逐镜设计、转场与编码细节——那些归 Content Producer。", - "narration-script": "- 子模块(仅口播类作品启用):起(从什么起步)、承(靠什么推进)、转(转折触发)、合(收束与 CTA),以及人称与语气、句长与语速、签名式表达。\n- 边界:非口播类或证据不足时写「未启用 / 未观测」;不得把单篇句式直接上升为规则。", + "biz-implant": "- 单篇观测:是否有业务植入(纯内容 / 软植入 / 硬广直给)、植入位置与时机、植入载体(剧情道具 / 口播一句话 / 字幕卡片 / 场景背景 / 案例与数据 / 清单第 N 项 / 教程步骤内嵌 / 产品截图 / 购物车或留资组件 / 主页与私信引导)、植入方式原型(反转植入 / 痛点→方案 / 场景带入 / 实测对比 / 身份认同 / 口碑故事 / 教程内嵌 / 硬广直给)、内容与业务的衔接句(原文摘录)、植入密度与占比、品牌词与产品名出现方式与频次。\n- 证据要求:位置 + 载体 + 原文摘录三样齐全;本篇无植入时写「无植入(纯内容)」,不得留空。\n- 边界:只记植入套路,不写逐句广告文案;形态层面的「影视解说 + 反转植入」由 `video-form` 记形态与制作指向。", + "interaction-cta": "- 单篇观测:行动目标(关注 / 评论 / 收藏 / 转发 / 私信 / 主页点击 / 进店 / 咨询 / 搜索品牌词 / 购物车 / 直播预约)与本篇主目标、CTA 出现位置与时机(口播收尾句 / 字幕卡 / 片尾贴片 / 描述区 / 正文结尾 / 图组末图 / 评论区)、CTA 句式与原文摘录(命令式 / 提问式 / 利益式 / 身份式 / 悬念式)、一篇放几个行动、诱因设计(利益点 / 情绪 / 身份认同 / 稀缺)、与业务转化目标的对应。\n- 合规边界:不隐藏站外联系方式、不绕平台检测、不以利益换互动,记为必须避免项。", + "narration-script": "- 子模块(仅口播类作品启用):起(从什么起步)、承(靠什么推进)、转(转折触发)、合(收束方式;CTA 的目标、位置与句式记在 `interaction-cta`),以及人称与语气、句长与语速、签名式表达。\n- 边界:非口播类或证据不足时写「未启用 / 未观测」;不得把单篇句式直接上升为规则。", "account-bio": "- 子模块(仅对标账号样本可得):账号昵称、简介写法、主页与置顶表达、对外承诺。\n- 边界:用户提供的单篇样本无法观测时写「未观测」,不得推导。", "content-mix-cadence": "- 子模块(仅对标账号批量样本可得):图文 / 视频等内容形式比例、发布时间段与频率、内容形式混合节奏(如三篇图文对一篇视频)、栏目化节奏。\n- 边界:必须由账号发布列表的批量样本推导;单篇样本只记本篇发布时间。", } @@ -102,7 +102,7 @@ } TEMPLATE_STAGES = { - "video": ("选题", "短标题与视频描述", "内容创意", "视频形态与制作指向", "制作规格", "口播文案"), + "video": ("选题", "短标题与视频描述", "内容创意", "业务植入与 CTA", "视频形态与制作指向", "制作规格", "口播文案"), } TEMPLATE_STAGE_FIELDS = { @@ -112,6 +112,7 @@ "视频形态与制作指向": ("视频内容形态", "制作指向", "委托边界", "未指定形态时"), "制作规格": ("横屏或竖屏", "时长带", "画面风格", "配音音色与声音形态", "BGM 与音效", "字幕"), "口播文案": ("是否启用", "起", "承", "转", "合", "人称与语气", "句长与语速", "签名式表达", "必须做", "避免"), + "业务植入与 CTA": ("植入位置与时机", "植入载体与方式原型", "内容与业务的衔接句", "植入密度与占比", "CTA 主目标", "CTA 位置与句式", "诱因与合规红线", "避免"), } TEMPLATE_INTROS = { @@ -119,7 +120,7 @@ } TEMPLATE_CHECKLISTS = { - "video": "- 是否只用一个 DNA,且作品类型与该 DNA 的 `kind` 一致。\n- 短标题与视频描述是否都已拟定(视频号两者都必须填),且写法来自 DNA 文档。\n- 内容创意是否落到可复用的创意原型,而不是照抄样本主题。\n- 视频形态是否明确指向 Content Producer `expert-video` 的某个 workflow,或 main 的某个素材加工技能。\n- Brief 是否只含制作所需信息(不含 DNA 内容),素材是否给了绝对路径与授权说明。\n- 口播类是否附口播文案(或真人口播录音路径);口播子模块未启用时是否避免规定逐句口播。\n- 制作规格(横竖屏、时长带、画面风格、配音音色)是否尊重样本覆盖度;样本不足时是否标注未观测。\n- 用户输入是否已转译为具体执行规则。", + "video": "- 是否只用一个 DNA,且作品类型与该 DNA 的 `kind` 一致。\n- 短标题与视频描述是否都已拟定(视频号两者都必须填),且写法来自 DNA 文档。\n- 内容创意是否落到可复用的创意原型,而不是照抄样本主题。\n- 视频形态是否明确指向 Content Producer `expert-video` 的某个 workflow,或 main 的某个素材加工技能。\n- Brief 是否只含制作所需信息(不含 DNA 内容),素材是否给了绝对路径与授权说明。\n- 口播类是否附口播文案(或真人口播录音路径);口播子模块未启用时是否避免规定逐句口播。\n- 制作规格(横竖屏、时长带、画面风格、配音音色)是否尊重样本覆盖度;样本不足时是否标注未观测。\n- 业务植入是否落到位置 + 载体 + 衔接句(而不是只写「自然植入」),CTA 是否只有一个主行动、句式可执行且未越合规红线。\n- 用户输入是否已转译为具体执行规则。", } DNA_SUBMODULE_NOTE = "- **口播文案子模块**(`narration-script`):仅口播类视频启用,用于指导 main agent 写同类型视频的口播文案;它不是独立 DNA,未启用时写「未启用」。\n- **账号运营子模块**(`account-bio`、`content-mix-cadence`):只在样本来自用户提供的对标账号(可从账号发布列表批量提取)时填写;结论只写进本 DNA 文档,不进 template;样本不足写「未观测」。" @@ -155,12 +156,6 @@ "tag_count": "话题标签数", } -STOP_TERMS = { - "一个", "我们", "你们", "这里", "不会", "这个", "那个", "什么", "可以", "因为", - "但是", "所以", "还是", "以及", "如果", "他们", "自己", "的时候", "to", "the", - "a", "an", "is", "are", "and", "or", "of", "in", "for", "on", "with", "you", "we", -} - def dimensions_for(kind: str) -> list[dict]: """把某个作品类型的分组配置摊平成带序号的维度列表。""" @@ -202,24 +197,6 @@ def average(values: list[float]) -> float: return rounded(sum(values) / len(values)) if values else 0.0 -def extract_terms(text: str) -> Counter: - terms: Counter = Counter() - for token in tokenize(text): - if re.fullmatch(r"[A-Za-z0-9_]+", token): - term = token.lower() - if term not in STOP_TERMS and len(term) > 1: - terms[term] += 1 - continue - cleaned = "".join(ENGLISH_WORD_RE.sub("", token).split()) - if len(cleaned) == 1: - continue - for start in range(len(cleaned) - 1): - term = cleaned[start : start + 2] - if term not in STOP_TERMS: - terms[term] += 1 - return terms - - def split_title_body(text: str) -> tuple[str, str]: """样本文件约定:首个一级标题行为作品标题,其余为正文(图文正文可含内联 #话题)。""" title = "" @@ -273,7 +250,6 @@ def document_metrics(path: Path, text: str, duration: float = 0.0) -> dict: "emoji_count": emoji_count, "emoji_density_per_100_characters": safe_ratio(emoji_count, character_count, 100), "tag_count": len(TAG_RE.findall(body)), - "terms": dict(extract_terms(text)), } @@ -362,46 +338,12 @@ def build_statistics(reports: list[dict], kind: str) -> dict: "max": rounded(max(value for value, _ in pairs)) if pairs else 0.0, } - term_weights: dict[str, float] = defaultdict(float) - term_counts: dict[str, list[int]] = defaultdict(list) - for report in reports: - for term, count in report["document"]["terms"].items(): - term_weights[term] += report["weight"] - term_counts[term].append(count) - stable_terms = [] - for term, coverage_weight in term_weights.items(): - stable_terms.append( - { - "term": term, - "weighted_coverage": rounded(coverage_weight / total_weight), - "report_count": len(term_counts[term]), - "median_count_per_report": rounded(median(term_counts[term])), - } - ) - stable_terms.sort( - key=lambda item: (item["weighted_coverage"], item["report_count"], item["term"]), - reverse=True, - ) - weights = [report["weight"] for report in reports] return { "report_count": len(reports), "total_weight": total_weight, "weighting": "user-specified" if any(abs(weight - 1) > 1e-9 for weight in weights) else "uniform", "numeric_metrics": numeric_metrics, - "stable_terms": stable_terms[:30], - "weighted_coverage": sorted( - ( - { - "report_id": report["report_id"], - "weight": report["weight"], - "focus": report["focus"], - } - for report in reports - ), - key=lambda item: item["weight"], - reverse=True, - ), } diff --git a/crews/main/skills/expert-wx-channel/workflows/account-benchmark.md b/crews/main/skills/expert-wx-channel/workflows/account-benchmark.md index f3d11130..b1ac9662 100644 --- a/crews/main/skills/expert-wx-channel/workflows/account-benchmark.md +++ b/crews/main/skills/expert-wx-channel/workflows/account-benchmark.md @@ -71,7 +71,7 @@ wx_channel/dna/{benchmark-dna-id}/{benchmark-dna-id}.dna.md wx_channel/dna/{benchmark-dna-id}/{benchmark-dna-id}.template.md ``` -对标 DNA template 用语义段与目标 DNA 一致:选题、短标题与视频描述、内容创意、视频形态与制作指向、制作规格、口播文案(可选)。每一部分都要能从对标 DNA 文档推导;账号运营子模块不进 template。 +对标 DNA template 用语义段与目标 DNA 一致:选题、短标题与视频描述、内容创意、业务植入与 CTA、视频形态与制作指向、制作规格、口播文案(可选)。每一部分都要能从对标 DNA 文档推导;账号运营子模块不进 template。 ### Step 3 - 选择比较基线 @@ -88,8 +88,8 @@ wx_channel/dna/{base-dna-id}/{base-dna-id}.template.md 比较必须覆盖两层: -1. **DNA 文档**:逐个比较选题与观看理由、短标题与视频描述、内容创意、视频形态与制作指向、制作规格、口播文案子模块,以及账号运营子模块(简介写法、发布习惯)。 -2. **template 语义段**:逐项比较选题、短标题与视频描述、内容创意、视频形态与制作指向、制作规格、口播文案。 +1. **DNA 文档**:逐个比较选题与观看理由、短标题与视频描述、内容创意、业务植入套路、互动引导与 CTA 套路、视频形态与制作指向、制作规格、口播文案子模块,以及账号运营子模块(简介写法、发布习惯)。 +2. **template 语义段**:逐项比较选题、短标题与视频描述、内容创意、业务植入与 CTA、视频形态与制作指向、制作规格、口播文案。 每个维度和模板语义段都输出四类结论: diff --git a/crews/main/skills/expert-wx-channel/workflows/content-production.md b/crews/main/skills/expert-wx-channel/workflows/content-production.md index 472243fd..c0e3e4fc 100644 --- a/crews/main/skills/expert-wx-channel/workflows/content-production.md +++ b/crews/main/skills/expert-wx-channel/workflows/content-production.md @@ -1,8 +1,8 @@ # 视频号内容生产 Workflow -从选题、Brief 到发布的完整视频运营流程。用户说“帮我做一条视频”“出几条视频”“这条的主题我们也做一条”走这个。 +从选题到发布的完整内容生产。用户说“帮我做条视频号视频”“发个视频号”“这条照着做一条”“这个选题我们也做一条”走这个。 -**分工硬边界**:main agent 负责选题策划、按 DNA 出 Brief、拟定短标题与视频描述、准备素材(含简单加工)、监督 CP 进程、成片后的发布与运营;视频全案的成片制作委托 `content-producer`。口播类视频的口播文案由 main agent 写好随 Brief 交付(真人口播时指导用户按口播稿录音,完成后向用户取得录音文件),其余声画制作归 CP。 +**视频作品分工硬边界**:main agent 负责选题策划、按 DNA 出 **Brief**、拟定短标题与视频描述、准备素材(用户素材预处理 / `ui-demo` 录屏 / 从 `campaign_assets/` 挑选,绝对路径写进 Brief)、监督并推动作为 subagent 的 `content-producer`、成片后的发布与运营;也直接做**已有视频素材的简单加工**(`video-edit` / `talking-head-cut`)。视频全案的成片制作一律委托 `content-producer`。本 workflow 的价值在于:用 DNA 锁定选题、包装、内容创意、业务植入与 CTA、视频形态与制作规格,编排输入分支与确认节点,衔接制作并把关发布记录。 ## Step 0 - 入口判断 @@ -12,19 +12,28 @@ | --- | --- | | 只有粗略想法或方向 | 想法模式 | | 参考视频(视频号 / 抖音 / B站 / 小红书链接,或文字稿 / 拆解报告) | 参考模式 | -| 已有脚本草稿(本地 `.md` / `.txt`、Word,或用户直接输入的一大段口播文案) | 草稿模式 | +| 已有素材(视频片段、图片、录音、产品演示) | 素材模式 | +| 已有脚本(用户写好或别的来源,含一大段口播文案) | 脚本模式 | -### 2. 参考视频的意图判断 +### 2. 识别制作路线 + +| 路线 | 判断 | 执行方 | +| --- | --- | --- | +| 素材组装 / 轻剪辑 | 用户手里有可用素材 | main 直接做:`video-edit` / `talking-head-cut` / `ui-demo` | +| 从零制作 | 没有素材,需要出脚本、拍摄 / 生成画面 | main 出制作简报,委托 `content-producer` | +| 脚本制作 | 用户已有脚本 | 按 DNA 对脚本做必要修改,提交用户确认后,脚本交 `content-producer` 制作 | + +### 3. 参考视频的意图判断 用户给参考视频时,先判断意图: -- **风格吸收**:“把这条的风格提取出来”“以后照着这个味道做”“融入到我们的风格里” -> 切换到 `style-dna.md` workflow,生成 report 并更新 DNA。 -- **内容参考**:“参考这条的主题做一条”“做一条同样内容的”“用我们的风格改写这条脚本” -> 继续本流程。 -- 意图不明时先问用户:是吸收风格,还是参考主题内容。 +- **风格吸收**:“把这条的风格提取出来”“以后照着这个味道做”“融入到我们的风格里” -> 切换到 `style-dna.md`,生成 report 并更新 DNA。 +- **内容参考**:“参考这条的主题做一条”“做一条同样内容的”“用我们的风格改写这条脚本” -> 继续本流程(参考模式)。 +- 意图不明时先问用户:是吸收风格,还是参考内容。 -### 3. 与改稿的边界 +### 4. 与改片的边界 -用户对一条已完成的脚本或成片(通常是我们此前产出的)做润色、换钩子、换风格、换结构、压时长,走 `editing.md`。本流程把用户输入当作素材,走完整生产链路产出新视频。 +用户对一条已完成的脚本或成片(通常是我们此前产出的)改文案、重新剪辑、换钩子、换封面、压时长,走 `editing.md`。本流程把用户输入当作素材,走完整生产链路产出新视频。 ## Step 1 - 生产契约锁定 @@ -35,7 +44,7 @@ 1. 用户明确指定 `dna-id` 时,只用该 DNA。 2. 用户没有指定时,用默认 `dna-0`。 3. 不得临场凭感觉拼一个风格。 -4. 若目标 DNA 不存在,先按 `account-setup.md` / `style-dna.md` 建立或更新 DNA;完成前不进入脚本创作。 +4. 若目标 DNA 不存在,先按 `account-setup.md` / `style-dna.md` 建立或更新 DNA;完成前不进入制作。 5. 用户意图是把参考视频的风格吸收进 DNA 时,先按 `style-dna.md` 处理,完成 DNA 更新后再回到本流程;仅参考主题内容时直接按本流程生产,不动 DNA。 写前必须读取两份文件: @@ -45,18 +54,18 @@ wx_channel/dna/{dna-id}/{dna-id}.dna.md wx_channel/dna/{dna-id}/{dna-id}.template.md ``` -DNA template = **Brief 正文模板 +(可选)口播文案模板**,覆盖选题与观看理由、短标题与视频描述、内容创意、视频形态与制作指向、制作规格、口播文案。它不规定创作细节:逐句台词、镜头表、转场或编码参数归 Content Producer。账号运营子模块(简介写法、发布习惯)只留在 DNA 文档,用于发布节奏决策。 +DNA template 是 main agent 的生产输入模板:**视频号 DNA 的 template = Brief 正文模板 +(可选)口播文案模板**,覆盖选题与观看理由、短标题与视频描述、内容创意、业务植入与 CTA、视频形态与制作指向、制作规格、口播文案(如果是口播类视频创作)。它不规定创作细节:逐句台词、镜头表、转场或编码参数,这些归 Content Producer 决定。 ### 2. 读取业务知识 -创作必须为 Workspace 根的 `business_knowledge.md` 服务。写前提取: +内容必须为 Workspace 根的 `business_knowledge.md` 服务。做前提取: - 产品 / 服务概括和核心差异 - 目标观众与具体痛点 - 收费项目、转化目标或下一步动作 - 红线、合规限制和品牌语气 -内容选题、案例、承诺、CTA 和商业表述必须与这些信息一致。关键信息缺失时先问用户,不虚构。 +内容选题、案例、承诺、业务植入与 CTA、商业表述必须与这些信息一致。关键信息缺失时先问用户,不虚构。 ### 3. 采集生产要求 @@ -64,13 +73,16 @@ DNA template = **Brief 正文模板 +(可选)口播文案模板**,覆盖 | 项目 | 规则 | | --- | --- | +| 制作路线 | 素材组装 / 从零制作 / 脚本制作,判断依据见 Step 0 | | 主题 / 方向 | 用户给了明确主题时不得另起炉灶,仅按 DNA template 细化选题、短标题与视频描述 | | 素材 | 用户提供的文案、笔记、截图、链接、数据、案例、实拍素材必须优先使用 | | 目标观众 | 未指定时按 `business_knowledge.md` 和 DNA 受众关系推导(视频号主力人群 30-55 岁,偏真实、有深度、有温度) | -| 时长 | 未指定时按 DNA template,再无要求默认 30-60 秒 | -| 形态 | 真人出镜 / 配音解说 / 素材剪辑 / AIGC,未指定按 DNA template 与账号人设推导;真人出镜占比建议 ≥ 60% | +| 时长 | 未指定时按 DNA template 的时长带;再无要求默认 30-60 秒 | +| 形态 | 真人出镜 / 配音解说 / 素材剪辑 / AIGC,未指定按 DNA template 与账号定位推导;真人出镜占比建议 ≥ 60% | | 出镜与拍摄 | 真人出镜时确认拍摄可行性;不可实拍时与用户确认改配音 / 素材 / AIGC 形态 | -| CTA | 用户指定 CTA 时优先执行;未指定时按业务目标与 DNA template 推导(关注 / 评论 / 转发 / 购物车 / 私信 / 直播预约选一个,不堆叠) | +| 业务植入 | 用户指定植入方式时优先执行;未指定时按 DNA 的 `biz-implant` 推导植入位置、载体与衔接句 | +| CTA | 用户指定 CTA 时优先执行;未指定时按业务目标与 DNA 的 `interaction-cta` 推导(关注 / 评论 / 转发 / 购物车 / 私信 / 直播预约选一个,不堆叠) | +| 封面 | 默认生成;用户自带封面时优先使用 | 优先级固定为: @@ -79,29 +91,29 @@ DNA template = **Brief 正文模板 +(可选)口播文案模板**,覆盖 > 用户提供的素材 > business_knowledge.md > DNA template 的风格规则 -> Agent 的一般创作判断 +> Agent 的一般内容判断 ``` -DNA 约束的是选题与观看理由、短标题与视频描述写法、内容创意原型、视频形态与制作指向、制作规格;口播文案子模块只在明确启用时约束口播;账号运营子模块只用于发布节奏决策,不进 Brief。DNA 不能覆盖用户指定的出镜人、事实、来源、CTA 和合规边界。 +DNA 约束的是选题与观看理由、短标题与视频描述写法、内容创意原型、业务植入套路、互动引导与 CTA 套路、视频形态与制作指向、制作规格;口播文案子模块只在明确启用时约束口播;账号运营子模块(简介写法、内容形式比例、发布习惯)只用于起号、对标与发布节奏决策,不进 Brief。DNA 不能覆盖用户指定的出镜人、素材、事实、来源、业务植入与 CTA 要求、合规边界。 ## Step 2 - 素材获取与整理 -先建立作品目录 `wx_channel/outputs//`(video-name 用主题的短 slug;选题尚未确定时可先用暂代名,选题确定后随之定名),下设 `materials/` 子目录,获取到的素材统一放入 `materials/`。 +先建立视频目录 `wx_channel/outputs//`(video-name 用主题的短 slug;选题尚未确定时可先用暂代名,选题确定后随之定名),下设 `materials/` 子目录,获取到的素材统一放入 `materials/`。 1. 按类型获取输入: - - 抖音 / B站 / 小红书视频链接 -> 调顶层 `viral-chaser` 下载、转写与拆解,拆解报告与逐字稿进 `materials/`。 + - 抖音 / B站 / 小红书视频链接 -> self-spawn subagent 走顶层 `viral-chaser` 下载、转写与拆解,拆解报告与逐字稿落入 `wx_channel/outputs//references/`(仿照制作时它就是参考素材)。 - 视频号链接 -> 无公开下载路径:请用户提供视频文件或文字稿;自己账号的作品可用 `wx-channel-engagement list` 补视频描述与数据线索。 - 其他链接 -> 用浏览器工具读取。 - - 本地 `.md` / `.txt` -> 直接读取;视频文件确认可读并记录格式(支持 `.mp4` / `.mov` / `.avi` / `.webm`)。 + - 本地视频 / 图片 / 音频素材 -> 复制进 `materials/`,视频确认可读并记录格式(支持 `.mp4` / `.mov` / `.avi` / `.webm`)。 - PDF / Word -> 先提取正文文本再进入流程。 - - 用户直接输入的大段文本 -> 直接作为草稿素材。 + - 用户文字输入(想法、脚本、要点、大段口播文案)-> 保存为 `.md` 放入 `materials/`。 2. 建立素材清单:用户原话、事实、数据、案例、实拍画面、图片、链接、可用观点、待确认信息。 3. 区分三类输入: - **必用素材**:用户明确要求采用的事实、观点、案例和画面物料。 - **参考素材**:提供方向或表达参考,不直接替代用户结论。 - **对标素材**:只用于模式比较,不能覆盖用户素材和业务事实。 -4. 素材不足时先补问;确实无法补充时,在脚本中避免无证据断言。素材之间冲突时列出冲突并问用户;商业事实以 `business_knowledge.md` 和用户最新说明为准。 -5. 用户意图是“保留内容、只换我们的风格”(同主题改写)时,整理**内容资产清单**而不是风格分析:主题、中心论点、事实、数据、时间、人物、案例、引用来源、需要保留的关键词、专有名词、免责声明。不能确认的信息标为「待核实」,禁止为了表达顺滑而补事实。 +4. 素材不足时先补问;确实无法补充时,在内容中避免无证据断言。素材之间冲突时列出冲突并问用户;商业事实以 `business_knowledge.md` 和用户最新说明为准。 +5. 用户意图是“保留内容、只换我们的表达方式”(同主题改写)时,整理**内容资产清单**而不是风格分析:主题、中心论点、事实、数据、时间、人物、案例、引用来源、需要保留的关键词与专有名词、免责声明。不能确认的信息标为「待核实」,禁止为了表达顺滑而补事实。 ## Step 3 - 选题 @@ -127,17 +139,17 @@ DNA 约束的是选题与观看理由、短标题与视频描述写法、内容 1. 使用了哪些用户素材。 2. 服务哪个业务目标。 -3. 符合 DNA template 的哪些选题与受众特征。 +3. 符合 DNA template 的哪些选题、钩子与受众特征。 4. 选题调研发现:相关主题在所选平台的讨论热度或用户痛点。 5. 转发动机:这个选题命中实用价值 / 情绪态度 / 社交人设中的哪一种(视频号破圈靠分享)。 -### 参考 / 草稿模式 +### 参考 / 素材 / 脚本模式 -从参考视频或草稿中提取选题方向,与 DNA template 的选题维度对比:选题已由参考视频或用户草稿确定时,可只做轻量调研(1 个平台、1 次查询)核实热度和痛点,不做全量调研: +从参考视频或用户输入中提取选题方向,与 DNA template 的选题维度对比:选题已由参考内容确定时,可只做轻量调研(1 个平台、1 次查询)核实热度和痛点,不做全量调研: - **相符** -> 直接采纳。 - **不相符** -> 明确指出差异,跟用户确认:综合调整以贴合 DNA,还是就用参考的选题。 -- 用户明确换了主题(“仿这条,主题换成 XX”)-> 用用户给的新主题,参考视频只提供结构参考。 +- 用户明确换了主题(“仿这条的结构,主题换成 XX”)-> 用用户给的新主题,参考视频只提供结构与钩子参考。 选题必须优先消耗用户素材,并服务于 `business_knowledge.md` 中的产品服务或转化目标。 @@ -154,8 +166,9 @@ DNA 约束的是选题与观看理由、短标题与视频描述写法、内容 1. **短标题**:按 DNA template 的「短标题模式」写,6-16 字,承担身份信号与价值预告;给 2-3 个候选。 2. **视频描述**(≤300 字,含 2-5 个话题标签,标签直接写在描述中):按 DNA template 的「视频描述结构」写;给 2-3 个候选,说明各自的观看理由与标签策略。 -3. 有参考视频或草稿时必须参考其文案内容(如有),但不得照抄。 +3. 有参考视频或用户文案时,必须参考其文案内容(如有),但不得照抄。 4. 用户已指定文案或原则时,候选必须在该约束内生成,不得偷换方向。 +5. 描述可提及产品或业务,但不放明显引流信息;禁止二维码、联系方式与谐音绕检测,按 DNA 的 `interaction-cta` 只保留一个主行动引导。 > **取数与入库只用视频描述**:作品管理页不展示短标题,`wx-channel-engagement` 抓取匹配与 `published-track record --title` 都用完整视频描述;**短标题不写库**,只记在作品目录的发布文案里,避免混淆。 @@ -163,11 +176,24 @@ DNA 约束的是选题与观看理由、短标题与视频描述写法、内容 第二个必停节点。务必与用户就短标题与完整视频描述一并确认。用户如果提出修改意见,按用户意见修改,直至与用户达成一致。 -## Step 5 - 制作 Brief +## Step 5 - 制作 + +按 Step 0 判定的路线执行。所有路线的成品最终落在 `wx_channel/outputs//`。 + +### 路线 A:素材组装 / 轻剪辑(main 直接做) + +只处理已有素材的简单加工,不升格为全案制作: -每条需要 CP 全案制作的视频,main agent 只产出 `wx_channel/outputs//brief.md`。Brief 是 main / CP 的唯一交接物。 +1. 按 DNA 的核心传达、业务植入与 CTA、内容形式整理剪辑顺序清单;若创作口播类视频,由 main 先写口播稿。 +2. 口播类素材去口气词、剪高光 -> `talking-head-cut`。 +3. 抽段拼接、加旁白 / BGM、烧字幕、片头片尾、编号合成 -> `video-edit`。 +4. 素材缺口经 AIGC 片段(`aigc-video-gen`)或免费素材库(`pexels-footage` / `pixabay-footage`)补充,补充素材在清单中标注来源。 +5. 产品操作演示 -> `ui-demo` 录制。 +6. 若需求超出“简单加工”(需要重写叙事、全新分镜、全案生成),停止自做,改走 Brief 委托路线。 -### Brief 必填字段 +### 路线 B / C:委托 content-producer 制作 + +1. 产出**制作简报** `wx_channel/outputs//brief.md`(Brief 是 main / CP 的唯一交接物): ```markdown # 视频号视频制作 Brief @@ -178,11 +204,11 @@ DNA 约束的是选题与观看理由、短标题与视频描述写法、内容 - 选题与观看理由: - 核心传达: - 内容创意:创意原型 + 展开逻辑 + 记忆点(+ 反转设计,如为反转植入类) +- 业务植入与 CTA:植入位置与方式原型 + 内容与业务的衔接句要求 + CTA 主目标与句式 - 封面要求:封面主文案(用短标题或核心传达)+ 视觉方向 - 制作规格:横屏 / 竖屏、时长带、画面风格、配音音色与声音形态、BGM 与音效、字幕 - 口播文案:`voiceover.md` 绝对路径(口播类必填)/ 真人口播录音绝对路径 / 不适用 - 素材清单:逐条**绝对路径** + 来源 + 授权(无素材时写「无,由 CP 按 Brief 取材」) -- 互动与分享目标:评论 / 关注 / 转发 / 私信 / 直播预约,选一个主目标 - 交付物与验收:`video.mp4` + `cover.jpg` + `final-deliver.md`,回报三者绝对路径;验收标准 - 闸门:GATE A / GATE B 批准人(用户或 main 代理批准 + 批准范围) - 禁止事项:事实与承诺边界、合规红线、禁用方向 @@ -190,48 +216,25 @@ DNA 约束的是选题与观看理由、短标题与视频描述写法、内容 Brief 硬性规则: -- **不写 DNA**:Brief 里不出现 dna-id、DNA 文档路径或 DNA 规则原文——CP 看不到 main 的 DNA,只按 Brief 制作。 +- **不写 DNA**:Brief 里不出现 dna-id、DNA 文档路径或 DNA 规则原文——CP 看不到 main 的 DNA,只按 Brief 制作。DNA 的结论由 main 消化后写成 Brief 的具体要求。 - **不写发布文案**:短标题与视频描述是 main 的发布职责(Step 4),不进 Brief;封面主文案除外。 - **不建工作区**:main 不替 CP 建目录、不指定项目路径;CP 在自己的 workspace 下自建工作区。双方 T3 权限可互访取文件。 -- **素材给绝对路径**:main 负责素材准备,把绝对路径写进 Brief。 +- **素材给绝对路径**:main 负责素材准备(用户素材预处理、`ui-demo` 录屏、从 `campaign_assets/` 挑选),把绝对路径写进 Brief。 - **甲乙方关系**:需求方向、品牌事实、发布文案归 main;制作方案、分镜、渲染参数归 CP。 -### 口播文案规则 - -- **口播类视频的口播文案由 main agent 出**:按 DNA 文档的 `narration-script`(口播文案子模块)写终稿,保存为作品目录下的 `voiceover.md`,在 Brief 中给**绝对路径**。CP 不重写策略文案,只做声画实现。 -- **真人口播**:明确要用用户真人声音时,指导用户按口播稿录音,完成后向用户取得录音文件。 -- **口播子模块未启用**:Brief 写明「口播文案:不适用」或只给要点,由 CP 按其 workflow 组织旁白;main 不再规定逐句台词。 -- 用户必用的事实、案例、承诺和 CTA 必须进入 Brief 或口播终稿,不得为了形式删除关键事实。 - -## 【确认】Brief - -第四个必停节点。用户确认 Brief(及已启用的口播终稿)后才委托 CP。Brief 变更时更新版本并推送变更要点;已开工中间产物按新版取舍,弃用部分记入交付说明。 - -## Step 6 - 视频制作 +2. 口播类视频:按 DNA 的 `narration-script` 子模块写口播终稿 `wx_channel/outputs//voiceover.md`,Brief 里给绝对路径;真人口播时指导用户按口播稿录音,完成后向用户取得录音文件。口播子模块未启用时 Brief 写「口播文案:不适用」或只给要点,由 CP 按其 workflow 组织旁白,main 不再规定逐句台词。用户必用的事实、案例、承诺和 CTA 必须进入 Brief 或口播终稿,不得为了形式删除关键事实。 +3. 参考模式下,把选题与创意结论写进 Brief 的「内容创意」段即可;`viral-chaser` 拆解报告是 main 的采样材料,**不作为 Brief 附件交给 CP**。 +4. spawn `content-producer` 委托制作:只交 Brief + 素材绝对路径 + 口播文案 / 录音;不指定 CP 的工作区与制作方案。Brief 指定 `workflow` 时 CP 必须采用,未指定时 CP 按通用阶段链自由发挥。 +5. Brief 变更时更新版本并推送变更要点;已开工中间产物按新版取舍,弃用部分记入交付说明。制作中发现 Brief 无法执行(素材缺失、时长超标)时,由 CP 回报、main 与用户确认后改 Brief,CP 不擅自改策略。 +6. CP 交付后,按其回报的绝对路径把成片与封面取回 `wx_channel/outputs//`(`video.mp4` / `cover.jpg`),并把交付说明要点记入作品目录。用户直接提供成片时校验格式(`.mp4` / `.mov` / `.avi` / `.webm`)与时长后复制进作品目录。 -取得成片 `wx_channel/outputs//video.mp4`(CP 交付后按其回报的绝对路径取回,main 不替 CP 建工作区): +main 负责监督并推动 CP 的进度,卡住时追问而不是替它做。 -| 情况 | 处理 | -| --- | --- | -| 用户直接提供成片 | 校验格式(`.mp4` / `.mov` / `.avi` / `.webm`)与时长,复制到作品目录 | -| 用户有素材但需简单加工 | 口播轻剪辑(去口癖 / 剪集锦)走顶层 `talking-head-cut`;拼接、字幕、BGM、片头片尾走顶层 `video-edit` | -| 全案制作 | 委托 `content-producer`,只交付确认后的 Brief;若 Brief 指定 `workflow`,CP 必须采用,未指定则 CP 自由发挥 | - -成片必须兑现 Brief 与口播终稿(如有);制作中发现 Brief 无法执行(素材缺失、时长超标)时回到【确认】Brief 节点处理,不擅自改策略。main 负责监督并推动 CP 的进度,卡住时追问而不是替它做。 - -## Step 8 - 存文件 +## 【确认】成片与封面 -定稿作品目录 `wx_channel/outputs//`: +第三个必停节点。成片与封面确认后才可发布。用户有意见按意见修改,直至确认。 -- `brief.md`(定稿 Brief) -- `voiceover.md`(口播类:main 写的口播终稿) -- `publish-copy.md`(短标题 + 视频描述定稿,发布与入库都以它为准) -- `video.mp4` 成片(或 `.mov` / `.avi` / `.webm`) -- `cover.jpg` 封面图 -- `materials/` 原始素材(Step 2 已放入) -- `dna-meta.json` 内容形如 `{"platform":"wx_channel","dna_id":""}`,记录本条所用 DNA(发布记录时 `published-track record` 自动读取建立关联,供后续 DNA 表现评估) - -## Step 9 - 发布 +## Step 6 - 发布 按 `wechat-channels-publish` 工具说明驱动 camoufox-cli 发布(该工具无 wrapper,逐条按其步骤执行): @@ -244,20 +247,37 @@ Brief 硬性规则: 必须等待发布流程完整返回后再判定结果,禁止提前自行判断发布成功。用户要求人工检查后再发布时,按该工具「手动模式」执行。 -## Step 10 - 入库 +## Step 7 - 记录 + +1. 定稿作品目录 `wx_channel/outputs//`: -调 `published-track record` 完成入库: + - `brief.md`(定稿 Brief) + - `voiceover.md`(口播类:main 写的口播终稿) + - `publish-copy.md`(短标题 + 视频描述定稿,发布与入库都以它为准) + - `video.mp4` 成片(或 `.mov` / `.avi` / `.webm`) + - `cover.jpg` 封面图 + - `materials/` 原始素材(Step 2 已放入) + +2. 写 `wx_channel/outputs//dna-meta.json`,记录本条所用 DNA: + +```json +{"platform": "wx_channel", "dna_id": ""} +``` + +3. 调 `published-track record` 入库: ```bash published-track record \ --platform wx_channel \ --source-folder wx_channel/outputs// \ - --account <发布所用账号> \ + --account <发布所用账号 alias> \ --title "" \ --publish-url "" ``` -- `--title` 是数据库字段名,**必须传完整视频描述**(含 hashtag)——`wx-channel-engagement` 抓取按它匹配后台作品。 -- **短标题不入库**:管理页不展示短标题,写库会造成匹配混淆;短标题只留在作品目录的 `publish-copy.md`。 -- `dna_id` 自动从 `dna-meta.json` 读取。 -- 发布时没拿到链接的,后续用户提供或抓取到链接后用相同 `--source-folder` 重跑 `published-track record` 补 `--publish-url`——upsert 语义升级记录,不重复插行。 + - `--title` 是数据库字段名,**必须传完整视频描述**(含 hashtag)——`wx-channel-engagement` 抓取按它匹配后台作品。 + - **短标题不入库**:管理页不展示短标题,写库会造成匹配混淆;短标题只留在作品目录的 `publish-copy.md`。 + - `dna_id` 自动从 `dna-meta.json` 读取。 + - 发布时没拿到链接的,后续用户提供或抓取到链接后用相同 `--source-folder` 重跑 `published-track record` 补 `--publish-url`——upsert 语义升级记录,不重复插行。 + +4. 发布流程到此结束;互动数据由每日定时采集任务统一抓取,复盘走 `review.md`。 diff --git a/crews/main/skills/expert-wx-channel/workflows/editing.md b/crews/main/skills/expert-wx-channel/workflows/editing.md index 28b189d6..d0305f39 100644 --- a/crews/main/skills/expert-wx-channel/workflows/editing.md +++ b/crews/main/skills/expert-wx-channel/workflows/editing.md @@ -67,8 +67,8 @@ ## 改后发布 -用户要求改完后发布 / 重新发布时,走 `content-production.md` 的成片后流程(Step 7-10): +用户要求改完后发布 / 重新发布时,走 `content-production.md` 的成片后流程(Step 5-7): -1. **成片同步**:脚本改动涉及画面或口播的,按 `content-production.md` Step 7 重新走视频制作(已有成片只需轻剪辑时走 `talking-head-cut` / `video-edit`)。 +1. **成片同步**:脚本改动涉及画面或口播的,按 `content-production.md` Step 5 重新走视频制作(已有成片只需轻剪辑时走 `talking-head-cut` / `video-edit`)。 2. **存文件**:稿件与成片归位 `wx_channel/outputs//`,同步更新 `brief.md`、`voiceover.md`(如有)、`cover.jpg` 与 `dna-meta.json`。 -3. **发布 + 入库**:按 `wechat-channels-publish` 工具说明发布,再按 `content-production.md` Step 10 调 `published-track record` 入库——`--title` 仍传完整视频描述;视频描述改过的,用新文案重跑记录。 +3. **发布 + 入库**:按 `wechat-channels-publish` 工具说明发布,再按 `content-production.md` Step 7 调 `published-track record` 入库——`--title` 仍传完整视频描述;视频描述改过的,用新文案重跑记录。 diff --git a/crews/main/skills/expert-wx-channel/workflows/review.md b/crews/main/skills/expert-wx-channel/workflows/review.md index bfe79044..5a110973 100644 --- a/crews/main/skills/expert-wx-channel/workflows/review.md +++ b/crews/main/skills/expert-wx-channel/workflows/review.md @@ -39,8 +39,9 @@ | 分享低 | 选题、内容创意 | topic-angle、content-idea | | 评论低 | 内容创意(讨论点)、口播文案 | content-idea、narration-script | | 收藏少 | 内容创意、制作规格 | content-idea、production-spec | -| 关注少 | 账号运营子模块、选题 | account-bio、content-mix-cadence、topic-angle | -| 转化少(私信/成交) | 选题、内容创意 | topic-angle、content-idea | +| 关注少 | 账号运营子模块、业务植入与 CTA | account-bio、content-mix-cadence、interaction-cta | +| 转化少(私信/成交) | 业务植入与 CTA、选题、内容创意 | biz-implant、interaction-cta、topic-angle、content-idea | +| 广告感重(分享跳水、负面评论) | 业务植入与 CTA | biz-implant、interaction-cta | 交叉判断:完播高、分享低 → 内容好看但缺社交价值;分享高、完播低 → 短标题 / 视频描述的承诺与内容兑现不足。 diff --git a/crews/main/skills/expert-wx-channel/workflows/style-dna.md b/crews/main/skills/expert-wx-channel/workflows/style-dna.md index 6d0a8a1e..952d40a4 100644 --- a/crews/main/skills/expert-wx-channel/workflows/style-dna.md +++ b/crews/main/skills/expert-wx-channel/workflows/style-dna.md @@ -4,7 +4,7 @@ ## 边界 -- DNA 是从一批作品样本中提取、聚合出的内容生产规则集,不存在「平台级 DNA」或「账号级 DNA」的说法;样本可以来自多个账号,也可以来自用户指定的一个账号的发布列表批量提取。 +- DNA 是从一批作品样本中提取、聚合出的内容生产规则集;样本可以来自多个账号,也可以来自用户指定的一个账号的发布列表批量提取。 - 账号初始化与默认 `dna-0` 建立走 `account-setup.md`;对标样本先走 `account-benchmark.md`。 - DNA 如何用于内容生产走 `content-production.md`;改稿走 `editing.md`;数据复盘走 `review.md`。 - DNA 指导 main agent 出内容或视频 Brief;全片制作委托 `content-producer`。 @@ -150,7 +150,7 @@ wx-channel-style-profiler update \ ## 对标接口 -对标样本进入独立 `dna-id`;比较时输出选题、短标题与视频描述、内容创意、视频形态与制作指向、制作规格的差异,以及(账号批量样本才有的)账号运营子模块差异:简介写法、发布习惯。用户明确采纳后才通过局部融合写进目标 DNA。 +对标样本进入独立 `dna-id`;比较时输出选题、短标题与视频描述、内容创意、业务植入与 CTA、视频形态与制作指向、制作规格的差异,以及(账号批量样本才有的)账号运营子模块差异:简介写法、发布习惯。用户明确采纳后才通过局部融合写进目标 DNA。 ## 编排原则 diff --git a/crews/main/skills/expert-xhs/SKILL.md b/crews/main/skills/expert-xhs/SKILL.md index 8fe8d351..cc52781d 100644 --- a/crews/main/skills/expert-xhs/SKILL.md +++ b/crews/main/skills/expert-xhs/SKILL.md @@ -48,7 +48,7 @@ metadata: DNA 存储目录是 `xhs/dna/`。未指定 DNA 时默认使用并更新 `dna-0`(图文);视频笔记另建 dna-id(如 `dna-0-video`)。生产前同时读取 DNA 文档与 DNA template;对标分析先建立独立对标 DNA,不默认写入 `dna-0`。 -DNA 是**从一批作品样本提取并聚合出的内容生产规则集**:图文 9 维——选题与观看理由、标题与封面图组、内容创意、**匹配的用户问题**、正文表达与语气、图组视觉、互动引导与转化,加账号运营子模块(简介写法、内容形式比例、发布习惯);视频 9 维——前四项加视频内容形态与制作指向、制作规格与视听倾向、口播文案子模块与账号运营子模块。搜索维度是小红书必备:最大流量池来自搜索,关键词必须落到用户可能的提问原句。DNA 指导 main agent 写图文或出视频 Brief(+ 口播文案),不规定创作细节与成片制作。维度框架 v2 位于 `xhs-style-profiler` 的 `references/note-dna-framework.md` 与 `references/video-dna-framework.md`。 +DNA 是**从一批作品样本提取并聚合出的内容生产规则集**:图文 10 维——选题与观看理由、标题与封面图组、内容创意、**匹配的用户问题**、正文表达与语气、图组视觉、**业务植入套路**、**互动引导与 CTA 套路**,加账号运营子模块(简介写法、内容形式比例、发布习惯);视频 11 维——前四项加**业务植入套路**、**互动引导与 CTA 套路**、视频内容形态与制作指向、制作规格与视听倾向、口播文案子模块与账号运营子模块。搜索维度是小红书必备:最大流量池来自搜索,关键词必须落到用户可能的提问原句。DNA 指导 main agent 写图文或出视频 Brief(+ 口播文案),不规定创作细节与成片制作。维度框架 v2 位于 `xhs-style-profiler` 的 `references/note-dna-framework.md` 与 `references/video-dna-framework.md`。 **视频全案分工硬边界**:main 负责选题策划、按 DNA 出 **Brief**、拟定标题与简介、准备素材(用户素材预处理 / `ui-demo` 录屏 / 从 `campaign_assets/` 挑选,绝对路径写进 Brief)、监督推动 CP、成片后的发布与运营,也直接做图文内容与已有素材轻加工;视频全案的成片制作委托 `content-producer`。口播类视频的口播文案由 main 按 `narration-script` 子模块写好并随 Brief 交付(真人口播时,指导用户录音并取得录音文件),CP 不重写策略文案。Brief 指定 `workflow` 时 CP 必须采用,未指定时 CP 按通用阶段链自由发挥。Brief **不含 DNA 信息**,main 也不替 CP 建工作区(双方 T3 权限可互访取文件)。 diff --git a/crews/main/skills/expert-xhs/tools/xhs-style-profiler/SKILL.md b/crews/main/skills/expert-xhs/tools/xhs-style-profiler/SKILL.md index 29e36a34..a4bf537b 100644 --- a/crews/main/skills/expert-xhs/tools/xhs-style-profiler/SKILL.md +++ b/crews/main/skills/expert-xhs/tools/xhs-style-profiler/SKILL.md @@ -1,6 +1,6 @@ --- name: xhs-style-profiler -description: 提取小红书作品 DNA:单篇笔记(图文 / 视频)生成 report,按 dna-id 聚合选题、标题与封面、内容创意、匹配的用户问题(搜索意图)、视频形态与制作指向、正文表达与图组视觉、口播文案子模块与账号运营子模块,推导 main agent 的图文 / Brief 生产 template。 +description: 提取小红书作品 DNA:单篇笔记(图文 / 视频)生成 report,按 dna-id 聚合选题、标题与封面、内容创意、匹配的用户问题(搜索意图)、业务植入套路、互动引导与 CTA 套路、视频形态与制作指向、正文表达与图组视觉、口播文案子模块与账号运营子模块,推导 main agent 的图文 / Brief 生产 template。 metadata: openclaw: emoji: 🧬 @@ -18,10 +18,10 @@ DNA 的用途是指导 main agent 选题、包装、出内容或出视频制作 | kind | 作品 | 维度框架 | 默认 dna-id | |------|------|----------|-------------| -| `video` | 视频 | `references/video-dna-framework.md` | `dna-0` | -| `note` | 图文 | `references/note-dna-framework.md` | `dna-0-note` | +| `note` | 图文(默认) | `references/note-dna-framework.md` | `dna-0` | +| `video` | 视频 | `references/video-dna-framework.md` | `dna-0-video` | -- 不传 `--kind` 时默认 `note`(小红书的主作品类型)。 +- 不传 `--kind` 时默认 `note`(小红书的主作品类型),因此**默认 `dna-0` 是图文 DNA**;视频样本另建 dna-id(如 `dna-0-video`)。 - `build` / `update` 会校验同一 DNA 目录下 report 的 kind 一致,混型直接报错——另一种类型请另建 dna-id。 - 默认 dna-id 只是约定:用户可以指定任意 dna-id,脚本不强制命名。 @@ -33,7 +33,7 @@ DNA 的用途是指导 main agent 选题、包装、出内容或出视频制作 DNA 文档 -> DNA template ``` -- **DNA report**:单篇作品的样本观测 + 维度提取结果(不是账号级结论,也不是模板)。 +- **DNA report**:单篇作品的样本观测 + 维度提取结果;跨篇共性与生产规则由聚合阶段给出,report 本身不是模板。 - **DNA 文档**:聚合后的生产规则、样本覆盖度、子模块结论与用户输入转译区。 - **DNA template**:main agent 的生产输入模板(视频 = Brief 正文 + 口播文案;图文 = 写作模板)。 @@ -55,11 +55,11 @@ xhs/dna/{dna-id}/ - **搜索优先**:`search-intent`(匹配的用户问题)是小红书必备维度——最大流量池来自搜索。关键词必须尽量落到用户可能的提问原句,不只写平台标签。 - 单篇 report 只提供候选信号,不判断跨篇稳定性;共性、偏好、孤例由聚合阶段判断。 -- 统计只做证据底座,不评分、不判定风格是否合格。 +- 脚本统计(句长、问句与人称密度、感叹号密度、口播密度;图文另有标题字数、正文行数、emoji 密度、话题标签数)只做证据底座,不评分、不判定风格是否合格;口头禅与签名式表达必须由 Agent 回读原文确认,不能凭统计直接下 DNA 结论。 - 视觉维度必须有图片 / 关键帧证据,由视觉模型读取;缺失写「未提供」,不得凭文本想象补齐。 - 口播文案子模块只在口播类作品启用;样本不足写「未启用」。 - 账号运营子模块(简介写法、内容形式比例、发布习惯)只在样本来自对标账号批量提取时填写,且**不进 template**。 -- 不输出合规结论、账号权重或风格评分。 +- 不输出风格评分或账号权重;合规只记「必须避免项」(虚假承诺、利益诱导互动、隐藏站外联系方式、谐音绕检测),不出具合规审查结论。 ## Report — 单篇提取 @@ -142,9 +142,10 @@ xhs-style-profiler update \ 2. 标题与封面 3. 内容创意 4. 关键词与用户问题 -5. 视频形态与制作指向 -6. 制作规格 -7. 口播文案 +5. 业务植入与 CTA +6. 视频形态与制作指向 +7. 制作规格 +8. 口播文案 **图文作品 template(= 图文写作模板)** @@ -154,7 +155,7 @@ xhs-style-profiler update \ 4. 内容创意与结构 5. 正文表达 6. 图组 -7. 互动与标签 +7. 业务植入与 CTA - 开头两段(**选题**、**标题与封面**)跨平台通用。 - 视频 template 的各段直接对应 Brief 正文字段;**Brief 不含 DNA 信息**(Content Producer 看不到 main 的 DNA),素材清单与授权、验收标准、闸门批准人按平台 Content Production Workflow 填。 @@ -194,6 +195,8 @@ content-idea:多用「实测对比」创意原型 | `search-intent` | 匹配的用户问题 | | `video-form` | 视频内容形态与制作指向 | | `production-spec` | 制作规格与视听倾向 | +| `biz-implant` | 业务植入套路 | +| `interaction-cta` | 互动引导与 CTA 套路 | | `narration-script` | 口播文案子DNA | | `account-bio` | 账号简介写法 | | `content-mix-cadence` | 内容形式比例与发布习惯 | @@ -208,16 +211,13 @@ content-idea:多用「实测对比」创意原型 | `search-intent` | 匹配的用户问题 | | `body-voice` | 正文表达与语气 | | `imageset-visual` | 图组视觉风格 | -| `interaction-cta` | 互动引导与转化 | +| `biz-implant` | 业务植入套路 | +| `interaction-cta` | 互动引导与 CTA 套路 | | `account-bio` | 账号简介写法 | | `content-mix-cadence` | 内容形式比例与发布习惯 | `--focus` 按作品类型校验:视频 report 不接受图文维度 ID,反之亦然。 -## 统计与分词 - -脚本统计句长、问句与人称密度、感叹号密度、口播密度(视频)/ 标题字数、正文行数、emoji 密度、话题标签数(图文)等指标作为聚合证据底座;中文高频信号使用相邻二字组合,仅作候选线索。口头禅与签名式表达必须由 Agent 回读原文确认,分词结果不能直接当 DNA 结论。 - ## 参考资料 - `references/video-dna-framework.md`(小红书视频作品 DNA 框架 v2:维度定义、制作指向映射、聚合边界与 Focus ID) diff --git a/crews/main/skills/expert-xhs/tools/xhs-style-profiler/references/note-dna-framework.md b/crews/main/skills/expert-xhs/tools/xhs-style-profiler/references/note-dna-framework.md index b69e84fb..d54f228b 100644 --- a/crews/main/skills/expert-xhs/tools/xhs-style-profiler/references/note-dna-framework.md +++ b/crews/main/skills/expert-xhs/tools/xhs-style-profiler/references/note-dna-framework.md @@ -1,11 +1,11 @@ # 小红书图文作品 DNA 框架(v2) -> 状态:v2(2026-09-10)。Focus ID、维度命名与 template 语义以本文件为准;调整维度必须升版本,并同步 `scripts/build_style_profile.py` 与工具 `SKILL.md`。 +> 状态:v2(2026-09-10 修订:补业务植入套路 `biz-implant` 与互动引导 / CTA 套路 `interaction-cta` 两维)。Focus ID、维度命名与 template 语义以本文件为准;调整维度必须升版本,并同步 `scripts/build_style_profile.py` 与工具 `SKILL.md`。 > 小红书有两套框架:视频作品用 `video-dna-framework.md`,本文件是**图文作品**框架。 ## 定位与边界 -DNA 不是「平台级 DNA」,也不是账号人设说明书,而是**从一批作品样本中提取、聚合出的内容生产规则集**。生产方式为三层产物: +DNA 是**从一批作品样本中提取、聚合出的内容生产规则集**:它回答「这类作品怎么选、怎么包装、业务怎么植入、怎么引导行动」,不是账号人设说明书。生产方式为三层产物: ```text 单篇作品 -> DNA report(本框架的维度逐项提取) @@ -21,11 +21,11 @@ DNA 文档 -> DNA template | 进 DNA | 不进 DNA | |--------|----------| -| 选题与观看理由、标题与封面写法、内容创意原型、匹配的用户问题(搜索流量)、正文表达与语气、图组视觉、互动引导与转化、(对标账号)账号运营子模块 | 创作细节、脚本结构、逐句台词、镜头表、转场与编码参数——那些归 Content Producer | +| 选题与观看理由、标题与封面写法、内容创意原型、匹配的用户问题(搜索流量)、正文表达与语气、图组视觉、业务植入套路、互动引导与 CTA 套路、(对标账号)账号运营子模块 | 创作细节、脚本结构、逐句台词、镜头表、转场与编码参数——那些归 Content Producer | 图文 DNA 的用途是指导 main agent 直接生产图文作品;template 就是图文写作模板。账号运营子模块(简介写法、内容形式比例、发布习惯)只写进 DNA 文档,**不进 template**。 -## 维度(9 维) +## 维度(10 维) ### 一、选题与包装 @@ -48,29 +48,31 @@ DNA 文档 -> DNA template | 5 | `body-voice` | 正文表达与语气 | 开头钩子、正文组织方式(清单体 / 教程步骤 / 故事线 / 对比 / 观点输出)、分行与段落节奏、口语化程度与人称、emoji 与标点用法、签名式表达 | | 6 | `imageset-visual` | 图组视觉风格 | 图片数量与顺序、构图类型(产品展示 / 场景 / 文字卡片 / 对比图 / 过程图)、图文信息分工、色调与质感、版式一致性、文字视觉 | -### 四、互动与转化 +### 四、业务植入与转化 | # | ID | 维度 | 观测内容 | |---|----|------|----------| -| 7 | `interaction-cta` | 互动引导与转化 | 评论 / 收藏 / 关注 / 进店 / 咨询等平台内行动的引导方式与位置、每篇行动引导数量、话题标签承载的意图、合规边界 | +| 7 | `biz-implant` | 业务植入套路 | 是否有业务植入(纯内容 / 软植入 / 硬广直给)、植入位置(开头 / 中段某一步 / 结尾 / 图组某一图 / 评论区自评)、植入载体(案例与数据、清单第 N 项、教程步骤内嵌、痛点故事转折、对比实测、产品截图、体验记录)、植入方式原型(痛点→方案 / 场景带入 / 实测对比 / 清单第 N 项 / 身份认同 / 口碑故事 / 教程内嵌 / 硬广直给)、内容与业务的衔接句(转折句原文)、植入密度与占比(正文字数占比、品牌词与产品名出现次数与方式) | +| 8 | `interaction-cta` | 互动引导与 CTA 套路 | 行动目标(评论 / 收藏 / 关注 / 进店 / 咨询 / 搜索品牌词)与本篇主目标、CTA 位置与时机(首段钩子后 / 正文中段 / 结尾 / 图组末图 / 评论区自评)、CTA 句式与原文摘录(命令式 / 提问式 / 利益式 / 身份式)、一篇放几个行动、诱因设计(利益点 / 情绪 / 身份认同 / 稀缺)、话题标签承载的意图、与业务转化目标的对应、合规边界 | ### 五、账号运营子模块(对标账号样本才有) | # | ID | 维度 | 观测内容 | |---|----|------|----------| -| 8 | `account-bio` | 账号简介写法 | 账号昵称、简介写法、主页与置顶表达、对外承诺(仅对标账号样本可得) | -| 9 | `content-mix-cadence` | 内容形式比例与发布习惯 | 图文 / 视频等内容形式比例、发布时间段与频率、内容形式混合节奏(如三篇图文对一篇视频)、栏目化节奏(仅对标账号批量样本可得) | +| 9 | `account-bio` | 账号简介写法 | 账号昵称、简介写法、主页与置顶表达、对外承诺(仅对标账号样本可得) | +| 10 | `content-mix-cadence` | 内容形式比例与发布习惯 | 图文 / 视频等内容形式比例、发布时间段与频率、内容形式混合节奏(如三篇图文对一篇视频)、栏目化节奏(仅对标账号批量样本可得) | ## Report 与聚合规则 1. 单篇 report 先填「样本观测」:作品类型、样本来源、账号与简介、发布时间、数据线索、图片数量与来源、关键词与标签。缺失一律写「未观测」,不得编造。 2. 单篇 report 不判断跨篇稳定性;聚合时区分高覆盖共性、高权重样本偏好、局部借鉴(focus)、孤例与例外,并标注样本覆盖度。 3. 视觉维度(封面 / 首帧 / 图组)必须有图片证据,由视觉模型读取本地图片后反推 AIGC 复现要素;没有图片写「未提供」。 -4. 正文表达维度里的高频词、口头禅与签名表达只是候选线索,必须回读原文确认后才能进 DNA 文档。 +4. 正文表达维度里的口头禅与签名表达只是候选线索,必须回读原文确认后才能进 DNA 文档。 5. 账号运营子模块(`account-bio`、`content-mix-cadence`)只在样本来自对标账号批量提取时填写;用户只提供单篇时写「未观测」。 6. 脚本统计只作证据底座(标题字数、正文行数、句长、问句密度、emoji 密度、话题标签数),不生成总分、不判定风格是否合格。 7. 关键词必须尽量落到**用户可能的提问原句**,不只写平台标签;单篇只记候选,聚合后形成「关键词 → 用户问题 → 内容形式」的搜索意图地图(小红书最大流量池来自搜索)。 -8. 高数据样本必须回读创意、结构与关键词再归因,不得把高阅读直接等同于风格好。 +8. 业务植入与 CTA 两个维度必须给出**位置 + 载体 + 原文摘录**三样证据;只写「自然植入」「引导关注」这类空泛结论不算提取完成。本篇没有业务植入时明确写「无植入(纯内容)」,不得留空;平台红线(虚假承诺、利益诱导互动、隐藏站外联系方式、谐音绕检测)一律记为必须避免项。 +9. 高数据样本必须回读创意、结构与关键词再归因,不得把高阅读直接等同于风格好。 ## Template 语义 @@ -82,7 +84,7 @@ DNA template = **图文写作模板**,固定语义段如下(脚本 `build` / 4. **[内容创意与结构部分]** — 创意原型、正文组织方式、信息密度、记忆点、避免 5. **[正文表达部分]** — 开头钩子、推进方式、段落与分行节奏、人称与语气、emoji 与标点、签名式表达、必须做、避免 6. **[图组部分]** — 图片数量与顺序、构图类型、色调与质感、版式一致性、文字视觉、AIGC 提示词要素 -7. **[互动与标签部分]** — 互动目标、引导方式、话题标签策略、合规边界 +7. **[业务植入与 CTA部分]** — 植入位置与时机、植入载体与方式原型、内容与业务的衔接句、植入密度与占比、CTA 主目标、CTA 位置与句式、诱因与合规红线、避免 - 开头两段(**选题**、**标题与封面**)跨平台通用:任何生产都先解决「做什么」和「怎么命名、怎么呈现封面」。 - `[图组部分]` 与 `[正文表达部分]` 是两条并行的生产轨道:图组规则管视觉,正文规则管文字。 @@ -99,7 +101,7 @@ DNA template = **图文写作模板**,固定语义段如下(脚本 `build` / | 选题与包装 | `topic-angle` `title-cover` | | 内容创意与搜索 | `content-idea` `search-intent` | | 正文与视觉 | `body-voice` `imageset-visual` | -| 互动与转化 | `interaction-cta` | +| 业务植入与转化 | `biz-implant` `interaction-cta` | | 账号运营子模块(对标账号样本才有) | `account-bio` `content-mix-cadence` | focus 校验按作品类型执行:视频 report 不接受图文维度 ID,反之亦然。 diff --git a/crews/main/skills/expert-xhs/tools/xhs-style-profiler/references/video-dna-framework.md b/crews/main/skills/expert-xhs/tools/xhs-style-profiler/references/video-dna-framework.md index 01fa489f..266dd643 100644 --- a/crews/main/skills/expert-xhs/tools/xhs-style-profiler/references/video-dna-framework.md +++ b/crews/main/skills/expert-xhs/tools/xhs-style-profiler/references/video-dna-framework.md @@ -1,11 +1,11 @@ # 小红书视频作品 DNA 框架(v2) -> 状态:v2(2026-09-10)。Focus ID、维度命名与 template 语义以本文件为准;调整维度必须升版本,并同步 `scripts/build_style_profile.py` 与工具 `SKILL.md`。 +> 状态:v2(2026-09-10 修订:补业务植入套路 `biz-implant` 与互动引导 / CTA 套路 `interaction-cta` 两维)。Focus ID、维度命名与 template 语义以本文件为准;调整维度必须升版本,并同步 `scripts/build_style_profile.py` 与工具 `SKILL.md`。 > 小红书有两套框架:图文作品用 `note-dna-framework.md`,本文件是**视频作品**框架。 ## 定位与边界 -DNA 不是「平台级 DNA」,也不是账号人设说明书,而是**从一批作品样本中提取、聚合出的内容生产规则集**。生产方式与其他平台完全一致,仍是三层产物: +DNA 是**从一批作品样本中提取、聚合出的内容生产规则集**:它回答「这类作品怎么选、怎么包装、业务怎么植入、怎么引导行动」,不是账号人设说明书。生产方式与其他平台完全一致,仍是三层产物: ```text 单篇作品 -> DNA report(本框架的维度逐项提取) @@ -21,11 +21,11 @@ DNA 文档 -> DNA template | 进 DNA | 不进 DNA | |--------|----------| -| 选题与观看理由、标题与封面写法、内容创意原型、匹配的用户问题(搜索流量)、视频内容形态与制作指向、制作规格与视听倾向、(口播类)口播文案子DNA、(对标账号)账号运营子模块 | 创作细节、脚本结构、逐句台词、镜头表、转场与编码参数——那些归 Content Producer | +| 选题与观看理由、标题与封面写法、内容创意原型、匹配的用户问题(搜索流量)、业务植入套路、互动引导与 CTA 套路、视频内容形态与制作指向、制作规格与视听倾向、(口播类)口播文案子DNA、(对标账号)账号运营子模块 | 创作细节、脚本结构、逐句台词、镜头表、转场与编码参数——那些归 Content Producer | 视频 DNA 的用途是指导 main agent 出具 **Brief.md** 与(口播类的)**口播文案**。template 就是 Brief 正文模板 + 口播文案模板(可选),不是成片制作模板。账号运营子模块(简介写法、内容形式比例、发布习惯)只写进 DNA 文档,**不进 template**。 -## 维度(9 维) +## 维度(11 维) ### 一、选题与包装 @@ -48,18 +48,25 @@ DNA 文档 -> DNA template | 5 | `video-form` | 视频内容形态与制作指向 | 视频内容形态(口播 / 实拍拼接 / 影视解说+反转植入 / 纯 AIGC 动画 / 创意转场动效 / 录屏演示 / 图文卡片视频 / 混合)+ 判定依据 + **制作指向**(见下方映射表) | | 6 | `production-spec` | 制作规格与视听倾向 | 横屏或竖屏、时长带、画面风格(色调、质感、字幕样式倾向、信息密度)、配音音色与声音形态(原声口播 / TTS / 旁白 / 纯画面字幕)、BGM 与音效倾向、封面规格 | -### 四、口播文案子模块(可选,仅口播类启用) +### 四、业务植入与转化 | # | ID | 维度 | 观测内容 | |---|----|------|----------| -| 7 | `narration-script` | 口播文案子DNA | 起(从什么起步)、承(靠什么推进)、转(转折触发)、合(收束与 CTA)、人称与语气、句长与语速、签名式表达——用于指导 main agent 写同类型视频的口播文案 | +| 7 | `biz-implant` | 业务植入套路 | 是否有业务植入(纯内容 / 软植入 / 硬广直给)、植入位置与时机(前 3 秒 / 中段转折 / 结尾收束 / 反转点 / 评论区置顶 / 主页与组件)、植入载体(剧情道具、口播一句话、字幕卡片、场景背景、案例与数据、测评对象、购物车或留资组件、私信与主页引导)、植入方式原型(反转植入 / 痛点→方案 / 场景带入 / 实测对比 / 清单第 N 项 / 身份认同 / 口碑故事 / 教程内嵌 / 硬广直给)、内容与业务的衔接句(转折触发词原文)、植入密度与占比(时长占比、品牌词与产品名出现次数与方式);形态层面的「影视解说 + 反转植入」在 `video-form` 记形态与制作指向,本维度记植入怎么设计 | +| 8 | `interaction-cta` | 互动引导与 CTA 套路 | 行动目标(关注 / 评论关键词 / 私信 / 主页点击 / 搜索品牌词 / 购物车 / 直播预约 / 转发)与本篇主目标、CTA 出现位置与时机(口播收尾句 / 字幕卡 / 片尾贴片 / 描述区 / 评论区置顶)、CTA 句式与原文摘录(命令式 / 提问式 / 利益式 / 身份式 / 悬念式)、一篇放几个行动、诱因设计(利益点 / 情绪 / 身份认同 / 稀缺)、与业务转化目标的对应、平台组件挂载与合规边界 | -### 五、账号运营子模块(对标账号样本才有) +### 五、口播文案子模块(可选,仅口播类启用) | # | ID | 维度 | 观测内容 | |---|----|------|----------| -| 8 | `account-bio` | 账号简介写法 | 账号昵称、简介写法、主页与置顶表达、对外承诺(仅对标账号样本可得) | -| 9 | `content-mix-cadence` | 内容形式比例与发布习惯 | 图文 / 视频等内容形式比例、发布时间段与频率、内容形式混合节奏(如三篇图文对一篇视频)、栏目化节奏(仅对标账号批量样本可得) | +| 9 | `narration-script` | 口播文案子DNA | 起(从什么起步)、承(靠什么推进)、转(转折触发)、合(收束方式;CTA 的目标、位置与句式记在 `interaction-cta`)、人称与语气、句长与语速、签名式表达——用于指导 main agent 写同类型视频的口播文案 | + +### 六、账号运营子模块(对标账号样本才有) + +| # | ID | 维度 | 观测内容 | +|---|----|------|----------| +| 10 | `account-bio` | 账号简介写法 | 账号昵称、简介写法、主页与置顶表达、对外承诺(仅对标账号样本可得) | +| 11 | `content-mix-cadence` | 内容形式比例与发布习惯 | 图文 / 视频等内容形式比例、发布时间段与频率、内容形式混合节奏(如三篇图文对一篇视频)、栏目化节奏(仅对标账号批量样本可得) | ## Report 与聚合规则 @@ -70,7 +77,8 @@ DNA 文档 -> DNA template 5. 账号运营子模块(`account-bio`、`content-mix-cadence`)只在样本来自对标账号批量提取时填写;用户只提供单篇时写「未观测」。 6. 脚本统计只作证据底座(句长、问句密度、人称密度、感叹号密度、口播密度),不生成总分、不判定风格是否合格。 7. 关键词必须尽量落到**用户可能的提问原句**,不只写平台标签;单篇只记候选,聚合后形成「关键词 → 用户问题 → 内容形式」的搜索意图地图(小红书最大流量池来自搜索)。 -8. 高数据样本必须回读创意、形态与包装再归因,不得把高播放直接等同于风格好。 +8. 业务植入与 CTA 两个维度必须给出**位置 + 载体 + 原文摘录**三样证据;只写「自然植入」「引导关注」这类空泛结论不算提取完成。本篇没有业务植入时明确写「无植入(纯内容)」,不得留空;平台红线(虚假承诺、利益诱导互动、隐藏站外联系方式、谐音绕检测)一律记为必须避免项。 +9. 高数据样本必须回读创意、形态与包装再归因,不得把高播放直接等同于风格好。 ## Template 语义 @@ -80,9 +88,10 @@ DNA template = **Brief.md 正文模板 + 口播文案模板(可选)**,固 2. **[标题与封面部分]** — 标题类型、参考标题、封面或首帧风格、封面 AIGC 提示词要素、话题标签策略 3. **[内容创意部分]** — 创意原型、展开逻辑、记忆点与反转设计、触发条件、避免 4. **[关键词与用户问题部分]** — 主关键词、相关词与长尾句、用户可能的提问、搜索意图与内容形式匹配、标签策略 -5. **[视频形态与制作指向部分]** — 视频内容形态、制作指向、委托边界、未指定形态时 -6. **[制作规格部分]** — 横屏或竖屏、时长带、画面风格、配音音色与声音形态、BGM 与音效、字幕 -7. **[口播文案部分]** — 是否启用、起、承、转、合、人称与语气、句长与语速、签名式表达、必须做、避免 +5. **[业务植入与 CTA部分]** — 植入位置与时机、植入载体与方式原型、内容与业务的衔接句、植入密度与占比、CTA 主目标、CTA 位置与句式、诱因与合规红线、避免 +6. **[视频形态与制作指向部分]** — 视频内容形态、制作指向、委托边界、未指定形态时 +7. **[制作规格部分]** — 横屏或竖屏、时长带、画面风格、配音音色与声音形态、BGM 与音效、字幕 +8. **[口播文案部分]** — 是否启用、起、承、转、合、人称与语气、句长与语速、签名式表达、必须做、避免 - 开头两段(**选题**、**标题与封面**)跨平台通用:任何生产都先解决「做什么」和「怎么命名、怎么呈现封面」。 - `[视频形态与制作指向部分]` 的「制作指向」必须写下方映射表里的真实资源名,Brief 的 `workflow` 字段据此填写。 @@ -114,6 +123,7 @@ Brief 写了 `workflow` 时 Content Producer 必须直接采用,不得替换 | 选题与包装 | `topic-angle` `title-cover` | | 内容创意与搜索 | `content-idea` `search-intent` | | 形态与规格 | `video-form` `production-spec` | +| 业务植入与转化 | `biz-implant` `interaction-cta` | | 口播文案子模块(可选,仅口播类启用) | `narration-script` | | 账号运营子模块(对标账号样本才有) | `account-bio` `content-mix-cadence` | diff --git a/crews/main/skills/expert-xhs/tools/xhs-style-profiler/scripts/build_style_profile.py b/crews/main/skills/expert-xhs/tools/xhs-style-profiler/scripts/build_style_profile.py index 3b4c6170..c2ba21d4 100755 --- a/crews/main/skills/expert-xhs/tools/xhs-style-profiler/scripts/build_style_profile.py +++ b/crews/main/skills/expert-xhs/tools/xhs-style-profiler/scripts/build_style_profile.py @@ -10,16 +10,13 @@ 脚本只做 scaffold 与统计证据底座:不评分、不判定风格合格,定性结论由 Agent 回读原文补齐。 """ import argparse, json, math, re, shutil -from collections import Counter, defaultdict from datetime import datetime, timezone from pathlib import Path -from statistics import median SENTENCE_SPLIT = re.compile(r"[。!?!?]+") PARAGRAPH_SPLIT = re.compile(r"\n\s*\n") TOKEN_RE = re.compile(r"[一-鿿A-Za-z0-9_]+") -ENGLISH_WORD_RE = re.compile(r"[A-Za-z0-9_]+") SECOND_PERSON_RE = re.compile(r"你们?|you", re.IGNORECASE) FIRST_PERSON_RE = re.compile(r"我们?|I|we", re.IGNORECASE) QUESTION_RE = re.compile(r"[??]") @@ -71,6 +68,7 @@ "选题与包装": [("topic-angle", "选题与观看理由"), ("title-cover", "标题与封面")], "内容创意与搜索": [("content-idea", "内容创意"), ("search-intent", "匹配的用户问题")], "形态与规格": [("video-form", "视频内容形态与制作指向"), ("production-spec", "制作规格与视听倾向")], + "业务植入与转化": [("biz-implant", "业务植入套路"), ("interaction-cta", "互动引导与 CTA 套路")], "口播文案子模块(可选,仅口播类启用)": [("narration-script", "口播文案子DNA")], "账号运营子模块(对标账号样本才有)": [("account-bio", "账号简介写法"), ("content-mix-cadence", "内容形式比例与发布习惯")], }, @@ -78,7 +76,7 @@ "选题与包装": [("topic-angle", "选题与观看理由"), ("title-cover", "标题与封面图组")], "内容创意与搜索": [("content-idea", "内容创意"), ("search-intent", "匹配的用户问题")], "正文与视觉": [("body-voice", "正文表达与语气"), ("imageset-visual", "图组视觉风格")], - "互动与转化": [("interaction-cta", "互动引导与转化")], + "业务植入与转化": [("biz-implant", "业务植入套路"), ("interaction-cta", "互动引导与 CTA 套路")], "账号运营子模块(对标账号样本才有)": [("account-bio", "账号简介写法"), ("content-mix-cadence", "内容形式比例与发布习惯")], }, } @@ -109,10 +107,11 @@ "search-intent": "- 单篇观测:本篇命中的关键词(核心词 / 痛点词 / 场景词 / 人群词)、用户可能的提问原句、搜索意图层级、标签承载的搜索意图。\n- 边界:单篇只记候选;聚合后才形成「关键词 → 用户问题 → 内容形式」的搜索意图地图。", "video-form": "- 单篇观测:视频内容形态(口播 / 实拍拼接 / 影视解说+反转植入 / 纯 AIGC 动画 / 创意转场动效 / 录屏演示 / 图文卡片视频 / 混合)与判定依据(画面证据、口播占比、素材来源)。\n- 制作指向:必须落到真实存在的资源名——Content Producer `expert-video` 的某个 workflow(Reversal Ad / Narration Video / Collage B-roll / 通用阶段链),或 main 的素材加工技能(`video-edit` / `talking-head-cut` / `ui-demo`);不得发明不存在的名字。", "production-spec": "- 单篇观测:横屏或竖屏、时长带、画面风格(色调、质感、字幕样式倾向、信息密度)、配音音色与声音形态(原声口播 / TTS / 旁白 / 纯画面字幕)、BGM 与音效倾向、封面规格。\n- 边界:只记规格与倾向,不规定镜头参数、逐镜设计、转场与编码细节——那些归 Content Producer。", - "narration-script": "- 子模块(仅口播类作品启用):起(从什么起步)、承(靠什么推进)、转(转折触发)、合(收束与 CTA),以及人称与语气、句长与语速、签名式表达。\n- 边界:非口播类或证据不足时写「未启用 / 未观测」;不得把单篇句式直接上升为规则。", + "narration-script": "- 子模块(仅口播类作品启用):起(从什么起步)、承(靠什么推进)、转(转折触发)、合(收束方式;CTA 的目标、位置与句式记在 `interaction-cta`),以及人称与语气、句长与语速、签名式表达。\n- 边界:非口播类或证据不足时写「未启用 / 未观测」;不得把单篇句式直接上升为规则。", "body-voice": "- 单篇观测:开头钩子(原文摘录)、正文组织方式(清单体 / 教程步骤 / 故事线 / 对比 / 观点输出)、分行与段落节奏、口语化程度与人称、emoji 与标点用法、签名式表达。\n- 证据边界:脚本统计只给句长 / 行数 / emoji / 标签等线索;口头禅与签名表达必须回读原文确认。", "imageset-visual": "- 单篇观测:图片数量与顺序、构图类型(产品展示 / 场景 / 文字卡片 / 对比图 / 过程图)、图文信息分工、色调与质感、版式一致性、文字视觉。\n- 视觉证据:必须由视觉模型读取本地图片并反推 AIGC 复现要素;无图片写「未提供」,不得凭正文想象补齐。", - "interaction-cta": "- 单篇观测:评论 / 收藏 / 关注 / 进店 / 咨询等平台内行动的引导方式与位置、每篇行动引导数量、话题标签承载的意图。\n- 合规边界:不隐藏站外联系方式、不绕平台检测、不以利益换互动,记为必须避免项。", + "biz-implant": "- 单篇观测:是否有业务植入(纯内容 / 软植入 / 硬广直给)、植入位置与时机、植入载体(剧情道具 / 口播一句话 / 字幕卡片 / 场景背景 / 案例与数据 / 清单第 N 项 / 教程步骤内嵌 / 产品截图 / 购物车或留资组件 / 主页与私信引导)、植入方式原型(反转植入 / 痛点→方案 / 场景带入 / 实测对比 / 身份认同 / 口碑故事 / 教程内嵌 / 硬广直给)、内容与业务的衔接句(原文摘录)、植入密度与占比、品牌词与产品名出现方式与频次。\n- 证据要求:位置 + 载体 + 原文摘录三样齐全;本篇无植入时写「无植入(纯内容)」,不得留空。\n- 边界:只记植入套路,不写逐句广告文案;形态层面的「影视解说 + 反转植入」由 `video-form` 记形态与制作指向。", + "interaction-cta": "- 单篇观测:行动目标(关注 / 评论 / 收藏 / 转发 / 私信 / 主页点击 / 进店 / 咨询 / 搜索品牌词 / 购物车 / 直播预约)与本篇主目标、CTA 出现位置与时机(口播收尾句 / 字幕卡 / 片尾贴片 / 描述区 / 正文结尾 / 图组末图 / 评论区)、CTA 句式与原文摘录(命令式 / 提问式 / 利益式 / 身份式 / 悬念式)、一篇放几个行动、诱因设计(利益点 / 情绪 / 身份认同 / 稀缺)、与业务转化目标的对应。\n- 合规边界:不隐藏站外联系方式、不绕平台检测、不以利益换互动,记为必须避免项。", "account-bio": "- 子模块(仅对标账号样本可得):账号昵称、简介写法、主页与置顶表达、对外承诺。\n- 边界:用户提供的单篇样本无法观测时写「未观测」,不得推导。", "content-mix-cadence": "- 子模块(仅对标账号批量样本可得):图文 / 视频等内容形式比例、发布时间段与频率、内容形式混合节奏(如三篇图文对一篇视频)、栏目化节奏。\n- 边界:必须由账号发布列表的批量样本推导;单篇样本只记本篇发布时间。", } @@ -123,8 +122,8 @@ } TEMPLATE_STAGES = { - "video": ("选题", "标题与封面", "内容创意", "关键词与用户问题", "视频形态与制作指向", "制作规格", "口播文案"), - "note": ("选题", "标题与封面", "关键词与用户问题", "内容创意与结构", "正文表达", "图组", "互动与标签"), + "video": ("选题", "标题与封面", "内容创意", "关键词与用户问题", "业务植入与 CTA", "视频形态与制作指向", "制作规格", "口播文案"), + "note": ("选题", "标题与封面", "关键词与用户问题", "内容创意与结构", "正文表达", "图组", "业务植入与 CTA"), } TEMPLATE_STAGE_FIELDS = { @@ -135,10 +134,10 @@ "视频形态与制作指向": ("视频内容形态", "制作指向", "委托边界", "未指定形态时"), "制作规格": ("横屏或竖屏", "时长带", "画面风格", "配音音色与声音形态", "BGM 与音效", "字幕"), "口播文案": ("是否启用", "起", "承", "转", "合", "人称与语气", "句长与语速", "签名式表达", "必须做", "避免"), + "业务植入与 CTA": ("植入位置与时机", "植入载体与方式原型", "内容与业务的衔接句", "植入密度与占比", "CTA 主目标", "CTA 位置与句式", "诱因与合规红线", "避免"), "内容创意与结构": ("创意原型", "正文组织方式", "信息密度", "记忆点", "避免"), "正文表达": ("开头钩子", "推进方式", "段落与分行节奏", "人称与语气", "emoji 与标点", "签名式表达", "必须做", "避免"), "图组": ("图片数量与顺序", "构图类型", "色调与质感", "版式一致性", "文字视觉", "AIGC 提示词要素"), - "互动与标签": ("互动目标", "引导方式", "话题标签策略", "合规边界"), } TEMPLATE_INTROS = { @@ -147,8 +146,8 @@ } TEMPLATE_CHECKLISTS = { - "video": "- 是否只用一个 DNA,且作品类型与该 DNA 的 `kind` 一致。\n- 选题、标题 / 描述、封面是否来自 DNA 文档。\n- 内容创意是否落到可复用的创意原型,而不是照抄样本主题。\n- 视频形态是否明确指向 Content Producer `expert-video` 的某个 workflow,或 main 的某个素材加工技能。\n- Brief 是否只含制作所需信息(不含 DNA 内容),素材是否给了绝对路径与授权说明。\n- 口播类是否附口播文案(或真人口播录音路径);口播子模块未启用时是否避免规定逐句口播。\n- 制作规格(横竖屏、时长带、画面风格、配音音色)是否尊重样本覆盖度;样本不足时是否标注未观测。\n- 关键词是否落到用户可能的提问原句(不只平台标签),并与内容形式匹配。\n- 用户输入是否已转译为具体执行规则。", - "note": "- 是否只用一个 DNA,且作品类型与该 DNA 的 `kind` 一致。\n- 选题、标题与封面图组是否来自 DNA 文档。\n- 正文表达的每条规则是否可从 DNA 文档推导,未使用空泛形容词。\n- 图组数量、构图与视觉风格是否与 DNA 一致;视觉结论是否有图片证据。\n- 互动引导是否每篇只放一个行动,且不越合规红线。\n- 关键词是否落到用户可能的提问原句(不只平台标签),并与内容形式匹配。\n- 用户输入是否已转译为具体执行规则。", + "video": "- 是否只用一个 DNA,且作品类型与该 DNA 的 `kind` 一致。\n- 选题、标题 / 描述、封面是否来自 DNA 文档。\n- 内容创意是否落到可复用的创意原型,而不是照抄样本主题。\n- 视频形态是否明确指向 Content Producer `expert-video` 的某个 workflow,或 main 的某个素材加工技能。\n- Brief 是否只含制作所需信息(不含 DNA 内容),素材是否给了绝对路径与授权说明。\n- 口播类是否附口播文案(或真人口播录音路径);口播子模块未启用时是否避免规定逐句口播。\n- 制作规格(横竖屏、时长带、画面风格、配音音色)是否尊重样本覆盖度;样本不足时是否标注未观测。\n- 关键词是否落到用户可能的提问原句(不只平台标签),并与内容形式匹配。\n- 业务植入是否落到位置 + 载体 + 衔接句(而不是只写「自然植入」),CTA 是否只有一个主行动、句式可执行且未越合规红线。\n- 用户输入是否已转译为具体执行规则。", + "note": "- 是否只用一个 DNA,且作品类型与该 DNA 的 `kind` 一致。\n- 选题、标题与封面图组是否来自 DNA 文档。\n- 正文表达的每条规则是否可从 DNA 文档推导,未使用空泛形容词。\n- 图组数量、构图与视觉风格是否与 DNA 一致;视觉结论是否有图片证据。\n- 业务植入是否落到位置 + 载体 + 衔接句(而不是只写「软性推荐」),CTA 是否每篇只放一个主行动、句式可执行且未越合规红线。\n- 关键词是否落到用户可能的提问原句(不只平台标签),并与内容形式匹配。\n- 用户输入是否已转译为具体执行规则。", } DNA_SUBMODULE_NOTE = "- **口播文案子模块**(`narration-script`):仅口播类视频启用,用于指导 main agent 写同类型视频的口播文案;它不是独立 DNA,未启用时写「未启用」。\n- **账号运营子模块**(`account-bio`、`content-mix-cadence`):只在样本来自用户提供的对标账号(可从账号发布列表批量提取)时填写;结论只写进本 DNA 文档,不进 template;样本不足写「未观测」。" @@ -184,12 +183,6 @@ "tag_count": "话题标签数", } -STOP_TERMS = { - "一个", "我们", "你们", "这里", "不会", "这个", "那个", "什么", "可以", "因为", - "但是", "所以", "还是", "以及", "如果", "他们", "自己", "的时候", "to", "the", - "a", "an", "is", "are", "and", "or", "of", "in", "for", "on", "with", "you", "we", -} - def dimensions_for(kind: str) -> list[dict]: """把某个作品类型的分组配置摊平成带序号的维度列表。""" @@ -231,24 +224,6 @@ def average(values: list[float]) -> float: return rounded(sum(values) / len(values)) if values else 0.0 -def extract_terms(text: str) -> Counter: - terms: Counter = Counter() - for token in tokenize(text): - if re.fullmatch(r"[A-Za-z0-9_]+", token): - term = token.lower() - if term not in STOP_TERMS and len(term) > 1: - terms[term] += 1 - continue - cleaned = "".join(ENGLISH_WORD_RE.sub("", token).split()) - if len(cleaned) == 1: - continue - for start in range(len(cleaned) - 1): - term = cleaned[start : start + 2] - if term not in STOP_TERMS: - terms[term] += 1 - return terms - - def split_title_body(text: str) -> tuple[str, str]: """样本文件约定:首个一级标题行为作品标题,其余为正文(图文正文可含内联 #话题)。""" title = "" @@ -302,7 +277,6 @@ def document_metrics(path: Path, text: str, duration: float = 0.0) -> dict: "emoji_count": emoji_count, "emoji_density_per_100_characters": safe_ratio(emoji_count, character_count, 100), "tag_count": len(TAG_RE.findall(body)), - "terms": dict(extract_terms(text)), } @@ -391,46 +365,12 @@ def build_statistics(reports: list[dict], kind: str) -> dict: "max": rounded(max(value for value, _ in pairs)) if pairs else 0.0, } - term_weights: dict[str, float] = defaultdict(float) - term_counts: dict[str, list[int]] = defaultdict(list) - for report in reports: - for term, count in report["document"]["terms"].items(): - term_weights[term] += report["weight"] - term_counts[term].append(count) - stable_terms = [] - for term, coverage_weight in term_weights.items(): - stable_terms.append( - { - "term": term, - "weighted_coverage": rounded(coverage_weight / total_weight), - "report_count": len(term_counts[term]), - "median_count_per_report": rounded(median(term_counts[term])), - } - ) - stable_terms.sort( - key=lambda item: (item["weighted_coverage"], item["report_count"], item["term"]), - reverse=True, - ) - weights = [report["weight"] for report in reports] return { "report_count": len(reports), "total_weight": total_weight, "weighting": "user-specified" if any(abs(weight - 1) > 1e-9 for weight in weights) else "uniform", "numeric_metrics": numeric_metrics, - "stable_terms": stable_terms[:30], - "weighted_coverage": sorted( - ( - { - "report_id": report["report_id"], - "weight": report["weight"], - "focus": report["focus"], - } - for report in reports - ), - key=lambda item: item["weight"], - reverse=True, - ), } diff --git a/crews/main/skills/expert-xhs/workflows/account-benchmark.md b/crews/main/skills/expert-xhs/workflows/account-benchmark.md index 61637bc4..ddcaf6fe 100644 --- a/crews/main/skills/expert-xhs/workflows/account-benchmark.md +++ b/crews/main/skills/expert-xhs/workflows/account-benchmark.md @@ -123,7 +123,7 @@ xhs/dna/{benchmark-dna-id}/{benchmark-dna-id}.dna.md xhs/dna/{benchmark-dna-id}/{benchmark-dna-id}.template.md ``` -对标 DNA template 用语义段与目标 DNA 一致:图文 = 选题、标题与封面、关键词与用户问题、内容创意与结构、正文表达、图组、互动与标签;视频 = 选题、标题与封面、内容创意、关键词与用户问题、视频形态与制作指向、制作规格、口播文案(可选)。每一部分都要能从对标 DNA 文档推导;账号运营子模块不进 template。 +对标 DNA template 用语义段与目标 DNA 一致:图文 = 选题、标题与封面、关键词与用户问题、内容创意与结构、正文表达、图组、业务植入与 CTA;视频 = 选题、标题与封面、内容创意、关键词与用户问题、业务植入与 CTA、视频形态与制作指向、制作规格、口播文案(可选)。每一部分都要能从对标 DNA 文档推导;账号运营子模块不进 template。 ## Step 5 - 模式分析与差异化(agent 推理) @@ -169,7 +169,7 @@ xhs/dna/{base-dna-id}/{base-dna-id}.template.md 比较必须覆盖两层: -1. **DNA 文档**:逐个比较选题与观看理由、标题与封面、内容创意、匹配的用户问题、正文表达与语气、图组视觉、互动引导与转化,以及账号运营子模块(简介写法、内容形式比例、发布习惯);视频对标另比视频形态与制作指向、制作规格、口播文案子模块。 +1. **DNA 文档**:逐个比较选题与观看理由、标题与封面、内容创意、匹配的用户问题、正文表达与语气、图组视觉、业务植入套路、互动引导与 CTA 套路,以及账号运营子模块(简介写法、内容形式比例、发布习惯);视频对标另比视频形态与制作指向、制作规格、口播文案子模块。 2. **template 语义段**:按作品类型逐项比较(图看看写作段与图组,视频看 Brief 相关段)。 每个维度和模板语义段都输出四类结论: diff --git a/crews/main/skills/expert-xhs/workflows/content-production.md b/crews/main/skills/expert-xhs/workflows/content-production.md index 459dc02b..b4a94420 100644 --- a/crews/main/skills/expert-xhs/workflows/content-production.md +++ b/crews/main/skills/expert-xhs/workflows/content-production.md @@ -56,7 +56,7 @@ xhs/dna/{dna-id}/{dna-id}.dna.md xhs/dna/{dna-id}/{dna-id}.template.md ``` -DNA template 是 main agent 的生产输入模板:**图文 DNA 的 template = 图文写作模板**(选题、标题与封面、关键词与用户问题、内容创意与结构、正文表达、图组、互动与标签);**视频 DNA 的 template = Brief 正文模板 +(可选)口播文案模板**。都不规定创作细节:逐句正文之外的排版参数、镜头表、转场或编码细节归执行方。 +DNA template 是 main agent 的生产输入模板:**图文 DNA 的 template = 图文写作模板**(选题、标题与封面、关键词与用户问题、内容创意与结构、正文表达、图组、业务植入与 CTA);**视频 DNA 的 template = Brief 正文模板 +(可选)口播文案模板**。都不规定创作细节:逐句正文之外的排版参数、镜头表、转场或编码细节归执行方。 ### 2. 读取业务知识 @@ -149,7 +149,7 @@ DNA template 是 main agent 的生产输入模板:**图文 DNA 的 template = 3. 开头必须兑现标题与封面承诺,优先回答用户问题或给出核心结论。 4. 用户必用素材优先进入正文,不得为了形式删除关键事实。 5. 商业承诺、服务范围、价格和案例必须与 `business_knowledge.md` 一致。 -6. 只选择一个平台内动作:评论 / 收藏 / 关注 / 进店 / 咨询。 +6. 业务植入按 DNA 的 `biz-implant` 决定位置、载体与衔接句;CTA 按 `interaction-cta` 只选择一个平台内动作:评论 / 收藏 / 关注 / 进店 / 咨询 / 搜索品牌词。 7. 风格转写只改表达与结构呈现,不改事实含义、数据、案例、引用和结论边界。 8. 同主题改写可借鉴参考结构,文字必须用自己的素材与表达重写,禁止搬运。 @@ -167,6 +167,7 @@ DNA template 是 main agent 的生产输入模板:**图文 DNA 的 template = - 核心传达: - 内容创意:创意原型 + 展开逻辑 + 记忆点 - 关键词与用户问题:主关键词、相关词与长尾句、用户可能的提问(供 CP 理解内容指向,不做 SEO 操作) +- 业务植入与 CTA:植入位置与方式原型 + 内容与业务的衔接句要求 + CTA 主目标与句式(只写要求,不写 DNA 规则原文) - 标题与简介:笔记标题、发布正文、话题标签(main 定稿) - 封面要求:封面主文案 + 视觉方向 - 制作规格:横屏 / 竖屏、时长带、画面风格、配音音色与声音形态、BGM 与音效、字幕 diff --git a/crews/main/skills/expert-xhs/workflows/review.md b/crews/main/skills/expert-xhs/workflows/review.md index c1ac24b9..6b682387 100644 --- a/crews/main/skills/expert-xhs/workflows/review.md +++ b/crews/main/skills/expert-xhs/workflows/review.md @@ -43,10 +43,12 @@ xhs 互动数据来自创作者后台笔记管理页 5 列(`xhs-engagement` |---------|--------------------------|------------------| | 阅读低(推荐/点击瓶颈) | 选题、标题与封面、关键词与用户问题 | topic-angle、title-cover、search-intent | | 阅读正常但收藏低 | 内容创意与结构、正文表达 | content-idea、body-voice | -| 阅读正常但评论低 | 互动与标签、内容创意 | interaction-cta、content-idea | +| 阅读正常但评论低 | 业务植入与 CTA、内容创意 | interaction-cta、biz-implant、content-idea | | 点赞低 | 内容创意、正文表达 | content-idea、body-voice | | 分享低 | 选题、正文表达 | topic-angle、body-voice | -| 关注转化低 | 账号运营子模块、互动与标签 | account-bio、content-mix-cadence、interaction-cta | +| 关注转化低 | 账号运营子模块、业务植入与 CTA | account-bio、content-mix-cadence、interaction-cta | +| 进店 / 咨询 / 私信转化低 | 业务植入与 CTA | biz-implant、interaction-cta | +| 广告感重(收藏高但评论转冷、负面评论) | 业务植入与 CTA、选题 | biz-implant、interaction-cta、topic-angle | | 搜索流量弱 | 关键词与用户问题、标题与封面 | search-intent、title-cover | | 图组跳失 / 图片被吐槽 | 图组、标题与封面 | imageset-visual、title-cover | @@ -57,7 +59,8 @@ xhs 互动数据来自创作者后台笔记管理页 5 列(`xhs-engagement` | 播放低 | 选题、标题与封面、关键词与用户问题 | topic-angle、title-cover、search-intent | | 完播估算低 | 内容创意、制作规格、口播文案 | content-idea、production-spec、narration-script | | 搜索流量弱 | 关键词与用户问题 | search-intent | -| 关注转化低 | 账号运营子模块 | account-bio、content-mix-cadence | +| 关注转化低 | 账号运营子模块、业务植入与 CTA | account-bio、content-mix-cadence、interaction-cta | +| 进店 / 咨询转化低 | 业务植入与 CTA | biz-implant、interaction-cta | ### 平台混杂因素(归因前必排) diff --git a/crews/main/skills/expert-xhs/workflows/style-dna.md b/crews/main/skills/expert-xhs/workflows/style-dna.md index a48db684..28ea35d2 100644 --- a/crews/main/skills/expert-xhs/workflows/style-dna.md +++ b/crews/main/skills/expert-xhs/workflows/style-dna.md @@ -154,7 +154,7 @@ xhs-style-profiler update \ ## 对标接口 -对标样本进入独立 `dna-id`(同样按作品类型分流);比较时输出选题、标题与封面、内容创意、关键词与用户问题、正文表达与图组视觉的差异,以及(账号批量样本才有的)账号运营子模块差异:简介写法、内容形式比例、发布习惯。用户明确采纳后才通过局部融合写进目标 DNA。 +对标样本进入独立 `dna-id`(同样按作品类型分流);比较时输出选题、标题与封面、内容创意、关键词与用户问题、正文表达与图组视觉、业务植入与 CTA 的差异,以及(账号批量样本才有的)账号运营子模块差异:简介写法、内容形式比例、发布习惯。用户明确采纳后才通过局部融合写进目标 DNA。 ## 编排原则 diff --git a/docs/expert-pack-dna-architecture.md b/docs/expert-pack-dna-architecture.md index 082035eb..78ca3039 100644 --- a/docs/expert-pack-dna-architecture.md +++ b/docs/expert-pack-dna-architecture.md @@ -402,15 +402,16 @@ crews//skills/expert-/tools/-style-profiler/ | 平台 | 框架文件 | 维度 | |------|----------|------| -| 抖音 | `video-dna-framework.md`(8 维)+ `note-dna-framework.md`(8 维) | 视频:选题与观看理由、标题与封面、内容创意、视频内容形态与制作指向、制作规格与视听倾向、口播文案子模块(可选)、账号运营子模块(简介写法、内容形式比例与发布习惯);图文:选题、标题与封面图组、内容创意、正文表达与语气、图组视觉、互动引导与转化、账号运营子模块 | -| 小红书 | `note-dna-framework.md`(9 维,默认)+ `video-dna-framework.md`(9 维) | 同上,另加**匹配的用户问题**(`search-intent`)——小红书最大流量池来自搜索,关键词必须落到用户可能的提问原句 | -| 视频号 | `video-dna-framework.md`(8 维,只有视频) | 同抖音视频,其中标题维度是**短标题 + 视频描述**两项(发布页都可填、官方称填短标题有更多流量;管理页不展示短标题,取数与入库只用视频描述) | +| 抖音 | `video-dna-framework.md`(10 维)+ `note-dna-framework.md`(9 维) | 视频:选题与观看理由、标题与封面、内容创意、**业务植入套路**(`biz-implant`)、**互动引导与 CTA 套路**(`interaction-cta`)、视频内容形态与制作指向、制作规格与视听倾向、口播文案子模块(可选)、账号运营子模块(简介写法、内容形式比例与发布习惯);图文:选题、标题与封面图组、内容创意、正文表达与语气、图组视觉、业务植入套路、互动引导与 CTA 套路、账号运营子模块 | +| 小红书 | `note-dna-framework.md`(10 维,默认)+ `video-dna-framework.md`(11 维) | 同上,另加**匹配的用户问题**(`search-intent`)——小红书最大流量池来自搜索,关键词必须落到用户可能的提问原句 | +| 视频号 | `video-dna-framework.md`(10 维,只有视频) | 同抖音视频,其中标题维度是**短标题 + 视频描述**两项(发布页都可填、官方称填短标题有更多流量;管理页不展示短标题,取数与入库只用视频描述) | -三条硬规则: +四条硬规则: 1. **一个 `dna-id` 只承载一种作品类型**(`--kind video|note`),混型 `build` 直接报错;抖音默认 `dna-0` 为视频、图文另建(如 `dna-0-note`),小红书默认 `dna-0` 为图文、视频另建(如 `dna-0-video`)。 -2. **子模块不是独立 DNA**:口播文案子模块(`narration-script`,参考微信的起承转合)只在口播类作品启用,用于指导 main 写同类型视频的口播文案;账号运营子模块(`account-bio`、`content-mix-cadence`)只在样本来自对标账号批量提取时填写,**只写进 DNA 文档、不进 template**。 -3. **视频 DNA 不含创作细节**:不写脚本结构、逐句台词、镜头表、转场与编码参数。视频类 template = **Brief.md 正文模板 + 口播文案模板(可选)**;图文类 template = 图文写作模板。 +2. **业务植入与 CTA 是三平台共有维度**(2026-09-10 补):`biz-implant` 记「营销内容怎么植入」(位置与时机、载体、方式原型、衔接句、密度与占比),`interaction-cta` 记「怎么引导行动」(主目标、位置与时机、句式原文、行动数量、诱因、合规边界);两维都必须给出**位置 + 载体 + 原文摘录**证据,只写「自然植入」「引导关注」不算提取完成。口播类的 `narration-script` 只记「合」的收束方式,CTA 目标与句式归 `interaction-cta`。 +3. **子模块不是独立 DNA**:口播文案子模块(`narration-script`,参考微信的起承转合)只在口播类作品启用,用于指导 main 写同类型视频的口播文案;账号运营子模块(`account-bio`、`content-mix-cadence`)只在样本来自对标账号批量提取时填写,**只写进 DNA 文档、不进 template**。 +4. **视频 DNA 不含创作细节**:不写脚本结构、逐句台词、镜头表、转场与编码参数。视频类 template = **Brief.md 正文模板 + 口播文案模板(可选)**;图文类 template = 图文写作模板。 `video-form`(视频内容形态:口播 / 实拍拼接 / 影视解说+反转植入 / 纯 AIGC 动画 / 创意转场 / 录屏 / 图文卡片)必须聚合成明确的**制作指向**,且只能写真实存在的资源名:Content Producer `expert-video` 的某个 workflow(Reversal Ad / Narration Video / Collage B-roll / 通用阶段链),或 main 的素材加工技能(`video-edit` / `talking-head-cut` / `ui-demo`)。Brief 的 `workflow` 字段据此填写。 From c5b771e2351acd7d7713b1644f350c91ee310285 Mon Sep 17 00:00:00 2001 From: codes-factory-of-bg Date: Thu, 10 Sep 2026 23:18:09 +0800 Subject: [PATCH 12/34] =?UTF-8?q?refactor(content-producer):=20crew=20?= =?UTF-8?q?=E7=BA=A7=E5=90=8E=E6=9C=9F=E8=84=9A=E6=9C=AC=E5=90=88=E5=85=A5?= =?UTF-8?q?=20expert-video=EF=BC=9B=E9=87=8D=E5=86=99=20expert-video=20SKI?= =?UTF-8?q?LL.md=EF=BC=9B=E7=BA=A0=E6=AD=A3=E3=80=8C=E9=80=9A=E7=94=A8?= =?UTF-8?q?=E5=88=B6=E4=BD=9C=E6=B5=81=E7=A8=8B=E3=80=8D=E5=AE=9A=E4=BD=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 一、crews/content-producer/scripts/ 合入 expert-video(回答"为什么不合并":没有外部引用,本该合) - 排查结论:五个后期脚本(normalize / burn-srt / duck / denoise / interp)只被 CP 自己的 三份文档引用(expert-video SKILL.md、video-producer SKILL.md 后期段、narration-video.md), main 侧、install/setup-crew/apply-addons、docker、allowlist 全都没有引用它们。 - 移到 skills/expert-video/tools/video-producer/scripts/,自动获得 wrapper 子命令形态 (`video-producer normalize|burn-srt|duck|denoise|interp`),符合仓规「wrapper 模式, 规避 agent 自行拼路径」;原调用方式是 workspace 根相对路径 `python3 scripts/x.py`, 与注入的 exec 规范(一律绝对路径、禁 cd 前缀)本来就冲突。 - 顺带修掉 crew 级目录的两个结构性问题:setup-crew 对 crews// 是 cp -R 拷贝(技能是软链, 改了即生效;crew 级 scripts 是拷贝,改了要重跑部署才生效),以及同一批脚本被 scripts/README.md + video-producer/SKILL.md + expert-video/SKILL.md 三处重复描述。 现在唯一权威落点是 video-producer/SKILL.md 的「后期处理子命令」段(含调用模板、 干湿分离约定、逐条旁路条件),scripts/README.md 删除。 - 五个脚本 docstring 的 Usage 改为 wrapper 形态;"由 caller(AGENTS.md 工作流)决定"、 "Content Producer 默认工作流"等旧措辞改为「通用制作流程」;顺手修 normalize.py 的 "B 竍" 错字。video-producer.sh help 补上先前漏列的 narration-align / clip-trim / audio-mix / timeline-compose 与五个后期子命令。 二、expert-video/SKILL.md 重写(296 → 217 行) - **纠正「通用制作流程」的定位**(此前理解错了):它不是与类型 workflow 并列的第四条路, 也不是 Brief 未指定类型时的 fallback,而是我做任何视频制作工作都必须遵循的基准准则; reversal-ad / narration-video / collage-broll 只是在这个基准上对特定类型的进一步细化。 原「Workflow 清单(按视频类型选)」表里把"通用制作流程"当成一行选项,已删除该行, 改为新增「这个包怎么读(三层)」:通用制作流程 = 基准 / 类型 workflow = 细化 / 工具说明 = 子命令参数,并写明冲突时以 workflow 为准但闸门与护栏不让步。 - **"本包" 全部改为 "我"**(expert-video SKILL.md 13 处、narration-video 3 处、 reversal-ad 4 处、expert-design 1 处),与 AGENTS.md 的第一人称口径一致。 - 瘦身去重:角色/两种工作模式/交接契约与 AGENTS.md 重复的部分只留视频特有的 (Stage 0 分模式做法、Brief 字段、素材与口播要求、交付三件套);Stage 12 的参数表 删掉(video-producer 工具说明已有),只留八个场景化组合套路;依赖表 11 行压成 公共技能一段 + env 一段;「资源命名约定」四条压成两句。硬规则一条没丢 (两闸门纪律、checkpoint 语义、返工与耗时上限、决策审计链、11 条禁止事项全保留)。 - 补一条容易被绕过的口径:已有素材的轻加工也走通用制作流程,中间阶段按需裁剪, 但 Stage 0 Brief 确认、Stage 13 自检与响度归一化、Stage 14 交付三件套不因活儿小跳过。 三、全仓同步「通用阶段链」这个错误提法 - 三份 DNA 框架的制作指向映射表:「expert-video → 通用阶段链(narrative/motion/montage 档)」 改为「**不指定类型 workflow**:CP 按其通用制作流程做,Stage 1 定档位」; "未写时按通用阶段链自由选择实现"改为明确它不是并列选项也不是 fallback。 - 三份 build_style_profile.py 的 video-form 提取提示同步(原来把"通用阶段链"列为 第四个 workflow 名,会让 DNA 写出错误的制作指向)。 - 三平台 content-production 的 Brief `workflow` 字段说明、三份专家包 SKILL.md 的 分工硬边界、viral-chaser 的制作指向表、CP AGENTS.md 的能力方向路由(新增"视频类铁律" 一句 + 每行改为"通用制作流程 + X 细化")、docs/expert-pack-dna-architecture.md 4.7/4.9(目录树注释、24 → 29 个子命令、manim 删除说明)一并纠正。 自测:video-producer help 与五个后期子命令 --help 经 wrapper 实跑通过;三份 style-profiler report/build/update 全链路重跑正常;bash -n 与 ast.parse 全过;全仓 grep「本包 / 通用阶段链 / 通用链 / crew 级」在 content-producer 与 main 三平台已清零。 注(未处理,需部署动作):~/.openclaw/workspace-content-producer/skills 仍是 09-08 的 四条悬挂软链(collage-broll / design-full / manim-explainer / video-producer), expert-video 与 expert-design 尚未链入,~/.openclaw/bin 下 video-producer / design-full / manim-explainer 也是悬挂——需重跑 ./scripts/setup-crew.sh(会 prune 悬挂链、重建软链与 wrapper);本次遵守"~/.openclaw 只读"未触碰。workspace-content-producer/scripts/ 里的 五个旧拷贝同理,重跑部署后可手工清掉。 --- crews/content-producer/AGENTS.md | 20 +- crews/content-producer/scripts/README.md | 63 ---- .../skills/expert-design/SKILL.md | 2 +- .../skills/expert-video/SKILL.md | 313 +++++++----------- .../tools/video-producer/SKILL.md | 39 ++- .../tools/video-producer}/scripts/burn-srt.py | 8 +- .../tools/video-producer}/scripts/denoise.py | 10 +- .../tools/video-producer}/scripts/duck.py | 8 +- .../tools/video-producer}/scripts/interp.py | 8 +- .../video-producer}/scripts/normalize.py | 12 +- .../scripts/reference-concepts.py | 4 +- .../tools/video-producer/video-producer.sh | 22 +- .../expert-video/workflows/collage-broll.md | 6 +- .../expert-video/workflows/narration-video.md | 12 +- .../expert-video/workflows/reversal-ad.md | 12 +- crews/main/skills/expert-douyin/SKILL.md | 2 +- .../references/video-dna-framework.md | 4 +- .../scripts/build_style_profile.py | 2 +- .../workflows/content-production.md | 2 +- crews/main/skills/expert-wx-channel/SKILL.md | 2 +- .../references/video-dna-framework.md | 4 +- .../scripts/build_style_profile.py | 2 +- .../workflows/content-production.md | 4 +- crews/main/skills/expert-xhs/SKILL.md | 2 +- .../references/video-dna-framework.md | 4 +- .../scripts/build_style_profile.py | 2 +- .../workflows/content-production.md | 4 +- crews/main/skills/viral-chaser/SKILL.md | 2 +- docs/expert-pack-dna-architecture.md | 13 +- 29 files changed, 241 insertions(+), 347 deletions(-) delete mode 100644 crews/content-producer/scripts/README.md rename crews/content-producer/{ => skills/expert-video/tools/video-producer}/scripts/burn-srt.py (94%) rename crews/content-producer/{ => skills/expert-video/tools/video-producer}/scripts/denoise.py (95%) rename crews/content-producer/{ => skills/expert-video/tools/video-producer}/scripts/duck.py (96%) rename crews/content-producer/{ => skills/expert-video/tools/video-producer}/scripts/interp.py (96%) rename crews/content-producer/{ => skills/expert-video/tools/video-producer}/scripts/normalize.py (94%) diff --git a/crews/content-producer/AGENTS.md b/crews/content-producer/AGENTS.md index 28938e3c..46ba9a75 100644 --- a/crews/content-producer/AGENTS.md +++ b/crews/content-producer/AGENTS.md @@ -17,14 +17,16 @@ ## 能力方向路由 -| 入口信号 | 专家包 | Workflow | -|---------|--------|----------| -| Brief 指定 `workflow`(如 `reversal-ad`) | `expert-video` | 按 Brief 指定的 workflow 制作 | -| "从零做视频""出一支完整视频""按这个主题拍片子" | `expert-video` | 通用阶段链 Stage 0→14 | -| 影视解说 / 剧情解说 + 突然反转插入品宣("万万没想到"式) | `expert-video` | Reversal Ad | -| 甲方交付口播文案或真人口播录音,要合成声画 | `expert-video` | Narration Video | -| "把这句口播做成拼贴 B-roll""纸拼贴动画""半调拼贴" | `expert-video` | Collage B-roll | -| 已有素材要剪辑、修整、拼接、配音、烧字幕 | `expert-video` | 通用链的 Stage 12 工具箱 | +**视频类铁律**:只要接的是视频制作活儿,`expert-video` 的**通用制作流程**(Stage 0→14 阶段链 + GATE A/B 两闸门 + 护栏 + 工作区与交付约定)**一律适用**——它是基准准则,不是"没匹配到类型时的备选",也不与类型 workflow 并列。下表匹配到的类型 workflow 只是叠加在基准上的进一步细化。 + +| 入口信号 | 专家包 | 怎么做 | +|---------|--------|--------| +| Brief 指定 `workflow`(如 `reversal-ad`) | `expert-video` | 通用制作流程 + 读 Brief 指定的 `workflows/<值>.md`,按其阶段裁剪执行 | +| 影视解说 / 剧情解说 + 突然反转插入品宣("万万没想到"式) | `expert-video` | 通用制作流程 + Reversal Ad 细化 | +| 甲方交付口播文案或真人口播录音,要合成声画 | `expert-video` | 通用制作流程 + Narration Video 细化 | +| "把这句口播做成拼贴 B-roll""纸拼贴动画""半调拼贴" | `expert-video` | 通用制作流程 + Collage B-roll 细化 | +| "从零做视频""出一支完整视频""按这个主题拍片子"(无匹配类型) | `expert-video` | 只按通用制作流程走,Stage 1 `intent-router` 定档位(narrative / motion / montage) | +| 已有素材要剪辑、修整、拼接、配音、烧字幕 | `expert-video` | 通用制作流程的 Stage 12 工具箱(只做几何级修整) | | "做网页/落地页/APP 界面/品牌视觉体系" | `expert-design` | Web Page / App UI / Brand Visual | ## 交接契约(硬边界) @@ -38,7 +40,7 @@ - **每接到一个活儿先自建工作区**:视频类走 `output_videos//`,平面设计类走 `design_assets/YYYY-MM-DD-<任务名>/`(由 `design-full init` 建)。甲方传入的现成目录只作素材来源,不当自己的工作区。 - **Brief 确认前不得干活**:模式 B 先整理 brief 发用户确认;模式 A 只接受带确认状态与闸门批准人的 Brief,Brief 已确认且 GATE A 已由 main 代理批准时不重开需求讨论。 -- **成品交付前必跑自检**:视频走公共 `video-review` + 响度归一化(-14 LUFS 必跑),平面设计走视觉 review(对照 brief + DESIGN.md)。 +- **成品交付前必跑自检**:视频走公共 `video-review` + 响度归一化(`video-producer normalize`,-14 LUFS 必跑),平面设计走视觉 review(对照 brief + DESIGN.md)。 - **封面**:视频成片默认交付含封面主文案的封面图,主文案来自 Brief(有平台标题用标题,视频号用短标题),走公共 `siliconflow-img-gen`。 - **不许声称没做过的事**:没有 tool result 或产物文件证明,不许声称已生成/已渲染/已改动。 - **平台运营不在 CP**:发布到抖音/视频号/小红书/B站等归 main agent 的各平台专家包,CP 不碰;也不私信用户、不代拟运营话术。 diff --git a/crews/content-producer/scripts/README.md b/crews/content-producer/scripts/README.md deleted file mode 100644 index 2e48a353..00000000 --- a/crews/content-producer/scripts/README.md +++ /dev/null @@ -1,63 +0,0 @@ -# Content Producer 脚本索引 - -五个后期脚本补我们没做的后期环节。**两个必跑、三个可选**——必跑的是发布质量硬伤,可选的是用户要才跑。 - -完整接入契约(落点 / 旁路条件 / 干湿分离)在 `../skills/expert-video/tools/video-producer/SKILL.md` 的「后期脚本」段;本文件只做脚本速查索引,**不重复契约**。 - -## 索引 - -| 脚本 | 用途 | 必跑/可选 | 落点 | -|------|------|---------|------| -| `normalize.py` | ffmpeg loudnorm 双 pass 把成片归一化到 -14 LUFS(抖音/视频号/B站竖屏发布通用标准) | **必跑** | `expert-video` 阶段链 Stage 13c,成片合成后、交付前强制跑 | -| `burn-srt.py` | ffmpeg `subtitles` 滤镜(libass)把 SRT 硬烧进画面,不可关 | 可选 | Brief / 甲方要字幕时跑 | -| `duck.py` | ffmpeg `sidechaincompress` 旁白作 sidechain 触发 BGM 自动压低(threshold=-25dB / ratio=8:1) | 可选 | 甲方要专业混音且可分轨时跑 | -| `denoise.py` | ffmpeg `afftdn`(默认)或 `arnndn`(RNN,要模型文件)给音频去环境噪声 | 可选 | 素材入库后,仅甲方素材音质差时跑(AI 生成视频音轨本来就干净,跳过) | -| `interp.py` | ffmpeg `minterpolate` 补帧到 30/60fps | 可选 | 渲染或拼接前,仅低 fps 源材(如 24fps AI 生成片)补到 30fps 顺滑 | - -## 调用模板 - -每个脚本都支持 `--help` 查完整入参。常用模板: - -```bash -# 响度归一化(必跑) -python3 ./scripts/normalize.py --output -# 默认 target -14 LUFS / true peak -1.5 dB / LRA 11 - -# 字幕硬烧(可选) -python3 ./scripts/burn-srt.py --output -# 默认中文字幕样式 Noto Sans CJK SC 24px,可 --font-name / --font-size / --force-style 覆盖 - -# BGM ducking(可选,需可分轨) -# 模式 1:视频自带 BGM + 外挂旁白 -python3 ./scripts/duck.py --output -# 模式 2:外挂 BGM + 外挂旁白 -python3 ./scripts/duck.py --bgm-source --output - -# 音频降噪(可选,仅素材音质差时) -python3 ./scripts/denoise.py --output -# 默认 afftdn(无外部模型依赖);要更强降噪走 arnndn:--method arnndn --rnn-model - -# 补帧(可选,仅低 fps 源材) -python3 ./scripts/interp.py --target-fps 30 --output -# 默认 minterpolate mode=blend;要更顺走 mode=mci(motion compensated,但慢且可能出鬼影) -``` - -## 干湿分离约定 - -五个都守:输出落 `_<处理名>.mp4`(如 `output_normalized.mp4`、`output_burned.mp4`、`output_ducked.mp4`、`_denoised.mp4`、`_interp.mp4`),**不覆盖输入**。多步串联时下一步以上一步产物为输入(如 ducking 后再 normalize),原产物保留作回退。 - -## 旁路条件速查 - -- `normalize.py`:无声轨 / 音频畸变 → exit 2 报错退回 exportMp4 重生;input_i 已在 ±0.3 LUFS of target → 自动跳过渲染直接拷贝 -- `burn-srt.py`:ffmpeg 不带 libass → exit 1 报错改发外挂 SRT;SRT 不存在 / 格式错 → exit 1 -- `duck.py`:AI 声画同出模式混轨没法分 → 报告用户等决策;视频无声轨且没 `--bgm-source` → exit 1 -- `denoise.py`:AI 生成视频音轨干净 → 跳过;ffmpeg 不带 afftdn/arnndn → exit 1;arnndn 没传 `--rnn-model` → exit 1 -- `interp.py`:源 fps ≥ target fps → 自动跳过拷贝;ffmpeg 不带 minterpolate → exit 1;mci 模式出鬼影 → 退 blend 模式 - -## 借鉴来源(审计用) - -- `normalize.py` ← 响度归一化(必跑步骤) -- `burn-srt.py` ← ffmpeg subtitles 滤镜烧录字幕 -- `duck.py` ← ffmpeg sidechaincompress 旁白触发 BGM 自动压低 -- `denoise.py` ← 用户素材降噪(只在用户素材用,AI 生成不用) -- `interp.py` ← ffmpeg minterpolate 补帧 diff --git a/crews/content-producer/skills/expert-design/SKILL.md b/crews/content-producer/skills/expert-design/SKILL.md index 65d59a69..2a129486 100644 --- a/crews/content-producer/skills/expert-design/SKILL.md +++ b/crews/content-producer/skills/expert-design/SKILL.md @@ -13,7 +13,7 @@ metadata: ## 角色:始终是乙方 -不管任务来自谁,本包都是**乙方(承制方)**:按 Brief 交付设计成品,不自作主张改需求方向,也不替甲方定品牌事实与投放策略。 +不管任务来自谁,我都是**乙方(承制方)**:按 Brief 交付设计成品,不自作主张改需求方向,也不替甲方定品牌事实与投放策略。 - 需求以 **Brief** 为唯一契约;Brief 未写的先问甲方,不脑补品牌色、卖点与合规承诺。 - 设计实现(设计系统选取、token 组织、页面结构、组件写法)是乙方专业范围。 diff --git a/crews/content-producer/skills/expert-video/SKILL.md b/crews/content-producer/skills/expert-video/SKILL.md index 12a9c6f4..c222480f 100644 --- a/crews/content-producer/skills/expert-video/SKILL.md +++ b/crews/content-producer/skills/expert-video/SKILL.md @@ -1,6 +1,6 @@ --- name: expert-video -description: 视频制作专家(乙方)。承接端到端视频制作——口播类、实拍拼接/蒙太奇、影视解说+反转植入(「万万没想到」)、纯 AIGC 动画、纸拼贴 B-roll 等类型,以及已有素材的成片加工,交付成片 + 封面。两种工作模式:作为 main agent 的 subagent 接 Brief,或直接对接用户。甲方只给 Brief 与已有素材绝对路径,工作区、制作方案、分镜与实现全部由本包自行负责;不发布、不做平台运营。 +description: 视频制作专家(乙方)。通用制作流程(Stage 0→14 + 两闸门 + 护栏)是我做任何视频都必须遵循的准则;workflows/ 下的类型文档(Reversal Ad / Narration Video / Collage B-roll)是在它之上针对特定类型的细化。承接端到端视频制作(口播类、实拍拼接 / 蒙太奇、影视解说 + 反转植入、纯 AIGC 动画、纸拼贴 B-roll)与已有素材的成片加工,交付成片 + 封面 + 交付说明。两种工作模式:作为 main agent 的 subagent 接 Brief,或直接对接用户。甲方只给 Brief 与素材绝对路径,工作区、制作方案、分镜与实现由我负责;不发布、不做平台运营。 metadata: openclaw: emoji: 🎬 @@ -13,159 +13,118 @@ metadata: # 视频制作专家(expert-video) -## 角色:始终是乙方 +## 我是乙方 -不管任务来自谁,本包都是**乙方(承制方)**:按 Brief 交付成片与封面,不自作主张改需求,也不替甲方做选题、标题、简介与发布运营。 +不管活儿来自谁,我都是**乙方(承制方)**:按 Brief 交付成片与封面,不自作主张改需求,也不替甲方做选题、标题、简介与发布运营。角色定位、两种工作模式与甲乙方硬边界的完整版在 crew 的 `AGENTS.md`;本文只写视频制作特有的部分。 -- 需求以 **Brief** 为唯一契约。Brief 未写的,先问甲方,不自行脑补品牌事实、授权与承诺。 -- 制作实现(工作区、分镜、素材方案、渲染参数、剪辑手法)是乙方的专业范围,甲方不插手,本包也不向甲方要这些决策。 -- 交付边界:成片 `video.mp4` + 封面 `cover.jpg` + 交付说明 `final-deliver.md`。**不发布到任何平台、不私信用户、不代拟运营话术**(发布用文案由甲方给)。 +- **Brief 是唯一契约**:Brief 没写的先问甲方,不自行脑补品牌事实、授权与承诺。 +- **制作实现归我**:工作区、分镜、素材方案、剪辑手法、渲染参数由我定,不反过来找甲方要这些决策。 +- **交付边界**:成片 `video.mp4` + 封面 `cover.jpg` + 交付说明 `final-deliver.md`,回报三者**绝对路径**。不发布到任何平台、不私信用户、不代拟运营话术、不替甲方把成片拷进平台目录。 -## 两种工作模式 +### Stage 0 按模式分两种做法 -| 模式 | 甲方 | 触发 | Stage 0 做什么 | -|------|------|------|----------------| -| A · Subagent 承制 | main agent | 被 spawn,收到 Brief(+ 已有素材绝对路径) | 读 Brief → 核对字段齐全 → 缺关键字段向 Brief owner 澄清;**不重开需求讨论** | -| B · 直接对接用户 | 用户(已配 channel) | 用户在自己的 channel 里直接提需求 | 用户已给 Brief → 核对确认;用户没给 → 引导讨论并**代用户整理 Brief,发用户确认后才开工** | +| 模式 | 甲方 | Stage 0 我做什么 | +|------|------|------------------| +| A · Subagent 承制 | main agent | 读 Brief → 核对字段齐全 → 缺关键字段向 Brief owner 澄清;**不重开需求讨论** | +| B · 直接对接用户 | 用户(已配 channel) | 用户已给 Brief → 核对确认;没给 → 引导讨论并**代用户整理 `brief.md`,发用户确认后才开工** | -模式 B 的额外要求(用户不一定专业,乙方要替他把需求收敛清楚): +模式 B 的用户不一定专业,我要替他把需求收敛清楚: -1. **先明确 Brief**:至少问清 —— 做什么类型视频、给谁看、要传达什么、时长与横竖屏、有没有现成素材、要不要口播(谁的声音)、什么时候要。整理成 `brief.md` 发用户确认。 -2. **涉及已有素材必须落实位置**:让用户给出素材的**绝对路径**(或明确授权从哪个目录取),逐条 `ls` 确认真实存在、可解码;缺素材就明说缺什么,不许拿"待补"开工。 -3. **口播内容**:口播文案由甲方出(模式 A 是 main agent,模式 B 是用户)。用户只会说大意时,本包可代拟文案,但必须发用户确认后才算定稿。明确是用户真人口播时,必须拿到用户的录音文件(绝对路径)。 -4. 用户提出的模糊想法("做个短片""帮我策划一下")**不算确认**,不得据此调渲染类工具。 +1. **先明确 Brief**:至少问清做什么类型视频、给谁看、要传达什么、时长与横竖屏、有没有现成素材、要不要口播(谁的声音)、什么时候要;整理成 `brief.md` 发用户确认。模糊想法("做个短片""帮我策划一下")**不算确认**,不得据此调渲染类工具。 +2. **素材必须落实位置**:让用户给出**绝对路径**(或明确授权从哪个目录取),逐条 `ls` 确认真实存在、可解码;缺什么明说,不许拿"待补"开工。 +3. **口播内容**:口播文案由甲方出(模式 A 是 main agent,模式 B 是用户)。用户只给大意时我可代拟,但必须发用户确认定稿;明确是用户真人口播时,必须拿到录音文件(绝对路径)。 -## 资源命名约定 +### 甲方交付什么、我交付什么 -- Workflows、Tools 等名称是本技能包内的**逻辑资源名**,不是 Workspace 路径,不要拼成相对路径执行。 -- 技能部署后整个包通过软链进入运行环境;不要假设包内资源被展开到 Workspace 下。 -- 文档中出现的 `output_videos/`、`design_assets/` 才是 Workspace 相对路径,从 Content Producer workspace 根解析。 -- 只有工具清单中列出的 wrapper 名称可以直接作为 shell 命令调用;其余 Tool 名称仅用于定位工具说明。 +| 甲方给我 | 要求 | +|----------|------| +| `brief.md` | 绝对路径。含视频类型 / workflow、主题与观看理由、核心传达、业务植入与 CTA(植入位置与方式、内容与业务的衔接句要求、CTA 主目标与句式)、时长与横竖屏、素材清单、封面要求、交付与验收、闸门批准人。**不含 DNA 信息**(甲方内部资产,我不读也不用) | +| 已有素材 | 绝对路径逐条列出,含来源与授权说明;我只做入库校验与技术处理 | +| 口播文案 / 录音 | 有口播时甲方出具 `voiceover.md`(绝对路径);我不重写策略文案,只做声画实现。真人口播时给录音文件绝对路径 | -## Workflow 清单(按视频类型选) - -Workflow = **某一类型视频怎么做**。制作流程本身是高度程式化、跨类型一致的(见下方「通用制作流程」),类型之间的差别只在叙事套路、素材来源与声画组织方式——那部分写在 workflow 里。 - -| 视频类型 / 入口信号 | Workflow | Brief `workflow` 字段值 | 说明 | -|--------------------|----------|------------------------|------| -| Brief 未指定类型,或"从零做一支视频""按这个主题拍片子" | 通用制作流程(本文下方阶段链) | 省略 / `未指定` | 按 Stage 1 定档位:故事讲述型 / 纯画面动效型 / 蒙太奇剪接型 | -| 影视解说 / 剧情解说 + 突然反转插入品宣("万万没想到"式) | Reversal Ad | `reversal-ad` | 解说正文占大头,反转点后集中植入,给人猝不及防感 | -| 口播类视频(甲方交付口播文案或真人口播录音,要合成声画) | Narration Video | `narration-video` | 文案已定稿不重写,本包负责声画合成、字幕、素材配画面 | -| "把这句口播做成拼贴 B-roll""纸拼贴动画""半调拼贴" | Collage B-roll | `collage-broll` | 一句文稿 → 一个视觉隐喻 → 静帧 → i2v 组装动画(三道闸门) | -| 已有素材要剪辑、修整、拼接、配音、烧字幕 | 通用制作流程的 Stage 12 工具箱 | 省略 | 只做几何级修整,不做语义级高光剪辑(那归 main 的 `video-edit` / `talking-head-cut`) | - -Workflow 文档在包内 `workflows/<字段值>.md`。Brief 指定了 `workflow` 时**必须先读对应文档并直接采用**,不得替换成自创流程;未指定时按通用流程自由选择实现。 - -不适用(交回甲方或转其他专家包): - -- 平面设计 / 网页 / APP 界面 / 品牌视觉 → `expert-design` -- 语义级高光剪辑(去口气词、智能剪重点)→ main 的 `talking-head-cut` / `video-edit` -- 视频下载、爆款拆解、转录抽帧 → main 的 `viral-chaser`(本包不自己下载转写) -- 平台发布与运营 → main 的各平台专家包 - -## 工具清单 +- ✅ 缺字段 → 向 Brief owner 澄清后再开工。 +- ❌ 缺字段 → 自己猜品牌卖点、自己编授权、自己改需求方向。 -零散活儿直接调用,不必走完整 workflow。 +| 我给甲方 | 位置 | +|----------|------| +| 成片 | `/video.mp4`(过 `video-review`,verdict=pass;响度已归一化) | +| 封面 | `/cover.jpg`(含封面主文案,主文案来自 Brief) | +| 交付说明 | `/final-deliver.md`:素材来源与授权、各段实际时长、自检结果、弃用中间产物、fallback 决策、遗留问题 | -| 工具 | 用途 | 命令 | -|------|------|------| -| `video-producer` | 视频制作原子能力集(意图路由、故事/剧本/分镜、素材 slot 与解析、渲染、混音对齐、拼接合成、动效审计、封面) | `video-producer <子命令>` | -| `collage-broll` | 纸拼贴 B-roll 的环境自检与 Gate 3 批量视频生成调度 | `collage-broll <子命令>` | +## 这个包怎么读(三层) -跨领域公共技能:`aigc-video-gen`(视频片段生成 / i2v 首尾帧插值)、`siliconflow-img-gen`(静帧、角色三视图、封面)、`awk-tts`(旁白 TTS,带字级时间戳)、`bgm-library` / `pexels-footage` / `pixabay-footage`(免版税 BGM 与素材)、`video-review`(成片技术自检闸门)、`video-edit subtitles`(烧字幕原子命令)。 +| 层 | 是什么 | 怎么用 | +|----|--------|--------| +| **通用制作流程**(本文下方) | 我做**任何**视频制作工作都必须遵循的准则:Stage 0→14 阶段链、GATE A / GATE B 两闸门、返工与耗时上限、决策审计链、工作区与交付约定 | 永远适用,不因视频类型而跳过或替换 | +| **类型 workflow**(`workflows/<值>.md`) | 在通用制作流程**之上**对某一类视频的进一步细化与明确化:阶段裁剪、叙事套路约束、声音 / 画面规范、验收补充 | Brief 指定 `workflow` 时必读必用,按其细化执行;细化内容与通用流程冲突时以 workflow 为准,但**闸门与护栏不让步** | +| **工具说明**(`tools/<工具>/SKILL.md`) | 每个子命令的入参、产物路径、退出码与旁路条件 | 调用前查;本文不重复参数细节 | -## 交接契约 +> 通用制作流程**不是**与类型 workflow 并列的第四条路,也**不是**"Brief 没指定类型时的 fallback"。它是底座;类型 workflow 只在底座上细化,产出特定类型的视频。 -### 输入(甲方交付) +命名约定:Workflow 名与 Tool 名是包内**逻辑资源名**,不是 Workspace 路径,不要拼成相对路径执行;只有工具清单里列出的 wrapper 名能直接当 shell 命令调用。`output_videos/`、`design_assets/` 才是 Workspace 相对路径(从 Content Producer workspace 根解析)。 -| 项 | 要求 | -|----|------| -| `brief.md` | 绝对路径。含视频类型/workflow、主题与观看理由、核心传达、业务植入与 CTA(植入位置与方式、内容与业务的衔接句要求、CTA 主目标与句式)、时长与横竖屏、素材清单、封面要求、交付与验收、闸门批准人。**不含 DNA 信息**(甲方内部资产,本包不读也不用) | -| 已有素材 | 绝对路径逐条列出,含来源与授权说明;本包只做入库校验与技术处理 | -| 口播文案 | 有口播时由甲方出具(`voiceover.md`,绝对路径)。本包不重写策略文案,只做声画实现 | -| 口播录音 | 明确真人口播时,甲方提供录音文件绝对路径 | +## 类型 workflow(细化层) -- ✅ 缺字段 → 向 Brief owner 澄清后再开工。 -- ❌ 缺字段 → 自己猜品牌卖点、自己编授权、自己改需求方向。 +| 视频类型 / 入口信号 | workflow | Brief `workflow` 值 | 它细化了什么 | +|--------------------|----------|---------------------|--------------| +| 影视解说 / 剧情解说 + 突然反转插入品宣("万万没想到"式) | Reversal Ad | `reversal-ad` | 三段结构占比、反转点落在 55%–76%、四种反转手法、意象桥与钩连句、植入段约束 | +| 口播类(甲方交付口播文案或真人录音,要合成声画) | Narration Video | `narration-video` | 口播稿落稿锁定不重写、按字级时间戳配画面、声音规范与验收清单 | +| "把这句口播做成拼贴 B-roll""纸拼贴动画""半调拼贴" | Collage B-roll | `collage-broll` | 一句文稿 → 一个视觉隐喻 → 静帧 → i2v,三道闸门与 Gate 3 批量调度 | -### 输出(本包交付) +- Brief 指定了 `workflow`:**先读对应文档并直接采用**,不得替换成自创流程。 +- Brief 未指定:仍走通用制作流程,由 Stage 1 `intent-router` 定档位——故事讲述型 narrative / 纯画面动效型 motion / 蒙太奇剪接型 montage。 +- 已有素材只要剪辑、修整、拼接、配音、烧字幕:仍走通用制作流程,中间阶段按实际裁剪,重心落在 Stage 12 工具箱(只做几何级修整;语义级高光剪辑归甲方 main)。**活儿小也不跳过** Stage 0 Brief 确认、Stage 13 自检与响度归一化、Stage 14 交付三件套。 -| 项 | 位置 | -|----|------| -| 成片 | `/video.mp4`(过 `video-review`,verdict=pass) | -| 封面 | `/cover.jpg`(含封面主文案,主文案来自 Brief) | -| 交付说明 | `/final-deliver.md`:素材来源与授权、各段实际时长、自检结果、弃用中间产物、fallback 决策、遗留问题 | +不属于我的活(交回甲方或转其他专家包): -交付时回报**三个文件的绝对路径**。模式 A 回报给 main agent(甲方自行取文件并做后续发布);模式 B 发给用户。本包不替甲方把成片拷进平台目录,也不代为发布。 +- 平面设计 / 网页 / APP 界面 / 品牌视觉 → `expert-design` +- 语义级高光剪辑(去口气词、智能剪重点)→ main 的 `talking-head-cut` / `video-edit` +- 视频下载、爆款拆解、转录抽帧 → main 的 `viral-chaser`(我不自己下载转写) +- 平台发布与运营 → main 的各平台专家包 -## 工作区目录约定 +## 工作区 -**每个活儿自建工作区**,甲方不指定、也不代建(甲方只给 Brief 与素材绝对路径)。落在 Content Producer workspace 的 `output_videos//`;slug 取自 Brief 的视频名或主题英文短横线式,便于与甲方对账。 +**每个活儿自建工作区**,甲方不指定、也不代建。落在 Content Producer workspace 的 `output_videos//`;slug 取自 Brief 的视频名或主题英文短横线式,便于与甲方对账。 ``` output_videos// # ├── brief.md # 甲方交付(拷贝入档)或 Stage 0 与用户定稿 ├── voiceover.md # 甲方交付的口播文案(如有) -├── reference/ # 可选:甲方(多为模式 B 的用户)给的参考拆解报告与差异化概念 -│ ├── reference-report.md -│ └── concepts.md -├── script/ -│ ├── intent.json # Stage 1 -│ ├── story.md # Stage 2 -│ ├── script.md # Stage 3(甲方交付口播时 = 落稿锁定版) -│ ├── self-eval.json # Stage 3b 自评 -│ └── decisions.json # 决策审计链(跨阶段累积) -├── storyboard/ -│ ├── storyboard.json # Stage 4 镜头表 -│ └── shot_decompose.json # Stage 5 每镜首尾帧 + 运动 + variation_type -├── characters/ -│ ├── registry.json # Stage 6 static/dynamic features -│ └── /{front,side,back}.png -├── gates/ -│ ├── gate-a.md # GATE A 文本闸门评审产物(含批准人与批准范围) -│ └── gate-b.md # GATE B 素材闸门评审产物 +├── reference/ # 可选:甲方给的参考拆解报告与差异化概念 +├── script/ # intent.json(1) / story.md(2) / script.md(3) / self-eval.json(3b) / decisions.json(审计链) +├── storyboard/ # storyboard.json(4) / shot_decompose.json(5) +├── characters/ # registry.json(6) + /{front,side,back}.png +├── gates/ # gate-a.md / gate-b.md(含批准人与批准范围) ├── raw_materials/ # 甲方素材入库副本 + 授权记录 -├── slots/ -│ ├── slot-plan.json # Stage 7 -│ ├── asset-resolve.json # Stage 8(含 rejected_picks) -│ ├── slideshow-risk.json # Stage 9a 六维打分 -│ └── delivery-promise.json # Stage 9b 承诺锁定 -├── render/ -│ └── shot-NN/ # Stage 10 每镜渲染产物(first-frame.png / last-frame.png / shot.mp4) -├── audio/ -│ ├── narration.mp3 # 旁白(awk-tts 或甲方录音) -│ ├── narration-segments.json # 字级时间戳 -│ ├── bgm.mp3 -│ └── subtitles.srt -├── artifacts/ # Stage 12 按镜顺序的最终段(01_*.mp4 … NN_*.mp4) -├── video.mp4 # Stage 12 成片 -├── review/ -│ ├── verdict.json # Stage 13a 公共 video-review -│ ├── frames/ -│ └── motion-audit.json # Stage 13b -├── cover.jpg # Stage 14a -└── final-deliver.md # Stage 14b +├── slots/ # slot-plan.json(7) / asset-resolve.json(8) / slideshow-risk.json(9a) / delivery-promise.json(9b) +├── render/shot-NN/ # (10) first-frame.png / last-frame.png / shot.mp4 +├── audio/ # narration.mp3 / narration-segments.json / bgm.mp3 / subtitles.srt +├── artifacts/ # (12) 按镜顺序的最终段 01_*.mp4 … NN_*.mp4 +├── video.mp4 # (12) 成片 +├── review/ # verdict.json(13a) / frames/ / motion-audit.json(13b) +├── cover.jpg # (14a) +└── final-deliver.md # (14b) ``` -Workflow 文档在技能包内,不是项目目录内容;项目目录只放 Brief、素材、脚本、渲染与交付产物。 +workflow 文档在技能包内,不是项目目录内容;项目目录只放 Brief、素材、脚本、渲染与交付产物。 -## 通用制作流程(阶段链 Stage 0→14,两闸门) +## 通用制作流程(Stage 0→14,两闸门) -所有视频类型共用这条链;类型差异由 Workflow 文档补充。每段的子命令是 `video-producer` 工具下的一个独立脚本,按本流程逐个调。**产物文件存在性即 checkpoint**——子命令先查产物文件是否存在,存在则 load 不重生成(允许手改 JSON 后续跑)。 +**我做任何视频都走这条链**;类型 workflow 只在此基础上裁剪与细化。每段的子命令是 `video-producer` 工具下的一个独立脚本,按流程逐个调。 ``` Stage 0 Brief 确认 模式 A:读甲方 Brief,核对字段,缺口向 Brief owner 澄清 模式 B:用户未给 Brief 时引导讨论 → 代拟 brief.md → 发用户确认 (两种模式的 Stage 0 都是"先把 Brief 定下来",无子命令) -Stage 1 intent-router 据 Brief 定档位(workflow 已指定类型时按 workflow 约束校验) - · 故事讲述型(narrative)——重情节、有人物弧光、含旁白;默认 3–5 镜/场 - · 纯画面动效型(motion)——重节奏感/视觉冲击/少对白;默认 5–8 镜快切 - · 蒙太奇剪接型(montage)——重氛围/抽象/纯视觉;默认 4–7 镜无叙事 -Stage 2 story-develop idea → 故事(受众/类型显式复述,100–200 词梗概,人物,分场) +Stage 1 intent-router 定档位(Brief 指定 workflow 时按该 workflow 的约束校验) + narrative 故事讲述型(重情节、有人物弧光、含旁白,默认 3–5 镜/场) + motion 纯画面动效型(重节奏与视觉冲击、少对白,默认 5–8 镜快切) + montage 蒙太奇剪接型(重氛围、抽象、纯视觉,默认 4–7 镜无叙事) +Stage 2 story-develop idea → 故事(受众/类型显式复述、100–200 词梗概、人物、分场) 甲方已交付口播文案时跳过:叙事以口播稿为准,不另起故事 -Stage 3 script-write 故事 → 分场剧本(同时间同地点分一场;可拍化描述;enhancer 润色) +Stage 3 script-write 故事 → 分场剧本(同时间同地点分一场、可拍化描述、enhancer 润色) 甲方已交付口播文案时改为落稿锁定:原样落 script/script.md,不重写策略文案 -Stage 3b script-self-eval 脚本自评 N 维打分,任一维 <3 必返工(落稿锁定时只做检查,不改写) +Stage 3b script-self-eval 脚本自评 N 维打分,任一维 <3 必返工(落稿锁定时只检查不改写) Stage 4 storyboard-build 剧本 → 镜头表(每镜叙事目的/机位复用/位置朝向/不写不可见) Stage 5 shot-decompose 每镜拆首帧静照/尾帧静照/运动描述(variation_type 三档) Stage 6 character-register 角色三视图 front/side/back + static/dynamic features 拆分 @@ -180,73 +139,29 @@ Stage 10 render-shot 按 slot 渲染(AIGC 走 aigc-video-gen i2v 首尾 Stage 11 mix-audio 配音配乐四场景分流(A 人物对话声画同出 / B 旁白一次性 TTS 带字级时间戳 + 对齐 / C BGM 成片后统一生成(优先 bgm-library 免版税曲库,pexels/pixabay 并列;定制风格用 aigc-video-gen music)/ D 甲方口播录音 → ASR 时间戳 → 按时间戳补素材) -Stage 12 assemble 按序拼接成片(原子工具箱,见下节,agent 按场景组合,不写死流程) +Stage 12 assemble 按序拼接成片(原子工具箱,见下节,我按场景组合,不写死流程) Stage 13a video-review 公共 video-review 技术自检(强制闸门,verdict=pass 才继续) Stage 13b motion-audit motion_led 抽查(兑付 delivery-promise) -Stage 13c normalize 响度归一化到 -14 LUFS(必跑,crew 级后期脚本,见「后期脚本」) +Stage 13c normalize 响度归一化到 -14 LUFS(**必跑**:`video-producer normalize`) Stage 14a make-cover 封面(siliconflow-img-gen,必含封面主文案) Stage 14b 交付 回报成片 + 封面 + final-deliver.md 的绝对路径与关键参数 ``` -> Stage 0–6 全是**文本产物**,付费生成前必停——GATE A 落在这条边界上。GATE B 落在素材就绪、pre-compose 闸门通过后,确认渲染前最终计划。指定 Workflow 时以 Workflow 文档的阶段映射为准;若甲方已在 Brief 中代理批准 GATE A,记录批准范围后继续。 - -可选工具:`reference-concepts`(甲方给了参考视频拆解报告时,据报告出 2–3 个差异化概念落 `reference/concepts.md`)。本包不自己下载视频、不自己做转录与抽帧。 - -## Stage 12 工具箱(场景化组合,不写死流程) - -Stage 12 不规定固定顺序——下面的原子工具由 agent 按实际场景组合。 - -| 工具 | 干什么 | 关键参数 | -|------|--------|---------| -| `clip-trim` | 精确切素材段(入点/出点/倍速/前置缓冲,视频和音频分别处理) | `--input/--output/--start/--end/--speed/--sync-audio/--pre-buffer` | -| `audio-mix` | 多轨混音(每轨独立延时和音量) | `--track(可重复)/--delay/--volume/--output/--duration` | -| `timeline-compose` | 按时间轴 JSON 调 clip-trim + audio-mix 合成片段 | ` --timeline timeline.json [--transition ...]` | -| `assemble` | 按序拼接已就绪的段 + 可选转场 + 自动分辨率归一化 + 自动统一音频格式 | ` [--transition hard/fade/dissolve/xfade] [--width 1080] [--fps 30] [--audio-format 24000/mono] [--low-memory] [--preview-duration 30]` | -| `add-silent-audio` | 给无音频视频片段补静音音轨(concat 前置,assemble 内部也自动调) | `--input/--output/--duration/--sample-rate 24000/--channels mono` | -| `scene-compose` | 单 Scene 分段合成(片段+旁白+对白 → 一个 Scene 片段) | ` --scene scene.json [--output scene-01.mp4]` | -| `make-outro` | 片尾制作(形象图+黑边+烧字幕+静音轨 → 标准比例片尾) | ` --image <形象图> --slogan <文本> [--color color.json] [--duration 5] [--width 1080] [--fps 30]` | - -### 场景化组合示例(非强制,按实际判断) - -**场景 A:无旁白直拼** —— 段就绪、无需切素材与混音:`assemble --transition fade` 一把过。 - -**场景 B:有旁白走时间轴** —— 旁白一次性 TTS + `narration-align` 拿字级时间戳后按时间戳对齐各段素材: -1. 据 `narration-segments.json` 各段 start/end 定素材入点/出点,写 `timeline.json` -2. `timeline-compose --timeline timeline.json`(内部调 clip-trim 切段 + audio-mix 叠旁白) -3. 全片 BGM 走 `timeline.json` 的 `audio_globals` 混入 - -**场景 C:分段先合再合(scene-compose 两阶段)** —— 长片或某些段需独立预合: -1. 写 `scene-01.json`(clips + narration + dialogue)→ `scene-compose --scene scene-01.json --output scene-01.mp4` -2. 同样出 `scene-02.mp4` -3. 两个 scene 当段素材 → `assemble --source-dir scenes --transition fade` - -**场景 D:素材尺寸不一** —— AIGC 720x1280、录屏 1080x2384、片尾 784x1176 混拼:`assemble --width 1080 --fps 30` 归一化后再 concat。 +- **产物文件存在性即 checkpoint**:子命令先查产物文件是否存在,存在则 load 不重生成(允许手改 JSON 后续跑);要改哪段就重跑对应子命令,未改的不会重生成。 +- Stage 0–6 全是**文本产物**,付费生成前必停——GATE A 落在这条边界上;GATE B 落在素材就绪、pre-compose 闸门通过后,确认渲染前最终计划。 +- 类型 workflow 指定时,阶段裁剪以该 workflow 文档为准;**闸门位置与"停下发甲方"的纪律不变**。甲方已在 Brief 中代理批准某道闸门时,把批准范围落 `gates/` 后继续。 +- 可选工具 `reference-concepts`:甲方给了参考视频拆解报告时,据报告出 2–3 个差异化概念落 `reference/concepts.md`。 -**场景 E:精确调速某段** —— `clip-trim --input <段> --output <快放段> --speed 2 --sync-audio`,快放段当段素材再拼。 - -**场景 F:低内存机器** —— `assemble --transition fade --low-memory`(preset=ultrafast、crf=28),避免 x264 缓冲爆内存。 - -**场景 G:先试听再合成** —— `assemble --preview-duration 30`,成片照常落,额外产 `video-preview.mp4`。 - -**场景 H:AIGC 无音频段混拼 + 旁白切段吞首字** —— `assemble` 已自动统一音频格式(默认 24000/mono,无音频段补静音)+ 不传 `--width/--fps` 时自动探测并统一到最低公共规格;旁白切段走 `clip-trim --pre-buffer 0.5`。 - -## 工具调用 - -- `video-producer <子命令>`:视频制作原子能力(意图路由、故事/剧本/分镜、素材 slot 与解析、渲染、混音对齐、拼接合成、动效审计、封面)。**完整子命令的入参、产物路径与退出码见 `video-producer` 工具说明**,不在本文重复。 -- `collage-broll check-setup` / `collage-broll gate3 --batch [--dry-run]`:纸拼贴 B-roll 的环境自检与 Gate 3 批量 i2v 调度(0 全通 / 1 参数错 / 2 部分失败,只重跑失败条目)。 -- 后期脚本(crew 级,Workspace `scripts/`):`normalize.py`(**必跑**,-14 LUFS)、`burn-srt.py`、`duck.py`、`denoise.py`、`interp.py`(后四个按 Brief 与素材状况可选)。从 workspace 根 `python3 scripts/.py` 调用,全部干湿分离不覆盖输入;落点与旁路条件见 `video-producer` 工具说明。 - -## 强制闸门与护栏 +## 闸门与护栏 ### GATE A(Stage 6 后):文本闸门 -文本产物全齐(脚本+分镜+机位+角色),**停下发甲方审**: +文本产物全齐(脚本 + 分镜 + 机位 + 角色),**停下发甲方审**: -- 呈交摘要:档位或 workflow、场次数、镜数、角色数、关键决策(路径/模型/风格选择的备选+置信度+理由) +- 呈交摘要:档位或 workflow、场次数、镜数、角色数、关键决策(路径 / 模型 / 风格选择的备选 + 置信度 + 理由) - **结束本轮回复**,不许在同条回复里进 Stage 7 - 批准人是 Brief owner(模式 A = main agent,模式 B = 用户);甲方已在 Brief 中代理批准时,把批准范围落 `gates/gate-a.md` 后继续 - 批准是**逐闸门的**——早先的一句"你继续"不覆盖本闸门 -- 要改哪段就重跑对应子命令(产物文件存在性即 checkpoint,不会重生成未改的) ### GATE B(Stage 9 后):素材闸门 @@ -254,43 +169,49 @@ Stage 12 不规定固定顺序——下面的原子工具由 agent 按实际场 - 呈交:slot 总数、素材就绪率、slideshow_risk 六维分与 verdict、delivery_promise 八类与 motion_ratio 预估、素材 contact sheet - 授权与来源记录必须一并呈交 -- 同 GATE A 收尾纪律 +- 收尾纪律同 GATE A(呈交后结束本轮回复,等批准) -### 返工与耗时上限 +### 返工、耗时与审计 - 每阶段最多返工 **3 次**;全片最多 **3 次** send-back - 每阶段 wall-time 默认上限 **20 分钟**——卡住要报,不要反复撞 - 技术故障(缺 key、依赖缺失、渲染报错)按 Dispatch Protocol spawn IT engineer,不静默卡死 +- **决策审计链**:每个选择(路径 / 模型 / 风格 / 音色 / 任何 fallback)记 `备选 + 置信度 + 理由`,跨阶段累积进 `script/decisions.json` + +## Stage 12 工具箱(场景化组合,不写死顺序) -### 决策审计链 +原子子命令(`clip-trim` / `audio-mix` / `timeline-compose` / `scene-compose` / `assemble` / `add-silent-audio` / `make-outro`)的入参与产物见 `video-producer` 工具说明。下面只给组合套路: -每个选择(路径/模型/风格/音色/任何 fallback)记 `备选 + 置信度 + 理由`,跨阶段累积进 `script/decisions.json`。 +- **无旁白直拼**:段就绪、无需切素材与混音 → `assemble --transition fade` 一把过。 +- **有旁白走时间轴**:旁白一次性 TTS + `narration-align` 拿字级时间戳 → 据各段 start/end 定素材入点出点写 `timeline.json` → `timeline-compose`(内部调 clip-trim 切段 + audio-mix 叠旁白);全片 BGM 走 `timeline.json` 的 `audio_globals` 混入。 +- **分段先合再合**:长片或某些段需独立预合 → 写 `scene-01.json`(clips + narration + dialogue)→ `scene-compose` 出 `scene-01.mp4`,同法出 `scene-02.mp4` → 两个 scene 当段素材 `assemble --source-dir scenes --transition fade`。 +- **素材尺寸不一**(AIGC 720x1280 / 录屏 1080x2384 / 片尾 784x1176 混拼):`assemble --width 1080 --fps 30` 归一化后再 concat。 +- **精确调速某段**:`clip-trim --speed 2 --sync-audio`,快放段当段素材再拼。 +- **低内存机器**:`assemble --low-memory`(preset=ultrafast、crf=28),避免 x264 缓冲爆内存。 +- **先试听再合成**:`assemble --preview-duration 30`,成片照常落,额外产 `video-preview.mp4`。 +- **AIGC 无音频段混拼 + 旁白切段吞首字**:`assemble` 已自动统一音频格式(默认 24000/mono,无音频段补静音)、不传 `--width/--fps` 时自动统一到最低公共规格;旁白切段走 `clip-trim --pre-buffer 0.5`。 -## 依赖 +## 工具与依赖 -| 依赖 | 来源 | 用在哪 | +| 工具 | 用途 | 命令 | |------|------|------| -| python3 / ffmpeg / ffprobe | 系统 | 各阶段脚本 | -| 公共 `aigc-video-gen` | skills/ | Stage 8/10 视频片段生成(百炼/火山声画同出,i2v 首尾帧插值) | -| 公共 `siliconflow-img-gen` | skills/ | Stage 6 角色三视图 / Stage 10 静帧 / Stage 14a 封面 | -| 公共 `awk-tts` | skills/ | Stage 11B 旁白一次性 TTS(OpenClaw 内置 TTS 优先 → awk-tts fallback;`--enable-subtitle` 让火山流式 HTTP 原生返回字级时间戳,落 `narration.subtitle.json`) | -| 火山 ASR 凭据 `VOLC_ASR_*` | 实例 env | Stage 11b narration-align 回退路径 + Stage 11D 甲方口播录音转写拿时间戳(旧控制台双头 `VOLC_ASR_APP_ID`+`VOLC_ASR_ACCESS_KEY`,或新控制台单头 `VOLC_ASR_APP_KEY`) | -| 公共 `pexels-footage` / `pixabay-footage` | skills/ | Stage 8 素材补充 / Stage 11C BGM 搜 | -| 公共 `bgm-library` | skills/ | Stage 11C BGM(ccMixter 免版税 + 自动 TASL 署名,商用安全,优先用) | -| 公共 `video-review` | skills/ | Stage 13a 成片技术自检闸门 | -| `video-edit subtitles` | main crew 暴露的 wrapper 原子 | 需要烧字幕时使用;不可用时向 Brief owner 报工具缺口,不手写 ffmpeg | -| `requests` | 仓根 requirements.txt | 各脚本 HTTP 调用 | +| `video-producer` | 阶段链全部原子能力(意图路由、故事 / 剧本 / 分镜、素材 slot 与解析、渲染、混音对齐、拼接合成、动效审计、封面)+ 后期处理(`normalize` **必跑**、`burn-srt` / `duck` / `denoise` / `interp` 可选,全部干湿分离不覆盖输入) | `video-producer <子命令>`;`video-producer help` 列全量 | +| `collage-broll` | 纸拼贴 B-roll 的环境自检与 Gate 3 批量 i2v 调度(0 全通 / 1 参数错 / 2 部分失败,只重跑失败条目) | `collage-broll check-setup` / `collage-broll gate3 --batch [--dry-run]` | + +跨领域公共技能:`aigc-video-gen`(视频片段生成 / i2v 首尾帧插值,Stage 8/10;输出路径须落在 `output_videos/` 下,调用时 workdir 是 Content Producer workspace 根)、`siliconflow-img-gen`(静帧、角色三视图、封面,Stage 6/10/14a)、`awk-tts`(旁白 TTS,带字级时间戳,Stage 11B;`--enable-subtitle` 让火山流式 HTTP 原生返回时间戳)、`bgm-library`(ccMixter 免版税 + 自动 TASL 署名,商用安全,Stage 11C 优先)、`pexels-footage` / `pixabay-footage`(免版税素材与 BGM 搜索)、`video-review`(成片技术自检闸门,Stage 13a)、`video-edit subtitles`(main crew 暴露的烧字幕原子;不可用时向 Brief owner 报工具缺口,不手写 ffmpeg)。 + +env 依赖:`AWK_API_KEY`(静帧 / 视频生成)、`VOLC_ASR_*`(`narration-align` 回退路径与甲方口播录音转写;旧控制台双头 `VOLC_ASR_APP_ID` + `VOLC_ASR_ACCESS_KEY`,或新控制台单头 `VOLC_ASR_APP_KEY`)。缺 env 时子命令 exit 2,补齐属 IT engineer 职责,不要静默降级。Python 依赖 `requests` 在仓根 `requirements.txt`。机器资源约束(线程数、分辨率上限、低载编码)读本 workspace `MEMORY.md` 或 Brief 的环境约束,不写死在技能包里。 ## 禁止事项(强制) -- **禁止跳过 GATE A/B 交付**:两闸门是流程的一部分,呈交摘要后必须结束本轮回复等甲方批 -- **禁止跳过 video-review 与响度归一化交付**:Stage 13a verdict=pass、Stage 13c 归一化已跑,才进 Stage 14 -- **禁止声称没做过的事**:没有 tool result 或产物文件证明,不许声称已渲染/已生成/已改动 -- **禁止替甲方做需求决策**:选题方向、品牌事实、卖点承诺、发布文案不由本包定;Brief 没写就问 -- **禁止让甲方建工作区**:工作区自建;也不要把中间产物写进甲方(main / 用户)的目录 -- **禁止把模糊想法擅自扩成多场多镜**:默认 1 场 3–5 镜,甲方要扩才扩 -- **禁止直接写 ffmpeg 命令**:所有 ffmpeg 调用走 `video-producer` / `collage-broll` 子命令、crew 级后期脚本或公共技能子命令;唯一例外是 Workflow 里给出的既定 ffmpeg 模板(如 Collage B-roll 的首尾帧处理与 contact sheet 拼图),照抄执行不自创 -- **禁止自己做视频下载/转写/抽帧**:那是 main 的 `viral-chaser` 的活 -- **禁止引入 CLIP / torch 系本地模型**:素材匹配走 Fast path 人核缩略图 -- **禁止扩充图库源**:保 Pexels + Pixabay 两源 -- **禁止批量生成撞运气**:逐条精做 +- **禁止跳过 GATE A / GATE B 交付**:两闸门是流程的一部分,呈交摘要后必须结束本轮回复等甲方批。 +- **禁止跳过 `video-review` 与响度归一化交付**:Stage 13a verdict=pass、Stage 13c `normalize` 已跑,才进 Stage 14。 +- **禁止声称没做过的事**:没有 tool result 或产物文件证明,不许声称已渲染 / 已生成 / 已改动。 +- **禁止替甲方做需求决策**:选题方向、品牌事实、卖点承诺、业务植入与 CTA 口径、发布文案不由我定;Brief 没写就问。 +- **禁止让甲方建工作区**:工作区自建;也不要把中间产物写进甲方(main / 用户)的目录。 +- **禁止把模糊想法擅自扩成多场多镜**:默认 1 场 3–5 镜,甲方要扩才扩。 +- **禁止直接写 ffmpeg 命令**:所有 ffmpeg 调用走 `video-producer` / `collage-broll` 子命令或公共技能子命令;唯一例外是 workflow 文档里给出的既定 ffmpeg 模板(如 Collage B-roll 的首尾帧处理与 contact sheet 拼图),照抄执行不自创。 +- **禁止自己做视频下载 / 转写 / 抽帧**:那是 main 的 `viral-chaser` 的活。 +- **禁止引入 CLIP / torch 系本地模型**:素材匹配走 Fast path 人核缩略图。 +- **禁止扩充图库源**:保 Pexels + Pixabay 两源。 +- **禁止批量生成撞运气**:逐条精做。 diff --git a/crews/content-producer/skills/expert-video/tools/video-producer/SKILL.md b/crews/content-producer/skills/expert-video/tools/video-producer/SKILL.md index 079e4fcd..c633ef23 100644 --- a/crews/content-producer/skills/expert-video/tools/video-producer/SKILL.md +++ b/crews/content-producer/skills/expert-video/tools/video-producer/SKILL.md @@ -61,16 +61,35 @@ metadata: - **env 依赖**:`AWK_API_KEY`(静帧/视频生成)、`VOLC_ASR_*`(narration-align 回退与口播录音转写)。缺 env 时子命令 exit 2,补齐属 IT engineer 职责,不要静默降级。 - **闸门不是子命令**:GATE A / GATE B 由 agent 按包内 SKILL.md 执行(呈交摘要 → 结束本轮回复 → 等甲方逐闸门批准)。 -## 后期脚本(crew 级,Workspace `scripts/`) +## 后期处理子命令(Stage 13c 与可选后期) -以下五个不在本工具 wrapper 内,从 Content Producer workspace 根按 `python3 scripts/.py` 调用;全部干湿分离(输出落 `_<处理名>.mp4`,不覆盖输入)。 +同在 `video-producer` wrapper 下,调用方式与其他子命令一致:`video-producer <子命令> [参数...]`,每个都支持 `--help` 查完整入参。 -| 脚本 | 用途 | 必跑/可选 | 落点 | -|------|------|----------|------| -| `normalize.py` | ffmpeg loudnorm 双 pass 归一化到 -14 LUFS(抖音/视频号/B站竖屏通用) | **必跑** | 成片合成后、自检与交付前 | -| `burn-srt.py` | libass 把 SRT 硬烧进画面 | 可选(Brief 或甲方要字幕时) | 归一化前后均可,串联时以上一步产物为输入 | -| `duck.py` | sidechaincompress 让旁白触发 BGM 自动压低 | 可选(要专业混音且可分轨时) | 混音阶段 | -| `denoise.py` | afftdn(默认)/ arnndn 去环境噪声 | 可选(仅甲方素材音质差时;AIGC 音轨干净跳过) | 素材入库后 | -| `interp.py` | minterpolate 补帧到 30/60fps | 可选(仅低 fps 源材) | 渲染或拼接前 | +| 子命令 | 用途 | 必跑/可选 | 落点 | +|--------|------|----------|------| +| `normalize` | ffmpeg loudnorm 双 pass 归一化到 -14 LUFS(抖音 / 视频号 / B站竖屏发布通用标准) | **必跑** | 阶段链 Stage 13c:成片合成后、自检与交付前强制跑 | +| `burn-srt` | libass 把 SRT 硬烧进画面(烧录后不可关) | 可选(Brief 或甲方要字幕时) | 归一化前后均可;串联时以上一步产物为输入 | +| `duck` | sidechaincompress 让旁白作 sidechain 触发 BGM 自动压低(默认 threshold -25dB / ratio 8:1) | 可选(要专业混音且可分轨时) | 混音阶段 | +| `denoise` | afftdn(默认)/ arnndn(RNN,要模型文件)去环境噪声 | 可选(仅甲方素材音质差时;AIGC 音轨本来就干净,跳过) | 素材入库后 | +| `interp` | minterpolate 补帧到 30/60fps | 可选(仅低 fps 源材,如 24fps AIGC 片) | 渲染或拼接前 | -旁路条件:`normalize.py` 无声轨/音频畸变 → exit 2 退回重生,已在 ±0.3 LUFS 内 → 自动跳过;`burn-srt.py` ffmpeg 无 libass → exit 1 改发外挂 SRT;`duck.py` 声画同出混轨不可分 → 报甲方决策;`interp.py` 源 fps ≥ 目标 → 自动跳过拷贝。 +**干湿分离(五个都守)**:输出落 `_<处理名>.mp4`(`_normalized` / `_burned` / `_ducked` / `_denoised` / `_interp`),不覆盖输入;多步串联时下一步以上一步产物为输入(如 ducking 后再 normalize),原产物保留作回退。 + +常用调用: + +```bash +video-producer normalize --output # 默认 -14 LUFS / true peak -1.5 dB / LRA 11 +video-producer burn-srt --output # 默认 Noto Sans CJK SC 24px,可 --font-name/--font-size/--force-style +video-producer duck --output # 视频自带 BGM +video-producer duck --bgm-source --output # 外挂 BGM +video-producer denoise --output # 更强降噪:--method arnndn --rnn-model +video-producer interp --target-fps 30 --output # 更顺但慢:--mode mci(高运动易出鬼影) +``` + +旁路条件(不满足就报错退出,不静默降级): + +- `normalize`:无声轨 / 音频畸变 → exit 2,退回 Stage 12 重生;input_i 已在 target ±0.3 LUFS 内 → 自动跳过渲染直接拷贝 +- `burn-srt`:ffmpeg 不带 libass → exit 1,改发外挂 SRT;SRT 不存在或格式错 → exit 1 +- `duck`:AI 声画同出模式混轨不可分 → 报甲方决策;视频无声轨且没传 `--bgm-source` → exit 1 +- `denoise`:ffmpeg 不带 afftdn/arnndn → exit 1;`--method arnndn` 没传 `--rnn-model` → exit 1 +- `interp`:源 fps ≥ 目标 fps → 自动跳过拷贝;ffmpeg 不带 minterpolate → exit 1;mci 出鬼影 → 退 blend diff --git a/crews/content-producer/scripts/burn-srt.py b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/burn-srt.py similarity index 94% rename from crews/content-producer/scripts/burn-srt.py rename to crews/content-producer/skills/expert-video/tools/video-producer/scripts/burn-srt.py index f3fc0913..387aa73c 100644 --- a/crews/content-producer/scripts/burn-srt.py +++ b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/burn-srt.py @@ -5,7 +5,7 @@ 平台播放器可开关)不同——硬烧适合"平台不支持外挂字幕"或"想保证画面字 一定显示"的场景。 -⚠️ 可选步骤,不是必跑。Content Producer 的 AGENTS.md 工作流默认不烧字幕 +⚠️ 可选步骤,不是必跑。通用制作流程默认不烧字幕 (assemble.py / exportMp4 都不烧);**仅当用户明确说"要字幕"/"烧字幕"/ "hardcode subtitles"时才跑**。 @@ -18,9 +18,9 @@ - 字幕样式由 SRT 内 cue style 或 force_style 覆盖,本脚本默认给一套可读样式 Usage: - python3 ./scripts/burn-srt.py - python3 ./scripts/burn-srt.py --output - python3 ./scripts/burn-srt.py --font-name "Noto Sans CJK SC" --font-size 24 + video-producer burn-srt + video-producer burn-srt --output + video-producer burn-srt --font-name "Noto Sans CJK SC" --font-size 24 Exit codes: 0 ok,字幕烧完 diff --git a/crews/content-producer/scripts/denoise.py b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/denoise.py similarity index 95% rename from crews/content-producer/scripts/denoise.py rename to crews/content-producer/skills/expert-video/tools/video-producer/scripts/denoise.py index 43e0692f..07d45938 100644 --- a/crews/content-producer/scripts/denoise.py +++ b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/denoise.py @@ -5,7 +5,7 @@ 只在用户素材音质差(环境噪声大、空调嗡、键盘吱)时用——AI 生成视频的音轨 是干净的,不需要降噪。 -⚠️ 可选步骤,不是必跑。Content Producer 默认工作流不做降噪处理。 +⚠️ 可选步骤,不是必跑。通用制作流程默认不做降噪处理。 **仅当用户素材音质明显差**(用户抱怨"听不清"/"有杂音"/"噪音大", 或 review.py 报噪声指标异常)时才跑。 @@ -21,10 +21,10 @@ 干湿分离:输出 `_denoised.mp4`,不覆盖输入。 Usage: - python3 ./scripts/denoise.py - python3 ./scripts/denoise.py --output - python3 ./scripts/denoise.py --method arnndn --rnn-model /path/to/model.rnn - python3 ./scripts/denoise.py --noise-floor -40 --nr 12 + video-producer denoise + video-producer denoise --output + video-producer denoise --method arnndn --rnn-model /path/to/model.rnn + video-producer denoise --noise-floor -40 --nr 12 Exit codes: 0 ok,降噪完成 diff --git a/crews/content-producer/scripts/duck.py b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/duck.py similarity index 96% rename from crews/content-producer/scripts/duck.py rename to crews/content-producer/skills/expert-video/tools/video-producer/scripts/duck.py index 36c39501..80f0fd43 100644 --- a/crews/content-producer/scripts/duck.py +++ b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/duck.py @@ -4,7 +4,7 @@ 把 BGM 轨在旁白/对话出现时自动压低,旁白停了再放开——专业混音的标配。 只在声画同出模式(gen.py 出的片旁白+BGM 同轨)且用户要专业混音时用。 -⚠️ 可选步骤,不是必跑。Content Producer 默认工作流不做混音处理—— +⚠️ 可选步骤,不是必跑。通用制作流程默认不做混音处理—— assemble.py / normalize.py 都只碰整体响度,不动轨间电平。 **仅当用户明确说"要混音"/"做 ducking"/"BGM 压旁白"/"professional mix"时才跑**。 @@ -26,9 +26,9 @@ - attack/release 不能太短,短了BGM抖;不能太长,长了旁白起了 BGM 没压下去 Usage: - python3 ./scripts/duck.py --bgm-track audio:0 - python3 ./scripts/duck.py --bgm-source bgm.mp3 --output mixed.mp4 - python3 ./scripts/duck.py --threshold -25 --ratio 8 + video-producer duck --bgm-track audio:0 + video-producer duck --bgm-source bgm.mp3 --output mixed.mp4 + video-producer duck --threshold -25 --ratio 8 Exit codes: 0 ok,ducking 完成 diff --git a/crews/content-producer/scripts/interp.py b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/interp.py similarity index 96% rename from crews/content-producer/scripts/interp.py rename to crews/content-producer/skills/expert-video/tools/video-producer/scripts/interp.py index d3dc1f0f..81cd671d 100644 --- a/crews/content-producer/scripts/interp.py +++ b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/interp.py @@ -5,7 +5,7 @@ 15fps 用户素材)补到 30fps 顺滑——发布平台播放器默认 30fps 起,低于这 画面会卡。 -⚠️ 可选步骤,不是必跑。Content Producer 默认工作流不动 fps。 +⚠️ 可选步骤,不是必跑。通用制作流程默认不动 fps。 **仅当源 fps < target fps** 且用户要"补帧"/"顺滑"/"提升帧率"时才跑。 minterpolate mode 怎么选: @@ -17,9 +17,9 @@ 干湿分离:输出 `_interp.mp4`,不覆盖输入。 Usage: - python3 ./scripts/interp.py - python3 ./scripts/interp.py --target-fps 30 --output - python3 ./scripts/interp.py --target-fps 60 --mode mci + video-producer interp + video-producer interp --target-fps 30 --output + video-producer interp --target-fps 60 --mode mci Exit codes: 0 ok,补帧完成(含源 fps ≥ target fps 自动跳过拷贝的 exit 0) diff --git a/crews/content-producer/scripts/normalize.py b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/normalize.py similarity index 94% rename from crews/content-producer/scripts/normalize.py rename to crews/content-producer/skills/expert-video/tools/video-producer/scripts/normalize.py index 698f7e1e..06365ba7 100644 --- a/crews/content-producer/scripts/normalize.py +++ b/crews/content-producer/skills/expert-video/tools/video-producer/scripts/normalize.py @@ -1,12 +1,12 @@ #!/usr/bin/env python3 """Loudness normalization — 发布平台通用响度归一化。 -把成片音频响度归一化到 -14 LUFS(短视频平台通用标准:抖音/视频号/B 竍竖屏通用)。 +把成片音频响度归一化到 -14 LUFS(短视频平台通用标准:抖音/视频号/B 站竖屏通用)。 跑在合成后、自检/交付前。这条是必跑步骤——但脚本 -本身尊重 --skip 时跳过,由 caller(AGENTS.md 工作流)决定是否强制。 +本身尊重 --skip 时跳过;是否强制由 caller 决定(通用制作流程 Stage 13c 规定必跑)。 为什么 -14 LUFS: -- 抖音/视频号/B 竍竖屏发布通用标准,与平台播放器电平匹配,避免"在我机 sound bar +- 抖音/视频号/B 站竖屏发布通用标准,与平台播放器电平匹配,避免"在我机 sound bar 听着正"但"在手机刷到时偏轻/偏响" - industry de-facto for short-form video @@ -18,9 +18,9 @@ caller 决定是 rename 替换还是双轨保留。 Usage: - python3 ./scripts/normalize.py - python3 ./scripts/normalize.py --output - python3 ./scripts/normalize.py + video-producer normalize --output + video-producer normalize /)+ 参考拆解报告路径 出:project_dir/reference/concepts.md(2–3 差异化概念 + 成本 + 备选路径) -无报告则跳过(本脚本不报错退出),agent 直接按通用阶段链推进。 +无报告则跳过(本脚本不报错退出),agent 直接按通用制作流程推进。 """ import argparse @@ -79,7 +79,7 @@ def main() -> None: concepts_path.write_text(stub, encoding="utf-8") print(f"[done] 报告已存档:{archived}") print(f"[stub] concepts.md 模板已落:{concepts_path}") - print(f"[next] agent 据报告填概念 → 呈交甲方选定 → 回通用阶段链(story-develop)") + print(f"[next] agent 据报告填概念 → 呈交甲方选定 → 回通用制作流程(story-develop)") if __name__ == "__main__": diff --git a/crews/content-producer/skills/expert-video/tools/video-producer/video-producer.sh b/crews/content-producer/skills/expert-video/tools/video-producer/video-producer.sh index 8069219e..cb0dbc88 100755 --- a/crews/content-producer/skills/expert-video/tools/video-producer/video-producer.sh +++ b/crews/content-producer/skills/expert-video/tools/video-producer/video-producer.sh @@ -22,8 +22,9 @@ video-producer — 视频制作原子能力(wrapper,expert-video 包内工 video-producer help 列可用子命令 流程: - Brief 指定 workflow 时,先读 expert-video 包内 workflows/.md,再按其阶段裁剪调用下列子命令。 - 未指定 workflow 时走 expert-video SKILL.md 的通用阶段链 Stage 0→14。 + 通用制作流程(expert-video SKILL.md 的 Stage 0→14 + 两闸门)是做**任何**视频都要遵循的基准, + 不是"没指定类型时的备选"。Brief 指定 workflow 时,先读包内 workflows/.md, + 按其阶段裁剪调用下列子命令;未指定时只按通用制作流程走,由 intent-router 定档位。 子命令(按阶段序): intent-router Stage 1 意图路由 → 三档脚本模板(故事讲述型/纯画面动效型/蒙太奇剪接型) @@ -40,12 +41,23 @@ video-producer — 视频制作原子能力(wrapper,expert-video 包内工 delivery-promise-lock Stage 9 交付承诺八类锁定 render-shot Stage 10 按 slot 渲染(AIGC i2v / 静图) mix-audio Stage 11 旁白(awk-tts)+ BGM + 字幕 - assemble Stage 12 按镜顺序拼接成片 + 转场 - add-silent-audio 给无音频的视频片段补静音音轨(concat 前置) + narration-align Stage 11 旁白字级时间戳对齐(复用 awk-tts 原生时间戳,缺失回退火山 ASR) + clip-trim Stage 12 精确切素材段(入点/出点/倍速/前置缓冲) + audio-mix Stage 12 多轨混音(每轨独立延时与音量) + timeline-compose Stage 12 按时间轴 JSON 合成片段(内部调 clip-trim + audio-mix) scene-compose Stage 12 单 Scene 分段合成(片段+旁白+对白 → 一个 Scene 片段) + assemble Stage 12 按镜顺序拼接成片 + 转场 + 规格归一化 + add-silent-audio Stage 12 给无音频的视频片段补静音音轨(concat 前置) make-outro Stage 12 片尾制作(形象图+黑边+烧字幕+静音轨 → 标准比例片尾) motion-audit Stage 13 motion_led 抽查(补公共 video-review) - make-cover Stage 14 封面(siliconflow-img-gen,必含标题文字) + normalize Stage 13c 响度归一化到 -14 LUFS(**必跑**) + make-cover Stage 14 封面(siliconflow-img-gen,必含封面主文案) + +后期处理(可选,全部干湿分离:输出落 _<处理名>.mp4,不覆盖输入): + burn-srt libass 把 SRT 硬烧进画面(甲方要字幕时) + duck sidechaincompress 让旁白触发 BGM 自动压低(要专业混音且可分轨时) + denoise afftdn / arnndn 去环境噪声(仅甲方素材音质差时) + interp minterpolate 补帧到 30/60fps(仅低 fps 源材) 闸门不是子命令——GATE A(Stage 6 后文本闸门)与 GATE B(Stage 9 后素材闸门)由 agent 按 expert-video SKILL.md 执行:呈交摘要 → 结束本轮回复 → 等甲方(main agent 或用户)逐闸门批准。 diff --git a/crews/content-producer/skills/expert-video/workflows/collage-broll.md b/crews/content-producer/skills/expert-video/workflows/collage-broll.md index a1410329..ca65c90d 100644 --- a/crews/content-producer/skills/expert-video/workflows/collage-broll.md +++ b/crews/content-producer/skills/expert-video/workflows/collage-broll.md @@ -2,6 +2,8 @@ Brief 里写 `workflow: collage-broll`,或甲方要"把这句口播做成拼贴 B-roll""纸拼贴动画""半调拼贴"时使用。把一句约 5 秒的口播压成一个 sharp visual idea,再做成高级编辑风纸拼贴组装动画。 +本文是**通用制作流程在纸拼贴 B-roll 上的细化**(三道闸门、隐喻与静帧规范、Gate 3 批量调度),不替代通用流程;原子能力、护栏、工作区与交付约定照 `expert-video` 的 SKILL.md 执行。 + ## 三道闸门 | 闸门 | 停在哪 | 交付给甲方看什么 | @@ -11,7 +13,7 @@ Brief 里写 `workflow: collage-broll`,或甲方要"把这句口播做成拼 | Gate 3 视频生成 | 静帧确认后不再问用哪个模型,直接调 `collage-broll gate3` 批量跑 i2v | 逐条 contact sheet + 末帧对照 + `gate3-qa.md` + `video-review` 结论 | - 每道闸门都要**结束本轮回复**等甲方批;甲方只确认部分编号时,只让通过的条目进下一道。 -- Gate 1 / Gate 2 分别对应通用链的 GATE A(文本)/ GATE B(素材)语义:付费生成前必停。 +- Gate 1 / Gate 2 分别对应通用制作流程的 GATE A(文本)/ GATE B(素材)语义:付费生成前必停。 - 甲方已在 Brief 中代理批准某道闸门时,把批准范围记进对应 QA 文件后继续。 ## 成功标准 @@ -32,7 +34,7 @@ Brief 里写 `workflow: collage-broll`,或甲方要"把这句口播做成拼 - 需要精确控制图层、遮挡、镜头穿越或可编辑时间线 → 改用分层动画方案,并向甲方说明本 workflow 做不到。 - 只要视频提示词、不要成片 → 直接写 prompt 交付,不走本流程。 -- 需要真实人物产品广告或口播演员 → 走 Narration Video 或通用流程。 +- 需要真实人物产品广告或口播演员 → 走 Narration Video,或只按通用制作流程做(不套类型 workflow)。 - 甲方明确要可逐层修改的透明素材 → 本 workflow 默认不拆透明图层。 ## 项目目录 diff --git a/crews/content-producer/skills/expert-video/workflows/narration-video.md b/crews/content-producer/skills/expert-video/workflows/narration-video.md index 2e6f1cb0..d0b0a473 100644 --- a/crews/content-producer/skills/expert-video/workflows/narration-video.md +++ b/crews/content-producer/skills/expert-video/workflows/narration-video.md @@ -1,14 +1,14 @@ # Workflow:Narration Video(口播类视频) -Brief 里写 `workflow: narration-video`,或 Brief 交付了口播文案 / 真人口播录音时使用。本 workflow 只写口播类视频的声画实现套路与阶段裁剪;原子能力、闸门纪律、工作区约定按 `expert-video` 的 SKILL.md 执行。 +Brief 里写 `workflow: narration-video`,或 Brief 交付了口播文案 / 真人口播录音时使用。本文是**通用制作流程在口播类视频上的细化**:声画实现套路、阶段裁剪与验收补充,不替代通用流程——阶段链、GATE A/B 闸门纪律、护栏、工作区与交付约定一律照 `expert-video` 的 SKILL.md 执行;本文与通用流程冲突处以本文为准,但闸门与护栏不让步。 ## 类型定义 -以**人声讲述**为主干的视频。口播文案是甲方(main agent 或用户)的策略产物,已定稿交付;本包负责声画实现——配音或录音处理、字级时间戳对齐、按语义配画面、字幕、BGM、成片与封面。 +以**人声讲述**为主干的视频。口播文案是甲方(main agent 或用户)的策略产物,已定稿交付;我负责声画实现——配音或录音处理、字级时间戳对齐、按语义配画面、字幕、BGM、成片与封面。 ## 三种输入形态 -| 形态 | 甲方交付 | 本包做什么 | +| 形态 | 甲方交付 | 我做什么 | |------|---------|-----------| | TTS 旁白 + 画面 | `voiceover.md`(口播终稿)+ 素材(可选) | `awk-tts` 生成旁白(音色按 Brief)→ `narration-align` 拿字级时间戳 → 按时间戳切配画面 → 字幕 → BGM | | 真人口播录音 + 画面 | 录音文件绝对路径 + 素材(可选) | ASR 拿时间戳(Stage 11 场景 D)→ 按时间戳排画面 → 字幕 → 音质差时降噪 → BGM ducking | @@ -19,9 +19,9 @@ Brief 里写 `workflow: narration-video`,或 Brief 交付了口播文案 / 真 - **口播文案原样落稿锁定**:落 `script/script.md`,不重写、不"顺手优化"措辞、不增删卖点。 - 发现文案有问题(超时长带、合规风险、抽象到无法配画面)→ **报甲方**,不自行改。 - 模式 B(用户直接对话)下用户只给大意时,可代拟文案,但必须发用户确认,定稿后才算 `voiceover`。 -- 语义级剪辑(去口气词、智能剪重点)归 main 的 `talking-head-cut`;本包只做几何级修整(切段/拼接/混音/烧字幕/补轨)。 +- 语义级剪辑(去口气词、智能剪重点)归 main 的 `talking-head-cut`;我只做几何级修整(切段/拼接/混音/烧字幕/补轨)。 -## 阶段裁剪(对通用链的差异) +## 阶段裁剪(对通用制作流程的细化) | 阶段 | 本 workflow 的做法 | |------|--------------------| @@ -46,7 +46,7 @@ Brief 里写 `workflow: narration-video`,或 Brief 交付了口播文案 / 真 - **语速**:默认 6–8 字/秒;Brief 指定优先。 - **字幕**:来自对齐时间轴;烧录前查目标画幅安全区,不落画面中部、不遮主体。 - **BGM**:口播下 ducking(人声主、BGM 从);免版税曲库(`bgm-library`)优先。 -- **录音质量差**:走 crew 级 `scripts/denoise.py`(afftdn),只降噪不改内容。 +- **录音质量差**:走 `video-producer denoise`(afftdn),只降噪不改内容。 ## 验收检查 diff --git a/crews/content-producer/skills/expert-video/workflows/reversal-ad.md b/crews/content-producer/skills/expert-video/workflows/reversal-ad.md index 9c032243..b74b32b8 100644 --- a/crews/content-producer/skills/expert-video/workflows/reversal-ad.md +++ b/crews/content-producer/skills/expert-video/workflows/reversal-ad.md @@ -1,6 +1,6 @@ # Workflow:Reversal Ad(「万万没想到」式反转植入) -Brief 里写 `workflow: reversal-ad` 时使用。本 workflow 只写这一类型视频的套路与阶段裁剪;原子能力、闸门纪律、工作区约定一律按 `expert-video` 的 SKILL.md 执行。 +Brief 里写 `workflow: reversal-ad` 时使用。本文是**通用制作流程在反转植入类视频上的细化**:三段结构、反转手法、素材与口播约束、阶段裁剪,不替代通用流程——原子能力、GATE A/B 闸门纪律、护栏、工作区与交付约定一律照 `expert-video` 的 SKILL.md 执行;本文与通用流程冲突处以本文为准,但闸门与护栏不让步。 ## 类型定义 @@ -39,7 +39,7 @@ Brief 里写 `workflow: reversal-ad` 时使用。本 workflow 只写这一类型 ## 口播(旁白) - 甲方交付 `voiceover.md` 时**原样落稿锁定**(Stage 3 不重写策略文案,仍跑 `script-self-eval` 做检查)。 -- 未交付时由本包写,GATE A 交审。默认规范:第三人称解说体;避免问句、感叹号、第二人称(你/您/家人们)与促销信号词(「接下来介绍」「这是我们的」);句长 10–15 字,语速 7–8 字/秒。 +- 未交付时由我写,GATE A 交审。默认规范:第三人称解说体;避免问句、感叹号、第二人称(你/您/家人们)与促销信号词(「接下来介绍」「这是我们的」);句长 10–15 字,语速 7–8 字/秒。 - 真人口播录音(甲方提供)走 Stage 11 场景 D:ASR 拿时间戳后按时间戳排素材,不重配旁白。 ## 素材 sourcing @@ -47,7 +47,7 @@ Brief 里写 `workflow: reversal-ad` 时使用。本 workflow 只写这一类型 来源模式只允许以下四类(含混合),每段素材都必须能追溯到来源模式与授权记录: 1. **open_license_footage**:Brief 指定的开源/免版权片源。记录原片 URL、许可证、署名要求,发布说明如实署名。不得把"网上能下载"等同于可商用或可改编。 -2. **user_provided**:甲方/用户给现成片段。入库前记录文件参数、来源说明、授权背景;版权风险由甲方确认承担,本包只做技术处理,不做授权背书。 +2. **user_provided**:甲方/用户给现成片段。入库前记录文件参数、来源说明、授权背景;版权风险由甲方确认承担,我只做技术处理,不做授权背书。 3. **aigc**:按 Brief 风格边界生成(公共 `aigc-video-gen`)。记录 prompt、模型、生成时间与产物 metadata;交付说明按平台要求标注 AI 生成。 4. **mixed**:以上混合。 @@ -68,9 +68,9 @@ Brief 里写 `workflow: reversal-ad` 时使用。本 workflow 只写这一类型 | core_message | 本条必须传达的核心信息 | | product_points | 产品/服务事实、允许讲的能力、禁用承诺(只以 Brief 为准,不内置品牌事实) | | implant_cta | Brief「业务植入与 CTA」字段:植入位置与方式、内容与业务的衔接句要求、CTA 主目标与句式;未给时按本 workflow 默认(反转点 55%–76%、植入段单点深打、片尾一个主行动),并在 GATE A 说明 | -| twist_variant | 任务指引式 / 双关置换式 / 身份彩蛋式 / 戏中戏式;未指定时由本包据素材与故事选定,并在 GATE A 说明理由 | +| twist_variant | 任务指引式 / 双关置换式 / 身份彩蛋式 / 戏中戏式;未指定时由我据素材与故事选定,并在 GATE A 说明理由 | | story_source | 解说正文的故事来源(开源片名 / 用户素材 / AIGC 生成) | -| voiceover | 甲方交付口播终稿路径;未交付时写明由本包起草 | +| voiceover | 甲方交付口播终稿路径;未交付时写明由我起草 | | source_mode | open_license_footage / user_provided / aigc / mixed | | assets | 素材绝对路径、来源 URL、许可证、授权确认记录 | | form | 横竖屏、时长带、画面风格、配音音色与 BGM 倾向 | @@ -78,7 +78,7 @@ Brief 里写 `workflow: reversal-ad` 时使用。本 workflow 只写这一类型 | gates | GATE A/B 批准人;甲方代理批准时写明批准范围 | | acceptance | 交付物、验收标准、遗留问题记录要求 | -## 阶段裁剪(对通用链的差异) +## 阶段裁剪(对通用制作流程的细化) | 阶段 | 本 workflow 的做法 | |------|--------------------| diff --git a/crews/main/skills/expert-douyin/SKILL.md b/crews/main/skills/expert-douyin/SKILL.md index 3dfb2a75..97d30f37 100644 --- a/crews/main/skills/expert-douyin/SKILL.md +++ b/crews/main/skills/expert-douyin/SKILL.md @@ -45,7 +45,7 @@ metadata: 素材加工相关技能:`video-edit`(素材加工拼接)、`talking-head-cut`(口播轻剪辑)、`ui-demo`(产品操作录屏)、`video-review`(成片质检闸门)、`siliconflow-img-gen`(封面图)、`pexels-footage` / `pixabay-footage`(免版权素材)。 -**视频全案分工硬边界**:main 负责选题策划、按 DNA 出 **Brief**、拟定标题与简介、准备素材(用户素材预处理 / `ui-demo` 录屏 / 从 `campaign_assets/` 挑选,绝对路径写进 Brief)、监督推动 CP、成片后的发布与运营,也直接做图文内容与已有素材轻加工;视频全案的成片制作委托 `content-producer`。口播类视频的口播文案由 main 按 `narration-script` 子模块写好并随 Brief 交付(真人口播时,指导用户录音并取得录音文件),CP 不重写策略文案。Brief 指定 `workflow` 时 CP 必须采用,未指定时 CP 按通用阶段链自由发挥。Brief **不含 DNA 信息**,main 也不替 CP 建工作区(双方 T3 权限可互访取文件)。 +**视频全案分工硬边界**:main 负责选题策划、按 DNA 出 **Brief**、拟定标题与简介、准备素材(用户素材预处理 / `ui-demo` 录屏 / 从 `campaign_assets/` 挑选,绝对路径写进 Brief)、监督推动 CP、成片后的发布与运营,也直接做图文内容与已有素材轻加工;视频全案的成片制作委托 `content-producer`。口播类视频的口播文案由 main 按 `narration-script` 子模块写好并随 Brief 交付(真人口播时,指导用户录音并取得录音文件),CP 不重写策略文案。Brief 指定 `workflow` 时 CP 必须采用;未指定时 CP 按其通用制作流程做(那是 CP 的基准准则,不是备选 workflow),档位由 Stage 1 定。Brief **不含 DNA 信息**,main 也不替 CP 建工作区(双方 T3 权限可互访取文件)。 ## 风格与 DNA diff --git a/crews/main/skills/expert-douyin/tools/douyin-style-profiler/references/video-dna-framework.md b/crews/main/skills/expert-douyin/tools/douyin-style-profiler/references/video-dna-framework.md index 70949299..a316e22e 100644 --- a/crews/main/skills/expert-douyin/tools/douyin-style-profiler/references/video-dna-framework.md +++ b/crews/main/skills/expert-douyin/tools/douyin-style-profiler/references/video-dna-framework.md @@ -104,12 +104,12 @@ DNA template = **Brief.md 正文模板 + 口播文案模板(可选)**,固 | 影视解说 / 剧情解说 + 反转植入(「万万没想到」式) | Content Producer `expert-video` → **Reversal Ad** workflow | `reversal-ad` | | 口播类(真人口播出镜,或旁白 + 画面) | Content Producer `expert-video` → **Narration Video** workflow | `narration-video` | | 一句文稿转视觉隐喻的纸拼贴动画 | Content Producer `expert-video` → **Collage B-roll** workflow | `collage-broll` | -| 纯 AIGC 动画 / 剧情短片 / 蒙太奇拼接(需从零出脚本分镜) | Content Producer `expert-video` → 通用阶段链(narrative / motion / montage 档) | 省略(由 CP 定档位) | +| 纯 AIGC 动画 / 剧情短片 / 蒙太奇拼接(需从零出脚本分镜) | Content Producer `expert-video` → **不指定类型 workflow**:CP 按其通用制作流程做,Stage 1 定档位(narrative / motion / montage) | 省略 | | 已有素材简单拼接、加旁白、烧字幕 | main `video-edit`(不委托 CP) | — | | 已有真人口播素材去口气词、剪高光 | main `talking-head-cut`(不委托 CP) | — | | 产品操作录屏 | main `ui-demo`(不委托 CP) | — | -Brief 写了 `workflow` 时 Content Producer 必须直接采用,不得替换成自创流程;未写时按通用阶段链自由选择实现。 +Brief 写了 `workflow` 时 Content Producer 必须直接采用,不得替换成自创流程;未写时 CP 按其**通用制作流程**做——那是 CP 所有视频工作的基准准则,不是与其他 workflow 并列的选项,也不是 fallback,档位由 Stage 1 `intent-router` 定。 ## Focus ID 表 diff --git a/crews/main/skills/expert-douyin/tools/douyin-style-profiler/scripts/build_style_profile.py b/crews/main/skills/expert-douyin/tools/douyin-style-profiler/scripts/build_style_profile.py index c026ed3f..c9af3557 100644 --- a/crews/main/skills/expert-douyin/tools/douyin-style-profiler/scripts/build_style_profile.py +++ b/crews/main/skills/expert-douyin/tools/douyin-style-profiler/scripts/build_style_profile.py @@ -104,7 +104,7 @@ "topic-angle": "- 单篇观测:选题类型、选题入口(现象 / 问题 / 冲突 / 数据 / 热点 / 挑战 / 个人经历)、目标人群为什么要看完(理解 / 判断 / 行动 / 避坑 / 身份认同 / 情绪共鸣)。\n- 边界:只记本篇,不判断跨篇稳定性;不评价选题好坏。", "title-cover": "- 单篇观测:标题类型(痛点 / 数字 / 反差 / 悬念 / 身份点名 / 搜索长尾)、标题与描述原文、话题标签策略。\n- 视觉证据:封面或首帧必须由视觉模型读取图片,至少提取画面主体与场景、构图与画幅、色彩体系、光线与质感、风格与媒介、文字视觉与图文关系、品牌识别元素、避免项,并反推为可执行的 AIGC 提示词要素;无图片写「未提供」,不得凭正文或标题想象补齐。", "content-idea": "- 单篇观测:一句话创意内核、创意类型、展开逻辑(悬念 / 反转 / 递进 / 对比 / 清单 / 实测)、记忆点。\n- 可复用信号:这个创意套路换成别的主题还能怎么用。\n- 边界:只记创意层,不记创作细节(逐句台词、镜头表、脚本结构、转场与编码参数)。", - "video-form": "- 单篇观测:视频内容形态(口播 / 实拍拼接 / 影视解说+反转植入 / 纯 AIGC 动画 / 创意转场动效 / 录屏演示 / 图文卡片视频 / 混合)与判定依据(画面证据、口播占比、素材来源)。\n- 制作指向:必须落到真实存在的资源名——Content Producer `expert-video` 的某个 workflow(Reversal Ad / Narration Video / Collage B-roll / 通用阶段链),或 main 的素材加工技能(`video-edit` / `talking-head-cut` / `ui-demo`);不得发明不存在的名字。", + "video-form": "- 单篇观测:视频内容形态(口播 / 实拍拼接 / 影视解说+反转植入 / 纯 AIGC 动画 / 创意转场动效 / 录屏演示 / 图文卡片视频 / 混合)与判定依据(画面证据、口播占比、素材来源)。\n- 制作指向:必须落到真实存在的资源名——Content Producer `expert-video` 的某个 workflow(Reversal Ad / Narration Video / Collage B-roll;不属这三类就写「不指定类型 workflow」,由 CP 按通用制作流程 + Stage 1 定档位),或 main 的素材加工技能(`video-edit` / `talking-head-cut` / `ui-demo`);不得发明不存在的名字。", "production-spec": "- 单篇观测:横屏或竖屏、时长带、画面风格(色调、质感、字幕样式倾向、信息密度)、配音音色与声音形态(原声口播 / TTS / 旁白 / 纯画面字幕)、BGM 与音效倾向、封面规格。\n- 边界:只记规格与倾向,不规定镜头参数、逐镜设计、转场与编码细节——那些归 Content Producer。", "narration-script": "- 子模块(仅口播类作品启用):起(从什么起步)、承(靠什么推进)、转(转折触发)、合(收束方式;CTA 的目标、位置与句式记在 `interaction-cta`),以及人称与语气、句长与语速、签名式表达。\n- 边界:非口播类或证据不足时写「未启用 / 未观测」;不得把单篇句式直接上升为规则。", "body-voice": "- 单篇观测:开头钩子(原文摘录)、正文组织方式(清单体 / 教程步骤 / 故事线 / 对比 / 观点输出)、分行与段落节奏、口语化程度与人称、emoji 与标点用法、签名式表达。\n- 证据边界:脚本统计只给句长 / 行数 / emoji / 标签等线索;口头禅与签名表达必须回读原文确认。", diff --git a/crews/main/skills/expert-douyin/workflows/content-production.md b/crews/main/skills/expert-douyin/workflows/content-production.md index 4b1e790e..fc084a80 100644 --- a/crews/main/skills/expert-douyin/workflows/content-production.md +++ b/crews/main/skills/expert-douyin/workflows/content-production.md @@ -194,7 +194,7 @@ DNA 约束的是选题与观看理由、标题与封面写法、内容创意原 - 视频名 / slug: - platform:douyin -- workflow:reversal-ad / narration-video / collage-broll / 未指定(未指定时 CP 按通用阶段链自选档位) +- workflow:reversal-ad / narration-video / collage-broll / 未指定(未指定 = CP 按其通用制作流程做,Stage 1 自定档位) - 选题与观看理由: - 核心传达: - 内容创意:创意原型 + 展开逻辑 + 记忆点(+ 反转设计,如为反转植入类) diff --git a/crews/main/skills/expert-wx-channel/SKILL.md b/crews/main/skills/expert-wx-channel/SKILL.md index a5142099..13106046 100644 --- a/crews/main/skills/expert-wx-channel/SKILL.md +++ b/crews/main/skills/expert-wx-channel/SKILL.md @@ -45,7 +45,7 @@ metadata: 素材加工相关技能:`video-edit`(素材加工拼接)、`talking-head-cut`(口播轻剪辑)、`ui-demo`(产品操作录屏)、`video-review`(成片质检闸门)、`siliconflow-img-gen`(封面图)、`pexels-footage` / `pixabay-footage`(免版权素材)。 -**视频全案分工硬边界**:main 负责选题策划、按 DNA 出 **Brief**、拟定标题与简介、准备素材(用户素材预处理 / `ui-demo` 录屏 / 从 `campaign_assets/` 挑选,绝对路径写进 Brief)、监督推动 CP、成片后的发布与运营,也直接做图文内容与已有素材轻加工;视频全案的成片制作委托 `content-producer`。口播类视频的口播文案由 main 按 `narration-script` 子模块写好并随 Brief 交付(真人口播时,指导用户录音并取得录音文件),CP 不重写策略文案。Brief 指定 `workflow` 时 CP 必须采用,未指定时 CP 按通用阶段链自由发挥。Brief **不含 DNA 信息**,main 也不替 CP 建工作区(双方 T3 权限可互访取文件)。 +**视频全案分工硬边界**:main 负责选题策划、按 DNA 出 **Brief**、拟定标题与简介、准备素材(用户素材预处理 / `ui-demo` 录屏 / 从 `campaign_assets/` 挑选,绝对路径写进 Brief)、监督推动 CP、成片后的发布与运营,也直接做图文内容与已有素材轻加工;视频全案的成片制作委托 `content-producer`。口播类视频的口播文案由 main 按 `narration-script` 子模块写好并随 Brief 交付(真人口播时,指导用户录音并取得录音文件),CP 不重写策略文案。Brief 指定 `workflow` 时 CP 必须采用;未指定时 CP 按其通用制作流程做(那是 CP 的基准准则,不是备选 workflow),档位由 Stage 1 定。Brief **不含 DNA 信息**,main 也不替 CP 建工作区(双方 T3 权限可互访取文件)。 ## 平台速查 diff --git a/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/references/video-dna-framework.md b/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/references/video-dna-framework.md index b53429a0..5b70a145 100644 --- a/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/references/video-dna-framework.md +++ b/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/references/video-dna-framework.md @@ -104,12 +104,12 @@ DNA template = **Brief.md 正文模板 + 口播文案模板(可选)**,固 | 影视解说 / 剧情解说 + 反转植入(「万万没想到」式) | Content Producer `expert-video` → **Reversal Ad** workflow | `reversal-ad` | | 口播类(真人口播出镜,或旁白 + 画面) | Content Producer `expert-video` → **Narration Video** workflow | `narration-video` | | 一句文稿转视觉隐喻的纸拼贴动画 | Content Producer `expert-video` → **Collage B-roll** workflow | `collage-broll` | -| 纯 AIGC 动画 / 剧情短片 / 蒙太奇拼接(需从零出脚本分镜) | Content Producer `expert-video` → 通用阶段链(narrative / motion / montage 档) | 省略(由 CP 定档位) | +| 纯 AIGC 动画 / 剧情短片 / 蒙太奇拼接(需从零出脚本分镜) | Content Producer `expert-video` → **不指定类型 workflow**:CP 按其通用制作流程做,Stage 1 定档位(narrative / motion / montage) | 省略 | | 已有素材简单拼接、加旁白、烧字幕 | main `video-edit`(不委托 CP) | — | | 已有真人口播素材去口气词、剪高光 | main `talking-head-cut`(不委托 CP) | — | | 产品操作录屏 | main `ui-demo`(不委托 CP) | — | -Brief 写了 `workflow` 时 Content Producer 必须直接采用,不得替换成自创流程;未写时按通用阶段链自由选择实现。 +Brief 写了 `workflow` 时 Content Producer 必须直接采用,不得替换成自创流程;未写时 CP 按其**通用制作流程**做——那是 CP 所有视频工作的基准准则,不是与其他 workflow 并列的选项,也不是 fallback,档位由 Stage 1 `intent-router` 定。 ## Focus ID 表 diff --git a/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/scripts/build_style_profile.py b/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/scripts/build_style_profile.py index 927d558f..bab59fca 100644 --- a/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/scripts/build_style_profile.py +++ b/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/scripts/build_style_profile.py @@ -88,7 +88,7 @@ "topic-angle": "- 单篇观测:选题类型、选题入口(现象 / 问题 / 冲突 / 数据 / 热点 / 挑战 / 个人经历)、目标人群为什么要看完(理解 / 判断 / 行动 / 避坑 / 身份认同 / 情绪共鸣)。\n- 边界:只记本篇,不判断跨篇稳定性;不评价选题好坏。", "title-cover": "- 单篇观测:**短标题**(发布页可填,官方称填了能获得更多流量;作品管理页不展示,取数时拿不到)与**视频描述**(含话题标签)两者的原文与写法模式。\n- 取数边界:抓取到的作品只有视频描述;短标题拿不到时写「未观测(管理页不展示)」,不得把描述当短标题。\n- 视觉证据:封面或首帧必须由视觉模型读取图片,至少提取画面主体与场景、构图与画幅、色彩体系、光线与质感、风格与媒介、文字视觉、品牌识别元素、避免项,并反推为可执行的 AIGC 提示词要素;无图片写「未提供」。", "content-idea": "- 单篇观测:一句话创意内核、创意类型、展开逻辑(悬念 / 反转 / 递进 / 对比 / 清单 / 实测)、记忆点。\n- 可复用信号:这个创意套路换成别的主题还能怎么用。\n- 边界:只记创意层,不记创作细节(逐句台词、镜头表、脚本结构、转场与编码参数)。", - "video-form": "- 单篇观测:视频内容形态(口播 / 实拍拼接 / 影视解说+反转植入 / 纯 AIGC 动画 / 创意转场动效 / 录屏演示 / 图文卡片视频 / 混合)与判定依据(画面证据、口播占比、素材来源)。\n- 制作指向:必须落到真实存在的资源名——Content Producer `expert-video` 的某个 workflow(Reversal Ad / Narration Video / Collage B-roll / 通用阶段链),或 main 的素材加工技能(`video-edit` / `talking-head-cut` / `ui-demo`);不得发明不存在的名字。", + "video-form": "- 单篇观测:视频内容形态(口播 / 实拍拼接 / 影视解说+反转植入 / 纯 AIGC 动画 / 创意转场动效 / 录屏演示 / 图文卡片视频 / 混合)与判定依据(画面证据、口播占比、素材来源)。\n- 制作指向:必须落到真实存在的资源名——Content Producer `expert-video` 的某个 workflow(Reversal Ad / Narration Video / Collage B-roll;不属这三类就写「不指定类型 workflow」,由 CP 按通用制作流程 + Stage 1 定档位),或 main 的素材加工技能(`video-edit` / `talking-head-cut` / `ui-demo`);不得发明不存在的名字。", "production-spec": "- 单篇观测:横屏或竖屏、时长带、画面风格(色调、质感、字幕样式倾向、信息密度)、配音音色与声音形态(原声口播 / TTS / 旁白 / 纯画面字幕)、BGM 与音效倾向、封面规格。\n- 边界:只记规格与倾向,不规定镜头参数、逐镜设计、转场与编码细节——那些归 Content Producer。", "biz-implant": "- 单篇观测:是否有业务植入(纯内容 / 软植入 / 硬广直给)、植入位置与时机、植入载体(剧情道具 / 口播一句话 / 字幕卡片 / 场景背景 / 案例与数据 / 清单第 N 项 / 教程步骤内嵌 / 产品截图 / 购物车或留资组件 / 主页与私信引导)、植入方式原型(反转植入 / 痛点→方案 / 场景带入 / 实测对比 / 身份认同 / 口碑故事 / 教程内嵌 / 硬广直给)、内容与业务的衔接句(原文摘录)、植入密度与占比、品牌词与产品名出现方式与频次。\n- 证据要求:位置 + 载体 + 原文摘录三样齐全;本篇无植入时写「无植入(纯内容)」,不得留空。\n- 边界:只记植入套路,不写逐句广告文案;形态层面的「影视解说 + 反转植入」由 `video-form` 记形态与制作指向。", "interaction-cta": "- 单篇观测:行动目标(关注 / 评论 / 收藏 / 转发 / 私信 / 主页点击 / 进店 / 咨询 / 搜索品牌词 / 购物车 / 直播预约)与本篇主目标、CTA 出现位置与时机(口播收尾句 / 字幕卡 / 片尾贴片 / 描述区 / 正文结尾 / 图组末图 / 评论区)、CTA 句式与原文摘录(命令式 / 提问式 / 利益式 / 身份式 / 悬念式)、一篇放几个行动、诱因设计(利益点 / 情绪 / 身份认同 / 稀缺)、与业务转化目标的对应。\n- 合规边界:不隐藏站外联系方式、不绕平台检测、不以利益换互动,记为必须避免项。", diff --git a/crews/main/skills/expert-wx-channel/workflows/content-production.md b/crews/main/skills/expert-wx-channel/workflows/content-production.md index c0e3e4fc..d7c82dd8 100644 --- a/crews/main/skills/expert-wx-channel/workflows/content-production.md +++ b/crews/main/skills/expert-wx-channel/workflows/content-production.md @@ -200,7 +200,7 @@ DNA 约束的是选题与观看理由、短标题与视频描述写法、内容 - 视频名 / slug: - platform:wx_channel -- workflow:reversal-ad / narration-video / collage-broll / 未指定(未指定时 CP 按通用阶段链自选档位) +- workflow:reversal-ad / narration-video / collage-broll / 未指定(未指定 = CP 按其通用制作流程做,Stage 1 自定档位) - 选题与观看理由: - 核心传达: - 内容创意:创意原型 + 展开逻辑 + 记忆点(+ 反转设计,如为反转植入类) @@ -224,7 +224,7 @@ Brief 硬性规则: 2. 口播类视频:按 DNA 的 `narration-script` 子模块写口播终稿 `wx_channel/outputs//voiceover.md`,Brief 里给绝对路径;真人口播时指导用户按口播稿录音,完成后向用户取得录音文件。口播子模块未启用时 Brief 写「口播文案:不适用」或只给要点,由 CP 按其 workflow 组织旁白,main 不再规定逐句台词。用户必用的事实、案例、承诺和 CTA 必须进入 Brief 或口播终稿,不得为了形式删除关键事实。 3. 参考模式下,把选题与创意结论写进 Brief 的「内容创意」段即可;`viral-chaser` 拆解报告是 main 的采样材料,**不作为 Brief 附件交给 CP**。 -4. spawn `content-producer` 委托制作:只交 Brief + 素材绝对路径 + 口播文案 / 录音;不指定 CP 的工作区与制作方案。Brief 指定 `workflow` 时 CP 必须采用,未指定时 CP 按通用阶段链自由发挥。 +4. spawn `content-producer` 委托制作:只交 Brief + 素材绝对路径 + 口播文案 / 录音;不指定 CP 的工作区与制作方案。Brief 指定 `workflow` 时 CP 必须采用;未指定时 CP 按其通用制作流程做(那是 CP 的基准准则,不是备选 workflow),档位由 Stage 1 定。 5. Brief 变更时更新版本并推送变更要点;已开工中间产物按新版取舍,弃用部分记入交付说明。制作中发现 Brief 无法执行(素材缺失、时长超标)时,由 CP 回报、main 与用户确认后改 Brief,CP 不擅自改策略。 6. CP 交付后,按其回报的绝对路径把成片与封面取回 `wx_channel/outputs//`(`video.mp4` / `cover.jpg`),并把交付说明要点记入作品目录。用户直接提供成片时校验格式(`.mp4` / `.mov` / `.avi` / `.webm`)与时长后复制进作品目录。 diff --git a/crews/main/skills/expert-xhs/SKILL.md b/crews/main/skills/expert-xhs/SKILL.md index cc52781d..b3092feb 100644 --- a/crews/main/skills/expert-xhs/SKILL.md +++ b/crews/main/skills/expert-xhs/SKILL.md @@ -50,7 +50,7 @@ DNA 存储目录是 `xhs/dna/`。未指定 DNA 时默认使用并更新 `dna-0` DNA 是**从一批作品样本提取并聚合出的内容生产规则集**:图文 10 维——选题与观看理由、标题与封面图组、内容创意、**匹配的用户问题**、正文表达与语气、图组视觉、**业务植入套路**、**互动引导与 CTA 套路**,加账号运营子模块(简介写法、内容形式比例、发布习惯);视频 11 维——前四项加**业务植入套路**、**互动引导与 CTA 套路**、视频内容形态与制作指向、制作规格与视听倾向、口播文案子模块与账号运营子模块。搜索维度是小红书必备:最大流量池来自搜索,关键词必须落到用户可能的提问原句。DNA 指导 main agent 写图文或出视频 Brief(+ 口播文案),不规定创作细节与成片制作。维度框架 v2 位于 `xhs-style-profiler` 的 `references/note-dna-framework.md` 与 `references/video-dna-framework.md`。 -**视频全案分工硬边界**:main 负责选题策划、按 DNA 出 **Brief**、拟定标题与简介、准备素材(用户素材预处理 / `ui-demo` 录屏 / 从 `campaign_assets/` 挑选,绝对路径写进 Brief)、监督推动 CP、成片后的发布与运营,也直接做图文内容与已有素材轻加工;视频全案的成片制作委托 `content-producer`。口播类视频的口播文案由 main 按 `narration-script` 子模块写好并随 Brief 交付(真人口播时,指导用户录音并取得录音文件),CP 不重写策略文案。Brief 指定 `workflow` 时 CP 必须采用,未指定时 CP 按通用阶段链自由发挥。Brief **不含 DNA 信息**,main 也不替 CP 建工作区(双方 T3 权限可互访取文件)。 +**视频全案分工硬边界**:main 负责选题策划、按 DNA 出 **Brief**、拟定标题与简介、准备素材(用户素材预处理 / `ui-demo` 录屏 / 从 `campaign_assets/` 挑选,绝对路径写进 Brief)、监督推动 CP、成片后的发布与运营,也直接做图文内容与已有素材轻加工;视频全案的成片制作委托 `content-producer`。口播类视频的口播文案由 main 按 `narration-script` 子模块写好并随 Brief 交付(真人口播时,指导用户录音并取得录音文件),CP 不重写策略文案。Brief 指定 `workflow` 时 CP 必须采用;未指定时 CP 按其通用制作流程做(那是 CP 的基准准则,不是备选 workflow),档位由 Stage 1 定。Brief **不含 DNA 信息**,main 也不替 CP 建工作区(双方 T3 权限可互访取文件)。 ## 数据与记录 diff --git a/crews/main/skills/expert-xhs/tools/xhs-style-profiler/references/video-dna-framework.md b/crews/main/skills/expert-xhs/tools/xhs-style-profiler/references/video-dna-framework.md index 266dd643..737c80f9 100644 --- a/crews/main/skills/expert-xhs/tools/xhs-style-profiler/references/video-dna-framework.md +++ b/crews/main/skills/expert-xhs/tools/xhs-style-profiler/references/video-dna-framework.md @@ -107,12 +107,12 @@ DNA template = **Brief.md 正文模板 + 口播文案模板(可选)**,固 | 影视解说 / 剧情解说 + 反转植入(「万万没想到」式) | Content Producer `expert-video` → **Reversal Ad** workflow | `reversal-ad` | | 口播类(真人口播出镜,或旁白 + 画面) | Content Producer `expert-video` → **Narration Video** workflow | `narration-video` | | 一句文稿转视觉隐喻的纸拼贴动画 | Content Producer `expert-video` → **Collage B-roll** workflow | `collage-broll` | -| 纯 AIGC 动画 / 剧情短片 / 蒙太奇拼接(需从零出脚本分镜) | Content Producer `expert-video` → 通用阶段链(narrative / motion / montage 档) | 省略(由 CP 定档位) | +| 纯 AIGC 动画 / 剧情短片 / 蒙太奇拼接(需从零出脚本分镜) | Content Producer `expert-video` → **不指定类型 workflow**:CP 按其通用制作流程做,Stage 1 定档位(narrative / motion / montage) | 省略 | | 已有素材简单拼接、加旁白、烧字幕 | main `video-edit`(不委托 CP) | — | | 已有真人口播素材去口气词、剪高光 | main `talking-head-cut`(不委托 CP) | — | | 产品操作录屏 | main `ui-demo`(不委托 CP) | — | -Brief 写了 `workflow` 时 Content Producer 必须直接采用,不得替换成自创流程;未写时按通用阶段链自由选择实现。 +Brief 写了 `workflow` 时 Content Producer 必须直接采用,不得替换成自创流程;未写时 CP 按其**通用制作流程**做——那是 CP 所有视频工作的基准准则,不是与其他 workflow 并列的选项,也不是 fallback,档位由 Stage 1 `intent-router` 定。 ## Focus ID 表 diff --git a/crews/main/skills/expert-xhs/tools/xhs-style-profiler/scripts/build_style_profile.py b/crews/main/skills/expert-xhs/tools/xhs-style-profiler/scripts/build_style_profile.py index c2ba21d4..dc475752 100755 --- a/crews/main/skills/expert-xhs/tools/xhs-style-profiler/scripts/build_style_profile.py +++ b/crews/main/skills/expert-xhs/tools/xhs-style-profiler/scripts/build_style_profile.py @@ -105,7 +105,7 @@ "title-cover": "- 单篇观测:标题类型(痛点 / 数字 / 反差 / 悬念 / 身份点名 / 搜索长尾)、标题与描述原文、话题标签策略。\n- 视觉证据:封面或首帧必须由视觉模型读取图片,至少提取画面主体与场景、构图与画幅、色彩体系、光线与质感、风格与媒介、文字视觉与图文关系、品牌识别元素、避免项,并反推为可执行的 AIGC 提示词要素;无图片写「未提供」,不得凭正文或标题想象补齐。", "content-idea": "- 单篇观测:一句话创意内核、创意类型、展开逻辑(悬念 / 反转 / 递进 / 对比 / 清单 / 实测)、记忆点。\n- 可复用信号:这个创意套路换成别的主题还能怎么用。\n- 边界:只记创意层,不记创作细节(逐句台词、镜头表、脚本结构、转场与编码参数)。", "search-intent": "- 单篇观测:本篇命中的关键词(核心词 / 痛点词 / 场景词 / 人群词)、用户可能的提问原句、搜索意图层级、标签承载的搜索意图。\n- 边界:单篇只记候选;聚合后才形成「关键词 → 用户问题 → 内容形式」的搜索意图地图。", - "video-form": "- 单篇观测:视频内容形态(口播 / 实拍拼接 / 影视解说+反转植入 / 纯 AIGC 动画 / 创意转场动效 / 录屏演示 / 图文卡片视频 / 混合)与判定依据(画面证据、口播占比、素材来源)。\n- 制作指向:必须落到真实存在的资源名——Content Producer `expert-video` 的某个 workflow(Reversal Ad / Narration Video / Collage B-roll / 通用阶段链),或 main 的素材加工技能(`video-edit` / `talking-head-cut` / `ui-demo`);不得发明不存在的名字。", + "video-form": "- 单篇观测:视频内容形态(口播 / 实拍拼接 / 影视解说+反转植入 / 纯 AIGC 动画 / 创意转场动效 / 录屏演示 / 图文卡片视频 / 混合)与判定依据(画面证据、口播占比、素材来源)。\n- 制作指向:必须落到真实存在的资源名——Content Producer `expert-video` 的某个 workflow(Reversal Ad / Narration Video / Collage B-roll;不属这三类就写「不指定类型 workflow」,由 CP 按通用制作流程 + Stage 1 定档位),或 main 的素材加工技能(`video-edit` / `talking-head-cut` / `ui-demo`);不得发明不存在的名字。", "production-spec": "- 单篇观测:横屏或竖屏、时长带、画面风格(色调、质感、字幕样式倾向、信息密度)、配音音色与声音形态(原声口播 / TTS / 旁白 / 纯画面字幕)、BGM 与音效倾向、封面规格。\n- 边界:只记规格与倾向,不规定镜头参数、逐镜设计、转场与编码细节——那些归 Content Producer。", "narration-script": "- 子模块(仅口播类作品启用):起(从什么起步)、承(靠什么推进)、转(转折触发)、合(收束方式;CTA 的目标、位置与句式记在 `interaction-cta`),以及人称与语气、句长与语速、签名式表达。\n- 边界:非口播类或证据不足时写「未启用 / 未观测」;不得把单篇句式直接上升为规则。", "body-voice": "- 单篇观测:开头钩子(原文摘录)、正文组织方式(清单体 / 教程步骤 / 故事线 / 对比 / 观点输出)、分行与段落节奏、口语化程度与人称、emoji 与标点用法、签名式表达。\n- 证据边界:脚本统计只给句长 / 行数 / emoji / 标签等线索;口头禅与签名表达必须回读原文确认。", diff --git a/crews/main/skills/expert-xhs/workflows/content-production.md b/crews/main/skills/expert-xhs/workflows/content-production.md index b4a94420..0924e76e 100644 --- a/crews/main/skills/expert-xhs/workflows/content-production.md +++ b/crews/main/skills/expert-xhs/workflows/content-production.md @@ -162,7 +162,7 @@ DNA template 是 main agent 的生产输入模板:**图文 DNA 的 template = - 视频名 / slug: - platform:xhs -- workflow:reversal-ad / narration-video / collage-broll / 未指定(未指定时 CP 按通用阶段链自选档位) +- workflow:reversal-ad / narration-video / collage-broll / 未指定(未指定 = CP 按其通用制作流程做,Stage 1 自定档位) - 选题与观看理由: - 核心传达: - 内容创意:创意原型 + 展开逻辑 + 记忆点 @@ -216,7 +216,7 @@ Brief 硬性规则: | --- | --- | | 用户直接提供成片 | 校验格式与时长,复制到作品目录 | | 已有素材需简单加工 | main 用 `video-edit` / `talking-head-cut` 处理 | -| 全案制作 | 委托 `content-producer`:只交 Brief + 素材绝对路径 + 口播文案 / 录音,不指定 CP 工作区;指定 `workflow` 必须采用,未指定由 CP 自由发挥。成片与封面按 CP 回报的绝对路径取回作品目录 | +| 全案制作 | 委托 `content-producer`:只交 Brief + 素材绝对路径 + 口播文案 / 录音,不指定 CP 工作区;指定 `workflow` 必须采用;未指定时 CP 按其通用制作流程做,档位由 Stage 1 定。成片与封面按 CP 回报的绝对路径取回作品目录 | 视频封面优先从成片选帧;需要更强视觉冲击时用 `siliconflow-img-gen`。 diff --git a/crews/main/skills/viral-chaser/SKILL.md b/crews/main/skills/viral-chaser/SKILL.md index 70b206d1..8f87ed9f 100644 --- a/crews/main/skills/viral-chaser/SKILL.md +++ b/crews/main/skills/viral-chaser/SKILL.md @@ -234,7 +234,7 @@ Read: /ref//references/frames/frame_01_3s.jpg | 影视解说 / 剧情解说 + 反转植入(「万万没想到」式) | Content Producer `expert-video` → Reversal Ad workflow | | 口播类(真人口播出镜,或旁白 + 画面) | Content Producer `expert-video` → Narration Video workflow | | 一句文稿转视觉隐喻的纸拼贴动画 | Content Producer `expert-video` → Collage B-roll workflow | -| 纯 AIGC 动画 / 剧情短片 / 蒙太奇(需从零出脚本分镜) | Content Producer `expert-video` → 通用阶段链(narrative / motion / montage) | +| 纯 AIGC 动画 / 剧情短片 / 蒙太奇(需从零出脚本分镜) | Content Producer `expert-video` → **不指定类型 workflow**(CP 按其通用制作流程做,Stage 1 定档位:narrative / motion / montage) | | 已有素材简单拼接、加旁白、烧字幕 | main `video-edit` | | 已有真人口播素材去口气词、剪高光 | main `talking-head-cut` | | 产品操作录屏 | main `ui-demo` | diff --git a/docs/expert-pack-dna-architecture.md b/docs/expert-pack-dna-architecture.md index 78ca3039..94a8047e 100644 --- a/docs/expert-pack-dna-architecture.md +++ b/docs/expert-pack-dna-architecture.md @@ -413,7 +413,7 @@ crews//skills/expert-/tools/-style-profiler/ 3. **子模块不是独立 DNA**:口播文案子模块(`narration-script`,参考微信的起承转合)只在口播类作品启用,用于指导 main 写同类型视频的口播文案;账号运营子模块(`account-bio`、`content-mix-cadence`)只在样本来自对标账号批量提取时填写,**只写进 DNA 文档、不进 template**。 4. **视频 DNA 不含创作细节**:不写脚本结构、逐句台词、镜头表、转场与编码参数。视频类 template = **Brief.md 正文模板 + 口播文案模板(可选)**;图文类 template = 图文写作模板。 -`video-form`(视频内容形态:口播 / 实拍拼接 / 影视解说+反转植入 / 纯 AIGC 动画 / 创意转场 / 录屏 / 图文卡片)必须聚合成明确的**制作指向**,且只能写真实存在的资源名:Content Producer `expert-video` 的某个 workflow(Reversal Ad / Narration Video / Collage B-roll / 通用阶段链),或 main 的素材加工技能(`video-edit` / `talking-head-cut` / `ui-demo`)。Brief 的 `workflow` 字段据此填写。 +`video-form`(视频内容形态:口播 / 实拍拼接 / 影视解说+反转植入 / 纯 AIGC 动画 / 创意转场 / 录屏 / 图文卡片)必须聚合成明确的**制作指向**,且只能写真实存在的资源名:Content Producer `expert-video` 的某个 workflow(Reversal Ad / Narration Video / Collage B-roll;不属这三类就写「不指定类型 workflow」,由 CP 按通用制作流程 + Stage 1 定档位),或 main 的素材加工技能(`video-edit` / `talking-head-cut` / `ui-demo`)。Brief 的 `workflow` 字段据此填写。 采样侧配套:`viral-chaser` 的输出必须够喂这套框架——视频 meta(时长、宽高与横竖屏、发布时间、作者与简介、话题标签、互动数据)、覆盖全片的关键帧(含 25%/50%/63%/75%/90% 比例点,反转点通常在 55%-76%)、按时间占比的结构拆解与反转点位置、内容形态判定与制作指向,以及可直接喂 profiler 的 DNA 样本文字稿格式。 @@ -457,14 +457,15 @@ crews/content-producer/ AGENTS.md # 薄:两种工作模式 + 专家包路由 + 交接契约 + 通用约定 skills/ expert-video/ - SKILL.md # 乙方角色 + 两种工作模式 + workflow 清单 + 工具清单 + 交接契约 - # + 工作区约定 + 通用制作流程(阶段链 Stage 0→14,两闸门)+ 护栏 - workflows/ # workflow = 某一类型视频怎么做 + SKILL.md # 乙方角色与交接契约 + 三层阅读法(通用制作流程 = 基准准则 / + # 类型 workflow = 细化 / 工具说明 = 子命令参数)+ 工作区 + # + 通用制作流程(阶段链 Stage 0→14,两闸门)+ 护栏 + 禁止事项 + workflows/ # workflow = 通用制作流程在某一类型视频上的细化(不替代基准) reversal-ad.md 「万万没想到」式:影视 / 剧情解说 + 突然反转植入品宣 narration-video.md 口播类:甲方交付口播文案或真人录音,做声画实现 collage-broll.md 纸拼贴 B-roll:隐喻 → 静帧 → i2v 三道闸门 tools/ - video-producer/ # 原子能力(24 个子命令)+ wrapper,PATH 名不变 + video-producer/ # 原子能力(29 个子命令,含 5 个后期处理)+ wrapper,PATH 名不变 collage-broll/ # check-setup / gate3 + wrapper expert-design/ SKILL.md # 通用骨架(七步两闸门)+ workflow 清单 + 工具清单 @@ -477,7 +478,7 @@ crews/content-producer/ - **workflow 的语义随 crew 变**:平台运营包里 workflow = 业务场景(起号 / 生产 / 复盘);content-producer 里 workflow = **某一类型视频 / 某一类设计任务怎么做**。制作流程本身高度程式化、跨类型一致,写在包根 `SKILL.md`(阶段链、两闸门、护栏、Stage 12 工具箱);类型差异(叙事套路、素材来源、声画组织方式)才写进 workflow。 - **原技能整体降级为 tool**:`video-producer` / `collage-broll` / `design-full` 从路由面消失,成为包内工具;wrapper 名与子命令不变(靠 `skill-wrappers.sh` 的 `*/tools/*/` 扫描层暴露),调用方零改动。 -- **`manim-explainer` 删除**:能力已被 `expert-video` 的通用阶段链与 AIGC 动画路径覆盖。 +- **`manim-explainer` 删除**:能力已被 `expert-video` 的通用制作流程与 AIGC 动画路径覆盖。 - **原「Pipeline 机制」废除**:Brief 字段从 `pipeline` 改为 `workflow`;不存在「viral-chaser 报告输入」阶段(追爆拆解是 main 的采样动作,CP 只吃 Brief),Stage 1 由 `intent-router` 承担定档位,`reference-concepts` 降为可选工具(仅直接对接用户模式下用户给了参考拆解报告时用)。 - **两种工作模式**:A 作为 main 的 subagent(甲方 = main,Brief 已确认,不重开需求讨论);B 直接对接用户(甲方 = 用户,可能不专业,先引导确认 Brief、落实素材位置与存在性,口播文案代拟需用户确认)。两种模式都坚持乙方角色、都自建工作区。 - **运行期数据只写 CP 自己的 Workspace**:`output_videos//`(视频)与 `design_assets/YYYY-MM-DD-<任务名>/`(设计);不写进 main 的平台目录,也不让 main 代建。 From 7b9deba88ce1f747a8aaa909d20cb7a848faff68 Mon Sep 17 00:00:00 2001 From: codes-factory-of-bg Date: Fri, 11 Sep 2026 07:44:57 +0800 Subject: [PATCH 13/34] =?UTF-8?q?docs:=20openclaw=202026.9.3=20=E5=8D=87?= =?UTF-8?q?=E7=BA=A7=E4=BA=8B=E5=AE=9E=E8=B0=83=E7=A0=94=EF=BC=88=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E6=8B=93=E6=89=91/patch=20=E5=AE=9E=E6=B5=8B/awada/?= =?UTF-8?q?=E5=B7=A5=E5=85=B7=E9=93=BE/=E8=AF=89=E6=B1=82=E6=98=A0?= =?UTF-8?q?=E5=B0=84/=E6=B5=8F=E8=A7=88=E5=99=A8=E4=B8=93=E9=A1=B9?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../openclaw-2026.9.3-upgrade-fact-finding.md | 474 ++++++++++++++++++ 1 file changed, 474 insertions(+) create mode 100644 docs/openclaw-2026.9.3-upgrade-fact-finding.md diff --git a/docs/openclaw-2026.9.3-upgrade-fact-finding.md b/docs/openclaw-2026.9.3-upgrade-fact-finding.md new file mode 100644 index 00000000..fd860cff --- /dev/null +++ b/docs/openclaw-2026.9.3-upgrade-fact-finding.md @@ -0,0 +1,474 @@ +# openclaw 2026.9.3 升级事实调研 + +> 落盘 2026-09-11 · 调研窗口 2026-09-10 06:23 – 2026-09-11 07:30(CST) +> 当前基座:`2026.7.1-2`(`0790d9f593ad30c940ed93b5872a8cf6d6f3cf8c`,pin 在仓根 `openclaw.version`) +> 调研目标:`2026.9.3`(`1391f7cd2d4`,tag `v2026.9.3`);过渡候选:`extended-stable/2026.7.33`(tip `f619d7a9fa3`) +> **本文只记事实与实测数据,不做决策**;待拍板项集中在 §10,施工方案另开文档。 +> 相关:[browser-stack-replacement-spec-2026-07.md](./browser-stack-replacement-spec-2026-07.md)(camoufox 双线栈 spec)、[browser-extension-replacement-research.md](./browser-extension-replacement-research.md)、`patches/browser-camoufox-pivot/README.md` + +## 证据口径 + +| 标记 | 含义 | +|---|---| +| `[A]` | 2026-09-10 早会话内实测(pristine worktree `/tmp/oc93` 逐个 `git apply`、`git log --grep`、`ls-tree`),**本次落盘未复验**,仅记录当时结论 | +| `[B]` | 2026-09-11 落盘时在 `/home/wukong/wiseflow/openclaw` 用 `v2026.7.1-2` / `v2026.9.3` 两个 tag 现场实测(可复跑,命令见附录 A) | +| `[C]` | 上游文档 / release notes 原文(路径 + 行号以 9.3 tag 为准) | + +--- + +## 0. 摘要(先读这段) + +1. **不是"切版本 + 验 patch"能过的升级**:`v2026.7.1-2` **不是** `v2026.9.3` 的祖先,merge-base = `b81666ca6af`(2026-07-08),9.3 侧 **24,853** commits、7.1-2 侧独有 **224** `[B]`(今早 `[A]` 的数字已复算一致)。37 个 patch 里 **30 个要重做**(3777/4240 行),其中 9 个删除型 patch **全部硬失败** `[A]`。 +2. **awada 插件在 9.3 会加载即死**:`src/plugin-sdk/index.ts` 被删(裸导入 `openclaw/plugin-sdk` 从 exports 消失),`ClawdbotConfig` 全仓 0 命中(改名 `OpenClawConfig`)`[A]`。这是生产链路,优先级最高。 +3. **工具链硬门槛**:9.3 `engines.node = >=24.16.0 <25 || >=26.1.0`(**Node 22 / 25 支持被整条砍掉**),`packageManager = pnpm@12.3.4` `[B]`;我们 `build-dist.yml` 还锁 `24.15.0`(5 处)`[B]`。 +4. **用户两项核心诉求都不在 extended-stable/2026.7.33 上**:①发现本机已装 codex/claude code 当 provider(最低要 **9.1**)、②更低对话 token 消耗(要拿满得 **9.3**)`[A]`。→ ES 7.33 只能当过渡,不能当终点。 +5. **浏览器子系统在 9.3 是一次重写**:`extensions/browser` **567 files changed(+100,528 / −23,896)**,其中新增 263 / 修改 286 / 删除 18 `[B]`;工具入口 `browser-tool.ts` 从 1087 行缩到 439 行并被拆成 8 个模块 `[B]`。 +6. **浏览器协议层没有第四种**:仍是「自管 CDP / Chrome DevTools MCP / Chrome 扩展 relay」三种,且 **7.1-2 已全有** `[B][C]`。但 9.x 新增了两种**控制面**:Computer Use(`extensions/cua-computer`,0 → 39 文件)与 cloud worker 的 attached browser runtime(`src/worker/`,0 → 68 文件)`[B]`。 +7. **9.3 的浏览器新形态对我们几乎全是无关面**(extension / existing-session 要真人 Chrome,CUA 要 Mac/Windows,worker 是云场景);真正有用的只有 `navigate` 内联 snapshot、`text`/`requests`/`errors`、能力裁剪三项——**都指向省 token / 省轮次** `[B][C]`。 +8. **已落地动作**:第三方插件 pin 已 bump(`openclaw-weixin` 2.4.6→2.4.8、`wecom-openclaw-cli` 1.1.0→1.1.1,commit `d5e112c`,已推 origin/master);**基座 pin 一行未动** `[B]`。 +9. **量级估算**:4.5–7 人日(patch re-port 2–3.5 / awada 0.5–1 / 工具链 0.5 / 配置+doctor 0.5–1 / build+tsgo+冒烟 1)`[A]`。 + +--- + +## 1. 版本线拓扑 `[A]`,其中祖先关系 / commit 计数 / ES tip 已于 `[B]` 复验 + +| 线 | ref | 最新 commit | 状态 | +|---|---|---|---| +| 我们现在用的 | tag `v2026.7.1-2` = `origin/release/2026.7.1` 分支 tip | 2026-07-18(8/4 发布) | **已冻结**,不会再动 | +| 延长稳定 7.x | `origin/extended-stable/2026.7.33` | `f619d7a9fa3`(2026-09-09)`[B]` | **活跃维护中**,版本号 2026.7.33 | +| 延长稳定 6.x | `origin/extended-stable/2026.6.33`(tag 6.33/6.34) | 2026-09-08 | 活跃;npm `extended-stable` dist-tag 当前指向 **2026.6.34** | +| 主稳定线 | `release/2026.8.1 → 8.2 → 9.1 → 9.2 → 9.3` | 2026-09-07/08 | 火车式,一周 1–2 个稳定版 | + +关键事实: + +- **`v2026.7.1-2` 是 `extended-stable/2026.7.33` 的直系祖先**(`git merge-base --is-ancestor` = **YES**),`git rev-list --count v2026.7.1-2..origin/extended-stable/2026.7.33` = **38**,我们侧 0 个独有 commit `[B]`。 +- **`v2026.7.1-2` 不是 `v2026.9.3` 的祖先**(`--is-ancestor` = **NO**):merge-base = `b81666ca6af`(2026-07-08,`Fix container image upgrade migrations before gateway readiness (#101881)`);9.3 侧 **24,853** commits,7.1-2 侧独有 **224** 个(多为 CI/QA)`[B]`。 +- 我们当年靠 7.1-1 / 7.1-2 拿到的修复**已随主线进 9.3**(按 PR 号核到:#108487 codex progress、#108652 memory sidecar、#108258 WSL EROFS、#107294 npm lock、#106065 SQLite WAL、#108336 plugin metadata)→ **没有丢修复** `[A]`。 +- **7.33 当时还没打 tag、没发 npm 包**(分支上全是 `package acceptance` / `release preflight` / `frozen upgrade baselines` 类收尾 commit)`[A]`。 +- 6.33/6.34 与 7.1-2 是**分叉**关系(merge-base 6/24,它侧 332 commit),我们 37 个 patch 里 **17 个目标文件已漂移** → 功能倒退 + 还要重做 patch,**不可选** `[A]`。 +- 上游文档明确:`openclaw update --channel extended-stable` **只对 package 安装生效,git checkout 会被拒**(`docs/install/development-channels.md:43`、`docs/install/updating.md:42-43`)`[C]`。我们是源码 clone + commit pin + 自己 build,所以对我们只是"pin 哪棵树"的问题,但意味着**不能靠 CLI 自动跟,必须自己 re-pin**(巡检项)。 + +### 1.1 `package.json` 硬指标对比 `[B]` + +| 字段 | v2026.7.1-2 | v2026.9.3 | +|---|---|---| +| `version` | 2026.7.1 | 2026.9.3 | +| `engines.node` | `>=22.22.3 <23 \|\| >=24.15.0 <25 \|\| >=25.9.0` | **`>=24.16.0 <25 \|\| >=26.1.0`** | +| `packageManager` | `pnpm@11.2.2` | **`pnpm@12.3.4`** | + +> Node 22 与 25 两条线在 9.3 被整体移除;上游同时说明 Node 22/25/更早 24.x 存在 **SQLite 文本截断**问题(数据损坏级)`[A]`。 + +--- + +## 2. 中间版本功能差异(7.1-2 → 9.3)`[A][C]` + +| 版本 | 发布 | 新功能要点 | 破坏性 | +|---|---|---|---| +| **8.1**(= OpenClaw 2.0) | 8/31 | 重建 Web 体验、onboarding 简化、memory/session 连续性、历史对话搜索、跨设备/云 worker 会话、durable 进度卡片、结构化提问卡片、chat 内 widgets/dashboards、私密凭据请求(masked prompt)、automation permission、音视频保真;**浏览器与 Computer Use 大改**(见 §7) | OpenProse 移除;`codex/*` → `openai/*` 路由迁移;显式 `modelPolicy.allow`;named agent(`main` 语义变化);**Skill Workshop + 自动自学习**;plugin SDK 子路径弃用(9/1 移除门) | +| **8.2** | 9/1 | Linux 桌面 companion、Home dock、后台会话、**无 Gateway 也能浏览器控制**(扩展唤醒本地 relay)、4 套主题;可靠性「回复要把活干完」(settled tool work 之后给最终答案、accepted turn 之后暴露失败) | — | +| **9.1** | 9/3 | Mermaid 图、Android 补齐、更新恢复更安全、长对话/大安装开销降低;**quick-start lane 检测本机已有 Claude Code/Codex 登录与 API key 并 live 校验**;standalone agent 浏览器 host-first 默认 | 见 §7.6-4 | +| **9.2** | 9/5 | **回复在 Gateway 重启后能恢复**(active/queued/delegated)、升级保留 active settings/enabled skills/default-agent 归属、GPT-6 Astra、备份保数据(NUL/拒绝损坏归档)、**改设置不用重启** | — | +| **9.3** | 9/8 | 更新预演(隔离候选态)、性能(保住 warm prompt cache、冷会话与记忆搜索少做功、worker build 复用)、Skill Workshop 技能改 agent-owned collection | **Node ≥24.16/≥26.1**;exec-policy/approval/SDK 别名;Find/Grep & Ls 结果回调字段;retire `allowSymlinkTargetWrites` | + +上游 release notes 位置 `[C]`:`docs/releases/2026.8.1/browser-and-computer-use.md`(401 行,浏览器专项)、`docs/releases/2026.9.1.md:1483-1493`(Browser and Computer Use)、`docs/releases/2026.9.2.md:2759-2771`、9.3 见仓根 `CHANGELOG.md`(未单独出 `docs/releases/2026.9.3.md`)。 + +--- + +## 3. Patch 迁移实测 `[A]` + +在 `/tmp/oc93` pristine worktree 里逐个独立 `git apply`(37 个 patch = `patches/002`、`patches/007` + `patches/browser-camoufox-pivot/patches/01-35`): + +| 结果 | 数量 | 明细 | +|---|---|---| +| 干净应用 | **7** | 5 个 plain clean + 2 个靠 `--3way` 兜住(`01-mod-docs--tools--browser`、`13-mod-openclaw-tools.plugin-context.ts`) | +| 冲突需重做 | **21** | 002 / 007 / 02-07 / 09 / 11 / 14 / 15 / 20-23 / 25 / 26 / 29-31 | +| 硬失败 | **9** | **全部是 `del-*` 删除型**(删除 patch 要求目标文件字节级一致) | + +合计 **3777 / 4240 行 patch 需要重做** `[A]`。 + +结构性结论: + +- **9 个删除型全挂是机制问题,不是语义问题** → 建议把"删除"改成一份 rm 清单(如 `delete.txt`,由 `apply-addons.sh` 执行),以后对上游漂移永久免疫。 +- **最贵的是 browser 家族**:上游 `extensions/browser` 新增 263 / 修改 286 / 删除 18 文件(该三项计数于 `[B]` 复算一致,合计 567 = `git diff --shortstat` 的 files changed)。`browser-tool.ts` 被拆成 `browser-tool-dispatch/routing/lifecycle/screenshot/snapshot/session-tabs/binding/description` 等模块 → `05`(camoufox target + default + adapter 早返回,214 行)要按新布局重做,`03/02/04` 跟着改。 +- **patch 面还变大了**:5 个新文件引用 sandbox browser bridge(`browser-tool.routing.ts`、`browser-tool.lifecycle.ts`、`attached-browser-tool-runtime.ts`、`src/agents/sandbox/config.ts`、`src/commands/doctor-sandbox.ts`),都不在现有 37 个 patch 覆盖范围内 `[A]`;`[B]` 另测得 `extensions/browser/src` 内引用 sandbox 的非测试文件 **14 → 22**。 +- `002`(`OPENCLAW_DISABLE_WEB_SEARCH`)和 `007`(system-prompt 引导优先用 camoufox-cli)在 9.3 原生**仍然没有**,需求还在,只是文件漂移;都是一二十行,重做很便宜 `[A]`。 + +### 3.1 逐 patch 目标文件漂移量 `[B]` + +`git diff --numstat v2026.7.1-2 v2026.9.3 -- <目标文件>`: + +| patch | 目标文件 | 漂移 | 备注 | +|---|---|---|---| +| 01 | `docs/tools/browser.md` | +263 −81 | 章节结构基本保留(见 §7.6) | +| 02 | `extensions/browser/plugin-registration.ts` | +155 −49 | | +| 03 | `extensions/browser/src/browser-tool.schema.ts` | +263 −96 | `BROWSER_TARGETS` 仍是 `sandbox\|host\|node`;新增 capability 机制可借用 | +| 04 | `extensions/browser/src/browser-tool.test.ts` | **+3505 −362** | **最贵,基本重写** | +| 05 | `extensions/browser/src/browser-tool.ts` | +261 −909 | 1087 → 439 行,camoufox 分支要落到 dispatch/routing/description | +| 06 | `.../browser/bridge-server.auth.test.ts` | +108 −6 | | +| 07 | `.../browser/bridge-server.ts` | +87 −28 | | +| 08 | `.../browser/client-fetch.ts` | +164 −62 | | +| 09 | `.../browser/profile-capabilities.ts` | **+31 −1** | 几乎没动,但新增一批 `supports*` 字段 → 正好承载 camoufox 能力声明 | +| 11 | `src/agents/agent-tools.ts` | +614 −693 | | +| 12 | `src/agents/openclaw-tools.plugin-context.test.ts` | +93 −110 | | +| 13 | `src/agents/openclaw-tools.plugin-context.ts` | +31 −6 | `--3way` 可兜 | +| 14 | `src/agents/openclaw-tools.ts` | +364 −310 | | +| 15 | `src/agents/sandbox.ts` | +5 −23 | | +| 20 | `src/agents/sandbox/context.ts` | +149 −44 | | +| 21 | `src/agents/sandbox/docker-backend.ts` | +230 −57 | | +| 22 | `src/agents/sandbox/manage.test.ts` | +72 −6 | | +| 23 | `src/agents/sandbox/manage.ts` | +10 −11 | | +| 25 | `src/agents/sandbox/prune.test.ts` | +84 −7 | | +| 26 | `src/agents/sandbox/prune.ts` | +14 −13 | | +| 27 | `src/cli/sandbox-cli.ts` | +3 −6 | | +| 29 | `src/commands/sandbox-display.ts` | +44 −67 | | +| 30 | `src/commands/sandbox.test.ts` | +19 −6 | | +| 31 | `src/commands/sandbox.ts` | +7 −5 | | +| 34 | `src/plugins/tool-types.ts` | +22 −0 | | + +### 3.2 9 个 `del-*` 目标文件在 9.3 的状态 `[B]` + +**全部仍然存在,且都漂移了** → 这就是"9 个硬失败"的根因: + +| del patch 目标 | 漂移 | +|---|---| +| `extensions/browser/src/browser/routes/agent.snapshot.local-managed.test.ts` | +317 −15 | +| `src/agents/sandbox/browser.create.test.ts` | +523 −152 | +| `src/agents/sandbox/browser.ts` | +104 −53 | +| `src/plugin-sdk/browser-bridge.test.ts` | +115 −41 | +| `src/agents/sandbox/browser-bridges.ts` | +33 −9 | +| `src/agents/sandbox/novnc-auth.ts` | +16 −44 | +| `src/plugin-sdk/browser-bridge.ts` | +6 −6 | +| `src/agents/sandbox/browser.novnc-url.test.ts` | +0 −6 | +| `src/security/audit-sandbox-browser.test.ts` | +0 −0 | +| `src/commands/doctor.warns-per-agent-sandbox-docker-browser-prune.e2e.test.ts` | +2 −2 | + +> 结论:**9.3 没有放弃 sandbox browser / noVNC**(配置面 `agents.defaults.sandbox.browser.*`、专用 docker 网络 `openclaw-sandbox-browser`、`enableNoVnc`/`noVncEnabled` 全在,见 `src/config/types.sandbox.ts:71-94` `[B]`)。我们"删 sandbox 整条路"的 pivot 目标与上游方向相反,删除面只会越来越大。 + +--- + +## 4. awada 插件破坏面 `[A]`(生产链路,优先级最高) + +- 9.3 **删掉了 `src/plugin-sdk/index.ts`**,`openclaw/plugin-sdk` 裸导入从 `exports` 消失 → awada **8 处 import** 会 `ERR_PACKAGE_PATH_NOT_EXPORTED`,**channel 加载即死**。 +- `ClawdbotConfig` 在 9.3 全仓 **0 命中**(已改名 `OpenClawConfig`)→ awada **7 个文件**要改。 +- 其余符号都还在:`DEFAULT_ACCOUNT_ID` / `ChannelPlugin` / `PluginRuntime` / `createTopLevelChannelDmPolicy` / `jsonResult` / `readStringParam` / `ChannelMessageActionAdapter` / `BaseProbeResult` / `RuntimeEnv` / `AgentToolResult`;adapter 的 `plugin-sdk/agent-core` 导入方式与上游一致,不用改。 +- 契约文件漂移量:`channels/plugins/types.core.ts` ±228、`plugins/plugin-api.types.ts` +476、`plugin-sdk/core.ts` ±101。 +- `[B]` 补充:`src/agents/sandbox` + `src/plugin-sdk` 两目录合计 **742 files changed, +56,738 / −21,580**;plugin-sdk 侧 **删除** `browser-host-inspection.ts`、`browser-node-host.ts`,**新增**导出子路径 `./plugin-sdk/browser-cdp`。 + +--- + +## 5. 工具链与治理门槛 + +### 5.1 工具链 `[A][B]` + +- Node:9.3 要 **≥24.16.0(或 ≥26.1.0)** `[B]`;上游明确 Node 22/25/更早 24.x 有 **SQLite 文本截断**(数据损坏级)`[A]`。 +- 我们 `build-dist.yml` 锁 **`24.15.0`**,共 5 处:`44`(build env)、`152-155`(linux-x64 / mac-arm64 / mac-x64 / win-x64 portable tarball)`[B]`;`ci.yml:31` 用浮动 `'24'` `[B]`。本机 node 当时为 `v24.16.0`,刚过线零余量 `[A]`。 +- pnpm `packageManager` **11.2.2 → 12.3.4** `[B]`,CI 里 bundle 的 standalone pnpm 要跟着换(`lockfileVersion` 仍是 9.0)`[A]`。 +- Docker 基础镜像用浮动 `node:24-bookworm`,建议钉死 `[A]`。 +- **注意**:Node 抬版这条**与 openclaw 版本无关**——只要把分发的 portable Node 抬到 ≥24.16 就拿到 SQLite 截断防护,留在 7.1-2 也该做。 + +### 5.2 配置 / 治理 `[A]` + +- **Skill Workshop 默认 `autonomous.mode="auto"` + `approvalPolicy="auto"`**(自动抓经验并自动落地技能)→ 与我们"仓是唯一真源 + 专家包按需加载"直接冲突,必须在 `config-templates/openclaw.json` 显式关掉。 +- 9.3 把 Workshop 技能改成 **agent-owned collection** 并 **retire `allowSymlinkTargetWrites`** → `skill-wrappers.sh` 的软链方案要复验。 +- skills 源码从 `src/agents/skills/` 搬到 **`src/skills/`** → 仓根 `AGENTS.md` 里引的 `frontmatter.ts` / `config-eval.ts:124` 路径要更新。 +- 专家包依赖的"扫到 SKILL.md 停止下钻"在 9.3 **仍成立**(`MAX_GROUPED_SKILL_SCAN_DEPTH=6`,depth>0 命中 SKILL.md 即停),但建议实测一次。 +- `plugins.entries.phone-control` 上游已 retire(doctor 会清);OpenProse 移除(我们没用);8.1 的 `codex/*`→`openai/*` 路由迁移与 `modelPolicy.allow` 与我们模板无关。 + +--- + +## 6. 用户诉求 ↔ 版本归属(关键判定)`[A]` + +诉求(用户 2026-09-10 原话归纳):①可以直接搜索本机已安装的 codex / claude code 作为 provider,onboard 流程简化;②更低的对话 token 消耗(社区用户诟病最大)。 + +**核查做了三层**(commit 列表 / PR 号 `git log --grep` / 文件存在性 `ls-tree`),结论一致:**两个都不在 ES 7.33 上**。 + +- ES 7.33 相对 7.1-2 只有 **38 个 commit**:1 个 `chore: prepare extended-stable 2026.7.33 (#133000)` + 37 个维护(deps advisory pin、上游自己的 npm/docker 打包、plugin 依赖扫描与 scaffold 校验、codex managed runtime → 0.153.4、Telegram workspace media、openshell / opencode-go 稳定性、tlon SSE 超时、release/QA)。**没有一个是 onboarding 或 token/compaction 功能**。改动构成:967 文件里 271 个是新测试、138 个是 package.json/lock pin。 +- PR 号级核查(在 ES 分支逐个 `git log --grep`,全部 0 命中): + - 能力①:#126515 #108977 #107086 #137550 #137561 + - 能力②:#123402 #123622 #130993 #131977 #133094 #123737 #127110 #133912 #134259 #134987 #127506 #140449 #140730 #140799 #140840 #141141 #140566 #140651 #140698 #140713 #140744 #140797 +- 文件级证据: + - 能力①落地文件只在 9.x 出现:`src/wizard/setup.inference-verification.ts`、`setup.memory-import.ts`、`setup.app-recommendations.ts`、`plugin-capability-consent.ts`、`setup.default-agent.ts`,外加整套新增的 `src/commands/onboard-non-interactive/`。7.1-2 与 ES 7.33 **都没有这些文件**;两版 `src/wizard/setup.ts` 里 `claude` / `codex` 关键字命中 **0 次**。7.1 只有 `agent-auth-discovery*.ts` 那一层(给 `openclaw attach` / catalog 会话用,不是"装完直接识别本机 Codex/Claude 登录开聊")。 + - 能力②:`src/context-engine/` 文件数 7.1-2 = **13**、ES 7.33 = **13**、9.3 = **18**(新增 `compaction-watchdog.ts`、`context-engine-abort.ts`、registry runtime adoption、host-param projection)。ES 7.33 里唯一沾 "token" 字样的改动是 `src/auto-reply/tokens.ts`——是**模型控制 token(sentinel)剥离正则**的文本保真修复,与对话 token 消耗无关;`post-compaction-context.ts` 那处是 UTF-16 安全截断。 + +### 6.1 两项能力的版本归属 `[A]` + +| 能力 | 打底 | 真正兑现 | +|---|---|---| +| ①发现本机 Codex/Claude Code + onboarding 简化 | 8.1(Import from another agent #126515、记忆导入检测 #108977、native catalog terminals #107086) | **9.1**:quick-start lane 检测已有 Claude Code/Codex 登录与 API key 并 live 校验;Model Setup 区分 account vs API-key 并显示运行时上报邮箱;catalog "+" 直接开原生 CLI;claude-cli 走 PATH shim / Windows PATHEXT。9.2 再补 account discovery | +| ②更低对话 token 消耗 | 8.1(Anthropic 服务端 compaction #123402、xAI #123622、compaction 三修 #130993/#131977/#133094)→ 8.2(停止 byte-triggered 重复 compaction #123737/#127110/#133912/#134259)→ 9.2(被丢弃 tool result 的 token 计入 #134987) | **9.3 是大头**:prompt cache continuity 一整串(#140566/#140651/#140698/#140713/#140744/#140797/#140799)、warm prompt cache + 冷会话/记忆搜索少做功(#140449/#140730/#140840/#141141)、compaction 按完整 pending request 定尺寸(#127506)、长对话与 retained memory(#136293/#139074…) | + +→ **能力① 最低要 9.1,能力② 要拿满得 9.3。** + +### 6.2 两个"升了也不会自动兑现"的前置判断 `[A]` + +1. **能力① 是 onboarding 期能力**,而社区用户走的是我们的 `install.sh` + 预置 `config-templates/openclaw.json`(provider 写死 `bailian-token-plan`),**根本不进上游 wizard**。要兑现只有两条路:让 `install.sh` 把首启 onboarding 交回上游(我们只注入 skills/plugins/awada),或自己做一个"检测本机 codex/claude 登录"的脚本/skill。**这是产品决策,升级本身带不来。** +2. **能力② 的收益要拆三类**: + - prompt cache continuity → 只有 provider 有缓存语义才省钱(Anthropic 直连 / Gemini / Bedrock / Responses 覆盖)。我们主力是百炼网关:`api: "anthropic-messages"` 路由大概率吃得到,纯 openai-compat 自定义网关未必 → **迁移前应在 9.3 上用百炼路由实测一次 usage 里的 cached token 字段**,不要假设。 + - compaction 类修复(少重复塞历史)→ **对无缓存的 GLM/DeepSeek 也直接生效**,这是最普适的真降 token,也最可能对上社区诟病。 + - 9.2/9.3 的长对话优化 → 降 CPU/延迟,**不降 token**,别算进收益。 + +### 6.3 不升级也能立刻做的一档 `[A]` + +`skills.limits.maxSkillsInPrompt` / `maxSkillsPromptChars` / `maxSkillsLoadedPerSource` / `maxSkillFileBytes` 在 **7.1-2 就有**(`src/config/types.skills.ts:50-56`,字段与 9.3 完全一致),但我们模板里 `skills` 只写了 `entries`、**没设 limits**(`[B]` 复核 `config-templates/openclaw.json` 确认:`skills` 只有 `entries`,`agents.defaults` 只有 model / imageModel / memorySearch / models / compaction / thinkingDefault / maxConcurrent / subagents)。`agents.defaults.contextInjection` 同理可用。→ 建议先量一版"单轮 token 构成"(system prompt / 技能块 / bootstrap 文件 / 历史),再定升级优先级。 + +### 6.4 ES 7.33 作为过渡的实测支撑 `[A]` + +- 37 个 patch 的目标文件 **零漂移**(`git diff` 全空)→ patch 全绿,**不需要 re-port** +- awada 依赖的契约文件 **零改动**(`plugin-sdk/index.ts`、`channels/plugins/types.core.ts`、`plugins/plugin-api.types.ts`、`plugin-sdk/core.ts`、`agent-runtime.ts` 全部 0 diff)→ **awada 不用改一行** +- `engines` 仍是 node `>=22.22.3 <23 || >=24.15.0 <25 || >=25.9.0`、`packageManager` 仍 pnpm 11.2.2 → **工具链不用抬** +- 白拿:安全 advisory pin、docker/plugin 安装修复、codex runtime 0.153.4、Responses stream 修复 +- 净收益判断:对我们只剩「安全 advisory pin + 2 个 plugin 扫描修复」(其余 docker/codex/Telegram/openshell 都与我们无关——我们有自己的 Dockerfile、模板里 `plugins.entries.codex.enabled=false`、不用 Telegram) +- **未 backport 到 ES 7.33 的可靠性修复**:#133520 / #133979 / #138071 / #138519("回复停在工具输出 / 重启后丢回复"那一类)→ 这是 9.x 迁移的触发条件之一 + +--- + +## 7. 浏览器子系统专项对比(7.1-2 → 9.3)`[B]`(除注明外全部本次实测) + +### 7.1 体量 + +| 口径 | 7.1-2 | 9.3 | +|---|---|---| +| `extensions/browser` 全量 diff | — | **567 files, +100,528 / −23,896**(A 263 / M 286 / D 18 / R 0) | +| `extensions/browser/src` 文件数 | 338 | **526** | +| └ `src/browser/` | 251 | **415** | +| └ `src/browser/extension-relay/` | 11 | **48** | +| └ `src/browser/screencast/` | 0 | **9**(+ `routes/agent.screencast.ts`) | +| └ `src/browser/routes/` | 41 | 49 | +| └ `chrome-extension/` | 11 | **61** | +| 全仓 browser/camoufox/patchright/novnc 相关文件 | 457 | **976** | +| 工具入口 `browser-tool.ts` 行数 | **1087** | **439**(+261 −909) | +| `extensions/browser/src` 内引用 sandbox 的非测试文件 | 14 | **22** | +| `extensions/cua-computer` 文件数 | **0** | **39** | +| `src/worker` 文件数 | **0** | **68** | + +### 7.2 工具层:从"一个大文件"到"注册壳 + 8 个模块" + +9.3 的 `browser-tool.ts` 只负责注册与编排,逻辑拆到: + +| 新模块 | 职责 | +|---|---| +| `browser-tool-dispatch.ts` | action 派发(`executeBrowserTabAction`) | +| `browser-tool.routing.ts` | host / sandbox / node 目标解析、baseUrl、timeout(`resolveBrowserBaseUrl`、`resolveBrowserToolNodeTarget`) | +| `browser-tool.lifecycle.ts` | `doctor` / `status` / `start` / `stop` / `profiles` / **`importprofile`**(`:79,158-164`) | +| `browser-tool.screenshot.ts` / `.snapshot.ts` | 截图 / 快照管线 | +| `browser-tool-session-tabs.ts` | 会话 tab 注册表 | +| `browser-tool-binding.ts` | run/tab 绑定(`BROWSER_TAB_BOUND_ACTIONS`,`:49`) | +| `browser-tool-description.ts` | **按能力动态生成工具描述**(`:19-21`) | +| `attached-browser-tool-runtime.ts` | attach 到已有 CDP 端点的独立运行时(7.1-2 无此文件) | +| `browser-node-proxy/-routing/-commands/-fallback`、`browser-proxy-envelope/-upload` | node 代理链路重写 | +| `browser-runtime-state.ts`、`system-profile-api.ts` | 运行时状态 / 系统 profile 枚举 | + +### 7.3 模型可见的能力变化 + +- **actions 19 → 24**(`browser-tool.schema.ts:32` vs 9.3 同文件):新增 `importprofile`、`requests`、`errors`、`text`、`emulate` `[C: docs/tools/browser.md:1004,1010-1013]` + - `text`:抽正文(首个 `selector` 命中,否则 `article`/`main`/`body`),`maxChars` 默认且上限 40,000 + - `requests` / `errors`:读网络日志 / 页面错误,支持 `filter`、`limit`(默认 50)、`clear`;页面内容与错误按**不可信外部内容**处理 + - `emulate`:`device`(Playwright 设备名)、`colorScheme`、`timezoneId`、`locale`,按此顺序应用、返回 `applied`,**非原子** + - `importprofile`:macOS 从 Chrome/Brave/Edge/Chromium 系统 profile 导 cookie(Keychain/Touch ID 授权),仅 cookie(不含 localStorage/IndexedDB),DBSC 设备绑定会话仍可能要重登;必须跑在 browser host 上(`browser-tool.lifecycle.ts:158-164` 会抛 "system profile import must run on the browser host");配套 CLI `openclaw browser import-profile` / `system-profiles` / `cookie-sync`(推送到远端 Gateway 的 managed profile)`[C: docs/cli/browser.md:105-133]` +- **`navigate` 现在内联返回新页面 snapshot**(efficient interactive tier,payload 有界),批量 `act` 跨文档导航会暂停并带 `pageState` → **少一轮调用,直接省 token** `[C: docs/tools/browser.md:1014-1018; PR #114814]` +- **能力声明化**:`resolveBrowserToolCapabilities({tabBound, evaluateEnabled, profileCapabilities})` 按 profile 的 `supports*` 裁 actions,`browser-tool-description.ts` 同步改描述 → **模型看不到就不会乱调**(9.3 新机制,7.1-2 无) +- `BROWSER_TARGETS` **未变**,仍是 `["sandbox","host","node"]`(7.1-2 `schema.ts:54` / 9.3 `:63`);profile driver 枚举仍封闭:`"openclaw" | "clawd" | "existing-session" | "extension"`(`src/config/types.browser.ts:20`)→ **没有自定义 driver 扩展点** + +### 7.4 三条 7.1-2 完全没有的新链路 + +1. **Screencast 实时看屏**:`POST /screencast` 铸单次令牌(48 位 hex、短时效)→ WS `/browser/screencast?token=`;每 profile+tab 共享一路 CDP screencast(Chrome 推 JPEG);不支持时回落截图并返回 `SCREENCAST_UNSUPPORTED`(`reason: existing-session | playwright | node`)`[C: docs/tools/browser-control.md:26,88-140]`。Control UI 的 Browser 面板可实时看 agent 页面、转发点击/滚轮/键盘、viewport 跟随面板尺寸 `[C: docs/web/control-ui/panels.md:83]`。7.1-2 全仓 `screencast` **0 命中**。 +2. **Chrome 扩展 relay 重做**:standalone relay daemon + native-host 唤醒(**#128379**,即 8.2 的"没有 Gateway 也能控浏览器");`owner-*` 协议族(`owner-server/client/playwright/protocol/auth-client`)、**auth-v2**(`auth-v2-crypto.ts`、`auth-v2-websocket.ts`)、`preauth-websocket-guard.ts`(准入分区 **#134241**)、`relay-access/fetch/request/runtime/session-owner/target`;扩展侧支持每 tab 独立 copilot(#109817)、把页面/文档/选中文字一次性交给主会话(#111158)、Puppeteer 客户端接扩展 CDP(#117915)、扩展版本漂移告警(#119641);`chrome-extension/` 从 11 → 61 文件(大量 relay/navigation/creation-lifecycle/native-cleanup/bootstrap-diagnostics 测试)。 +3. **Computer Use + cloud worker 浏览器**:`extensions/cua-computer`(0 → 39 文件,含 `src/browser-actions.ts`)走**操作系统层**(截图 + 像素/辅助功能)控制配对 Mac(与显式开启的 Windows)上的 app/window,Linux 仍 experimental;`src/worker/browser-runtime.ts` 自述为 "Core-private adapter for the bundled Browser plugin's **attached** worker runtime",提供 `createAttachedBrowserToolRuntime({cdpUrl, ensureAttachTarget, agentSessionKey, workspaceDir})` + 30s 启动超时 → worker/独立进程**不借 Gateway 凭据**即可 attach CDP 并挂出 browser 工具。 + +### 7.5 四个 driver 到底是什么 + +**先纠正**:四个名字里只有**三种真实驱动**,`clawd` 是 `openclaw` 的历史别名(clawdbot 时代遗留)。证据: + +- `src/config/schema.help.runtime.ts:37`:`Per-profile browser driver mode. Use "openclaw" (or legacy "clawd") for CDP-based profiles, "existing-session" for Chrome DevTools MCP attachment, or "extension" for the authenticated Chrome extension relay.` +- `extensions/browser/src/browser/config.ts:507-509`:只有 `existing-session` / `extension` 保留原值,**其它一律归一为 `"openclaw"`** +- `extensions/browser/src/doctor-browser.ts:24`:`LEGACY_CLAWD_BROWSER_PROFILE_NAME = "clawd"`,`doctor --fix` 会归档 legacy clawd profile 残留(`:402-430`) +- `extensions/browser/src/browser/routes/basic.ts:463-467` 仍接受 `clawd`,错误文案三个并列 +- `docs/tools/browser.md:347`:远端端点场景下 schema **拒绝** `openclaw` / `clawd` profile + +| driver | 是什么 | 谁启动浏览器 | 传输 | 能力 | +|---|---|---|---|---| +| **`openclaw`(≡ `clawd`)** | OpenClaw 自管的隔离浏览器(内置 profile 名 `openclaw`),独立 user-data-dir | OpenClaw(或 attach 已有的) | loopback / remote **CDP** + Playwright | **最全**:per-tab WS、`/json` 端点、reset、managed tab limit、batch、pdf、download、requests/errors/text/emulate 全支持 | +| **`existing-session`** | attach 用户**真实登录的 Chrome**(内置 profile 名 `user`) | 用户的 Chrome(OpenClaw 只 attach) | **Chrome DevTools MCP** 子进程,默认 `npx -y --audit=false chrome-devtools-mcp@1.8.0 --autoConnect` | **被砍**:无 batch/pdf/download/responsebody/requests/errors/text/emulate;act 只能用 snapshot ref(CSS selector 不行,`click-coords` 例外);click 仅左键;`type` 无 `slowly`;`wait --load networkidle` 不支持;snapshot 强制 `ai` 格式;首次 attach 有阻塞式 "Allow remote debugging?" 弹窗,**必须有人在电脑前** | +| **`extension`** | 经 OpenClaw Chrome 扩展 relay 驱动用户已登录的浏览器(内置 profile 名 `chrome`) | 用户的 Chrome | 扩展 relay(loopback 端口)+ Playwright 连 relay 端点 | 中等:`usesPersistentPlaywright=true`、`requiresCompleteTargetEnumeration=true`、`headless` 强制 false、`attachOnly=true`;**无远程调试弹窗 → 人不在电脑前也能用**(手机 Telegram/WhatsApp 场景官方推荐) | + +`extension` 的端口与凭据机制(`config.ts:516-545`):每个 extension profile 分配独立 loopback relay 端口(显式 `cdpPort` 优先,否则 `resolveExtensionRelayPorts` 自动分配,避免多 profile 抢同一端口静默失败);内部客户端用**进程级一次性 token** 拼进 URL(`http://:@127.0.0.1:`),持久 relay key 只用于 HMAC 证明、**绝不进 URL 或 header**。 + +`existing-session` 的可定制面(`docs/tools/browser.md:862-909`):`mcpCommand`(**任意可执行文件**,绝对路径原样解析)、`mcpArgs`(原样透传;`mcpCommand=npx` 时上游仍会前置 `-y --audit=false chrome-devtools-mcp@1.8.0`)、`userDataDir`、`cdpUrl`(`http(s)://` → `--browserUrl`,`ws(s)://` → `--wsEndpoint`;`mcpArgs` 里显式端点覆盖 `cdpUrl`;**一旦选了端点,`userDataDir` 被忽略**)。target/ref 作用域绑单个 MCP 子进程,进程重启后要重跑 `browser tabs`、重选 target、重拍 snapshot。 + +### 7.6 控制形态盘点:协议层还是三种,控制面多了两种 + +**协议/驱动层:没有第四种。** 仍是「自管 CDP / Chrome DevTools MCP / Chrome 扩展 relay」,CDP 是其中两条的共同底座(扩展 relay 本质是把 relay 端点当 CDP 端点交给 Playwright 连)。而且这三种 **7.1-2 就已全有**:两版 `docs/tools/browser.md` 大纲逐条比对,`Local vs remote control`、`Node browser proxy (zero-config default)`、`Browserless (hosted remote CDP)`、`Direct WebSocket CDP providers`(Browserbase / Notte)、`Existing session via Chrome DevTools MCP`、`Use Brave or another Chromium-based browser`、`Browser selection` 这些章节 **7.1-2 已存在**;9.3 只多了 `Browser panel in the Control UI`(:155)与 `Tab cleanup ownership`(:245)两节。 + +同一个 `openclaw` driver 内部的三个子形态(`docs/tools/browser.md:487-520`、`profile-capabilities.ts`): + +| 子形态 | capability mode | 判定 | +|---|---|---| +| 本地启动(自管) | `local-managed` | loopback + 非 attachOnly(`isLocalManagedProfile` = `driver==="openclaw" && cdpIsLoopback && !attachOnly`,`config.ts:227`) | +| loopback 外部 CDP + `attachOnly:true` | `local-managed`,但 `browserFilesystemLocal=false` | 例:Browserless in Docker 发布到 127.0.0.1。**不配 `attachOnly` 会被误当自管 profile** | +| 非 loopback `cdpUrl` | `remote-cdp`(`isRemote=true`、`usesPersistentPlaywright=true`) | 托管服务 Browserless / Browserbase / Notte;URL 可带 `?token=` 或 HTTP Basic,OpenClaw 在 `/json/*` 与 CDP WS 上保留凭据 | + +**9.x 新增的控制面(不是新协议)**: + +1. **Computer Use(`extensions/cua-computer`)** = 真正意义上的第四种形态:**不走浏览器协议,走操作系统层**(截图 + 像素/辅助功能)控制配对 Mac / 显式开启的 Windows 上的 app/window,其中包含浏览器窗口;Linux experimental。9.2 修了"CUA 窗口图里的点击/拖拽要按 OpenClaw 做的缩放换算"、"截图型 CUA 会话可暂停后看新图再继续"(`docs/releases/2026.9.2.md:2769`)。 +2. **cloud worker attached browser runtime**(`src/worker/` 0 → 68 文件)。 +3. **node browser proxy 重写**(形态旧、链路新):节点靠 `caps:["browser"]` 或 `browser.proxy` 命令声明(`browser-node-commands.ts`、`browser-node-routing.ts`);策略 `gateway.nodes.browser.mode = off|auto|manual`;新增 `browser.proxy.upload.v1`(远端上传需节点批准,未批准会给明确提示);代理**永不**放行 `create-profile`/`delete-profile`/`reset-profile`;`nodeHost.browserProxy.allowProfiles` 是最小权限边界;动作一旦落到节点,后续 snapshot/设置就钉在该节点不再切浏览器。 +4. **9.1 默认值变化(破坏性)**:standalone(`openclaw agent exec`)在没选 Gateway/node 路由时**默认用宿主机浏览器**、不需要 Gateway 凭据;依赖隐式 node 发现的要显式 `gateway.nodes.browser.mode="auto"`(`docs/releases/2026.9.1.md:1489-1493` 明说这是有意变更)。 +5. **人成为共同驾驶者**:Control UI Browser 面板 + screencast(见 §7.4-1);macOS 可把外链开成原生 WebKit tab(9.3 CHANGELOG:13,42,#140988 #141031)。 +6. **sandbox Docker 浏览器依然健在**(见 §3.2 注)。 + +自管浏览器的二进制选择顺序(`docs/tools/browser.md:920-940`):**Chrome → Brave → Edge → Chromium → Chrome Canary**,可用 `browser.executablePath` 覆盖;Linux 会扫 `/usr/bin`、`/snap/bin`、`/opt/google`、`/opt/brave.com`、`/usr/lib/chromium`、`/usr/lib/chromium-browser`,以及 `PLAYWRIGHT_BROWSERS_PATH` / `~/.cache/ms-playwright` 下 Playwright 托管的 Chromium。→ **"内置 Chromium" 的准确表述是"自管的 Chromium 家族浏览器"**,且**只支持 Chromium 家族**。 + +### 7.7 配置面破坏性变化 `[B]` + +`src/config/types.browser.ts`:**14 insertions / 36 deletions**(9.3 全文仅 77 行)。 + +| 变化 | 内容 | +|---|---| +| **删除** | `ssrfPolicy.hostnameAllowlist`;`tabCleanup.idleMinutes` / `maxTabsPerSession` / `sweepMinutes`(只剩 `enabled`);`actionTimeoutMs`、`localLaunchTimeoutMs`、`localCdpReadyTimeoutMs`、`remoteCdpTimeoutMs`、`remoteCdpHandshakeTimeoutMs`、`cdpPortRangeStart` | +| **新增** | `browser.allowSystemProfileImport`(默认 true,macOS)、`browser.extensionRelay.allowLegacyAuth`(默认 true)、`ssrfPolicy.blockedHostnames`(支持 `*.example.com`,**覆盖 allow**)、`ssrfPolicy.allowRfc2544BenchmarkRange`、`ssrfPolicy.allowIpv6UniqueLocalRange` | +| **改语义** | `ssrfPolicy` 变成共享类型别名 `BrowserSsrFPolicyConfig = SsrFPolicyConfig`(新文件 `src/config/types.ssrf.ts`,7.1-2 无此文件);`color` 标 `@deprecated`(doctor-only,canonical schema 会拒);extension driver 的 relay 端口改为每 profile 自动分配 | +| **plugin-sdk** | 新增导出子路径 `./plugin-sdk/browser-cdp`;**删除** `browser-host-inspection.ts`、`browser-node-host.ts` | + +**对我们模板的影响(已核)**: + +- ✅ `config-templates/openclaw.json` 与 `openclaw-awk.json` 里的 `browser` 块只有 `enabled` / `headless` / `ssrfPolicy.dangerouslyAllowPrivateNetwork: true` 三项 —— **9.3 全部仍然有效**。`navigation-guard.ts` 两版都是"只有显式 `=== false` 才拦私网"(7.1-2 `:66`、9.3 `:85`),语义未变。 +- ⚠️ 若将来有人用 `browser.ssrfPolicy.hostnameAllowlist`,**9.3 会失效**(改用 `allowedHostnames` + `blockedHostnames`)。 +- ⚠️ `tabCleanup` 与各类超时旋钮失去可配性 → 若线上曾靠调 `actionTimeoutMs` / `maxTabsPerSession` 解决问题,9.3 上无对应开关。 + +### 7.8 对 camoufox pivot 的判断 + +1. **camoufox 不能靠配置接进 9.3**:attach 路线是 CDP + Playwright(`cdpUrl` / `attachOnly` / `executablePath` 都只指 Chromium 家族),camoufox 是 Firefox 系**无 CDP**;driver 枚举封闭、无自定义 driver 扩展点 → pivot 的"新 target/driver + adapter"在 9.3 仍然是 patch 工程。 +2. **但 9.3 让 pivot 更划算**:capability gating + description 动态生成,意味着现在 adapter 里"不支持的 action 返回错误引导 `target=host`"(`patches/browser-camoufox-pivot/README.md` 认的 R4 残留:console / dialog / `act:drag|clickCoords` / resize)可以改成 `supports*` 声明——模型根本看不到这些 action,既减少无效调用又省 token;插入点也从"改 1087 行大文件"变成"改 dispatch / routing / description 三个小模块"。 +3. **9.3 浏览器新价值我们大部分吃不到**:screencast / Control UI 面板 / 扩展 relay / macOS cookie 导入 / Computer Use / cloud worker 都对着"有人在看、有 GUI、有 Mac、在云上"的场景;我们是 headless Linux 容器 + 反检测。真正有用的只有 §7.3 那三项(`navigate` 内联 snapshot、`text`/`requests`/`errors`、能力裁剪)——**全部指向省 token / 省轮次**。 +4. **一条值得评估的零 patch 备选路线**:`existing-session` 的 `mcpCommand` 是"任意可执行文件",只要求保持 Chrome MCP 的连接参数语义。理论上可写一个 **camoufox 后端的 chrome-devtools-mcp 兼容 server**,配 `driver:"existing-session"` + `mcpCommand: <我们的 bridge>`,**零 patch** 让 openclaw 驱动 camoufox。代价明确:拿不到 batch/pdf/download/requests/errors/text/emulate,act 只能用 snapshot ref,snapshot 强制 `ai` 格式,target/ref 绑子进程生命周期,且 `cdp-reachability-policy.ts:92-99` 在受管 CDP 策略下会拒带显式 `cdpUrl` 的 existing-session profile。**未验证可行性,仅列为待评估选项。** + +> 单看浏览器维度:**收益 = 省 token / 省轮次;成本 = browser 家族 30 个 patch 里的重头(04 / 05 / 03 / 11 / 14 + 9 个删除型)**。只为浏览器不值;要值就得与 §6 的能力①②一起算总账。 + +--- + +## 8. 第三方 pin 与本轮已完成动作 + +### 8.1 pin 现状 `[A]`(版本号文件于 `[B]` 复核) + +| 包 | 原 | 现(`openclaw-weixin.version.json`) | 备注 | +|---|---|---|---| +| `@tencent-weixin/openclaw-weixin` | 2.4.6 | **2.4.8** | peer `openclaw>=2026.5.12`,与 7.1-2 兼容 | +| `@wecom/wecom-openclaw-cli` | 1.1.0 | **1.1.1** | 安装流程更稳健(`plugins install` 透传 `--force`、多源 failover 不再删插件目录等) | +| `@tencent-weixin/openclaw-weixin-cli` | 2.1.4 | 2.1.4(不变) | 仍是 npm latest | +| camoufox-cli(上游) | 0.7.3 | 0.7.3 | 上游最新 = 我们 fork baseline,本轮不用跟 | + +commit **`d5e112c`**(`chore(deps): bump openclaw-weixin 2.4.6 -> 2.4.8, wecom-openclaw-cli 1.1.0 -> 1.1.1`),改 5 个文件 + `CHANGELOG.md` v5.7.1 条目;`[B]` 复核已在 `origin/master` 上(当次会话内 push 两次 rc=124 失败,后续成功)。同步把 `scripts/install.sh` / `scripts/install-atomgit.sh` / `docker/docker-bootstrap.sh` 三处兜底版本 2.4.6 → 2.4.8。 + +### 8.2 为什么 2.4.8 在 7.1-2 上安全 `[A]` + +2.4.8 的**唯一代码改动**是把 `createTypingCallbacks` 的 import 从 `openclaw/plugin-sdk/channel-runtime` 换到 `openclaw/plugin-sdk/channel-message`——那是为适配 **8.1 删掉旧子路径**。对 7.1-2 已 build 的 `dist/` 做过运行时校验:11 个 `openclaw/plugin-sdk/*` 子路径全部存在于 7.1-2 的 `package.json` exports;18 个具名导入中 12 个 value import 全命中(含关键的 `channel-message :: createTypingCallbacks`),6 个 type-only import 在 `.d.ts` 中也都在。**不验这一步,微信通道有可能加载即挂。** + +### 8.3 已知坑:改了 pin,已装实例不会自动升 `[A]` + +- `scripts/install.sh`:`plugins list` 里已有 openclaw-weixin 就**直接 return,不比版本** +- `scripts/update.sh`:走 `npx openclaw-weixin-cli@2.1.4 install`,该 CLI 读到 `plugins.installs[].spec` 是固定版本号时(我们正是用 `--pin` 装的)会打印「本地已安装插件为固定版本 2.4.6,跳过升级」直接 return + +→ 部署/迁移时已装实例要显式跑一次: + +```bash +cd openclaw && npm_config_registry=https://registry.npmmirror.com \ + pnpm openclaw plugins install @tencent-weixin/openclaw-weixin@2.4.8 --pin --force +``` + +wecom 那条不受影响:`install-wecom-channel.sh` 按 pin 文件 `npm pack` + sha512 校验后装,会拿到 1.1.1(需 `WISEFLOW_CONFIRM_WECOM_INSTALL=confirmed`)。 + +--- + +## 9. 当前仓库状态(2026-09-11 复验)`[B]` + +| 项 | 状态 | +|---|---| +| 基座 pin | `openclaw.version` = `OPENCLAW_VERSION=2026.7.1-2` / `OPENCLAW_COMMIT=0790d9f593ad30c940ed93b5872a8cf6d6f3cf8c`,**未动** | +| `openclaw/` 工作树 | `git log -1` = `0790d9f593a`,`git describe --tags` = `v2026.7.1-2` | +| 产品版本 | 仓根 `version` = `v5.6.6`,而 `CHANGELOG.md` **没有 v5.6.6 条目**(最新是 `v5.7.1 (2026-09-10)`,其次 `v5.7.0`)。`version` 的变更历史显示它被分支来回覆盖过(`3bbe234` 把 `v5.7.0` 改回 `v5.6.6`;`fdc03d6` 曾把 `v5.6.5` 改成 `v5.6.6`)→ **既有不一致,与本轮 pin bump 无关**(`d5e112c` 只改 5 个文件,未动 `version`),但升级/发布时要一并理顺版本口径 | +| git | `master` 领先 `origin/master` **2 个 commit**(`53d4d19`、`c5b771e`,均为 DNA / expert-video 工作);工作树有 1 个未提交修改:`crews/main/skills/published-track/SKILL.md` | +| portable Node | `.github/workflows/build-dist.yml` 仍是 `24.15.0`(行 44、152-155 共 5 处);`ci.yml:31` 用浮动 `'24'` → **§5.1 的 Node 抬版尚未做** | +| `skills.limits` | `config-templates/openclaw.json` 里**仍未设置** → **§6.3 的零成本降 token 项尚未做** | +| 本机部署实例 | 未动(`~/.openclaw` 只读铁律) | + +--- + +## 10. 待拍板项 + +| # | 决策 | 选项 | 已知成本 / 依据 | +|---|---|---|---| +| 1 | **基线选哪条** | (a) 直接迁 `2026.9.3`;(b) 先切 `extended-stable/2026.7.33`(pin `f619d7a9fa3`,或等它打 tag)过渡,再排期 9.3;(c) 暂不动 | (a) 4.5–7 人日 `[A]`;(b) ≈半天,patch/awada 零漂移,但拿不到能力①② `[A]`;能力①最低 9.1、能力②要 9.3(§6.1) | +| 2 | **portable Node 抬到哪** | 24.16+ / 直接 26(上游推荐 26) | 与 openclaw 版本解耦,留 7.x 也该做(§5.1);改 `build-dist.yml` 5 处 + `ci.yml` + Docker 基础镜像钉死 | +| 3 | **9.x 迁移的触发条件认不认** | 认 / 不认(改为现在就一次性做完) | 触发条件草案:生产出现"回复停在工具输出 / 重启后丢回复"(#133520 #133979 #138071 #138519 **均未 backport 到 ES 7.33**);或 ES 7.33 停止提交;或需要 9.x 独有能力 | +| 4 | **`install.sh` 幂等判断要不要改** | 改成"已装版本 ≠ pin 版本 → `--force` 升级" / 维持手动补装 | 十几行,根治 §8.3 的坑 | +| 5 | **浏览器路线** | (a) 按 9.3 新模块布局重画 pivot(05/03/04/09 + `del-*` 改 rm 清单 + 补 5 个新文件);(b) 先在 7.1-2 上把"能力裁剪"等价实现到自有 adapter 层(半天量级,不需动基座);(c) 评估 §7.8-4 的"MCP 桥零 patch"路线 | 见 §7.8;(a) 的工时已含在 #1 的 4.5–7 人日里 | +| 6 | **是否先做"单轮 token 构成"实测 + `skills.limits` 调参** | 做 / 不做 | 半天、立刻见效、与升级解耦(§6.3);也是判断能力② 收益基线的前置数据 | + +--- + +## 附录 A:复验命令 + +```bash +cd /home/wukong/wiseflow/openclaw # 上游工作树,两个 tag 都已在本地对象库 + +# 体量 +git diff --shortstat v2026.7.1-2 v2026.9.3 -- extensions/browser +for f in A M D R; do echo -n "$f: "; git diff --name-only --diff-filter=$f v2026.7.1-2 v2026.9.3 -- extensions/browser | wc -l; done +for t in v2026.7.1-2 v2026.9.3; do echo -n "$t "; git ls-tree -r --name-only $t -- extensions/browser/src | wc -l; done +git show v2026.7.1-2:extensions/browser/src/browser-tool.ts | wc -l # 1087 +git show v2026.9.3:extensions/browser/src/browser-tool.ts | wc -l # 439 + +# 工具链 +for t in v2026.7.1-2 v2026.9.3; do git show $t:package.json | python3 -c 'import json,sys;d=json.load(sys.stdin);print(d["version"],d["engines"],d["packageManager"].split("+")[0])'; done + +# 逐 patch 目标漂移 +for p in /home/wukong/wiseflow/patches/browser-camoufox-pivot/patches/*.patch; do + f=$(grep -m1 "^+++ b/" "$p" | sed 's|^+++ b/||'); [ -z "$f" ] && f=$(grep -m1 "^--- a/" "$p" | sed 's|^--- a/||') + printf '%-70s %s\n' "$f" "$(git diff --numstat v2026.7.1-2 v2026.9.3 -- "$f" | awk '{print "+"$1" -"$2}')" +done + +# driver / 能力 / 配置面出处 +git show v2026.9.3:src/config/types.browser.ts +git show v2026.9.3:src/config/types.ssrf.ts +git show v2026.9.3:extensions/browser/src/browser/profile-capabilities.ts | head -140 +git show v2026.9.3:extensions/browser/src/browser/config.ts | sed -n '495,620p' +git show v2026.9.3:extensions/browser/src/browser-tool.schema.ts | sed -n '1,120p' +git show v2026.9.3:docs/tools/browser.md | sed -n '487,560p;860,950p' + +# 版本线拓扑 +git log --oneline -1 origin/extended-stable/2026.7.33 # f619d7a9fa3 +git rev-list --count v2026.7.1-2..origin/extended-stable/2026.7.33 # 38 +git merge-base --is-ancestor v2026.7.1-2 origin/extended-stable/2026.7.33 && echo YES # YES +git merge-base --is-ancestor v2026.7.1-2 v2026.9.3 && echo YES || echo NO # NO +git merge-base v2026.7.1-2 v2026.9.3 | xargs git log -1 --format='%h %ad %s' --date=short +MB=$(git merge-base v2026.7.1-2 v2026.9.3) +git rev-list --count $MB..v2026.9.3 # 24853 +git rev-list --count $MB..v2026.7.1-2 # 224 +``` + +## 附录 B:关键证据指针(9.3 tag) + +| 主题 | 位置 | +|---|---| +| driver 枚举 | `src/config/types.browser.ts:20` | +| driver 语义说明 | `src/config/schema.help.runtime.ts:37` | +| driver 归一化(clawd → openclaw) | `extensions/browser/src/browser/config.ts:507-509` | +| legacy clawd profile 清理 | `extensions/browser/src/doctor-browser.ts:24,402-430` | +| 远端端点拒绝 openclaw/clawd profile | `extensions/browser/src/browser/routes/basic.ts:463-467`;`docs/tools/browser.md:347` | +| extension relay 端口 / token | `extensions/browser/src/browser/config.ts:516-545` | +| existing-session 解析(MCP) | `extensions/browser/src/browser/config.ts:546-575`;`docs/tools/browser.md:752-909` | +| capability mode 判定 | `extensions/browser/src/browser/profile-capabilities.ts`(`remote-cdp` = `!cdpIsLoopback`;`local-managed.browserFilesystemLocal = !attachOnly`) | +| 工具能力裁剪 | `extensions/browser/src/browser-tool.schema.ts`(`resolveBrowserToolCapabilities`);`browser-tool-description.ts:19-21`;`browser-tool-binding.ts:49` | +| Chrome MCP 与受管 CDP 策略冲突 | `extensions/browser/src/browser/cdp-reachability-policy.ts:92-99` | +| SSRF 生效逻辑 | `extensions/browser/src/browser/navigation-guard.ts:85`(7.1-2 为 `:66`) | +| importprofile 必须在 browser host | `extensions/browser/src/browser-tool.lifecycle.ts:158-164` | +| screencast | `docs/tools/browser-control.md:26,88-140`;`extensions/browser/src/browser/screencast/*` | +| Control UI Browser 面板 | `docs/web/control-ui/panels.md:83` | +| node proxy | `extensions/browser/src/browser-node-commands.ts`、`browser-node-routing.ts`(`gateway.nodes.browser.mode`) | +| worker attached runtime | `src/worker/browser-runtime.ts`;`extensions/browser/src/attached-browser-tool-runtime.ts` | +| 浏览器二进制选择顺序 | `docs/tools/browser.md:920-940` | +| sandbox browser / noVNC 仍在 | `src/agents/sandbox/browser.ts`、`novnc-auth.ts`、`browser-bridges.ts`、`src/plugin-sdk/browser-bridge.ts`、`src/config/types.sandbox.ts:71-94` | +| 8.1 浏览器专项 release notes | `docs/releases/2026.8.1/browser-and-computer-use.md`(401 行) | +| 9.1 host-first 默认 | `docs/releases/2026.9.1.md:1483-1493` | +| 9.2 Browser & Computer Use | `docs/releases/2026.9.2.md:2759-2771` | +| 我们的 pivot 现状 | `patches/browser-camoufox-pivot/README.md`、`patches/browser-camoufox-pivot/patches/01-35`、`patches/browser-camoufox-pivot/files/camoufox-cli.adapter.ts` | From b2acb22cb687cbe330f85844982e683f44ff08ec Mon Sep 17 00:00:00 2001 From: codes-factory-of-bg Date: Fri, 11 Sep 2026 23:52:23 +0800 Subject: [PATCH 14/34] =?UTF-8?q?docs(fact-finding):=20=E8=A1=A5=20=C2=A76?= =?UTF-8?q?.5=20cherry-pick=20=E5=8F=AF=E8=A1=8C=E6=80=A7=E5=AE=9E?= =?UTF-8?q?=E6=B5=8B=EF=BC=885=20commit=20=E5=85=A8=E5=86=B2=E7=AA=81/153?= =?UTF-8?q?=20=E6=96=87=E4=BB=B6=E6=AC=A1=EF=BC=89+=209.3=20=E5=88=A0?= =?UTF-8?q?=E9=99=A4=20token=20=E8=B0=83=E4=BC=98=E6=97=8B=E9=92=AE=20+=20?= =?UTF-8?q?=E6=9B=B4=E6=AD=A3=E8=83=BD=E5=8A=9B=E2=91=A1=20PR=20=E5=BD=92?= =?UTF-8?q?=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../openclaw-2026.9.3-upgrade-fact-finding.md | 70 ++++++++++++++++++- 1 file changed, 67 insertions(+), 3 deletions(-) diff --git a/docs/openclaw-2026.9.3-upgrade-fact-finding.md b/docs/openclaw-2026.9.3-upgrade-fact-finding.md index fd860cff..b97d69b8 100644 --- a/docs/openclaw-2026.9.3-upgrade-fact-finding.md +++ b/docs/openclaw-2026.9.3-upgrade-fact-finding.md @@ -195,10 +195,12 @@ | 能力 | 打底 | 真正兑现 | |---|---|---| | ①发现本机 Codex/Claude Code + onboarding 简化 | 8.1(Import from another agent #126515、记忆导入检测 #108977、native catalog terminals #107086) | **9.1**:quick-start lane 检测已有 Claude Code/Codex 登录与 API key 并 live 校验;Model Setup 区分 account vs API-key 并显示运行时上报邮箱;catalog "+" 直接开原生 CLI;claude-cli 走 PATH shim / Windows PATHEXT。9.2 再补 account discovery | -| ②更低对话 token 消耗 | 8.1(Anthropic 服务端 compaction #123402、xAI #123622、compaction 三修 #130993/#131977/#133094)→ 8.2(停止 byte-triggered 重复 compaction #123737/#127110/#133912/#134259)→ 9.2(被丢弃 tool result 的 token 计入 #134987) | **9.3 是大头**:prompt cache continuity 一整串(#140566/#140651/#140698/#140713/#140744/#140797/#140799)、warm prompt cache + 冷会话/记忆搜索少做功(#140449/#140730/#140840/#141141)、compaction 按完整 pending request 定尺寸(#127506)、长对话与 retained memory(#136293/#139074…) | +| ②更低对话 token 消耗 | 8.1(Anthropic 服务端 compaction #123402、xAI #123622、compaction 三修 #130993/#131977/#133094)→ 8.2(停止 byte-triggered 重复 compaction #123737/#127110/#133912/#134259)→ 9.2(被丢弃 tool result 的 token 计入 #134987) | **9.3 是大头**:prompt cache continuity 一整串(#140566/#140651/#140698/#140713/#140744/#140797/#140799)、compaction 按完整 pending request 定尺寸(#127506)、长对话与 retained memory(#136293/#139074…) | → **能力① 最低要 9.1,能力② 要拿满得 9.3。** +> **2026-09-11 复核更正**:上表 ② 的原始清单里混进了两个**与对话 token 无关**的 PR,已剔除 —— `#140449` 实为 `improve: retain current worker builds between sessions`(云 worker 构建复用,我们不用 worker),`#140730` 实为 `fix(memory): avoid repeated vector search startup delays`(记忆向量检索**启动延迟**,且我们模板 `agents.defaults.memorySearch.provider = none` 本来就没开)`[B]`。剔除后,② 对我们**真正可能有效**的只剩两类:compaction 决策类、provider prompt-cache 类;后者的有效性还取决于百炼端点是否实现缓存语义(见 §6.5-4)。 + ### 6.2 两个"升了也不会自动兑现"的前置判断 `[A]` 1. **能力① 是 onboarding 期能力**,而社区用户走的是我们的 `install.sh` + 预置 `config-templates/openclaw.json`(provider 写死 `bailian-token-plan`),**根本不进上游 wizard**。要兑现只有两条路:让 `install.sh` 把首启 onboarding 交回上游(我们只注入 skills/plugins/awada),或自己做一个"检测本机 codex/claude 登录"的脚本/skill。**这是产品决策,升级本身带不来。** @@ -211,6 +213,67 @@ `skills.limits.maxSkillsInPrompt` / `maxSkillsPromptChars` / `maxSkillsLoadedPerSource` / `maxSkillFileBytes` 在 **7.1-2 就有**(`src/config/types.skills.ts:50-56`,字段与 9.3 完全一致),但我们模板里 `skills` 只写了 `entries`、**没设 limits**(`[B]` 复核 `config-templates/openclaw.json` 确认:`skills` 只有 `entries`,`agents.defaults` 只有 model / imageModel / memorySearch / models / compaction / thinkingDefault / maxConcurrent / subagents)。`agents.defaults.contextInjection` 同理可用。→ 建议先量一版"单轮 token 构成"(system prompt / 技能块 / bootstrap 文件 / 历史),再定升级优先级。 +### 6.5 「升 ES 7.33 + 单独 cherry-pick 9.3 省 token 的 commit」可行性实测 `[B]` + +> 2026-09-11 针对该提议做的专项实测。结论:**升 ES 7.33 可行且便宜;cherry-pick 不可行**。 + +**(1) 省 token 不是一个 commit,是一条压在结构重写上的链** + +| 口径(`origin/extended-stable/2026.7.33..v2026.9.3`) | 实测值 | +|---|---| +| 动过 `src/context-engine` 的 commit 数 | **39** | +| `src/context-engine` 目录 diff | **15 files, +2,620 / −1,773** | +| 该目录文件数 7.1-2 / ES 7.33 / 9.3 | 13 / 13 / **18** | +| 全仓 commit 主题含 `compaction` 的数量 | **332** | +| 同期 `src/agents` 全目录 commit 数 | 3,997 | + +`src/context-engine` 那 39 个 commit 里包含多个**结构性重写**,省 token 的修复就长在它们之上:`refactor: flip sessions and transcripts to sqlite storage (#98236)`、`refactor(sessions): remove file-era transcript runtime (#113233)`、`refactor(agents): consolidate compaction and context-engine ownership (#117482)`、`refactor(agents): consolidate context budgets and compaction recovery (#117149)`、`refactor: replace context-engine retry proxy with declared params (#115872)`。 + +**(2) cherry-pick 实测:5 个代表性 commit 全部失败** + +在 `/tmp/es733b`(ES 7.33 detached worktree,tip `f619d7a9fa3`)上逐个 `git cherry-pick -n`: + +| commit | 主题 | rc | 冲突文件 / 涉及文件 | +|---|---|---|---| +| `28f5f63ea42` | fix(agents): preserve prompt-cache prefix under aggregate truncation (#132017) | 1 | 4 / 6 | +| `110a636dd16` | fix(agents): preserve Responses cache prefixes across user turns (#140849) | 1 | 7 / 10 | +| `8e3f572ffa2` | fix(openai): honor native prompt cache settings (#140853) | 1 | 19 / 29 | +| `f0cc57d6b46` | fix(agents): preserve cached history when background work changes (#140799) | 1 | 32 / 52 | +| `60adac1aff6` | fix(agents): fit compacted context and prioritize foreground replies (#139822) | 1 | **91 / 129** | + +合计 **153 个冲突文件次**。这不是 cherry-pick,是 re-port;而且 re-port 的是**对话装配路径**——它出 bug 的表现是"回复停在工具输出 / 历史被静默截断 / 人格漂移",不是崩溃,很难被测出来(正是我们历史上 AtomCode 卡 busy、awada 回复丢失那一类故障的邻区)。 + +**(3) 更反直觉的发现:9.3 把 token 调优旋钮收走了** + +`src/config/types.agent-defaults.ts` 中,7.1-2 有而 **9.3 已删除**的字段(逐个 grep 计数 7.1-2=有 / 9.3=0): + +| 字段 | 作用 | 7.1-2 | 9.3 | +|---|---|---|---| +| `compaction.maxHistoryShare` | 历史占上下文窗口比例上限(0.1–0.9,默认 0.5) | ✅ | **删除** | +| `compaction.reserveTokens` / `reserveTokensFloor` | 压缩预留 token 与下限 | ✅ | **删除** | +| `compaction.customInstructions` | 压缩摘要附加指令(保语言/人格连续性) | ✅ | **删除** | +| `contextPruning.keepLastAssistants` | 保护最近 N 个 assistant 轮不被裁剪 | ✅ | **删除** | +| `contextPruning.softTrimRatio` / `hardClearRatio` | 软裁剪 / 硬清除的上下文压力阈值 | ✅ | **删除** | +| `contextPruning.minPrunableToolChars` | 工具结果达到多少字符才值得裁 | ✅ | **删除** | + +9.3 侧只新增 `compaction.enabled`、`compaction.thinkingLevel`,并把 `contextPruning` 简化成 `mode` / `ttl` / `tools` / `hardClear`。`AgentCompactionMode` 两版都是 `"default" | "safeguard"`(未变)。 + +→ **含义**:9.3 的 token 收益来自内部行为(缓存连续性、压缩决策),但**运维侧可调粒度变粗**。对我们这种"要给社区分发、模型上下文各异(GLM 输入上限 196,608)、需要按部署调参"的场景,丢掉 `maxHistoryShare` / `reserveTokens` 可能是**净退化**,迁移前必须评估。 + +**(4) provider 匹配性:cache 类修复对我们是否有效,尚未验证** + +我们模板的 provider 是 `bailian-token-plan`,`api = "anthropic-messages"`,`baseUrl = https://token-plan.cn-beijing.maas.aliyuncs.com/apps/anthropic` `[B]`。9.3 那批 prompt-cache 修复分别是 Anthropic CLI(#140566)、OpenAI native(#140853)、Bedrock(#140797)、Responses(#140849)路径。**百炼的 anthropic 兼容端点是否实现 `cache_control` 并在 usage 里回 `cache_read_input_tokens`,必须先实测**;若不支持,合并这批 commit 的收益为 0。 + +**(5) 不升级就能拿的三档(7.1-2 已支持,我们模板未开)`[B]`** + +| 开关 | 上游注释原文 | 我们模板现状 | +|---|---|---| +| `agents.defaults.contextPruning` | "Opt-in: prune old tool results from the LLM context to reduce token usage" | **完全未设置** | +| `skills.limits.*`(maxSkillsInPrompt / maxSkillsPromptChars / maxSkillsLoadedPerSource / maxSkillFileBytes) | 见 §6.3 | **未设置**(`skills` 只有 `entries`) | +| `compaction.maxHistoryShare` / `keepRecentTokens` / `recentTurnsPreserve` / `model` | 历史占比、保留近期 token、逐字保留轮数、**用更便宜的模型做压缩摘要** | 只设了 `compaction.mode = "safeguard"` | + +→ 建议顺序:**先量"单轮 token 构成"→ 开这三档 → 再决定是否为了 compaction 那部分整体迁 9.3**(而不是 cherry-pick)。 + ### 6.4 ES 7.33 作为过渡的实测支撑 `[A]` - 37 个 patch 的目标文件 **零漂移**(`git diff` 全空)→ patch 全绿,**不需要 re-port** @@ -397,12 +460,13 @@ wecom 那条不受影响:`install-wecom-channel.sh` 按 pin 文件 `npm pack` | # | 决策 | 选项 | 已知成本 / 依据 | |---|---|---|---| -| 1 | **基线选哪条** | (a) 直接迁 `2026.9.3`;(b) 先切 `extended-stable/2026.7.33`(pin `f619d7a9fa3`,或等它打 tag)过渡,再排期 9.3;(c) 暂不动 | (a) 4.5–7 人日 `[A]`;(b) ≈半天,patch/awada 零漂移,但拿不到能力①② `[A]`;能力①最低 9.1、能力②要 9.3(§6.1) | +| 1 | **基线选哪条** | (a) 直接迁 `2026.9.3`;(b) 先切 `extended-stable/2026.7.33`(pin `f619d7a9fa3`,或等它打 tag)过渡,再排期 9.3;(c) 暂不动;(d) **ES 7.33 + cherry-pick 9.3 省 token commit —— 已实测否决**(§6.5-2:5 个 commit 全部冲突,合计 153 个冲突文件次) | (a) 4.5–7 人日 `[A]`;(b) ≈半天,patch/awada 零漂移,但拿不到能力①② `[A]`;能力①最低 9.1、能力②要 9.3(§6.1) | | 2 | **portable Node 抬到哪** | 24.16+ / 直接 26(上游推荐 26) | 与 openclaw 版本解耦,留 7.x 也该做(§5.1);改 `build-dist.yml` 5 处 + `ci.yml` + Docker 基础镜像钉死 | | 3 | **9.x 迁移的触发条件认不认** | 认 / 不认(改为现在就一次性做完) | 触发条件草案:生产出现"回复停在工具输出 / 重启后丢回复"(#133520 #133979 #138071 #138519 **均未 backport 到 ES 7.33**);或 ES 7.33 停止提交;或需要 9.x 独有能力 | | 4 | **`install.sh` 幂等判断要不要改** | 改成"已装版本 ≠ pin 版本 → `--force` 升级" / 维持手动补装 | 十几行,根治 §8.3 的坑 | | 5 | **浏览器路线** | (a) 按 9.3 新模块布局重画 pivot(05/03/04/09 + `del-*` 改 rm 清单 + 补 5 个新文件);(b) 先在 7.1-2 上把"能力裁剪"等价实现到自有 adapter 层(半天量级,不需动基座);(c) 评估 §7.8-4 的"MCP 桥零 patch"路线 | 见 §7.8;(a) 的工时已含在 #1 的 4.5–7 人日里 | -| 6 | **是否先做"单轮 token 构成"实测 + `skills.limits` 调参** | 做 / 不做 | 半天、立刻见效、与升级解耦(§6.3);也是判断能力② 收益基线的前置数据 | +| 6 | **是否先做"单轮 token 构成"实测 + 三档配置调参** | 做 / 不做 | 半天、立刻见效、与升级解耦(§6.3、§6.5-5:`contextPruning` / `skills.limits` / `compaction.*` 三档都是 7.1-2 已支持但我们没开);也是判断能力② 收益基线的前置数据 | +| 7 | **是否先实测百炼端点的 prompt cache 语义** | 做 / 不做 | 决定 9.3 那批 cache 修复对我们是否有价值(§6.5-4);不做这一步,能力② 的收益无法量化 | --- From 8d2647e1136743dd87b20f6cd70665cf62d7839c Mon Sep 17 00:00:00 2001 From: codes-factory-of-bg Date: Sat, 12 Sep 2026 00:19:44 +0800 Subject: [PATCH 15/34] =?UTF-8?q?chore(openclaw):=20=E5=9F=BA=E5=BA=A7=20p?= =?UTF-8?q?in=202026.7.1-2=20->=20extended-stable/2026.7.33=20(f619d7a9fa3?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit openclaw.version: - OPENCLAW_VERSION=2026.7.33 / OPENCLAW_COMMIT=f619d7a9fa3db8b7e9831a0b00f586a9b55559ab - 该分支尚未打 tag / 未发 npm 包,只能 pin SHA;文件内补了 re-pin 前的四项复验清单 复验结果(v2026.7.1-2 -> ES 7.33,38 commit): - 37 个 patch 目标文件零漂移(patch 全绿,无需 re-port) - awada 契约 5 文件零 diff(plugin-sdk/index.ts、channels/plugins/types.core.ts、 plugins/plugin-api.types.ts、plugin-sdk/core.ts、agents/agent-runtime.ts) - engines.node 与 packageManager(pnpm 11.2.2) 未变 -> CI 的 24.15.0 仍合法,本轮不抬工具链 - skill 加载机制逐字未变(src/skills/loading/frontmatter.ts、src/shared/config-eval.ts、 src/skills/loading/workspace.ts 含 MAX_GROUPED_SKILL_SCAN_DEPTH=6)-> 专家包架构不受影响 - src/skills 全目录唯一改动是 installGitSkill 的 git option 注入加固,与本仓无关 仅改代码仓 pin,未触碰本机部署实例(~/.openclaw 只读)。 8.x/9.x 迁移评估见 docs/openclaw-2026.9.3-upgrade-fact-finding.md。 另含 crews/main/skills/published-track/SKILL.md:删去视频号短标题"官方称填了有更多流量"的表述。 --- crews/main/skills/published-track/SKILL.md | 2 +- openclaw.version | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/crews/main/skills/published-track/SKILL.md b/crews/main/skills/published-track/SKILL.md index d800ab53..63fb50ab 100644 --- a/crews/main/skills/published-track/SKILL.md +++ b/crews/main/skills/published-track/SKILL.md @@ -51,7 +51,7 @@ published-track init-db 每张表共享通用字段:`id`(自增主键)、`title`、`content_type`(article/video/post)、`source_folder`(原始文件夹,如 `wx_mp/outputs/xxx`,**不做唯一约束,同内容可同平台多次发布**)、`publish_url`、`publish_date`(YYYY-MM-DD)、`distribute_status`(0=待分发,1=无需分发,2=已分发)、`notes`、`created_at`、`updated_at`。各平台特有互动指标默认 0,另有 `top_comment`(主要留言摘要)。 -> **视频号(`pub_wx_channel`)特例**:`title` 列存的是**完整视频描述**(含 hashtag,最长约 300 字),即 `wechat-channels-publish` Step 5 填的视频描述;`wx-channel-engagement` 抓取按它匹配后台作品管理页。发布页改版后虽可另填**短标题**(官方称填了有更多流量),但作品管理页不展示短标题,因此**短标题不入库**——调用方调 `record.sh --platform wx_channel --title` 必须传完整视频描述,不要传短标题、也不要把两者拼接。 +> **视频号(`pub_wx_channel`)特例**:`title` 列存的是**完整视频描述**(含 hashtag,最长约 300 字),即 `wechat-channels-publish` Step 5 填的视频描述;`wx-channel-engagement` 抓取按它匹配后台作品管理页。发布页改版后虽可另填**短标题**,但作品管理页不展示短标题,因此**短标题不入库**——调用方调 `record.sh --platform wx_channel --title` 必须传完整视频描述,不要传短标题、也不要把两者拼接。 ### DNA 关联字段(v3 schema) diff --git a/openclaw.version b/openclaw.version index 71eba656..9c870884 100644 --- a/openclaw.version +++ b/openclaw.version @@ -6,5 +6,14 @@ # git clone https://github.com/openclaw/openclaw openclaw # git -C openclaw checkout $OPENCLAW_COMMIT # -OPENCLAW_VERSION=2026.7.1-2 -OPENCLAW_COMMIT=0790d9f593ad30c940ed93b5872a8cf6d6f3cf8c +# ── 2026-09-12:2026.7.1-2 → extended-stable/2026.7.33(7 系延长稳定线)── +# 该分支**尚未打 tag、未发 npm 包**,所以只能 pin commit SHA(不能 pin tag); +# `openclaw update --channel extended-stable` 对 git checkout 无效,跟版必须手动 re-pin。 +# re-pin 前必须复验四项(命令见 docs/openclaw-2026.9.3-upgrade-fact-finding.md 附录 A): +# 1) 37 个 patch 的目标文件相对上一 pin 零漂移 +# 2) awada 依赖的 5 个契约文件零 diff +# 3) package.json 的 engines.node / packageManager 未变(否则要抬 CI 与 portable Node) +# 4) skill 加载机制文件未变(专家包架构依赖,见 docs/expert-pack-dna-architecture.md §2.4/§2.5) +# 本次 pin 的 f619d7a9fa3 四项均已复验通过(详见上述文档 §1、§6.4)。 +OPENCLAW_VERSION=2026.7.33 +OPENCLAW_COMMIT=f619d7a9fa3db8b7e9831a0b00f586a9b55559ab From b1b7cb567c829d1947cf8728a95df9f1e7c2d850 Mon Sep 17 00:00:00 2001 From: codes-factory-of-bg Date: Sat, 12 Sep 2026 00:41:52 +0800 Subject: [PATCH 16/34] =?UTF-8?q?fix(install):=20openclaw-weixin=20?= =?UTF-8?q?=E5=B9=82=E7=AD=89=E5=88=A4=E6=96=AD=E6=94=B9=E4=B8=BA=E6=8C=89?= =?UTF-8?q?=E7=89=88=E6=9C=AC=E6=AF=94=E5=AF=B9=EF=BC=8C=E4=B8=8D=E7=AD=89?= =?UTF-8?q?=E5=88=99=20--force=20=E5=8D=87=E7=BA=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 问题:install_weixin_plugin() 只用 `plugins list | grep openclaw-weixin` 判断,已装即 return,不比版本 -> bump pin(如 2.4.6 -> 2.4.8)后已装实例永远停在旧版,必须人工跑 `plugins install ... --pin --force` 才升得上去。 改动(三处同源实现一起改,避免裸机 / atomgit / docker 行为分叉): - scripts/install.sh、scripts/install-atomgit.sh、docker/docker-bootstrap.sh - 新增 weixin_installed_version():主路径 `plugins list --json` 取 version 字段,回落到 $OPENCLAW_HOME/npm/projects/*/node_modules//package.json(目录名带 hash,只能 glob) - 判定:已装 == pin -> 跳过;已装 != pin -> `plugins install @ --pin --force`; 插件在但版本读不到 -> 也走 --force(正确性优先) - docker 侧同理需要比版本:/root/.openclaw 是持久卷,镜像升级后插件包不会跟着 pin 走 实测(本机实例正好是「已装 2.4.6 / pin 2.4.8」的样本,只测判断逻辑不真装): - 主路径 plugins list --json -> 2.4.6 OK - CLI 不可用时回落 glob -> 2.4.6 OK - 未安装的包名 -> 空输出 + rc=1 OK - 决策分支:pin=2.4.8 -> UPGRADE --force;pin=2.4.6 -> SKIP(不会无谓重装)OK - bash -n 三个脚本均通过 未修(已记入 fact-finding §8.3 / §10-4):scripts/update.sh 的在线路径走 weixin-cli, 遇到固定版本 spec 会自行跳过;bundled tarball 路径也没带 --force。 升级只换 npm/projects 下的包,登录态在 $OPENCLAW_HOME/openclaw-weixin/ 数据目录不受影响; 更新路线随后 refresh_gateway_env_only 会重启 gateway 使新版本生效。 --- docker/docker-bootstrap.sh | 45 +++++++++++++-- .../openclaw-2026.9.3-upgrade-fact-finding.md | 10 ++-- scripts/install-atomgit.sh | 55 ++++++++++++++++--- scripts/install.sh | 55 ++++++++++++++++--- 4 files changed, 140 insertions(+), 25 deletions(-) diff --git a/docker/docker-bootstrap.sh b/docker/docker-bootstrap.sh index 0022f655..c96cec32 100755 --- a/docker/docker-bootstrap.sh +++ b/docker/docker-bootstrap.sh @@ -100,6 +100,30 @@ log "STEP 4 done" # ─── 预装 openclaw-weixin 插件(非致命,首启可补)──────────────── # 与裸机 install.sh 的 install_weixin_plugin() 同源:读 pin 走在线 plugins install +# 幂等:**已装版本 == pin 版本**才跳过;不等则 --force 升级到 pin。 +# 容器里 /root/.openclaw 是持久卷,镜像升级后插件包不会自己跟着 pin 走,所以这里必须比版本。 +weixin_installed_version() { + local pkg="$1" oc_home="${OPENCLAW_HOME:-/root/.openclaw}" v f + v="$((cd "$PROJECT_ROOT/openclaw" && pnpm openclaw plugins list --json) 2>/dev/null | python3 -c " +import json, sys +try: + d = json.load(sys.stdin) +except Exception: + sys.exit(0) +for p in d.get('plugins', []): + if p.get('id') == 'openclaw-weixin' or p.get('name') == '$pkg': + print(p.get('version') or '') + break +" 2>/dev/null)" + if [ -n "$v" ]; then printf '%s\n' "$v"; return 0; fi + for f in "$oc_home"/npm/projects/*/node_modules/"$pkg"/package.json; do + [ -f "$f" ] || continue + v="$(python3 -c "import json;print(json.load(open('$f')).get('version',''))" 2>/dev/null)" + if [ -n "$v" ]; then printf '%s\n' "$v"; return 0; fi + done + return 1 +} + install_weixin_plugin() { local pin_file="$PROJECT_ROOT/openclaw-weixin.version.json" local pkg ver @@ -109,16 +133,25 @@ install_weixin_plugin() { fi pkg="${pkg:-@tencent-weixin/openclaw-weixin}" ver="${ver:-2.4.8}" - # 幂等检查 - if (cd "$PROJECT_ROOT/openclaw" && pnpm openclaw plugins list 2>/dev/null | grep -q "openclaw-weixin"); then - log "openclaw-weixin plugin already installed" + # 幂等检查:已装版本 == pin 才跳过 + local installed force_flag="" + installed="$(weixin_installed_version "$pkg" || true)" + if [ -n "$installed" ] && [ "$installed" = "$ver" ]; then + log "openclaw-weixin plugin already installed (${ver})" return 0 fi + if [ -n "$installed" ]; then + log "openclaw-weixin 已装 ${installed},pin=${ver} → --force 升级" + force_flag="--force" + elif (cd "$PROJECT_ROOT/openclaw" && pnpm openclaw plugins list 2>/dev/null | grep -q "openclaw-weixin"); then + log "⚠️ openclaw-weixin 已装但版本读不到;按 pin ${ver} 强制重装" + force_flag="--force" + fi log "installing openclaw-weixin plugin (${pkg}@${ver})" - if (cd "$PROJECT_ROOT/openclaw" && pnpm openclaw plugins install "${pkg}@${ver}" --pin); then - log "openclaw-weixin plugin installed" + if (cd "$PROJECT_ROOT/openclaw" && pnpm openclaw plugins install "${pkg}@${ver}" --pin $force_flag); then + log "openclaw-weixin plugin installed (${ver})" else - log "⚠️ openclaw-weixin 插件预装失败;首启可手动:pnpm openclaw plugins install ${pkg}@${ver} --pin" + log "⚠️ openclaw-weixin 插件预装失败;首启可手动:pnpm openclaw plugins install ${pkg}@${ver} --pin $force_flag" fi } log "STEP 5: installing openclaw-weixin plugin..." diff --git a/docs/openclaw-2026.9.3-upgrade-fact-finding.md b/docs/openclaw-2026.9.3-upgrade-fact-finding.md index b97d69b8..cdc6cd4f 100644 --- a/docs/openclaw-2026.9.3-upgrade-fact-finding.md +++ b/docs/openclaw-2026.9.3-upgrade-fact-finding.md @@ -426,12 +426,12 @@ commit **`d5e112c`**(`chore(deps): bump openclaw-weixin 2.4.6 -> 2.4.8, wecom- 2.4.8 的**唯一代码改动**是把 `createTypingCallbacks` 的 import 从 `openclaw/plugin-sdk/channel-runtime` 换到 `openclaw/plugin-sdk/channel-message`——那是为适配 **8.1 删掉旧子路径**。对 7.1-2 已 build 的 `dist/` 做过运行时校验:11 个 `openclaw/plugin-sdk/*` 子路径全部存在于 7.1-2 的 `package.json` exports;18 个具名导入中 12 个 value import 全命中(含关键的 `channel-message :: createTypingCallbacks`),6 个 type-only import 在 `.d.ts` 中也都在。**不验这一步,微信通道有可能加载即挂。** -### 8.3 已知坑:改了 pin,已装实例不会自动升 `[A]` +### 8.3 已知坑:改了 pin,已装实例不会自动升 `[A]` → **install 路径已于 2026-09-12 修复 `[B]`** -- `scripts/install.sh`:`plugins list` 里已有 openclaw-weixin 就**直接 return,不比版本** -- `scripts/update.sh`:走 `npx openclaw-weixin-cli@2.1.4 install`,该 CLI 读到 `plugins.installs[].spec` 是固定版本号时(我们正是用 `--pin` 装的)会打印「本地已安装插件为固定版本 2.4.6,跳过升级」直接 return +- ~~`scripts/install.sh`:`plugins list` 里已有 openclaw-weixin 就**直接 return,不比版本**~~ → **已修**:`install.sh` / `install-atomgit.sh` / `docker/docker-bootstrap.sh` 三处同源的 `install_weixin_plugin()` 改为**按版本判定**——新增 `weixin_installed_version()`(主路径 `plugins list --json` 取 `version`,回落到 `$OPENCLAW_HOME/npm/projects/*/node_modules//package.json`),已装 == pin 才跳过,不等则 `plugins install @ --pin --force`;版本读不到但插件在时也走 `--force`(正确性优先)。已用本机实例(已装 2.4.6 / pin 2.4.8)实测三条路径:主路径 ✓、回落路径 ✓、未安装返回非零 ✓,决策分支 `UPGRADE --force` / `SKIP` 均正确。 +- `scripts/update.sh`:**仍未修**。它走 `npx openclaw-weixin-cli@2.1.4 install`,该 CLI 读到 `plugins.installs[].spec` 是固定版本号时(我们正是用 `--pin` 装的)会打印「本地已安装插件为固定版本 2.4.6,跳过升级」直接 return;bundled tarball 路径(`update.sh:169`)也没带 `--force`。→ 走 `update.sh` 的实例仍需下面这条手动命令(或后续单独修 update.sh)。 -→ 部署/迁移时已装实例要显式跑一次: +→ `update.sh` 路线 / 修复前已装的实例,仍需显式跑一次: ```bash cd openclaw && npm_config_registry=https://registry.npmmirror.com \ @@ -463,7 +463,7 @@ wecom 那条不受影响:`install-wecom-channel.sh` 按 pin 文件 `npm pack` | 1 | **基线选哪条** | (a) 直接迁 `2026.9.3`;(b) 先切 `extended-stable/2026.7.33`(pin `f619d7a9fa3`,或等它打 tag)过渡,再排期 9.3;(c) 暂不动;(d) **ES 7.33 + cherry-pick 9.3 省 token commit —— 已实测否决**(§6.5-2:5 个 commit 全部冲突,合计 153 个冲突文件次) | (a) 4.5–7 人日 `[A]`;(b) ≈半天,patch/awada 零漂移,但拿不到能力①② `[A]`;能力①最低 9.1、能力②要 9.3(§6.1) | | 2 | **portable Node 抬到哪** | 24.16+ / 直接 26(上游推荐 26) | 与 openclaw 版本解耦,留 7.x 也该做(§5.1);改 `build-dist.yml` 5 处 + `ci.yml` + Docker 基础镜像钉死 | | 3 | **9.x 迁移的触发条件认不认** | 认 / 不认(改为现在就一次性做完) | 触发条件草案:生产出现"回复停在工具输出 / 重启后丢回复"(#133520 #133979 #138071 #138519 **均未 backport 到 ES 7.33**);或 ES 7.33 停止提交;或需要 9.x 独有能力 | -| 4 | **`install.sh` 幂等判断要不要改** | 改成"已装版本 ≠ pin 版本 → `--force` 升级" / 维持手动补装 | 十几行,根治 §8.3 的坑 | +| 4 | ~~**`install.sh` 幂等判断要不要改**~~ | **已做(2026-09-12)**:install.sh / install-atomgit.sh / docker-bootstrap.sh 三处改为按版本判定 + `--force` 升级 | 详见 §8.3;**遗留**:`update.sh` 的 weixin-cli 路径与 bundled tarball 路径仍未带 `--force`,要不要一并修 | | 5 | **浏览器路线** | (a) 按 9.3 新模块布局重画 pivot(05/03/04/09 + `del-*` 改 rm 清单 + 补 5 个新文件);(b) 先在 7.1-2 上把"能力裁剪"等价实现到自有 adapter 层(半天量级,不需动基座);(c) 评估 §7.8-4 的"MCP 桥零 patch"路线 | 见 §7.8;(a) 的工时已含在 #1 的 4.5–7 人日里 | | 6 | **是否先做"单轮 token 构成"实测 + 三档配置调参** | 做 / 不做 | 半天、立刻见效、与升级解耦(§6.3、§6.5-5:`contextPruning` / `skills.limits` / `compaction.*` 三档都是 7.1-2 已支持但我们没开);也是判断能力② 收益基线的前置数据 | | 7 | **是否先实测百炼端点的 prompt cache 语义** | 做 / 不做 | 决定 9.3 那批 cache 修复对我们是否有价值(§6.5-4);不做这一步,能力② 的收益无法量化 | diff --git a/scripts/install-atomgit.sh b/scripts/install-atomgit.sh index 8be9fdc4..b694a093 100755 --- a/scripts/install-atomgit.sh +++ b/scripts/install-atomgit.sh @@ -24,6 +24,7 @@ # 9. setup-crew.sh(裸跑,无 --force;--force 只用户手动修复用;crew 模板来自 WISEFLOW_ROOT/crews,workspace 落 OPENCLAW_HOME) # 10. camoufox-cli:npm install -g 本地 fork(ship 的 portable node)+ camoufox-cli install 下 Firefox # 11. openclaw-weixin 插件:openclaw plugins install @tencent-weixin/openclaw-weixin@ --pin(npmmirror) +# 幂等按**版本**判定:已装版本 == pin 才跳过,不等则 --force 升级到 pin # 12. 交互问 AWK_API_KEY → 写 gateway env(Linux daemon.env / Darwin service-env/ai.openclaw.gateway.env,均落 OPENCLAW_HOME) # → openclaw daemon install + restart(唯一人工输入点;不走 onboard,小白友好) # 13. 打印访问指引 @@ -759,9 +760,39 @@ install_camoufox_cli() { ui_success "camoufox-cli ready" } +# 读已装 openclaw-weixin 的版本号;读不到返回非零。 +# 优先 `plugins list --json`(字段 id / name / version),回落到 npm projects 下实装 package.json。 +weixin_installed_version() { + local claw_cmd="$1" pkg="$2" oc_home="${OPENCLAW_HOME:-$HOME/.openclaw}" + local v f + v="$("$claw_cmd" plugins list --json 2>/dev/null | python3 -c " +import json, sys +try: + d = json.load(sys.stdin) +except Exception: + sys.exit(0) +for p in d.get('plugins', []): + if p.get('id') == 'openclaw-weixin' or p.get('name') == '$pkg': + print(p.get('version') or '') + break +" 2>/dev/null)" + if [[ -n "$v" ]]; then printf '%s\n' "$v"; return 0; fi + # 回落:CLI/JSON 不可用时直接读实装 package.json(目录名带 hash,只能 glob) + for f in "$oc_home"/npm/projects/*/node_modules/"$pkg"/package.json; do + [[ -f "$f" ]] || continue + v="$(python3 -c "import json;print(json.load(open('$f')).get('version',''))" 2>/dev/null)" + if [[ -n "$v" ]]; then printf '%s\n' "$v"; return 0; fi + done + return 1 +} + # 装 openclaw-weixin 插件(config template 已预置 channel,但插件本体要 openclaw plugins install) # 读 tarball 内 openclaw-weixin.version.json 的 pin,走国内 npmmirror。 -# 幂等:openclaw plugins list 含 openclaw-weixin 则跳过。 +# 幂等:**已装版本 == pin 版本**才跳过;已装版本 ≠ pin 则 `--force` 升级到 pin。 +# (旧逻辑只 grep 插件名就 return,导致 bump pin 后已装实例永远停在旧版; +# `plugins install --force` = "Overwrite an existing installed plugin",7.1-2 起就有该 flag。 +# 升级只换 npm/projects 下的包,登录态在 $OPENCLAW_HOME/openclaw-weixin/ 数据目录,不受影响。 +# 升级后需 gateway 重启才生效——更新路线在后面 refresh_gateway_env_only 里会重启。) install_weixin_plugin() { local claw_cmd="$WISEFLOW_ROOT/bin/openclaw" local pin_file="$WISEFLOW_ROOT/openclaw-weixin.version.json" @@ -773,16 +804,26 @@ install_weixin_plugin() { fi pkg="${pkg:-@tencent-weixin/openclaw-weixin}" ver="${ver:-2.4.8}" - # 幂等检查:plugins list 已含则跳过 - if "$claw_cmd" plugins list 2>/dev/null | grep -q "openclaw-weixin"; then - ui_success "openclaw-weixin plugin already installed" + # 幂等检查:已装版本 == pin 才跳过 + local installed force_flag="" + installed="$(weixin_installed_version "$claw_cmd" "$pkg" || true)" + if [[ -n "$installed" && "$installed" == "$ver" ]]; then + ui_success "openclaw-weixin plugin already installed (${ver})" return 0 fi + if [[ -n "$installed" ]]; then + ui_info "openclaw-weixin 已装 ${installed},pin=${ver} → --force 升级" + force_flag="--force" + elif "$claw_cmd" plugins list 2>/dev/null | grep -q "openclaw-weixin"; then + # 插件在但版本读不到(CLI/JSON 异常):按 pin 强制重装,保证与 pin 一致 + ui_warn "openclaw-weixin 已装但版本读不到;按 pin ${ver} 强制重装" + force_flag="--force" + fi ui_info "Installing openclaw-weixin plugin (${pkg}@${ver}) via npmmirror" - if npm_config_registry=https://registry.npmmirror.com "$claw_cmd" plugins install "${pkg}@${ver}" --pin 2>/dev/null; then - ui_success "openclaw-weixin plugin installed" + if npm_config_registry=https://registry.npmmirror.com "$claw_cmd" plugins install "${pkg}@${ver}" --pin $force_flag 2>/dev/null; then + ui_success "openclaw-weixin plugin installed (${ver})" else - ui_warn "openclaw-weixin 插件安装失败;可后续手动:npm_config_registry=https://registry.npmmirror.com $claw_cmd plugins install ${pkg}@${ver} --pin" + ui_warn "openclaw-weixin 插件安装失败;可后续手动:npm_config_registry=https://registry.npmmirror.com $claw_cmd plugins install ${pkg}@${ver} --pin $force_flag" fi } diff --git a/scripts/install.sh b/scripts/install.sh index f5f8c44e..6b684793 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -20,6 +20,7 @@ # 9. setup-crew.sh(裸跑,无 --force;--force 只用户手动修复用;crew 模板来自 WISEFLOW_ROOT/crews,workspace 落 OPENCLAW_HOME) # 10. camoufox-cli:npm install -g 本地 fork(ship 的 portable node)+ camoufox-cli install 下 Firefox # 11. openclaw-weixin 插件:openclaw plugins install @tencent-weixin/openclaw-weixin@ --pin(npmmirror) +# 幂等按**版本**判定:已装版本 == pin 才跳过,不等则 --force 升级到 pin # 12. 交互问 AWK_API_KEY → 写 gateway env(Linux daemon.env / Darwin service-env/ai.openclaw.gateway.env,均落 OPENCLAW_HOME) # → openclaw daemon install + restart(唯一人工输入点;不走 onboard,小白友好) # 13. 打印访问指引 @@ -1071,9 +1072,39 @@ install_camoufox_cli() { ui_success "camoufox-cli ready" } +# 读已装 openclaw-weixin 的版本号;读不到返回非零。 +# 优先 `plugins list --json`(字段 id / name / version),回落到 npm projects 下实装 package.json。 +weixin_installed_version() { + local claw_cmd="$1" pkg="$2" oc_home="${OPENCLAW_HOME:-$HOME/.openclaw}" + local v f + v="$("$claw_cmd" plugins list --json 2>/dev/null | python3 -c " +import json, sys +try: + d = json.load(sys.stdin) +except Exception: + sys.exit(0) +for p in d.get('plugins', []): + if p.get('id') == 'openclaw-weixin' or p.get('name') == '$pkg': + print(p.get('version') or '') + break +" 2>/dev/null)" + if [[ -n "$v" ]]; then printf '%s\n' "$v"; return 0; fi + # 回落:CLI/JSON 不可用时直接读实装 package.json(目录名带 hash,只能 glob) + for f in "$oc_home"/npm/projects/*/node_modules/"$pkg"/package.json; do + [[ -f "$f" ]] || continue + v="$(python3 -c "import json;print(json.load(open('$f')).get('version',''))" 2>/dev/null)" + if [[ -n "$v" ]]; then printf '%s\n' "$v"; return 0; fi + done + return 1 +} + # 装 openclaw-weixin 插件(config template 已预置 channel,但插件本体要 openclaw plugins install) # 读 tarball 内 openclaw-weixin.version.json 的 pin,走国内 npmmirror。 -# 幂等:openclaw plugins list 含 openclaw-weixin 则跳过。 +# 幂等:**已装版本 == pin 版本**才跳过;已装版本 ≠ pin 则 `--force` 升级到 pin。 +# (旧逻辑只 grep 插件名就 return,导致 bump pin 后已装实例永远停在旧版; +# `plugins install --force` = "Overwrite an existing installed plugin",7.1-2 起就有该 flag。 +# 升级只换 npm/projects 下的包,登录态在 $OPENCLAW_HOME/openclaw-weixin/ 数据目录,不受影响。 +# 升级后需 gateway 重启才生效——更新路线在后面 refresh_gateway_env_only 里会重启。) install_weixin_plugin() { local claw_cmd="$WISEFLOW_ROOT/bin/openclaw" local pin_file="$WISEFLOW_ROOT/openclaw-weixin.version.json" @@ -1085,16 +1116,26 @@ install_weixin_plugin() { fi pkg="${pkg:-@tencent-weixin/openclaw-weixin}" ver="${ver:-2.4.8}" - # 幂等检查:plugins list 已含则跳过 - if "$claw_cmd" plugins list 2>/dev/null | grep -q "openclaw-weixin"; then - ui_success "openclaw-weixin plugin already installed" + # 幂等检查:已装版本 == pin 才跳过 + local installed force_flag="" + installed="$(weixin_installed_version "$claw_cmd" "$pkg" || true)" + if [[ -n "$installed" && "$installed" == "$ver" ]]; then + ui_success "openclaw-weixin plugin already installed (${ver})" return 0 fi + if [[ -n "$installed" ]]; then + ui_info "openclaw-weixin 已装 ${installed},pin=${ver} → --force 升级" + force_flag="--force" + elif "$claw_cmd" plugins list 2>/dev/null | grep -q "openclaw-weixin"; then + # 插件在但版本读不到(CLI/JSON 异常):按 pin 强制重装,保证与 pin 一致 + ui_warn "openclaw-weixin 已装但版本读不到;按 pin ${ver} 强制重装" + force_flag="--force" + fi ui_info "Installing openclaw-weixin plugin (${pkg}@${ver}) via npmmirror" - if npm_config_registry=https://registry.npmmirror.com "$claw_cmd" plugins install "${pkg}@${ver}" --pin 2>/dev/null; then - ui_success "openclaw-weixin plugin installed" + if npm_config_registry=https://registry.npmmirror.com "$claw_cmd" plugins install "${pkg}@${ver}" --pin $force_flag 2>/dev/null; then + ui_success "openclaw-weixin plugin installed (${ver})" else - ui_warn "openclaw-weixin 插件安装失败;可后续手动:npm_config_registry=https://registry.npmmirror.com $claw_cmd plugins install ${pkg}@${ver} --pin" + ui_warn "openclaw-weixin 插件安装失败;可后续手动:npm_config_registry=https://registry.npmmirror.com $claw_cmd plugins install ${pkg}@${ver} --pin $force_flag" fi } From 068f87e6ac816a1ec0a38d22c54e4c8f461dcc43 Mon Sep 17 00:00:00 2001 From: codes-factory-of-bg Date: Sat, 12 Sep 2026 01:10:14 +0800 Subject: [PATCH 17/34] =?UTF-8?q?fix(update):=20bundled=20openclaw-weixin?= =?UTF-8?q?=20tarball=20=E5=AE=89=E8=A3=85=E8=A1=A5=20--force?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit bundled 路径(vendor/openclaw-plugins/*.tgz,sha512 校验后安装)本来就每次都装、 没有幂等跳过,但 `plugins install ` 不带 --force 时在已装过该插件的实例上会失败, 进而触发紧随其后的 exit 1,把整次 update 打断。 --force = "Overwrite an existing installed plugin"(7.1-2 起就有该 flag), 与 install.sh / install-atomgit.sh / docker-bootstrap.sh 上一轮的修法保持一致。 成功日志带上 pin 版本号便于核对。 未动:update.sh 的在线路径(npx openclaw-weixin-cli install)——该 CLI 遇到固定版本 spec 会自行跳过升级,要修得改调用方式,按用户决定暂不处理;已记入 docs/openclaw-2026.9.3-upgrade-fact-finding.md §8.3 / §10-4。 --- docs/openclaw-2026.9.3-upgrade-fact-finding.md | 5 +++-- scripts/update.sh | 7 +++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/openclaw-2026.9.3-upgrade-fact-finding.md b/docs/openclaw-2026.9.3-upgrade-fact-finding.md index cdc6cd4f..b1229e5d 100644 --- a/docs/openclaw-2026.9.3-upgrade-fact-finding.md +++ b/docs/openclaw-2026.9.3-upgrade-fact-finding.md @@ -429,7 +429,8 @@ commit **`d5e112c`**(`chore(deps): bump openclaw-weixin 2.4.6 -> 2.4.8, wecom- ### 8.3 已知坑:改了 pin,已装实例不会自动升 `[A]` → **install 路径已于 2026-09-12 修复 `[B]`** - ~~`scripts/install.sh`:`plugins list` 里已有 openclaw-weixin 就**直接 return,不比版本**~~ → **已修**:`install.sh` / `install-atomgit.sh` / `docker/docker-bootstrap.sh` 三处同源的 `install_weixin_plugin()` 改为**按版本判定**——新增 `weixin_installed_version()`(主路径 `plugins list --json` 取 `version`,回落到 `$OPENCLAW_HOME/npm/projects/*/node_modules//package.json`),已装 == pin 才跳过,不等则 `plugins install @ --pin --force`;版本读不到但插件在时也走 `--force`(正确性优先)。已用本机实例(已装 2.4.6 / pin 2.4.8)实测三条路径:主路径 ✓、回落路径 ✓、未安装返回非零 ✓,决策分支 `UPGRADE --force` / `SKIP` 均正确。 -- `scripts/update.sh`:**仍未修**。它走 `npx openclaw-weixin-cli@2.1.4 install`,该 CLI 读到 `plugins.installs[].spec` 是固定版本号时(我们正是用 `--pin` 装的)会打印「本地已安装插件为固定版本 2.4.6,跳过升级」直接 return;bundled tarball 路径(`update.sh:169`)也没带 `--force`。→ 走 `update.sh` 的实例仍需下面这条手动命令(或后续单独修 update.sh)。 +- `scripts/update.sh` bundled tarball 路径 → **已修**(2026-09-12):`plugins install "$plugin_tgz"` 补上 `--force`。该路径本来就每次都装(无幂等跳过),缺 flag 时在已装实例上会失败并触发下面的 `exit 1`,把整次 update 打断。(注:仓内无 `vendor/openclaw-plugins/`,该路径只在带 vendor 的发行 tarball 里触发。) +- `scripts/update.sh` 在线路径 → **仍未修**(按用户决定暂不动)。它走 `npx openclaw-weixin-cli@2.1.4 install`,该 CLI 读到 `plugins.installs[].spec` 是固定版本号时(我们正是用 `--pin` 装的)会打印「本地已安装插件为固定版本 2.4.6,跳过升级」直接 return。→ 走 `update.sh` 在线路径的实例仍需下面这条手动命令。 → `update.sh` 路线 / 修复前已装的实例,仍需显式跑一次: @@ -463,7 +464,7 @@ wecom 那条不受影响:`install-wecom-channel.sh` 按 pin 文件 `npm pack` | 1 | **基线选哪条** | (a) 直接迁 `2026.9.3`;(b) 先切 `extended-stable/2026.7.33`(pin `f619d7a9fa3`,或等它打 tag)过渡,再排期 9.3;(c) 暂不动;(d) **ES 7.33 + cherry-pick 9.3 省 token commit —— 已实测否决**(§6.5-2:5 个 commit 全部冲突,合计 153 个冲突文件次) | (a) 4.5–7 人日 `[A]`;(b) ≈半天,patch/awada 零漂移,但拿不到能力①② `[A]`;能力①最低 9.1、能力②要 9.3(§6.1) | | 2 | **portable Node 抬到哪** | 24.16+ / 直接 26(上游推荐 26) | 与 openclaw 版本解耦,留 7.x 也该做(§5.1);改 `build-dist.yml` 5 处 + `ci.yml` + Docker 基础镜像钉死 | | 3 | **9.x 迁移的触发条件认不认** | 认 / 不认(改为现在就一次性做完) | 触发条件草案:生产出现"回复停在工具输出 / 重启后丢回复"(#133520 #133979 #138071 #138519 **均未 backport 到 ES 7.33**);或 ES 7.33 停止提交;或需要 9.x 独有能力 | -| 4 | ~~**`install.sh` 幂等判断要不要改**~~ | **已做(2026-09-12)**:install.sh / install-atomgit.sh / docker-bootstrap.sh 三处改为按版本判定 + `--force` 升级 | 详见 §8.3;**遗留**:`update.sh` 的 weixin-cli 路径与 bundled tarball 路径仍未带 `--force`,要不要一并修 | +| 4 | ~~**`install.sh` 幂等判断要不要改**~~ | **已做(2026-09-12)**:install.sh / install-atomgit.sh / docker-bootstrap.sh 三处改为按版本判定 + `--force` 升级;`update.sh` 的 bundled tarball 路径也补了 `--force` | 详见 §8.3;**遗留(用户决定暂不动)**:`update.sh` 在线路径走 weixin-cli,遇固定版本 spec 会自行跳过 | | 5 | **浏览器路线** | (a) 按 9.3 新模块布局重画 pivot(05/03/04/09 + `del-*` 改 rm 清单 + 补 5 个新文件);(b) 先在 7.1-2 上把"能力裁剪"等价实现到自有 adapter 层(半天量级,不需动基座);(c) 评估 §7.8-4 的"MCP 桥零 patch"路线 | 见 §7.8;(a) 的工时已含在 #1 的 4.5–7 人日里 | | 6 | **是否先做"单轮 token 构成"实测 + 三档配置调参** | 做 / 不做 | 半天、立刻见效、与升级解耦(§6.3、§6.5-5:`contextPruning` / `skills.limits` / `compaction.*` 三档都是 7.1-2 已支持但我们没开);也是判断能力② 收益基线的前置数据 | | 7 | **是否先实测百炼端点的 prompt cache 语义** | 做 / 不做 | 决定 9.3 那批 cache 修复对我们是否有价值(§6.5-4);不做这一步,能力② 的收益无法量化 | diff --git a/scripts/update.sh b/scripts/update.sh index 12595dbf..3997580b 100755 --- a/scripts/update.sh +++ b/scripts/update.sh @@ -166,8 +166,11 @@ install_weixin_channel() { } ' "$plugin_tgz" "$plugin_integrity" fi - if (cd "$OPENCLAW_DIR" && pnpm openclaw plugins install "$plugin_tgz"); then - echo " ✅ bundled openclaw-weixin installed" + # --force = "Overwrite an existing installed plugin"(7.1-2 起就有该 flag)。 + # bundled 路径本来就每次都装(不像 install.sh 有幂等跳过),缺这个 flag 时 + # 在已装过该插件的实例上会失败,进而触发下面的 exit 1,把整次 update 打断。 + if (cd "$OPENCLAW_DIR" && pnpm openclaw plugins install "$plugin_tgz" --force); then + echo " ✅ bundled openclaw-weixin installed (${plugin_version})" else echo "❌ Bundled openclaw-weixin install failed" echo " Re-run with --skip-weixin only if you intentionally want to configure the onboarding channel later." From 48818d847e2de3cac28c46dc3f8c93a59a4136f9 Mon Sep 17 00:00:00 2001 From: bigbrother666sh Date: Sat, 12 Sep 2026 22:20:47 +0800 Subject: [PATCH 18/34] Remove deprecated scripts for software copyright registration - Deleted `generate_code_doc.py`, `generate_form_info.py`, `generate_manual.py`, and the associated shell wrapper `swcr-register.sh` as they are no longer needed. - Updated version information in `openclaw.version` to reflect the latest stable release. --- README.md | 64 ++- crews/content-producer/AGENTS.md | 6 - .../skills/expert-design/SKILL.md | 2 +- .../skills/expert-video/SKILL.md | 6 +- .../tools/video-producer/SKILL.md | 8 - .../tools/douyin-style-profiler/SKILL.md | 1 - .../references/note-dna-framework.md | 1 - .../references/video-dna-framework.md | 3 +- .../tools/wx-channel-style-profiler/SKILL.md | 1 - .../references/video-dna-framework.md | 3 +- .../tools/xhs-style-profiler/SKILL.md | 1 - .../references/note-dna-framework.md | 3 +- .../references/video-dna-framework.md | 3 +- crews/main/skills/swcr-register/SKILL.md | 204 -------- .../scripts/generate_code_doc.py | 436 ---------------- .../scripts/generate_form_info.py | 486 ------------------ .../swcr-register/scripts/generate_manual.py | 272 ---------- .../skills/swcr-register/swcr-register.sh | 37 -- openclaw.version | 13 +- 19 files changed, 39 insertions(+), 1511 deletions(-) delete mode 100644 crews/main/skills/swcr-register/SKILL.md delete mode 100644 crews/main/skills/swcr-register/scripts/generate_code_doc.py delete mode 100644 crews/main/skills/swcr-register/scripts/generate_form_info.py delete mode 100644 crews/main/skills/swcr-register/scripts/generate_manual.py delete mode 100755 crews/main/skills/swcr-register/swcr-register.sh diff --git a/README.md b/README.md index 3de2a7ee..5ab9f66f 100755 --- a/README.md +++ b/README.md @@ -7,17 +7,17 @@ - 图文海报生成 - 短视频生成与多平台分发(支持视频号、抖音、小红书) - Twitter/X、微博、知乎等平台发文 +- 微信朋友圈内容发布(通过企业微信接口) - 爆款视频追爆分析、仿写与再创作(支持抖音、B站和小红书视频链接) +- 赛道头部账号DNA级复刻(目前支持小红书、微信视频号、抖音、微信公众号四个平台) - 已发布作品数据监控与每日定时复盘 -- 内置小红书、抖音、twitter/x、公众号、视频号平台“专家包” -- 信息搜集与情报:内置 Smart Search,覆盖小红书、抖音、微博、知乎、B站、Twitter、YouTube、视频号、LinkedIn、Reddit、新闻、政务、财经、学术、购物、GitHub 等 18 类信源——无需配置任何 key、纯免费 -- 指定信源监控与提取 +- 自媒体评论区获客 +- 7*24 小时智能客服(售前接待) - 通过社交媒体寻找潜在客户或市场调研 -- 灵感记录与思路梳理 +- 信息搜集与情报:内置 Smart Search,覆盖小红书、抖音、微博、知乎、B站、Twitter、YouTube、视频号、LinkedIn、Reddit、新闻、政务、财经、学术、购物、GitHub 等 18 类信源——无需配置任何 key、纯免费 - "四声分析"法战略研判与讨论 - 产品deck、ppt制作,投资/IR 材料准备 -- 软件著作权、ICP 备案等材料辅助生成 -- 闲鱼运营、企业微信朋友圈触达 +- 网站设计与制作、ICP 备案等材料辅助 - …… 并且你只需通过手机上的微信与他沟通,即可实现全部功能!(同时支持飞书、企业微信) @@ -32,31 +32,18 @@ xiaobei 由Wiseflow (原AI首席情报官)作者 bigbrother666sh 开发。 --- -## 🚀 **v5.7.0 更新** - -> Let's do this like an expert. - -- **专家包(Expert Pack)——像专家一样干活**:公众号、小红书、抖音、视频号能力升级为“专家包”,起号定位、对标拆解、内容生产、改稿调整、数据复盘六条工作流跑通账号运营全生命周期。你只管把任务丢过来,小贝按专业路数把活办完,不再拿一份越写越厚的通用说明书应付所有平台。 -- **内容风格 DNA——先像人写,再自我进化**:16+维细颗粒度复刻对标账号,统计学聚合做证据底座、定性判断提炼规则,最后落成内容生产 DNA template;发布数据回流后按量触发评估 -> DNA不断自我进化,迎合目标客群口味。 -- **OpenClaw 上游同步 v2026.7.1-2**。 -- **camoufox-cli 升 0.7.3 + 浏览器二进制升 FF152**:同步上游安装链路修复(不再误拉 beta 版浏览器、broken install 立即报错),FF152 解决重负载下鼠标输入卡死等稳定性顽疾。 +## 🚀 **v5.7.1 更新** -详见 [CHANGELOG.md](CHANGELOG.md) - ---- - -## 🚀 **v5.6.3 更新** - -- **🎬 Content Producer 正式发布**:我们参考了GitHub上几乎所有热门的视频生产项目,并重点解决了纯AIGC模式容易被平台限流,以及难以融合业务素材的问题。xiaobei系统的content-producer就是你目前能够找到的最适合获客内容制作的开源免费方案! - - > ☀️测试阶段,我们应用content-producer制作了xiaobei系统的第一支品牌故事宣传片: - > +- 小红书、抖音、视频号 DNA系统升级到2.0架构,Let's do this like an expert! +- content producer 升级为专家系统,现在除了AIGC大片外,还可以复刻众多短视频平台流行的“套路”,摆脱“低创”、限流: + > 默认模式 - AIGC大片:小贝宣传片 > - YouTube:https://youtu.be/eK8aWWCNVZQ?si=K-MbWai-j6ydqCdy > - bilibili:https://www.bilibili.com/video/BV1euMR6PEDh - > - 开发与使用心得: https://mp.weixin.qq.com/s/zxvWdCMUd0XquWxujvxkSg -- **安装脚本大幅优化**:对比 5.6.0,install.sh / install-atomgit.sh / install.ps1 / install-atomgit.ps1 四脚本修了若干实测踩坑——tarball 下载原子写、Windows `.env`/`daemon.env` 换行与 BOM、技能 wrapper 在 Windows 用 `.cmd` shim 替代软链、`OPENCLAW_HOME` 在 `set -u` 下报 unbound、atomgit 国内线路默认跳过 gum spinner bootstrap 避免连 GitHub 超时、Windows 软链需要开发者模式等,产品稳定性显著提高。 -- **wx-mp-hunter 更新**:原微信公众号素材接口方案因官方调整已不可用,我们更新了微信公众号获取方案 + > 套路一 - 贴纸人 + + > 套路二 - “万万没想到” +- 修复一键安装脚本中,openclaw-weixin不会自动升级的问题 详见 [CHANGELOG.md](CHANGELOG.md) @@ -201,17 +188,24 @@ irm https://raw.atomgit.com/wiseflow/xiaobei/raw/master/scripts/install-atomgit. > > 推荐用 [SiliconFlow](https://cloud.siliconflow.cn/i/WNLYbBpi)(🎁 xiaobei 邀请链接,注册认证后你可获得一张 16 元代金券),它提供 `BAAI/bge-m3` 与 `Qwen/Qwen3-VL-Embedding` 系列,均为 OpenAI 接口格式,可直接配置为 `memorySearch` 的 embedding provider。配置方法:把 `agents.defaults.memorySearch.provider` 从 `"none"` 改为 `"openai-compatible"`,并补上 `remote.baseUrl` / `remote.apiKey` / `model`;想开做梦就把 `plugins.entries.memory-core.config.dreaming.enabled` 改回 `true`。改完重启 gateway 生效。可以让小贝帮你完成配置。 -🎉 xiaobei 项目目前提供 **VIP Club**(售价 **168 元/年**),权益包括: +### 配置繁琐,不想操心? + +🎉 wiseflow团队现提供 **远程安装** 与 **远程技术支持** 增值服务,同时继续提供**VIP Club**(售价 **168 元/年**)服务,陪伴你从"小白"到"大神"! + +VIP Club(售价 **168 元/年**),权益包括: + +- 付费知识库:「手把手」安装教程、上手指南、配置全案、高阶玩法、最佳实践等…… +- VIP 微信交流群:问题讨论、产品反馈、技术沟通、专家 DNA 分享…… +- 官方中转服务:针对部分自媒体平台的发布技能要求固定 IP 和签名的问题,向 VIP Club 会员免费提供官方中转服务 +- 会员期内所有增值服务享半价(不含 awada lane 租赁) +- 会员可申请成为服务合作商 +- 会员可申请定制开发服务 -- **付费知识库**:包含《手把手从零开始安装教程》、《安装之后三分钟上手指南》、《Openclaw 自定义配置全案教程》、《Windows 下安装 WSL2 无脑教程》以及各种最佳实践分享 -- **vip 微信交流群**,共同探讨交流各种自动化获客玩法,搞钱路上不孤单 -- 免费加入 Wiseflow 知识星球 -- 每月一次的线上闭门分享(腾讯会议),陪伴你从"小白"到"大神"! -- **会员有效期内免费使用官方中转服务**:涉及小红书、抖音、bili、快手、微信公众号、企业微信朋友圈的技能都需要固定IP(平台要求),一般的家庭网络或办公网络环境并没有固定IP,Wiseflow团队已经搭建了官方的中转服务,vipclub会员期内畅用,不必再单独自建或购买。 +*⚠️ 如存在不当使用官方中转服务、在社群发表不当言论等行为,Wiseflow 团队有权提前终止会员资格且不予退款,详见会员协议。* -此外,我们也面向 VIP Club 会员提供如下增值服务:**远程安装部署、远程技术支持、awada lane 租赁** (需额外付费) +另开放 **服务代理商** 合作,共享AI时代红利!详见:https://openclaw-for-business.com/pricing -欢迎添加"掌柜的"企业微信(这背后接的就是 xiaobei sales-cs)咨询了解: +也欢迎添加"掌柜的"企业微信(由 xiaobei sales-cs 驱动)咨询了解: xiaobei掌柜 diff --git a/crews/content-producer/AGENTS.md b/crews/content-producer/AGENTS.md index 46ba9a75..cd4ebd22 100644 --- a/crews/content-producer/AGENTS.md +++ b/crews/content-producer/AGENTS.md @@ -9,12 +9,6 @@ | A · Subagent 承制 | main agent(小贝) | 读甲方 Brief + 素材绝对路径 → 核对字段 → 缺关键字段向 Brief owner 澄清;**不重开需求讨论** | | B · 直接对接用户 | 用户(已绑工作 channel) | 用户给了 Brief → 核对确认;没给 → 先引导讨论,**代用户整理 brief 并发用户确认后才开工** | -模式 B 的用户不一定专业,我要替他把需求收敛清楚: - -1. **先明确 brief**:做什么、给谁看、要传达什么、成品形态与规格、什么时候要;整理成 `brief.md` 发用户确认。用户的模糊想法("做个短片""帮我策划")**不算确认**。 -2. **涉及已有素材必须落实位置**:让用户给出素材**绝对路径**(或明确授权从哪个目录取),逐条确认文件真实存在、可打开;缺什么明说,不许拿"待补"开工。 -3. **口播内容**:口播文案由甲方出。用户只给大意时可代拟,但必须发用户确认定稿;明确是用户真人口播时,必须拿到用户的录音文件(绝对路径)。 - ## 能力方向路由 **视频类铁律**:只要接的是视频制作活儿,`expert-video` 的**通用制作流程**(Stage 0→14 阶段链 + GATE A/B 两闸门 + 护栏 + 工作区与交付约定)**一律适用**——它是基准准则,不是"没匹配到类型时的备选",也不与类型 workflow 并列。下表匹配到的类型 workflow 只是叠加在基准上的进一步细化。 diff --git a/crews/content-producer/skills/expert-design/SKILL.md b/crews/content-producer/skills/expert-design/SKILL.md index 2a129486..56405b07 100644 --- a/crews/content-producer/skills/expert-design/SKILL.md +++ b/crews/content-producer/skills/expert-design/SKILL.md @@ -1,6 +1,6 @@ --- name: expert-design -description: 平面设计专家(乙方)。承接完整网页/落地页、APP 与产品界面原型、品牌视觉体系(DESIGN.md + 组件预览)三类平面设计全案,从需求 brief、设计系统选取、素材获取、HTML/CSS 编写到视觉 review 与交付归档。两种工作模式:作为 main agent 的 subagent 接 Brief,或直接对接用户。不做视频(走 expert-video),不做平台发布与运营。 +description: 平面设计专家技能包 metadata: openclaw: emoji: 🎨 diff --git a/crews/content-producer/skills/expert-video/SKILL.md b/crews/content-producer/skills/expert-video/SKILL.md index c222480f..5382cd92 100644 --- a/crews/content-producer/skills/expert-video/SKILL.md +++ b/crews/content-producer/skills/expert-video/SKILL.md @@ -1,6 +1,6 @@ --- name: expert-video -description: 视频制作专家(乙方)。通用制作流程(Stage 0→14 + 两闸门 + 护栏)是我做任何视频都必须遵循的准则;workflows/ 下的类型文档(Reversal Ad / Narration Video / Collage B-roll)是在它之上针对特定类型的细化。承接端到端视频制作(口播类、实拍拼接 / 蒙太奇、影视解说 + 反转植入、纯 AIGC 动画、纸拼贴 B-roll)与已有素材的成片加工,交付成片 + 封面 + 交付说明。两种工作模式:作为 main agent 的 subagent 接 Brief,或直接对接用户。甲方只给 Brief 与素材绝对路径,工作区、制作方案、分镜与实现由我负责;不发布、不做平台运营。 +description: 视频制作专家技能包 metadata: openclaw: emoji: 🎬 @@ -15,7 +15,7 @@ metadata: ## 我是乙方 -不管活儿来自谁,我都是**乙方(承制方)**:按 Brief 交付成片与封面,不自作主张改需求,也不替甲方做选题、标题、简介与发布运营。角色定位、两种工作模式与甲乙方硬边界的完整版在 crew 的 `AGENTS.md`;本文只写视频制作特有的部分。 +不管活儿来自谁,我都是**乙方(承制方)**:按 Brief 交付成片与封面,不自作主张改需求,也不替甲方做选题、标题、简介与发布运营。角色定位、两种工作模式与甲乙方硬边界的完整版见 `AGENTS.md`;本文只写视频制作特有的部分。 - **Brief 是唯一契约**:Brief 没写的先问甲方,不自行脑补品牌事实、授权与承诺。 - **制作实现归我**:工作区、分镜、素材方案、剪辑手法、渲染参数由我定,不反过来找甲方要这些决策。 @@ -116,7 +116,7 @@ workflow 文档在技能包内,不是项目目录内容;项目目录只放 B Stage 0 Brief 确认 模式 A:读甲方 Brief,核对字段,缺口向 Brief owner 澄清 模式 B:用户未给 Brief 时引导讨论 → 代拟 brief.md → 发用户确认 (两种模式的 Stage 0 都是"先把 Brief 定下来",无子命令) -Stage 1 intent-router 定档位(Brief 指定 workflow 时按该 workflow 的约束校验) +Stage 1 intent-router 定档位(Brief 指定 workflow 时按该 workflow 的约束校验,未给定时从下面三个档位选一个) narrative 故事讲述型(重情节、有人物弧光、含旁白,默认 3–5 镜/场) motion 纯画面动效型(重节奏与视觉冲击、少对白,默认 5–8 镜快切) montage 蒙太奇剪接型(重氛围、抽象、纯视觉,默认 4–7 镜无叙事) diff --git a/crews/content-producer/skills/expert-video/tools/video-producer/SKILL.md b/crews/content-producer/skills/expert-video/tools/video-producer/SKILL.md index c633ef23..2462f0bc 100644 --- a/crews/content-producer/skills/expert-video/tools/video-producer/SKILL.md +++ b/crews/content-producer/skills/expert-video/tools/video-producer/SKILL.md @@ -1,14 +1,6 @@ --- name: video-producer description: 视频制作原子能力集——意图路由、故事/剧本/分镜、素材 slot 与解析、渲染、混音对齐、拼接合成、动效审计、封面。子命令范式,产物文件存在性即 checkpoint。 -metadata: - openclaw: - emoji: 🎬 - requires: - bins: - - python3 - - ffmpeg - - ffprobe --- # video-producer — 工具说明 diff --git a/crews/main/skills/expert-douyin/tools/douyin-style-profiler/SKILL.md b/crews/main/skills/expert-douyin/tools/douyin-style-profiler/SKILL.md index 7acc625d..afa7df61 100644 --- a/crews/main/skills/expert-douyin/tools/douyin-style-profiler/SKILL.md +++ b/crews/main/skills/expert-douyin/tools/douyin-style-profiler/SKILL.md @@ -55,7 +55,6 @@ douyin/dna/{dna-id}/ - 抖音视频与图文都常见:先判作品类型再选框架,不要把图文笔记塞进视频 DNA。 - 单篇 report 只提供候选信号,不判断跨篇稳定性;共性、偏好、孤例由聚合阶段判断。 -- 脚本统计(句长、问句与人称密度、感叹号密度、口播密度;图文另有标题字数、正文行数、emoji 密度、话题标签数)只做证据底座,不评分、不判定风格是否合格;口头禅与签名式表达必须由 Agent 回读原文确认,不能凭统计直接下 DNA 结论。 - 视觉维度必须有图片 / 关键帧证据,由视觉模型读取;缺失写「未提供」,不得凭文本想象补齐。 - 口播文案子模块只在口播类作品启用;样本不足写「未启用」。 - 账号运营子模块(简介写法、内容形式比例、发布习惯)只在样本来自对标账号批量提取时填写,且**不进 template**。 diff --git a/crews/main/skills/expert-douyin/tools/douyin-style-profiler/references/note-dna-framework.md b/crews/main/skills/expert-douyin/tools/douyin-style-profiler/references/note-dna-framework.md index f600658e..2ae146df 100644 --- a/crews/main/skills/expert-douyin/tools/douyin-style-profiler/references/note-dna-framework.md +++ b/crews/main/skills/expert-douyin/tools/douyin-style-profiler/references/note-dna-framework.md @@ -15,7 +15,6 @@ DNA 文档 -> DNA template - 同一批样本可以来自多个不同账号,也可以来自用户指定的一个账号(从其发布列表批量提取);是否归入同一个 DNA 由用户指定,未指定入 `dna-0`。 - 一个 `dna-id` 只承载一种作品类型:抖音默认 dna-0 为视频,图文样本请另建 dna-id(如 dna-0-note)。 -- **与其他平台的差别只在维度**:本平台作品的观测物是标题 + 正文 + 图组 + 标签,因此不照搬公众号 17 维里的表层 / 深层写作维度(用词习惯、句式节奏、修辞手法、论证逻辑等)。 ### 进 DNA 与不进 DNA diff --git a/crews/main/skills/expert-douyin/tools/douyin-style-profiler/references/video-dna-framework.md b/crews/main/skills/expert-douyin/tools/douyin-style-profiler/references/video-dna-framework.md index a316e22e..9d496675 100644 --- a/crews/main/skills/expert-douyin/tools/douyin-style-profiler/references/video-dna-framework.md +++ b/crews/main/skills/expert-douyin/tools/douyin-style-profiler/references/video-dna-framework.md @@ -15,7 +15,6 @@ DNA 文档 -> DNA template - 同一批样本可以来自多个不同账号,也可以来自用户指定的一个账号(从其发布列表批量提取);是否归入同一个 DNA 由用户指定,未指定入 `dna-0`。 - 一个 `dna-id` 只承载一种作品类型:抖音默认 dna-0 为视频,图文样本请另建 dna-id(如 dna-0-note)。 -- **与其他平台的差别只在维度**:本平台作品的观测物是口播转录文本 + 画面 + 声音 + 结构节奏,因此不照搬公众号 17 维里的表层 / 深层写作维度(用词习惯、句式节奏、修辞手法、论证逻辑等)。 ### 进 DNA 与不进 DNA @@ -74,7 +73,7 @@ DNA 文档 -> DNA template 3. 视觉维度(封面 / 首帧 / 图组)必须有图片证据,由视觉模型读取本地图片后反推 AIGC 复现要素;没有图片写「未提供」。 4. 口播文案子模块只在口播类作品启用;非口播类或样本不足写「未启用 / 未观测」,不得把单篇句式上升为规则。 5. 账号运营子模块(`account-bio`、`content-mix-cadence`)只在样本来自对标账号批量提取时填写;用户只提供单篇时写「未观测」。 -6. 脚本统计只作证据底座(句长、问句密度、人称密度、感叹号密度、口播密度),不生成总分、不判定风格是否合格。 +6. 对于口播类视频样本,脚本统计只作证据底座(句长、问句密度、人称密度、感叹号密度、口播密度),不生成总分、不判定风格是否合格。 7. 业务植入与 CTA 两个维度必须给出**位置 + 载体 + 原文摘录**三样证据;只写「自然植入」「引导关注」这类空泛结论不算提取完成。本篇没有业务植入时明确写「无植入(纯内容)」,不得留空;平台红线(虚假承诺、利益诱导互动、隐藏站外联系方式、谐音绕检测)一律记为必须避免项。 8. 高数据样本必须回读创意、形态与包装再归因,不得把高播放直接等同于风格好。 diff --git a/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/SKILL.md b/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/SKILL.md index 063ac190..c0ae3392 100644 --- a/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/SKILL.md +++ b/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/SKILL.md @@ -46,7 +46,6 @@ wx_channel/dna/{dna-id}/ - **短标题与视频描述是两项独立内容**:发布页两项都可填,官方称填短标题能获得更多流量;但作品管理页不展示短标题,取数时只能拿到视频描述。report 里短标题拿不到就写「未观测(管理页不展示)」,不得把视频描述当短标题;两者都由 main agent 拟定,发布时都必须填。 - 单篇 report 只提供候选信号,不判断跨篇稳定性;共性、偏好、孤例由聚合阶段判断。 -- 脚本统计(句长、问句与人称密度、感叹号密度、口播密度)只做证据底座,不评分、不判定风格是否合格;口头禅与签名式表达必须由 Agent 回读原文确认,不能凭统计直接下 DNA 结论。 - 视觉维度必须有图片 / 关键帧证据,由视觉模型读取;缺失写「未提供」,不得凭文本想象补齐。 - 口播文案子模块只在口播类作品启用;样本不足写「未启用」。 - 账号运营子模块(简介写法、内容形式比例、发布习惯)只在样本来自对标账号批量提取时填写,且**不进 template**。 diff --git a/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/references/video-dna-framework.md b/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/references/video-dna-framework.md index 5b70a145..69162ac8 100644 --- a/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/references/video-dna-framework.md +++ b/crews/main/skills/expert-wx-channel/tools/wx-channel-style-profiler/references/video-dna-framework.md @@ -15,7 +15,6 @@ DNA 文档 -> DNA template - 同一批样本可以来自多个不同账号,也可以来自用户指定的一个账号(从其发布列表批量提取);是否归入同一个 DNA 由用户指定,未指定入 `dna-0`。 - 视频号作品都是视频,无需按作品类型分 DNA。 -- **与其他平台的差别只在维度**:本平台作品的观测物是口播转录文本 + 画面 + 声音 + 结构节奏,因此不照搬公众号 17 维里的表层 / 深层写作维度(用词习惯、句式节奏、修辞手法、论证逻辑等)。 ### 进 DNA 与不进 DNA @@ -74,7 +73,7 @@ DNA 文档 -> DNA template 3. 视觉维度(封面 / 首帧 / 图组)必须有图片证据,由视觉模型读取本地图片后反推 AIGC 复现要素;没有图片写「未提供」。 4. 口播文案子模块只在口播类作品启用;非口播类或样本不足写「未启用 / 未观测」,不得把单篇句式上升为规则。 5. 账号运营子模块(`account-bio`、`content-mix-cadence`)只在样本来自对标账号批量提取时填写;用户只提供单篇时写「未观测」。 -6. 脚本统计只作证据底座(句长、问句密度、人称密度、感叹号密度、口播密度),不生成总分、不判定风格是否合格。 +6. 对于口播类视频样本,脚本统计只作证据底座(句长、问句密度、人称密度、感叹号密度、口播密度),不生成总分、不判定风格是否合格。 7. 业务植入与 CTA 两个维度必须给出**位置 + 载体 + 原文摘录**三样证据;只写「自然植入」「引导关注」这类空泛结论不算提取完成。本篇没有业务植入时明确写「无植入(纯内容)」,不得留空;平台红线(虚假承诺、利益诱导互动、隐藏站外联系方式、谐音绕检测)一律记为必须避免项。 8. 高数据样本必须回读创意、形态与包装再归因,不得把高播放直接等同于风格好。 diff --git a/crews/main/skills/expert-xhs/tools/xhs-style-profiler/SKILL.md b/crews/main/skills/expert-xhs/tools/xhs-style-profiler/SKILL.md index a4bf537b..d3eb77b2 100644 --- a/crews/main/skills/expert-xhs/tools/xhs-style-profiler/SKILL.md +++ b/crews/main/skills/expert-xhs/tools/xhs-style-profiler/SKILL.md @@ -55,7 +55,6 @@ xhs/dna/{dna-id}/ - **搜索优先**:`search-intent`(匹配的用户问题)是小红书必备维度——最大流量池来自搜索。关键词必须尽量落到用户可能的提问原句,不只写平台标签。 - 单篇 report 只提供候选信号,不判断跨篇稳定性;共性、偏好、孤例由聚合阶段判断。 -- 脚本统计(句长、问句与人称密度、感叹号密度、口播密度;图文另有标题字数、正文行数、emoji 密度、话题标签数)只做证据底座,不评分、不判定风格是否合格;口头禅与签名式表达必须由 Agent 回读原文确认,不能凭统计直接下 DNA 结论。 - 视觉维度必须有图片 / 关键帧证据,由视觉模型读取;缺失写「未提供」,不得凭文本想象补齐。 - 口播文案子模块只在口播类作品启用;样本不足写「未启用」。 - 账号运营子模块(简介写法、内容形式比例、发布习惯)只在样本来自对标账号批量提取时填写,且**不进 template**。 diff --git a/crews/main/skills/expert-xhs/tools/xhs-style-profiler/references/note-dna-framework.md b/crews/main/skills/expert-xhs/tools/xhs-style-profiler/references/note-dna-framework.md index d54f228b..536607c8 100644 --- a/crews/main/skills/expert-xhs/tools/xhs-style-profiler/references/note-dna-framework.md +++ b/crews/main/skills/expert-xhs/tools/xhs-style-profiler/references/note-dna-framework.md @@ -15,7 +15,6 @@ DNA 文档 -> DNA template - 同一批样本可以来自多个不同账号,也可以来自用户指定的一个账号(从其发布列表批量提取);是否归入同一个 DNA 由用户指定,未指定入 `dna-0`。 - 一个 `dna-id` 只承载一种作品类型:小红书默认 dna-0 为图文,视频样本请另建 dna-id(如 dna-0-video)。 -- **与其他平台的差别只在维度**:本平台作品的观测物是标题 + 正文 + 图组 + 标签,因此不照搬公众号 17 维里的表层 / 深层写作维度(用词习惯、句式节奏、修辞手法、论证逻辑等)。 ### 进 DNA 与不进 DNA @@ -69,7 +68,7 @@ DNA 文档 -> DNA template 3. 视觉维度(封面 / 首帧 / 图组)必须有图片证据,由视觉模型读取本地图片后反推 AIGC 复现要素;没有图片写「未提供」。 4. 正文表达维度里的口头禅与签名表达只是候选线索,必须回读原文确认后才能进 DNA 文档。 5. 账号运营子模块(`account-bio`、`content-mix-cadence`)只在样本来自对标账号批量提取时填写;用户只提供单篇时写「未观测」。 -6. 脚本统计只作证据底座(标题字数、正文行数、句长、问句密度、emoji 密度、话题标签数),不生成总分、不判定风格是否合格。 +6. 对于口播类视频样本,脚本统计只作证据底座(标题字数、正文行数、句长、问句密度、emoji 密度、话题标签数),不生成总分、不判定风格是否合格。 7. 关键词必须尽量落到**用户可能的提问原句**,不只写平台标签;单篇只记候选,聚合后形成「关键词 → 用户问题 → 内容形式」的搜索意图地图(小红书最大流量池来自搜索)。 8. 业务植入与 CTA 两个维度必须给出**位置 + 载体 + 原文摘录**三样证据;只写「自然植入」「引导关注」这类空泛结论不算提取完成。本篇没有业务植入时明确写「无植入(纯内容)」,不得留空;平台红线(虚假承诺、利益诱导互动、隐藏站外联系方式、谐音绕检测)一律记为必须避免项。 9. 高数据样本必须回读创意、结构与关键词再归因,不得把高阅读直接等同于风格好。 diff --git a/crews/main/skills/expert-xhs/tools/xhs-style-profiler/references/video-dna-framework.md b/crews/main/skills/expert-xhs/tools/xhs-style-profiler/references/video-dna-framework.md index 737c80f9..696c1029 100644 --- a/crews/main/skills/expert-xhs/tools/xhs-style-profiler/references/video-dna-framework.md +++ b/crews/main/skills/expert-xhs/tools/xhs-style-profiler/references/video-dna-framework.md @@ -15,7 +15,6 @@ DNA 文档 -> DNA template - 同一批样本可以来自多个不同账号,也可以来自用户指定的一个账号(从其发布列表批量提取);是否归入同一个 DNA 由用户指定,未指定入 `dna-0`。 - 一个 `dna-id` 只承载一种作品类型:小红书默认 dna-0 为图文,视频样本请另建 dna-id(如 dna-0-video)。 -- **与其他平台的差别只在维度**:本平台作品的观测物是口播转录文本 + 画面 + 声音 + 结构节奏,因此不照搬公众号 17 维里的表层 / 深层写作维度(用词习惯、句式节奏、修辞手法、论证逻辑等)。 ### 进 DNA 与不进 DNA @@ -75,7 +74,7 @@ DNA 文档 -> DNA template 3. 视觉维度(封面 / 首帧 / 图组)必须有图片证据,由视觉模型读取本地图片后反推 AIGC 复现要素;没有图片写「未提供」。 4. 口播文案子模块只在口播类作品启用;非口播类或样本不足写「未启用 / 未观测」,不得把单篇句式上升为规则。 5. 账号运营子模块(`account-bio`、`content-mix-cadence`)只在样本来自对标账号批量提取时填写;用户只提供单篇时写「未观测」。 -6. 脚本统计只作证据底座(句长、问句密度、人称密度、感叹号密度、口播密度),不生成总分、不判定风格是否合格。 +6. 对于口播类视频样本,脚本统计只作证据底座(句长、问句密度、人称密度、感叹号密度、口播密度),不生成总分、不判定风格是否合格。 7. 关键词必须尽量落到**用户可能的提问原句**,不只写平台标签;单篇只记候选,聚合后形成「关键词 → 用户问题 → 内容形式」的搜索意图地图(小红书最大流量池来自搜索)。 8. 业务植入与 CTA 两个维度必须给出**位置 + 载体 + 原文摘录**三样证据;只写「自然植入」「引导关注」这类空泛结论不算提取完成。本篇没有业务植入时明确写「无植入(纯内容)」,不得留空;平台红线(虚假承诺、利益诱导互动、隐藏站外联系方式、谐音绕检测)一律记为必须避免项。 9. 高数据样本必须回读创意、形态与包装再归因,不得把高播放直接等同于风格好。 diff --git a/crews/main/skills/swcr-register/SKILL.md b/crews/main/skills/swcr-register/SKILL.md deleted file mode 100644 index c91875dc..00000000 --- a/crews/main/skills/swcr-register/SKILL.md +++ /dev/null @@ -1,204 +0,0 @@ ---- -name: swcr-register -description: > - 软件著作权登记全流程:从代码仓/目录生成程序鉴别材料(源程序文档)、 - 软件操作手册、申请填报信息 Markdown,并可辅助在线填报。 - 当用户需要申请软件著作权、生成软著材料时触发。 -metadata: - openclaw: - emoji: 📜 ---- - -# 软件著作权登记 - -一键生成中国计算机软件著作权登记所需的全部材料,并可辅助完成在线填报。 - -**依赖**:`web-form-fill`(在线填报时使用)。材料生成命令通过 PATH wrapper `swcr-register <子命令>` 调用,不要拼接脚本路径。 - ---- - -## 核心工作流 - -``` -1. 收集信息 → 2. 生成材料 → 3. 用户确认 → 4. 在线填报(可选) -``` - -### Step 1:收集信息 - -向用户确认以下信息(已有明确答案的跳过): - -| 信息项 | 说明 | 示例 | -|--------|------|------| -| 代码来源 | GitHub URL 或本地目录路径 | `https://github.com/user/repo` 或 `/path/to/project` | -| 软件全称 | 完整软件名称,需与简称不同 | `智能数据分析平台软件` | -| 软件简称 | 缩写或简称 | `智数平台` | -| 版本号 | 格式 V1.0 或 1.0 | `V1.0` | -| 开发完成日期 | 格式 YYYY-MM-DD | `2026-05-20` | -| 首次发表日期 | 格式 YYYY-MM-DD,未发表填"未发表" | `2026-06-01` | -| 开发方式 | 独立开发 / 合作开发 | `独立开发` | -| 著作权人 | 著作权人姓名/名称 | `张三` | -| 作者 | 开发者姓名(独立开发时与著作权人一致) | `张三` | - -**可选信息**(有默认值,用户可覆盖): - -| 信息项 | 默认值 | 说明 | -|--------|--------|------| -| 源代码后缀 | 自动检测 | 不指定时脚本自动识别 | -| 注释字符 | 自动检测 | 不指定时脚本自动识别 | -| 排除目录 | `node_modules, .git, __pycache__, venv, dist, build` | 常见非源码目录 | -| 输出目录 | 当前工作区 | 生成的文件存放位置 | - -**合作开发额外信息**: - -如果开发方式为合作开发,还需收集: -- 其他作者姓名 -- 其他著作权人(必须在登记网站注册并完成实名认证) -- 合作开发协议文件路径(如已有) - -### Step 2:准备代码 - -- 如果用户提供的是 GitHub URL:`git clone ` 到临时目录 -- 如果是本地目录:直接使用 -- 自动分析代码结构,识别主要编程语言和文件扩展名 - -### Step 3:生成材料 - -依次执行三个脚本,生成三份材料: - -#### 3-A. 程序鉴别材料(源程序文档) - -```bash -swcr-register code-doc \ - --title "<软件全称>" \ - --version "<版本号>" \ - --source-dir "<代码目录>" \ - --output "<输出目录>/<软件简称>_源程序.docx" \ - [--exts py js ts] \ - [--comment-chars "#" "//"] \ - [--excludes "node_modules" ".git"] \ - [--max-front-pages 30] \ - [--max-back-pages 30] -``` - -生成规则: -- 每页 50 行有效代码(非空行、非注释行) -- 前 30 页 + 后 30 页,中间省略页 -- 源程序量 > 3000 行时,文档必须为 61 页 -- 源程序量 ≤ 3000 行时,文档可少于 61 页 -- 页眉:软件名称 + 版本号(左)+ 页码(右) -- 代码字体:Courier New 8pt -- 中文辅助字体:SimSun - -#### 3-B. 软件操作手册 - -```bash -swcr-register manual \ - --title "<软件全称>" \ - --version "<版本号>" \ - --readme "" \ - --output "<输出目录>/<软件简称>_操作手册.docx" -``` - -生成规则: -- 从 README.md 转换为格式化 DOCX -- 页眉:软件名称 + 版本号 -- 标题、正文、代码块、列表等正确排版 -- 中文字体:SimHei;代码字体:Courier New - -#### 3-C. 申请填报信息 Markdown - -```bash -swcr-register form-info \ - --title "<软件全称>" \ - --short-name "<软件简称>" \ - --version "<版本号>" \ - --source-dir "<代码目录>" \ - --completion-date "<开发完成日期>" \ - --publish-date "<首次发表日期>" \ - --dev-method "<开发方式>" \ - --author "<作者>" \ - --copyright-holder "<著作权人>" \ - --output "<输出目录>/<软件简称>_填报信息.md" \ - [--co-authors "作者2" "作者3"] \ - [--co-holders "著作权人2" "著作权人3"] -``` - -生成内容包含: -- 软件全称与简称(确保不同) -- 版本号 -- 开发完成日期与首次发表日期 -- 开发方式与著作权人信息 -- **软件主要功能**(100 字以上,从 README 提取) -- **软件技术特点**(50 字以上,从代码结构分析) -- 源程序量(行数) -- 源程序文档页数 -- 需上传的文件清单及对应字段 -- 线下邮寄材料清单 - -### Step 4:用户确认 - -**必须等用户确认后再进行下一步。** - -向用户展示: -1. 三份生成文件的路径 -2. 填报信息 Markdown 的关键内容摘要 -3. 询问: - - "请检查生成的材料是否有问题,需要修改请告知。" - - "确认无误后,是否需要我辅助进行在线填报?(是/否)" - -### Step 5:在线填报(用户确认后) - -仅在用户明确要求辅助填报时执行。 - -1. 打开 https://register.ccopyright.com.cn/registration.html#/registerSoft -2. 选择 **R11** → **计算机软件著作权登记申请** → 点击 **立即登记** -3. **提醒用户登录账号**(如未注册需先注册 + 实名认证,认证需 1-3 天) -4. 用户确认登录完成后,调用 **web-form-fill** 技能: - - 选择"我是申请人" - - 填写软件信息(全称、简称、版本号) - - 填写开发信息(开发方式、完成日期、发表日期、作者、著作权人) - - 填写软件功能与特点(主要功能 100+ 字、技术特点 50+ 字) - - 上传程序鉴别材料(源程序文档 .docx) - - 上传文档鉴别材料(操作手册 .docx) - - 信息确认页填写 - - 选择邮寄方式 → 挂号信 → 填写收信地址 -5. **web-form-fill 的提交前确认步骤**:所有内容填完后,截图让用户确认,**禁止自动提交** - ---- - -## 填报注意事项 - -| 事项 | 要求 | -|------|------| -| 软件全称与简称 | **必须不同** | -| 版本号 | V1.0 或 1.0 | -| 主要功能 | **100 字以上** | -| 技术特点 | **50 字以上** | -| 合作开发 | 需上传合作开发协议,其他著作权人须在网站注册并实名认证 | -| 证书副本数量 | 有几个其他著作权人就填几 | -| 源程序量 > 3000 行 | 源程序文档必须 61 页,每页 50 行 | -| 源程序量 ≤ 3000 行 | 源程序文档可少于 61 页 | -| 身份证复印件 | 一页即可 | -| 打印要求 | 所有材料**单面打印** | -| 证书领取 | 选择挂号信邮寄 | - ---- - -## 线下邮寄材料清单 - -在线填报提交后,需打印以下材料邮寄: - -1. 软件著作权登记申请表(网站自动生成,下载打印) -2. 申请人身份证明(身份证复印件,一页) -3. 程序鉴别材料(源程序文档打印件) -4. 文档鉴别材料(操作手册打印件) -5. 合作开发协议(如适用) - ---- - -## 与其他技能协作 - -| 场景 | 配合技能 | -|------|---------| -| 在线填报表单 | `web-form-fill` | -| 记录申报状态 | `ir-record` | diff --git a/crews/main/skills/swcr-register/scripts/generate_code_doc.py b/crews/main/skills/swcr-register/scripts/generate_code_doc.py deleted file mode 100644 index bd48563e..00000000 --- a/crews/main/skills/swcr-register/scripts/generate_code_doc.py +++ /dev/null @@ -1,436 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Generate software copyright registration source code document (DOCX). - -Produces a formatted Word document containing source code that meets -Chinese software copyright authority requirements: -- Each page contains 50 effective lines of code (non-blank, non-comment) -- Front 30 pages + back 30 pages with an ellipsis page in between -- Header: software name + version (left) + page number (right) -- Code font: Courier New 8pt; Chinese auxiliary font: SimSun - -Usage: - python generate_code_doc.py \ - --title "智能数据分析平台软件" \ - --version "V1.0" \ - --source-dir /path/to/code \ - --output output.docx -""" - -import argparse -import codecs -import logging -import sys -from os import scandir -from os.path import abspath, relpath -from typing import List - -try: - import chardet - CHARDET_AVAILABLE = True -except ImportError: - CHARDET_AVAILABLE = False - -try: - from docx import Document - from docx.shared import Pt, Inches - from docx.enum.text import WD_ALIGN_PARAGRAPH - from docx.oxml.ns import qn - DOCX_AVAILABLE = True -except ImportError: - DOCX_AVAILABLE = False - -logger = logging.getLogger(__name__) - -DEFAULT_EXTS = [ - "c", "h", "py", "js", "ts", "java", "cpp", "hpp", - "go", "rs", "rb", "php", "cs", "swift", "kt", "scala", - "jsx", "tsx", "vue", "svelte", -] -DEFAULT_COMMENT_CHARS = ["/*", "* ", "*/", "//", "#"] -DEFAULT_EXCLUDES = [ - "node_modules", ".git", "__pycache__", "venv", ".venv", - "dist", "build", ".next", ".nuxt", "target", "bin", - "obj", ".idea", ".vscode", "coverage", ".cache", -] - - -def detect_encoding(file_path: str) -> str: - """Detect file encoding using chardet with fallback.""" - if not CHARDET_AVAILABLE: - return "utf-8" - - with open(file_path, "rb") as fd: - raw_data = fd.read(32768) # 32KB sample is sufficient for chardet - - result = chardet.detect(raw_data) - encoding = result.get("encoding", "utf-8") - confidence = result.get("confidence", 0) - - if confidence < 0.7: - for enc in ["utf-8", "gbk", "gb2312", "big5", "latin-1"]: - try: - raw_data.decode(enc) - encoding = enc - break - except (UnicodeDecodeError, LookupError): - continue - - return encoding - - -def find_code_files( - source_dir: str, - exts: List[str], - excludes: List[str], -) -> List[str]: - """Recursively find source code files matching extensions, excluding dirs.""" - files: List[str] = [] - abs_excludes = [abspath(e) for e in excludes] - - def _should_exclude(path: str) -> bool: - abs_path = abspath(path) - return any(abs_path.startswith(ex) for ex in abs_excludes) - - def _scan(directory: str) -> None: - try: - for entry in scandir(directory): - if entry.name.startswith("."): - continue - if _should_exclude(entry.path): - continue - if entry.is_file(): - if any(entry.name.endswith(f".{ext}") for ext in exts): - files.append(abspath(entry.path)) - elif entry.is_dir(): - _scan(entry.path) - except PermissionError: - logger.warning("Permission denied: %s", directory) - - _scan(source_dir) - return files - - -def is_blank_line(line: str) -> bool: - return not bool(line.strip()) - - -def is_comment_line(line: str, comment_chars: List[str]) -> bool: - stripped = line.lstrip() - return any(stripped.startswith(cc) for cc in comment_chars) - - -def wrap_long_line(line: str, max_chars: int = 90) -> List[str]: - if len(line) <= max_chars: - return [line] - wrapped = [] - while len(line) > max_chars: - wrapped.append(line[:max_chars]) - line = line[max_chars:] - if line: - wrapped.append(line) - return wrapped - - -def collect_code_lines( - files: List[str], - comment_chars: List[str], - base_dir: str, -) -> List[str]: - """Read all source files and collect code lines.""" - all_lines: List[str] = [] - - for filepath in files: - encoding = detect_encoding(filepath) - logger.info("Processing: %s (encoding: %s)", filepath, encoding) - - try: - relative = relpath(filepath, base_dir) - except ValueError: - relative = filepath - - all_lines.append(f"# File: {relative}") - - try: - with codecs.open(filepath, "r", encoding, errors="replace") as fp: - for line in fp: - line = line.rstrip() - all_lines.extend(wrap_long_line(line, max_chars=90)) - except Exception as exc: - logger.error("Error reading %s: %s", filepath, exc) - all_lines.append(f"# Error reading file: {exc}") - - return all_lines - - -def count_effective_lines(lines: List[str], comment_chars: List[str]) -> int: - """Count non-blank, non-comment lines.""" - return sum( - 1 for line in lines - if not is_blank_line(line) and not is_comment_line(line, comment_chars) - ) - - -def _is_effective(line: str, comment_chars: List[str]) -> bool: - """Check if a line is effective (non-blank and non-comment).""" - return not is_blank_line(line) and not is_comment_line(line, comment_chars) - - -def split_into_pages( - all_lines: List[str], - comment_chars: List[str], - lines_per_page: int = 50, - max_front_pages: int = 30, - max_back_pages: int = 30, -) -> tuple: - """Split code lines into front pages and back pages. - - Each page contains lines_per_page effective lines (non-blank, non-comment). - """ - if not all_lines: - return [], [] - - front_pages: List[List[str]] = [] - back_pages: List[List[str]] = [] - - current_page: List[str] = [] - effective_count = 0 - page_count = 0 - - i = 0 - while i < len(all_lines) and page_count < max_front_pages: - line = all_lines[i] - current_page.append(line) - if _is_effective(line, comment_chars): - effective_count += 1 - if effective_count >= lines_per_page or i == len(all_lines) - 1: - front_pages.append(current_page.copy()) - logger.info( - "Front page %d: %d lines, %d effective", - page_count + 1, len(current_page), effective_count, - ) - current_page = [] - effective_count = 0 - page_count += 1 - i += 1 - - if i < len(all_lines): - remaining = all_lines[i:] - remaining_effective = count_effective_lines(remaining, comment_chars) - - if remaining_effective > max_back_pages * lines_per_page: - target = max_back_pages * lines_per_page - start_pos = len(remaining) - 1 - eff = 0 - for j in range(len(remaining) - 1, -1, -1): - if _is_effective(remaining[j], comment_chars): - eff += 1 - if eff >= target: - start_pos = j - break - back_source = remaining[start_pos:] - else: - back_source = remaining - - current_page = [] - effective_count = 0 - for line in back_source: - current_page.append(line) - if _is_effective(line, comment_chars): - effective_count += 1 - if effective_count >= lines_per_page: - back_pages.append(current_page.copy()) - current_page = [] - effective_count = 0 - if current_page: - back_pages.append(current_page) - - return front_pages, back_pages - - -def create_docx( - filename: str, - title: str, - version: str, - front_pages: List[List[str]], - back_pages: List[List[str]], -) -> None: - """Create the source code DOCX document.""" - if not DOCX_AVAILABLE: - print("Error: python-docx is required. Install with: pip install python-docx") - sys.exit(1) - - doc = Document() - - for section in doc.sections: - section.top_margin = Inches(0.8) - section.bottom_margin = Inches(0.5) - section.left_margin = Inches(0.8) - section.right_margin = Inches(0.5) - - total_front = len(front_pages) - - for page_num, page_lines in enumerate(front_pages, 1): - _add_code_page(doc, page_lines, page_num, title, version) - if page_num < total_front or back_pages: - doc.add_page_break() - - if back_pages: - _add_ellipsis_page(doc, total_front + 1, title, version) - for page_num, page_lines in enumerate(back_pages, total_front + 2): - doc.add_page_break() - _add_code_page(doc, page_lines, page_num, title, version) - - doc.save(filename) - - -def _add_code_page( - doc: Document, - lines: List[str], - page_num: int, - title: str, - version: str, -) -> None: - """Add one page of code to the document.""" - header_text = f"{title} {version}" - p = doc.add_paragraph() - run = p.add_run(header_text) - run.font.size = Pt(9) - run = p.add_run(f"\t\t\t\t\t\t\t\t\t\t{page_num}") - run.font.size = Pt(9) - p.paragraph_format.space_before = Pt(0) - p.paragraph_format.space_after = Pt(2) - p.paragraph_format.line_spacing = 1.0 - - p = doc.add_paragraph() - run = p.add_run("_" * 95) - run.font.size = Pt(8) - p.paragraph_format.space_before = Pt(0) - p.paragraph_format.space_after = Pt(4) - p.paragraph_format.line_spacing = 1.0 - - for line in lines: - p = doc.add_paragraph() - run = p.add_run(line if line.strip() else " ") - run.font.name = "Courier New" - run.font.size = Pt(8) - p.paragraph_format.space_before = Pt(0) - p.paragraph_format.space_after = Pt(0) - p.paragraph_format.line_spacing = 1.0 - r = run._element - r.rPr.rFonts.set(qn("w:eastAsia"), "SimSun") - - -def _add_ellipsis_page( - doc: Document, - page_num: int, - title: str, - version: str, -) -> None: - """Add an ellipsis page to the document.""" - header = f"{title} {version}" - p = doc.add_paragraph() - run = p.add_run(header) - run.font.size = Pt(10) - run = p.add_run(f"\t\t\t\t\t\t\t\t\t\t{page_num}") - run.font.size = Pt(10) - - p = doc.add_paragraph() - p.add_run("_" * 100) - - for _ in range(20): - doc.add_paragraph() - - p = doc.add_paragraph() - run = p.add_run("......") - p.alignment = WD_ALIGN_PARAGRAPH.CENTER - run.font.size = Pt(24) - - -def main() -> int: - parser = argparse.ArgumentParser( - description="Generate source code document for software copyright registration." - ) - parser.add_argument("--title", required=True, help="Software full name") - parser.add_argument("--version", default="V1.0", help="Software version") - parser.add_argument("--source-dir", required=True, help="Source code directory") - parser.add_argument("--output", required=True, help="Output DOCX file path") - parser.add_argument( - "--exts", nargs="+", default=None, - help="Source code file extensions (auto-detect if omitted)", - ) - parser.add_argument( - "--comment-chars", nargs="+", default=None, - help="Comment characters (auto-detect if omitted)", - ) - parser.add_argument( - "--excludes", nargs="+", default=DEFAULT_EXCLUDES, - help="Directories to exclude", - ) - parser.add_argument( - "--max-front-pages", type=int, default=30, - help="Max front pages (default: 30)", - ) - parser.add_argument( - "--max-back-pages", type=int, default=30, - help="Max back pages (default: 30)", - ) - parser.add_argument( - "--verbose", action="store_true", - help="Enable verbose logging", - ) - - args = parser.parse_args() - - if args.verbose: - logging.basicConfig(level=logging.DEBUG) - else: - logging.basicConfig(level=logging.INFO) - - if not DOCX_AVAILABLE: - print("Error: python-docx is required. Install with: pip install python-docx") - return 1 - - exts = args.exts if args.exts else DEFAULT_EXTS - comment_chars = args.comment_chars if args.comment_chars else DEFAULT_COMMENT_CHARS - - source_dir = abspath(args.source_dir) - - print(f"Scanning source code in: {source_dir}") - print(f"Extensions: {exts}") - print(f"Comment chars: {comment_chars}") - print(f"Excludes: {args.excludes}") - - files = find_code_files(source_dir, exts, args.excludes) - print(f"Found {len(files)} source code files") - - if not files: - print("Error: No source code files found. Check --source-dir and --exts.") - return 1 - - all_lines = collect_code_lines(files, comment_chars, source_dir) - print(f"Total lines collected: {len(all_lines)}") - - effective = count_effective_lines(all_lines, comment_chars) - print(f"Effective lines (non-blank, non-comment): {effective}") - - front_pages, back_pages = split_into_pages( - all_lines, comment_chars, - lines_per_page=50, - max_front_pages=args.max_front_pages, - max_back_pages=args.max_back_pages, - ) - print(f"Front pages: {len(front_pages)}") - print(f"Back pages: {len(back_pages)}") - total_pages = len(front_pages) + (1 if back_pages else 0) + len(back_pages) - print(f"Total pages (including ellipsis): {total_pages}") - - create_docx(args.output, args.title, args.version, front_pages, back_pages) - print(f"Source code document created: {args.output}") - - return 0 - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/crews/main/skills/swcr-register/scripts/generate_form_info.py b/crews/main/skills/swcr-register/scripts/generate_form_info.py deleted file mode 100644 index 9fa38e36..00000000 --- a/crews/main/skills/swcr-register/scripts/generate_form_info.py +++ /dev/null @@ -1,486 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Generate form-filling information Markdown for software copyright registration. - -Analyzes the codebase and README to produce a Markdown file containing all -information needed to fill the online registration form at -https://register.ccopyright.com.cn/registration.html#/registerSoft - -The Markdown includes: -- Software name (full and short), version -- Development info (dates, method, authors, copyright holders) -- Software functions and technical features (extracted from README) -- Source code statistics (line count, page count) -- Upload file checklist -- Offline mailing material checklist - -Usage: - python generate_form_info.py \ - --title "智能数据分析平台软件" \ - --short-name "智数平台" \ - --version "V1.0" \ - --source-dir /path/to/code \ - --completion-date 2026-05-20 \ - --publish-date 2026-06-01 \ - --dev-method "独立开发" \ - --author "张三" \ - --copyright-holder "张三" \ - --output form_info.md -""" - -import argparse -import logging -import os -import re -import sys -from os import scandir -from os.path import abspath -from typing import List - -logger = logging.getLogger(__name__) - -DEFAULT_EXTS = [ - "c", "h", "py", "js", "ts", "java", "cpp", "hpp", - "go", "rs", "rb", "php", "cs", "swift", "kt", "scala", - "jsx", "tsx", "vue", "svelte", -] -DEFAULT_COMMENT_CHARS = ["/*", "* ", "*/", "//", "#"] -DEFAULT_EXCLUDES = [ - "node_modules", ".git", "__pycache__", "venv", ".venv", - "dist", "build", ".next", ".nuxt", "target", "bin", - "obj", ".idea", ".vscode", "coverage", ".cache", -] - - -def count_source_lines( - source_dir: str, - exts: List[str], - excludes: List[str], - comment_chars: List[str], -) -> dict: - """Count total and effective source code lines.""" - total_lines = 0 - effective_lines = 0 - file_count = 0 - abs_excludes = [abspath(e) for e in excludes] - - def _should_exclude(path: str) -> bool: - abs_path = abspath(path) - return any(abs_path.startswith(ex) for ex in abs_excludes) - - def _scan(directory: str) -> None: - nonlocal total_lines, effective_lines, file_count - try: - for entry in scandir(directory): - if entry.name.startswith("."): - continue - if _should_exclude(entry.path): - continue - if entry.is_file(): - if any(entry.name.endswith(f".{ext}") for ext in exts): - try: - with open(entry.path, "r", encoding="utf-8", errors="replace") as f: - for line in f: - total_lines += 1 - stripped = line.strip() - if stripped and not any( - stripped.startswith(cc) for cc in comment_chars - ): - effective_lines += 1 - file_count += 1 - except Exception as exc: - logger.warning("Error reading file %s: %s", entry.path, exc) - elif entry.is_dir(): - _scan(entry.path) - except PermissionError: - logger.warning("Permission denied: %s", directory) - - _scan(source_dir) - return { - "total_lines": total_lines, - "effective_lines": effective_lines, - "file_count": file_count, - } - - -def calculate_pages(effective_lines: int, lines_per_page: int = 50) -> dict: - """Calculate source code document page count.""" - if effective_lines <= 0: - return {"total_pages": 0, "front_pages": 0, "back_pages": 0} - - total_pages_needed = (effective_lines + lines_per_page - 1) // lines_per_page - - if total_pages_needed <= 60: - return { - "total_pages": total_pages_needed, - "front_pages": total_pages_needed, - "back_pages": 0, - } - - front = 30 - back = 30 - total = front + 1 + back # +1 for ellipsis page - return { - "total_pages": total, - "front_pages": front, - "back_pages": back, - } - - -def detect_languages(source_dir: str, excludes: List[str]) -> List[str]: - """Detect primary programming languages in the codebase.""" - lang_map = { - "py": "Python", "js": "JavaScript", "ts": "TypeScript", - "java": "Java", "c": "C", "h": "C", "cpp": "C++", "hpp": "C++", - "go": "Go", "rs": "Rust", "rb": "Ruby", "php": "PHP", - "cs": "C#", "swift": "Swift", "kt": "Kotlin", "scala": "Scala", - "jsx": "React JSX", "tsx": "React TSX", "vue": "Vue", "svelte": "Svelte", - } - ext_counts: dict = {} - abs_excludes = [abspath(e) for e in excludes] - - def _should_exclude(path: str) -> bool: - abs_path = abspath(path) - return any(abs_path.startswith(ex) for ex in abs_excludes) - - def _scan(directory: str) -> None: - try: - for entry in scandir(directory): - if entry.name.startswith("."): - continue - if _should_exclude(entry.path): - continue - if entry.is_file(): - ext = entry.name.rsplit(".", 1)[-1] if "." in entry.name else "" - if ext in lang_map: - ext_counts[ext] = ext_counts.get(ext, 0) + 1 - elif entry.is_dir(): - _scan(entry.path) - except PermissionError: - logger.warning("Permission denied: %s", directory) - - _scan(source_dir) - - sorted_exts = sorted(ext_counts.keys(), key=lambda x: ext_counts[x], reverse=True) - languages = [lang_map[ext] for ext in sorted_exts if ext in lang_map] - return languages[:5] # top 5 languages - - -def extract_functions_from_readme(readme_path: str) -> dict: - """Extract software functions and features from README.""" - result = { - "main_functions": "", - "tech_features": "", - "description": "", - } - - if not readme_path or not os.path.isfile(readme_path): - return result - - try: - with open(readme_path, "r", encoding="utf-8", errors="replace") as f: - content = f.read() - except Exception: - return result - - # Extract description (first paragraph after title) - lines = content.split("\n") - desc_parts: List[str] = [] - past_title = False - for line in lines: - stripped = line.strip() - if stripped.startswith("# "): - past_title = True - continue - if past_title and stripped and not stripped.startswith("#") and not stripped.startswith("!["): - # Skip table of contents lines, horizontal rules, and short labels - if re.match(r"^[\s\-=*]+$", stripped): - continue - if len(stripped) < 5 and not any(c in stripped for c in ",。、;"): - continue - desc_parts.append(stripped) - if len(desc_parts) >= 3: - break - if past_title and not stripped and desc_parts: - break - - result["description"] = " ".join(desc_parts) - - # Extract "功能" or "Features" section - in_features = False - feature_parts: List[str] = [] - for line in lines: - stripped = line.strip() - if re.match(r"^#+\s*(功能|特性|feature|function)", stripped, re.IGNORECASE): - in_features = True - continue - if in_features: - if stripped.startswith("#"): - break - if stripped and not stripped.startswith("```"): - feature_parts.append(stripped) - - result["main_functions"] = " ".join(feature_parts[:10]) - - # Extract "技术" or "Tech" section - in_tech = False - tech_parts: List[str] = [] - for line in lines: - stripped = line.strip() - if re.match(r"^#+\s*(技术|架构|tech|arch|stack)", stripped, re.IGNORECASE): - in_tech = True - continue - if in_tech: - if stripped.startswith("#"): - break - if stripped and not stripped.startswith("```"): - tech_parts.append(stripped) - - result["tech_features"] = " ".join(tech_parts[:5]) - - return result - - -def generate_form_markdown( - title: str, - short_name: str, - version: str, - source_dir: str, - completion_date: str, - publish_date: str, - dev_method: str, - author: str, - copyright_holder: str, - co_authors: List[str], - co_holders: List[str], - stats: dict, - pages: dict, - languages: List[str], - readme_info: dict, -) -> str: - """Generate the form-filling information Markdown.""" - is_coop = dev_method == "合作开发" - all_authors = [author] + co_authors - all_holders = [copyright_holder] + co_holders - - # Generate main functions text (ensure 100+ chars) - main_functions = readme_info.get("main_functions", "") - desc = readme_info.get("description", "") - if len(main_functions) < 100 and desc: - main_functions = f"{main_functions} {desc}".strip() - if len(main_functions) < 100: - lang_str = "、".join(languages) if languages else "多种编程语言" - main_functions = ( - f"{main_functions} 本软件基于{lang_str}开发," - "提供完整的数据处理、分析和管理功能," - "支持多种数据源的接入和处理," - "具备良好的扩展性、稳定性和易用性," - "可满足不同场景下的业务需求。" - ).strip() - - # Generate tech features text (ensure 50+ chars) - tech_features = readme_info.get("tech_features", "") - if len(tech_features) < 50: - lang_str = "、".join(languages) if languages else "多种编程语言" - tech_features = ( - f"{tech_features} 本软件基于{lang_str}开发," - "采用模块化架构设计," - "具有良好的可维护性和可扩展性," - "支持跨平台部署和运行。" - ).strip() - - # Source code document page info - effective = stats.get("effective_lines", 0) - if effective > 3000: - source_doc_pages = 61 - source_doc_note = "源程序量 > 3000 行,文档必须为 61 页" - else: - source_doc_pages = pages.get("total_pages", 0) - source_doc_note = f"源程序量 ≤ 3000 行,文档 {source_doc_pages} 页" - - lines = [ - f"# 软件著作权登记 — 填报信息", - "", - f"## 软件基本信息", - "", - f"| 字段 | 值 |", - f"|------|-----|", - f"| 软件全称 | {title} |", - f"| 软件简称 | {short_name} |", - f"| 版本号 | {version} |", - f"| 开发完成日期 | {completion_date} |", - f"| 首次发表日期 | {publish_date} |", - f"| 开发方式 | {dev_method} |", - "", - f"## 著作权人信息", - "", - f"| 字段 | 值 |", - f"|------|-----|", - f"| 著作权人 | {'、'.join(all_holders)} |", - f"| 作者 | {'、'.join(all_authors)} |", - ] - - if is_coop: - lines += [ - f"| 合作开发 | 是 |", - f"| 证书副本数量 | {len(co_holders)} |", - "", - "### 合作开发注意事项", - "", - "- 需上传**合作开发协议**", - "- 其他著作权人必须在登记网站注册并完成实名认证", - "- 证书副本数量 = 其他著作权人数量", - ] - else: - lines += [ - f"| 合作开发 | 否 |", - f"| 证书副本数量 | 0 |", - ] - - lines += [ - "", - f"## 软件功能与特点", - "", - f"### 主要功能({len(main_functions)} 字)", - "", - main_functions, - "", - f"### 技术特点({len(tech_features)} 字)", - "", - tech_features, - "", - f"## 源程序统计", - "", - f"| 项目 | 值 |", - f"|------|-----|", - f"| 源代码文件数 | {stats.get('file_count', 0)} |", - f"| 总行数 | {stats.get('total_lines', 0)} |", - f"| 有效行数(非空非注释) | {effective} |", - f"| 源程序文档页数 | {source_doc_pages} |", - f"| 说明 | {source_doc_note} |", - "", - f"## 上传文件清单", - "", - f"| 表单字段 | 文件 | 格式 |", - f"|---------|------|------|", - f"| 程序鉴别材料 | {short_name}_源程序.docx | DOCX |", - f"| 文档鉴别材料 | {short_name}_操作手册.docx | DOCX |", - ] - - if is_coop: - lines += [ - f"| 合作开发协议 | 合作开发协议.pdf | PDF |", - ] - - lines += [ - "", - f"## 线下邮寄材料清单", - "", - "在线填报提交后,需**单面打印**以下材料邮寄:", - "", - "1. 软件著作权登记申请表(网站自动生成,下载打印)", - "2. 申请人身份证明(身份证复印件,**一页即可**)", - "3. 程序鉴别材料(源程序文档打印件)", - "4. 文档鉴别材料(操作手册打印件)", - ] - - if is_coop: - lines += ["5. 合作开发协议"] - - lines += [ - "", - "## 填报流程提示", - "", - "1. 打开 https://register.ccopyright.com.cn/registration.html#/registerSoft", - "2. 选择 **R11** → **计算机软件著作权登记申请** → 点击 **立即登记**", - "3. 登录账号(未注册需先注册 + 实名认证,认证需 1-3 天)", - "4. 选择 **我是申请人**", - "5. 填写软件信息(全称、简称、版本号)", - "6. 填写开发信息(开发方式、完成日期、发表日期、作者、著作权人)", - "7. 填写软件功能与特点(主要功能 100+ 字、技术特点 50+ 字)", - "8. 上传程序鉴别材料和文档鉴别材料", - "9. 信息确认页:填写身份证复印件页数(1 页),其他自动填充", - "10. 选择邮寄 → 挂号信 → 填写收信地址 → 保存并提交申请", - "", - "### 关键提醒", - "", - "- 软件全称与简称**必须不同**", - "- 主要功能**100 字以上**,技术特点**50 字以上**", - "- 所有材料**单面打印**", - "- 证书领取选择**挂号信**", - ] - - return "\n".join(lines) - - -def main() -> int: - parser = argparse.ArgumentParser( - description="Generate form-filling info Markdown for software copyright registration." - ) - parser.add_argument("--title", required=True, help="Software full name") - parser.add_argument("--short-name", required=True, help="Software short name") - parser.add_argument("--version", default="V1.0", help="Software version") - parser.add_argument("--source-dir", required=True, help="Source code directory") - parser.add_argument("--completion-date", required=True, help="Development completion date (YYYY-MM-DD)") - parser.add_argument("--publish-date", required=True, help="First publication date (YYYY-MM-DD) or '未发表'") - parser.add_argument("--dev-method", required=True, choices=["独立开发", "合作开发"], help="Development method") - parser.add_argument("--author", required=True, help="Primary author name") - parser.add_argument("--copyright-holder", required=True, help="Copyright holder name") - parser.add_argument("--co-authors", nargs="*", default=[], help="Co-author names") - parser.add_argument("--co-holders", nargs="*", default=[], help="Co-copyright holder names") - parser.add_argument("--readme", default=None, help="Path to README.md for extracting features") - parser.add_argument("--output", required=True, help="Output Markdown file path") - parser.add_argument( - "--excludes", nargs="+", default=DEFAULT_EXCLUDES, - help="Directories to exclude from line count", - ) - - args = parser.parse_args() - - if args.title == args.short_name: - print("Error: 软件全称和简称不能相同!") - return 1 - - source_dir = abspath(args.source_dir) - - print(f"Analyzing source code in: {source_dir}") - - stats = count_source_lines(source_dir, DEFAULT_EXTS, args.excludes, DEFAULT_COMMENT_CHARS) - print(f"Files: {stats['file_count']}, Total lines: {stats['total_lines']}, Effective lines: {stats['effective_lines']}") - - pages = calculate_pages(stats["effective_lines"]) - print(f"Source doc pages: {pages['total_pages']}") - - languages = detect_languages(source_dir, args.excludes) - print(f"Languages: {', '.join(languages)}") - - readme_info = extract_functions_from_readme(args.readme) if args.readme else {} - - markdown = generate_form_markdown( - title=args.title, - short_name=args.short_name, - version=args.version, - source_dir=source_dir, - completion_date=args.completion_date, - publish_date=args.publish_date, - dev_method=args.dev_method, - author=args.author, - copyright_holder=args.copyright_holder, - co_authors=args.co_authors, - co_holders=args.co_holders, - stats=stats, - pages=pages, - languages=languages, - readme_info=readme_info, - ) - - with open(args.output, "w", encoding="utf-8") as f: - f.write(markdown) - - print(f"Form info Markdown created: {args.output}") - return 0 - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/crews/main/skills/swcr-register/scripts/generate_manual.py b/crews/main/skills/swcr-register/scripts/generate_manual.py deleted file mode 100644 index 45669dc8..00000000 --- a/crews/main/skills/swcr-register/scripts/generate_manual.py +++ /dev/null @@ -1,272 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Generate software operation manual (DOCX) from README. - -Converts a README.md file into a formatted Word document suitable for -software copyright registration "文档鉴别材料" (Document Identification Material). - -Features: -- Proper heading hierarchy (H0/H1/H2) -- Code blocks with monospace font -- Bullet lists -- Tables (basic support) -- Header: software name + version -- Chinese font: SimHei; Code font: Courier New - -Usage: - python generate_manual.py \ - --title "智能数据分析平台软件" \ - --version "V1.0" \ - --readme /path/to/README.md \ - --output manual.docx -""" - -import argparse -import re -import sys -from typing import List, Tuple - -try: - from docx import Document - from docx.shared import Pt, Inches, RGBColor - from docx.enum.text import WD_ALIGN_PARAGRAPH - from docx.oxml.ns import qn - DOCX_AVAILABLE = True -except ImportError: - DOCX_AVAILABLE = False - - -def parse_markdown(content: str) -> List[Tuple[str, str]]: - """Parse markdown into a list of (type, content) tokens. - - Token types: - h0, h1, h2, h3 - headings - code_start, code_end - code block delimiters - code_line - line inside code block - list_item - bullet list item - table_row - table row (pipe-separated) - table_separator - table separator line (---|---) - paragraph - regular text - blank - empty line - hr - horizontal rule - """ - tokens: List[Tuple[str, str]] = [] - in_code_block = False - - for line in content.split("\n"): - if line.strip().startswith("```"): - if in_code_block: - tokens.append(("code_end", "")) - in_code_block = False - else: - lang = line.strip()[3:].strip() - tokens.append(("code_start", lang)) - in_code_block = True - continue - - if in_code_block: - tokens.append(("code_line", line)) - continue - - stripped = line.strip() - - if not stripped: - tokens.append(("blank", "")) - elif stripped == "---" or stripped == "***" or stripped == "___": - tokens.append(("hr", "")) - elif line.startswith("# "): - tokens.append(("h0", stripped[2:])) - elif line.startswith("## "): - tokens.append(("h1", stripped[3:])) - elif line.startswith("### "): - tokens.append(("h2", stripped[4:])) - elif line.startswith("#### "): - tokens.append(("h3", stripped[5:])) - elif re.match(r"^\|.*\|$", stripped): - if re.match(r"^\|[\s\-:|]+\|$", stripped): - tokens.append(("table_separator", stripped)) - else: - tokens.append(("table_row", stripped)) - elif re.match(r"^[\s]*[-*+]\s", line): - text = re.sub(r"^[\s]*[-*+]\s", "", line) - tokens.append(("list_item", text)) - elif re.match(r"^\d+\.\s", stripped): - text = re.sub(r"^\d+\.\s", "", stripped) - tokens.append(("list_item", text)) - else: - tokens.append(("paragraph", stripped)) - - return tokens - - -def add_header(doc: Document, title: str, version: str) -> None: - """Add document header with software name and version.""" - section = doc.sections[0] - header = section.header - header_para = header.paragraphs[0] - header_para.text = f"{title} {version}" - header_para.alignment = WD_ALIGN_PARAGRAPH.LEFT - for run in header_para.runs: - run.font.size = Pt(9) - - -def set_chinese_font(doc: Document) -> None: - """Set default Chinese font to SimHei.""" - style = doc.styles["Normal"] - style.font.name = "SimHei" - style._element.rPr.rFonts.set(qn("w:eastAsia"), "SimHei") - - -def add_table_from_rows(doc: Document, rows: List[str]) -> None: - """Add a simple table from pipe-separated row strings.""" - if not rows: - return - - parsed = [] - for row in rows: - cells = [c.strip() for c in row.strip("|").split("|")] - parsed.append(cells) - - if not parsed: - return - - num_cols = max(len(r) for r in parsed) - table = doc.add_table(rows=len(parsed), cols=num_cols) - table.style = "Table Grid" - - for i, row_data in enumerate(parsed): - for j, cell_text in enumerate(row_data): - if j < num_cols: - cell = table.cell(i, j) - cell.text = cell_text - for paragraph in cell.paragraphs: - for run in paragraph.runs: - run.font.size = Pt(9) - - -def build_document( - tokens: List[Tuple[str, str]], - title: str, - version: str, -) -> Document: - """Build the DOCX document from parsed markdown tokens.""" - doc = Document() - - for section in doc.sections: - section.top_margin = Inches(1.0) - section.bottom_margin = Inches(0.8) - section.left_margin = Inches(1.0) - section.right_margin = Inches(0.8) - - set_chinese_font(doc) - add_header(doc, title, version) - - pending_paragraph: List[str] = [] - pending_table_rows: List[str] = [] - - def flush_paragraph() -> None: - if pending_paragraph: - text = " ".join(pending_paragraph) - p = doc.add_paragraph(text) - for run in p.runs: - run.font.size = Pt(10.5) - pending_paragraph.clear() - - def flush_table() -> None: - if pending_table_rows: - add_table_from_rows(doc, pending_table_rows) - pending_table_rows.clear() - - for token_type, content in tokens: - if token_type in ("h0", "h1", "h2", "h3"): - flush_paragraph() - flush_table() - level = int(token_type[1]) - doc.add_heading(content, level=level) - - elif token_type == "code_start": - flush_paragraph() - flush_table() - - elif token_type == "code_end": - flush_paragraph() - - elif token_type == "code_line": - p = doc.add_paragraph() - run = p.add_run(content) - run.font.name = "Courier New" - run.font.size = Pt(8) - p.paragraph_format.space_before = Pt(0) - p.paragraph_format.space_after = Pt(0) - p.paragraph_format.line_spacing = 1.0 - - elif token_type == "list_item": - flush_paragraph() - flush_table() - p = doc.add_paragraph(content, style="List Bullet") - for run in p.runs: - run.font.size = Pt(10.5) - - elif token_type == "table_row": - flush_paragraph() - pending_table_rows.append(content) - - elif token_type == "table_separator": - pass # skip separator lines - - elif token_type == "hr": - flush_paragraph() - flush_table() - p = doc.add_paragraph() - p.add_run("—" * 40) - - elif token_type == "blank": - flush_paragraph() - flush_table() - - elif token_type == "paragraph": - flush_table() - pending_paragraph.append(content) - - flush_paragraph() - flush_table() - - return doc - - -def main() -> int: - parser = argparse.ArgumentParser( - description="Generate software operation manual (DOCX) from README." - ) - parser.add_argument("--title", required=True, help="Software full name") - parser.add_argument("--version", default="V1.0", help="Software version") - parser.add_argument("--readme", required=True, help="Path to README.md") - parser.add_argument("--output", required=True, help="Output DOCX file path") - - args = parser.parse_args() - - if not DOCX_AVAILABLE: - print("Error: python-docx is required. Install with: pip install python-docx") - return 1 - - try: - with open(args.readme, "r", encoding="utf-8") as f: - content = f.read() - except FileNotFoundError: - print(f"Error: README file not found: {args.readme}") - return 1 - except Exception as exc: - print(f"Error reading README: {exc}") - return 1 - - tokens = parse_markdown(content) - doc = build_document(tokens, args.title, args.version) - doc.save(args.output) - - print(f"Operation manual created: {args.output}") - return 0 - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/crews/main/skills/swcr-register/swcr-register.sh b/crews/main/skills/swcr-register/swcr-register.sh deleted file mode 100755 index 638cac0a..00000000 --- a/crews/main/skills/swcr-register/swcr-register.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env bash -# swcr-register.sh — swcr-register 工具 wrapper(子命令分发) -# 让 agent 用 `swcr-register <子命令> [参数...]` 走 PATH,零路径拼接。 -# 每个子命令 exec 转发到 scripts/ 下对应 Python 脚本,不改语义。 -set -euo pipefail -SELF="${BASH_SOURCE[0]}" -# 解析 ~/.openclaw/bin 软链一层,定位工具目录(见 bd-record.sh 同款说明)。 -if [ -L "$SELF" ]; then - _target="$(readlink "$SELF")" - case "$_target" in - /*) SELF="$_target" ;; - *) SELF="$(cd "$(dirname "$SELF")" && pwd)/$_target" ;; - esac -fi -SCRIPT_DIR="$(cd "$(dirname "$SELF")" && pwd)" - -cmd="${1:-}" -if [ $# -gt 0 ]; then shift; fi - -case "$cmd" in - code-doc) exec python3 "$SCRIPT_DIR/scripts/generate_code_doc.py" "$@" ;; - manual) exec python3 "$SCRIPT_DIR/scripts/generate_manual.py" "$@" ;; - form-info) exec python3 "$SCRIPT_DIR/scripts/generate_form_info.py" "$@" ;; - *) - cat >&2 <<'USAGE' -用法: swcr-register <子命令> [参数...] - -子命令: - code-doc 生成程序鉴别材料(源程序文档 .docx) - manual 生成软件操作手册(README -> .docx) - form-info 生成申请填报信息 Markdown - -各子命令参数见 swcr-register 工具说明(SKILL.md)。 -USAGE - exit 1 - ;; -esac diff --git a/openclaw.version b/openclaw.version index 9c870884..71eba656 100644 --- a/openclaw.version +++ b/openclaw.version @@ -6,14 +6,5 @@ # git clone https://github.com/openclaw/openclaw openclaw # git -C openclaw checkout $OPENCLAW_COMMIT # -# ── 2026-09-12:2026.7.1-2 → extended-stable/2026.7.33(7 系延长稳定线)── -# 该分支**尚未打 tag、未发 npm 包**,所以只能 pin commit SHA(不能 pin tag); -# `openclaw update --channel extended-stable` 对 git checkout 无效,跟版必须手动 re-pin。 -# re-pin 前必须复验四项(命令见 docs/openclaw-2026.9.3-upgrade-fact-finding.md 附录 A): -# 1) 37 个 patch 的目标文件相对上一 pin 零漂移 -# 2) awada 依赖的 5 个契约文件零 diff -# 3) package.json 的 engines.node / packageManager 未变(否则要抬 CI 与 portable Node) -# 4) skill 加载机制文件未变(专家包架构依赖,见 docs/expert-pack-dna-architecture.md §2.4/§2.5) -# 本次 pin 的 f619d7a9fa3 四项均已复验通过(详见上述文档 §1、§6.4)。 -OPENCLAW_VERSION=2026.7.33 -OPENCLAW_COMMIT=f619d7a9fa3db8b7e9831a0b00f586a9b55559ab +OPENCLAW_VERSION=2026.7.1-2 +OPENCLAW_COMMIT=0790d9f593ad30c940ed93b5872a8cf6d6f3cf8c From b1d32d0e9cb4e69738a9cac2eb826b4a5ad37c75 Mon Sep 17 00:00:00 2001 From: bigbrother666sh Date: Sat, 12 Sep 2026 22:32:24 +0800 Subject: [PATCH 19/34] fix: update media sending instructions for awada in agent-skills.sh --- .../openclaw-2026.9.3-upgrade-fact-finding.md | 539 ------------------ scripts/lib/agent-skills.sh | 1 - 2 files changed, 540 deletions(-) delete mode 100644 docs/openclaw-2026.9.3-upgrade-fact-finding.md diff --git a/docs/openclaw-2026.9.3-upgrade-fact-finding.md b/docs/openclaw-2026.9.3-upgrade-fact-finding.md deleted file mode 100644 index b1229e5d..00000000 --- a/docs/openclaw-2026.9.3-upgrade-fact-finding.md +++ /dev/null @@ -1,539 +0,0 @@ -# openclaw 2026.9.3 升级事实调研 - -> 落盘 2026-09-11 · 调研窗口 2026-09-10 06:23 – 2026-09-11 07:30(CST) -> 当前基座:`2026.7.1-2`(`0790d9f593ad30c940ed93b5872a8cf6d6f3cf8c`,pin 在仓根 `openclaw.version`) -> 调研目标:`2026.9.3`(`1391f7cd2d4`,tag `v2026.9.3`);过渡候选:`extended-stable/2026.7.33`(tip `f619d7a9fa3`) -> **本文只记事实与实测数据,不做决策**;待拍板项集中在 §10,施工方案另开文档。 -> 相关:[browser-stack-replacement-spec-2026-07.md](./browser-stack-replacement-spec-2026-07.md)(camoufox 双线栈 spec)、[browser-extension-replacement-research.md](./browser-extension-replacement-research.md)、`patches/browser-camoufox-pivot/README.md` - -## 证据口径 - -| 标记 | 含义 | -|---|---| -| `[A]` | 2026-09-10 早会话内实测(pristine worktree `/tmp/oc93` 逐个 `git apply`、`git log --grep`、`ls-tree`),**本次落盘未复验**,仅记录当时结论 | -| `[B]` | 2026-09-11 落盘时在 `/home/wukong/wiseflow/openclaw` 用 `v2026.7.1-2` / `v2026.9.3` 两个 tag 现场实测(可复跑,命令见附录 A) | -| `[C]` | 上游文档 / release notes 原文(路径 + 行号以 9.3 tag 为准) | - ---- - -## 0. 摘要(先读这段) - -1. **不是"切版本 + 验 patch"能过的升级**:`v2026.7.1-2` **不是** `v2026.9.3` 的祖先,merge-base = `b81666ca6af`(2026-07-08),9.3 侧 **24,853** commits、7.1-2 侧独有 **224** `[B]`(今早 `[A]` 的数字已复算一致)。37 个 patch 里 **30 个要重做**(3777/4240 行),其中 9 个删除型 patch **全部硬失败** `[A]`。 -2. **awada 插件在 9.3 会加载即死**:`src/plugin-sdk/index.ts` 被删(裸导入 `openclaw/plugin-sdk` 从 exports 消失),`ClawdbotConfig` 全仓 0 命中(改名 `OpenClawConfig`)`[A]`。这是生产链路,优先级最高。 -3. **工具链硬门槛**:9.3 `engines.node = >=24.16.0 <25 || >=26.1.0`(**Node 22 / 25 支持被整条砍掉**),`packageManager = pnpm@12.3.4` `[B]`;我们 `build-dist.yml` 还锁 `24.15.0`(5 处)`[B]`。 -4. **用户两项核心诉求都不在 extended-stable/2026.7.33 上**:①发现本机已装 codex/claude code 当 provider(最低要 **9.1**)、②更低对话 token 消耗(要拿满得 **9.3**)`[A]`。→ ES 7.33 只能当过渡,不能当终点。 -5. **浏览器子系统在 9.3 是一次重写**:`extensions/browser` **567 files changed(+100,528 / −23,896)**,其中新增 263 / 修改 286 / 删除 18 `[B]`;工具入口 `browser-tool.ts` 从 1087 行缩到 439 行并被拆成 8 个模块 `[B]`。 -6. **浏览器协议层没有第四种**:仍是「自管 CDP / Chrome DevTools MCP / Chrome 扩展 relay」三种,且 **7.1-2 已全有** `[B][C]`。但 9.x 新增了两种**控制面**:Computer Use(`extensions/cua-computer`,0 → 39 文件)与 cloud worker 的 attached browser runtime(`src/worker/`,0 → 68 文件)`[B]`。 -7. **9.3 的浏览器新形态对我们几乎全是无关面**(extension / existing-session 要真人 Chrome,CUA 要 Mac/Windows,worker 是云场景);真正有用的只有 `navigate` 内联 snapshot、`text`/`requests`/`errors`、能力裁剪三项——**都指向省 token / 省轮次** `[B][C]`。 -8. **已落地动作**:第三方插件 pin 已 bump(`openclaw-weixin` 2.4.6→2.4.8、`wecom-openclaw-cli` 1.1.0→1.1.1,commit `d5e112c`,已推 origin/master);**基座 pin 一行未动** `[B]`。 -9. **量级估算**:4.5–7 人日(patch re-port 2–3.5 / awada 0.5–1 / 工具链 0.5 / 配置+doctor 0.5–1 / build+tsgo+冒烟 1)`[A]`。 - ---- - -## 1. 版本线拓扑 `[A]`,其中祖先关系 / commit 计数 / ES tip 已于 `[B]` 复验 - -| 线 | ref | 最新 commit | 状态 | -|---|---|---|---| -| 我们现在用的 | tag `v2026.7.1-2` = `origin/release/2026.7.1` 分支 tip | 2026-07-18(8/4 发布) | **已冻结**,不会再动 | -| 延长稳定 7.x | `origin/extended-stable/2026.7.33` | `f619d7a9fa3`(2026-09-09)`[B]` | **活跃维护中**,版本号 2026.7.33 | -| 延长稳定 6.x | `origin/extended-stable/2026.6.33`(tag 6.33/6.34) | 2026-09-08 | 活跃;npm `extended-stable` dist-tag 当前指向 **2026.6.34** | -| 主稳定线 | `release/2026.8.1 → 8.2 → 9.1 → 9.2 → 9.3` | 2026-09-07/08 | 火车式,一周 1–2 个稳定版 | - -关键事实: - -- **`v2026.7.1-2` 是 `extended-stable/2026.7.33` 的直系祖先**(`git merge-base --is-ancestor` = **YES**),`git rev-list --count v2026.7.1-2..origin/extended-stable/2026.7.33` = **38**,我们侧 0 个独有 commit `[B]`。 -- **`v2026.7.1-2` 不是 `v2026.9.3` 的祖先**(`--is-ancestor` = **NO**):merge-base = `b81666ca6af`(2026-07-08,`Fix container image upgrade migrations before gateway readiness (#101881)`);9.3 侧 **24,853** commits,7.1-2 侧独有 **224** 个(多为 CI/QA)`[B]`。 -- 我们当年靠 7.1-1 / 7.1-2 拿到的修复**已随主线进 9.3**(按 PR 号核到:#108487 codex progress、#108652 memory sidecar、#108258 WSL EROFS、#107294 npm lock、#106065 SQLite WAL、#108336 plugin metadata)→ **没有丢修复** `[A]`。 -- **7.33 当时还没打 tag、没发 npm 包**(分支上全是 `package acceptance` / `release preflight` / `frozen upgrade baselines` 类收尾 commit)`[A]`。 -- 6.33/6.34 与 7.1-2 是**分叉**关系(merge-base 6/24,它侧 332 commit),我们 37 个 patch 里 **17 个目标文件已漂移** → 功能倒退 + 还要重做 patch,**不可选** `[A]`。 -- 上游文档明确:`openclaw update --channel extended-stable` **只对 package 安装生效,git checkout 会被拒**(`docs/install/development-channels.md:43`、`docs/install/updating.md:42-43`)`[C]`。我们是源码 clone + commit pin + 自己 build,所以对我们只是"pin 哪棵树"的问题,但意味着**不能靠 CLI 自动跟,必须自己 re-pin**(巡检项)。 - -### 1.1 `package.json` 硬指标对比 `[B]` - -| 字段 | v2026.7.1-2 | v2026.9.3 | -|---|---|---| -| `version` | 2026.7.1 | 2026.9.3 | -| `engines.node` | `>=22.22.3 <23 \|\| >=24.15.0 <25 \|\| >=25.9.0` | **`>=24.16.0 <25 \|\| >=26.1.0`** | -| `packageManager` | `pnpm@11.2.2` | **`pnpm@12.3.4`** | - -> Node 22 与 25 两条线在 9.3 被整体移除;上游同时说明 Node 22/25/更早 24.x 存在 **SQLite 文本截断**问题(数据损坏级)`[A]`。 - ---- - -## 2. 中间版本功能差异(7.1-2 → 9.3)`[A][C]` - -| 版本 | 发布 | 新功能要点 | 破坏性 | -|---|---|---|---| -| **8.1**(= OpenClaw 2.0) | 8/31 | 重建 Web 体验、onboarding 简化、memory/session 连续性、历史对话搜索、跨设备/云 worker 会话、durable 进度卡片、结构化提问卡片、chat 内 widgets/dashboards、私密凭据请求(masked prompt)、automation permission、音视频保真;**浏览器与 Computer Use 大改**(见 §7) | OpenProse 移除;`codex/*` → `openai/*` 路由迁移;显式 `modelPolicy.allow`;named agent(`main` 语义变化);**Skill Workshop + 自动自学习**;plugin SDK 子路径弃用(9/1 移除门) | -| **8.2** | 9/1 | Linux 桌面 companion、Home dock、后台会话、**无 Gateway 也能浏览器控制**(扩展唤醒本地 relay)、4 套主题;可靠性「回复要把活干完」(settled tool work 之后给最终答案、accepted turn 之后暴露失败) | — | -| **9.1** | 9/3 | Mermaid 图、Android 补齐、更新恢复更安全、长对话/大安装开销降低;**quick-start lane 检测本机已有 Claude Code/Codex 登录与 API key 并 live 校验**;standalone agent 浏览器 host-first 默认 | 见 §7.6-4 | -| **9.2** | 9/5 | **回复在 Gateway 重启后能恢复**(active/queued/delegated)、升级保留 active settings/enabled skills/default-agent 归属、GPT-6 Astra、备份保数据(NUL/拒绝损坏归档)、**改设置不用重启** | — | -| **9.3** | 9/8 | 更新预演(隔离候选态)、性能(保住 warm prompt cache、冷会话与记忆搜索少做功、worker build 复用)、Skill Workshop 技能改 agent-owned collection | **Node ≥24.16/≥26.1**;exec-policy/approval/SDK 别名;Find/Grep & Ls 结果回调字段;retire `allowSymlinkTargetWrites` | - -上游 release notes 位置 `[C]`:`docs/releases/2026.8.1/browser-and-computer-use.md`(401 行,浏览器专项)、`docs/releases/2026.9.1.md:1483-1493`(Browser and Computer Use)、`docs/releases/2026.9.2.md:2759-2771`、9.3 见仓根 `CHANGELOG.md`(未单独出 `docs/releases/2026.9.3.md`)。 - ---- - -## 3. Patch 迁移实测 `[A]` - -在 `/tmp/oc93` pristine worktree 里逐个独立 `git apply`(37 个 patch = `patches/002`、`patches/007` + `patches/browser-camoufox-pivot/patches/01-35`): - -| 结果 | 数量 | 明细 | -|---|---|---| -| 干净应用 | **7** | 5 个 plain clean + 2 个靠 `--3way` 兜住(`01-mod-docs--tools--browser`、`13-mod-openclaw-tools.plugin-context.ts`) | -| 冲突需重做 | **21** | 002 / 007 / 02-07 / 09 / 11 / 14 / 15 / 20-23 / 25 / 26 / 29-31 | -| 硬失败 | **9** | **全部是 `del-*` 删除型**(删除 patch 要求目标文件字节级一致) | - -合计 **3777 / 4240 行 patch 需要重做** `[A]`。 - -结构性结论: - -- **9 个删除型全挂是机制问题,不是语义问题** → 建议把"删除"改成一份 rm 清单(如 `delete.txt`,由 `apply-addons.sh` 执行),以后对上游漂移永久免疫。 -- **最贵的是 browser 家族**:上游 `extensions/browser` 新增 263 / 修改 286 / 删除 18 文件(该三项计数于 `[B]` 复算一致,合计 567 = `git diff --shortstat` 的 files changed)。`browser-tool.ts` 被拆成 `browser-tool-dispatch/routing/lifecycle/screenshot/snapshot/session-tabs/binding/description` 等模块 → `05`(camoufox target + default + adapter 早返回,214 行)要按新布局重做,`03/02/04` 跟着改。 -- **patch 面还变大了**:5 个新文件引用 sandbox browser bridge(`browser-tool.routing.ts`、`browser-tool.lifecycle.ts`、`attached-browser-tool-runtime.ts`、`src/agents/sandbox/config.ts`、`src/commands/doctor-sandbox.ts`),都不在现有 37 个 patch 覆盖范围内 `[A]`;`[B]` 另测得 `extensions/browser/src` 内引用 sandbox 的非测试文件 **14 → 22**。 -- `002`(`OPENCLAW_DISABLE_WEB_SEARCH`)和 `007`(system-prompt 引导优先用 camoufox-cli)在 9.3 原生**仍然没有**,需求还在,只是文件漂移;都是一二十行,重做很便宜 `[A]`。 - -### 3.1 逐 patch 目标文件漂移量 `[B]` - -`git diff --numstat v2026.7.1-2 v2026.9.3 -- <目标文件>`: - -| patch | 目标文件 | 漂移 | 备注 | -|---|---|---|---| -| 01 | `docs/tools/browser.md` | +263 −81 | 章节结构基本保留(见 §7.6) | -| 02 | `extensions/browser/plugin-registration.ts` | +155 −49 | | -| 03 | `extensions/browser/src/browser-tool.schema.ts` | +263 −96 | `BROWSER_TARGETS` 仍是 `sandbox\|host\|node`;新增 capability 机制可借用 | -| 04 | `extensions/browser/src/browser-tool.test.ts` | **+3505 −362** | **最贵,基本重写** | -| 05 | `extensions/browser/src/browser-tool.ts` | +261 −909 | 1087 → 439 行,camoufox 分支要落到 dispatch/routing/description | -| 06 | `.../browser/bridge-server.auth.test.ts` | +108 −6 | | -| 07 | `.../browser/bridge-server.ts` | +87 −28 | | -| 08 | `.../browser/client-fetch.ts` | +164 −62 | | -| 09 | `.../browser/profile-capabilities.ts` | **+31 −1** | 几乎没动,但新增一批 `supports*` 字段 → 正好承载 camoufox 能力声明 | -| 11 | `src/agents/agent-tools.ts` | +614 −693 | | -| 12 | `src/agents/openclaw-tools.plugin-context.test.ts` | +93 −110 | | -| 13 | `src/agents/openclaw-tools.plugin-context.ts` | +31 −6 | `--3way` 可兜 | -| 14 | `src/agents/openclaw-tools.ts` | +364 −310 | | -| 15 | `src/agents/sandbox.ts` | +5 −23 | | -| 20 | `src/agents/sandbox/context.ts` | +149 −44 | | -| 21 | `src/agents/sandbox/docker-backend.ts` | +230 −57 | | -| 22 | `src/agents/sandbox/manage.test.ts` | +72 −6 | | -| 23 | `src/agents/sandbox/manage.ts` | +10 −11 | | -| 25 | `src/agents/sandbox/prune.test.ts` | +84 −7 | | -| 26 | `src/agents/sandbox/prune.ts` | +14 −13 | | -| 27 | `src/cli/sandbox-cli.ts` | +3 −6 | | -| 29 | `src/commands/sandbox-display.ts` | +44 −67 | | -| 30 | `src/commands/sandbox.test.ts` | +19 −6 | | -| 31 | `src/commands/sandbox.ts` | +7 −5 | | -| 34 | `src/plugins/tool-types.ts` | +22 −0 | | - -### 3.2 9 个 `del-*` 目标文件在 9.3 的状态 `[B]` - -**全部仍然存在,且都漂移了** → 这就是"9 个硬失败"的根因: - -| del patch 目标 | 漂移 | -|---|---| -| `extensions/browser/src/browser/routes/agent.snapshot.local-managed.test.ts` | +317 −15 | -| `src/agents/sandbox/browser.create.test.ts` | +523 −152 | -| `src/agents/sandbox/browser.ts` | +104 −53 | -| `src/plugin-sdk/browser-bridge.test.ts` | +115 −41 | -| `src/agents/sandbox/browser-bridges.ts` | +33 −9 | -| `src/agents/sandbox/novnc-auth.ts` | +16 −44 | -| `src/plugin-sdk/browser-bridge.ts` | +6 −6 | -| `src/agents/sandbox/browser.novnc-url.test.ts` | +0 −6 | -| `src/security/audit-sandbox-browser.test.ts` | +0 −0 | -| `src/commands/doctor.warns-per-agent-sandbox-docker-browser-prune.e2e.test.ts` | +2 −2 | - -> 结论:**9.3 没有放弃 sandbox browser / noVNC**(配置面 `agents.defaults.sandbox.browser.*`、专用 docker 网络 `openclaw-sandbox-browser`、`enableNoVnc`/`noVncEnabled` 全在,见 `src/config/types.sandbox.ts:71-94` `[B]`)。我们"删 sandbox 整条路"的 pivot 目标与上游方向相反,删除面只会越来越大。 - ---- - -## 4. awada 插件破坏面 `[A]`(生产链路,优先级最高) - -- 9.3 **删掉了 `src/plugin-sdk/index.ts`**,`openclaw/plugin-sdk` 裸导入从 `exports` 消失 → awada **8 处 import** 会 `ERR_PACKAGE_PATH_NOT_EXPORTED`,**channel 加载即死**。 -- `ClawdbotConfig` 在 9.3 全仓 **0 命中**(已改名 `OpenClawConfig`)→ awada **7 个文件**要改。 -- 其余符号都还在:`DEFAULT_ACCOUNT_ID` / `ChannelPlugin` / `PluginRuntime` / `createTopLevelChannelDmPolicy` / `jsonResult` / `readStringParam` / `ChannelMessageActionAdapter` / `BaseProbeResult` / `RuntimeEnv` / `AgentToolResult`;adapter 的 `plugin-sdk/agent-core` 导入方式与上游一致,不用改。 -- 契约文件漂移量:`channels/plugins/types.core.ts` ±228、`plugins/plugin-api.types.ts` +476、`plugin-sdk/core.ts` ±101。 -- `[B]` 补充:`src/agents/sandbox` + `src/plugin-sdk` 两目录合计 **742 files changed, +56,738 / −21,580**;plugin-sdk 侧 **删除** `browser-host-inspection.ts`、`browser-node-host.ts`,**新增**导出子路径 `./plugin-sdk/browser-cdp`。 - ---- - -## 5. 工具链与治理门槛 - -### 5.1 工具链 `[A][B]` - -- Node:9.3 要 **≥24.16.0(或 ≥26.1.0)** `[B]`;上游明确 Node 22/25/更早 24.x 有 **SQLite 文本截断**(数据损坏级)`[A]`。 -- 我们 `build-dist.yml` 锁 **`24.15.0`**,共 5 处:`44`(build env)、`152-155`(linux-x64 / mac-arm64 / mac-x64 / win-x64 portable tarball)`[B]`;`ci.yml:31` 用浮动 `'24'` `[B]`。本机 node 当时为 `v24.16.0`,刚过线零余量 `[A]`。 -- pnpm `packageManager` **11.2.2 → 12.3.4** `[B]`,CI 里 bundle 的 standalone pnpm 要跟着换(`lockfileVersion` 仍是 9.0)`[A]`。 -- Docker 基础镜像用浮动 `node:24-bookworm`,建议钉死 `[A]`。 -- **注意**:Node 抬版这条**与 openclaw 版本无关**——只要把分发的 portable Node 抬到 ≥24.16 就拿到 SQLite 截断防护,留在 7.1-2 也该做。 - -### 5.2 配置 / 治理 `[A]` - -- **Skill Workshop 默认 `autonomous.mode="auto"` + `approvalPolicy="auto"`**(自动抓经验并自动落地技能)→ 与我们"仓是唯一真源 + 专家包按需加载"直接冲突,必须在 `config-templates/openclaw.json` 显式关掉。 -- 9.3 把 Workshop 技能改成 **agent-owned collection** 并 **retire `allowSymlinkTargetWrites`** → `skill-wrappers.sh` 的软链方案要复验。 -- skills 源码从 `src/agents/skills/` 搬到 **`src/skills/`** → 仓根 `AGENTS.md` 里引的 `frontmatter.ts` / `config-eval.ts:124` 路径要更新。 -- 专家包依赖的"扫到 SKILL.md 停止下钻"在 9.3 **仍成立**(`MAX_GROUPED_SKILL_SCAN_DEPTH=6`,depth>0 命中 SKILL.md 即停),但建议实测一次。 -- `plugins.entries.phone-control` 上游已 retire(doctor 会清);OpenProse 移除(我们没用);8.1 的 `codex/*`→`openai/*` 路由迁移与 `modelPolicy.allow` 与我们模板无关。 - ---- - -## 6. 用户诉求 ↔ 版本归属(关键判定)`[A]` - -诉求(用户 2026-09-10 原话归纳):①可以直接搜索本机已安装的 codex / claude code 作为 provider,onboard 流程简化;②更低的对话 token 消耗(社区用户诟病最大)。 - -**核查做了三层**(commit 列表 / PR 号 `git log --grep` / 文件存在性 `ls-tree`),结论一致:**两个都不在 ES 7.33 上**。 - -- ES 7.33 相对 7.1-2 只有 **38 个 commit**:1 个 `chore: prepare extended-stable 2026.7.33 (#133000)` + 37 个维护(deps advisory pin、上游自己的 npm/docker 打包、plugin 依赖扫描与 scaffold 校验、codex managed runtime → 0.153.4、Telegram workspace media、openshell / opencode-go 稳定性、tlon SSE 超时、release/QA)。**没有一个是 onboarding 或 token/compaction 功能**。改动构成:967 文件里 271 个是新测试、138 个是 package.json/lock pin。 -- PR 号级核查(在 ES 分支逐个 `git log --grep`,全部 0 命中): - - 能力①:#126515 #108977 #107086 #137550 #137561 - - 能力②:#123402 #123622 #130993 #131977 #133094 #123737 #127110 #133912 #134259 #134987 #127506 #140449 #140730 #140799 #140840 #141141 #140566 #140651 #140698 #140713 #140744 #140797 -- 文件级证据: - - 能力①落地文件只在 9.x 出现:`src/wizard/setup.inference-verification.ts`、`setup.memory-import.ts`、`setup.app-recommendations.ts`、`plugin-capability-consent.ts`、`setup.default-agent.ts`,外加整套新增的 `src/commands/onboard-non-interactive/`。7.1-2 与 ES 7.33 **都没有这些文件**;两版 `src/wizard/setup.ts` 里 `claude` / `codex` 关键字命中 **0 次**。7.1 只有 `agent-auth-discovery*.ts` 那一层(给 `openclaw attach` / catalog 会话用,不是"装完直接识别本机 Codex/Claude 登录开聊")。 - - 能力②:`src/context-engine/` 文件数 7.1-2 = **13**、ES 7.33 = **13**、9.3 = **18**(新增 `compaction-watchdog.ts`、`context-engine-abort.ts`、registry runtime adoption、host-param projection)。ES 7.33 里唯一沾 "token" 字样的改动是 `src/auto-reply/tokens.ts`——是**模型控制 token(sentinel)剥离正则**的文本保真修复,与对话 token 消耗无关;`post-compaction-context.ts` 那处是 UTF-16 安全截断。 - -### 6.1 两项能力的版本归属 `[A]` - -| 能力 | 打底 | 真正兑现 | -|---|---|---| -| ①发现本机 Codex/Claude Code + onboarding 简化 | 8.1(Import from another agent #126515、记忆导入检测 #108977、native catalog terminals #107086) | **9.1**:quick-start lane 检测已有 Claude Code/Codex 登录与 API key 并 live 校验;Model Setup 区分 account vs API-key 并显示运行时上报邮箱;catalog "+" 直接开原生 CLI;claude-cli 走 PATH shim / Windows PATHEXT。9.2 再补 account discovery | -| ②更低对话 token 消耗 | 8.1(Anthropic 服务端 compaction #123402、xAI #123622、compaction 三修 #130993/#131977/#133094)→ 8.2(停止 byte-triggered 重复 compaction #123737/#127110/#133912/#134259)→ 9.2(被丢弃 tool result 的 token 计入 #134987) | **9.3 是大头**:prompt cache continuity 一整串(#140566/#140651/#140698/#140713/#140744/#140797/#140799)、compaction 按完整 pending request 定尺寸(#127506)、长对话与 retained memory(#136293/#139074…) | - -→ **能力① 最低要 9.1,能力② 要拿满得 9.3。** - -> **2026-09-11 复核更正**:上表 ② 的原始清单里混进了两个**与对话 token 无关**的 PR,已剔除 —— `#140449` 实为 `improve: retain current worker builds between sessions`(云 worker 构建复用,我们不用 worker),`#140730` 实为 `fix(memory): avoid repeated vector search startup delays`(记忆向量检索**启动延迟**,且我们模板 `agents.defaults.memorySearch.provider = none` 本来就没开)`[B]`。剔除后,② 对我们**真正可能有效**的只剩两类:compaction 决策类、provider prompt-cache 类;后者的有效性还取决于百炼端点是否实现缓存语义(见 §6.5-4)。 - -### 6.2 两个"升了也不会自动兑现"的前置判断 `[A]` - -1. **能力① 是 onboarding 期能力**,而社区用户走的是我们的 `install.sh` + 预置 `config-templates/openclaw.json`(provider 写死 `bailian-token-plan`),**根本不进上游 wizard**。要兑现只有两条路:让 `install.sh` 把首启 onboarding 交回上游(我们只注入 skills/plugins/awada),或自己做一个"检测本机 codex/claude 登录"的脚本/skill。**这是产品决策,升级本身带不来。** -2. **能力② 的收益要拆三类**: - - prompt cache continuity → 只有 provider 有缓存语义才省钱(Anthropic 直连 / Gemini / Bedrock / Responses 覆盖)。我们主力是百炼网关:`api: "anthropic-messages"` 路由大概率吃得到,纯 openai-compat 自定义网关未必 → **迁移前应在 9.3 上用百炼路由实测一次 usage 里的 cached token 字段**,不要假设。 - - compaction 类修复(少重复塞历史)→ **对无缓存的 GLM/DeepSeek 也直接生效**,这是最普适的真降 token,也最可能对上社区诟病。 - - 9.2/9.3 的长对话优化 → 降 CPU/延迟,**不降 token**,别算进收益。 - -### 6.3 不升级也能立刻做的一档 `[A]` - -`skills.limits.maxSkillsInPrompt` / `maxSkillsPromptChars` / `maxSkillsLoadedPerSource` / `maxSkillFileBytes` 在 **7.1-2 就有**(`src/config/types.skills.ts:50-56`,字段与 9.3 完全一致),但我们模板里 `skills` 只写了 `entries`、**没设 limits**(`[B]` 复核 `config-templates/openclaw.json` 确认:`skills` 只有 `entries`,`agents.defaults` 只有 model / imageModel / memorySearch / models / compaction / thinkingDefault / maxConcurrent / subagents)。`agents.defaults.contextInjection` 同理可用。→ 建议先量一版"单轮 token 构成"(system prompt / 技能块 / bootstrap 文件 / 历史),再定升级优先级。 - -### 6.5 「升 ES 7.33 + 单独 cherry-pick 9.3 省 token 的 commit」可行性实测 `[B]` - -> 2026-09-11 针对该提议做的专项实测。结论:**升 ES 7.33 可行且便宜;cherry-pick 不可行**。 - -**(1) 省 token 不是一个 commit,是一条压在结构重写上的链** - -| 口径(`origin/extended-stable/2026.7.33..v2026.9.3`) | 实测值 | -|---|---| -| 动过 `src/context-engine` 的 commit 数 | **39** | -| `src/context-engine` 目录 diff | **15 files, +2,620 / −1,773** | -| 该目录文件数 7.1-2 / ES 7.33 / 9.3 | 13 / 13 / **18** | -| 全仓 commit 主题含 `compaction` 的数量 | **332** | -| 同期 `src/agents` 全目录 commit 数 | 3,997 | - -`src/context-engine` 那 39 个 commit 里包含多个**结构性重写**,省 token 的修复就长在它们之上:`refactor: flip sessions and transcripts to sqlite storage (#98236)`、`refactor(sessions): remove file-era transcript runtime (#113233)`、`refactor(agents): consolidate compaction and context-engine ownership (#117482)`、`refactor(agents): consolidate context budgets and compaction recovery (#117149)`、`refactor: replace context-engine retry proxy with declared params (#115872)`。 - -**(2) cherry-pick 实测:5 个代表性 commit 全部失败** - -在 `/tmp/es733b`(ES 7.33 detached worktree,tip `f619d7a9fa3`)上逐个 `git cherry-pick -n`: - -| commit | 主题 | rc | 冲突文件 / 涉及文件 | -|---|---|---|---| -| `28f5f63ea42` | fix(agents): preserve prompt-cache prefix under aggregate truncation (#132017) | 1 | 4 / 6 | -| `110a636dd16` | fix(agents): preserve Responses cache prefixes across user turns (#140849) | 1 | 7 / 10 | -| `8e3f572ffa2` | fix(openai): honor native prompt cache settings (#140853) | 1 | 19 / 29 | -| `f0cc57d6b46` | fix(agents): preserve cached history when background work changes (#140799) | 1 | 32 / 52 | -| `60adac1aff6` | fix(agents): fit compacted context and prioritize foreground replies (#139822) | 1 | **91 / 129** | - -合计 **153 个冲突文件次**。这不是 cherry-pick,是 re-port;而且 re-port 的是**对话装配路径**——它出 bug 的表现是"回复停在工具输出 / 历史被静默截断 / 人格漂移",不是崩溃,很难被测出来(正是我们历史上 AtomCode 卡 busy、awada 回复丢失那一类故障的邻区)。 - -**(3) 更反直觉的发现:9.3 把 token 调优旋钮收走了** - -`src/config/types.agent-defaults.ts` 中,7.1-2 有而 **9.3 已删除**的字段(逐个 grep 计数 7.1-2=有 / 9.3=0): - -| 字段 | 作用 | 7.1-2 | 9.3 | -|---|---|---|---| -| `compaction.maxHistoryShare` | 历史占上下文窗口比例上限(0.1–0.9,默认 0.5) | ✅ | **删除** | -| `compaction.reserveTokens` / `reserveTokensFloor` | 压缩预留 token 与下限 | ✅ | **删除** | -| `compaction.customInstructions` | 压缩摘要附加指令(保语言/人格连续性) | ✅ | **删除** | -| `contextPruning.keepLastAssistants` | 保护最近 N 个 assistant 轮不被裁剪 | ✅ | **删除** | -| `contextPruning.softTrimRatio` / `hardClearRatio` | 软裁剪 / 硬清除的上下文压力阈值 | ✅ | **删除** | -| `contextPruning.minPrunableToolChars` | 工具结果达到多少字符才值得裁 | ✅ | **删除** | - -9.3 侧只新增 `compaction.enabled`、`compaction.thinkingLevel`,并把 `contextPruning` 简化成 `mode` / `ttl` / `tools` / `hardClear`。`AgentCompactionMode` 两版都是 `"default" | "safeguard"`(未变)。 - -→ **含义**:9.3 的 token 收益来自内部行为(缓存连续性、压缩决策),但**运维侧可调粒度变粗**。对我们这种"要给社区分发、模型上下文各异(GLM 输入上限 196,608)、需要按部署调参"的场景,丢掉 `maxHistoryShare` / `reserveTokens` 可能是**净退化**,迁移前必须评估。 - -**(4) provider 匹配性:cache 类修复对我们是否有效,尚未验证** - -我们模板的 provider 是 `bailian-token-plan`,`api = "anthropic-messages"`,`baseUrl = https://token-plan.cn-beijing.maas.aliyuncs.com/apps/anthropic` `[B]`。9.3 那批 prompt-cache 修复分别是 Anthropic CLI(#140566)、OpenAI native(#140853)、Bedrock(#140797)、Responses(#140849)路径。**百炼的 anthropic 兼容端点是否实现 `cache_control` 并在 usage 里回 `cache_read_input_tokens`,必须先实测**;若不支持,合并这批 commit 的收益为 0。 - -**(5) 不升级就能拿的三档(7.1-2 已支持,我们模板未开)`[B]`** - -| 开关 | 上游注释原文 | 我们模板现状 | -|---|---|---| -| `agents.defaults.contextPruning` | "Opt-in: prune old tool results from the LLM context to reduce token usage" | **完全未设置** | -| `skills.limits.*`(maxSkillsInPrompt / maxSkillsPromptChars / maxSkillsLoadedPerSource / maxSkillFileBytes) | 见 §6.3 | **未设置**(`skills` 只有 `entries`) | -| `compaction.maxHistoryShare` / `keepRecentTokens` / `recentTurnsPreserve` / `model` | 历史占比、保留近期 token、逐字保留轮数、**用更便宜的模型做压缩摘要** | 只设了 `compaction.mode = "safeguard"` | - -→ 建议顺序:**先量"单轮 token 构成"→ 开这三档 → 再决定是否为了 compaction 那部分整体迁 9.3**(而不是 cherry-pick)。 - -### 6.4 ES 7.33 作为过渡的实测支撑 `[A]` - -- 37 个 patch 的目标文件 **零漂移**(`git diff` 全空)→ patch 全绿,**不需要 re-port** -- awada 依赖的契约文件 **零改动**(`plugin-sdk/index.ts`、`channels/plugins/types.core.ts`、`plugins/plugin-api.types.ts`、`plugin-sdk/core.ts`、`agent-runtime.ts` 全部 0 diff)→ **awada 不用改一行** -- `engines` 仍是 node `>=22.22.3 <23 || >=24.15.0 <25 || >=25.9.0`、`packageManager` 仍 pnpm 11.2.2 → **工具链不用抬** -- 白拿:安全 advisory pin、docker/plugin 安装修复、codex runtime 0.153.4、Responses stream 修复 -- 净收益判断:对我们只剩「安全 advisory pin + 2 个 plugin 扫描修复」(其余 docker/codex/Telegram/openshell 都与我们无关——我们有自己的 Dockerfile、模板里 `plugins.entries.codex.enabled=false`、不用 Telegram) -- **未 backport 到 ES 7.33 的可靠性修复**:#133520 / #133979 / #138071 / #138519("回复停在工具输出 / 重启后丢回复"那一类)→ 这是 9.x 迁移的触发条件之一 - ---- - -## 7. 浏览器子系统专项对比(7.1-2 → 9.3)`[B]`(除注明外全部本次实测) - -### 7.1 体量 - -| 口径 | 7.1-2 | 9.3 | -|---|---|---| -| `extensions/browser` 全量 diff | — | **567 files, +100,528 / −23,896**(A 263 / M 286 / D 18 / R 0) | -| `extensions/browser/src` 文件数 | 338 | **526** | -| └ `src/browser/` | 251 | **415** | -| └ `src/browser/extension-relay/` | 11 | **48** | -| └ `src/browser/screencast/` | 0 | **9**(+ `routes/agent.screencast.ts`) | -| └ `src/browser/routes/` | 41 | 49 | -| └ `chrome-extension/` | 11 | **61** | -| 全仓 browser/camoufox/patchright/novnc 相关文件 | 457 | **976** | -| 工具入口 `browser-tool.ts` 行数 | **1087** | **439**(+261 −909) | -| `extensions/browser/src` 内引用 sandbox 的非测试文件 | 14 | **22** | -| `extensions/cua-computer` 文件数 | **0** | **39** | -| `src/worker` 文件数 | **0** | **68** | - -### 7.2 工具层:从"一个大文件"到"注册壳 + 8 个模块" - -9.3 的 `browser-tool.ts` 只负责注册与编排,逻辑拆到: - -| 新模块 | 职责 | -|---|---| -| `browser-tool-dispatch.ts` | action 派发(`executeBrowserTabAction`) | -| `browser-tool.routing.ts` | host / sandbox / node 目标解析、baseUrl、timeout(`resolveBrowserBaseUrl`、`resolveBrowserToolNodeTarget`) | -| `browser-tool.lifecycle.ts` | `doctor` / `status` / `start` / `stop` / `profiles` / **`importprofile`**(`:79,158-164`) | -| `browser-tool.screenshot.ts` / `.snapshot.ts` | 截图 / 快照管线 | -| `browser-tool-session-tabs.ts` | 会话 tab 注册表 | -| `browser-tool-binding.ts` | run/tab 绑定(`BROWSER_TAB_BOUND_ACTIONS`,`:49`) | -| `browser-tool-description.ts` | **按能力动态生成工具描述**(`:19-21`) | -| `attached-browser-tool-runtime.ts` | attach 到已有 CDP 端点的独立运行时(7.1-2 无此文件) | -| `browser-node-proxy/-routing/-commands/-fallback`、`browser-proxy-envelope/-upload` | node 代理链路重写 | -| `browser-runtime-state.ts`、`system-profile-api.ts` | 运行时状态 / 系统 profile 枚举 | - -### 7.3 模型可见的能力变化 - -- **actions 19 → 24**(`browser-tool.schema.ts:32` vs 9.3 同文件):新增 `importprofile`、`requests`、`errors`、`text`、`emulate` `[C: docs/tools/browser.md:1004,1010-1013]` - - `text`:抽正文(首个 `selector` 命中,否则 `article`/`main`/`body`),`maxChars` 默认且上限 40,000 - - `requests` / `errors`:读网络日志 / 页面错误,支持 `filter`、`limit`(默认 50)、`clear`;页面内容与错误按**不可信外部内容**处理 - - `emulate`:`device`(Playwright 设备名)、`colorScheme`、`timezoneId`、`locale`,按此顺序应用、返回 `applied`,**非原子** - - `importprofile`:macOS 从 Chrome/Brave/Edge/Chromium 系统 profile 导 cookie(Keychain/Touch ID 授权),仅 cookie(不含 localStorage/IndexedDB),DBSC 设备绑定会话仍可能要重登;必须跑在 browser host 上(`browser-tool.lifecycle.ts:158-164` 会抛 "system profile import must run on the browser host");配套 CLI `openclaw browser import-profile` / `system-profiles` / `cookie-sync`(推送到远端 Gateway 的 managed profile)`[C: docs/cli/browser.md:105-133]` -- **`navigate` 现在内联返回新页面 snapshot**(efficient interactive tier,payload 有界),批量 `act` 跨文档导航会暂停并带 `pageState` → **少一轮调用,直接省 token** `[C: docs/tools/browser.md:1014-1018; PR #114814]` -- **能力声明化**:`resolveBrowserToolCapabilities({tabBound, evaluateEnabled, profileCapabilities})` 按 profile 的 `supports*` 裁 actions,`browser-tool-description.ts` 同步改描述 → **模型看不到就不会乱调**(9.3 新机制,7.1-2 无) -- `BROWSER_TARGETS` **未变**,仍是 `["sandbox","host","node"]`(7.1-2 `schema.ts:54` / 9.3 `:63`);profile driver 枚举仍封闭:`"openclaw" | "clawd" | "existing-session" | "extension"`(`src/config/types.browser.ts:20`)→ **没有自定义 driver 扩展点** - -### 7.4 三条 7.1-2 完全没有的新链路 - -1. **Screencast 实时看屏**:`POST /screencast` 铸单次令牌(48 位 hex、短时效)→ WS `/browser/screencast?token=`;每 profile+tab 共享一路 CDP screencast(Chrome 推 JPEG);不支持时回落截图并返回 `SCREENCAST_UNSUPPORTED`(`reason: existing-session | playwright | node`)`[C: docs/tools/browser-control.md:26,88-140]`。Control UI 的 Browser 面板可实时看 agent 页面、转发点击/滚轮/键盘、viewport 跟随面板尺寸 `[C: docs/web/control-ui/panels.md:83]`。7.1-2 全仓 `screencast` **0 命中**。 -2. **Chrome 扩展 relay 重做**:standalone relay daemon + native-host 唤醒(**#128379**,即 8.2 的"没有 Gateway 也能控浏览器");`owner-*` 协议族(`owner-server/client/playwright/protocol/auth-client`)、**auth-v2**(`auth-v2-crypto.ts`、`auth-v2-websocket.ts`)、`preauth-websocket-guard.ts`(准入分区 **#134241**)、`relay-access/fetch/request/runtime/session-owner/target`;扩展侧支持每 tab 独立 copilot(#109817)、把页面/文档/选中文字一次性交给主会话(#111158)、Puppeteer 客户端接扩展 CDP(#117915)、扩展版本漂移告警(#119641);`chrome-extension/` 从 11 → 61 文件(大量 relay/navigation/creation-lifecycle/native-cleanup/bootstrap-diagnostics 测试)。 -3. **Computer Use + cloud worker 浏览器**:`extensions/cua-computer`(0 → 39 文件,含 `src/browser-actions.ts`)走**操作系统层**(截图 + 像素/辅助功能)控制配对 Mac(与显式开启的 Windows)上的 app/window,Linux 仍 experimental;`src/worker/browser-runtime.ts` 自述为 "Core-private adapter for the bundled Browser plugin's **attached** worker runtime",提供 `createAttachedBrowserToolRuntime({cdpUrl, ensureAttachTarget, agentSessionKey, workspaceDir})` + 30s 启动超时 → worker/独立进程**不借 Gateway 凭据**即可 attach CDP 并挂出 browser 工具。 - -### 7.5 四个 driver 到底是什么 - -**先纠正**:四个名字里只有**三种真实驱动**,`clawd` 是 `openclaw` 的历史别名(clawdbot 时代遗留)。证据: - -- `src/config/schema.help.runtime.ts:37`:`Per-profile browser driver mode. Use "openclaw" (or legacy "clawd") for CDP-based profiles, "existing-session" for Chrome DevTools MCP attachment, or "extension" for the authenticated Chrome extension relay.` -- `extensions/browser/src/browser/config.ts:507-509`:只有 `existing-session` / `extension` 保留原值,**其它一律归一为 `"openclaw"`** -- `extensions/browser/src/doctor-browser.ts:24`:`LEGACY_CLAWD_BROWSER_PROFILE_NAME = "clawd"`,`doctor --fix` 会归档 legacy clawd profile 残留(`:402-430`) -- `extensions/browser/src/browser/routes/basic.ts:463-467` 仍接受 `clawd`,错误文案三个并列 -- `docs/tools/browser.md:347`:远端端点场景下 schema **拒绝** `openclaw` / `clawd` profile - -| driver | 是什么 | 谁启动浏览器 | 传输 | 能力 | -|---|---|---|---|---| -| **`openclaw`(≡ `clawd`)** | OpenClaw 自管的隔离浏览器(内置 profile 名 `openclaw`),独立 user-data-dir | OpenClaw(或 attach 已有的) | loopback / remote **CDP** + Playwright | **最全**:per-tab WS、`/json` 端点、reset、managed tab limit、batch、pdf、download、requests/errors/text/emulate 全支持 | -| **`existing-session`** | attach 用户**真实登录的 Chrome**(内置 profile 名 `user`) | 用户的 Chrome(OpenClaw 只 attach) | **Chrome DevTools MCP** 子进程,默认 `npx -y --audit=false chrome-devtools-mcp@1.8.0 --autoConnect` | **被砍**:无 batch/pdf/download/responsebody/requests/errors/text/emulate;act 只能用 snapshot ref(CSS selector 不行,`click-coords` 例外);click 仅左键;`type` 无 `slowly`;`wait --load networkidle` 不支持;snapshot 强制 `ai` 格式;首次 attach 有阻塞式 "Allow remote debugging?" 弹窗,**必须有人在电脑前** | -| **`extension`** | 经 OpenClaw Chrome 扩展 relay 驱动用户已登录的浏览器(内置 profile 名 `chrome`) | 用户的 Chrome | 扩展 relay(loopback 端口)+ Playwright 连 relay 端点 | 中等:`usesPersistentPlaywright=true`、`requiresCompleteTargetEnumeration=true`、`headless` 强制 false、`attachOnly=true`;**无远程调试弹窗 → 人不在电脑前也能用**(手机 Telegram/WhatsApp 场景官方推荐) | - -`extension` 的端口与凭据机制(`config.ts:516-545`):每个 extension profile 分配独立 loopback relay 端口(显式 `cdpPort` 优先,否则 `resolveExtensionRelayPorts` 自动分配,避免多 profile 抢同一端口静默失败);内部客户端用**进程级一次性 token** 拼进 URL(`http://:@127.0.0.1:`),持久 relay key 只用于 HMAC 证明、**绝不进 URL 或 header**。 - -`existing-session` 的可定制面(`docs/tools/browser.md:862-909`):`mcpCommand`(**任意可执行文件**,绝对路径原样解析)、`mcpArgs`(原样透传;`mcpCommand=npx` 时上游仍会前置 `-y --audit=false chrome-devtools-mcp@1.8.0`)、`userDataDir`、`cdpUrl`(`http(s)://` → `--browserUrl`,`ws(s)://` → `--wsEndpoint`;`mcpArgs` 里显式端点覆盖 `cdpUrl`;**一旦选了端点,`userDataDir` 被忽略**)。target/ref 作用域绑单个 MCP 子进程,进程重启后要重跑 `browser tabs`、重选 target、重拍 snapshot。 - -### 7.6 控制形态盘点:协议层还是三种,控制面多了两种 - -**协议/驱动层:没有第四种。** 仍是「自管 CDP / Chrome DevTools MCP / Chrome 扩展 relay」,CDP 是其中两条的共同底座(扩展 relay 本质是把 relay 端点当 CDP 端点交给 Playwright 连)。而且这三种 **7.1-2 就已全有**:两版 `docs/tools/browser.md` 大纲逐条比对,`Local vs remote control`、`Node browser proxy (zero-config default)`、`Browserless (hosted remote CDP)`、`Direct WebSocket CDP providers`(Browserbase / Notte)、`Existing session via Chrome DevTools MCP`、`Use Brave or another Chromium-based browser`、`Browser selection` 这些章节 **7.1-2 已存在**;9.3 只多了 `Browser panel in the Control UI`(:155)与 `Tab cleanup ownership`(:245)两节。 - -同一个 `openclaw` driver 内部的三个子形态(`docs/tools/browser.md:487-520`、`profile-capabilities.ts`): - -| 子形态 | capability mode | 判定 | -|---|---|---| -| 本地启动(自管) | `local-managed` | loopback + 非 attachOnly(`isLocalManagedProfile` = `driver==="openclaw" && cdpIsLoopback && !attachOnly`,`config.ts:227`) | -| loopback 外部 CDP + `attachOnly:true` | `local-managed`,但 `browserFilesystemLocal=false` | 例:Browserless in Docker 发布到 127.0.0.1。**不配 `attachOnly` 会被误当自管 profile** | -| 非 loopback `cdpUrl` | `remote-cdp`(`isRemote=true`、`usesPersistentPlaywright=true`) | 托管服务 Browserless / Browserbase / Notte;URL 可带 `?token=` 或 HTTP Basic,OpenClaw 在 `/json/*` 与 CDP WS 上保留凭据 | - -**9.x 新增的控制面(不是新协议)**: - -1. **Computer Use(`extensions/cua-computer`)** = 真正意义上的第四种形态:**不走浏览器协议,走操作系统层**(截图 + 像素/辅助功能)控制配对 Mac / 显式开启的 Windows 上的 app/window,其中包含浏览器窗口;Linux experimental。9.2 修了"CUA 窗口图里的点击/拖拽要按 OpenClaw 做的缩放换算"、"截图型 CUA 会话可暂停后看新图再继续"(`docs/releases/2026.9.2.md:2769`)。 -2. **cloud worker attached browser runtime**(`src/worker/` 0 → 68 文件)。 -3. **node browser proxy 重写**(形态旧、链路新):节点靠 `caps:["browser"]` 或 `browser.proxy` 命令声明(`browser-node-commands.ts`、`browser-node-routing.ts`);策略 `gateway.nodes.browser.mode = off|auto|manual`;新增 `browser.proxy.upload.v1`(远端上传需节点批准,未批准会给明确提示);代理**永不**放行 `create-profile`/`delete-profile`/`reset-profile`;`nodeHost.browserProxy.allowProfiles` 是最小权限边界;动作一旦落到节点,后续 snapshot/设置就钉在该节点不再切浏览器。 -4. **9.1 默认值变化(破坏性)**:standalone(`openclaw agent exec`)在没选 Gateway/node 路由时**默认用宿主机浏览器**、不需要 Gateway 凭据;依赖隐式 node 发现的要显式 `gateway.nodes.browser.mode="auto"`(`docs/releases/2026.9.1.md:1489-1493` 明说这是有意变更)。 -5. **人成为共同驾驶者**:Control UI Browser 面板 + screencast(见 §7.4-1);macOS 可把外链开成原生 WebKit tab(9.3 CHANGELOG:13,42,#140988 #141031)。 -6. **sandbox Docker 浏览器依然健在**(见 §3.2 注)。 - -自管浏览器的二进制选择顺序(`docs/tools/browser.md:920-940`):**Chrome → Brave → Edge → Chromium → Chrome Canary**,可用 `browser.executablePath` 覆盖;Linux 会扫 `/usr/bin`、`/snap/bin`、`/opt/google`、`/opt/brave.com`、`/usr/lib/chromium`、`/usr/lib/chromium-browser`,以及 `PLAYWRIGHT_BROWSERS_PATH` / `~/.cache/ms-playwright` 下 Playwright 托管的 Chromium。→ **"内置 Chromium" 的准确表述是"自管的 Chromium 家族浏览器"**,且**只支持 Chromium 家族**。 - -### 7.7 配置面破坏性变化 `[B]` - -`src/config/types.browser.ts`:**14 insertions / 36 deletions**(9.3 全文仅 77 行)。 - -| 变化 | 内容 | -|---|---| -| **删除** | `ssrfPolicy.hostnameAllowlist`;`tabCleanup.idleMinutes` / `maxTabsPerSession` / `sweepMinutes`(只剩 `enabled`);`actionTimeoutMs`、`localLaunchTimeoutMs`、`localCdpReadyTimeoutMs`、`remoteCdpTimeoutMs`、`remoteCdpHandshakeTimeoutMs`、`cdpPortRangeStart` | -| **新增** | `browser.allowSystemProfileImport`(默认 true,macOS)、`browser.extensionRelay.allowLegacyAuth`(默认 true)、`ssrfPolicy.blockedHostnames`(支持 `*.example.com`,**覆盖 allow**)、`ssrfPolicy.allowRfc2544BenchmarkRange`、`ssrfPolicy.allowIpv6UniqueLocalRange` | -| **改语义** | `ssrfPolicy` 变成共享类型别名 `BrowserSsrFPolicyConfig = SsrFPolicyConfig`(新文件 `src/config/types.ssrf.ts`,7.1-2 无此文件);`color` 标 `@deprecated`(doctor-only,canonical schema 会拒);extension driver 的 relay 端口改为每 profile 自动分配 | -| **plugin-sdk** | 新增导出子路径 `./plugin-sdk/browser-cdp`;**删除** `browser-host-inspection.ts`、`browser-node-host.ts` | - -**对我们模板的影响(已核)**: - -- ✅ `config-templates/openclaw.json` 与 `openclaw-awk.json` 里的 `browser` 块只有 `enabled` / `headless` / `ssrfPolicy.dangerouslyAllowPrivateNetwork: true` 三项 —— **9.3 全部仍然有效**。`navigation-guard.ts` 两版都是"只有显式 `=== false` 才拦私网"(7.1-2 `:66`、9.3 `:85`),语义未变。 -- ⚠️ 若将来有人用 `browser.ssrfPolicy.hostnameAllowlist`,**9.3 会失效**(改用 `allowedHostnames` + `blockedHostnames`)。 -- ⚠️ `tabCleanup` 与各类超时旋钮失去可配性 → 若线上曾靠调 `actionTimeoutMs` / `maxTabsPerSession` 解决问题,9.3 上无对应开关。 - -### 7.8 对 camoufox pivot 的判断 - -1. **camoufox 不能靠配置接进 9.3**:attach 路线是 CDP + Playwright(`cdpUrl` / `attachOnly` / `executablePath` 都只指 Chromium 家族),camoufox 是 Firefox 系**无 CDP**;driver 枚举封闭、无自定义 driver 扩展点 → pivot 的"新 target/driver + adapter"在 9.3 仍然是 patch 工程。 -2. **但 9.3 让 pivot 更划算**:capability gating + description 动态生成,意味着现在 adapter 里"不支持的 action 返回错误引导 `target=host`"(`patches/browser-camoufox-pivot/README.md` 认的 R4 残留:console / dialog / `act:drag|clickCoords` / resize)可以改成 `supports*` 声明——模型根本看不到这些 action,既减少无效调用又省 token;插入点也从"改 1087 行大文件"变成"改 dispatch / routing / description 三个小模块"。 -3. **9.3 浏览器新价值我们大部分吃不到**:screencast / Control UI 面板 / 扩展 relay / macOS cookie 导入 / Computer Use / cloud worker 都对着"有人在看、有 GUI、有 Mac、在云上"的场景;我们是 headless Linux 容器 + 反检测。真正有用的只有 §7.3 那三项(`navigate` 内联 snapshot、`text`/`requests`/`errors`、能力裁剪)——**全部指向省 token / 省轮次**。 -4. **一条值得评估的零 patch 备选路线**:`existing-session` 的 `mcpCommand` 是"任意可执行文件",只要求保持 Chrome MCP 的连接参数语义。理论上可写一个 **camoufox 后端的 chrome-devtools-mcp 兼容 server**,配 `driver:"existing-session"` + `mcpCommand: <我们的 bridge>`,**零 patch** 让 openclaw 驱动 camoufox。代价明确:拿不到 batch/pdf/download/requests/errors/text/emulate,act 只能用 snapshot ref,snapshot 强制 `ai` 格式,target/ref 绑子进程生命周期,且 `cdp-reachability-policy.ts:92-99` 在受管 CDP 策略下会拒带显式 `cdpUrl` 的 existing-session profile。**未验证可行性,仅列为待评估选项。** - -> 单看浏览器维度:**收益 = 省 token / 省轮次;成本 = browser 家族 30 个 patch 里的重头(04 / 05 / 03 / 11 / 14 + 9 个删除型)**。只为浏览器不值;要值就得与 §6 的能力①②一起算总账。 - ---- - -## 8. 第三方 pin 与本轮已完成动作 - -### 8.1 pin 现状 `[A]`(版本号文件于 `[B]` 复核) - -| 包 | 原 | 现(`openclaw-weixin.version.json`) | 备注 | -|---|---|---|---| -| `@tencent-weixin/openclaw-weixin` | 2.4.6 | **2.4.8** | peer `openclaw>=2026.5.12`,与 7.1-2 兼容 | -| `@wecom/wecom-openclaw-cli` | 1.1.0 | **1.1.1** | 安装流程更稳健(`plugins install` 透传 `--force`、多源 failover 不再删插件目录等) | -| `@tencent-weixin/openclaw-weixin-cli` | 2.1.4 | 2.1.4(不变) | 仍是 npm latest | -| camoufox-cli(上游) | 0.7.3 | 0.7.3 | 上游最新 = 我们 fork baseline,本轮不用跟 | - -commit **`d5e112c`**(`chore(deps): bump openclaw-weixin 2.4.6 -> 2.4.8, wecom-openclaw-cli 1.1.0 -> 1.1.1`),改 5 个文件 + `CHANGELOG.md` v5.7.1 条目;`[B]` 复核已在 `origin/master` 上(当次会话内 push 两次 rc=124 失败,后续成功)。同步把 `scripts/install.sh` / `scripts/install-atomgit.sh` / `docker/docker-bootstrap.sh` 三处兜底版本 2.4.6 → 2.4.8。 - -### 8.2 为什么 2.4.8 在 7.1-2 上安全 `[A]` - -2.4.8 的**唯一代码改动**是把 `createTypingCallbacks` 的 import 从 `openclaw/plugin-sdk/channel-runtime` 换到 `openclaw/plugin-sdk/channel-message`——那是为适配 **8.1 删掉旧子路径**。对 7.1-2 已 build 的 `dist/` 做过运行时校验:11 个 `openclaw/plugin-sdk/*` 子路径全部存在于 7.1-2 的 `package.json` exports;18 个具名导入中 12 个 value import 全命中(含关键的 `channel-message :: createTypingCallbacks`),6 个 type-only import 在 `.d.ts` 中也都在。**不验这一步,微信通道有可能加载即挂。** - -### 8.3 已知坑:改了 pin,已装实例不会自动升 `[A]` → **install 路径已于 2026-09-12 修复 `[B]`** - -- ~~`scripts/install.sh`:`plugins list` 里已有 openclaw-weixin 就**直接 return,不比版本**~~ → **已修**:`install.sh` / `install-atomgit.sh` / `docker/docker-bootstrap.sh` 三处同源的 `install_weixin_plugin()` 改为**按版本判定**——新增 `weixin_installed_version()`(主路径 `plugins list --json` 取 `version`,回落到 `$OPENCLAW_HOME/npm/projects/*/node_modules//package.json`),已装 == pin 才跳过,不等则 `plugins install @ --pin --force`;版本读不到但插件在时也走 `--force`(正确性优先)。已用本机实例(已装 2.4.6 / pin 2.4.8)实测三条路径:主路径 ✓、回落路径 ✓、未安装返回非零 ✓,决策分支 `UPGRADE --force` / `SKIP` 均正确。 -- `scripts/update.sh` bundled tarball 路径 → **已修**(2026-09-12):`plugins install "$plugin_tgz"` 补上 `--force`。该路径本来就每次都装(无幂等跳过),缺 flag 时在已装实例上会失败并触发下面的 `exit 1`,把整次 update 打断。(注:仓内无 `vendor/openclaw-plugins/`,该路径只在带 vendor 的发行 tarball 里触发。) -- `scripts/update.sh` 在线路径 → **仍未修**(按用户决定暂不动)。它走 `npx openclaw-weixin-cli@2.1.4 install`,该 CLI 读到 `plugins.installs[].spec` 是固定版本号时(我们正是用 `--pin` 装的)会打印「本地已安装插件为固定版本 2.4.6,跳过升级」直接 return。→ 走 `update.sh` 在线路径的实例仍需下面这条手动命令。 - -→ `update.sh` 路线 / 修复前已装的实例,仍需显式跑一次: - -```bash -cd openclaw && npm_config_registry=https://registry.npmmirror.com \ - pnpm openclaw plugins install @tencent-weixin/openclaw-weixin@2.4.8 --pin --force -``` - -wecom 那条不受影响:`install-wecom-channel.sh` 按 pin 文件 `npm pack` + sha512 校验后装,会拿到 1.1.1(需 `WISEFLOW_CONFIRM_WECOM_INSTALL=confirmed`)。 - ---- - -## 9. 当前仓库状态(2026-09-11 复验)`[B]` - -| 项 | 状态 | -|---|---| -| 基座 pin | `openclaw.version` = `OPENCLAW_VERSION=2026.7.1-2` / `OPENCLAW_COMMIT=0790d9f593ad30c940ed93b5872a8cf6d6f3cf8c`,**未动** | -| `openclaw/` 工作树 | `git log -1` = `0790d9f593a`,`git describe --tags` = `v2026.7.1-2` | -| 产品版本 | 仓根 `version` = `v5.6.6`,而 `CHANGELOG.md` **没有 v5.6.6 条目**(最新是 `v5.7.1 (2026-09-10)`,其次 `v5.7.0`)。`version` 的变更历史显示它被分支来回覆盖过(`3bbe234` 把 `v5.7.0` 改回 `v5.6.6`;`fdc03d6` 曾把 `v5.6.5` 改成 `v5.6.6`)→ **既有不一致,与本轮 pin bump 无关**(`d5e112c` 只改 5 个文件,未动 `version`),但升级/发布时要一并理顺版本口径 | -| git | `master` 领先 `origin/master` **2 个 commit**(`53d4d19`、`c5b771e`,均为 DNA / expert-video 工作);工作树有 1 个未提交修改:`crews/main/skills/published-track/SKILL.md` | -| portable Node | `.github/workflows/build-dist.yml` 仍是 `24.15.0`(行 44、152-155 共 5 处);`ci.yml:31` 用浮动 `'24'` → **§5.1 的 Node 抬版尚未做** | -| `skills.limits` | `config-templates/openclaw.json` 里**仍未设置** → **§6.3 的零成本降 token 项尚未做** | -| 本机部署实例 | 未动(`~/.openclaw` 只读铁律) | - ---- - -## 10. 待拍板项 - -| # | 决策 | 选项 | 已知成本 / 依据 | -|---|---|---|---| -| 1 | **基线选哪条** | (a) 直接迁 `2026.9.3`;(b) 先切 `extended-stable/2026.7.33`(pin `f619d7a9fa3`,或等它打 tag)过渡,再排期 9.3;(c) 暂不动;(d) **ES 7.33 + cherry-pick 9.3 省 token commit —— 已实测否决**(§6.5-2:5 个 commit 全部冲突,合计 153 个冲突文件次) | (a) 4.5–7 人日 `[A]`;(b) ≈半天,patch/awada 零漂移,但拿不到能力①② `[A]`;能力①最低 9.1、能力②要 9.3(§6.1) | -| 2 | **portable Node 抬到哪** | 24.16+ / 直接 26(上游推荐 26) | 与 openclaw 版本解耦,留 7.x 也该做(§5.1);改 `build-dist.yml` 5 处 + `ci.yml` + Docker 基础镜像钉死 | -| 3 | **9.x 迁移的触发条件认不认** | 认 / 不认(改为现在就一次性做完) | 触发条件草案:生产出现"回复停在工具输出 / 重启后丢回复"(#133520 #133979 #138071 #138519 **均未 backport 到 ES 7.33**);或 ES 7.33 停止提交;或需要 9.x 独有能力 | -| 4 | ~~**`install.sh` 幂等判断要不要改**~~ | **已做(2026-09-12)**:install.sh / install-atomgit.sh / docker-bootstrap.sh 三处改为按版本判定 + `--force` 升级;`update.sh` 的 bundled tarball 路径也补了 `--force` | 详见 §8.3;**遗留(用户决定暂不动)**:`update.sh` 在线路径走 weixin-cli,遇固定版本 spec 会自行跳过 | -| 5 | **浏览器路线** | (a) 按 9.3 新模块布局重画 pivot(05/03/04/09 + `del-*` 改 rm 清单 + 补 5 个新文件);(b) 先在 7.1-2 上把"能力裁剪"等价实现到自有 adapter 层(半天量级,不需动基座);(c) 评估 §7.8-4 的"MCP 桥零 patch"路线 | 见 §7.8;(a) 的工时已含在 #1 的 4.5–7 人日里 | -| 6 | **是否先做"单轮 token 构成"实测 + 三档配置调参** | 做 / 不做 | 半天、立刻见效、与升级解耦(§6.3、§6.5-5:`contextPruning` / `skills.limits` / `compaction.*` 三档都是 7.1-2 已支持但我们没开);也是判断能力② 收益基线的前置数据 | -| 7 | **是否先实测百炼端点的 prompt cache 语义** | 做 / 不做 | 决定 9.3 那批 cache 修复对我们是否有价值(§6.5-4);不做这一步,能力② 的收益无法量化 | - ---- - -## 附录 A:复验命令 - -```bash -cd /home/wukong/wiseflow/openclaw # 上游工作树,两个 tag 都已在本地对象库 - -# 体量 -git diff --shortstat v2026.7.1-2 v2026.9.3 -- extensions/browser -for f in A M D R; do echo -n "$f: "; git diff --name-only --diff-filter=$f v2026.7.1-2 v2026.9.3 -- extensions/browser | wc -l; done -for t in v2026.7.1-2 v2026.9.3; do echo -n "$t "; git ls-tree -r --name-only $t -- extensions/browser/src | wc -l; done -git show v2026.7.1-2:extensions/browser/src/browser-tool.ts | wc -l # 1087 -git show v2026.9.3:extensions/browser/src/browser-tool.ts | wc -l # 439 - -# 工具链 -for t in v2026.7.1-2 v2026.9.3; do git show $t:package.json | python3 -c 'import json,sys;d=json.load(sys.stdin);print(d["version"],d["engines"],d["packageManager"].split("+")[0])'; done - -# 逐 patch 目标漂移 -for p in /home/wukong/wiseflow/patches/browser-camoufox-pivot/patches/*.patch; do - f=$(grep -m1 "^+++ b/" "$p" | sed 's|^+++ b/||'); [ -z "$f" ] && f=$(grep -m1 "^--- a/" "$p" | sed 's|^--- a/||') - printf '%-70s %s\n' "$f" "$(git diff --numstat v2026.7.1-2 v2026.9.3 -- "$f" | awk '{print "+"$1" -"$2}')" -done - -# driver / 能力 / 配置面出处 -git show v2026.9.3:src/config/types.browser.ts -git show v2026.9.3:src/config/types.ssrf.ts -git show v2026.9.3:extensions/browser/src/browser/profile-capabilities.ts | head -140 -git show v2026.9.3:extensions/browser/src/browser/config.ts | sed -n '495,620p' -git show v2026.9.3:extensions/browser/src/browser-tool.schema.ts | sed -n '1,120p' -git show v2026.9.3:docs/tools/browser.md | sed -n '487,560p;860,950p' - -# 版本线拓扑 -git log --oneline -1 origin/extended-stable/2026.7.33 # f619d7a9fa3 -git rev-list --count v2026.7.1-2..origin/extended-stable/2026.7.33 # 38 -git merge-base --is-ancestor v2026.7.1-2 origin/extended-stable/2026.7.33 && echo YES # YES -git merge-base --is-ancestor v2026.7.1-2 v2026.9.3 && echo YES || echo NO # NO -git merge-base v2026.7.1-2 v2026.9.3 | xargs git log -1 --format='%h %ad %s' --date=short -MB=$(git merge-base v2026.7.1-2 v2026.9.3) -git rev-list --count $MB..v2026.9.3 # 24853 -git rev-list --count $MB..v2026.7.1-2 # 224 -``` - -## 附录 B:关键证据指针(9.3 tag) - -| 主题 | 位置 | -|---|---| -| driver 枚举 | `src/config/types.browser.ts:20` | -| driver 语义说明 | `src/config/schema.help.runtime.ts:37` | -| driver 归一化(clawd → openclaw) | `extensions/browser/src/browser/config.ts:507-509` | -| legacy clawd profile 清理 | `extensions/browser/src/doctor-browser.ts:24,402-430` | -| 远端端点拒绝 openclaw/clawd profile | `extensions/browser/src/browser/routes/basic.ts:463-467`;`docs/tools/browser.md:347` | -| extension relay 端口 / token | `extensions/browser/src/browser/config.ts:516-545` | -| existing-session 解析(MCP) | `extensions/browser/src/browser/config.ts:546-575`;`docs/tools/browser.md:752-909` | -| capability mode 判定 | `extensions/browser/src/browser/profile-capabilities.ts`(`remote-cdp` = `!cdpIsLoopback`;`local-managed.browserFilesystemLocal = !attachOnly`) | -| 工具能力裁剪 | `extensions/browser/src/browser-tool.schema.ts`(`resolveBrowserToolCapabilities`);`browser-tool-description.ts:19-21`;`browser-tool-binding.ts:49` | -| Chrome MCP 与受管 CDP 策略冲突 | `extensions/browser/src/browser/cdp-reachability-policy.ts:92-99` | -| SSRF 生效逻辑 | `extensions/browser/src/browser/navigation-guard.ts:85`(7.1-2 为 `:66`) | -| importprofile 必须在 browser host | `extensions/browser/src/browser-tool.lifecycle.ts:158-164` | -| screencast | `docs/tools/browser-control.md:26,88-140`;`extensions/browser/src/browser/screencast/*` | -| Control UI Browser 面板 | `docs/web/control-ui/panels.md:83` | -| node proxy | `extensions/browser/src/browser-node-commands.ts`、`browser-node-routing.ts`(`gateway.nodes.browser.mode`) | -| worker attached runtime | `src/worker/browser-runtime.ts`;`extensions/browser/src/attached-browser-tool-runtime.ts` | -| 浏览器二进制选择顺序 | `docs/tools/browser.md:920-940` | -| sandbox browser / noVNC 仍在 | `src/agents/sandbox/browser.ts`、`novnc-auth.ts`、`browser-bridges.ts`、`src/plugin-sdk/browser-bridge.ts`、`src/config/types.sandbox.ts:71-94` | -| 8.1 浏览器专项 release notes | `docs/releases/2026.8.1/browser-and-computer-use.md`(401 行) | -| 9.1 host-first 默认 | `docs/releases/2026.9.1.md:1483-1493` | -| 9.2 Browser & Computer Use | `docs/releases/2026.9.2.md:2759-2771` | -| 我们的 pivot 现状 | `patches/browser-camoufox-pivot/README.md`、`patches/browser-camoufox-pivot/patches/01-35`、`patches/browser-camoufox-pivot/files/camoufox-cli.adapter.ts` | diff --git a/scripts/lib/agent-skills.sh b/scripts/lib/agent-skills.sh index 23a1cb9e..67a1bd0d 100644 --- a/scripts/lib/agent-skills.sh +++ b/scripts/lib/agent-skills.sh @@ -499,7 +499,6 @@ inject_media_send_guide() { - 飞书:`message(action="send", media="<绝对路径>")`(对于 HTML 类型文件,飞书要求先复制到 `/tmp/openclaw/`,再执行发送)。 - `openclaw-weixin`:`message(action="send", media="<本地绝对路径或 HTTPS URL>")`,当前会话可不传 target。 -- `awada`: 不支持本地路径;当前回复用 `MEDIA:`,预置云文件用 `message(action="sendAttachment", file_name="<文件名>")`。 GUIDE } From 73f6677308e53b0df9fae417f5b1f57a485fb43e Mon Sep 17 00:00:00 2001 From: codes-factory-of-bg Date: Sat, 12 Sep 2026 23:43:27 +0800 Subject: [PATCH 20/34] =?UTF-8?q?fix(install):=20Windows=20ps1=20=E8=A1=A5?= =?UTF-8?q?=E9=BD=90=20openclaw-weixin=20=E6=8C=89=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E6=AF=94=E5=AF=B9=E5=8D=87=E7=BA=A7=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit b1b7cb5 只改了 install.sh / install-atomgit.sh / docker-bootstrap.sh 三处 shell 实现,scripts/install.ps1 与 scripts/install-atomgit.ps1 的 Install-WeixinPlugin 仍是旧逻辑(plugins list 匹配到插件名即 return),pin bump(2.4.6 -> 2.4.8)后 Windows 老用户重跑安装器永远停在旧版。 改动(两文件同一份): - 新增 Get-WeixinInstalledVersion:主路径 `plugins list --json` 取 id/name/version(stdout-only 捕获,沿用 Capture-Streamed 的 EAP Continue 模式 规避 PS 5.1 NativeCommandError);回落 $OPENCLAW_HOME\npm\projects\*\ node_modules\\package.json glob——与 bash 版 weixin_installed_version() 逐分支对齐 - Install-WeixinPlugin 判定:已装 == pin -> 跳过;!= pin -> --force 升级; 插件在但版本读不到 -> --force 按 pin 重装;失败提示同步带 --force - 头部步骤注释补版本幂等说明 - ps1 侧无 vendor/bundled-tarball 路径(068f87e 的 update.sh 修法无 Windows 对应物;Windows 升级即重跑安装器,更新路线装完后有 gateway restart 生效) 验证(便携 pwsh 7.6.6,Linux 侧): - Parser::ParseFile 两脚本 0 错误;新增块两文件逐字节一致;UTF-8 无 BOM + CRLF 保持不变;只用 PS 3.0+ 语法,5.1 兼容 - AST 抽取真实函数 + 假 openclaw 命令跑 6 场景全过:裸装(无 --force)/ 同版跳过(零安装调用)/ 2.4.6->2.4.8 --force 升级 / JSON 损坏走 glob 回落 / 版本读不到走 --force 重装 / stderr 噪音不污染 JSON 主路径 顺带合入(48818d8 删除 swcr-register 后的引用清理,同批工作区改动): - expert-ir SKILL.md + workflows/project-application.md:移除 swcr-register 依赖与委派表条目,软著/商标/专利申报明确移出职责范围 - docs/d21-symlink-skill.md:C 类清单注记同步清理 + 2026-09-12 变更历史条目 --- crews/main/skills/expert-ir/SKILL.md | 9 ++- .../workflows/project-application.md | 14 ++-- docs/d21-symlink-skill.md | 6 +- scripts/install-atomgit.ps1 | 73 +++++++++++++++++-- scripts/install.ps1 | 73 +++++++++++++++++-- 5 files changed, 150 insertions(+), 25 deletions(-) diff --git a/crews/main/skills/expert-ir/SKILL.md b/crews/main/skills/expert-ir/SKILL.md index 21001304..fb970b7e 100644 --- a/crews/main/skills/expert-ir/SKILL.md +++ b/crews/main/skills/expert-ir/SKILL.md @@ -1,6 +1,6 @@ --- name: expert-ir -description: 投资人关系(IR)专家。承接投资人发掘、融资沟通流水线(状态机跟进)、项目申报(认定/补贴/大赛/资质)的完整工作。零散的投资人记录、进展查询等操作也可以直接做。不涉及商务获客(找客户/“截流”/商业情报走 expert-bd)。 +description: 投资人关系(IR)专家。承接投资人发掘、融资沟通流水线(状态机跟进)、项目申报(认定/补贴/大赛)的完整工作。零散的投资人记录、进展查询等操作也可以直接做。不涉及商务获客(找客户/“截流”/商业情报走 expert-bd)。 metadata: openclaw: emoji: 📈 @@ -18,7 +18,7 @@ metadata: | 融资材料 | Investor Materials | Pitch Deck / One-Pager / 投资人备忘录 / 财务模型 / 加速器申请材料 | | 投资人触达 | Investor Outreach | 冷邮件、暖介绍请求、跟进邮件、投资人更新等沟通文案 | | 融资流水线 | Investor Pipeline | 完整的融资沟通编排:发掘 → 材料 → 触达 → 跟进 → 状态机推进 | -| 项目申报 | Project Application | 高企认定 / 加速器申请 / 政府补贴 / 软著商标专利配套 / 行业奖项:材料准备 + 时间线 + 状态跟踪 | +| 项目申报 | Project Application | 高企认定 / 加速器申请 / 政府补贴 / 行业奖项:材料准备 + 时间线 + 状态跟踪 | ## 执行方式与定时任务 @@ -41,7 +41,7 @@ metadata: |------|------|------| | `ir-record` | 投资人档案 / 接触历史 / 项目申报数据库(状态机数据层) | `ir-record` | -跨领域通用技能:`smart-search`(构造搜索 URL)、`browser-guide`(浏览器操作)、`email-ops`(邮件发送)、`market-research`(基金/竞品尽调)、`pitch-deck`(HTML 路演材料)、`council`(商业模式多视角复盘)、`swcr-register`(软著材料生成)。 +跨领域通用技能:`smart-search`(构造搜索 URL)、`browser-guide`(浏览器操作)、`email-ops`(邮件发送)、`market-research`(基金/竞品尽调)、`pitch-deck`(HTML 路演材料)、`council`(商业模式多视角复盘)。 ## 数据与记录 @@ -54,7 +54,8 @@ metadata: ## 边界 - 商业模式打磨(融资前的电梯版梳理 / 5 问结构化):由 agent 结合 `business_knowledge.md` 直接与用户完成,多路径权衡用 `council`;打磨结论落 `MEMORY.md` 后才进入投资人接触。 -- 项目申报 / 补贴 / 创业大赛 → 包内 Project Application Workflow(数据落 `ir-record` 的 applications 表);软著材料生成走顶层技能 `swcr-register`。 +- 项目申报 / 补贴 / 创业大赛 → 包内 Project Application Workflow(数据落 `ir-record` 的 applications 表)。 +- 软著 / 商标 / 专利等知识产权申报:不在职责范围,不承接。 - 商务获客(找客户 / 评论区 / 情报)→ `expert-bd`。 ## 红线 diff --git a/crews/main/skills/expert-ir/workflows/project-application.md b/crews/main/skills/expert-ir/workflows/project-application.md index 844b5dec..60548ed7 100644 --- a/crews/main/skills/expert-ir/workflows/project-application.md +++ b/crews/main/skills/expert-ir/workflows/project-application.md @@ -1,8 +1,10 @@ # Project Application(项目申报) -帮用户准备、跟踪各类外部申报项目:高新技术企业认定、加速器申请、政府补贴、资质认证(软著 / 商标 / 专利配套)、行业奖项。涵盖材料生成 + 时间线管理 + 状态跟踪。 +帮用户准备、跟踪各类外部申报项目:高新技术企业认定、加速器申请、政府补贴、行业奖项。涵盖材料生成 + 时间线管理 + 状态跟踪。 -**依赖**:`swcr-register`(软著材料)、`market-research`(行业数据 / 竞品分析)、Investor Materials Workflow(BP / One-Pager)、`ir-record`(applications 表状态跟踪)。 +软著 / 商标 / 专利等知识产权申报不在本 workflow 范围,不承接。 + +**依赖**:`market-research`(行业数据 / 竞品分析)、Investor Materials Workflow(BP / One-Pager)、`ir-record`(applications 表状态跟踪)。 ## 适用场景 @@ -11,18 +13,15 @@ - "我想申请高新技术企业认定 / 专精特新 / 科技型中小企业" - "我看到 X 加速器在招创业团队,能帮我准备申请吗" - "政府有 Y 补贴项目,截止日期 Z,能帮我看下材料吗" -- "我想申请软著 / 商标 / 专利" - "我要申报 X 行业奖项" ## 常见申报类型 | 类型 | 典型材料 | 材料协作 | |------|---------|---------| -| 高新技术企业认定 | 知识产权 + 研发费用 + 人员名单 + 财务审计 | `swcr-register` + `market-research` | +| 高新技术企业认定 | 知识产权 + 研发费用 + 人员名单 + 财务审计 | `market-research` | | 加速器申请 | BP + One-Pager + 团队介绍 + 牵引数据 | Investor Materials Workflow(包内) | | 政府补贴 | 申报书 + 财务报表 + 项目实施方案 | `market-research`(行业数据)| -| 软著登记 | 源程序文档 + 操作手册 | `swcr-register` | -| 商标 / 专利 | 技术交底书 + 权利要求书 | 直接走,不委派 | | 行业奖项 | 案例描述 + 客户证言 + 量化数据 | `market-research`(行业 baseline)| --- @@ -44,7 +43,6 @@ | 材料 | 委派给 | |------|--------| -| 软著材料(源程序 + 操作手册)| `swcr-register` | | 行业市场数据 / 竞品分析 | `market-research` | | BP / One-Pager | Investor Materials Workflow(包内) | @@ -83,7 +81,6 @@ ir-record update-application --id --status ## 与其他环节的关系 -- **`swcr-register`**(顶层技能):软著专用(频繁需要的子材料,合规性边界) - **`market-research`**(顶层技能):行业数据(多个申报类型需要) - **Investor Materials Workflow**(包内):加速器申请等需要 BP / One-Pager - **商业模式打磨**(IR 模式 1):申报前先打磨商业模式(很多申报材料要先有清晰的商业故事) @@ -111,6 +108,5 @@ ir-record update-application --id --status ## Notes -- 软著 / 商标 / 专利的"材料生成"严格走 `swcr-register` skill(合规性边界) - 财务审计报告、税务证明等"硬材料"由用户/会计师提供,AI 不替生成 - 申报通过率不承诺,AI 只保证材料齐整 / 表达清晰 / 时间线追踪 diff --git a/docs/d21-symlink-skill.md b/docs/d21-symlink-skill.md index 8ecd20a9..06c5fdb1 100644 --- a/docs/d21-symlink-skill.md +++ b/docs/d21-symlink-skill.md @@ -160,7 +160,7 @@ login-manager check douyin # wrapper 在 PATH 中 | sales-cs-enablement | scripts/symlink_business_knowledge.py | wrapper → py(2026-08-27 转子命令分发器:`link` / `check-channel`,随 sales-cs-manager 专家包收纳至 `crews/main/skills/sales-cs-manager/tools/sales-cs-enablement/`) | | sales-cs-review | scripts/scan_feedback.py | wrapper → py(2026-08-27 随 sales-cs-manager 专家包收纳至 `crews/main/skills/sales-cs-manager/tools/sales-cs-review/`) | -**C 类清单(多并列脚本,暂不加分发器 wrapper,维持 SKILL.md 绝对路径调用)**:bd-record(5)、info-record(4)、ir-record(11)、work-channel-binding(7)、customer-db(7)、pitch-deck(3)、video-product(6)、html-video(2,功能分裂)。(published-track、content-calibrator 已于 2026-08-21 转分发器 wrapper;swcr-register 已于 2026-08-27 转分发器 wrapper 并保持顶层位置(曾短暂折入 expert-bd/tools,同日移出),见变更历史。) +**C 类清单(多并列脚本,暂不加分发器 wrapper,维持 SKILL.md 绝对路径调用)**:bd-record(5)、info-record(4)、ir-record(11)、work-channel-binding(7)、customer-db(7)、pitch-deck(3)、video-product(6)、html-video(2,功能分裂)。(published-track、content-calibrator 已于 2026-08-21 转分发器 wrapper,见变更历史。) > **为何 C 类不加**:分发器 wrapper(` ...` 呺由到对应脚本)是为每个 skill 单定制分发表,引入新子命令方言、agent 还要学一套;现 SKILL.md 已把 `./skills//scripts/.sh` 绝对路径写死(CLAUDE.md 也强制要求),多并列脚本那种靠 SKILL.md 路径明文已治拼错。分发器是未来可选演进,本轮不做。 @@ -219,7 +219,9 @@ dev plan §Phase 7 续 写"验收": - **2026-09-10**:`content-producer` 引入专家包,四个技能整合为两个包——`video-producer` / `collage-broll` 收纳进 `expert-video/tools/`(`pipelines/` 改造为 `expert-video/workflows/`,`dna-ad-video-pipeline.md` 重构为 `reversal-ad.md`),`design-full` 收纳进 `expert-design/tools/`,`manim-explainer` 删除。**PATH wrapper 名与子命令全部不变**(`video-producer <子命令>` / `design-full `),靠 `expose_skill_wrappers` 的 `*/tools/*/` 扫描层暴露;新增 `collage-broll ` wrapper(原先 agent 直接拼 `scripts/run_gate3.py` 路径)。同时给 `expose_skill_wrappers` 与 `sync_crew_skills` 加**悬挂软链清理**(技能改名 / 收纳后,`~/.openclaw/bin` 与 `workspace-*/skills/` 里的旧软链会指向不存在的仓路径),并让 `collect_skill_script_commands` 扫 `skills//tools/*/scripts/`(否则包内 `.sh` 脚本会从 ALLOWED_COMMANDS 掉出去)。 -- **2026-08-27**:`swcr-register` 加子命令分发器 wrapper(`swcr-register `,-> scripts 下三个 Python 脚本),移出 C 类清单。同日随 expert-bd 专家包落地时曾折入 `expert-bd/tools/`,当天移出回归顶层 `crews/main/skills/swcr-register/`(软著属项目申报配套,非 BD 领域专属,跨包复用保持顶层)。wrapper 为 `SCRIPT_DIR` 自定位写法,与所在层级无关,移入移出均无需改动;`expert-bd/SKILL.md` 工具清单与 AGENTS.md 路由同步去除包内引用。同日 sales-cs-enablement / sales-cs-review 随 `sales-cs-manager` 专家包收纳至 tools/ 层(见 §4.2 表注与 §8.2 条目 21),sales-cs-enablement 转子命令分发器。 +- **2026-09-12**:`swcr-register` 技能删除(软著材料生成,产品定位调整),其 wrapper 与 expert-ir 侧引用、本文档 C 类清单注记一并清理。 + +- **2026-08-27**:sales-cs-enablement / sales-cs-review 随 `sales-cs-manager` 专家包收纳至 tools/ 层(见 §4.2 表注与 §8.2 条目 21),sales-cs-enablement 转子命令分发器。 - **2026-08-21**:数据直连 DNA 改造连带 wrapper 补强:`published-track`(C 类转分发器 wrapper:`published-track `)与 `content-calibrator`(`content-calibrator `)落顶层分发器 wrapper,SKILL.md / HEARTBEAT.md / AGENTS.md / expert-wx-mp workflows 全部 PATH 化,agent 零路径拼接。继 video-edit 之后第 2、3 个分发器 wrapper。 diff --git a/scripts/install-atomgit.ps1 b/scripts/install-atomgit.ps1 index f8ed0ac5..fb5569f9 100755 --- a/scripts/install-atomgit.ps1 +++ b/scripts/install-atomgit.ps1 @@ -36,6 +36,7 @@ # manually later) # 7. camoufox-cli: .cmd shim + camoufox-cli install downloads Firefox # 8. openclaw-weixin plugin: openclaw plugins install ... --pin (npmmirror) +# idempotency by VERSION: skip only when installed == pin; --force upgrade otherwise # 9. Interactive prompt for AWK_API_KEY -> write daemon.env + setx user env var -> attempt # openclaw daemon install # @@ -481,6 +482,53 @@ function Install-CamoufoxCli { } # --- 9. openclaw-weixin plugin --- +# Read the installed openclaw-weixin version; returns $null when unreadable. +# Primary path: `plugins list --json` (fields id / name / version); fallback: the real +# package.json under npm\projects\* (project dir names carry a hash, so glob). +function Get-WeixinInstalledVersion([string]$pkg) { + # Capture stdout only (mirrors bash 2>/dev/null): stderr noise would break ConvertFrom-Json. + # EAP must be Continue here - under Stop, `2>$null` on a .cmd still throws + # NativeCommandError on Windows PowerShell 5.1 (same pitfall as Capture-Streamed above). + $prev = $ErrorActionPreference + $ErrorActionPreference = 'Continue' + try { $json = (& $ClawCmd plugins list --json 2>$null | Out-String) } + finally { $ErrorActionPreference = $prev } + if ($json) { + try { + foreach ($p in ($json | ConvertFrom-Json).plugins) { + if ($p.id -eq "openclaw-weixin" -or $p.name -eq $pkg) { + if ($p.version) { return [string]$p.version } + break + } + } + } catch { } + } + # Fallback: CLI/JSON unavailable - read the installed package.json directly + $projRoot = Join-Path $OpenclawHome "npm\projects" + if (Test-Path $projRoot) { + $pkgRel = $pkg.Replace("/", "\") + foreach ($dir in (Get-ChildItem $projRoot -Directory -ErrorAction SilentlyContinue)) { + $f = Join-Path $dir.FullName "node_modules\$pkgRel\package.json" + if (-not (Test-Path $f)) { continue } + try { + $v = (Get-Content $f -Raw | ConvertFrom-Json).version + if ($v) { return [string]$v } + } catch { } + } + } + return $null +} + +# Install the openclaw-weixin plugin (the config template pre-populates the channel, but the +# plugin itself needs `openclaw plugins install`). Pin comes from openclaw-weixin.version.json +# in the tarball; registry is npmmirror. +# Idempotency by VERSION, not by name: skip only when installed == pin; installed != pin -> +# `--force` upgrade to pin. (The old logic merely matched the plugin name in `plugins list` +# and returned, so after a pin bump existing installs stayed on the old version forever.) +# --force = "Overwrite an existing installed plugin" (flag exists since 7.1-2). The upgrade +# only replaces the package under npm\projects; login state in $OpenclawHome\openclaw-weixin\ +# is untouched, and the update route restarts the gateway afterwards so the new version takes +# effect. function Install-WeixinPlugin { Write-Stage "Installing WeChat plugin" if (-not (Test-Path $ClawCmd)) { Write-Warn "openclaw wrapper not found: $ClawCmd; skipping"; return } @@ -493,11 +541,26 @@ function Install-WeixinPlugin { } catch { Write-Warn "pin file parse failed, using default $pkg@$ver" } } $env:npm_config_registry = "https://registry.npmmirror.com" - $listOut = Capture-Streamed { & $ClawCmd plugins list } - if ($listOut -match "openclaw-weixin") { Write-Ok "openclaw-weixin plugin already installed"; return } - Invoke-Streamed { & $ClawCmd plugins install "$pkg@$ver" --pin } - if ($LASTEXITCODE -eq 0) { Write-Ok "openclaw-weixin plugin installed" } - else { Write-Warn "plugin install failed; you can run manually later: $ClawCmd plugins install $pkg@$ver --pin" } + # Idempotency check: skip only when installed version == pin + $installed = Get-WeixinInstalledVersion $pkg + $forceFlag = @() + if ($installed -and $installed -ceq $ver) { Write-Ok "openclaw-weixin plugin already installed ($ver)"; return } + if ($installed) { + Write-Host " [i] openclaw-weixin installed $installed, pin $ver -> --force upgrade" -ForegroundColor Yellow + $forceFlag = @("--force") + } else { + $listOut = Capture-Streamed { & $ClawCmd plugins list } + if ($listOut -match "openclaw-weixin") { + # Plugin present but version unreadable (CLI/JSON anomaly): force reinstall at pin + Write-Warn "openclaw-weixin installed but version unreadable; force reinstalling at pin $ver" + $forceFlag = @("--force") + } + } + $installArgs = @("plugins", "install", "$pkg@$ver", "--pin") + $forceFlag + $forceHint = if ($forceFlag) { " --force" } else { "" } + Invoke-Streamed { & $ClawCmd @installArgs } + if ($LASTEXITCODE -eq 0) { Write-Ok "openclaw-weixin plugin installed ($ver)" } + else { Write-Warn "plugin install failed; you can run manually later: $ClawCmd plugins install $pkg@$ver --pin$forceHint" } } # --- 10. awada local plugin deps (ws + zod) --- diff --git a/scripts/install.ps1 b/scripts/install.ps1 index bb68ac2b..162a5f19 100755 --- a/scripts/install.ps1 +++ b/scripts/install.ps1 @@ -38,6 +38,7 @@ # manually later) # 7. camoufox-cli: .cmd shim + camoufox-cli install downloads Firefox # 8. openclaw-weixin plugin: openclaw plugins install ... --pin (npmmirror) +# idempotency by VERSION: skip only when installed == pin; --force upgrade otherwise # 9. Interactive prompt for AWK_API_KEY -> write daemon.env + setx user env var -> attempt # openclaw daemon install # @@ -498,6 +499,53 @@ function Install-CamoufoxCli { } # --- 9. openclaw-weixin plugin --- +# Read the installed openclaw-weixin version; returns $null when unreadable. +# Primary path: `plugins list --json` (fields id / name / version); fallback: the real +# package.json under npm\projects\* (project dir names carry a hash, so glob). +function Get-WeixinInstalledVersion([string]$pkg) { + # Capture stdout only (mirrors bash 2>/dev/null): stderr noise would break ConvertFrom-Json. + # EAP must be Continue here - under Stop, `2>$null` on a .cmd still throws + # NativeCommandError on Windows PowerShell 5.1 (same pitfall as Capture-Streamed above). + $prev = $ErrorActionPreference + $ErrorActionPreference = 'Continue' + try { $json = (& $ClawCmd plugins list --json 2>$null | Out-String) } + finally { $ErrorActionPreference = $prev } + if ($json) { + try { + foreach ($p in ($json | ConvertFrom-Json).plugins) { + if ($p.id -eq "openclaw-weixin" -or $p.name -eq $pkg) { + if ($p.version) { return [string]$p.version } + break + } + } + } catch { } + } + # Fallback: CLI/JSON unavailable - read the installed package.json directly + $projRoot = Join-Path $OpenclawHome "npm\projects" + if (Test-Path $projRoot) { + $pkgRel = $pkg.Replace("/", "\") + foreach ($dir in (Get-ChildItem $projRoot -Directory -ErrorAction SilentlyContinue)) { + $f = Join-Path $dir.FullName "node_modules\$pkgRel\package.json" + if (-not (Test-Path $f)) { continue } + try { + $v = (Get-Content $f -Raw | ConvertFrom-Json).version + if ($v) { return [string]$v } + } catch { } + } + } + return $null +} + +# Install the openclaw-weixin plugin (the config template pre-populates the channel, but the +# plugin itself needs `openclaw plugins install`). Pin comes from openclaw-weixin.version.json +# in the tarball; registry is npmmirror. +# Idempotency by VERSION, not by name: skip only when installed == pin; installed != pin -> +# `--force` upgrade to pin. (The old logic merely matched the plugin name in `plugins list` +# and returned, so after a pin bump existing installs stayed on the old version forever.) +# --force = "Overwrite an existing installed plugin" (flag exists since 7.1-2). The upgrade +# only replaces the package under npm\projects; login state in $OpenclawHome\openclaw-weixin\ +# is untouched, and the update route restarts the gateway afterwards so the new version takes +# effect. function Install-WeixinPlugin { Write-Stage "Installing WeChat plugin" if (-not (Test-Path $ClawCmd)) { Write-Warn "openclaw wrapper not found: $ClawCmd; skipping"; return } @@ -510,11 +558,26 @@ function Install-WeixinPlugin { } catch { Write-Warn "pin file parse failed, using default $pkg@$ver" } } $env:npm_config_registry = "https://registry.npmmirror.com" - $listOut = Capture-Streamed { & $ClawCmd plugins list } - if ($listOut -match "openclaw-weixin") { Write-Ok "openclaw-weixin plugin already installed"; return } - Invoke-Streamed { & $ClawCmd plugins install "$pkg@$ver" --pin } - if ($LASTEXITCODE -eq 0) { Write-Ok "openclaw-weixin plugin installed" } - else { Write-Warn "plugin install failed; you can run manually later: $ClawCmd plugins install $pkg@$ver --pin" } + # Idempotency check: skip only when installed version == pin + $installed = Get-WeixinInstalledVersion $pkg + $forceFlag = @() + if ($installed -and $installed -ceq $ver) { Write-Ok "openclaw-weixin plugin already installed ($ver)"; return } + if ($installed) { + Write-Host " [i] openclaw-weixin installed $installed, pin $ver -> --force upgrade" -ForegroundColor Yellow + $forceFlag = @("--force") + } else { + $listOut = Capture-Streamed { & $ClawCmd plugins list } + if ($listOut -match "openclaw-weixin") { + # Plugin present but version unreadable (CLI/JSON anomaly): force reinstall at pin + Write-Warn "openclaw-weixin installed but version unreadable; force reinstalling at pin $ver" + $forceFlag = @("--force") + } + } + $installArgs = @("plugins", "install", "$pkg@$ver", "--pin") + $forceFlag + $forceHint = if ($forceFlag) { " --force" } else { "" } + Invoke-Streamed { & $ClawCmd @installArgs } + if ($LASTEXITCODE -eq 0) { Write-Ok "openclaw-weixin plugin installed ($ver)" } + else { Write-Warn "plugin install failed; you can run manually later: $ClawCmd plugins install $pkg@$ver --pin$forceHint" } } # --- 10. awada local plugin deps (ws + zod) --- From 018784ba8ce7811afd8ae45973ffa7b868918573 Mon Sep 17 00:00:00 2001 From: codes-factory-of-bg Date: Sun, 13 Sep 2026 09:48:04 +0800 Subject: [PATCH 21/34] improve cp video product reversal-ad --- .../skills/expert-video/SKILL.md | 2 +- .../expert-video/workflows/reversal-ad.md | 59 ++++++++++++++----- 2 files changed, 44 insertions(+), 17 deletions(-) diff --git a/crews/content-producer/skills/expert-video/SKILL.md b/crews/content-producer/skills/expert-video/SKILL.md index 5382cd92..5fcd849c 100644 --- a/crews/content-producer/skills/expert-video/SKILL.md +++ b/crews/content-producer/skills/expert-video/SKILL.md @@ -67,7 +67,7 @@ metadata: | 视频类型 / 入口信号 | workflow | Brief `workflow` 值 | 它细化了什么 | |--------------------|----------|---------------------|--------------| -| 影视解说 / 剧情解说 + 突然反转插入品宣("万万没想到"式) | Reversal Ad | `reversal-ad` | 三段结构占比、反转点落在 55%–76%、四种反转手法、意象桥与钩连句、植入段约束 | +| 影视解说 / 剧情解说 + 突然反转插入品宣("万万没想到"式) | Reversal Ad | `reversal-ad` | 三段结构占比、反转点落在 55%–76%、四种反转手法、反转幅度与接入丝滑度两轴(含二次反转 CTA)、素材三模式 sourcing(Blender 片库 / 用户直供三查 / AIGC)、意象桥与钩连句、植入段约束 | | 口播类(甲方交付口播文案或真人录音,要合成声画) | Narration Video | `narration-video` | 口播稿落稿锁定不重写、按字级时间戳配画面、声音规范与验收清单 | | "把这句口播做成拼贴 B-roll""纸拼贴动画""半调拼贴" | Collage B-roll | `collage-broll` | 一句文稿 → 一个视觉隐喻 → 静帧 → i2v,三道闸门与 Gate 3 批量调度 | diff --git a/crews/content-producer/skills/expert-video/workflows/reversal-ad.md b/crews/content-producer/skills/expert-video/workflows/reversal-ad.md index b74b32b8..0e94ec56 100644 --- a/crews/content-producer/skills/expert-video/workflows/reversal-ad.md +++ b/crews/content-producer/skills/expert-video/workflows/reversal-ad.md @@ -4,7 +4,7 @@ Brief 里写 `workflow: reversal-ad` 时使用。本文是**通用制作流程 ## 类型定义 -一整段**视频解说**(影视解说 / 剧情解说 / 纪录片式解说)铺垫,观众以为自己在看内容;到某一帧突然反转插入品宣——猝不及防、万万没想到。笑点与记忆点全部来自「预期落差」,所以反转前必须让观众完全相信这是一条内容视频。 +一整段**视频解说**(影视解说 / 剧情解说 / 纪录片式解说)铺垫,观众以为自己在看内容;到某一帧突然反转插入品宣——猝不及防、万万没想到。笑点与记忆点全部来自「预期落差」,所以反转前必须让观众完全相信这是一条内容视频。两条第一质量轴:**反转幅度足够大、接入足够丝滑**——实践里最常见的失败不是没有反转,而是幅度不够或接入生硬,见「反转幅度与接入丝滑度」。 ## 三段结构 @@ -12,9 +12,9 @@ Brief 里写 `workflow: reversal-ad` 时使用。本文是**通用制作流程 |----|---------|------|--------| | 解说正文 | 63%–76% | 讲一个自洽、有冲突、有悬念的故事 | 主悬念一句话可复述,且贯穿到反转点;段尾停在「求助 / 任务 / 待发 / 冲突」节点 | | 反转过渡 | 3%–13% | 用一句话或一帧把剧情指向产品 | 必须双关或因果可追,不许硬切;口播明写因果 | -| 产品植入 | 15%–27% | 集中讲产品,讲完立即收尾 | 3–4 句单点深打,覆盖 Brief 允许的 ≥3 个价值点,每点有对应画面 | +| 产品植入 | 15%–27% | 集中讲产品,讲完立即收尾 | 3–4 句单点深打,覆盖 Brief 允许的 ≥3 个价值点,每点有对应画面;片尾 CTA 优先用二次反转剧情化承载 | -反转点落在总时长 **55%–76%** 之间(集中植入偏前,两段式偏后)。占比是创作约束,不是硬编码参数;Brief 指定时以 Brief 为准。 +反转点落在总时长 **55%–76%** 之间(集中植入偏前,两段式偏后)。占比是创作约束,不是硬编码参数;Brief 指定时以 Brief 为准。产品植入段末尾可设**二次反转**承载 CTA(见「反转幅度与接入丝滑度」),其时长计入产品植入段,不另立第四段。 ## 反转手法(四选一,或叠加) @@ -25,6 +25,31 @@ Brief 里写 `workflow: reversal-ad` 时使用。本文是**通用制作流程 | 身份彩蛋式 | 主角身份在反转处变成与产品相关的角色 | 身份转换要由剧情自然推出(求职、比赛、任务),不靠旁白硬说 | | 戏中戏式 | 剧情内的媒介(录像机屏幕、照片、窗外、手机屏)先出现产品画面 | 口播仍讲剧情句,画面先行;随后全屏切产品段 | +## 反转幅度与接入丝滑度(第一质量轴) + +实践中最常见的失败不是没有反转,而是**反转幅度不够大、或接入生硬**。两轴缺一不可: + +- **幅度大**:反转前后两个世界的距离拉到最远——题材跳(现实求助 / 悬念事件 ↔ 商业产品)、身份跳(普通人 ↔ 产品相关角色)、场景跳、情绪跳。铺垫段演得越认真、越沉浸,反转越强;铺垫段禁止自嘲、禁止对镜头挤眼、禁止流露任何「广告感」。 +- **丝滑接入**:接入点必须由剧情自身逻辑自然推出——自问自答、因果链、意象复用。观众事后的反应应是「原来如此,说得通」,而不是「怎么突然打广告」。接入句的因果必须一句话可复述。 +- **二次反转(可选,强烈推荐)**:片尾互动号召(转发 / 点赞 / 评论)不做广告口号,种进剧情动机里——观众完成互动时感觉自己在推进剧情,而不是在点广告。 + +✅ 正例(真人口播爆款,借结构不照搬措辞):一个人拍视频「我丢了 2000 块钱,大家帮我找找,找到必有重谢」——一转:「我答谢你什么呢?答谢你带你去呼伦贝尔旅游」,身份揭晓:他其实是呼伦贝尔地接导游,宣传的是旅游线路——二转:「这 2000 块对我真的很重要,看到的哥哥姐姐帮我转发一下」,转发号召成了「找钱」剧情的自然延伸。三个可复制点:铺垫严肃可信(现实求助、零广告感)→ 落差巨大;「答谢你什么呢」自问自答桥接两个世界 → 接入丝滑;CTA 由剧情动机给出 → 转发不是打广告。 + +❌ 反例: + +- 铺垫段本身松垮、观众开场就闻到广告味——反转再大也无效。 +- 反转靠硬切加口号(「万万没想到,这就是 XX 品牌」),剧情内无因果——幅度大但接入生硬。 +- 片尾 CTA 喊「求转发求点赞一键三连」——与剧情割裂,浪费二次反转位。 + +GATE A 交审脚本时必须附四问答案(`script-self-eval` 同查): + +1. 首次观看的观众,反转点之前会认真相信这是一条纯内容视频吗? +2. 反转瞬间能让观众脱口而出「万万没想到」吗(幅度)? +3. 接入句的因果能一句话复述吗(丝滑度)? +4. CTA 也是剧情的一部分吗(设了二次反转时)? + +> 正例为真人口播场景,其第二人称口语不受本文口播规范约束;由我代笔第三人称解说体时,CTA 句式仍按口播规范,但 CTA **动机必须由剧情给出**。 + ## 叙事与植入规则 - **单线闭环**:主角动机一句话说清并在片内闭环;素材撑不住就改写,不留断头线。 @@ -33,6 +58,7 @@ Brief 里写 `workflow: reversal-ad` 时使用。本文是**通用制作流程 - **意象桥**:产品段第一镜必须复用解说正文的核心意象;禁止无关风景空镜硬切。 - **反转前零产品提及**:不插 punch 句、不散落卖点、不出现品牌暗示(含 logo、包装、界面)。 - **收束**:产品段讲完立即收尾,不加无信息量的仪式句或氛围空镜。 +- **CTA 剧情化**:片尾行动号召优先由剧情二次反转承载(动机由剧情给出),不用空洞广告口号;见「反转幅度与接入丝滑度」。 - **音效**:可在反转瞬间用 whoosh 等音效标记剧情→产品切换,但不得用音效替代因果衔接。 - **合规**:正片零 URL、零域名、零联系方式;官网、仓库、价格、活动入口只写进 `final-deliver.md` 交甲方(发布说明由甲方拟)。产品能力只讲 Brief 允许的范围,不承诺收益数字。 @@ -46,14 +72,14 @@ Brief 里写 `workflow: reversal-ad` 时使用。本文是**通用制作流程 来源模式只允许以下四类(含混合),每段素材都必须能追溯到来源模式与授权记录: -1. **open_license_footage**:Brief 指定的开源/免版权片源。记录原片 URL、许可证、署名要求,发布说明如实署名。不得把"网上能下载"等同于可商用或可改编。 -2. **user_provided**:甲方/用户给现成片段。入库前记录文件参数、来源说明、授权背景;版权风险由甲方确认承担,我只做技术处理,不做授权背书。 -3. **aigc**:按 Brief 风格边界生成(公共 `aigc-video-gen`)。记录 prompt、模型、生成时间与产物 metadata;交付说明按平台要求标注 AI 生成。 -4. **mixed**:以上混合。 +1. **open_license_footage**:首选 **Blender 开源电影**——官网直链 `studio.blender.org/films/…`,无需登录,协议均为 CC BY。已验证可用片库:Spring / Sintel / Caminandes 系列 / Tears of Steel / Big Buck Bunny / Coffee Run。逐部记录原片 URL、许可证与署名要求;**发布简介必须带原片署名**——署名文案写入 `final-deliver.md` 交甲方并入发布说明。片库以外的片源逐案核实许可证允许商用与改编,不得把"网上能下载"等同于可用。 +2. **user_provided**:用户直供现成影视片段。入库**三查**:① `ffprobe` 全片 decode 校验(整片解码,不只读文件头,防尾部损坏);② 记录分辨率、帧率、时长、音轨;③ 来源与授权背景向用户确认,原样记入 `final-deliver.md`。署名按用户提供的信息如实标注,不猜测、不做授权背书;版权风险由甲方确认承担,我只做技术处理。入库副本落 `raw_materials/`。 +3. **aigc**:按 Brief 风格边界生成(公共 `aigc-video-gen`)。**每段 3–15s**;prompt 必须含**画面描述 + 音频描述**——AIGC 声画同出,源文件自带音频可作环境音床(见护栏 5)。产物存 `/generations/`(天然满足 `aigc-video-gen` 的 `output_videos/` 路径约束),每段附 metadata(prompt、模型、生成时间)。发布时按平台要求勾「AI 生成」标注——标注要求写入 `final-deliver.md` 交甲方。 +4. **mixed**:以上混合,每段素材独立追溯到各自的模式与授权记录。 通用入库要求: -- 逐条校验可解码,记录分辨率、帧率、时长、音轨。 +- 逐条按来源模式完成入库检查:`ffprobe` 全片 decode 校验(不只读文件头),记录分辨率、帧率、时长、音轨。 - 建素材清单与候选区索引;候选区文件名能反查源素材时间点。 - **素材四查**:URL / 域名、旧品牌名或水印、残缺元素、字幕遮挡。发现风险优先换干净素材窗口,裁切只能作为确认安全后的次选。 - 素材支撑度不足时回到 Brief owner,不为了凑成片编造叙事。 @@ -67,9 +93,9 @@ Brief 里写 `workflow: reversal-ad` 时使用。本文是**通用制作流程 | platform | douyin / wx_channel / xhs 等,用于画幅、时长带与合规边界 | | core_message | 本条必须传达的核心信息 | | product_points | 产品/服务事实、允许讲的能力、禁用承诺(只以 Brief 为准,不内置品牌事实) | -| implant_cta | Brief「业务植入与 CTA」字段:植入位置与方式、内容与业务的衔接句要求、CTA 主目标与句式;未给时按本 workflow 默认(反转点 55%–76%、植入段单点深打、片尾一个主行动),并在 GATE A 说明 | +| implant_cta | Brief「业务植入与 CTA」字段:植入位置与方式、内容与业务的衔接句要求、CTA 主目标与句式;未给时按本 workflow 默认(反转点 55%–76%、植入段单点深打、片尾一个主行动且优先二次反转剧情化承载),并在 GATE A 说明 | | twist_variant | 任务指引式 / 双关置换式 / 身份彩蛋式 / 戏中戏式;未指定时由我据素材与故事选定,并在 GATE A 说明理由 | -| story_source | 解说正文的故事来源(开源片名 / 用户素材 / AIGC 生成) | +| story_source | 解说正文的故事来源(开源片名 / 用户素材 / AIGC 生成;开源片名优先 Blender 开源电影片库,见「素材 sourcing」) | | voiceover | 甲方交付口播终稿路径;未交付时写明由我起草 | | source_mode | open_license_footage / user_provided / aigc / mixed | | assets | 素材绝对路径、来源 URL、许可证、授权确认记录 | @@ -84,7 +110,7 @@ Brief 里写 `workflow: reversal-ad` 时使用。本文是**通用制作流程 |------|--------------------| | Stage 1 | 档位固定 **故事讲述型(narrative)** | | Stage 2 | 甲方交付口播时跳过;未交付时故事梗概必须写明三段结构与反转手法 | -| Stage 3 | `script/script.md` 按段标注(解说 / 反转 / 植入)+ 每段时长占比 + 反转点时间码 | +| Stage 3 | `script/script.md` 按段标注(解说 / 反转 / 植入)+ 每段时长占比 + 反转点时间码;自评过「反转幅度与接入丝滑度」四问,GATE A 随脚本交答案 | | Stage 4 | 镜头表每镜标所属段;反转点前后各一镜写明衔接意象(意象桥) | | Stage 6 | 现成素材轨且无 AIGC 角色时跳过角色三视图 | | Stage 7–8 | slot 按段规划,解说段 slot 数最多;植入段 slot 必须能承载 Brief 的价值点 | @@ -110,8 +136,9 @@ Brief 里写 `workflow: reversal-ad` 时使用。本文是**通用制作流程 2. 品牌名、产品名、能力表述与 Brief 一致;无收益承诺。 3. 叙事单线闭环,节拍 ≤ 6,转折因果可追。 4. 反转前零产品提及;意象桥成立(产品段第一镜复用剧情核心意象)。 -5. 产品段占比与反转点位置落在承诺区间,且与选定手法一致。 -6. 每段素材来源与授权可追溯;AIGC 标注完整。 -7. 口播、字幕、画面、音效与 Brief / `voiceover.md` 一致。 -8. `video-review` verdict=pass;未通过不得交付。 -9. `final-deliver.md` 含素材来源与协议、口播终稿、各段实际时长、自检结果、弃用产物与遗留问题。 +5. 反转两轴达标:铺垫段可信为纯内容视频且与产品世界落差足够(幅度)、接入句因果一句话可复述(丝滑);设二次反转时 CTA 是剧情动机的延伸。 +6. 产品段占比与反转点位置落在承诺区间,且与选定手法一致。 +7. 每段素材来源与授权可追溯;Blender 署名、用户素材授权确认、AIGC 标注要求均已写入 `final-deliver.md`。 +8. 口播、字幕、画面、音效与 Brief / `voiceover.md` 一致。 +9. `video-review` verdict=pass;未通过不得交付。 +10. `final-deliver.md` 含素材来源与协议、口播终稿、各段实际时长、自检结果、弃用产物与遗留问题。 From 58edf56a91a780672e920dc022d0b1925fb9495a Mon Sep 17 00:00:00 2001 From: codes-factory-of-bg Date: Sun, 13 Sep 2026 10:34:49 +0800 Subject: [PATCH 22/34] =?UTF-8?q?fix(scripts):=20pnpm=20=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E5=AE=88=E5=8D=AB=E2=80=94=E2=80=94=E5=AF=B9=E9=BD=90=20opencl?= =?UTF-8?q?aw=20=E5=B7=A5=E4=BD=9C=E5=8C=BA=20pin=20(pnpm=2011)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pnpm 10.x 的 install CLI 不认 --fetch-retries(apply-addons.sh 依赖同步 直接 unknown argument 炸掉),新机装了 10.30.2 且 update.sh 零预检、 install.sh 的 install_pnpm() 只判断存在即跳过,报错完全没指向真因。 - update.sh: 步骤 1.5 加 pnpm 大版本预检(读 openclaw/package.json 的 packageManager pin),缺失/过低给出明确升级命令;版本探测在 / 下做, 避开 pnpm ≥11 manage-package-manager-versions 在带 pin 目录返回假版本 - install.sh: 定义 PNPM_VERSION(此前从未定义,真走到安装分支会执行 npm install -g pnpm@ 空版本);install_pnpm() 加 major 守卫,低于 要求版本时升级而非跳过 - package.json: packageManager 从 4 月遗留的 10.30.2 对齐 11.2.2 (hash 与 openclaw/package.json 同版本 pin 一致) --- package.json | 2 +- scripts/install.sh | 25 ++++++++++++++++++++++--- scripts/update.sh | 20 ++++++++++++++++++++ 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 422286cc..54ff191c 100644 --- a/package.json +++ b/package.json @@ -1,3 +1,3 @@ { - "packageManager": "pnpm@10.30.2+sha512.36cdc707e7b7940a988c9c1ecf88d084f8514b5c3f085f53a2e244c2921d3b2545bc20dd4ebe1fc245feec463bb298aecea7a63ed1f7680b877dc6379d8d0cb4" + "packageManager": "pnpm@11.2.2+sha512.36e6621fad506178936455e70247b8808ef4ec25797a9f437a93281a020484e2607f6a469a22e982987c3dbb8866e3071514ab10a4a1749e06edcd1ec118436f" } diff --git a/scripts/install.sh b/scripts/install.sh index 6b684793..93358909 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -929,12 +929,31 @@ install_git() { # ═══════════════════════════════════════════════════════════════════ # pnpm # ═══════════════════════════════════════════════════════════════════ +# 与 openclaw/package.json 的 packageManager pin 保持同步(apply-addons.sh 的依赖 +# 同步用了 pnpm 11 的 install CLI flags——如 --fetch-retries,pnpm 10.x 不认会炸)。 +# 升级 openclaw 换 pin 时此处要跟着改。 +PNPM_VERSION="${OPENCLAW_PNPM_VERSION:-11.2.2}" + +# 在无 packageManager pin 的目录下探测 pnpm 真实安装版本。 +# pnpm ≥11 默认开 manage-package-manager-versions,在带 pin 的目录里 +# `pnpm --version` 返回 pin 版本而非实际安装的版本,版本判断会被骗。 +pnpm_real_version() { + (cd / && pnpm --version 2>/dev/null) || true +} + install_pnpm() { - if command -v pnpm >/dev/null 2>&1; then - ui_success "pnpm already installed ($(pnpm --version 2>/dev/null || echo unknown))" + local required_major installed_major + required_major="${PNPM_VERSION%%.*}" + installed_major="$(pnpm_real_version | cut -d. -f1)" + if command -v pnpm >/dev/null 2>&1 && [ "${installed_major:-0}" -ge "$required_major" ]; then + ui_success "pnpm already installed ($(pnpm_real_version || echo unknown))" return 0 fi - ui_info "Installing pnpm@${PNPM_VERSION} globally" + if command -v pnpm >/dev/null 2>&1; then + ui_info "Upgrading pnpm to ${PNPM_VERSION} (found $(pnpm_real_version || echo unknown); openclaw 工作区需要 pnpm ${required_major}+)" + else + ui_info "Installing pnpm@${PNPM_VERSION} globally" + fi # 用 corepack 路线(与 openclaw 仓 packageManager 对齐,最稳) if command -v corepack >/dev/null 2>&1; then run_required_step "Enabling corepack" corepack enable diff --git a/scripts/update.sh b/scripts/update.sh index 3997580b..197d9bd8 100755 --- a/scripts/update.sh +++ b/scripts/update.sh @@ -85,6 +85,26 @@ if [ ! -f "$PROJECT_ROOT/scripts/apply-addons.sh" ] || [ ! -d "$OPENCLAW_DIR" ]; exit 1 fi +# ─── 1.5 pnpm 版本预检 ──────────────────────────────────────────────── +# openclaw 工作区按其 package.json 的 packageManager pin 要求 pnpm 大版本: +# apply-addons.sh 的依赖同步传了 --fetch-retries 等 flag,只有 pnpm 11 的 +# install CLI 才认(pnpm 10.x 直接 unknown argument 炸掉)。 +# 注意 pnpm ≥11 默认开 manage-package-manager-versions,在带 pin 的目录里 +# `pnpm --version` 返回 pin 版本而非实际安装版本,须在无 pin 的 / 下探测。 +REQUIRED_PNPM_MAJOR="$(grep -m1 '"packageManager"' "$OPENCLAW_DIR/package.json" 2>/dev/null | grep -o 'pnpm@[0-9.]*' | head -1 | sed 's/^pnpm@//; s/\..*//')" +: "${REQUIRED_PNPM_MAJOR:=11}" +if ! command -v pnpm >/dev/null 2>&1; then + echo "❌ pnpm 未安装(openclaw 工作区需要 pnpm ${REQUIRED_PNPM_MAJOR}.x+)" + echo " 安装:npm install -g pnpm@${REQUIRED_PNPM_MAJOR} --registry=https://registry.npmmirror.com" + exit 1 +fi +INSTALLED_PNPM_MAJOR="$( (cd / && pnpm --version 2>/dev/null) | cut -d. -f1)" +if [ "${INSTALLED_PNPM_MAJOR:-0}" -lt "$REQUIRED_PNPM_MAJOR" ]; then + echo "❌ pnpm ${REQUIRED_PNPM_MAJOR}.x+ required (openclaw/package.json packageManager pin), found $(cd / && pnpm --version 2>/dev/null || echo unknown)" + echo " 升级:npm install -g pnpm@${REQUIRED_PNPM_MAJOR} --registry=https://registry.npmmirror.com" + exit 1 +fi + ensure_openclaw_config() { if [ ! -f "$OPENCLAW_CONFIG_PATH" ]; then mkdir -p "$(dirname "$OPENCLAW_CONFIG_PATH")" From aa1ba0d162b484522433927b457b7767083e49c9 Mon Sep 17 00:00:00 2001 From: codes-factory-of-bg Date: Sun, 13 Sep 2026 22:56:53 +0800 Subject: [PATCH 23/34] =?UTF-8?q?fix(skills):=20twitter-post=20CJK=20?= =?UTF-8?q?=E8=BE=93=E5=85=A5=E5=AE=89=E5=85=A8=E9=97=B8=E9=97=A8=20+=20?= =?UTF-8?q?=E8=A7=86=E9=A2=91=E5=8F=B7=E7=99=BB=E5=BD=95=E5=AF=B9=E9=BD=90?= =?UTF-8?q?=E6=97=A0=E5=A4=B4=E6=88=AA=20QR?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit twitter-post:CJK 正文禁用 type(逐字符按键流与 X Draft.js 异步处理竞态, 实测中文丢字+乱序),改 eval + execCommand insertText 整段插入;新增发布前 MATCH 校验闸门与发布后 profile 终验;「Something went wrong」先过闸门判因 (MISMATCH 重插 / MATCH 瞬时错误重试),替换原「优先精简正文」误判。含草稿 回填重复、占位符假警报、emoji 渲染为 img 三个坑。 expert-bd comment-engagement:X 评论同套 CJK 禁 type 规则,复用 twitter-post 闸门 browser-guide:微信视频号与公众号同模式无头截 QR 发用户扫码登录,渲染失败才 --headed 兜底;连带修正 docs 两处 spec 的旧说法 --- .../expert-bd/workflows/comment-engagement.md | 2 +- .../tools/twitter-post/SKILL.md | 65 ++++++++++++++----- .../browser-stack-replacement-spec-2026-07.md | 4 +- docs/platform-login-and-browser-spec.md | 6 +- skills/browser-guide/SKILL.md | 4 +- 5 files changed, 57 insertions(+), 24 deletions(-) diff --git a/crews/main/skills/expert-bd/workflows/comment-engagement.md b/crews/main/skills/expert-bd/workflows/comment-engagement.md index 84d4ded7..7921cbee 100644 --- a/crews/main/skills/expert-bd/workflows/comment-engagement.md +++ b/crews/main/skills/expert-bd/workflows/comment-engagement.md @@ -36,7 +36,7 @@ ### Step 3: 逐内容互动 -对每个搜索到的内容,按配置的互动策略执行。通用要求:输入使用 `type` + `slowly: true`,不要用 `fill()`。 +对每个搜索到的内容,按配置的互动策略执行。通用要求:输入使用 `type` + `slowly: true`,不要用 `fill()`。**X/Twitter 例外**:评论/回复含中文/日文/韩文时**禁用 `type`**(camoufox-cli `type` 逐字符按键流与 X Draft.js 异步处理竞态,中文实测丢字+乱序)——按 `expert-twitter/tools/twitter-post/SKILL.md`「CJK 正文输入与校验闸门」改用 eval + `document.execCommand("insertText")` + 发布前校验 MATCH。X 回复应走平台表中 `twitter-post` Reply workflow,同样适用该闸门。 #### 策略 A:直接留言(direct_comment) diff --git a/crews/main/skills/expert-twitter/tools/twitter-post/SKILL.md b/crews/main/skills/expert-twitter/tools/twitter-post/SKILL.md index a383852f..1732756b 100644 --- a/crews/main/skills/expert-twitter/tools/twitter-post/SKILL.md +++ b/crews/main/skills/expert-twitter/tools/twitter-post/SKILL.md @@ -51,7 +51,35 @@ camoufox-cli --session twitter --persistent --headed --json open "https://x.com/ ## 通用约束 - 文件上传用 forked camoufox-cli 的 `upload` 命令(`camoufox-cli --session --persistent --json upload `,底层 Playwright `setInputFiles`,无需 DataTransfer hack) -- 正文输入使用 `type` + `slowly: true`,不要用 `fill()` +- 正文输入:**CJK(中文/日文/韩文)内容禁用 `type` 命令**——camoufox-cli `type` 逐字符按键流与 X 编辑器(Draft.js)异步处理存在竞态,中文实测丢字+乱序(2026-09-13 事故,首字被挪到结尾、中段整段消失,含分段 type+停顿仍错乱)。CJK 正文一律走下方「CJK 正文输入与校验闸门」的 eval + `document.execCommand("insertText")` 整段插入;纯 ASCII 短文本仍可用 `type`。**不要用 `fill()`** + +### CJK 正文输入与校验闸门 + +含中文 / 日文 / 韩文的正文必须走本节的 insertText 方案 + 校验闸门;纯 ASCII 短文本可用 `type`,但**发布前校验闸门**与**发布后终验**对**所有正文**强制执行。 + +**1. 清空回填草稿(open compose 后必做)**:X 重新打开 compose 页可能回填上次草稿,直接 insertText 会叠成两份: + +```bash +camoufox-cli --session twitter --json eval '(function(){var el=document.querySelector("[data-testid=tweetTextarea_0]");if(!el){return "NO_BOX";}el.focus();var s=document.execCommand("selectAll",false);var d=document.execCommand("delete",false);return (s&&d)?"CLEARED":"CLEAR_FAILED";})()' +``` + +**2. 插入正文**(CJK 用 insertText 整段插入;正文含单引号或反斜杠时先转义,避免破坏命令引号): + +```bash +camoufox-cli --session twitter --json eval '(function(){var el=document.querySelector("[data-testid=tweetTextarea_0]");if(!el){return "NO_BOX";}el.focus();var ok=document.execCommand("insertText",false,"<正文>");return ok?"INSERTED":"EXEC_FAILED";})()' +``` + +**3. 发布前校验闸门(强制——点击 Post / Reply 之前必须执行,非 MATCH 一律不发布)**: + +```bash +camoufox-cli --session twitter --json eval '(function(){var el=document.querySelector("[data-testid=tweetTextarea_0]");var t=el?el.innerText:"NO_BOX";var target="<正文>";return t===target?"MATCH":"MISMATCH:"+t;})()' +``` + +- **选择器必须精确匹配 `[data-testid=tweetTextarea_0]`**——`[data-testid^=tweetTextarea]` 前缀匹配会同时命中 `tweetTextarea_0_label`(占位符层),读到占位符文本、误报 MISMATCH。 +- **插入与校验必须分两次 eval 调用(间隔 ≥1s)**——写在同一 eval 里同步执行时,React 未及重渲染,innerText 会混入「What's happening?」占位符(占位符假警报)。MISMATCH 先看是否混有占位符再定性。 +- **emoji 是 `` 不是丢字**:✅ 等 emoji 在 DOM 里渲染为 ``,`innerText` 提取时只显示周围空格——校验按「剔除 img 节点后的文本」比对。 + +**4. 发布后终验(强化,所有发布流程共用)**:点 Post 后导航 profile 页,读最新推文 `[data-testid=tweetText]` innerText 与 status 链接,与目标正文比对(剔除 emoji img 因素)后才算发布成功;不符立即走删除流程(More → Delete → confirmationSheetConfirm)。 ### 字符计数规则(X 平台特殊) @@ -88,14 +116,16 @@ camoufox-cli --session twitter --persistent --headed --json open "https://x.com/ ``` 1. Navigate to https://x.com/compose/post 2. Wait for the compose box to load -3. Click into the text area and type the content +3. 按「通用约束 → CJK 正文输入与校验闸门」输入正文(CJK 走 insertText;纯 ASCII 可 type) - Plain text only (no Markdown) - Max 280 characters for standard accounts -4. Verify character count — trim if over limit -5. **立即点击 "Post" 按钮——不要等待用户确认!** -6. Wait for success confirmation (URL changes or "Your post was sent" toast) -7. Extract and report the post URL -8. **Parse stats**: +4. **发布前校验闸门**:eval 校验正文返回 MATCH(通用约束 step 3;非 MATCH 一律不发布) +5. Verify character count — trim if over limit +6. **立即点击 "Post" 按钮——不要等待用户确认!** +7. Wait for success confirmation (URL changes or "Your post was sent" toast) +8. **发布后终验**(通用约束 step 4):导航 profile 页比对最新推文正文,MATCH 才算发布成功;不符走删除重发 +9. Extract and report the post URL +10. **Parse stats**: - snapshot eval: `JSON.stringify({ retweet: document.querySelector('[data-testid="retweet"]')?.innerText, like: document.querySelector('[data-testid="like"]')?.innerText, @@ -103,7 +133,7 @@ camoufox-cli --session twitter --persistent --headed --json open "https://x.com/ view: document.querySelector('[href*="/analytics"]')?.innerText, permalink: window.location.href })` -9. Update frequency tracker +11. Update frequency tracker ``` --- @@ -115,7 +145,7 @@ camoufox-cli --session twitter --persistent --headed --json open "https://x.com/ 2. Wait for the compose box to load 3. Upload the image file using camoufox-cli upload(见下方选择器说明) 4. Wait for image upload to complete (thumbnail / "Media" group appears) -5. Click into the text area and type the caption +5. 按「通用约束 → CJK 正文输入与校验闸门」输入 caption(CJK 走 insertText);**发布前校验闸门**:eval 校验 MATCH 才点 Post(通用约束 step 3) - Plain text only (no Markdown) - Max 280 characters for standard accounts 6. **立即点击 "Post" 按钮——不要等待用户确认!** @@ -145,7 +175,7 @@ camoufox-cli --session twitter --persistent --json upload "[data-testid=fileInpu 2. Click the media icon 3. Upload the video file (MP4 recommended, max 512MB, max 2min 20sec) 4. Wait for video processing — this can take 30–120 seconds or more for larger files. Look for the thumbnail preview to confirm completion. -5. Click into the caption area and type the caption +5. 按「通用约束 → CJK 正文输入与校验闸门」输入 caption(CJK 走 insertText);**发布前校验闸门**:eval 校验 MATCH 才点 Post(通用约束 step 3) - Plain text only (no Markdown) - Max 280 characters for standard accounts 6. **立即点击 "Post" 按钮——不要等待用户确认!** @@ -160,11 +190,11 @@ camoufox-cli --session twitter --persistent --json upload "[data-testid=fileInpu ``` 1. Navigate to https://x.com/compose/post -2. Click into the compose box and type the first tweet +2. 按「通用约束 → CJK 正文输入与校验闸门」输入第一条推文(CJK 走 insertText) - Plain text only (no Markdown) - Max 280 characters for standard accounts 3. Click the "+" icon to add another tweet to the thread -4. Click into the new compose box and type the second tweet +4. 同上输入第二条推文(CJK 走 insertText),**发布前校验闸门**:每条 eval 校验 MATCH 才 Post all(通用约束 step 3) - Plain text only (no Markdown) - Max 280 characters for standard accounts 5. Repeat for each additional tweet @@ -185,7 +215,7 @@ camoufox-cli --session twitter --persistent --json upload "[data-testid=fileInpu - ⚠️ 区分 "Repost"(纯转推,无评论)vs "Quote"(引用+评论) 3. Compose box 打开,**已自动填入引用卡片** 4. Click into text area below the quoted card -5. Type your comment (max 280 chars) +5. 按「通用约束 → CJK 正文输入与校验闸门」输入评论(CJK 走 insertText);**发布前校验闸门**:eval 校验 MATCH(通用约束 step 3) 6. Verify character count 7. **立即点击 "Post" 按钮** 8. Wait for confirmation, report post URL @@ -206,7 +236,7 @@ camoufox-cli --session twitter --persistent --json upload "[data-testid=fileInpu 1. Navigate to source tweet URL(如 https://x.com/username/status/1234567890) 2. Click "Reply" icon(不是 reply 文本框) 3. Compose box 打开,**自动显示 reply context** -4. Type your reply (max 280 chars) +4. 按「通用约束 → CJK 正文输入与校验闸门」输入回复(CJK 走 insertText);**发布前校验闸门**:eval 校验 MATCH(通用约束 step 3) 5. Verify character count 6. **立即点击 "Reply" 按钮**(不是 "Post") 7. Wait for confirmation, report reply URL @@ -234,7 +264,7 @@ snapshot eval: document.querySelector('[data-testid="icon-verified"]') !== null ``` 1. Navigate to https://x.com/compose/post 2. Wait for compose box to load -3. Type content up to 25,000 chars +3. 按「通用约束 → CJK 正文输入与校验闸门」输入内容(CJK 走 insertText);**发布前校验闸门**:eval 校验 MATCH 才 Post all(通用约束 step 3) 4. **注意**:URL 仍 23 字符,Emoji 仍 2 字符 5. 按钮文字从 "Post" 变成 "**Post all**"(X 长帖是 1 个"post all"动作,但内容被服务端分页) 6. Click "Post all" @@ -325,7 +355,10 @@ snapshot eval: document.querySelector('[data-testid="icon-verified"]') !== null | Character limit exceeded (Premium 25K) | Trim or use thread | | Media upload fails | Retry once; check file format and size | | Upload strict mode violation (2 elements) | **用 `[data-testid=fileInput] >> nth=0` 消歧**(见 Workflow: Post with Image) | -| "Something went wrong, but don't fret" after Post | X 服务端瞬时错误。**优先精简正文**——这种情况大概率是字数超限(X 的字符计数规则与前端显示不完全一致,尤其 URL/emoji 计数偏差时实际超限但按钮未变灰)。先缩短正文再重试,而不是原样重发。重试流程:精简正文 → reload compose 页 → retype → reupload → re-click Post,最多 3 次。3 次仍失败才报告用户。 | +| "Something went wrong, but don't fret" after Post | **先过发布前校验闸门判因**:MISMATCH → 正文损坏/丢失,走清草稿 → insertText → MATCH 后重发;MATCH → X 服务端瞬时错误(实测与字数无关,第 3 次同内容重发成功),reload compose 页 → 清空回填草稿 → insertText → 复验 → re-click Post,**最多 3 次,每次重试必须重走校验闸门**。3 次仍失败才报告用户。仅当校验确认字数超限(URL=23 / emoji=2 计数偏差)时才精简正文。 | +| 重新 open compose 回填上次草稿 | 插入前先清空(focus → `execCommand("selectAll")` → `execCommand("delete")`),否则直接 insertText 会叠成两份 | +| 校验 innerText 混入 "What's happening?" 占位符 | 插入与校验必须**分两次 eval 调用(间隔 ≥1s)**——同次调用 React 未及重渲染会混入占位符;MISMATCH 先看是否混有占位符再定性 | +| 校验判读 emoji 缺失(✅ 变空格) | emoji 在 DOM 渲染为 ``,innerText 只显示周围空格——**不是丢字**;终验以 profile 页 `[data-testid=tweetText]` innerText + 时间线 `img` 节点共同判读 | | Rate limit error | **Wait 30 min minimum** (not 15) + check frequency tracker | | Post button greyed out | Content is empty or over limit — check before clicking | | Frequency tracker warns high-risk | Ask user: continue or defer to tomorrow? | diff --git a/docs/browser-stack-replacement-spec-2026-07.md b/docs/browser-stack-replacement-spec-2026-07.md index b0639dad..06f1fa4c 100644 --- a/docs/browser-stack-replacement-spec-2026-07.md +++ b/docs/browser-stack-replacement-spec-2026-07.md @@ -24,7 +24,7 @@ | 0 | **全线使用 forked camoufox-cli**,放弃 patchright 的 patch | | 1 | 涉及登录的自媒体平台,**每平台一个且只一个持久化 session**,必须顺次使用(fail-first 队列,见 §fork 改造清单) | | 2 | browser-guide 约定:需要用户配合过验证码的,**必须用 camoufox-cli 有头模式** | -| 3 | login-manager 约定:wx-mp 登录可无头启动截图发 QR;**wechat-channel(视频号)/ douyin / twitter / xhs(xhs-publish \| xhs-browse)/ weibo / zhihu / xianyu 登录必须 有头模式**(扫码登录页无法无头截 QR;有头扫码一律 `--viewport 1920x1080` 强制桌面比例,camoufox 默认移动端比例二维码看不全) | +| 3 | login-manager 约定:wx-mp 与 wechat-channel(视频号)登录可无头启动截图发 QR(2026-09-13 实测视频号无头 QR 可渲染,与 wx-mp 同模式);douyin / twitter / xhs(xhs-publish \| xhs-browse)/ weibo / zhihu / xianyu 登录仍必须 有头模式(扫码登录页无法无头截 QR;有头扫码一律 `--viewport 1920x1080` 强制桌面比例,camoufox 默认移动端比例二维码看不全) | | 4 | login-manager 导出 cookie **同时导出 UA**;所有用中央 cookie 的脚本导入 cookie 同时导入 UA | | 5 | **严禁浏览器方案导入 cookie**(登录失效必须重新登录流程,见 §profile 丢失处理) | | 6 | 恢复 twitter-interact 脚本操作模式(参考 AiToEarn 上游,见 §twitter-interact) | @@ -154,7 +154,7 @@ spike 文档 L30-33 已设计: | `douyin-publish` | 由脚本方案改为浏览器自动化方案:forked cli 持久化 session `douyin` + upload;有头登录 | | `weibo-publish` | forked cli 持久化 session `weibo` + upload;有头登录 | | `zhihu-publish` | forked cli 持久化 session `zhihu` + upload;有头登录 | -| `wechat-channels-publish` | forked cli 持久化 session `wechat-channel` + upload;有头手动扫码登录(`--headed --viewport 1920x1080`,视频号扫码页无法无头截 QR) | +| `wechat-channels-publish` | forked cli 持久化 session `wechat-channel` + upload;无头截图扫码登录(截 QR PNG 发用户扫码,渲染失败才 `--headed` 兜底) | | `viral-chaser` | 适配修改后的login-manager中央cookie格式,尤其是导入Cookie的时候,要同时导入UA。 | | `wx-mp-hunter` | 见 §6 收编 | | `xianyu-ops` | forked cli 持久化 session `xianyu`;有头登录 | diff --git a/docs/platform-login-and-browser-spec.md b/docs/platform-login-and-browser-spec.md index ae49eaf5..3b215eec 100644 --- a/docs/platform-login-and-browser-spec.md +++ b/docs/platform-login-and-browser-spec.md @@ -153,9 +153,9 @@ camoufox-cli --session wx_mp --persistent --json identity export ~/.openclaw/log **其他场景默认走 camoufox 持久化 session,不显式指定有头/无头**——camoufox-cli 默认行为即可(headless 是默认)。 -**browser-guide §1-B 那句「wechat-channel / wx-mp 可无头启动截图发 QR;douyin / twitter / xhs / weibo / zhihu / xianyu / reddit / youtube 登录必须有头模式」要改**: -- wx-mp 那个无头特例只属于 wx-mp-hunter/engagement 的自有体系,不属于 login-manager 体系,不应在 browser-guide 里和 wechat-channel 并列提。 -- wechat-channel(视频号)扫码登录页**无法无头截 QR**,必须 `--headed --viewport 1920x1080` 弹窗手动扫码(同 weibo / xianyu),按现行 wechat-channels-publish 技能自有 SKILL.md 走。 +**browser-guide §1-B 有头/无头规则(2026-09-13 实测修正)**: +- wx-mp 无头特例只属于 wx-mp-hunter/engagement 的自有体系,不属于 login-manager 体系,browser-guide 里与 wechat-channel 并列标注「按各自专家包约定走无头截 QR」。 +- wechat-channel(视频号)与 wx-mp 同模式:**无头截 QR 发用户扫码**(2026-09-13 实测无头二维码完整渲染可扫;此前「视频号无法无头截 QR、必须 --headed」说法是早期失败残留,同日有头窗口的二维码加载失败实为代理 fake-ip 拦截,与有头/无头模式无关)。按 wechat-channels-publish / wx-channel-engagement 技能自有 SKILL.md 的无头截图扫码登录流走;无头渲染失败才 `--headed` 兜底。 ## 8. published-track 流程 2A·自动更新(定时任务用)取数方案 diff --git a/skills/browser-guide/SKILL.md b/skills/browser-guide/SKILL.md index 2cb25045..daf4ec55 100644 --- a/skills/browser-guide/SKILL.md +++ b/skills/browser-guide/SKILL.md @@ -29,8 +29,8 @@ camoufox-cli --session [--persistent] [--headed] [--json] [args - **`--session `**:会话隔离单元,同名 session 共享一个 profile 目录。**涉及登录的平台用一个且只用一个持久化 session 名**。 - **`--persistent`**:冻结指纹到 `~/.camoufox-cli/profiles//camoufox-cli.json`(首次生成后冻结)。持久化平台 session 必带;临时性 session(新闻等不登录站点)**不带**——走默认临时 profile,每次随机指纹,关闭自清。 -- **`--headed`**:有头模式。**需要用户配合过验证码、扫码、收短信的,或者填表场景,必须 `--headed`**。例外:**微信公众号 wx_mp** 可无头截含二维码区域截图发用户登录;**微信视频号 wechat-channel / 微博 / 闲鱼等扫码登录页无法无头截 QR,必须 `--headed` 弹窗让用户在浏览器里手动扫码**。其他场景,包括探活,都可以使用默认的无头模式。 -- **`--viewport `**:固定窗口尺寸,如 `1920x1080`。camoufox 默认按指纹给**移动端窗口比例**,导致有头登录时二维码看不全;有头扫码登录(微博 / 闲鱼 / 视频号等)一律加 `--viewport 1920x1080` 强制桌面比例。业务无头操作无需此 flag。 +- **`--headed`**:有头模式。**需要用户配合过验证码、收短信的,或者填表场景,必须 `--headed`**。例外:**微信公众号 wx_mp 与微信视频号 wechat-channel** 可无头截图二维码发用户远程扫码(按各自专家包约定:截 QR PNG 发用户聊天窗口 → 用户手机扫码 → 轮询 URL 确认登录就位);无头下二维码渲染失败(等 10s 仍无 QR img、截图空白或「加载失败」)才 teardown 换 `--headed` 弹窗兜底。**微博 / 闲鱼等扫码登录页维持必须 `--headed`** 弹窗让用户在浏览器里手动扫码。其他场景,包括探活,都可以使用默认的无头模式。 +- **`--viewport `**:固定窗口尺寸,如 `1920x1080`。camoufox 默认按指纹给**移动端窗口比例**,导致有头登录时二维码看不全;`--headed` 兜底扫码登录(微博 / 闲鱼等)或窗口内容看不全时加 `--viewport 1920x1080` 强制桌面比例。业务无头操作无需此 flag。 - **`--json`**:命令输出走 JSON 信封(`{ok, ...}` / `{error, ...}`),agent 解析稳定,推荐常带。 - 命令集(含 `upload` / `identity export`): `open / back / forward / reload / url / title / close / snapshot / click / fill / type / select / check / hover / press / text / eval / screenshot / pdf / scroll / wait / tabs / switch / close-tab / sessions / cookies / install / upload / identity` From ee059ff47d16c2e852893a241d8fc049fcd65f68 Mon Sep 17 00:00:00 2001 From: codes-factory-of-bg Date: Mon, 14 Sep 2026 00:24:28 +0800 Subject: [PATCH 24/34] =?UTF-8?q?refactor(scripts):=20setup-crew=20?= =?UTF-8?q?=E5=8F=AA=E4=B8=BA=E5=AF=B9=E5=A4=96=20crew=20=E7=94=9F?= =?UTF-8?q?=E6=88=90=20ALLOWED=5FCOMMANDS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit internal → security:full,apply_exec_tiers 不读该文件(exec-tiers.sh 权限模型), 4b/4b.5 加 crew-type 闸门,避免对内 crew workspace 生成死文件并误导成受白名单约束。 闸门以 workspace SOUL.md 为准(与 4c 一致);SOUL.md 缺失时 resolve_crew_type 兜底 external 照常生成,保持 fail-closed。已存在的旧文件不删除(可能含用户 手工添加的私有条目,且运行时无副作用)。 --- scripts/setup-crew.sh | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/scripts/setup-crew.sh b/scripts/setup-crew.sh index 3add90fa..af168ae1 100755 --- a/scripts/setup-crew.sh +++ b/scripts/setup-crew.sh @@ -552,10 +552,13 @@ if [ -f "$CONFIG_PATH" ]; then echo " ✅ Agent skill filters synchronized" echo " ✅ openclaw.json updated" - # ─── 4b. 幂等同步模板 ALLOWED_COMMANDS → workspace ──────────────── + # ─── 4b. 幂等同步模板 ALLOWED_COMMANDS → workspace(仅对外 crew)── # 只注入模板中的 + 行(缺失时追加),不覆盖 workspace 已有条目(包括私有技能) + # 对内 crew 跳过:internal → security:full,apply_exec_tiers 不读 ALLOWED_COMMANDS(见 exec-tiers.sh 权限模型)。 + # crew-type 以 workspace SOUL.md 为准(与 4c 一致);SOUL.md 缺失时 resolve_crew_type 兜底 external,照常生成(fail-closed)。 while IFS=$'\t' read -r a_id a_ws; do [ -n "$a_id" ] || continue + [ "$(resolve_crew_type "$a_ws/SOUL.md")" = "external" ] || continue template_ac="$CREWS_DIR/$a_id/ALLOWED_COMMANDS" workspace_ac="$a_ws/ALLOWED_COMMANDS" [ -f "$template_ac" ] || continue @@ -573,14 +576,16 @@ if [ -f "$CONFIG_PATH" ]; then fi done < <(list_agent_workspaces) - # ─── 4b.5. 自动注入 skill scripts → ALLOWED_COMMANDS(幂等)── + # ─── 4b.5. 自动注入 skill scripts → ALLOWED_COMMANDS(幂等,仅对外 crew)── # 扫描每个 agent 的 skill 列表,将带 scripts/ 的技能脚本路径追加到 ALLOWED_COMMANDS。 # workspace-local skill → +./skills//scripts/(相对路径) # 全局 skill(openclaw/skills/)→ +(绝对路径) + # 对内 crew 跳过:internal → security:full,该文件无任何运行时消费者,生成只会误导成"受白名单约束"。 echo " 📝 Auto-injecting skill script commands into ALLOWED_COMMANDS..." while IFS=$'\t' read -r a_id a_ws; do [ -n "$a_id" ] || continue [ -d "$a_ws" ] || continue + [ "$(resolve_crew_type "$a_ws/SOUL.md")" = "external" ] || continue local_ac="$a_ws/ALLOWED_COMMANDS" # 读取该 agent 在 openclaw.json 中已写入的 skills 列表 From e16279a65690aa2ce69413a8efb4d6a71744a98f Mon Sep 17 00:00:00 2001 From: bigbrother666sh Date: Mon, 14 Sep 2026 21:58:31 +0800 Subject: [PATCH 25/34] update README --- README.md | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 5ab9f66f..da0411fb 100755 --- a/README.md +++ b/README.md @@ -35,14 +35,9 @@ xiaobei 由Wiseflow (原AI首席情报官)作者 bigbrother666sh 开发。 ## 🚀 **v5.7.1 更新** - 小红书、抖音、视频号 DNA系统升级到2.0架构,Let's do this like an expert! -- content producer 升级为专家系统,现在除了AIGC大片外,还可以复刻众多短视频平台流行的“套路”,摆脱“低创”、限流: - > 默认模式 - AIGC大片:小贝宣传片 - > - YouTube:https://youtu.be/eK8aWWCNVZQ?si=K-MbWai-j6ydqCdy - > - bilibili:https://www.bilibili.com/video/BV1euMR6PEDh - - > 套路一 - 贴纸人 - - > 套路二 - “万万没想到” +- content producer 升级为专家系统,现在除了AIGC大片外,还可以复刻众多短视频平台流行的“套路”,更易获得平台推荐流量: + > 效果展示,xiaobei的视频号:https://openclaw-for-business.com/xiaobei-wxchannel.jpg +- xiaobei 可直接指挥content producer,用户可选择将brief出具、节点验收等委托xiaobei - 修复一键安装脚本中,openclaw-weixin不会自动升级的问题 详见 [CHANGELOG.md](CHANGELOG.md) From f01a014842b7231689df71119bc1a65b8721ab24 Mon Sep 17 00:00:00 2001 From: codes-factory-of-bg Date: Mon, 14 Sep 2026 22:35:08 +0800 Subject: [PATCH 26/34] =?UTF-8?q?refactor(scripts):=20=E5=88=A0=E9=99=A4?= =?UTF-8?q?=20install.sh/install-atomgit.sh=20=E6=97=A7=E6=BA=90=E7=A0=81?= =?UTF-8?q?=E8=B7=AF=E7=BA=BF=E6=AD=BB=E4=BB=A3=E7=A0=81=20+=20=E5=A4=B4?= =?UTF-8?q?=E6=B3=A8=E9=87=8A=E5=AF=B9=E9=BD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tarball 路线 main() 从不调用系统依赖 bootstrap(install_node/install_git/ install_pnpm)与 git clone(clone_wiseflow/checkout_openclaw_at_pin)——这些是 tarball 定案前旧 curl-源码路线的遗留;58edf56 修的 install_pnpm 守卫实为死代码 (真生效的 pnpm 11 预检在 update.sh 步骤 1.5)。 - install.sh: 删 19 个零调用者函数(含 require_sudo/install_homebrew/node 版本 解析链等传递死代码)+ PNPM_VERSION/pnpm_real_version + USE_LOCAL/--use-local; NODE_MIN_VERSION/NODE_DEFAULT_MAJOR 从未定义,走到必在 set -u 下炸,铁证死代码 - install-atomgit.sh: 删同款死 helper(run_remote_bash/refresh_shell_command_cache/ is_root/require_sudo)+ --use-local no-op 分支 - 两脚本头注释:update.sh 描述修正(原写"拉新 tarball",实为 git clone 源码升级路线) - README: 删 --use-local 行(该 flag 由静默 no-op 变为明确 Unknown option) 验证:bash -n 通过、--help exit 0、全仓 grep 无残留引用;ps1 未动(本就 pnpm 11 原生)。 --- scripts/README.md | 1 - scripts/install-atomgit.sh | 39 +--- scripts/install.sh | 382 +------------------------------------ 3 files changed, 6 insertions(+), 416 deletions(-) diff --git a/scripts/README.md b/scripts/README.md index 554bab83..841d6291 100755 --- a/scripts/README.md +++ b/scripts/README.md @@ -53,7 +53,6 @@ irm https://raw.atomgit.com/wiseflow/xiaobei/raw/master/scripts/install-atomgit. | — | `XIAOBEI_TAG=` | 指定版本 tag(默认拉最新 release;sh 也认 `XIAOBEI_TAG` env) | | — | `XIAOBEI_TARBALL=` | 本地已下好的 tarball 路径,跳过下载(sh 也认此 env) | | `--verbose` | — | 打印 debug 输出(仅 sh) | -| `--use-local` | — | 复用 `WISEFLOW_ROOT` 已有本地 checkout,跳 fetch(仅 sh,开发/调试用) | 环境变量:`XIAOBEI_REPO`(仅 GitHub 线路认,atomgit 线路硬编码 `wiseflow/xiaobei`)、`XIAOBEI_TAG`(指定版本)、`XIAOBEI_TARBALL`(本地已下好的 tarball 路径,跳过下载)、`XIAOBEI_HOME`(程序目录覆盖)、`OPENCLAW_HOME`(运行数据目录覆盖)。 diff --git a/scripts/install-atomgit.sh b/scripts/install-atomgit.sh index b694a093..9d853ec5 100755 --- a/scripts/install-atomgit.sh +++ b/scripts/install-atomgit.sh @@ -8,9 +8,9 @@ # bash -c "$(curl -fsSL https://raw.atomgit.com/wiseflow/xiaobei/raw/master/scripts/install-atomgit.sh)" # bash -c "$(curl -fsSL https://raw.atomgit.com/wiseflow/xiaobei/raw/master/scripts/install-atomgit.sh)" -s -- [options] # -# 与 update.sh 区别: -# - install-atomgit.sh = 首装路线(拉预构建 tarball → pnpm install --prod → 交互收 AWK_API_KEY + daemon install,全程无需用户预装 Node/git/pnpm) -# - update.sh = 已装用户的升级路线(拉新 tarball → pnpm install --prod → daemon reload) +# 与 update.sh 区别(两条互不混用的分发路线): +# - install-atomgit.sh = tarball 路线首装(已装机器重跑即更新;拉预构建 tarball → pnpm install --prod → 交互收 AWK_API_KEY + daemon install,全程无需用户预装 Node/git/pnpm) +# - update.sh = git clone 源码用户的升级路线(git fetch + reset → checkout openclaw@pin → apply-addons.sh → pnpm build → daemon reload;需系统 Node/git/pnpm,pnpm 必须 11+) # # 执行流程: # 1. 检测 OS + arch → 选 tarball asset(linux-x64 / mac-arm64 / mac-x64 / win-x64) @@ -503,14 +503,6 @@ install_python_deps() { ui_success "Python deps done" } -run_remote_bash() { - local url="$1" - local tmp - tmp="$(mktempfile)" - download_file "$url" "$tmp" - /bin/bash "$tmp" -} - # ═══════════════════════════════════════════════════════════════════ # UI helpers # ═══════════════════════════════════════════════════════════════════ @@ -695,10 +687,6 @@ run_required_step() { exit 1 } -refresh_shell_command_cache() { - hash -r 2>/dev/null || true -} - is_promptable() { if [[ "$NO_PROMPT" == "1" ]]; then return 1 @@ -709,20 +697,6 @@ is_promptable() { return 1 } -is_root() { - [[ "$(id -u 2>/dev/null || echo 1)" -eq 0 ]] -} - -require_sudo() { - if is_root; then - return 0 - fi - if ! command -v sudo >/dev/null 2>&1; then - ui_error "sudo required but not available" - exit 1 - fi -} - # ═══════════════════════════════════════════════════════════════════ # camoufox-cli(Firefox 反指纹浏览器) # ═══════════════════════════════════════════════════════════════════ @@ -948,7 +922,6 @@ OPENCLAW_HOME="${OPENCLAW_HOME:-$HOME/.openclaw}" export OPENCLAW_STATE_DIR="$OPENCLAW_HOME" VERBOSE=0 NO_PROMPT=0 -USE_LOCAL=false FORCE_RUNTIME=false SKIP_WEIXIN_BIND=false SKIP_BROWSER=false @@ -971,12 +944,6 @@ parse_args() { FORCE_RUNTIME=true shift ;; - --use-local) - # 复用 WISEFLOW_ROOT 已有的本地 wiseflow checkout,跳 clone/fetch,保本地改动 - # 主要给开发/调试场景:在仓内跑 install.sh 验流程,不想被 fetch+reset 盖掉改动 - USE_LOCAL=true - shift - ;; --skip-bind) # 跳过末尾微信扫码绑定(CI/自动化或想后续手动绑) SKIP_WEIXIN_BIND=true diff --git a/scripts/install.sh b/scripts/install.sh index 93358909..5bc9b946 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -4,9 +4,9 @@ # 用法: # curl -fsSL https://raw.githubusercontent.com/TeamWiseFlow/xiaobei/master/scripts/install.sh | bash # -# 与 update.sh 区别: -# - install.sh = 首装路线(拉预构建 tarball → pnpm install --prod → 交互收 AWK_API_KEY + daemon install,全程无需用户预装 Node/git/pnpm) -# - update.sh = 已装用户的升级路线(拉新 tarball → pnpm install --prod → daemon reload) +# 与 update.sh 区别(两条互不混用的分发路线): +# - install.sh = tarball 路线首装(已装机器重跑即更新;拉预构建 tarball → pnpm install --prod → 交互收 AWK_API_KEY + daemon install,全程无需用户预装 Node/git/pnpm) +# - update.sh = git clone 源码用户的升级路线(git fetch + reset → checkout openclaw@pin → apply-addons.sh → pnpm build → daemon reload;需系统 Node/git/pnpm,pnpm 必须 11+) # # 执行流程: # 1. 检测 OS + arch → 选 tarball asset(linux-x64 / mac-arm64 / mac-x64 / win-x64) @@ -491,14 +491,6 @@ install_python_deps() { ui_success "Python deps done" } -run_remote_bash() { - local url="$1" - local tmp - tmp="$(mktempfile)" - download_file "$url" "$tmp" - /bin/bash "$tmp" -} - # ═══════════════════════════════════════════════════════════════════ # UI helpers # ═══════════════════════════════════════════════════════════════════ @@ -683,10 +675,6 @@ run_required_step() { exit 1 } -refresh_shell_command_cache() { - hash -r 2>/dev/null || true -} - is_promptable() { if [[ "$NO_PROMPT" == "1" ]]; then return 1 @@ -697,363 +685,6 @@ is_promptable() { return 1 } -is_root() { - [[ "$(id -u 2>/dev/null || echo 1)" -eq 0 ]] -} - -require_sudo() { - if is_root; then - return 0 - fi - if ! command -v sudo >/dev/null 2>&1; then - ui_error "sudo required but not available" - exit 1 - fi -} - -# ═══════════════════════════════════════════════════════════════════ -# Homebrew(mac 才用) -# ═══════════════════════════════════════════════════════════════════ -is_macos_admin_user() { - local groups - groups="$(id -Gn 2>/dev/null || true)" - if [[ "$groups" == *"admin"* ]]; then - return 0 - fi - return 1 -} - -print_homebrew_admin_fix() { - ui_error "Homebrew install requires an admin user" - echo "Add your user to the 'admin' group or run as admin: sudo dscl . -append /Users/$(id -un) GroupMembership admin" -} - -install_homebrew() { - if [[ "$OS" == "macos" ]]; then - if ! command -v brew &> /dev/null; then - if ! is_macos_admin_user; then - print_homebrew_admin_fix - exit 1 - fi - ui_info "Homebrew not found, installing" - run_quiet_step "Installing Homebrew" run_remote_bash "https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh" - - # Add Homebrew to PATH for this session - if [[ -f "/opt/homebrew/bin/brew" ]]; then - eval "$(/opt/homebrew/bin/brew shellenv)" - elif [[ -f "/usr/local/bin/brew" ]]; then - eval "$(/usr/local/bin/brew shellenv)" - fi - ui_success "Homebrew installed" - else - ui_success "Homebrew already installed" - fi - fi -} - -# ═══════════════════════════════════════════════════════════════════ -# Node.js -# ═══════════════════════════════════════════════════════════════════ -parse_node_version_components_for_binary() { - local node_bin="${1:-node}" - if ! command -v "$node_bin" &> /dev/null && [[ ! -x "$node_bin" ]]; then - return 1 - fi - local version major minor - version="$("$node_bin" -v 2>/dev/null || true)" - major="${version#v}" - major="${major%%.*}" - minor="${version#v}" - minor="${minor#*.}" - minor="${minor%%.*}" - - if [[ ! "$major" =~ ^[0-9]+$ ]]; then - return 1 - fi - if [[ ! "$minor" =~ ^[0-9]+$ ]]; then - return 1 - fi - echo "${major} ${minor}" - return 0 -} - -parse_node_version_components() { - if ! command -v node &> /dev/null; then - return 1 - fi - parse_node_version_components_for_binary node -} - -node_major_version() { - local version_components major minor - version_components="$(parse_node_version_components || true)" - read -r major minor <<< "$version_components" - if [[ "$major" =~ ^[0-9]+$ && "$minor" =~ ^[0-9]+$ ]]; then - echo "$major" - return 0 - fi - return 1 -} - -node_is_at_least_required() { - local version_components major minor - version_components="$(parse_node_version_components || true)" - read -r major minor <<< "$version_components" - if [[ ! "$major" =~ ^[0-9]+$ || ! "$minor" =~ ^[0-9]+$ ]]; then - return 1 - fi - if [[ "$major" -gt "$NODE_MIN_MAJOR" ]]; then - return 0 - fi - if [[ "$major" -eq "$NODE_MIN_MAJOR" && "$minor" -ge "$NODE_MIN_MINOR" ]]; then - return 0 - fi - return 1 -} - -prepend_path_dir() { - local dir="${1%/}" - if [[ -z "$dir" || ! -d "$dir" ]]; then - return 1 - fi - local current=":${PATH:-}:" - current="${current//:${dir}:/:}" - current="${current#:}" - current="${current%:}" - if [[ -n "$current" ]]; then - export PATH="${dir}:${current}" - else - export PATH="${dir}" - fi - refresh_shell_command_cache -} - -check_node() { - if command -v node &> /dev/null; then - NODE_VERSION="$(node_major_version || true)" - if node_is_at_least_required; then - ui_success "Node.js v$(node -v | cut -d'v' -f2) found" - return 0 - else - if [[ -n "$NODE_VERSION" ]]; then - ui_info "Node.js $(node -v) found, upgrading to v${NODE_MIN_VERSION}+" - else - ui_info "Node.js found but version could not be parsed; reinstalling v${NODE_MIN_VERSION}+" - fi - return 1 - fi - else - ui_info "Node.js not found, installing it now" - return 1 - fi -} - -install_node() { - if [[ "$OS" == "macos" ]]; then - ui_info "Installing Node.js via Homebrew" - if ! run_quiet_step "Installing node@${NODE_DEFAULT_MAJOR}" brew install "node@${NODE_DEFAULT_MAJOR}"; then - echo "Re-run with --verbose or run 'brew install node@${NODE_DEFAULT_MAJOR}' directly, then rerun the installer." - exit 1 - fi - brew link "node@${NODE_DEFAULT_MAJOR}" --overwrite --force 2>/dev/null || true - ui_success "Node.js installed" - elif [[ "$OS" == "linux" ]]; then - require_sudo - ui_info "Installing Node.js on Linux" - # 走 NodeSource 官方安装脚本(稳定跨发行版) - if ! run_quiet_step "Installing Node.js ${NODE_DEFAULT_MAJOR}.x via NodeSource" \ - run_remote_bash "https://deb.nodesource.com/setup_${NODE_DEFAULT_MAJOR}.x"; then - ui_error "NodeSource setup script failed" - exit 1 - fi - if command -v apt-get &> /dev/null; then - run_required_step "Installing nodejs" apt-get install -y nodejs - elif command -v dnf &> /dev/null; then - run_required_step "Installing nodejs" dnf install -y nodejs - elif command -v yum &> /dev/null; then - run_required_step "Installing nodejs" yum install -y nodejs - else - ui_error "Unsupported Linux distribution for Node.js auto-install" - echo "Install Node.js ${NODE_DEFAULT_MAJOR} manually then rerun." - exit 1 - fi - ui_success "Node.js installed" - else - ui_error "Unsupported OS for Node.js install: $OS" - exit 1 - fi - - if ! node_is_at_least_required; then - local active_path active_version - active_path="$(command -v node 2>/dev/null || echo "not found")" - active_version="$(node -v 2>/dev/null || echo "missing")" - ui_error "Installed Node.js must be v${NODE_MIN_VERSION}+ but this shell is using ${active_version} (${active_path})" - exit 1 - fi - ui_success "Node.js v$(node -v | cut -d'v' -f2) ready" -} - -# ═══════════════════════════════════════════════════════════════════ -# Git -# ═══════════════════════════════════════════════════════════════════ -check_git() { - if command -v git &> /dev/null; then - ui_success "Git already installed" - return 0 - fi - return 1 -} - -install_git() { - if [[ "$OS" == "macos" ]]; then - install_homebrew - run_quiet_step "Installing Git" brew install git - elif [[ "$OS" == "linux" ]]; then - require_sudo - if command -v apt-get &> /dev/null; then - run_required_step "Installing git" apt-get install -y git - elif command -v dnf &> /dev/null; then - run_required_step "Installing git" dnf install -y git - elif command -v yum &> /dev/null; then - run_required_step "Installing git" yum install -y git - elif command -v apk &> /dev/null; then - run_required_step "Installing git" apk add --no-cache git - else - ui_error "Unsupported Linux distribution for git auto-install" - exit 1 - fi - fi - ui_success "Git installed" -} - -# ═══════════════════════════════════════════════════════════════════ -# pnpm -# ═══════════════════════════════════════════════════════════════════ -# 与 openclaw/package.json 的 packageManager pin 保持同步(apply-addons.sh 的依赖 -# 同步用了 pnpm 11 的 install CLI flags——如 --fetch-retries,pnpm 10.x 不认会炸)。 -# 升级 openclaw 换 pin 时此处要跟着改。 -PNPM_VERSION="${OPENCLAW_PNPM_VERSION:-11.2.2}" - -# 在无 packageManager pin 的目录下探测 pnpm 真实安装版本。 -# pnpm ≥11 默认开 manage-package-manager-versions,在带 pin 的目录里 -# `pnpm --version` 返回 pin 版本而非实际安装的版本,版本判断会被骗。 -pnpm_real_version() { - (cd / && pnpm --version 2>/dev/null) || true -} - -install_pnpm() { - local required_major installed_major - required_major="${PNPM_VERSION%%.*}" - installed_major="$(pnpm_real_version | cut -d. -f1)" - if command -v pnpm >/dev/null 2>&1 && [ "${installed_major:-0}" -ge "$required_major" ]; then - ui_success "pnpm already installed ($(pnpm_real_version || echo unknown))" - return 0 - fi - if command -v pnpm >/dev/null 2>&1; then - ui_info "Upgrading pnpm to ${PNPM_VERSION} (found $(pnpm_real_version || echo unknown); openclaw 工作区需要 pnpm ${required_major}+)" - else - ui_info "Installing pnpm@${PNPM_VERSION} globally" - fi - # 用 corepack 路线(与 openclaw 仓 packageManager 对齐,最稳) - if command -v corepack >/dev/null 2>&1; then - run_required_step "Enabling corepack" corepack enable - run_required_step "Preparing pnpm@${PNPM_VERSION}" corepack prepare "pnpm@${PNPM_VERSION}" --activate - else - # corepack 不可用回退 npm 全局装(走阿里云镜像,国内用户裸跑 npm registry 慢得离谱) - run_required_step "Installing pnpm via npm" npm install -g "pnpm@${PNPM_VERSION}" --registry=https://registry.npmmirror.com - fi - if ! command -v pnpm >/dev/null 2>&1; then - ui_error "pnpm install failed" - exit 1 - fi - ui_success "pnpm ready ($(pnpm --version))" -} - -# ═══════════════════════════════════════════════════════════════════ -# wiseflow clone + checkout openclaw -# ═══════════════════════════════════════════════════════════════════ -# 三个分支: -# 1. --use-local + WISEFLOW_ROOT 已是 wiseflow 仓 → 直接复用,跳 clone/fetch(保本地改动) -# 2. WISEFLOW_ROOT 已是 wiseflow 仓但未开 --use-local → fetch + reset --hard origin/master(覆盖本地改动) -# 3. WISEFLOW_ROOT 不存在 → git clone -clone_wiseflow() { - local target="$WISEFLOW_ROOT" - - # 分支 1:本地复用 - if [[ "$USE_LOCAL" == "true" && -d "$target/.git" ]]; then - ui_success "Using local wiseflow checkout at $target (--use-local, skipping clone/fetch)" - # 验下基本结构,免得跑下去 apply-addons 段才炸 - if [[ ! -f "$target/scripts/apply-addons.sh" || ! -d "$target/openclaw" ]]; then - ui_error "$target is a git checkout but missing scripts/apply-addons.sh or openclaw/ subdir" - exit 1 - fi - return 0 - fi - - # 分支 2:已是仓但没开 --use-local,fetch + reset 走升级路线 - if [[ -d "$target/.git" ]]; then - ui_warn "wiseflow already cloned at $target" - if [[ "$USE_LOCAL" != "true" ]]; then - ui_warn "Fetching + resetting to origin/master — THIS WILL DISCARD LOCAL CHANGES" - ui_warn "Pass --use-local to preserve local working tree" - run_quiet_step "Fetching latest wiseflow" git -C "$target" fetch origin master - run_required_step "Resetting to origin/master" git -C "$target" reset --hard origin/master - fi - return 0 - fi - - # 分支 3:全新 clone - if [[ -d "$target" ]]; then - ui_error "$target exists but is not a git checkout; refusing to overwrite" - echo "Move or remove it, then rerun." - exit 1 - fi - run_required_step "Cloning wiseflow repo" git clone "$WISEFLOW_REPO" "$target" - ui_success "wiseflow cloned to $target" -} - -checkout_openclaw_at_pin() { - local target="$WISEFLOW_ROOT" - local version_file="$target/openclaw.version" - local openclaw_dir="$target/openclaw" - - if [[ ! -f "$version_file" ]]; then - ui_error "openclaw.version missing in cloned wiseflow repo" - exit 1 - fi - - # shellcheck source=/dev/null - source "$version_file" - if [[ -z "$OPENCLAW_COMMIT" ]]; then - ui_error "OPENCLAW_COMMIT not set in openclaw.version" - exit 1 - fi - - ui_info "openclaw target: ${OPENCLAW_VERSION:-unknown} (${OPENCLAW_COMMIT})" - - if [[ ! -d "$openclaw_dir/.git" ]]; then - run_required_step "Cloning openclaw upstream" git clone https://github.com/openclaw/openclaw.git "$openclaw_dir" - fi - - local current_commit - current_commit="$(git -C "$openclaw_dir" rev-parse HEAD 2>/dev/null || echo "")" - if [[ "$current_commit" = "$OPENCLAW_COMMIT" ]]; then - ui_success "openclaw already at target commit" - return 0 - fi - - # reset 上游到干净状态(之前可能 apply 过 patches) - git -C "$openclaw_dir" reset --hard HEAD 2>/dev/null || true - git -C "$openclaw_dir" clean -fd 2>/dev/null || true - - if ! git -C "$openclaw_dir" cat-file -e "${OPENCLAW_COMMIT}^{tree}" 2>/dev/null; then - ui_info "Fetching openclaw target commit" - run_required_step "Fetching openclaw commit" git -C "$openclaw_dir" fetch origin "$OPENCLAW_COMMIT" - fi - run_required_step "Checking out openclaw@pin" git -C "$openclaw_dir" checkout "$OPENCLAW_COMMIT" - ui_success "openclaw checked out at ${OPENCLAW_VERSION:-unknown}" -} - # ═══════════════════════════════════════════════════════════════════ # camoufox-cli(Firefox 反指纹浏览器) # ═══════════════════════════════════════════════════════════════════ @@ -1279,7 +910,6 @@ OPENCLAW_HOME="${OPENCLAW_HOME:-$HOME/.openclaw}" export OPENCLAW_STATE_DIR="$OPENCLAW_HOME" VERBOSE=0 NO_PROMPT=0 -USE_LOCAL=false FORCE_RUNTIME=false SKIP_WEIXIN_BIND=false SKIP_BROWSER=false @@ -1302,12 +932,6 @@ parse_args() { FORCE_RUNTIME=true shift ;; - --use-local) - # 复用 WISEFLOW_ROOT 已有的本地 wiseflow checkout,跳 clone/fetch,保本地改动 - # 主要给开发/调试场景:在仓内跑 install.sh 验流程,不想被 fetch+reset 盖掉改动 - USE_LOCAL=true - shift - ;; --skip-bind) # 跳过末尾微信扫码绑定(CI/自动化或想后续手动绑) SKIP_WEIXIN_BIND=true From b1b9711fd24da981ad2aa8d72f66b1b898ddc422 Mon Sep 17 00:00:00 2001 From: codes-factory-of-bg Date: Mon, 14 Sep 2026 23:36:42 +0800 Subject: [PATCH 27/34] =?UTF-8?q?docs(skills):=20wechat-channels-publish?= =?UTF-8?q?=20=E5=AE=9E=E6=B5=8B=E8=B7=AF=E5=BE=84=E5=9B=9E=E5=86=99?= =?UTF-8?q?=E2=80=94=E2=80=94wujie=20snapshot=20=E4=BD=9C=E7=94=A8?= =?UTF-8?q?=E5=9F=9F/=E5=B0=81=E9=9D=A2=E4=B8=8A=E4=BC=A0/=E6=8F=8F?= =?UTF-8?q?=E8=BF=B0=E6=A1=86=20eval=20=E8=B7=AF=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - snapshot 一律 -s "wujie-app"(整页 snapshot 撞 wujie 双 body strict violation) - Step 3 视频上传改直连 input[type=file] 选择器(click 触发按钮路线拿不到 ref) - 新增 Step 5 设置封面:编辑入口/上传封面/accept*=image 精确选择器(双 file input 防冲突)/裁剪确认 - Step 6 描述框改 eval 四步(contenteditable 空串致 aria 无独立 ref):聚焦→insertText→insertParagraph→innerText 校验 - 短标题 fill + eval 读 .value 校验(aria 占位文本填后不消失,snapshot 复核不可信) - 新增 4 个 pitfall + 错误处理表 3 行;原 Step 5-8 顺延为 6-9,内部交叉引用同步 - content-production.md Step 6 发布清单补「设置封面」项 来源:小贝 2026-09-14 实战 proposal(expert-wx-channel-proposal.md 提案一/二), eval 片段全部取自当日发布实录并通过 node --check --- .../tools/wechat-channels-publish/SKILL.md | 152 +++++++++++++----- .../workflows/content-production.md | 9 +- 2 files changed, 119 insertions(+), 42 deletions(-) diff --git a/crews/main/skills/expert-wx-channel/tools/wechat-channels-publish/SKILL.md b/crews/main/skills/expert-wx-channel/tools/wechat-channels-publish/SKILL.md index d1d82e88..0aca54fd 100644 --- a/crews/main/skills/expert-wx-channel/tools/wechat-channels-publish/SKILL.md +++ b/crews/main/skills/expert-wx-channel/tools/wechat-channels-publish/SKILL.md @@ -1,13 +1,15 @@ --- name: wechat-channels-publish -description: 通过 camoufox-cli 持久化 session wechat-channel 发布视频到微信视频号,支持视频上传、视频描述与短标题填写、即时发布。 +description: 通过 camoufox-cli 持久化 session wechat-channel 发布视频到微信视频号,支持视频上传、封面上传、视频描述与短标题填写、即时发布。 --- # wechat-channels-publish — 工具说明 > 本文是 `expert-wx-channel` 专家包内的工具说明书,不独立出现在技能列表中。由相关 Workflow 指引调用。 -通过 **camoufox-cli** 持久化 session `wechat-channel`(有且只有一个,fail-first 队列:同 session 已有命令在跑时新命令直接 fail)在微信视频号创作者中心发布视频。视频号创作者中心使用 **wujie 微前端**,所有表单元素在 `::shadow-root` 内——camoufox-cli 的 `snapshot` 默认穿透 shadow DOM 拿 ref,后续 `click` / `type` / `upload` 按 ref 操作即可,无需 CDP hack。 +通过 **camoufox-cli** 持久化 session `wechat-channel`(有且只有一个,fail-first 队列:同 session 已有命令在跑时新命令直接 fail)在微信视频号创作者中心发布视频。视频号创作者中心使用 **wujie 微前端**,所有表单元素在 `::shadow-root` 内。 + +**业务页 `snapshot` 一律加 `-s "wujie-app"` 作用域**(下文简写 `snapshot -s`):wujie 页面里主文档与子应用各有一个 body,整页 `snapshot` 会报 `locator('body') resolved to 2 elements` strict violation,拿不到任何 ref。只有登录页(无 wujie)可整页 snapshot。`click` / `fill` / `type` 按 ref 操作;`upload` 支持 ref 或 CSS 选择器(底层 Playwright setInputFiles,穿透 shadow DOM),无需 CDP hack。 **输入**:本地视频文件(`.mp4` / `.mov` / `.avi` / `.webm`)、**视频描述**(含话题标签,最长约 300 字)、**短标题**(6-16 字)。发布页改版后两项都能填,官方明确「填写短标题会获得更多流量」,因此**两项都必须填**,都由 main agent 拟定后交给本工具。 **输出**:视频号已发布作品;能取到时附带公开链接(`https://weixin.qq.com/sph/xxxx`)。 @@ -46,90 +48,137 @@ camoufox-cli --session wechat-channel --persistent --json open "https://channels ### Step 2: 检查登录态 -`snapshot` 看页面 URL 是否含 `login` 或出现登录二维码——命中走前置条件的无头截图扫码登录流。 +``` +camoufox-cli --session wechat-channel --json url +``` + +URL 含 `login` → 走前置条件的无头截图扫码登录流。需要看页面内容时:登录页可整页 `snapshot`;发布页(wujie 已加载)必须 `snapshot -s "wujie-app"`。 ### Step 3: 上传视频 ``` -1. snapshot 拿到上传触发按钮 ref(shadow DOM 内的 span.add-icon 或 div.upload-content) -2. camoufox-cli --session wechat-channel --persistent --json click <上传触发-ref> -3. snapshot 拿到弹出的 ref -4. camoufox-cli --session wechat-channel --persistent --json upload - - camoufox-cli upload 命令底层走 Playwright setInputFiles,穿透 shadow DOM,无需 CDP setFileInput / base64 hack +camoufox-cli --session wechat-channel --persistent --json upload "input[type=file]" ``` +- 发布页常驻一个隐藏 ``,**直接用选择器 upload,不要先 click 上传按钮**——click 触发按钮后 snapshot 里并不会出现 file input ref,「click → snapshot 拿 ref → upload ref」路线走不通。 +- 此时页面只有这一个 file input,裸选择器 `input[type=file]` 唯一命中。**封面弹窗打开后页面会有两个 file input**,裸选择器 strict violation——视频上传必须在打开封面弹窗之前完成(封面见 Step 5)。 + ### Step 4: 等待上传+转码完成 -每 3 秒 `snapshot` 检查一次页面状态: +每 3 秒 `snapshot -s "wujie-app"` 检查一次页面状态: - 上传中:shadow DOM 内存在 `[class*="uploading"]` 或 `[class*="progress"]` - 转码中:`[class*="transcoding"]` - 完成:出现 `