Agent 不该只会点按钮:用 MCP 为浏览器视频编辑器建立可验证执行链
当 Codex 开放 Agent Harness 后,我们尝试把一款浏览器视频编辑器改造成 Agent 可以理解、调用、审批和验证的专业工具。
项目地址:Timeline Studio
MCP 实现分支:codex/add-timeline-studio-mcp
过去两年,AI Agent 的演示往往有一种熟悉的画面:
打开网页、移动鼠标、点击按钮、填写表单,然后告诉用户“任务完成”。
这种方式很直观,但当 Agent 开始处理视频剪辑、工程文件、长时间渲染等复杂任务时,问题也会迅速暴露。
一次按钮点击成功,不代表时间线修改正确;页面显示“导出完成”,也不代表输出视频能够正常解码;重复执行同一个操作,还可能产生两份素材或覆盖原工程。
最近,我们在开源浏览器视频编辑器 Timeline Studio 中实现了一套 Skill-first MCP 架构,使 Codex 不再依赖页面坐标完成剪辑,而是通过结构化工具读取工程、预览变更、执行事务并验证结果。
一、从“模型能力”转向“执行系统”
OpenAI 将 Codex 背后的系统称为 Agent Harness。
Harness 不是新的语言模型,而是包围模型的一整套执行基础设施,包括:
- 上下文管理;
- 工具调用;
- 沙箱和权限控制;
- 用户审批;
- 失败处理;
- 多轮任务状态;
- 长时间任务推进。
在 OpenAI 的定义中,一个真正可用的 Agent,不只是“提示词加一次模型回复”,还需要能够理解任务、保持上下文、调用工具、暴露进度、处理失败并返回可验证结果。OpenAI 官方介绍
但 Harness 解决的是 Agent 如何运行,并不会自动理解每一款专业软件。
如果希望 Codex 操作视频编辑器,还需要回答另外三个问题:
- Agent 如何理解专业剪辑流程?
- Agent 通过什么接口修改时间线?
- 如何证明修改和渲染结果正确?
二、为什么浏览器自动化不适合无人值守剪辑
Timeline Studio 是一款本地优先的浏览器视频编辑器,支持:
- 主画面与画中画轨道;
- 字幕、贴纸、配音和音乐;
- AI 音乐和自动字幕;
- 视频修复与主体效果;
.timeline可编辑工程;- 浏览器本地离线导出。
最初,Agent 可以通过浏览器控制完成基本编辑。
例如:
上传视频
→ 点击片段
→ 拖动时间线
→ 打开属性
→ 修改速度
→ 点击导出
这种路径适合操作尚未开放编程接口的功能,但很难成为稳定的无人值守方案。
1. 操作依赖临时界面状态
时间线可能正在横向滚动,属性面板可能被关闭,第一次导入素材还可能出现引导弹窗。
即使按钮名称不变,点击对象也可能已经改变。
2. 拖拽不是一个稳定协议
视频片段的拖拽结果会受到以下因素影响:
- 时间线缩放比例;
- 滚动位置;
- 当前轨道高度;
- 指针命中区域;
- 自动滚动;
- 响应式布局。
Agent 即使把鼠标拖到了“看起来正确”的位置,也不代表片段的时间值符合预期。
3. 写入前无法准确预览变化
传统 UI 自动化通常是先执行,再观察结果。
对于剪辑工程,更合理的顺序应该是先生成修改计划,展示字段级差异,然后才真正写入。
4. 页面成功不等于媒体成功
导出进度达到 100%,不能证明:
- 视频流能够完整解码;
- 音频轨道真实存在;
- 输出时长与时间线一致;
- 相邻片段没有重复尾帧;
- 配音没有在左右声道中错位。
因此,浏览器路径应该是兼容机制,而不是 Agent 的主要执行接口。
三、项目原本已经具备 Agent 化基础
在设计 MCP 之前,我们没有立刻创建新的 timeline-studio-agent 仓库,而是先梳理现有代码。
结果发现,Timeline Studio 已经拥有三块关键基础。
1. 可移植工程格式
Timeline Studio 使用 .timeline 工程保存:
- 项目状态;
- 轨道结构;
- 片段属性;
- 素材引用;
- 字幕与音频关联;
- 工程版本信息。
它本质上是一个包含 project.json 和媒体文件的可移植归档。
Agent 因此可以修改结构化工程,而不是直接破坏原始视频。
2. 纯状态命令内核
项目已经将部分剪辑能力抽离为纯状态 reducer,包括:
- 素材导入;
- 画面追加和插入;
- 裁剪、分割和重排;
- 画中画添加;
- 定时片段移动;
- 字幕增加和更新;
- 属性、速度和静音设置;
- 转场设置;
- 轨道显示与锁定;
- 工程画幅设置。
这些操作不依赖 DOM 坐标,也不需要 React 组件处于某个特定状态。
3. JSON 命令入口
项目已经提供统一命令层:
project.inspect
track.inspect
clip.inspect
transcript.inspect
project.diff
project.run
project.render
所以我们真正缺少的不是另一套 Agent,而是一层很薄的 MCP 适配器。
四、最终架构:Skill-first,而不是 MCP-first
最终结构如下:
Codex Agent Harness
↓
edit-timeline-studio Skill
↓
Timeline Studio MCP Server
↓
timeline-command.mjs
↓
Project Command Registry
↓
.timeline 工程与本地渲染
这里有一个重要判断:Skill 和 MCP 不能互相替代。
Skill 负责专业决策
Skill 保存了大量非通用的剪辑知识,例如:
- 自动剪辑前应该如何分析素材;
- 有源对白时不能重复生成旁白;
- 配音应该先形成音频骨架,再决定画面节奏;
- 字幕必须在完整显示区间内有可听语音;
- 成片交付时必须同时保留可编辑工程;
- 高级效果需要验证实时预览与导出一致性。
这些不是一个 trim_clip 工具能够表达的。
MCP 负责可靠执行
MCP 提供结构化、可验证的工具调用,包括参数 schema、读写属性和安全注解。
命令内核负责确定性修改
真正修改 .timeline 的仍然是原有命令注册表。
MCP 不重新实现剪辑逻辑,从而避免出现:
网页编辑一套逻辑
CLI 编辑一套逻辑
MCP 再编辑一套逻辑
五、我们提供了哪些 MCP 工具
本次实现提供 7 个工具:
timeline_project_inspect
timeline_track_inspect
timeline_clip_inspect
timeline_transcript_inspect
timeline_project_diff
timeline_project_apply
timeline_project_render
工程读取类
timeline_project_inspect 返回:
- 工程 schema 版本;
- 当前修订号;
- 总时长;
- 当前画幅;
- 各轨道片段数量;
- 已应用操作 ID;
- 工程警告;
- 媒体清单。
timeline_track_inspect 用于读取一个轨道,timeline_clip_inspect 则读取单个片段的时间、属性、源素材映射和关联关系。
变更预览类
timeline_project_diff 接收一个版本化修改计划:
{
"project": "/workspace/demo.timeline",
"baseRevision": 12,
"operations": [
{
"id": "trim-main-001",
"type": "visual.trim",
"clipId": "visual-123",
"sourceIn": 1.2,
"sourceOut": 8.4
}
]
}
它会执行完整的项目级校验,但不写入文件。
返回结果包括:
- 修改前状态;
- 修改后状态;
- 工程字段变化;
- 片段增删;
- 片段属性变化;
- 顺序变化;
- 校验警告。
工程写入类
timeline_project_apply 使用相同的工程、修订号和操作列表,将结果写入一个新的 .timeline 文件。
MCP 层禁止覆盖:
- 输入工程;
- 已存在的输出工程;
- 已存在的输出视频。
本地渲染类
timeline_project_render 调用现有无头渲染能力,将受支持的工程内容输出为 MP4,并通过 ffprobe 验证视频尺寸、时长和音频流。
六、为什么 Diff 是整个流程的核心
这套设计最关键的部分不是 MCP,而是写入前强制 Diff。
标准流程为:
inspect
→ diff
→ apply
→ inspect
→ render
Agent 首先读取当前工程修订号,然后生成带稳定操作 ID 的修改计划。
只有 Diff 成功,才能进入 Apply。
Apply 时还会再次检查:
- 基础修订号是否仍然一致;
- 操作类型是否受支持;
- 操作 ID 是否重复;
- 目标片段是否存在;
- 时间范围是否合法;
- 字幕是否保持语音覆盖;
- 输出路径是否安全。
如果其他程序已经修改工程,修订号会发生变化,旧计划将收到 REVISION_CONFLICT,而不是覆盖新状态。
这实际上为视频工程增加了轻量级的乐观并发控制。
七、MCP Server 的实现细节
本次实现采用本地 STDIO MCP Server。
Codex、桌面端和 IDE 扩展都支持连接本地 STDIO MCP 服务,并共享对应主机上的 MCP 配置。OpenAI MCP 文档
项目级配置如下:
[mcp_servers.timeline_studio]
command = "npm"
args = ["run", "mcp", "--silent"]
cwd = "."
startup_timeout_sec = 10
tool_timeout_sec = 300
default_tools_approval_mode = "writes"
服务使用官方 MCP SDK 和 Zod 定义工具:
server.registerTool(
"timeline_project_diff",
{
title: "Preview Timeline Studio edits",
description:
"Validate an operation plan and return a field-level diff without writing files.",
inputSchema: {
project: z.string().min(1),
baseRevision: z.number().int().nonnegative(),
operations: z.array(operationSchema).min(1),
},
annotations: {
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: false,
},
},
handler,
);
MCP Server 不使用 Shell 字符串执行命令,而是通过 Node.js execFile 调用已有 CLI:
await execFile(
process.execPath,
[cliPath, command, ...args],
{
cwd: repositoryRoot,
maxBuffer: 32 * 1024 * 1024,
},
);
这样可以避免因为工程路径、文件名或用户输入造成不必要的 Shell 解析风险。
修改计划会写入系统临时目录,命令完成后立即清理,不在产品仓库中保留测试或运行垃圾。
八、画幅修改不是 Agent 的默认行为
在验证 MCP 写入链路时,我们使用了一个显式画幅操作:
{
"id": "ratio-1",
"type": "project.set_ratio",
"ratio": "9:16"
}
这个操作只用于验证:
inspect → diff → apply
能否真正修改工程。
它不会把 Timeline Studio 的默认画幅改成 9:16。
实际产品中:
- 新项目仍默认
16:9; - MCP 不会自动选择画幅;
- 只有用户明确提出竖屏、方形或其他输出需求,修改计划才会包含
project.set_ratio; - 支持的画幅仍为
16:9、9:16、1:1和4:5。
这也体现了 Skill-first 的作用:工具负责“能不能改”,Skill 负责“什么时候应该改”。
九、如何验证 Agent 工具不是“看起来能用”
本次验证分为四层。
第一层:协议验证
使用 MCP 客户端完成真实 STDIO 握手,确认:
- 服务初始化成功;
- instructions 正确返回;
- 7 个工具全部可发现;
- 工具 schema 可以解析;
- 不存在的工程返回结构化错误。
第二层:真实工程事务
在系统临时目录创建最小 .timeline 工程,执行:
timeline_project_inspect
timeline_project_diff
timeline_project_apply
结果确认:
- Diff 正确识别
16:9 → 9:16; - Apply 生成了新工程;
- 输出修订号从
0增加到1; - 操作 ID 被写入工程状态;
- 输入工程没有被覆盖;
- 输出工程可以重新打开并读取。
验证完成后,临时目录被删除。
第三层:代码质量
执行并通过:
- Skill 元数据校验;
- JavaScript 语法检查;
- ESLint;
- TypeScript 类型检查;
- Vite 生产构建。
第四层:产品回归
本地启动 Timeline Studio,在浏览器中检查:
- 页面正常加载;
- 时间线正常出现;
- 编辑器主要区域可访问;
- 浏览器控制台没有运行时错误。
MCP 属于 Node.js 服务端路径,没有进入 Vite 前端 Bundle,因此不会增加浏览器首屏执行逻辑。
十、当前能力边界
这套 MCP 并没有立即覆盖编辑器的全部能力。
目前无头命令层更适合:
- 工程检查;
- 基础素材导入;
- 片段裁剪和重排;
- 画中画;
- 字幕结构;
- 轨道状态;
- 基础属性;
- 部分本地渲染。
以下能力目前仍主要属于浏览器编辑器:
- Color Wheels;
- 源时间速度曲线;
- 人物和物体抠像;
- 主体描边;
- 本地授权换脸;
- 部分复杂视觉效果。
当工程包含不受支持的效果时,无头渲染器会明确拒绝,而不是静默丢失效果。
这比“尽量渲染一个结果”更重要,因为一个看似成功但丢失关键效果的视频,比明确失败更难发现。
十一、下一步:让传统应用真正 Agent-native
这次实践带来的最大启发,不是“Codex 可以剪视频”。
真正的变化是:传统应用正在出现第二套交互界面。
第一套界面提供给人:
- 按钮;
- 面板;
- 时间线;
- 拖拽;
- 实时预览。
第二套界面提供给 Agent:
- Skill;
- 工具 schema;
- 工程版本;
- 字段级 Diff;
- 原子事务;
- 结构化错误;
- 可验证输出。
二者不应该互相替代。
人类仍然负责目标、审美、取舍和最终确认;Agent 则负责分析、计划、重复劳动和可验证执行。
一个较完整的 Agent-native 应用可以表示为:
领域 Skill
+ MCP 工具
+ 稳定命令内核
+ 可移植工程格式
+ 权限审批
+ 输出验证
未来我们计划继续补充:
- 长时间渲染的结构化进度;
- MCP 级真实取消;
- ASR、视觉分析和本地模型的统一工具接口;
- 无头渲染与浏览器渲染的一致性;
- React 编辑器与 Agent 共用同一命令分发层。
结语
过去,我们常把 Agent 接入理解为“给软件增加一个聊天框”。
但聊天框只是输入形式。
真正决定 Agent 能否进入生产环境的,是软件有没有向它提供清晰的状态、受控的操作、写入前的预览、失败后的恢复,以及可以独立验证的输出。
Codex Harness 解决了 Agent 如何持续工作的问题。
Skill 解决了 Agent 如何理解专业任务的问题。
MCP 解决了 Agent 如何调用应用能力的问题。
而稳定的工程内核,最终决定了它是否值得被信任。
当这几层真正连接起来时,AI 操作视频编辑器才不再是一次漂亮的网页自动化演示,而开始成为一条可以审计、复现和扩展的工程链路。