Skip to content

统一模型运行时设计

1. 目标

统一管理文本、图片、视频、语音、音色和音乐模型,同时保留供应商及模型的特有能力。模型层不以“统一成相同参数”为目标,而以以下能力为目标:

  • 统一模型注册、启停、版本和租户凭证;
  • 统一生成意图与任务生命周期;
  • 显式描述每个模型支持什么、不支持什么;
  • 在请求发出前完成素材、参数和组合合法性校验;
  • 允许用户指定供应商和模型,也支持按策略自动路由;
  • 统一记录成本、质量、延迟、错误和可追溯快照。

1.1 首期供应商范围

首期只接入两个供应商:

  • 火山引擎:承载豆包、Seedance 等图像、视频和音频模型;
  • 阿里云百炼:承载通义万相、Qwen-Image、CosyVoice 等图像、视频和音频模型。

原型、配置中心、模型注册表和路由策略均不得把其他供应商展示为已接入。后续如需扩展,必须新增供应商适配器并完成能力矩阵、成本、错误映射和回归验证。

2. 核心对象

typescript
type ProviderType = 'volcengine' | 'bailian';

type ModelModality =
  | 'text'
  | 'image'
  | 'video'
  | 'speech'
  | 'voice'
  | 'music';

type GenerationMode =
  | 'text-to-image'
  | 'image-to-image'
  | 'text-to-video'
  | 'first-frame-to-video'
  | 'first-last-frame-to-video'
  | 'reference-to-video'
  | 'audio-driven-video'
  | 'video-extension'
  | 'video-edit'
  | 'text-to-speech'
  | 'voice-clone'
  | 'text-to-music';

ModelDefinition

typescript
interface ModelDefinition {
  id: string;
  provider: ProviderType;
  providerModelId: string;
  displayName: string;
  version?: string;
  modality: ModelModality;
  enabled: boolean;
  capabilities: ModelCapabilities;
  pricing: PricingDefinition;
  adapterKey: string;
}

ModelCapabilities

typescript
interface ReferenceConstraint {
  minCount: number;
  maxCount: number;
  mediaTypes: string[];
  maxFileSize?: number;
  durationRange?: [number, number];
  dimensionRange?: {
    minWidth?: number;
    maxWidth?: number;
    minHeight?: number;
    maxHeight?: number;
  };
}

interface ModelCapabilities {
  modes: GenerationMode[];
  references: {
    firstFrame?: ReferenceConstraint;
    lastFrame?: ReferenceConstraint;
    images?: ReferenceConstraint;
    videos?: ReferenceConstraint;
    audios?: ReferenceConstraint;
    masks?: ReferenceConstraint;
  };
  output: {
    resolutions: string[];
    aspectRatios: string[];
    durationRange?: [number, number];
    durations?: number[];
    fps?: number[];
    supportsAudio?: boolean;
    supportsSeed?: boolean;
    supportsWatermarkControl?: boolean;
  };
  compatibilityRules: CompatibilityRule[];
  providerOptionsSchema?: Record<string, unknown>;
}

MediaReference

typescript
interface MediaReference {
  assetId: string;
  role:
    | 'first-frame'
    | 'last-frame'
    | 'character'
    | 'scene'
    | 'prop'
    | 'style'
    | 'pose'
    | 'motion'
    | 'camera'
    | 'video'
    | 'driving-audio'
    | 'voice'
    | 'music';
  subjectId?: string;
  priority?: number;
  weight?: number;
  timeRange?: {
    start: number;
    end: number;
  };
}

GenerationRequest

typescript
interface GenerationRequest {
  tenantId: string;
  projectId: string;
  mode: GenerationMode;
  modelId?: string;
  routingPolicyId?: string;
  promptSource: {
    type: 'raw' | 'shot-spec' | 'music-brief' | 'speech-script';
    id?: string;
    text?: string;
  };
  references: MediaReference[];
  output: {
    aspectRatio?: string;
    resolution?: string;
    duration?: number;
    fps?: number;
    audio?: boolean;
  };
  options: {
    seed?: number;
    count?: number;
    negativePrompt?: string;
  };
  providerOptions?: Record<string, unknown>;
}

3. 兼容规则

能力矩阵必须支持表达“单项支持,但组合不支持”。例如:

typescript
type CompatibilityRule =
  | {
      type: 'mutually-exclusive';
      groups: string[][];
    }
  | {
      type: 'requires';
      when: Record<string, unknown>;
      require: Record<string, unknown>;
    }
  | {
      type: 'forbids';
      when: Record<string, unknown>;
      forbid: Record<string, unknown>;
    };

示例:

  • 首尾帧模式不能和多模态参考模式混用;
  • 开启原生音频时只能使用特定模型版本;
  • 选择首帧后,输出比例由首帧决定;
  • 某模型 1080P 仅允许特定时长;
  • 某模型参考图最多 9 张、参考音频最多 1 段。

4. Provider Adapter

typescript
interface ModelProviderAdapter {
  validate(request: GenerationRequest): Promise<ValidationResult>;
  estimate(request: GenerationRequest): Promise<CostEstimate>;
  submit(request: ResolvedGenerationRequest): Promise<ProviderTask>;
  getStatus(task: ProviderTask): Promise<ProviderTaskStatus>;
  cancel?(task: ProviderTask): Promise<void>;
  normalizeResult(result: unknown): Promise<GenerationResult>;
  normalizeError(error: unknown): ProviderError;
}

建议包结构:

text
packages/model-runtime/
├── contracts/
├── capability-registry/
├── validation/
├── routing/
├── pricing/
├── lifecycle/
├── prompt-compiler/
└── adapters/

packages/provider-volcengine/
├── ark/
├── jimeng/
├── speech/
└── music/

packages/provider-bailian/
├── image/
├── video/
├── speech/
└── music/

5. Prompt Compiler

不直接把用户文本发送给模型。编译输入包括:

text
ShotSpec
+ 已审核角色资产描述
+ 已审核场景资产描述
+ 道具和服装状态
+ 项目风格规范
+ 镜头语言
+ 连续性要求
+ 模型专属提示词规范
+ 模型专属负面词

输出包括:

  • 平台标准提示词;
  • Provider 专属提示词;
  • 参考素材及顺序;
  • 被裁剪或降级的能力说明;
  • 警告与阻断项。

如果模型无法表达关键约束,默认阻断并提示选择更合适的模型,不能静默丢弃参考素材。

6. 模型路由

路由输入:

  • 生成模式和必需参考类型;
  • 质量档位;
  • 最大预算;
  • 最大等待时间;
  • 目标比例、时长和分辨率;
  • 租户允许使用的供应商;
  • 模型实时成功率、排队和限流状态;
  • 历史同类镜头质量评分。

路由结果:

typescript
interface RoutingDecision {
  modelId: string;
  reason: string[];
  estimatedCost: number;
  estimatedDuration: number;
  degradedCapabilities: string[];
}

MVP 可以先支持手工选模型和静态优先级。积累数据后再加入质量/成本自动路由。

7. 生成任务生命周期

text
DRAFT
  → VALIDATING
  → QUEUED
  → SUBMITTED
  → RUNNING
  → DOWNLOADING
  → VERIFYING
  → SUCCEEDED / FAILED / CANCELLED
  → REVIEW_PENDING
  → APPROVED / REJECTED

Generation 记录必须保存:

  • 标准请求快照;
  • 最终模型和版本;
  • 编译后的供应商请求;
  • 参考素材版本与 OSS 对象版本;
  • 供应商 taskId/requestId;
  • 全部状态变化与时间;
  • 输出文件、媒体探测信息和缩略图;
  • 成本、质量评分和审核结果;
  • 原始供应商错误与平台归一化错误。

8. 统一错误分类

text
AUTHENTICATION_ERROR
PERMISSION_ERROR
QUOTA_EXCEEDED
RATE_LIMITED
INVALID_PARAMETER
UNSUPPORTED_CAPABILITY
INVALID_REFERENCE
CONTENT_REJECTED
PROVIDER_UNAVAILABLE
PROVIDER_TIMEOUT
TASK_FAILED
DOWNLOAD_FAILED
MEDIA_INVALID
CANCELLED
UNKNOWN

前端展示平台错误,同时为管理员保留供应商 requestId 和原始错误。

9. OSS 媒体策略

对象 Key 建议:

text
/{tenantId}/{workspaceId}/{projectId}/
  source/
  assets/{assetType}/{assetId}/{version}/
  generations/{generationId}/input/
  generations/{generationId}/output/
  proxies/
  renders/{renderId}/
  deliveries/{deliveryId}/

规则:

  • 供应商调用使用时效覆盖任务周期的签名 URL;
  • 输出先下载到平台 OSS,再写入业务状态;
  • 不把供应商临时 URL 当永久产物;
  • 保存 ETag、大小、MIME、分辨率、时长和校验值;
  • 原始素材、代理素材、中间产物和成片使用不同生命周期策略。

10. 前端模型选择器

交互顺序:

text
选择生成类型
  → 选择自动路由或指定供应商
  → 选择模型
  → 根据能力动态生成表单
  → 添加带语义角色的参考素材
  → 实时校验
  → 展示预计成本与耗时
  → 提交生成

界面必须明确提示:

  • 当前模型支持哪些参考方式;
  • 哪些素材会被使用;
  • 哪些能力不能组合;
  • 是否会自动调整比例、分辨率或时长;
  • 是否产生原生音频;
  • 是否使用企业自己的模型额度。

11. 员工模型凭证与执行身份

公司为每位员工分别签发火山引擎和阿里云百炼凭证;同一员工可以同时绑定两家。叙镜只负责凭证绑定、可用性验证、任务执行身份和实际用量归属。供应商侧独立控制可用额度与限流,叙镜不设置内部额度上限,不做额度占位、任务拦截或备用账号切换。

11.1 核心对象

typescript
interface EmployeeModelCredential {
  id: string;
  employeeId: string;
  providerId: 'volcengine' | 'aliyun_bailian';
  region: string;
  credentialRef: string; // 只存密钥系统引用
  maskedKey: string; // 仅用于界面展示,不可逆推出密钥
  credentialStatus: 'valid' | 'missing' | 'invalid' | 'disabled';
  authorizedModelIds: string[];
  lastVerifiedAt?: string;
  version: number;
}

interface ProjectExecutionIdentity {
  id: string;
  projectId: string;
  volcengineEmployeeId: string;
  bailianEmployeeId: string;
  version: number;
}

interface ModelUsageLedger {
  id: string;
  employeeId: string;
  credentialId: string;
  providerId: 'volcengine' | 'aliyun_bailian';
  generationId: string;
  providerTaskId?: string;
  requestCount: number;
  videoSeconds: number;
  imageCount: number;
  audioMinutes: number;
  actualCost: number;
  createdAt: string;
}

ModelUsageLedger 只记录供应商返回或账单核对后的实际请求、视频秒数、图像张数、音频分钟和费用,用于观察、财务核对与员工归属;它不是限额计数器,也不参与任务提交判断。

11.2 执行身份解析与锁定

用户手工发起任务时,Server 解析发起人在目标供应商下的 EmployeeModelCredential。自动化或定时任务使用项目 ProjectExecutionIdentity 为该供应商指定的员工执行账号。两类任务不得互相借用身份。

生成任务创建时必须把最终 credentialRefcredentialIdemployeeId 固化进请求快照;Worker 只能读取锁定引用,不能重新选择员工或凭证。凭证缺失、失效或停用时,任务直接进入需用户处理状态;Conductor 不对同一凭证盲目重试。

11.3 供应商错误与明确不实现项

本系统明确不实现 ModelQuotaQuotaReservationRoutingPolicy 及其内部限额、占位、阻断、优先级和备用账号语义。供应商返回的额度耗尽继续归一化为 QUOTA_EXCEEDED,限流归一化为 RATE_LIMITED;这只是在任务错误模型中表达供应商结果,不代表叙镜维护或执行内部额度策略。响应保留 providerTaskId / requestId、是否建议稍后重试、用户处理建议和追踪 ID。

11.4 密钥安全

明文密钥只能加密存储到 KMS、Vault 或 Secret Manager。Nacos 仅允许保存 credentialRef 与供应商、地域、状态、授权模型等非敏感元数据,不允许保存 Access Key、Secret Key 或 API Key 明文。业务日志、错误日志、链路追踪和审计记录均不得输出 Key;界面和通知只能使用不可逆掩码。

11.5 审计要求

以下操作写入不可变审计记录:员工凭证绑定、更新、验证、启停、授权模型变化、项目执行账号变化,以及任务的执行身份解析与 credentialRef 锁定。记录包含 employeeId、credentialId、Provider、操作者、变更前后非敏感元数据、generationId / providerTaskId、原因、时间和追踪 ID;任何情况下都不记录明文或可恢复的密钥内容。

NarraCine AI Production Platform