Skip to main content

架构概览

核心组件

组件作用说明
generateVideo Tool任务创建验证权限、NSFW 检测、选择 Provider
Agent onFinish消息发布将 pending 任务发布到 NATS
NATS JetStream异步队列TASKS stream,subject tasks.video.*
kira-video-worker任务执行扣费、调用 Provider API、后处理、上传
Supabase Storage视频存储agent_message bucket
Centrifugo状态推送WebSocket 实时通知前端

视频生成服务商

Provider模型说明
Seedanceseedance-1-5-pro (BytePlus)NSFW 内容专用,支持 i2v,720p
Grokgrok-imagine-video (xAI)默认 Provider,支持多种比例
Veoveo-3.1-fast-generate-preview (Google)高质量,3x 成本(当前已禁用)

Provider 选择逻辑

Provider 选择基于 NSFW 内容检测,而非用户订阅等级:
Veo Provider 因 Google 文件处理问题暂时禁用。启用后将作为 Max 用户的高级选项。

Provider 能力对比

能力SeedanceGrokVeo
文生视频 (t2v)
图生视频 (i2v)
支持比例 *1:1, 3:4, 4:3, 16:9, 9:16, 21:916:9, 4:3, 1:1, 9:16, 3:4, 3:2, 2:316:9, 9:16
分辨率720p720p720p+
轮询方式每 2 秒,最长 10 分钟Deferred Request (202/200)每 10 秒 Long-running Operation
* 上表列出的是各 Provider 的原生支持比例。当前 generateVideo 工具 schema 仅允许 4 种比例:auto | 16:9 | 9:16 | 1:1。其他比例(如 21:9, 3:4 等)是 Provider 能力但工具层未暴露。

生成模式

模式输入说明
文生视频 (t2v)prompt纯文本描述生成视频
图生视频 (i2v)prompt + sourceImageId基于起始帧图片生成视频

参数配置

参数可选值默认值
duration"5", "10""5"
ratio"auto", "16:9", "9:16", "1:1""auto"
当 ratio 为 "auto" 且提供了 sourceImageId 时,Worker 会从源图片自动检测最接近的比例。

Credits 计费

时长基础成本 (Grok/Seedance)高级成本 (Veo)
5 秒50 credits150 credits
10 秒100 credits300 credits

计费流程

Credits 扣费使用 taskId 做幂等性保障。同一 taskId 不会重复扣费或重复退款。

任务生命周期

状态说明

状态说明
pending任务已创建,等待 Worker 处理
processingWorker 正在调用 Provider API
completed视频生成成功,已上传存储
failed生成失败,已自动退款,errorMessage 记录原因
insufficient_creditsCredits 不足,用户充值后可重新提交
insufficient_plan方案不支持视频,用户升级后可重新提交

kira-video-worker 架构

并发控制

  • 同一用户的视频任务串行处理(一次只处理一个)
  • 不同用户的任务并发处理
  • 最大并发数:100 个任务

NATS Consumer 配置

配置项说明
StreamTASKSJetStream 持久化流
Subjecttasks.video.*过滤视频任务
Consumervideo-worker持久化消费者
Max Deliver3最多重试 3 次
Ack Wait5 分钟处理超时时间

Worker 处理流程

1

接收任务

从 NATS JetStream 接收视频任务消息
2

扣费(Pre-deduction)

原子化扣除 Credits,使用 taskId 保证幂等。扣费失败 → NAK → NATS 自动重试(安全,无 Credits 损失)
3

调用 Provider

更新状态为 processing,通过 Centrifugo 通知前端,调用对应 Provider API 生成视频
4

后处理

下载视频 → ffprobe 提取尺寸 → ffmpeg 截取首帧缩略图 → 生成 blurhash
5

上传存储

视频和缩略图上传到 Supabase Storage,更新 JSONB 状态为 completed
6

通知前端

通过 Centrifugo WebSocket 推送完成状态和 Signed URL
7

内容审核

异步审核缩略图(Fire-and-forget)

错误处理策略

阶段失败行为说明
扣费前NAK → NATS 重试安全,无 Credits 损失
扣费后退款 + 标记 failedCredits 原子退款,不再重试
Provider 超时退款 + 标记 failed超过轮询时限

数据模型

视频状态存储在 thread_version 表的 JSONB videos 数组中,无独立视频表:
interface Video {
  // 创建时确定(不可变)
  toolCallId?: string;
  toolName?: string;
  taskId: string;          // UUID
  provider: "seedance" | "grok" | "veo";
  prompt?: string;
  sourceImageId?: string;
  duration: number;        // 5 或 10
  ratio: string;           // "auto" | "16:9" | "9:16" | "1:1"
  createdAt: string;       // ISO 8601

  // Worker 更新(可变)
  status: "pending" | "processing" | "completed" | "failed"
    | "insufficient_credits" | "insufficient_plan";
  videoId?: string;        // 存储路径
  thumbId?: string;        // 缩略图路径
  width?: number;
  height?: number;
  errorMessage?: string;
}

异步处理流程

1

AI Tool 创建任务

generateVideo tool 检测 NSFW → 选择 Provider → 验证 Plan 和 Credits → 在 thread_version.videos 中插入记录
2

发布到 NATS

Agent onFinish 回调中,遍历 pending 状态的视频任务,逐个发布到 tasks.video.{taskId} subject
3

Worker 处理

kira-video-worker 消费消息,原子扣费,调用对应 Provider API,更新状态为 processing
4

后处理和上传

视频生成后下载 → 提取尺寸 → 生成缩略图 → 上传到 Supabase Storage → 更新状态为 completed
5

实时通知

通过 Centrifugo WebSocket 推送状态变更到前端(processing → completed/failed)

NATS 消息格式

interface VideoTaskMessage {
  taskId: string;
  userId: string;
  threadId: string;
  provider: "seedance" | "grok" | "veo";
  prompt?: string;
  duration: "5" | "10";
  ratio: "auto" | "16:9" | "9:16" | "1:1";
  sourceImageId?: string;
}

WebSocket 通知格式

// Centrifugo 频道: {userId}/{threadId}#{userId}
{
  type: "video_status";
  taskId: string;
  video: {
    taskId: string;
    status: "processing" | "completed" | "failed";
    videoId?: string;       // completed 时存在
    thumbId?: string;       // completed 时存在
    width?: number;
    height?: number;
    errorMessage?: string;  // failed 时存在
    url?: string;           // completed 时 Signed URL
    thumbUrl?: string;      // completed 时 Signed URL
  }
}

文件存储结构

agent_message/
├── {userId}/
│   └── {threadId}/
│       ├── video_{uuid}.mp4       # 原始视频
│       ├── vc_{uuid}.jpg          # 视频缩略图
│       └── wv_{uuid}.mp4          # 带水印视频(按需生成)
└── feed/
    └── {userId}/
        ├── video_{uuid}.mp4       # Feed 视频副本
        └── vc_{uuid}.jpg          # Feed 缩略图副本

水印处理

免费用户下载视频时自动添加水印,Pro+ 用户可下载无水印版本。

Feed 集成

视频作为一等内容类型发布到 Feed:
{
  videoId: string;      // Feed 存储路径
  thumbId: string;      // 缩略图路径
  prompt?: string;
  duration: number;
  ratio: string;
  width: number;
  height: number;
  blurhash?: string;    // 缩略图 blurhash
}
  • Feed 表 media_type 字段区分 "image""video"
  • 视频和缩略图从 message 存储复制到 feed 存储
  • 使用 origin_image_id 进行去重检查
  • 发布前进行内容审核和向量相似度检查(LanceDB)

清理与删除

删除消息或回溯时,自动清理关联的视频文件:
  1. thread_version.videos 提取 taskId 列表
  2. 获取对应的 videoIdthumbId 存储路径
  3. agent_message bucket 中删除文件
  4. 清理 NATS 中未消费的任务(purgeVideoTasks

成本追踪

// PostHog 成本追踪
trackAICost({
  type: "tool_video",
  model: provider,           // "seedance" | "grok" | "veo"
  duration: videoDurationSeconds,
  cost: pricePerSecond * videoDurationSeconds,
});
Provider单价
Seedance / Grok$0.05/秒
Veo$0.15/秒