关于 explainer
explainer 是一项将 URL 转为约 60–80 秒讲解视频的 Skill。它适用于产品页面、文档、文章和 GitHub 仓库,并为 GitHub 链接提供仓库识别流程。
主要功能
- 01在浏览器中浏览指定 URL
- 02生成旁白并制作头像口型同步
- 03按节拍表安排内容并定向缩放
- 04默认添加字幕
- 05扫描 GitHub 仓库并检测演示链接
适用场景
- 01用视频讲解产品页面
- 02带观众浏览文档或文章
- 03介绍 GitHub 仓库及其在线演示
MCP 相关
该 Skill 通过 MCP 调用生成头像和图像、分析媒体、合成语音、制作口型同步、添加字幕、合成视频、检查余额及轮询任务状态。它没有说明会向 MCP 客户端暴露 MCP 服务。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ pika-labs/ pika-plugins --skill explainer安装后在 Claude Code 中输入/explainer确认已加载。
详细介绍
/pika:explainer
生成一段约 60–80 秒的 URL 解说视频:按照节拍表时间线,让真实浏览器访问该 URL,为旁白生成数字人对口型效果,并将所有内容合成到 1280×800 的 macOS Sonoma 画面中。画面内数字人为 240 像素,包含 3px 白色描边环的外框为 246 像素,画布坐标为 (20, 476),并在每个中段节拍上对目标元素进行缩放。适用于任何 URL,包括产品页面、文档网站、博客文章和发布页面。GitHub URL 会启用仓库感知模式(扫描 README + 检测在线演示);其他 URL 均使用通用页面浏览流程。
用法: /pika:explainer <url> [--focus "angles"] [--avatar <url>] [--voice <id>] [--lipsync-provider pika|kling] [--preview] [--live-url <url>]
费用透明度关卡
调用任何付费 MCP 工具前,调用一次 identity_balance({verbose: true})。告知用户当前余额、近期消耗速度和剩余可用时间,然后用以下原文提示,等待用户确认:
预计费用:约 50-500 积分(约 $0.50-$5.00),具体取决于对口型服务提供商、旁白、字幕和预览模式。费用可能达到 $5,请回复
proceed继续,或回复cancel停止。
在用户回复 proceed 前,不得调用任何付费 MCP 工具。如果用户回复 cancel,则停止,不生成任何内容。必须在确认 URL 和可选标志后、生成数字人、语音、对口型效果、字幕或合成视频前,执行此关卡。
行为
默认设置——快速开始,不在流程中进行不必要的确认
- 静默确定数字人 / 语音。 开始处理前,绝不询问“要使用你的数字人吗?”或“要用哪个语音?”。如果用户提供了显式覆盖选项(
--avatar、--voice),则遵循这些选项;否则生成一个演示者数字人并选择默认语音,然后继续。完整的确定流程请参阅步骤 1。 - 只有费用透明度关卡需要用户回复
proceed。 显式指定数字人时,步骤 5 的预览通常需要通过--preview主动启用;但如果数字人来源是generated或regenerated回退方案,则必须自动预览。费用关卡通过后,流程会端到端运行,只有这一必需的回退数字人预览保护措施例外。 - 也不要询问用户是否提供
--focus。 根据页面结构大胆尝试;如果切入角度不理想,用户可以使用--focus "X"重新运行。
这些默认设置符合媒体生成工具(Midjourney / Sora / Runway / HeyGen / Pika.art)的行业标准:提交 → 渲染 → 返回。账户积分余额和服务提供商故障转移(步骤 9)是标准保护措施。
Claude Desktop 上的本地数字人图像
Claude Desktop 目前无法将直接粘贴的图像传递给 MCP 工具(Anthropic 方面的限制)。如果用户直接粘贴了照片,或提到想用作 --avatar 的本地文件,请暂停步骤 1,并友好地发送类似以下内容的提示:
提醒一下——Claude Desktop 目前还无法将粘贴的图像传递给 MCP 工具(Anthropic 的限制)。数字人图像有两个简单的选项:
- 粘贴 URL,如果图像已经托管在网上(Imgur、S3 或你的网站)——这是最快的方式
- 附加图像文件,我就可以在生成前上传。
收到本地文件后,使用 upload_asset 将其转换为公开 URL,并在步骤 1 前将返回的 public_url 用作 --avatar <url>。已有的 https://... URL 可直接使用,无需进行上述操作。如果用户完全没有提供数字人图像,则静默生成演示者肖像(步骤 1)。
步骤 0 — 解析 URL(空参数菜单)
从 $ARGUMENTS 中移除标志(--focus、--avatar、--voice、--live-url、--lipsync-provider、--no-captions、--preview、--skip-preview、--yes)和 key=value 参数。如果剩余内容中没有 https://... URL(或内容为空/仅包含空白字符),请将此菜单原样作为完整回复打印,然后停止并等待用户的下一条消息。此处调用工具可能会导致记录或解释错误的页面。如果 $ARGUMENTS 中已包含 URL,则静默跳过此步骤并继续步骤 1。
你想让我带你浏览哪个 URL? 以下类型均可:
- GitHub 仓库 — 例如
https://github.com/anthropics/claude-code(启用仓库感知模式:扫描 README + 检测在线演示)- 产品页面/发布页面 — 例如
https://pika.art- 文档网站 — 例如
https://docs.anthropic.com- 博客文章/文章 URL
输出:1280×800 macOS Sonoma 画面,左下角带有口型同步的头像,并在每个中段节点对目标元素进行缩放。默认流程在成本门控后端到端运行;如果指定了头像且想进行 3 秒口型同步检查,请传入
--preview。自动生成/重新生成的备用头像会自动运行此预览保护流程。回复 URL,我就会开始。
提示:无需输入
/pika:explainer——只需说“带我了解 ”“为 制作演示视频”或“解释这个仓库:”之类的话,我就会自动启动此技能。
用户回复 URL 后,将其视为已解析的输入并继续步骤 1。不要再次询问。
步骤 1 — 解析输入 + 检测模式
必需:url(必须是 https://...)。
可选:--avatar <url>(演示者照片;若省略则自动生成)、--voice <minimax-voice-id>、--focus "..."(融入 vo_text 的编辑指导)、--live-url <url>(强制指定在线演示 URL——仅限 GitHub 模式)、--lipsync-provider <pika|kling>(默认为 pika——parrot a2v,耗时约 2-5 分钟,头部动作略显夸张。传入 kling 可获得更紧凑、以面部为中心的输出,耗时约 5-30 分钟——Kling 生成的演示者镜头头部动作极少,但这是耗时最长的阶段;请留给高风险渲染使用)、--no-captions(跳过步骤 11 的字幕烧录——默认开启字幕)、--preview(选择启用显式头像的步骤 5 预览门控;自动生成和重新生成的备用头像会在完整口型同步前自动运行预览门控)。--skip-preview 和 --yes 会被接受,但不执行任何操作,以保持向后兼容。
模式检测:
- GitHub 模式 — URL 主机为
github.com,且路径符合/{owner}/{repo}(仓库根目录之后没有其他路径段)。启用仓库感知增强功能:扫描 README、检测在线演示,以及使用 GitHub 专用选择器。 - 通用 URL 模式 — 其他所有情况(产品页面、文档网站、博客文章、GitHub 更深层路径,如
/blob/HEAD/path)。跳过 GitHub 增强功能;使用通用 CSS 选择器并浏览该 URL 本身。
头像解析(静默执行——绝不询问用户):
- 如果传入了
--avatar <url>,则使用该 URL。 - 否则调用一次
generate_image,提示词为"professional presenter, friendly tech narrator, studio portrait, 1:1, natural lighting",并使用返回的 URL。不要询问用户“是否要生成头像?”——直接静默生成。
将 avatar_source 记录为 explicit、generated 或 regenerated 之一。
头像适用性门控(任何口型同步费用发生前必须执行):
在步骤 4 的 TTS 之前,以及任何 generate_lipsync 预览/完整调用之前,对解析出的头像图像调用一次 analyze_media(media=<avatar>, query=<gate_query>)。这是唯一必须分析头像的情况,因为头像是核心演示素材,生成质量不佳的头像可能会浪费整个渲染的成本。
门控查询:
Return JSON only: {
"is_single_front_facing_coherent_human_face": boolean,
"has_visible_mouth": boolean,
"is_faceless_or_masked": boolean,
"is_mascot_illustration_or_non_human": boolean,
"face_suitability_score": 0-100,
"apparent_gender": "female" | "male" | "unclear",
"reason": string
}
Is this image suitable for talking-head lipsync: a single front-facing coherent human face with a visible mouth? Flag faceless, masked, mascot, illustration-only, non-human, or distorted avatars.
仅当 is_single_front_facing_coherent_human_face == true、has_visible_mouth == true、is_faceless_or_masked == false、is_mascot_illustration_or_non_human == false 且 face_suitability_score >= 75 时才通过。
如果头像未通过,且 avatar_source 为 explicit,则在付费口型同步前停止,并请求提供 --avatar <real-looking-photo-url> 或允许生成演讲者肖像。如果头像未通过,且 avatar_source 为 generated,则使用提示词 "realistic professional presenter portrait, single front-facing coherent human face, visible mouth, friendly tech narrator, neutral studio background, 1:1, natural lighting" 调用一次 generate_image;将 avatar_source 设为 "regenerated",并对重新生成的头像重新运行此适用性检查。如果重新生成的头像也未通过,则明确报错并中止,不要尝试口型同步。
当 avatar_source 为 generated 或 regenerated 时,设置 avatar_auto_preview_required = true;否则,除非用户传入了 --preview,否则设为 false。
语音选择(静默执行——绝不询问用户):
-
如果传入了
--voice <id>,则使用该语音。 -
否则,根据已确定头像的表观性别,选择一个随性的 MiniMax
speech-2.8-hd预设:- 女性风格头像 →
English_PlayfulGirl(温暖、随性、明显为女性声线——已验证) - 男性风格头像 →
English_Jovialman(温暖、随性的男性声线) - 性别不明 / 性别中性 →
English_Jovialman(默认)
根据头像适用性检查结果(
apparent_gender)推断性别。不要询问用户。不要使用
English_FriendlyPerson——尽管 MiniMax 目录将其归类为“女性”语音,但其显示名称是“Friendly Guy”,播放时听起来是男性声线。English_PlayfulGirl是标准的随性女性声线选择。其他已验证的女性声线备选项:English_Upbeat_Woman、English_LovelyGirl、English_radiant_girl。 - 女性风格头像 →
以下流程按步骤标注:仅限 GitHub、仅限通用模式或两种模式。
步骤 2 — 阅读源内容(不调用 MCP)
**两种模式:**使用 Claude 的 WebFetch 获取输入 URL 的页面主要内容(h1、主视觉区、标题、主要文案)。
在阅读源内容时构建 proper_noun_glossary。纳入 URL/域名、页面标题、h1、README 标题、仓库元数据、包名和重复出现的首字母大写词中的产品、仓库、公司、模型、框架及专有名词的规范拼写。对于 GitHub 仓库,请保留 README/源代码扫描中出现的确切拼写,例如 Ollama、Llama、DeepSeek、Gemma。稍后撰写旁白和烧录手动字幕时使用此词汇表,避免字幕文本因语音转写而出现“Olama”或“DeepSeq”之类的拼写错误。
**GitHub 模式补充:**还要获取顶层文件树、(尽力获取)package.json/pyproject.toml,以及 GitHub API 仓库元数据(通过 gh api repos/{owner}/{repo} 获取 homepage、description、language、topics)。按以下优先级检测候选 live_url:
- 用户提供的
--live-url。 - GitHub API 的
meta.homepage字段——当维护者在 GitHub 设置中配置仓库主页时,此字段会有值。 package.json的"homepage"字段。- README 中第一个匹配以下正则表达式的 URL:
https?://[^\s)\"'<>]+(?:vercel\.app|netlify\.app|github\.io|fly\.dev|railway\.app|render\.com|herokuapp\.com|surge\.sh)[^\s)\"'<>]*。 - **README 中徽章区域/“Live Demo”/“Project Page”/“Demo”文字指向的任何其他 URL。**上面的允许列表正则会漏掉任意自定义域名(例如
<project>-project-page.com);如果 README 明确指定了项目页面,应优先选择该页面,而不是 github.io 回退地址。 - GitHub Pages 约定地址
https://{owner}.github.io/{repo}——但仅当深层文件树中包含前端信号(index.html、App.tsx、App.jsx、App.vue、app.py、main.py中的一个)时才使用。
如果没有可用的候选地址,分镜表跳过第 6–7 个镜头。
**通用 URL 模式:**镜头只浏览输入 URL,不推断 live_url,也不额外获取元数据。跳过步骤 2.5 和步骤 3.0;直接进入步骤 3。
步骤 2.5 — 验证 live_url 是否可访问(仅限 GitHub 模式,不调用 MCP)
如果已选定候选 live_url,请在编写第 6–7 个镜头前,验证它能否提供实际内容。对候选地址使用 WebFetch 并检查响应:
- 如果响应状态为 4xx/5xx,将
live_url设为 None,并跳过第 6–7 个镜头。尤其是 github.io 回退地址:虽然主机名可访问,但对于尚未启用 Pages 的仓库,通常会返回 404(“这里没有 GitHub Pages 网站”);记录该 404 页面会让解说视频约 12 秒的内容与主题不符。 - 如果响应呈现 GitHub Pages 的“404 — 这里没有 GitHub Pages 网站。”模板(判断依据:响应正文包含
"There isn't a GitHub Pages site here"),将live_url设为 None,并跳过第 6–7 个镜头。 - 否则,将
live_url保留用于第 6–7 个镜头。
这与旧版的可访问性检查一致:使用较短超时检查 live_url,并跟随重定向。
步骤 2.6 — 通用 URL 预检(仅限通用 URL 模式,不调用 MCP)
为非 GitHub URL 编写镜头前,使用 WebFetch 获取输入 URL 并检查响应。此步骤可避免三种常见的通用 URL 模式故障:(a)录制到验证码/机器人拦截页面而非实际内容;(b)Cookie/同意提示横幅占用视频开头约 3 秒;(c)通用 CSS 选择器无法匹配页面实际的主视觉区/版块。
A. 机器人拦截/验证码检测——若匹配则中止:
如果响应正文包含以下任一内容:
"Verify you are human"/"verify you are not a robot""captcha"/"CAPTCHA"/"reCAPTCHA""403 Forbidden"/"Access Denied""Just a moment"+cf-chl-bypass(Cloudflare 验证)"We're sorry, something went wrong"(Amazon 风格的机器人拦截)<title>或 h1 仅为 "Robot Check"/"Are you a robot?" → ABORT,并向用户清楚地报错:"Generic-URL 模式无法渲染此网站——该页面在无头 Chrome 中显示了机器人检测/验证码挑战。请尝试其他 URL,或先运行真实用户版本的页面以确认其能正常加载。"
B. Cookie/同意横幅检测——使用 extra_css + 可选点击来处理:
扫描响应,查找以下模式(不区分大小写):
- 以
onetrust-、truste-、cookie-banner、cookie-consent、gdpr-、consent-、cmp-开头的 ID/类名 - 匹配
(?i)accept (all )?cookies/(?i)agree.{0,10}cookies/(?i)i (accept|agree)的按钮 - Apple 专属横幅:id
ac-gdpr-banner或 classas-globalfooter-curtain - Google 同意提示:包含文本 "Before you continue" 的
[role="dialog"]
如果检测到,将 cookie_banner_present = true。为加强防护,录制时会同时使用以下两种方式:
- 在
capture_website调用中使用 CSS 注入(extra_css),以隐藏常见横幅——即使下面的点击没有成功,横幅也会在视觉上消失。 - 在
at_s: 0.0处执行一个clicktimed_action,使用最可能的关闭选择器(从 WebFetch DOM 中提取,例如#onetrust-accept-btn-handler、[aria-label*="Accept all" i]、button[id*="accept"])。
extra_css 负载(请原样使用——覆盖约 80% 的同意管理平台):
#onetrust-banner-sdk, #onetrust-pc-sdk, #onetrust-consent-sdk { display: none !important; }
#truste-consent-track, #truste-consent-content, .truste_box_overlay { display: none !important; }
[id*="gdpr-cookie"], [id*="cookie-consent"], [id*="cookie-banner"] { display: none !important; }
[class*="cookie-banner"], [class*="cookie-consent"], [class*="consent-banner"] { display: none !important; }
[class*="CookieBanner"], [class*="CookieConsent"], [class*="ConsentBanner"] { display: none !important; }
#ac-gdpr-banner, .as-globalfooter-curtain { display: none !important; } /* Apple */
[role="dialog"][aria-label*="cookie" i], [role="dialog"][aria-label*="consent" i] { display: none !important; }
.cmp-container, .cmp-modal, .cmp-banner { display: none !important; }
C. 识别真实 DOM 元素——生成具体选择器:
通用 CSS 选择器(h1、[class*="hero"]、section h2)适用于语义清晰/标记规范的网站,但在知名大型企业网站上可能无法匹配经过混淆处理的类名(apple.com 使用的是 tile-headline / as-headline-section-title,而不是 hero-*)。对于每个分镜,优先使用 WebFetch 中观察到的实际 DOM 元素:
- 阅读 WebFetch 返回的渲染后 HTML/Markdown。记下页面实际的主
<h1>文本和类名。 - 记下页面的分区结构(h2 标题及其父容器)。
- 记下醒目的 CTA/注册/定价元素。
- 对每个 Generic-URL
zoom_target.selector,按顺序应用以下选择器优先级:- 在渲染后 DOM 中观察到的稳定 id 或类名(
#hero、.tile-headline、.as-headline-section-title、.pricing-card),前提是它们易读且具有明确指向性。 - CTA 或导航分镜使用无障碍/链接属性(
[aria-label*="Get started" i]、a[href*="pricing"]、a[href="/new"])。 - 当类名看起来是自动生成的(Tailwind
_1a2b3c、CSS modulesmodule__hero___xYz)时,使用语义结构(main > section:nth-of-type(N) h2、main section:nth-of-type(N) [role="img"]、footer h2)。 - 仅在前三种选择都不可用时,使用宽泛的回退选择器(
h1、main、section h2、button、a[href])。
- 在渲染后 DOM 中观察到的稳定 id 或类名(
所有生成的选择器都必须是 document.querySelector 可解析的原生 CSS。不要生成 :contains(...)、:has-text(...)、text=... 或 XPath;这些是 Playwright/文本查询的便捷语法,在 capture_website 的平滑滚动路径中不起作用。如果需要按文本匹配,请改为定位附近稳定的 href、aria-label、id/类名或位置选择器。
D. SPA/延迟渲染检测——延长初始等待时间:
如果 WebFetch 响应中可见标题少于 3 个/文本内容很少,页面可能是在 domcontentloaded 之后通过 SPA 渲染的。此时应生成一个更长的初始 wait 操作({type: "wait", at_s: 0.0, ms: 2500}),放在任何分镜触发之前,而不是使用默认的 600ms 等待时间。
E. 提供 --focus 时应予以遵循(不要询问):
如果未提供 --focus,则根据通用结构线索选择分镜——直接静默地尝试一次,不要询问用户“你希望我重点关注什么?”。如果第一次尝试没有抓住用户想要的角度,用户会通过使用 --focus "the X feature" 重新运行。提供 --focus 时,应依据该短语选择分镜:使用与之匹配的具体页面分区,忽略无关的营销装饰内容。
步骤 3.0 — 必需 README 章节扫描(仅 GitHub 模式,不调用 MCP)
编写节拍表之前,扫描 README(不区分大小写,全文检索),查找以下任一章节名称。如果找到匹配项,必须在步骤 3 中为该章节添加专门的节拍;必要时替换通用节拍 4–5 之一:
| README 包含…… | 必需节拍 |
|---|---|
overview / what is | 滚动到该标题;缩放 .markdown-heading:has(#user-content-overview) .heading-element(或匹配的 slug) |
how it works | 滚动到该标题;缩放 .markdown-heading:has(#user-content-how-it-works) .heading-element |
audio layer / audio timeline | 滚动到音频层示意图;缩放渲染后的图示或其周围的标题 |
claude code / mcp integration | 滚动到该章节;缩放 article pre 或 .highlight(终端截图 / 代码块) |
architecture / system design | 滚动到该章节;缩放 .markdown-heading:has(#user-content-architecture) .heading-element |
features(位于显眼的顶部位置时) | 滚动到该标题;缩放 .markdown-heading:has(#user-content-features) .heading-element |
getting started / quick start / installation | 滚动到该标题;缩放 .markdown-heading:has(#user-content-installation) .heading-element(或匹配的 slug)——如果想改为安装代码块,可退而缩放 article pre |
usage / examples | 滚动到该标题;缩放 .markdown-heading:has(#user-content-usage) .heading-element(或匹配的 slug)——也可以缩放其下方的第一个代码块 |
**GitHub 标题 slug 规则:**转换为小写,空格替换为连字符,去掉非 [a-z0-9-] 字符。因此,“How it works” → #user-content-how-it-works,“Quick Start” → #user-content-quick-start。GitHub 目前会将 README 标题渲染为 .markdown-heading 包装元素,其中包含 .heading-element 和一个同级的永久链接锚点。按 slug 定位包装元素,再定位标题:.markdown-heading:has(#user-content-<slug>) .heading-element。
**2026-05-28 在 GitHub 上验证的 DOM:**当前仓库根页面在 strong[itemprop="name"] a 处显示仓库名称;在 .markdown-body 处显示渲染后的 README 正文;在 .markdown-body h1.heading-element 处显示 README 标题;在 .markdown-heading:has(#user-content-<slug>) .heading-element 处显示 README 章节标题,并可用 .markdown-body h2.heading-element 作为通用章节标题回退选择器。验证期间,如果 GitHub 节拍中任何 action_bboxes[].found 值为 false,请停止并重新验证 GitHub 当前的 DOM,然后再继续;仓库导览视觉效果不要依赖默认位置回退。
选择器约定:bbox_selector 必须是可由 document.querySelector 解析的原生 CSS(capture_website 会通过 page.evaluate 执行操作后的平滑滚动 JS,使用浏览器原生选择器引擎)。避免使用 Playwright 扩展,例如 :has-text("...")、text=... 或 :visible:这些选择器可由 Playwright 的 page.query_selector 解析(因此边界框捕获能够找到元素),但会在平滑滚动的 document.querySelector 中静默失效(因此页面不会滚动到目标位置,bbox.y 最终会是相对于文档的 Y 坐标,而不是 top - 60 px,触发步骤 8b 的 bbox.y > recording_viewport.h 退化过滤器,并回退到默认位置缩放)。CSS 第 4 级的 :has(...) 是原生 CSS,且受现代 Chromium 支持。
这些章节是大多数值得制作讲解内容的仓库中信息量最高的视觉素材。遗漏它们会让导览变得千篇一律;加入它们则能为讲解带来具体的“展示,而非空谈”节拍。步骤 3.0 将这些章节视为硬性要求,而非可有可无的指导,并列出了开源软件 README 中常见的七个高信号标题。
步骤 3 — 编写节拍表(主线程,不调用 MCP)
编写一个包含 8–10 个节拍的 JSON 数组,总时长必须为 65–80 秒,总词数必须为 165–200 个(假设语速为每秒 2.5 个词)。每个节拍:
{
"t_start": 0.0,
"t_end": 7.5,
"action": { "type": "navigate" | "scroll_to" | "hover", "url": "...", "selector": "..." },
"zoom_target": { "selector": "...", "description": "..." },
"vo_text": "exact words to speak — 1 to 2 conversational sentences"
}
硬性约束(输出节拍表前先验证:任一项不满足,就拒绝草稿):
- 每个节拍都必须包含全部五个字段:
t_start、t_end、action(包含type和url)、zoom_target(包含selector)、vo_text。缺少字段 ⇒ 拒绝并重新撰写。 - 节拍 0 的
t_start= 0.0;t_end[i] == t_start[i+1](时间连续)。 - 每个节拍的
len(vo_text.split()) / 2.5≈t_end - t_start。目标误差为此估算值的 ±10%;如果草稿的语速高于 2.5 wps,就缩短vo_text,直到时长合适。 - 最后一个节拍的
t_end总时长 ≤ 80 秒。(参考输出包含片头,总时长为 86.5 秒;唇形同步音频约为 83 秒。目前负载下,Kling avatar/image2video 的音频时长超过约 90 秒时会稳定卡住——超过 80 秒有导致 Kling 超时 20 分钟的风险。) - 口播总词数在 165 到 200 之间。
- 每个节拍的
zoom_target.selector都必须是对应节拍落地页面上的有效 CSS 选择器。GitHub 模式优先使用当前 GitHub 仓库/README 选择器:strong[itemprop="name"] a、.markdown-body h1.heading-element、.markdown-heading:has(#user-content-<slug>) .heading-element、.markdown-body h2.heading-element、.blob-code-inner、.highlight、.octicon-star、nav。通用 URL 模式优先使用稳健的通用选择器:h1、[role="main"]、main、header、nav、.hero、.feature、section h2、[class*="cta"]、[class*="hero"]、button、a[href]。选择器必须能在节拍的操作完成后渲染的页面上找到匹配项——输出前通过 WebFetch 检查可见 DOM。 vo_text为 1–2 句口语化句子。使用开发者口吻。不含舞台指示。不使用 Markdown。- 当
action.type == "navigate"时,action.url必须是有效的https://...URL;此字段为必填项。
**步骤 4 前的自检:**确认 total_words 在 [165, 200] 内,且 total_seconds(即 beats[-1].t_end)在 [65, 80] 内。如果任一项不符合范围,就重新撰写节拍表——不要继续生成 TTS。(无需在任何地方“打印”这些数据——这是内部草稿验证;只需拒绝草稿并重新撰写,直到满足要求。)
结构骨架——GitHub 模式(对视觉规范至关重要——与源结构保持一致,但如适用,以步骤 3.0 为准):
- 节拍 1:
navigate到仓库根目录,将strong[itemprop="name"] a(仓库标题)设为缩放目标,并使用钩子句。 - 节拍 2–3:
navigate到具体源文件(https://github.com/{owner}/{repo}/blob/HEAD/<path>),将.blob-code-inner或.highlight设为缩放目标。选择与旁白陈述相符的文件——不要跳转到不会提及的文件。 - 节拍 4–5:
scroll_to到 README 章节,将.markdown-heading:has(#user-content-<slug>) .heading-element、.markdown-body h2.heading-element或.markdown-body设为缩放目标。如果步骤 3.0 指定了必需章节,就用这些章节替换此处的节拍。 - 节拍 6–7(仅当
live_url在步骤 2.5 后仍有效时):navigate到live_url,将nav/h1/.hero/main/button/.feature设为缩放目标。 - **节拍 8:**返回仓库根目录,将
.octicon-star设为缩放目标,并作结。
结构骨架——通用 URL 模式:
- 节拍 1:
navigate到输入 URL,将h1或[class*="hero"] h1(页面主标题)设为缩放目标,并使用钩子句。 - 节拍 2–3:
scroll_to到页面的主视觉区 / 价值主张 / 第一个功能区。将.hero、[class*="hero"]、[class*="feature"]或section:nth-of-type(1) h2设为缩放目标。选择旁白提及的可见元素。 - 节拍 4–5:
scroll_to到更深层的章节——功能列表、截图、定价、社会认同。将section h2、[class*="feature"] img、[class*="testimonial"]、[class*="pricing"]或页面上其他显著的语义元素设为缩放目标。 - 节拍 6–7:
scroll_to到 CTA / 注册 / 演示嵌入区域。将[class*="cta"]、button、a[class*="button"]或[id*="signup"]设为缩放目标。(通用模式下不导航到在线演示——输入 URL 本身就是演示页面。) - 节拍 8:
scroll_to到页脚 / 收尾元素,将footer h2、footer设为缩放目标,或返回顶部并以h1为缩放目标。使用收尾句。 如果提供了--focus,请将其角度融入vo_text,但不要更改结构骨架。在zoom_target.selector中,优先使用 CSS 选择器,而非text_content——边界框捕获仅支持选择器(参见已知限制)。
步骤 4 — TTS
调用 generate_speech,并设置 provider: "minimax-tts"、text: <full vo_text join>,以及可选的 voice_id(来自 --voice 或步骤 1 的默认预设)。获取 result.audio_url(调度器返回的音频字段是 audio_url,而非 url)和 result.duration_seconds。
**过期语音回退检测:**当 Minimax 返回 status_code:2054(未找到语音 ID——通常是每个 Agent 的工作区指针,Minimax 会在闲置 7 天后自动删除)时,调度器会重试一次,并使用默认的 Calm_Woman 语音。重试成功后,响应中会包含文档化架构之外的两个额外字段(透传):voice_id_requested(工作进程首次尝试使用的已配置但过期的 ID)和 fallback_reason: "invalid_minimax_voice_id"。如果响应中出现 fallback_reason == "invalid_minimax_voice_id",请向用户简短说明,例如:“你在 Minimax 上注册的语音已过期(闲置 7 天后被自动清理);我们使用了系统默认语音。如需恢复个性化语音,请通过 clone_voice 重新克隆。”渲染不会失败——只是使用默认语音——因此这只是提示信息,不是重试触发条件。
**Cookie 横幅音频填充(第 2.6 步 §B 中 cookie_banner_present == true 的通用 URL 模式):**如果第 4 步调用使用默认 MiniMax 路径,请在调用 generate_speech 之前,将 MiniMax 的暂停标记 <#1.5#> 加到 text: 参数开头。MiniMax 的 speech-2.8-hd 会将 <#N#> 处理为 N 秒静音;返回的 audio_url 和 duration_seconds 会原生包含这段 1.5 秒的前导静音。如果第 4 步使用 provider="elevenlabs",请跳过 MiniMax 标记,并使用下方的回退音频混合填充路径。这会使音频与第 4.5 步中录屏应用的关闭 Cookie 横幅后 +1.5 秒的偏移对齐。
回退方案(仅当冒烟测试表明此语音会忽略该标记时):正常调用 generate_speech,然后调用 edit_audio_mix,将结果叠加到 1.5 秒的静音底音上,偏移量设为 1.5 秒。然后调用 analyze_media(url=<padded_audio_url>) 探测填充后的时长,并将 duration_seconds = result.duration_seconds 重新绑定,再由第 4.5 步使用。analyze_media 是唯一权威的时长探测工具——不要依赖 edit_audio_mix 的返回负载(其时长字段不保证符合约定)。
第 4.5 步 — 音频时长验证 + 节拍表缩放
在第 4 步之后应用于 audio_duration_seconds(其中包含任何 cookie 引导填充)。最终状态:beats[].t_start / t_end 是与音频播放时间线匹配的绝对时钟秒数。所有对 beats[] 的修改都在此处进行;第 6 步和第 8 步是只读使用者。
门槛 1 — Kling 停滞上限(提供方上限,原始 audio_duration_seconds):
如果 audio_duration_seconds > 90,则中止,并使用更紧凑的字数预算重新编写节拍表。Kling avatar/image2video 在时长超过约 90 秒时会停滞。
门槛 2 — 退化的 TTS(口播内容时长):
计算 narration_duration = audio_duration_seconds - (1.5 if cookie_banner_present else 0.0)。如果 narration_duration < 30s,则重试一次第 4 步(并根据重试返回的音频重新计算 narration_duration)。如果重试后 narration_duration 仍然 < 30s,则中止并调查原因——可能的故障模式:MiniMax 响应被截断、音频无声、vo_text 拼接不正确。
门槛 3 — 缩放:
narration_duration = audio_duration_seconds - (1.5 if cookie_banner_present else 0.0)scale = narration_duration / beats[-1].t_end- 如果
scale < 0.5或scale > 1.5,则中止并重新编写。TTS 在结构上存在问题(或字数预算偏差很大);缩放无法挽救。 - 对每个节拍:
beat.t_start *= scale; beat.t_end *= scale - 如果
cookie_banner_present:对每个节拍执行beat.t_start += 1.5; beat.t_end += 1.5 - 最终钳制:
beats[-1].t_end = audio_duration_seconds(精确值)。无论 cookie 模式如何,也无论浮点数累积误差如何,都能保证不变量的浮点相等。
通过门槛 3 后,输出一行操作员日志,以便在运行后诊断时查看缩放值:
Rescaled beats by scale=X.XX (audio=Y.YYs, narration_duration=Z.ZZs, cookie_pad=W.Ws)
建议(非门槛): scale 接近 1.0 最理想。scale > 1.2 表示音频明显比预期慢——视觉效果会显得“拉长”,但仍与音频同步。scale < 0.85 表示音频更快——视觉效果会显得“仓促”,但仍同步。两种情况都能通过门槛;如果用户反馈“节奏不对劲”,而不是“不同步”,则使用更紧凑 / 更宽松的字数预算重新编写。
第 5 步 — 预览门槛(用户选择预览,或为后备头像自动预览)
只有在用户未传入 --preview 且 avatar_auto_preview_required == false 时,才跳过第 5 步。当头像来自静默生成的后备头像,或来自 Avatar 适用性门槛后的重新生成头像时,必须自动预览。这些路径可能会在不知情的情况下引入无面部或非人类头像,因此在投入完整的 60-80 秒唇形同步生成前,简短且成本低的预览是必要的保护措施。
为保持向后兼容,接受 --skip-preview 和 --yes,但不执行任何操作——它们是旧的退出预览标志。
如果传入了 --preview,或 avatar_auto_preview_required == true:
-
使用
provider: "minimax-tts"、可选的voice_id和text: "Hi, I'm your presenter. Let's explore this repo together."调用generate_speech→preview_audio_url。 -
使用
provider: <resolved_lipsync_provider>(默认为pika;如果传入了--lipsync-provider kling,则遵从该设置)、image: <avatar>、audio: preview_audio_url调用generate_lipsync→preview_lipsync_url(纯唇形同步,约 3 秒)。此处使用的提供方必须与第 9 步完整音频所用的提供方相同——预览的目的是在耗时较长的渲染前,确认头像 + 声音 + 提供方的组合。 -
原样向用户展示:
预览已就绪:
<preview_lipsync_url>这确认了头像 + 声音的组合。完整渲染耗时较长(完整音频的 Kling 唇形同步约需 5–30 分钟)。 回复yes以继续,或回复其他内容以取消。 -
匹配
^(yes|go|proceed|confirm|y)$(不区分大小写)。其他任何输入 → 停止,不再调用 MCP。
第 6 步 — 构建 timed_actions 并记录
将节拍表转换为 capture_website timed_actions。每个节拍对应一个 timed_action——将 bbox_selector 设为该节拍的 zoom_target.selector,capture_website 会在内部捕获该元素执行操作后的边界框(旧流程:等待 600 ms → 平滑滚动至 top - 60 px → 动画结束后等待 1300 ms → 测量,全部在服务端完成)。
按顺序为每个节拍输出一项:
navigate节拍:{type: "navigate", at_s: <t_start>, url: <action.url>, bbox_selector: <zoom_target.selector>}。工作进程会导航,在绝对时间at_s + 0.6 s等待,然后将bbox_selector滚动至可视区域并测量边界框——调用方无需安排后续步骤。scroll_to/hover节拍:{type: "scroll", at_s: <t_start>, selector: <action.selector or zoom_target.selector>, bbox_selector: <zoom_target.selector>}。操作自身的selector控制页面滚动;bbox_selector控制边界框测量(两者可以相同,也可以不同,通常相同)。capture_website不支持hover;滚动至可视区域是对应操作。
不要在编写的节拍前添加开场滚动浏览。对口型音频的时间从节拍表的 t=0 开始;添加开场内容会让屏幕录制向后推移约 3 s,而音频不变,造成音画不同步。capture_website 录制会在 t=0 开始,此时已加载第 0 个节拍的 URL,因此第一个编写的节拍就是视觉定位点。
调用 capture_website:
url: <第 0 个节拍的 action.url>timed_actions: <按上述规则构建的 N 项列表>(每个节拍对应一项)duration_s: ceil(audio_duration_seconds)——beats[].t_start和t_end已在步骤 4.5 中根据 TTS 音频时间线重新缩放,因此duration_s直接使用音频时长即可。无需再使用旧的max(...)防止 TTS 超时。
通用 URL 模式的附加项(根据步骤 2.6 预检):
extra_css: <步骤 2.6 §B 中用于隐藏 Cookie 横幅的 CSS 载荷>——作为防护措施,通过display: none !important;隐藏常见的同意管理平台,这样即使可选点击操作未命中,录制中也不会显示横幅。- 添加前置
wait操作{type: "wait", at_s: 0.0, ms: 2500},用于 SPA / 延迟渲染页面(根据步骤 2.6 §D);普通页面使用 1500ms。这样可在第一个节拍触发前,留出时间让主视觉图像延迟加载、字体替换完成,并让滚动触发的动画准备就绪。 - 如果步骤 2.6 §B 中的
cookie_banner_present为真,还要添加前置click操作{type: "click", at_s: 0.5, selector: <从 WebFetch DOM 检测到的关闭按钮选择器>}。步骤 4.5 中,beats[]数组已整体向后移动+1.5s,以补偿关闭 Cookie 横幅造成的延迟;TTS 音频也已在步骤 4 中补入 1.5s 的静音;此处无需再次移动时间。节拍 1 的timed_action.at_s直接读取beats[0].t_start;在 Cookie 模式下,该值为 1.5。 - 如果
cookie_banner_present == false,无需添加 Cookie 横幅操作;只添加前置等待操作。
捕获 video_url、recording_viewport、action_bboxes。结果会在 video_url 旁返回 recording_viewport: {w, h} 和 action_bboxes: [{idx, selector, found, bbox: {x,y,w,h}}]。
action_bboxes[].idx 的语义:idx 字段表示该项在输入 timed_actions 数组中的位置。
- GitHub 模式:每个节拍对应一个
timed_action,因此idx与节拍索引一一对应;步骤 8 使用entry.idx作为beat_idx。 - 通用 URL 模式:前置的
wait(以及可选的关闭 Cookie 横幅click)会使数组索引偏移 1 或 2。计算beat_idx = entry.idx - prepend_count,其中prepend_count为 1(仅wait)或 2(wait+click)。跳过beat_idx < 0的项(这些是前置设置操作,不是节拍)。
每个条目中的 selector 字段表示 bbox_selector(即 zoom_target.selector),而非操作自身的 selector。
通用 URL 的边界框命中率警告:capture_website 返回后、步骤 8 使用测量结果前,计算边界框覆盖率:
bbox_total_count = number of authored beats with a zoom_target.selector
bbox_found_count = count of beat entries where found == true and bbox is not degenerate
bbox_hit_rate = bbox_found_count / max(1, bbox_total_count)
使用上文所述的相同 prepend_count 映射,确保预置的设置操作不会计入命中率。按照步骤 8b 的筛选条件,将边界框视为退化边界框(bbox.y > recording_viewport.h 或 bbox.h > recording_viewport.h * 1.5)。如果 bbox_hit_rate < 0.70,将 bbox_warning 设为以下这句面向用户的原文,并将其传递到步骤 12:
在
<missed>/<total>个节拍上,针对元素的缩放未命中——这些节拍将改为以画面中心为目标缩放。该网站可能使用了混淆的类名,或采用了由滚动触发的渲染。
步骤 7 — 浏览器框架
edit_browser_frame:
video_url: <步骤 6 的 video_url>url: (GitHub 模式下,若通过步骤 2.5 则为 live_url,否则为 input_url,截断至 65 个字符)tab_title: <30 个字符的标题>— GitHub 模式:(meta.description or repo_name or "")[:30]。通用 URL 模式:页面的<title>(来自步骤 2 中的 WebFetch),或 URL 的主机名,截断至 30 个字符。确保值不是None或空值。
返回 framed_url(1280×800 Sonoma + 浏览器框架)。
步骤 8 — 构建 zoom_keyframes 并应用
常量:
INTRO_BEATS = 2— 按节拍表索引进行筛选。跳过索引 0 和 1 的节拍(即上方结构骨架中的“节拍 1”和“节拍 2”)。HOLD_GAP = 0.6— 每次放大前和缩小后保持 1.0× 的秒数。MIN_BEAT_DUR = 1.5— 跳过短于此时长的节拍(没有足够空间进行有意义的缩放)。SCALE = 1.35(精准的元素定位缩放)。FALLBACK_SCALE = 1.25(没有可用边界框时,回退到默认位置)。FALLBACK_RAMP = 0.4。
注意:beats[].t_start / t_end 已在步骤 4.5 中重新缩放到音频时间线(如适用,还进行了 Cookie 偏移)。HOLD_GAP(0.6 秒)、MIN_BEAT_DUR(1.5 秒)以及 1.0 秒的内部区间检查,均使用这些最终值——它们是渲染后视频中实际的视觉秒数。
edit_browser_frame 的内容区域偏移:CONTENT_X=56, CONTENT_Y=108, CONTENT_W=1168, CONTENT_H=637。
坐标变换(录制像素 → 加框后的像素):
cx_framed = 56 + (bbox.x + bbox.w/2) * (1168 / recording_viewport.w)
cy_framed = 108 + (bbox.y + bbox.h/2) * (637 / recording_viewport.h)
**按“每个节拍先设默认值,再由边界框覆盖”的模式构建缩放列表。**旧版方案遵循“每个非开场节拍都缩放——有边界框时根据边界框计算,否则使用默认位置”的规则。在此处重现该规则:
步骤 8a — 为每个非开场且时长足够的节拍预填默认位置关键帧。
默认位置常量:
DEFAULT_CX = 56 + 1168 // 2(加框画布的屏幕中心)DEFAULT_CY = 108 + 637 // 3(内容区域上方三分之一处,大多数 GitHub UI 的显眼元素都位于此处)
从索引 INTRO_BEATS(= 2)开始遍历节拍表,直到末尾。对每个节拍:
- 如果
t_end - t_start < MIN_BEAT_DUR(1.5 秒),则跳过——时长太短,无法进行有意义的缩放。 - 将关键帧的内部区间计算为
[t_start + HOLD_GAP, t_end - HOLD_GAP]。如果该区间短于 1.0 秒,则跳过。 - 否则,将该节拍在按节拍索引映射表中的位置(称为
zoom_keyframes_by_beat[beat_idx])预填为{cx: DEFAULT_CX, cy: DEFAULT_CY, scale: FALLBACK_SCALE (1.25), ramp_s: FALLBACK_RAMP (0.4)},并附上裁剪后的t_start/t_end。
步骤 8b — 当 action_bboxes 提供了可用测量值时,使用根据边界框计算的精准缩放进行覆盖。
对 action_bboxes 中的每个条目:
- GitHub 模式:
beat_idx = entry.idx,因为步骤 6 会为每个节拍输出一个 timed_action。 - 通用 URL 模式:
beat_idx = entry.idx - prepend_count,因为步骤 6 会插入等待操作,有时还会插入 Cookie 关闭点击操作。如果beat_idx < 0,则跳过;该条目属于设置操作,而非编写的节拍。 - 如果
beat_idx < INTRO_BEATS,则跳过。 - 如果
entry.found为 false,则跳过。 - 如果该节拍尚未在
zoom_keyframes_by_beat中(在步骤 8a 中因MIN_BEAT_DUR/1.0s规则被筛除),则跳过。 - **筛除退化边界框:**如果
bbox.y > recording_viewport.h(捕获时元素在屏幕外——页面未能及时滚动到元素所在位置),或bbox.h > recording_viewport.h * 1.5(整页<main>元素——会导致缩放中心失去意义),则跳过。 - 使用上方所示的录制像素 → 加框像素坐标变换,根据边界框中心计算
cx_framed/cy_framed。用{cx: cx_framed, cy: cy_framed, scale: SCALE (1.35), ramp_s: min(0.5, (t_end - t_start) * 0.15)}覆盖该节拍的位置。
**最终列表:**将 zoom_keyframes_by_beat 中的值按 t_start 排序,生成 zoom_keyframes 数组。
这样可确保每个非开场且时长足够的节拍都会缩放——边界框捕获成功时精准定位,否则使用默认位置。避免出现“整段视频始终没有缩放”的故障情况。
如果 len(zoom_keyframes) > 0,则调用 edit_animate_zoom,并传入 video_url: framed_url, zoom_keyframes。返回 zoomed_url。否则(没有符合条件的节拍——鉴于步骤 3 的 65-80 秒限制,这种情况应该很少见),则跳过并将 framed_url 用作 zoomed_url。
步骤 9 — 为完整音频生成口型同步视频
generate_lipsync:
provider: <resolved_lipsync_provider>— 默认值:pika(parrot a2v)。如果显式传入--lipsync-provider kling,则遵照该设置。image: <avatar>audio: <Step 4 audio_url>- 仅适用于 kling 的参数:当
provider == "kling"时,添加mode: "pro"和prompt: "talking head, face centered, mouth syncs to audio, minimal head movement, professional presenter",以呈现精致的演讲者风格。在pika中,这两个参数都会被静默忽略(parrot 有自己的驱动程序)。
服务商权衡:
| 服务商 | 实际耗时 | 头部动作 | 适用场景 |
|---|---|---|---|
pika(默认) | 约 2–5 分钟 | 略显夸张,更具自然感 | 大多数运行的默认选择——迭代快速,输出效果耐看,速度约为 kling 的 10 倍 |
kling(可选) | 约 5–30 分钟 | 动作幅度小,面部居中,演讲者风格 | 对成片要求高、头像必须呈现精致演讲者效果的场景;需接受漫长的等待时间 |
服务端等待会在调用过程中等待结果;如果响应格式为 {task_id, status: "queued"},则紧密循环轮询 task_status(不休眠),直到状态进入终态(completed、failed 或 cancelled)。状态为 completed 时,记录 lipsync_url。状态为 failed / cancelled 时,按照下方的故障转移说明切换到另一个服务商(kling ↔ pika)。
故障转移:
- 如果
pika失败(少见——parrot a2v 对常见的讲解音频时长表现稳定)→ 使用provider: "kling"重试一次。 - 如果
kling超过工作器的 1200s 上限仍未完成(表现为状态反复显示processing,且始终未完成)→ 切换到provider: "pika"。步骤 4.5 的音频时长门槛应能在此之前拦截长音频情况,但故障转移可以应对剩余风险。
为什么默认使用 pika:
- 速度——由于口型同步是耗时最长的环节,典型讲解视频的总耗时可从约 10–15 分钟缩短至约 5–7 分钟。
- 质量足够好——parrot a2v 具有自然感;在带有头像圆形画中画的 60-80s 视频片段中,略多的头部动作会显得生动,而非分散注意力。
- kling 的 pro 模式所带来的精致效果,在 246 像素的圆形画面中基本难以察觉——面部区域太小,大多数观众看不出头部动作幅度较小的差异。
如需呈现参考输出那种典型的“精致演讲者”风格,请显式传入 --lipsync-provider kling。
面部连贯性门槛(画中画合成前必须执行):
获取 lipsync_url 后,在步骤 10 之前对原始口型同步视频采样。调用 extract_frame(video_url=<lipsync_url>, at_times=[1.0, audio_duration_seconds * 0.5, max(1.0, audio_duration_seconds - 1.0)]) 获取起始帧 / 中间帧 / 结束帧。在批处理模式下,保存 frame_urls = extract_result.urls;只有在 urls 不存在时,才将旧版单个 url 字段用作回退值。由于 analyze_media.media 接受单个 URL 字符串,应为每个帧 URL 分别调用一次 analyze_media;不要传入数组。
对于每个 frame_url,使用以下查询调用 analyze_media(media=<frame_url>, query=<single_frame_gate_query>):
Return JSON only: {
"face_integrity_score": 0-100,
"is_coherent_human_face": boolean,
"has_visible_mouth": boolean,
"is_abstract_melting_mask_or_non_human": boolean,
"observation": string,
"recommended_action": "pass" | "retry_other_provider" | "abort"
}
Does this frame show a coherent human presenter face with a visible mouth, not an abstract blob, melting mask, mascot, faceless illustration, or distorted non-human face?
继续处理前,先汇总各帧的判定结果。仅当所有采样帧中最低(最差)的 face_integrity_score 为 >= 75、每一帧的 is_coherent_human_face == true、每一帧的 has_visible_mouth == true,且每一帧的 is_abstract_melting_mask_or_non_human == false 时,才算通过。保留每帧的观察结果,以便中止错误消息能指出是起始帧、中间帧还是结束帧未通过。
如果首选服务商未通过门槛,则使用相同的头像和音频切换到另一个服务商(pika ↔ kling)重试一次,然后对第二个原始 lipsync_url 再执行一次面部连贯性门槛检查。如果两个服务商都未通过,则以清晰的错误信息中止并返回门槛检查观察结果;不要带着损坏的演讲者画面继续执行步骤 10。如果第二个服务商通过,则在后续步骤中使用该服务商的 lipsync_url。
步骤 10 — 画中画合成
edit_pip:
main_video_url: <zoomed_url>overlay_video_url: <lipsync_url>shape: "circle"size_px: 246← 外径固定为 246px(头像内径为 240,描边环为 3+3)stroke_width_px: 3stroke_color: "white"position_px: {x: 20, y: 476}←800 − 246 − 78,为停靠区域留出空间
传入 size_px,而不是 size;这两个字段不能同时使用。返回 final_url。
**主时长 / 音频来源约定:**默认情况下,edit_pip 使用 shortest=1 语义,这意味着合成视频的时长取(缩放后的屏幕录制)与(口型同步视频)中较短的那个。步骤 6 中的 duration_s = ceil(audio_duration_seconds) 可确保屏幕录制时长与口型同步视频完全一致(步骤 4.5 已将节拍重新缩放到音频时间线上)。合成视频的时长由 edit_pip 的 shortest=1 语义根据口型同步视频确定。音频来自口型同步视频的音轨(口型同步视频中嵌入了原始 TTS 音频);不会重新混入独立的 audio_url。如果口型同步视频比屏幕录制短(Kling 有时会裁掉尾部静音),屏幕画面会在口型同步视频结束时被截断——请接受这一点;对于讲解内容,另一种方案(循环播放屏幕画面)效果更差。
步骤 11 — 烧录字幕
应用 proper_noun_glossary 中的规范拼写后,从最终合并的 vo_text 构建 caption_script_text。制作讲解视频时,不要依赖自动转录来处理品牌名或型号名:即使浏览器画面中显示了正确文本,自动转录也可能按发音错误拼写可见的专有名词。
调用工具前,先选择字幕位置:
- 普通讲解页面默认使用
position: "bottom"(caption_position = "bottom")。 - 如果当前节拍计划或捕获的页面显示,下三分之一处有重要的主视觉文案、标题、价值主张、CTA 或代码,会被经典底部栏遮挡,则使用
position: "top"(caption_position = "top")。
调用 add_captions(video_url=<final_url>, style="classic", caption_mode: "manual", subtitle_text: <caption_script_text>, position: <caption_position>)。手动模式会将更正后的旁白文本铺排到检测到的时长内,并防止专有名词出现偏差。将结果记为 captioned_url。
只有当用户传入 --no-captions(在步骤 1 中解析)时才跳过此步骤——默认开启字幕。(注意:/pika:podcast 不会烧录字幕——讲解视频的旁白比两位主持人的快速对话更适合转录。)
步骤 12 — 返回
如果在步骤 6 中设置了 bbox_warning,则在最终 URL 前立即输出该警告,让用户知道部分节拍的视觉效果已退化为画面中心缩放。然后在一行中输出 captioned_url(如果跳过了步骤 11,则输出 final_url):Done: <url>。
后处理质量门槛
宣布成功之前,对 captioned_url 或 final_url 调用 analyze_media,并要求其给出结构化判定:
Return JSON only: {
"verdict": "clean" | "degraded" | "catastrophic",
"observations": string[],
"quality_warning": string | null,
"re_roll_suggestion": string | null
}
Check that captions are present unless --no-captions was used, zoom target / bbox focus lands on the narrated UI element, there are no blank frames or white flash issues, and the avatar PiP does not cover important page content.
Also verify that the presenter face remains coherent (not abstract, melting, faceless, masked, or non-human), proper nouns from `proper_noun_glossary` are spelled correctly in captions, and captions do not occlude or cover hero text, headlines, value-prop copy, CTAs, or code that the narration is pointing at.
- 如果
verdict为clean,正常输出最终 URL。 - 如果
verdict为degraded,输出最终 URL 和quality_warning,以便用户在发布前检查。 - 如果
verdict为catastrophic,不要将讲解视频标记为完成;应显示该判定和re_roll_suggestion,而不是宣布成功。
故障模式
| 现象 | 可能原因 | 恢复方法 |
|---|---|---|
| 首次调用 MCP 时出现 OAuth / 401 | 用户的 Pika 连接器令牌缺失或已过期 | 停止操作,并告知用户重新验证 Pika MCP 连接器;身份验证成功前,不要重试需要付费的步骤。 |
capture_website 返回空白帧、空的 action_bboxes 或被拦截的页面 | 目标 URL 受到访问限制、检测到机器人、被 Cookie 覆盖,或在等待时限结束后才完成渲染 | 如果 WebFetch 文本回退足以提供事实依据,则使用该方式;否则请用户粘贴源材料或提供更简单的 URL。 |
| TTS 或口型同步提供商返回暂时性 5xx / 429 | 提供商队列或速率限制 | 根据提示的退避时间后,仅重试一次完全相同的调用;如果再次失败,则停止操作,并提供提供商错误以及最后一个已完成的检查点。 |
| 字幕或最终 QA 报告专有名词难以辨认、遮挡、空白帧或虚拟形象不连贯 | 生成的媒体尚未达到可发布状态 | 返回 not publish-ready,并附上 QA JSON 和具体的重新生成建议;如果结果存在严重问题,不要宣告成功。 |
关键短语
这些锚点可在不同页面类型中保持视觉约定一致:
| 短语 | 位置 | 关键原因 |
|---|---|---|
vanilla CSS that resolves via document.querySelector | 选择器约定 | 确保 capture_website 中的滚动、边界框捕获和缩放定位保持一致。 |
GitHub URLs activate repo-aware mode | 模式检测 | 防止通用产品页节奏替代 README / 代码演示节奏。 |
8-10 beats、65-80 seconds、165-200 words | 节奏表编写 | 确保旁白、屏幕录制、口型同步和字幕处于可靠的时长范围内。 |
all beats[] mutations happen here | 音频重新缩放步骤 | 确保后续的捕获、缩放和合成步骤使用同一条稳定时间线。 |
extra_css 隐藏 Cookie 横幅的载荷 | 通用 URL 预检查 | 如果点击横幅失败,可减少首帧被横幅遮挡的情况。 |
引擎选择:默认使用 Pika 口型同步,Kling 仅按需启用
默认使用 Pika/parrot 口型同步,因为速度更快,并且能让大多数解说视频保持较短的迭代周期。仅在用户明确请求 --lipsync-provider kling,或高风险渲染需要更居中的主持人形象且可以接受耗时更长的关键阶段时,才使用 Kling。屏幕捕获、浏览器画面、缩放、画中画和字幕仍是围绕该口型同步选择执行的确定性编辑 / 合成步骤。
运行时间预期
使用 Pika 口型同步时,典型耗时为 5-10 分钟;使用 Kling 口型同步时,为 10-30 分钟以上:
| 步骤 | 墙钟时间 | 说明 |
|---|---|---|
| URL 读取 + 预检查 | 10-60s | 扫描 GitHub README,或检查通用 URL 的 DOM / Cookie |
| TTS + 音频重新缩放 | 30-90s | 根据实际音频时长调整节奏时间 |
| 屏幕录制 | 60-180s | 取决于页面加载和导航次数 |
| 浏览器画面 + 缩放 | 1-3 min | 确定性的编辑 / 合成阶段 |
| 口型同步 | Pika:2-5 min / Kling:5-30 min | Kling 仅按需启用,因为这是耗时最长的阶段 |
| 画中画 + 字幕 | 1-3 min | 设置 --no-captions 时跳过字幕 |
已知缺口(作为后续服务端工作跟进)
- Kling 头像模式和提示词已可用。 若要启用精修演讲者模式,请传入
--lipsync-provider kling,并在第 9 步的调用中添加mode: "pro",以及类似"talking head, face centered, mouth syncs to audio, minimal head movement, professional presenter"的提示词。这是减少口型同步中夸张头部动作的质量调节项。 - 无法由调用方控制屏幕录制的白帧裁剪。
capture_website有内部裁剪启发式逻辑,但不会向调用方公开。页面仍在加载时,解释视频的开头会出现短暂的白屏闪烁。在at_s: 0.0处执行 800ms 的wait操作可以稍微缓解此问题,为页面留出渲染时间,但不会裁掉已录制的白帧。待 Worker 增强。 - 逐节导航时不等待
networkidle。capture_website会等待到domcontentloaded,并在 bbox 捕获分支执行操作后等待 600 ms(服务端行为,设置了bbox_selector时生效);但如果 SPA blob 页面在domcontentloaded之后才完成最终渲染,仍可能会对尚未挂载的代码块捕获 bbox。Worker 增强:为timed_actions[].navigate暴露wait_until参数。 - 没有逐步输出文件大小验证门槛。 稳健的文件大小检查应在每一步之后验证 TTS ≥ 50KB、预览 ≥ 100KB、屏幕录制 ≥ 200KB、口型同步视频 ≥ 500KB,最终视频 ≥ 1MB。MCP 路径只返回 URL;验证文件大小需要每一步额外调用一次
analyze_media(每次约增加 30 秒开销)。等用户侧延迟预算允许后,值得添加此功能。目前,下游故障级联(例如,零字节 TTS → 无声的口型同步视频 → 空白合成视频)要到第 11 步才会显现。 - 尚未实现
text_contentbbox 捕获。capture_websitev1 只会为带 CSSselector的步骤返回action_bboxes。仅包含text_content的步骤不会产生条目。请优先在zoom_target中使用 CSS 选择器,以确保缩放范围覆盖目标。 - 节拍表文案并非确定性的。 同一输入运行两次,会生成不同的 vo_text 和不同的缩放位置。约定的是视觉类型,而不是像素级精确复现。
- 通用 URL 模式的质量因网站而异。 使用语义化标记(
<h1>+ 清晰的<section>+ 命名类钩子)的现代独立开发者 / SaaS 落地页效果良好。知名大型企业网站(apple.com、microsoft.com、amazon.com)会遇到若干已知限制:(a) 机器人检测——页面在无头 Chrome 中可能会提供降级版本,或弹出验证码;第 2.6 步 §A 会对此中止,但启发式规则并不全面;(b) 混淆类名——tile-headline而不是hero-title会导致通用选择器失效;第 2.6 步 §C 的 WebFetch DOM 扫描有所帮助,但并不完美;(c) 滚动触发的动画不会播放——由 IntersectionObserver 驱动的首屏内容展示会在真实用户滚动时触发,而不是在 Playwright 的scrollIntoView时触发;录制帧可能只是静态占位内容;(d) 延迟加载的图片——带有loading="lazy"的 picture/source 元素可能无法在 600ms 或 2500ms 的等待窗口内加载完成;bbox 会落在透明占位元素上。应对方法:发布演示时优先选择更简单或规模更小的营销页面;始终传入--focus "the X feature"来锚定节拍选择;接受知名大型网站需要后续服务端 PR(点击 Cookie 横幅后重试 +wait_until=networkidle+ 通过IntersectionObserverpolyfill 触发动画)。 - Cookie 横幅点击只尝试一次。 第 2.6 步 §B 会根据从 WebFetch DOM 中提取的关闭选择器执行一次
click。如果 WebFetch 的 HTML 不包含该横幅(横幅由 JS 渲染),或选择器有误,点击就会悄然落空——extra_css负载是关键的防护措施。待 Worker 增强:支持为每个click操作提供备用选择器列表,让 Worker 按顺序逐一尝试。
身份验证
如果任何调用返回 401:用户的 OAuth 令牌已过期或尚未签发。下一次经过身份验证的 MCP 调用会自动触发 OAuth(浏览器将打开 @pika.art Google 登录页面)。在非交互式环境中,请设置 MCP_AUTH_TOKEN。
示例
GitHub 模式(了解仓库:扫描 README + 检测实时演示):
/pika:explainer https://github.com/leigest519/OpenGame/pika:explainer https://github.com/anthropics/claude-cookbooks --focus "Claude Code MCP 集成"/pika:explainer https://github.com/openai/whisper --preview(测试新头像时选择启用预览门控)
通用 URL 模式(任何非 GitHub URL — 直接通过页面操作):
/pika:explainer https://pika.art/pika:explainer https://vercel.com --focus "部署工作流"/pika:explainer https://docs.anthropic.com/en/docs/claude-code/plugins/pika:explainer https://your-product-page.com --avatar https://cdn.example.com/me.png --preview