Files
yingqing/docs/todo/后端管线提示词溯源.html
T
seaislee1209andClaude Opus 4.8 901d6920f1 docs(todo): 管线「提示词从哪来」溯源页(说人话+竖向流程+点击溯源+播放)
按用户对齐:竖向流程图,提示词拆 3 块(@图指定谁/画面描述/质量词),每块标来源色
(AI编/你填/代码模板/生成的图)+ 真实示例;重点砸在 3 个最模糊点(第2步人物描述哪来、
第3/4步参考图怎么引)各一个高亮答案;质量词如实标"现在基本没有,只代码模板写死几句"。
点积木高亮溯源 + ▶播放流动,纯静态无依赖,iPad 浏览器可开。11 块交互实测通、0 报错。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 16:09:06 +08:00

240 lines
18 KiB
HTML

<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>AirShelf 管线 · 提示词从哪来(说人话版)</title>
<style>
:root{
--bg:#f7f7f6; --surface:#fff; --ink:#262626; --line:#ececec; --a45:rgba(38,38,38,.45); --a60:rgba(38,38,38,.60); --a75:rgba(38,38,38,.75);
--ai:#fa5d19; --ai-bg:rgba(250,93,25,.10); --ai-bd:rgba(250,93,25,.32);
--you:#2563eb; --you-bg:rgba(37,99,235,.09); --you-bd:rgba(37,99,235,.30);
--tpl:#6b7280; --tpl-bg:rgba(107,114,128,.10); --tpl-bd:rgba(107,114,128,.28);
--img:#16a34a; --img-bg:rgba(22,163,74,.10); --img-bd:rgba(22,163,74,.30);
--mono:'SFMono-Regular',Consolas,Menlo,monospace;
--sans:Inter,'Alibaba PuHuiTi','PingFang SC','Microsoft YaHei',system-ui,sans-serif;
}
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--ink);font-family:var(--sans);line-height:1.62;font-size:15px;-webkit-font-smoothing:antialiased}
.wrap{max-width:860px;margin:0 auto;padding:32px 20px 120px}
h1{font-size:26px;font-weight:600;letter-spacing:-.02em;margin:0 0 4px}
.sub{font-family:var(--mono);font-size:12px;color:var(--a45);letter-spacing:.03em;margin-bottom:18px}
.lead{font-size:14.5px;color:var(--a75);margin:0 0 18px}
.lead b{color:var(--ink)}
/* legend + play */
.bar{position:sticky;top:0;z-index:50;background:rgba(247,247,246,.92);backdrop-filter:blur(8px);padding:12px 0;margin-bottom:8px;border-bottom:1px solid var(--line)}
.legend{display:flex;gap:8px;flex-wrap:wrap;align-items:center}
.chip{display:inline-flex;align-items:center;gap:6px;font-size:12.5px;padding:4px 10px;border-radius:999px;cursor:default;border:1px solid transparent}
.chip .d{width:10px;height:10px;border-radius:3px}
.chip.ai{background:var(--ai-bg);color:var(--ai);border-color:var(--ai-bd)} .chip.ai .d{background:var(--ai)}
.chip.you{background:var(--you-bg);color:var(--you);border-color:var(--you-bd)} .chip.you .d{background:var(--you)}
.chip.tpl{background:var(--tpl-bg);color:var(--tpl);border-color:var(--tpl-bd)} .chip.tpl .d{background:var(--tpl)}
.chip.img{background:var(--img-bg);color:var(--img);border-color:var(--img-bd)} .chip.img .d{background:var(--img)}
.play{margin-left:auto;display:inline-flex;align-items:center;gap:7px;background:var(--ink);color:#fff;border:0;border-radius:999px;padding:8px 16px;font-size:13px;font-family:inherit;cursor:pointer;font-weight:500}
.play:active{transform:scale(.97)}
.hint{font-size:12px;color:var(--a45);margin:8px 0 22px;font-family:var(--mono)}
/* flow spine */
.flow{position:relative;padding-left:34px}
.flow::before{content:'';position:absolute;left:11px;top:8px;bottom:24px;width:2px;background:var(--line)}
.flow .runner{position:absolute;left:6px;width:12px;height:12px;border-radius:50%;background:var(--ai);box-shadow:0 0 0 4px var(--ai-bg);opacity:0;transition:top .5s cubic-bezier(.4,0,.2,1),opacity .3s;z-index:3}
.node{position:relative;margin-bottom:16px}
.node::before{content:'';position:absolute;left:-28px;top:18px;width:14px;height:14px;border-radius:50%;background:#fff;border:2px solid var(--line);transition:border-color .3s,box-shadow .3s;z-index:2}
.node.lit::before{border-color:var(--ai);box-shadow:0 0 0 5px var(--ai-bg)}
.card{background:var(--surface);border:1px solid var(--line);border-radius:12px;padding:16px 18px;transition:box-shadow .3s,border-color .3s}
.node.lit .card{border-color:var(--ai-bd);box-shadow:0 6px 22px rgba(250,93,25,.08)}
.st{display:flex;align-items:baseline;gap:10px;margin-bottom:4px}
.st .no{font-family:var(--mono);font-size:12px;color:var(--ai);font-weight:600}
.st .nm{font-size:17px;font-weight:600}
.st .who{margin-left:auto;font-size:12px;color:var(--a45);font-family:var(--mono)}
.desc{font-size:14px;color:var(--a75);margin:2px 0 12px}
.desc b{color:var(--ink)}
/* prompt 3-block */
.pp-h{font-size:12px;color:var(--a45);font-family:var(--mono);letter-spacing:.04em;margin:10px 0 7px;text-transform:uppercase}
.blocks{display:flex;flex-direction:column;gap:7px}
.blk{border-radius:9px;padding:9px 12px;cursor:pointer;border:1px solid;transition:transform .12s,box-shadow .2s;position:relative}
.blk:active{transform:scale(.99)}
.blk .bt{font-size:13.5px;font-weight:600;display:flex;align-items:center;gap:7px}
.blk .bt .tg{font-family:var(--mono);font-size:10.5px;padding:1px 7px;border-radius:4px;font-weight:500}
.blk .src{font-size:12.5px;margin-top:3px;line-height:1.5}
.blk.ai{background:var(--ai-bg);border-color:var(--ai-bd)} .blk.ai .tg{background:var(--ai);color:#fff} .blk.ai .src{color:var(--ai)}
.blk.you{background:var(--you-bg);border-color:var(--you-bd)} .blk.you .tg{background:var(--you);color:#fff} .blk.you .src{color:var(--you)}
.blk.tpl{background:var(--tpl-bg);border-color:var(--tpl-bd)} .blk.tpl .tg{background:var(--tpl);color:#fff} .blk.tpl .src{color:var(--tpl)}
.blk.img{background:var(--img-bg);border-color:var(--img-bd)} .blk.img .tg{background:var(--img);color:#fff} .blk.img .src{color:var(--img)}
.blk.flash{box-shadow:0 0 0 3px currentColor}
/* example */
.eg{font-family:var(--mono);font-size:12px;line-height:1.7;background:#1e2330;color:#cfd6e0;border-radius:9px;padding:12px 14px;margin-top:10px;white-space:pre-wrap;overflow-x:auto}
.eg .a{color:#ff9e6e}.eg .g{color:#7dd0ad}.eg .b{color:#7fb0ff}.eg .m{color:#8a96a6}
/* fuzzy callout */
.ask{background:#fff;border:1.5px solid var(--ai);border-radius:11px;padding:13px 15px;margin-top:13px}
.ask .q{font-size:14px;font-weight:600;color:var(--ai);display:flex;align-items:center;gap:7px}
.ask .q .i{width:20px;height:20px;border-radius:50%;background:var(--ai);color:#fff;display:grid;place-items:center;font-size:13px;flex-shrink:0}
.ask .a{font-size:14px;color:var(--ink);margin-top:6px}
.ask .a b{color:var(--ai)}
.field{font-family:var(--mono);font-size:12px;background:rgba(38,38,38,.06);padding:1px 6px;border-radius:4px;color:var(--ink);white-space:nowrap}
.arrow{color:var(--a45);font-family:var(--mono)}
.foot{margin-top:34px;padding:16px 18px;background:var(--surface);border:1px solid var(--line);border-radius:12px;font-size:13.5px;color:var(--a75)}
.foot b{color:var(--ink)}
</style>
</head>
<body>
<div class="wrap">
<h1>这条视频是怎么"攒"出来的</h1>
<div class="sub">// 商品 → 脚本 → 角色/场景图 → 分镜图 → 视频 · 每句提示词从哪来</div>
<p class="lead">从上往下看一条竖线。重点只有一件事:<b>喂给 AI 的每句提示词,到底是谁写的</b> —— AI 临场编的?你填的?代码里写死的套话?还是之前生成的图。<b>点任意一块</b>看它从哪来;点右上<b>▶ 播放</b>看数据一路怎么流下来。</p>
<div class="bar">
<div class="legend">
<span class="chip ai"><span class="d"></span>AI 编的</span>
<span class="chip you"><span class="d"></span>你填的</span>
<span class="chip tpl"><span class="d"></span>代码死模板</span>
<span class="chip img"><span class="d"></span>之前生成的图</span>
<button class="play" id="play">▶ 播放流程</button>
</div>
</div>
<div class="hint">// 一句话:提示词其实就 3 块 —— ① @图(指定谁是谁)· ② 画面描述 · ③ 质量词。点积木看来源。</div>
<div class="flow">
<div class="runner" id="runner"></div>
<!-- 0 商品 -->
<div class="node" data-step="0">
<div class="card">
<div class="st"><span class="no">0</span><span class="nm">商品信息</span><span class="who">你填的</span></div>
<div class="desc">你建商品时填的:<b>商品名、卖点、主图</b>。整条链的起点。商品主图后面会一直当"参考图"用。</div>
<div class="blocks">
<div class="blk you"><div class="bt"><span class="tg">你填</span>商品名 / 卖点 / 商品主图</div><div class="src">→ 喂给第1步脚本 AI;商品主图 → 后面所有"商品参考图"都用它</div></div>
</div>
</div>
</div>
<!-- 1 脚本 -->
<div class="node" data-step="1">
<div class="card">
<div class="st"><span class="no">1</span><span class="nm">脚本 AI(源头)</span><span class="who">AI · 1 次文本调用</span></div>
<div class="desc"><b>所有"人话"都在这一步由 AI 写好</b>:它读商品信息,按一套"系统说明书"(skill)一口气产出脚本。<b>角色长什么样、每镜画面、谁出场</b> —— 全在这里定,后面不再重新问 AI。</div>
<div class="pp-h">AI 这一步吐出的东西(后面全靠它)</div>
<div class="blocks">
<div class="blk ai"><div class="bt"><span class="tg">AI编</span>角色 / 场景 / 商品 "实体"</div><div class="src">每个带:名字 <span class="field">name</span> + 长相描述 <span class="field">visual_prompt</span>(AI 自动写,小白不打字)</div></div>
<div class="blk ai"><div class="bt"><span class="tg">AI编</span>每镜画面 + 这镜用谁</div><div class="src">每 15 秒一镜:画面描述 <span class="field">visual</span> / 旁白对白 <span class="field">narration</span> + 这镜引用哪些实体 <span class="field">entity_refs</span></div></div>
</div>
<div class="ask">
<div class="q"><span class="i">?</span>AI 凭什么知道要提取角色/场景、还自动写长相?</div>
<div class="a">靠一份<b>系统说明书 skill</b>(<span class="field">skills/ecommerce-video-script/</span>)。里面明文要求 AI:"识别角色/场景/商品,给每个写一份全脚本通用的长相描述(visual_prompt),保证多镜同一张脸"。<b>所以"提取"就发生在这一步,不是第2步。</b></div>
</div>
<div class="eg"><span class="m"># AI 产出(节选)</span>
角色 <span class="a">c1</span> 名=<span class="g">"通勤女主"</span> 长相=<span class="g">"26岁都市女性,自然妆,米色针织衫…"</span>
场景 <span class="a">s1</span> 名=<span class="g">"早高峰地铁"</span>
镜3 画面=<span class="g">"女主挤地铁戴上耳机"</span> 用谁=<span class="a">[c1, s1, 商品]</span></div>
</div>
</div>
<!-- 2 基础资产 -->
<div class="node" data-step="2">
<div class="card">
<div class="st"><span class="no">2</span><span class="nm">生成角色/场景/商品图</span><span class="who">AI · 画图</span></div>
<div class="desc">把第1步 AI 写的<b>长相描述</b>当提示词,生成一张角色立绘 / 场景图。生成完你"采用",这张图贴上<b>标签 = 角色名</b>,第3步就靠这名字把图对回去。</div>
<div class="ask">
<div class="q"><span class="i">?</span>你最模糊的:第2步怎么"正确拿到人物描述"?</div>
<div class="a"><b>不是这步重新提取</b> —— 是直接读第1步存好的 <span class="field">cast_prompts[角色名]</span>(就是 AI 写的那段长相)。链路:<span class="arrow">AI写长相 → 存进 cast_prompts → 第2步读出来当提示词</span>。中间纯搬运,不再问 AI。</div>
</div>
<div class="pp-h">这步给画图 AI 的提示词 = 2 块</div>
<div class="blocks">
<div class="blk ai"><div class="bt"><span class="tg">AI编</span>② 画面描述 = 角色长相</div><div class="src">直接用第1步 <span class="field">visual_prompt</span>(AI 写的"26岁都市女性…")</div></div>
<div class="blk tpl"><div class="bt"><span class="tg">模板</span>③ 质量词(写死)</div><div class="src">"电商真人模特,自然妆容,柔和影棚光,真实质感,纯色背景" —— 代码模板,每次一样</div></div>
</div>
<div class="eg"><span class="m"># 人物图提示词</span>
电商真人模特,氛围正面全身照,<span class="g">{26岁都市女性,自然妆,米色针织衫…}</span>,
自然妆容,柔和影棚光,真实质感,单人,纯色背景</div>
<div class="desc" style="margin:11px 0 0;font-size:13px"><b>商品图特殊</b>:多一块 ① —— 拿商品真实主图当参考(锁包装/Logo 一致),所以商品三视图很还原。</div>
</div>
</div>
<!-- 3 故事板 -->
<div class="node" data-step="3">
<div class="card">
<div class="st"><span class="no">3</span><span class="nm">分镜图(故事板)</span><span class="who">AI · 多图合成</span></div>
<div class="desc">每 15 秒一镜出一张分镜图。提示词把<b>参考图点名</b> + <b>这镜画面</b> + <b>质量词</b>拼一起,喂给画图 AI 合成。</div>
<div class="ask">
<div class="q"><span class="i">?</span>你最模糊的:第3步怎么"正确引用参考图"?</div>
<div class="a">这镜脚本里有 <span class="field">entity_refs</span>(第1步 AI 写好的"这镜用谁",如 c1+s1+商品)。代码照着它:<span class="arrow">实体 → 按 标签=名字 找你第2步采用的图</span>。商品没单独生成 → <b>自动用商品主图兜底</b>(这条我已修)。再按 角色→商品→场景 排好,对上 @图1/@图2/@图3。</div>
</div>
<div class="pp-h">这步提示词 = 3 块(点每块看来源)</div>
<div class="blocks">
<div class="blk img"><div class="bt"><span class="tg"></span>① @图N:指定谁是谁</div><div class="src">@图1=角色图 @图2=商品图 @图3=场景图 ← 你第2步采用的图 +(商品)主图兜底</div></div>
<div class="blk ai"><div class="bt"><span class="tg">AI编</span>② 画面描述</div><div class="src">这镜脚本的 <span class="field">visual</span>(画面)+ <span class="field">narration</span>(对白/旁白)—— 第1步 AI 写的</div></div>
<div class="blk tpl"><div class="bt"><span class="tg">模板</span>③ 质量词(写死)</div><div class="src">"严格锁同一张脸/同一商品,9:16 竖屏,一镜一图,画面清晰" —— 代码模板</div></div>
</div>
<div class="eg"><span class="b">【设定】</span>@图1是<span class="g">通勤女主</span>(角色),@图2是<span class="g">耳机</span>(商品),@图3是<span class="g">地铁</span>(场景)。
根据脚本生成导演故事板。
<span class="b">【画面】</span><span class="g">女主挤地铁戴上耳机…</span>
请严格保持各参考图同一张脸/同一商品,9:16 竖屏,画面清晰。</div>
</div>
</div>
<!-- 4 视频 -->
<div class="node" data-step="4">
<div class="card">
<div class="st"><span class="no">4</span><span class="nm">视频片段(Seedance)</span><span class="who">AI · 出视频带音</span></div>
<div class="desc">每镜 → 一段视频。跟第3步几乎一样,<b>多塞一张刚生成的分镜图</b>当参考,再交给 Seedance 出片(自带音效+人声)。</div>
<div class="ask">
<div class="q"><span class="i">?</span>你最模糊的:第4步参考图怎么引?</div>
<div class="a"><b>跟第3步同一套</b>(一样按 entity_refs 找采用的角色/场景/商品图),<b>额外加一张第3步出的分镜图</b>。所以视频既锁人/锁商品、又跟分镜构图一致。</div>
</div>
<div class="pp-h">这步提示词 = 3 块 + 1 张分镜图</div>
<div class="blocks">
<div class="blk img"><div class="bt"><span class="tg"></span>① @图N:谁是谁 + @分镜图</div><div class="src">角色/场景/商品图(同第3步)+ 末位加第3步生成的分镜图</div></div>
<div class="blk ai"><div class="bt"><span class="tg">AI编</span>② 画面描述</div><div class="src">同一镜脚本的 <span class="field">visual</span> / <span class="field">narration</span></div></div>
<div class="blk tpl"><div class="bt"><span class="tg">模板</span>③ 质量词(写死)</div><div class="src">"15s · 9:16 竖屏电商带货,镜头稳定,商品露出清晰,节奏有转化感" —— 代码模板</div></div>
</div>
<div class="eg"><span class="b">【设定】</span>@图1=<span class="g">女主</span> @图2=<span class="g">地铁</span> @图3=<span class="g">耳机</span>
<span class="b">【分镜】</span>根据@图4分镜图生成「耳机」短视频。
<span class="b">【脚本】</span><span class="g">女主挤地铁戴上耳机…</span>
15s · 9:16 竖屏带货,镜头稳定,商品露出清晰。</div>
</div>
</div>
</div>
<div class="foot">
<b>关于"质量词"(③):你猜对了,现在基本没有独立系统。</b> 只有上面每步那几句质量/规格词<b>写死在代码模板里</b>(画面清晰、镜头稳定、9:16、真实质感…),不能配置、各步各一套。<br><br>
<b>一句话收口:</b> ①@图(谁是谁)来自<span style="color:var(--img)">你采用的图/商品主图</span> · ②画面描述来自<span style="color:var(--ai)">第1步 AI 写的脚本</span> · ③质量词是<span style="color:var(--tpl)">代码死模板</span>。三步图/视频都这套,只是参考图越往后越多(故事板用基础资产图,视频再加分镜图)。
</div>
</div>
<script>
// 点积木:闪一下 + 顶它所在 node 高亮
document.querySelectorAll('.blk').forEach(function(b){
b.addEventListener('click', function(){
document.querySelectorAll('.node').forEach(n=>n.classList.remove('lit'));
document.querySelectorAll('.blk').forEach(x=>x.classList.remove('flash'));
var node = b.closest('.node'); if(node) node.classList.add('lit');
b.classList.add('flash');
setTimeout(function(){ b.classList.remove('flash'); }, 900);
});
});
// ▶ 播放:runner 顺着竖线流下来,每到一个 node 点亮
var nodes = Array.from(document.querySelectorAll('.node'));
var runner = document.getElementById('runner');
var flow = document.querySelector('.flow');
var btn = document.getElementById('play');
var playing = false;
btn.addEventListener('click', function(){
if(playing) return; playing = true;
nodes.forEach(n=>n.classList.remove('lit'));
runner.style.opacity = 1;
var i = 0;
function step(){
if(i>0) nodes[i-1] && nodes[i-1].classList.remove('lit');
if(i>=nodes.length){ runner.style.opacity = 0; playing=false; nodes[nodes.length-1].classList.add('lit'); return; }
var n = nodes[i];
var top = n.offsetTop + 12;
runner.style.top = top + 'px';
n.classList.add('lit');
n.scrollIntoView({behavior:'smooth', block:'center'});
i++; setTimeout(step, 1100);
}
step();
});
</script>
</body>
</html>