Medeo.app 深度工程拆解(HTML整合版)
原创
灵阙教研团队
A 推荐 提升 |
约 13 分钟阅读
更新于 2026-01-04 AI 导读
Medeo.app 现象级产品:工程实现方法深度拆解 目标:从“对话式视频创作”背后的核心工程结构出发,拆成可落地的系统设计、DSL、编排、渲染、计费与规模化策略。 Chat → 可执行 DSL 工程文件(Project)为中心 异步任务编排 + 优先级队列 增量渲染(Preview / Final) Credits 成本模型 目录 0. 结论先行(护城河是什么) 1. 产品形态与交互范式 2....
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
用户主路径(抽象)
- 选择 Recipe(比如 URL→视频、脚本→短视频、PPT→视频…)
- 提供输入(文本/URL/素材/偏好)
- 系统生成初稿(脚本、分镜、配音、字幕、素材、音乐)
- 用户通过对话进行修改:节奏、镜头、文案、风格、字幕样式
- 进入轨道精修(拖拽、剪切、替换)
- 导出多比例与多清晰度
工程隐含要求
- 对话修改必须落到“可执行操作”,且可重复(幂等)。
- 轨道编辑必须与对话一致:二者读写同一 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_cliptrim_clip/split_clipset_playback_speedset_transition(type/duration)
生成/替换
generate_broll(依据脚本/关键词)replace_asset(stock/上传/生成)regenerate_scene(镜头级重做)generate_captions/align_captionstts_generate/mix_audio
3.4 高级:为何需要“Scene Graph”(分镜层)而不只是 Timeline?
仅有时间线会让“语义编辑”很难:用户说“把第二个观点讲清楚一点”,这是脚本/分镜语义,未必直接对应某个 Clip。
因此建议 Project 同时维护:
- Script/Outline:观点结构
- Scenes:每段对应的镜头组(可映射到时间线片段范围)
- Timeline:真正渲染执行的轨道对象
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;若落后,走三种策略:- Auto-rebase:如果只改不冲突字段(比如字幕样式),自动合并。
- Ask for disambiguation:如果用户说“那段”,但 clip 已移动。
- 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” 做成闭环,后续再接更强的生成模型。