Ürünereklam
已完成贴上商品链接,就能拿到做好的土耳其语和英语视频广告和图片广告,每一条在花钱投放之前都会先由预测模型打分。项目起步于 2026 年的 BTK Akademi Hackathon。
- 我的角色
- 独立完成设计与开发
- 年份
- 2026
- 使用技术
- Next.js · Inngest · Supabase · Gemini · Veo · ElevenLabs · Remotion · PyTorch on Modal · Polar
横向滚动可以看到完整的图。
问题
要按网店需要的量做两种语言的广告,通常意味着要长期雇一家代理公司。即便如此,在预算花出去之前,谁也不知道哪个版本会有效。
实现方式
Next.js 应用负责编排 Inngest 的持久化任务。Python 爬虫读取商品页面,碰到难抓的店铺页面,会走一条加固过的抓取路径。Gemini 写策略、文案和分镜,Veo 或 Seedance 渲染视频,ElevenLabs 配音,Remotion 加上逐词对齐的字幕、水印和 CTA。另有一个独立的工作台,用来制作静态图片广告和广告文案。之后,每个变体都要经过一个自托管的模型(TRIBE v2),预测人们会对它有什么反应。模型跑在 Modal 的 GPU 上,先启动任务再轮询结果,因为一次渲染的耗时会超过边缘函数的超时时间。Supabase 保存状态,由行级安全保护,媒体用短时有效的签名 URL。积分记在只追加的账本里,扣费和退款都是原子操作;结账走 Polar,价格以里拉和美元标示。
关键决策
失败的步骤会自动退款
流水线由持久化任务组成,每一步都有失败处理器。某一步挂掉时,会把它的积分恰好退还一次,并释放这次运行,而不是让它卡住。
媒体文件默认私有
原始渲染文件放在私有存储桶里,列级 SELECT 权限已撤销,媒体链接都是短时有效的签名 URL。包括免费版在内,没有任何媒体地址能被猜到。
每条广告在有人为它花钱之前都会打分
没有分数的广告不会离开工作台。在有人为某个变体投入预算之前,模型就已经给各个变体排好了名次。
仓库里的代码
async function safeRefund(
opts: Parameters<typeof refundCredits>[0],
): Promise<void> {
try {
await refundCredits(opts);
} catch (err) {
console.error(
`[billing][RECONCILE-NEEDED] refund failed user=${opts.userId} ` +1
`amount=${opts.amount} ref=${opts.refType}:${opts.refId} ` +
`key=${opts.idempotencyKey ?? "-"}`,
err,
);
}
}
// …
await safeRefund({
supa,
userId: campaign.user_id,
amount: campaignCreditCost({
videoCount: brief.videoCount ?? 1,
videoModel: brief.videoModel ?? "seedance",
durationSec: brief.durationSec,
}),
refType: "campaign",
refId: campaignId,
idempotencyKey: `refund:campaign-all:${campaignId}`,2
description: "Kampanya başlatılamadı — otomatik kredi iadesi",3
});
第 8 行. 退款失败时不抛异常,而是记录一个 RECONCILE-NEEDED 标记,这样积分不会悄无声息地丢失,流水线也照样会释放这次运行。
第 27 行. 每个事件一个幂等键:失败处理器可能会再次运行,但账本只会记录一次退款。
第 28 行. 退款原因用的是土耳其语,因为这个产品从头到尾都是双语的。
目前进展
它从一个黑客松 demo 成长为一个带 GPU 打分的双语产品。现在已闭源,正在准备付费上线。