Medeo.app 现象级产品:工程实现方法深度拆解

目标:从“对话式视频创作”背后的核心工程结构出发,拆成可落地的系统设计、DSL、编排、渲染、计费与规模化策略。

Chat → 可执行 DSL 工程文件(Project)为中心 异步任务编排 + 优先级队列 增量渲染(Preview / Final) Credits 成本模型

0) 结论先行:Medeo 的“现象级”来自工程化,而不只是模型

它真正卖的是什么

  • 把“视频创作”工程化:每一次生成/编辑都是对 Project 的可追溯变更。
  • 把“对话”可控化:LLM 不直接操作时间线,而是产出 DSL patch → 执行器落地。
  • 把“AI 能力”服务化:检索、TTS、字幕、渲染、生成模型以 Worker 形式被编排。
  • 把“成本”产品化:Credits 预算、估算、分阶段扣费、取消结算,配合优先级队列。

这套体系的直接收益

  • 可迭代:多轮编辑不崩溃(patch 小、状态稳定)。
  • 可回滚:版本历史、撤销、对比,降低“生成不可控”的挫败感。
  • 可规模化:异步 DAG + 队列,天然适配峰值与排队。
  • 可变现:每一步任务可计量、可预估、可结算。
核心对象Project(工程文件)
核心接口Chat → DSL Patch
核心系统Orchestrator(编排器)
核心体验增量 Preview(快速回显)
一句话概括:“视频版 IDE”——对话只是输入方式,真正的底座是 可执行的工程表示 + 可编排的 AI 工人集群

1) 产品形态与交互范式:Chat + Timeline + Generator Recipes

用户主路径(抽象)

  1. 选择 Recipe(比如 URL→视频、脚本→短视频、PPT→视频…)
  2. 提供输入(文本/URL/素材/偏好)
  3. 系统生成初稿(脚本、分镜、配音、字幕、素材、音乐)
  4. 用户通过对话进行修改:节奏、镜头、文案、风格、字幕样式
  5. 进入轨道精修(拖拽、剪切、替换)
  6. 导出多比例与多清晰度

工程隐含要求

  • 对话修改必须落到“可执行操作”,且可重复(幂等)。
  • 轨道编辑必须与对话一致:二者读写同一 Project 状态。
  • 生成入口多≠系统复杂:入口只是不同的 Recipe,核心引擎一致。
  • 任何重操作必须是异步任务(Job),支持排队与取消。
对比:为什么“纯一键生成”更难做成现象级?
纯“一键生成”在第二次修改就会崩:用户说“把第 12 秒那句换个说法并把镜头拉近”,如果没有工程表示, 系统只能“重新生成一遍”或“猜着改”,体验很快到顶。Medeo 的突破在于:把每个片段变成对象(Clip/Caption/AudioSegment),让编辑变成 patch。

2) 三层架构:DSL / Context / Environment

系统结构图(高层)

┌─────────────────────────────── Client ───────────────────────────────┐
│  Chat UI  │  Timeline UI  │  Asset Browser  │  Export / Preview       │
└───────────┬───────────────┬────────────────┬──────────────────────────┘
            │               │                │
            ▼               ▼                ▼
┌────────────────────────── API Gateway / BFF ─────────────────────────┐
│ Auth / RateLimit / Project CRUD / WS Updates / Upload / Billing       │
└───────────────────────────┬──────────────────────────────────────────┘
                            ▼
┌──────────────────────── Planning Layer ──────────────────────────────┐
│ LLM Router → Context Builder → DSL Planner → Validator → Cost Estimator│
└───────────────────────────┬──────────────────────────────────────────┘
                            ▼
┌──────────────────────── Orchestration Layer ─────────────────────────┐
│ DAG Builder → Priority Queue → Worker Dispatch → Retries/Cancel       │
└───────────────────────────┬──────────────────────────────────────────┘
                            ▼
┌──────────────────────── Execution Layer (Workers) ────────────────────┐
│ Stock Search │ TTS │ Music Mix │ ASR/Caption │ Gen Image/Video │ Render│
└───────────────────────────┬──────────────────────────────────────────┘
                            ▼
┌──────────────────────── Data Layer ───────────────────────────────────┐
│ Project Store │ Asset Store │ Job Store │ Cache │ Metrics/Logs/Traces  │
└───────────────────────────────────────────────────────────────────────┘
关键点:把不确定性(LLM 自由表达)关进 Planning Layer;把确定性(可执行变更)交给 DSL+Executor。

3) DSL:视频工程中间表示(IR)

目标:让“对话式修改”变成可验证、可回滚、可计费的工程操作。

3.1 DSL 的设计原则(强烈建议)

原则为什么重要怎么做
可执行 避免 LLM 直接写时间线全文导致漂移 输出 patch(增量操作),执行器应用到 Project AST
可验证 防止越界/负时长/断引用 Validator:类型/范围/依赖校验;失败返回可解释错误
幂等 重试必然发生(模型/渲染/网络) 每个 op 有 op_id + 目标对象稳定 ID
可计费 商业化依赖每步成本可估算 op 标注 cost hints(分辨率/时长/模型)
可回滚 对话编辑必须支持撤销/版本 事件溯源(Event Sourcing)或版本快照 + Patch log

3.2 最小 DSL 结构(示例)

{
  "project_id": "p_123",
  "base_revision": 42,
  "ops": [
    {
      "op_id": "op_9f31",
      "type": "trim_clip",
      "target": { "clip_id": "clip_7" },
      "params": { "in": 1.20, "out": 4.10 },
      "cost_hint": { "unit": "cpu", "weight": 0.2 }
    },
    {
      "op_id": "op_aa02",
      "type": "replace_voiceover",
      "target": { "audio_id": "aud_vo_2" },
      "params": {
        "text": "更新后的旁白文案…",
        "voice_id": "voice_zh_male_01",
        "pace": 1.05
      },
      "cost_hint": { "unit": "tts_chars", "weight": 530 }
    }
  ],
  "intent": {
    "user_utterance": "把第 12 秒那句旁白更简洁,并把那段节奏加快一点"
  }
}
实现要点:LLM 输出时必须带 base_revision。执行器应用 patch 前检查是否冲突(并发编辑/轨道编辑会改变 revision)。

3.3 原子操作(建议清单)

时间线编辑

  • insert_clip / remove_clip / move_clip
  • trim_clip / split_clip
  • set_playback_speed
  • set_transition(type/duration)

生成/替换

  • generate_broll(依据脚本/关键词)
  • replace_asset(stock/上传/生成)
  • regenerate_scene(镜头级重做)
  • generate_captions / align_captions
  • tts_generate / mix_audio
3.4 高级:为何需要“Scene Graph”(分镜层)而不只是 Timeline?
仅有时间线会让“语义编辑”很难:用户说“把第二个观点讲清楚一点”,这是脚本/分镜语义,未必直接对应某个 Clip。 因此建议 Project 同时维护:
  • Script/Outline:观点结构
  • Scenes:每段对应的镜头组(可映射到时间线片段范围)
  • Timeline:真正渲染执行的轨道对象
这样 LLM 可以先定位 Scene,再输出 Timeline patch。

4) Context Engineering:把“对话 + 工程状态 + 约束”变成可控上下文

4.1 Context 的分层(推荐)

内容格式建议
Project Summary 脚本摘要、分镜列表、当前风格/字体/配色、节奏目标 短摘要 + 关键字段(JSON)
Timeline Snapshot Clip/Audio/Caption 的稳定 ID、时间范围、素材引用 结构化列表(避免自然语言长文本)
User Intent (This Turn) 本轮最小目标:改哪段、改什么、优先级 1-3 条 bullet + 目标对象定位
Policies & Budget 可用 credits、允许的模型/分辨率、品牌合规、敏感词规则 硬约束(可机读)
关键技巧:LLM 的输入尽量结构化;输出必须可解析(JSON schema + 严格验证),减少“幻觉操作”。

4.2 意图到 patch 的标准流水线

1) Locate: 识别用户说的“那段/第二个观点/12秒附近”对应的 Scene/Clip IDs
2) Plan: 生成候选 edits(可能多方案:A/B),但最终输出一个可执行 patch
3) Validate: schema/type/range/dependency 校验 + 约束(品牌、预算、分辨率)
4) Estimate: 估算 credits + 预计耗时 + 是否触发重渲染
5) Execute: 进入 DAG + Queue
6) Summarize back: 用人话解释“我做了什么”,同时把 patch 写入版本历史
4.3 防止“上下文膨胀”的做法(非常实用)
  • Project Digest:每次执行后生成 0.5~1KB 的摘要(可缓存),而不是把全聊天历史带上。
  • Selective Snapshot:只取本轮可能受影响的 Scene/Clips(例如 8–20s),而不是全片时间线。
  • Tool-assisted locate:先用规则/索引定位,再让 LLM 做细化(减少 token)。
  • Schema-first prompting:严格输出 JSON,拒绝自然语言解释混入结构。

5) 编排器与任务系统:DAG + 优先级队列 + 可取消

5.1 Job 状态机(建议)

QUEUED → PLANNED → RUNNING → (SUCCEEDED | FAILED | CANCELED)
                    ↑
                 RETRYING

编排器(Orchestrator)职责

  • 把 patch 变成 DAG:哪些步骤可并行、哪些有依赖
  • 优先级调度:订阅层级/系统负载/预算/截止时间
  • 重试策略:指数退避、最大重试、可替代模型降级
  • 取消:传播取消信号到子任务;结算已完成部分
  • 幂等:同一 op_id 重试不会重复扣费/重复写状态

Worker(AI 工人)职责

  • 单一职责:TTS / ASR / Stock Search / Render / VideoGen
  • 输入输出标准化:artifact id + 元数据 + 可复现参数
  • 可缓存:相同输入(hash)命中缓存节省成本
  • 可观测:每步记录耗时/失败原因/质量指标

5.2 DAG 示例:替换旁白 + 重新对齐字幕 + 局部重渲染

[TTS Generate] ─┬─> [Audio Mix & Loudness] ─┬─> [Caption Align] ─┬─> [Preview Render]
                │                          │                   │
                └─> [Phoneme/Timing]       └─> [Ducking BGM]    └─> [Final Render (optional)]
为什么必须 DAG:很多操作不是“串行脚本”,而是并行可加速;并行也能更好地“先预览、后全量”。
5.3 优先级队列策略(可直接照抄的思路)
  • Priority = TierWeight + SLAWeight + AgeBoost
  • TierWeight:订阅层级(Business > Pro > Free)
  • SLAWeight:Preview < Final,预览优先
  • AgeBoost:排队时间越久越加权,避免饥饿
  • 资源隔离:渲染池、生成池分别限流,避免互相拖死

6) 渲染与“实时感”:Preview / Final + 增量渲染

6.1 两阶段渲染(强推荐)

Preview Render

  • 低分辨率 / 低码率 / 只渲受影响片段
  • 2–10 秒内尽量给结果(哪怕只是局部)
  • 用于验证节奏、字幕、镜头语义

Final Render

  • 1080p / 全片 / 多比例导出
  • 可进入后台队列(SLA 更长)
  • 支持断点续渲(segment cache)

6.2 增量渲染:只重算受影响的 segments

核心:把时间线切成可缓存的 Render Segments,任何变更都能定位“脏区间”。

Segment = hash(clip_ids + timing + effects + assets + style + resolution + fps)

DirtyRange =
  union(
    affected_clips_time_range,
    neighboring_transitions,
    audio_mix_window
  )

Render strategy:
  - reuse clean segments from cache
  - render dirty segments
  - stitch segments (concat) for preview/final
体验关键:用户感知的是“我说完话,画面立刻变了”。工程上用“局部 preview + 复用缓存”来换取这种实时感。
6.3 轨道编辑与对话编辑的冲突处理(必做)
  • 所有编辑都写入同一 Project revision
  • LLM patch 带 base_revision;若落后,走三种策略:
    1. Auto-rebase:如果只改不冲突字段(比如字幕样式),自动合并。
    2. Ask for disambiguation:如果用户说“那段”,但 clip 已移动。
    3. Fail fast:冲突严重则拒绝执行,并提示重新发起编辑。

7) 素材系统与角色一致性:资产是第一等公民

7.1 资产系统(Asset Store)需要存什么

资产类型关键字段用途
Stock Asset source/license/tags/duration/resolution/hash 检索、去重、授权合规
User Upload user_id/project_id/virus_scan/hash/exif 私有素材、复用缓存
Generated Image/Video prompt/model/seed/ref_ids/quality_score 可复现、可追责、可复用
Voice Profile voice_id/lang/style/default_pace 旁白一致性、品牌声音
Character Profile char_id/ref_images/embedding/version 跨场景角色一致性

7.2 角色一致性(Character Consistency)常用工程组合

输入侧:角色卡(Profile)

  • 参考图(多角度) + 文本设定(年龄、穿着、发型)
  • embedding/adapter 参数(版本化)
  • 风格锁定(写实/卡通/像素)

生成侧:一致性约束

  • 镜头级:每个 Scene 绑定 char_id
  • 时序级:关键帧一致 + 过渡平滑(避免跳脸)
  • 缓存:同一角色复用 seed/prompt 模板,减少漂移
坑位提醒:角色一致性不是“一个模型参数”解决的,而是 资产、版本、缓存、约束、回滚 一整套工程协作。

8) Credits:成本模型、估算、扣费、取消与对账

8.1 成本模型(建议从一开始就做)

estimated_credits =
  Σ op_i.cost(
    model, resolution, duration, frames, retries, priority, caching_hit
  )

典型计费维度:
- VideoGen: frames * model_rate
- Render: pixels * duration * fps
- TTS: characters * voice_rate
- ASR/Align: audio_seconds * rate
- Stock search: usually cheap (can be bundled)

8.2 扣费策略(用户体验与风控兼顾)

动作推荐策略原因
提交任务 预扣(reserve)+ 显示估算 避免跑完才发现余额不足
任务执行中 分阶段确认(commit) 可取消且结算准确
取消 已完成部分结算 + 未开始部分释放预扣 公平且可解释
失败重试 同一 op_id 幂等不重复扣费(或只计一次) 减少争议与对账复杂度
工程抓手:每个 Worker 输出 usage_report(模型名/耗时/帧数/字符数/缓存命中)写入账本,后续才能做优化与申诉处理。

9) 可观测性与质量控制:让“生成系统”可运营

9.1 必备观测指标(建议最低配)

系统指标

  • 队列长度、等待时间 P50/P95
  • Job 成功率、失败原因 TopN
  • Worker 耗时分布、重试率
  • 缓存命中率(segment/asset)

质量指标

  • 字幕对齐偏差(ms)
  • 音频响度/峰值/ducking 违规率
  • 镜头切换密度与节奏(每分钟 cut)
  • 用户回滚/撤销频率(哪里不满意)

9.2 “生成质量”如何工程化(可执行)

  • Quality Gates:关键环节自动检查(音量、字幕越界、空白帧、黑屏)不通过就阻断导出。
  • Fallback Ladder:某模型失败→降级模型→改用 stock→缩短片段→保证交付。
  • Human-in-the-loop(可选):对高价值客户/高风险内容加入人工审核队列。

10) 复刻路线图:从 MVP 到“现象级”护城河

10.1 MVP(4 个模块先跑通)

① Project + Timeline 数据模型

稳定 ID、revision、版本历史、patch 应用。

② LLM → DSL Patch

严格 JSON schema 输出 + Validator + 可解释错误。

③ Orchestrator + Queue

异步任务、取消、重试、进度回推(WebSocket)。

④ Preview Render(增量)

只渲脏区间 + segment cache,先让“实时感”成立。

10.2 护城河(你要追上“现象级”的关键)

  • Recipes 模板库:把不同入口固化成可复用 workflow(并持续优化)。
  • 角色/品牌一致性:Profile 版本化 + 复用缓存 + 约束策略。
  • 成本运营系统:usage_report → 成本归因 → 自动降级 → SLA 控制。
  • 编辑闭环:对话编辑、轨道编辑、版本回滚、差异对比统一在一个 Project 上。
最容易走弯路的点:只堆模型、不做工程文件与编排。短期“能生成”,长期“无法编辑、不可控、不可运营”。

附录:数据模型 / 协议示例(可直接改造成你自己的系统)

A) Project(工程文件)最小 schema(示例)

{
  "project_id": "p_123",
  "revision": 42,
  "meta": {
    "title": "科普短视频",
    "aspect": "9:16",
    "fps": 30,
    "style": { "caption": "bold", "theme": "dark" }
  },
  "script": {
    "outline": ["痛点", "原因", "解决方案", "总结"],
    "full_text": "..."
  },
  "scenes": [
    { "scene_id": "s1", "range": [0.0, 6.5], "summary": "开场钩子", "bindings": { "char_id": "c_7" } },
    { "scene_id": "s2", "range": [6.5, 18.0], "summary": "解释原因", "bindings": { "char_id": "c_7" } }
  ],
  "timeline": {
    "tracks": [
      { "track_id": "t_v1", "kind": "video", "clips": ["clip_1","clip_2","clip_7"] },
      { "track_id": "t_a1", "kind": "audio", "clips": ["aud_vo_2","aud_bgm_1"] },
      { "track_id": "t_c1", "kind": "caption", "clips": ["cap_1","cap_2"] }
    ],
    "objects": {
      "clip_7": { "type":"video_clip","asset_id":"as_stock_91","in":0.0,"out":3.1,"start":11.2,"speed":1.0 },
      "aud_vo_2": { "type":"audio_clip","asset_id":"as_tts_33","start":0.0,"gain_db":-2.0 },
      "cap_2": { "type":"caption_seg","start":11.2,"end":14.0,"text":"..." }
    }
  },
  "history": [
    { "rev": 41, "ops": ["op_aa02"], "by":"assistant", "ts":"2026-01-05T10:12:11Z" }
  ]
}

B) Worker 输出规范(artifact + usage_report)

{
  "job_id": "job_88",
  "step_id": "step_tts_1",
  "status": "SUCCEEDED",
  "artifacts": [
    { "artifact_id":"as_tts_33", "type":"audio", "uri":"s3://.../as_tts_33.wav", "hash":"..." }
  ],
  "usage_report": {
    "provider": "tts_vendor_x",
    "model": "zh-male-v1",
    "characters": 530,
    "seconds": 12.4,
    "cache_hit": false,
    "cost_units": { "credits": 14.2 }
  }
}

C) Patch 应用伪代码(幂等 + 版本)

// applyPatch(project, patch):
if patch.base_revision != project.revision:
  return CONFLICT

for op in patch.ops:
  if ledger.has(op.op_id): continue // 幂等:已执行过
  validate(op, project)
  estimateAndReserve(op)
  project = applyOpToAST(project, op)
  ledger.record(op.op_id, op.cost_estimate)

project.revision += 1
store(project)
emitWSUpdate(project_id, revision)
落地建议:如果你要把这套体系用于你自己的产品,先把 “Project + Patch + Orchestrator + Preview” 做成闭环,后续再接更强的生成模型。
这份文档是“工程实现方法”导向:如果你告诉我你最关心的具体场景(例如“URL→短视频”“脚本→解说视频”“游戏剪辑→高光”), 我可以把 DSL 原子操作集、DAG 依赖与成本模型进一步定制成一套可直接开工的规格说明书(PRD+Tech Spec 合体版)。