AI 产品教学向长文自动生成 SOP
#这份文档是干嘛的
固化这样一条流水线:给一个 AI 产品的官网链接 → 自动抓官网 + 全网调研 → 出一篇 3000-7000 字的上手教程长文 → 自动配「产品真实界面截图」(不是图库意境图)。
把本文件拖进新的 Claude Code 会话,就能照着复现整条链路——无论是在现有项目里加功能,还是从零搭一个独立网站(本文最后一节专门讲迁出与扩展到视频脚本)。
沉淀自 2026-08-05 在 ~/personal/media-studio 上的实践:功能已上线 https://studio.saveme505.help/teach,首篇成品是 Gemini Notebook(原 NotebookLM)教程,线上实测调研 64 秒、出稿+配图 67 秒、6971 字、4 张真实截图、零告警。
#环境与前置条件
| 项 | 要求 | 说明 |
|---|---|---|
| 运行时 | Node.js 20+ / Next.js 16 / React 19 | Vercel serverless(Node runtime,非 Edge) |
| 抓取与搜索 | Firecrawl API Key | 一把 key 通吃「抓网页 + 全网搜索 + 图片搜索 + 网页截图」四件事。key 从 ~/Desktop/配置信息/firecrawl-api-key.md 读取 |
| 文案引擎 | 云雾中转(yunwu.ai)等 OpenAI 兼容接口 | 配置从 ~/Desktop/配置信息/云雾API-中转/README.md 读取。模型能力是本流水线的质量瓶颈,见「踩过的坑」第 4 条 |
| 图片存储 | Vercel Blob(BLOB_READ_WRITE_TOKEN) |
必需,不是可选项,原因见「踩过的坑」第 5 条 |
| SDK | ai (Vercel AI SDK v5)、@ai-sdk/openai-compatible、@vercel/blob |
Firecrawl 不装 SDK,直接 fetch REST v2 |
| 数据库 | 任意(本例 Supabase Postgres) | 不是必需——纯生成链路可以完全不落库 |
Vercel 计划限制:Hobby 免费版单函数 maxDuration 上限 300 秒。这个数字直接决定了架构必须拆成两步,见「踩过的坑」第 1 条。
#正确流程(照着做)
#第 0 步:验证 Firecrawl 的四个能力
在写任何代码之前先用 curl 打通,确认 key 有效、返回结构符合预期。以下四条命令都是本轮真实跑通的。
KEY=$(grep -oE 'fc-[A-Za-z0-9]+' ~/Desktop/配置信息/firecrawl-api-key.md | head -1)
# ① 抓正文 + 首屏截图(一次请求同时拿到)
curl -s -X POST https://api.firecrawl.dev/v2/scrape \
-H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{"url":"https://notebooklm.google/","formats":["markdown",{"type":"screenshot","fullPage":false,"quality":85,"viewport":{"width":1440,"height":900}}],"onlyMainContent":true,"waitFor":2500}' \
| python3 -c "import json,sys;d=json.load(sys.stdin)['data'];print(list(d.keys()));print(str(d.get('screenshot'))[:100])"
# ② 全网搜索(web 源)
curl -s -X POST https://api.firecrawl.dev/v2/search \
-H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{"query":"NotebookLM review 2026","sources":[{"type":"web"}],"limit":5}'
# ③ 图片搜索(images 源)—— 这就是「谷歌图片」的平替
curl -s -X POST https://api.firecrawl.dev/v2/search \
-H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{"query":"NotebookLM audio overview interface","sources":[{"type":"images"}],"limit":8}'成功判据:① 返回 data.screenshot 是一个 storage.googleapis.com/firecrawl-scrape-media/... 的签名 URL,data.metadata.ogImage 有值;② data.web[] 每条含 url/title/description;③ data.images[] 每条含 imageUrl/url/imageWidth/imageHeight,且 creditsUsed: 2。
#第 1 步:调研(四小步,约 60 秒)
① 抓官网正文 scrapePage(url) → markdown(截 14000 字)
② 规划搜索方向 模型读官网正文 → 产品名 + 一句话定位 + 5-6 条搜索式
③ 并发搜 + 抓正文 searchWeb(每条搜索式, limit 5) → 去重、排除官网自身域名
→ 取前 8 条 → scrapePage 并发抓回正文(并发数 4)
④ 提炼底稿 官网正文 + 7-8 篇第三方正文 → 模型提炼成十节结构、逐条带出处的底稿搜索式规划的提示词要点(这条决定了调研广度,是整条链路质量的第一道阀门):
- 强制覆盖六个互不重复的角度:官方公告 / 第三方评测 / 上手教程 / 竞品对比 / 真实吐槽(Reddit 等社区)/ 定价与配额
- 英文为主,至少一条中文(覆盖中文社区的用法与「国内能不能用」)
- 每条带年份,避免搜到过时资料
- 用「产品名 + 角度词」,不要写成整句问句
提炼底稿的提示词要点(第二道阀门,防幻觉全靠它):
- 明确告知模型:下游写作环节只能看到这份底稿,看不到原始资料
- 十节结构:是什么 / 时间线与改名 / 功能清单 / 定价与配额 / 上手步骤 / 使用场景 / 进阶技巧 / 限制与坑 / 竞品对比 / 可信度备注
- 每条结尾括号标来源 URL;官方来源与第三方来源分开标注
- 只写资料里有的,不许"合理推断"
- 价格/配额这类会过期的数字,逐条标注来源与查询日期;不同来源打架时如实写出冲突("官网写 A,第三方评测写 B"),不许自己拍板
#第 2 步:出稿(约 40 秒)
把底稿 + 来源清单 + 用户附加指令喂给模型,按固定骨架写长文。骨架八段:
开头(用读者天天遇到的场景切入,不许铺垫行业趋势)
→ 它到底是什么(核心机制 + 所以对你意味着什么)
→ 核心功能逐项拆解(全文主体,每项写:干什么 / 效果如何 / 免费还是付费,并明确哪些是噱头)
→ 上手步骤(从零到第一个结果,能写到界面元素名就写)
→ 进阶技巧(5-8 条,每条写清怎么做 + 什么时候用得上)
→ 限制、坑与风险(必须写实,教学文的信任感全在这里)
→ 竞品对比(给判断,不要只罗列)
→ 适合谁 / 不适合谁(明确选型建议收尾,不许喊口号)三条硬约束写进提示词:① 事实全部来自底稿,没有的不写;② 不许编造第一人称体验(不许"我实测了三天",要转述就写"据官方文档");③ 会过期的数字必须写「截至 X 年 X 月」+ 提醒以官网为准。
#第 3 步:配产品真实截图(约 25 秒)
① 模型挑 2-4 个插图点 → 每个点给一条英文图片搜索式 + 中文图注
② 第 1 个点优先用官网首屏现截(Firecrawl screenshot 1440×900),失败退 og:image
③ 其余点走 searchImages,按来源域名分级择优
④ 每张图立刻下载 → 转存 Vercel Blob → 用 Blob 的长期 URL 插回正文来源分级(决定采不采用,也决定图注怎么标出处):
| 等级 | 判定 | 图注标注 |
|---|---|---|
official_site |
图片直链或来源页在产品自己的域名下 | 图片来源:产品官网 |
official_blog |
命中 blog.google / gweb-uniblog-publish-prod / support.google.com / openai.com / anthropic.com 等官方渠道 |
图片来源:官方渠道 xxx |
tech_media |
命中 The Verge / TechCrunch / XDA / 9to5Google / Android Police / MakeUseOf / ZDNet / Engadget / Ars Technica / TestingCatalog | 图片来源:xxx |
other |
其余 | 图片来源:xxx |
候选图过滤:宽度 < 600px 丢弃;宽高比 < 0.8 或 > 3.2 丢弃(横幅/图标);文件名含 logo|icon|favicon|avatar|sprite 丢弃。
#第 4 步:验证
# 本地预览(不落库,只出 md 看效果)
npm run teach:preview -- "https://产品官网/" "附加指令"
# 线上端到端(需要先登录拿 cookie)
curl -s -c /tmp/c.txt -X POST https://studio.saveme505.help/api/auth/login \
-H 'Content-Type: application/json' -d '{"password":"<从 .env.local 的 ACCESS_PASSWORD 读取>"}'
curl -s -m 300 -b /tmp/c.txt -X POST https://studio.saveme505.help/api/teach/research \
-H 'Content-Type: application/json' \
-d '{"url":"https://notebooklm.google/","extra":"写给中文 AI 重度用户"}' -o /tmp/r.json
curl -s -m 300 -b /tmp/c.txt -X POST https://studio.saveme505.help/api/teach/write \
-H 'Content-Type: application/json' \
-d "{\"topicId\":\"$(python3 -c "import json;print(json.load(open('/tmp/r.json'))['topicId'])")\"}"成功判据:research 返回 stats: {queries: 6, pagesFetched: 5-8} 且 sources 有 6-9 条;write 返回 draft.content 长度 > 3000、draft.meta.shots 有 2-4 张且 origin 里有 official_site 或 official_blog、warnings 为空数组。
#踩过的坑
#1. 单个 serverless 函数塞不下整条链路
- 现象:调研要抓十几个页面(约 60 秒),出稿要写四五千字(约 40 秒),配图还要搜图+下载+转存(约 25 秒)。串在一个函数里稳稳撞 Vercel Hobby 的 300 秒上限。
- 原因:抓取和长文生成都是分钟级操作,叠加后没有余量。
- 正确做法:拆成两个 API(
/research和/write),前端顺序调用,中间把调研结果落库(或存 KV)。附带收益:调研底稿可以先给用户过目,不满意直接换链接重来,不用白烧出稿的 token。两个函数各自export const maxDuration = 300。
#2. Firecrawl 会直接拒绝抓某些站点
- 现象:
Firecrawl /scrape 失败:We apologize for the inconvenience but we do not support this site. - 原因:Firecrawl 对部分站点(含某些大厂站点)有黑名单,付费也抓不了。
- 正确做法:单点失败绝不能阻断整条链路。用并发池逐项 try/catch,失败的返回 null 后过滤掉;只有「官网都抓不到」才抛错终止(那种情况下写出来的必然是编的)。实测每次调研有 1-3 篇会失败,属正常。
#3. Kimi 新模型强制 temperature = 1
- 现象:
invalid temperature: only 1 is allowed for this model(kimi-k2.5 / k2.6 / k3 全部如此)。 - 原因:月之暗面新模型不再支持自定义 temperature。
- 正确做法:要么改用支持 temperature 的老模型(
moonshot-v1-128k),要么在 provider 元信息里加「强制 temperature」的字段。本轮选择直接换引擎。
#4. ⚠️ 隐性坑:弱模型会把官网的营销标题当功能名照抄
- 现象:不报错,链路全绿,但产出的稿子完全没有信息量。用
moonshot-v1-128k跑出来的正文,"核心功能"一节列的是Upload your sources、Instant insights、Spark new ideas——这些全是官网首页的营销 slogan,不是功能名;每条下面写"实际效果:它能够提供即时的洞见和摘要"这种同义反复;Audio Overview / Video Overview / Mind Map / Deep Research 这些真正的功能一个没提。 - 原因:官网正文里营销标题的视觉权重最高,弱模型抓不住"哪些是功能、哪些是广告词"的区别,也不会去调研底稿里找第三方补充的真实功能清单。
- 正确做法:用 deepseek-v4 级别以上的模型出稿。同一条链路、同一份提示词,换成云雾的
deepseek-v4-flash后,正文自动变成「按实用价值排序、标注免费/付费、明说哪些是噱头」的结构,长度从 3221 字涨到 5000-7000 字。这条是全流水线最贵的坑——它不报错,容易让人以为功能做完了。
#5. ⚠️ 隐性坑:Firecrawl 的截图 URL 会过期
- 现象:截图接口返回的是
storage.googleapis.com/firecrawl-scrape-media/...?GoogleAccessId=...&Expires=1786520926&Signature=...这样的签名链接。当场访问一切正常,直接写进稿子几天后全部变成裂图。 - 原因:Google 云存储签名 URL 有过期时间(
Expires参数是 Unix 时间戳,实测约 7 天;官方文档另有 24 小时的口径,以更短的为准)。 - 正确做法:拿到 URL 后立刻下载并转存到自己的存储(Vercel Blob / R2 / S3),入库存转存后的长期 URL。第三方媒体的图直链同理——随时可能防盗链或改版失效。
#6. 转存图片不带浏览器 UA,Google 的 CDN 返回 HTML 拦截页
- 现象:
转存 Blob 失败:目标不是图片,URL 是storage.googleapis.com/gweb-uniblog-publish-prod/images/xxx.webp(Google 官方博客的图床)。 - 原因:裸 fetch 没有 UA/Referer,被当成爬虫,返回的是 HTML 而不是图片字节。
- 正确做法:下载图片时带上完整的浏览器请求头:
const res = await fetch(imageUrl, {
signal: AbortSignal.timeout(30_000),
cache: "no-store",
headers: {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0 Safari/537.36",
Accept: "image/avif,image/webp,image/apng,image/*,*/*;q=0.8",
Referer: new URL(imageUrl).origin + "/",
},
});顺带把报错信息改成带 content-type 的(目标不是图片(content-type: text/html)),否则这个坑根本定位不到。
#7. ⚠️ 隐性坑:产品名带括号补充,会把图片搜索结果搅烂
- 现象:模型把产品名识别成
Gemini Notebook (formerly NotebookLM),这串拼进图片搜索词后,4 个插图点只配上 2 张。 - 原因:搜索引擎把括号里的内容也当关键词,命中率骤降。
- 正确做法:产品名字段做清洗——剥掉括号补充、按
| | - – — : :切第一段。新旧名的说明交给调研底稿和正文去写,不要塞进这个字段。
const productName = String(plan.productName ?? official.title ?? "")
.replace(/[((].*?[))]/g, "")
.split(/[||\-–—::]/)[0]
.trim() || official.title;#8. ⚠️ 隐性坑:模型把标题重复写进正文首行
- 现象:schema 里
title和content是两个字段,模型仍然在content第一行又写一遍# 标题。发到公众号就是标题出现两遍。 - 原因:模型的 markdown 写作惯性。
- 正确做法:出稿后确定性剥掉正文首行的一级标题(分节标题一律从
##起):
function stripLeadingTitle(content: string): string {
const trimmed = content.trimStart();
const m = trimmed.match(/^#\s+[^\n]+\n+/);
return m ? trimmed.slice(m[0].length) : content;
}#9. 中转通道对 json_schema 的顶层键名约束不严
- 现象:schema 里要求返回
{ images: [...] },经聚合中转(云雾等)的模型有时返回{ illustrations: [...] }。内容是对的,只是键名漂了,代码取object.images拿到 undefined,整轮配图白跑。 - 原因:中转层对
response_format: json_schema的转译不严格。 - 正确做法:取不到目标键时,退而找对象里第一个数组值;数字字段一律
Number()收编(模型可能给"12"字符串):
const raw = object as Record<string, unknown>;
const rawList = Array.isArray(raw.images)
? raw.images
: ((Object.values(raw).find(Array.isArray) as unknown[]) ?? []);#10. DeepSeek 官方 key 失效,与「欠费」不是一回事
- 现象:
Authentication Fails, Your api key: ****3ae5 is invalid(HTTP 401)。 - 原因:这是 key 本身失效(被吊销/重置),不是余额耗尽——余额耗尽报的是
Insufficient Balance。 - 正确做法:先用
curl https://api.deepseek.com/user/balance区分两种情况。本轮的解法是继续用云雾中转(线上引擎本来就是云雾,未受影响)。~/Desktop/配置信息/deepseek-api-key.md里那把已作废。
#11. 本地 DATABASE_URL 是占位值,本地跑不了任何落库流程
- 现象:
Client network socket disconnected before secure TLS connection was established。 - 原因:
~/personal/media-studio/.env.local里DATABASE_URL是postgresql://placeholder:***@127.0.0.1:5432/placeholder,只够本地npm run build通过。 - 正确做法:本地只做「不落库的纯生成预览」(写个
teach-preview.ts脚本直接调 lib 函数,产出 md 文件);正式产出一律打生产站 API。注意配置读取函数(getStoredConfig/getPromptOverrides)都要对 DB 失败做.catch()兜底退回默认值,否则本地预览也会被 DB 拖死。
#已知限制 / 死路
| 方向 | 结论 | 原因 |
|---|---|---|
| Google Custom Search JSON API(图片搜索) | ❌ 别走 | 已不对新用户开放,且官方公告将于 2027-01-01 完全下线。老账号能用也是等死。 |
| Bing Image Search API | ❌ 已死 | 微软全系列 Search API 于 2025-08-11 彻底退役,不接受新签约,现有实例已下线。迁到 Azure AI Agents 的 Grounding with Bing 成本高 40%~483%。 |
| 自建 Puppeteer 截 SaaS 内部界面 | ❌ 基本不可行 | 需要登录态的产品(NotebookLM/ChatGPT 这类)会触发验证码与风控;Vercel serverless 上跑无头浏览器比本地慢 4-8 倍,单次约 50 秒,容易超时。 |
| 直接搬运图片搜索里的第三方博客截图 | ⚠️ 风险中高 | 授权状态不明,公众号被投诉会限流/删文。只采官方渠道与认可的科技媒体,且必须标出处。 |
| Serper.dev / SerpAPI | 🟡 可作三级兜底 | 能用、便宜(约 $1/1000 起),但既然 Firecrawl 的 images 源已经够用,没必要多接一家、多管一把 key。 |
| 本流水线覆盖不了的场景 | — | ① 需要登录才能看到的产品内部界面(只能用官网宣传图代替);② 官网是纯 SPA 且 Firecrawl 抓不到正文的产品;③ 太新、全网没有第三方评测的产品(底稿会很薄,写出来必然空洞)。 |
#可复用的代码/脚本
现有实现全部在 ~/personal/media-studio,文档见该项目的 docs/teach-article.md。
| 路径 | 作用 |
|---|---|
lib/firecrawl.ts |
Firecrawl REST v2 封装:scrapePage / searchWeb / searchImages。迁出时这个文件可以整个复制,零项目耦合 |
lib/teach-research.ts |
调研四步编排(抓官网 → 规划搜索式 → 并发搜抓 → 提炼底稿),含并发池 mapWithLimit |
lib/teach-generate.ts |
出稿(schema + 骨架提示词 + 剥重复标题) |
lib/teach-shots.ts |
产品真实截图配图(来源分级 / 候选过滤 / 转存 Blob / 插回正文) |
app/api/teach/{research,write,ping}/route.ts |
两步 API + 连通性测试 |
app/teach/page.tsx |
两步式前端页面 |
scripts/teach-preview.ts |
本地预览,不落库,产出 预览-*.md |
prompts/system/teach/*.md、prompts/pipeline/teach-article.md |
四条提示词(调研方向规划 / 调研提炼 / 长文骨架 / 截图选点) |
#Firecrawl 封装核心(迁出时直接抄这段)
const API_BASE = "https://api.firecrawl.dev/v2";
async function callFirecrawl(path: string, body: Record<string, unknown>, timeout: number) {
const res = await fetch(`${API_BASE}${path}`, {
method: "POST",
headers: { Authorization: `Bearer ${process.env.FIRECRAWL_API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify(body),
signal: AbortSignal.timeout(timeout),
cache: "no-store",
});
const json = await res.json().catch(() => null);
if (!res.ok || !json?.success) throw new Error(`Firecrawl ${path} 失败:${json?.error || `HTTP ${res.status}`}`);
return json.data ?? {};
}
// 抓正文(+ 可选首屏截图)
scrapePage: callFirecrawl("/scrape", {
url,
formats: withScreenshot
? ["markdown", { type: "screenshot", fullPage: false, quality: 85, viewport: { width: 1440, height: 900 } }]
: ["markdown"],
onlyMainContent: true,
waitFor: withScreenshot ? 3000 : 1500, // SPA 不等一下截出来是白屏
}, 60_000)
// 全网搜索
searchWeb: callFirecrawl("/search", { query, sources: [{ type: "web" }], limit: 6 }, 45_000)
// → data.web[]: { url, title, description }
// 图片搜索(「谷歌图片」平替)
searchImages: callFirecrawl("/search", { query, sources: [{ type: "images" }], limit: 10 }, 45_000)
// → data.images[]: { imageUrl, url, imageWidth, imageHeight, title }#并发池(Firecrawl 限流友好,也避免 serverless 内存尖峰)
async function mapWithLimit<T, R>(items: T[], limit: number, fn: (item: T) => Promise<R>): Promise<(R | null)[]> {
const out: (R | null)[] = new Array(items.length).fill(null);
let cursor = 0;
const workers = Array.from({ length: Math.min(limit, items.length) }, async () => {
while (cursor < items.length) {
const i = cursor++;
try { out[i] = await fn(items[i]); } catch (e) { console.error("单项失败(跳过)", e); }
}
});
await Promise.all(workers);
return out;
}
// 并发 4 是实测舒适值#成本与耗时(实测)
Firecrawl credits(scrape 1 credit/页,search 2 credits/次):
| 阶段 | 消耗 |
|---|---|
| 抓官网 | 1 |
| 6 条搜索式 × search | 12 |
| 抓 8 篇第三方正文 | ≤ 8 |
| 官网首屏截图 | 1 |
| 3 次图片搜索 | 6 |
| 单篇合计 | 约 28 credits |
模型调用:4 次(规划搜索式 / 提炼底稿 / 写正文 / 挑配图点)。其中提炼底稿的输入最大(官网 14k 字 + 7 篇各 8k 字),是 token 大头。
耗时(线上实测):调研 64 秒,出稿 + 配图 67 秒。
存储:每篇 2-4 张图进 Vercel Blob,单张约 100-500KB。
#迁出成独立网站 / 扩展到视频脚本的建议
如果后续要把这条链路从 media-studio 里拆出来做成独立站,按下面这个顺序最省事。
#依赖梳理:哪些能直接搬,哪些要重写
| 模块 | 迁出难度 | 说明 |
|---|---|---|
lib/firecrawl.ts |
⭐ 直接复制 | 只依赖 fetch 和一个环境变量,零耦合 |
lib/teach-research.ts |
⭐⭐ 改两行 | 把 getPrompt(id) 换成读本地 md 文件或常量,把 getLlmModel() 换成新项目的模型构造函数 |
lib/teach-generate.ts |
⭐⭐⭐ 需要拆 | 现在走的是 media-studio 的 styledGenerateObject(风格注入 + 成稿净化 + 输出契约兜底)。这套「防跑偏」机制值得一起搬——尤其是「模型把正文塞进别的键」的兜底逻辑(salvageContent),DeepSeek 系模型在长 system 下确实会跑偏 |
lib/teach-shots.ts |
⭐⭐ 改存储层 | 只有 persistToBlob 绑了 Vercel Blob,换 R2/S3 只改这一个函数 |
| 提示词 | ⭐ 直接复制 | 四个 md 文件 |
| 落库 | — | 独立站建议直接建自己的表:products(产品/链接/产品名)、researches(底稿/来源清单/统计)、articles(正文/配图清单/类型) |
强烈建议保留的两个设计:① 两步式(调研与出稿分离、底稿给用户过目)——这不只是为了绕超时,更是产品逻辑上的正确切分;② 提示词与代码分离、可在网页热改——教学文的调性要反复调,改代码重新部署一次成本太高。
#扩展到视频文案脚本
调研阶段(第 1 步)完全不用改——底稿是中立的事实资料,喂给什么下游都行。要改的只有出稿阶段,按输出形态分叉:
┌→ 公众号教学长文(现有)
调研底稿(十节结构,共用)─┼→ 口播视频脚本(新增)
├→ 分镜脚本 / 逐镜表(新增)
└→ 短视频钩子 + 三段式脚本(新增)具体做法:新增一份 schema + 一份骨架提示词即可,其余全部复用。
- 口播脚本的 schema 建议:
{ title, hook, sections: [{ heading, narration, duration_sec, b_roll_hint }], cta } b_roll_hint(这一段配什么画面)字段是关键——它天然对接现有的teach-shots.ts:把每段的b_roll_hint当图片搜索式,就能自动配出「这段口播配哪张产品截图」的素材清单- 视频脚本对时长敏感,schema 的
description里要写死「每段 narration 控制在 X 字,对应约 Y 秒口播」,并在最后做一次总时长校验
#再往后:生成视频
这一步本 SOP 未验证,只给已知的接口线索:
- 静态图 + 口播 TTS + 转场 是最省的路径:现有
teach-shots.ts出的产品截图正好当画面,配 TTS 音轨用 ffmpeg 合成。注意 ffmpeg 在 Vercel 上有坑(见project_gifmoji的记录),这一步建议放本地或独立的容器服务,不要塞进 serverless - 家里已有的相关能力:阶跃/可灵的图生视频(见
~/Desktop/配置信息/)、gpt-image-2 生图(生图API-gpt-image-2.md)、scribe-studio 的白板手绘渲染(后端已随东京 VPS 下线,需要重建) - 建议的推进顺序:先把「口播脚本 + 配图素材清单」做扎实并人工验证几期,再考虑自动合成——脚本质量不过关的话,自动合成只是把废稿变成废视频
#下次可以直接从这里开始
当前进度:功能已在 media-studio 上线并跑通端到端。
- 线上入口:https://studio.saveme505.help/teach(顶部导航「教学向」)
- 首篇成品稿件:https://studio.saveme505.help/drafts/7f569186-68f2-4037-a825-904c310d4097
- 本地成品副本:
~/Desktop/Gemini Notebook(原 NotebookLM)上手教程-media-studio生成.md - 调研底稿副本:
~/Desktop/Gemini Notebook-调研底稿.md - 代码:
~/personal/media-studio,commit69429e1,项目内文档docs/teach-article.md - Firecrawl key 已写进线上 DB 配置(设置页「网页抓取」卡片,source=db),无需再配 env
下一步可选方向(按投入产出排序):
- 再跑几个产品验证稳定性——换不同形态的官网试(纯 SPA 的、中文产品的、小众工具的),看调研底稿会不会变薄。这是决定要不要迁出的前提。
- 加「批量」入口——一次贴 5 个链接排队跑,晚上跑完早上看。现有两步 API 直接串就行。
- 接口化——把
/api/teach/*加上 token 鉴权对外,方便未来独立站或自动化流程调用。 - 扩展视频脚本——按上一节做法,新增一份 schema + 骨架提示词,调研阶段零改动。
- 迁出独立站——建议等 1 和 4 都验证过再动,那时需求边界才清楚。
来源:沉淀/AI产品教学向长文自动生成-SOP.md(整理于 2026-08-18)