obs-studio harness:一个不启动 OBS 的直播场景操控面

2026-08-10

CLI-Anything 里的 harness 高度同构:一样的 cli_anything/<pkg>/<pkg>_cli.py 入口、一样的 Click 命令树、一样的 core/utils/ 分工。所以看某一个具体 harness 时,值得花时间的不是再把这套骨架讲一遍,而是找出它跟别人不一样的那一处。

obs-studio 这个 harness 的不一样非常干脆:它不启动 OBS

我们采集时(2026-08-10,对应仓库快照 39634a6)对整个 obs-studio 目录做过一次排查,排除 tests 之后,grep -rl subprocess obs-studio --include='*.py' 命中 0 个文件;同样地,import requestsurllib 的文件也是 0 个。也就是说,这个 harness 的生产代码路径里既不起子进程,也不发网络请求。它做的全部事情,是读写一份 JSON 格式的场景集合。

这一点它自己在 skills/SKILL.md 第 11 行写得很直白,原文是:Uses a JSON scene collection format. No OBS installation required for editing.

先把它的规模和命令面摆出来

单看体量,obs-studio 在同仓属于中等偏小的一档。我们采集时数到非测试代码 2986 行、测试代码 1554 行,入口文件 obs_studio_cli.py 是 869 行。core/ 下 8 个模块,分域很直观:project.pyscenes.pysources.pyfilters.pyaudio.pytransitions.pyoutput.pysession.py

命令面是 8 个组、42 条组内命令,外加一条顶层 replobs_studio_cli.py:773 起)。逐组数量是:

命令组组内命令数
project5
scene5
source6
filter5
audio7
transition5
output5
session4

这张表值得对着 OBS 自己的概念读一遍:场景集合(project)、场景(scene)、源(source)、滤镜(filter)、音频混音(audio)、转场(transition)、推流与录制输出(output)。命令组基本就是 OBS 的名词表。audio 组是命令最多的一组,有 7 条。

反直觉的那一处:一个不碰宿主的 harness

CLI-Anything 的方法论主文档 cli-anything-plugin/HARNESS.md 里,这件事分两层写。实现层在规范目录树那一节:utils/ 下那份 backend 的注释原文是 Backend: invokes the real software(HARNESS.md 692 到 695 行)。测试层另有一条硬规则:E2E tests MUST invoke the real software and produce real output files(HARNESS.md 538 到 547 行)。这是两条各自成立的要求,别混着用。

obs-studio 这一份没有走这条路。先看实现层:它的非测试代码零 subprocess、零 HTTP,规范给 backend 的注释写的是 invokes the real software,而这个 harness 的实现是纯 JSON 编辑。再看测试层:tests/test_full_e2e.py 也不需要真的装 OBS——skills/SKILL.md 第 11 行那句 No OBS installation required for editing 就是它对这件事的自述,与 538 到 547 行那条 E2E 的 MUST 同样对不上。两层各是一个可以自己核对的事实,我们只陈述到这里。

比它更值得你留意的是由此产生的能力边界。同仓里那些真的会去动宿主的 harness,路径是清楚可查的:blender 是拼 blender --background --python <script> 起子进程(utils/blender_backend.py:54),kdenlive 是把 JSON 转成 MLT XML 再交给 melt 子进程(utils/melt_backend.py:130 一带),QGIS 是直接 import PyQGIS 再叠一路 qgis_process --json 子进程。obs-studio 没有这样一条通道。

所以,「agent 能操控 OBS」这句话在这个 harness 上的准确说法是:agent 能编排一份场景集合 JSON。至于这份 JSON 之后怎么进到 OBS 里、进去之后行为如何,我们采集的这一轮没有覆盖,也没有安装或运行过 OBS,不做任何推断——注意 SKILL.md 那句话的限定语本身就是 for editing

顺带说明一个和你本机安全直接相关的事实边界:obs-studio 这一个 harness 的生产代码里我们没有数出 subprocess,但这不能推广到 CLI-Anything 的其它 harness。这类 harness 的常规形态就是在你的机器上执行外部程序与脚本(上面几个例子都是),并且需要你自己先装好宿主软件。注册表里有多少条,跟这些条目在你机器上是否齐件、能否驱动宿主,是两回事。

内置枚举:agent 只能在这些名字里选

纯 JSON 编辑带来的直接后果是:这个 harness 必须自己在代码里把 OBS 的类型名钉死一份,否则 agent 写进 JSON 的字符串没有任何东西会拦。我们采集时数到的几张表是这样的:

枚举位置数量
源类型 SOURCE_TYPEScore/sources.py:1712
滤镜类型 FILTER_TYPEScore/filters.py:813
编码预设 ENCODING_PRESETScore/output.py:78
推流服务白名单core/output.py:194
录制格式core/output.py:205

12 种源类型是:video_capture、display_capture、window_capture、image、media、browser、text、color、audio_input、audio_output、group、scene。推流服务白名单四个值是 twitch、youtube、facebook、custom——注意 custom 也在白名单里,这一栏是”四选一”而不是”三家平台”。

编码预设那 8 个里包含 nvenc_fastnvenc_quality 两条走 NVENC 的。这里要克制一点:预设名和它们在代码里带的默认参数,是这份 JSON 的取值范围,不是对你实际推流效果的任何承诺——我们没有跑过任何一路推流,给不出效果判断。

这几张表的实际用法是:当你让 agent 加一个源、加一条滤镜、换一个编码预设时,它能填的合法值就是上面这些名字。想知道某个你要用的类型在不在,直接打开对应文件那一行看键名,比问模型可靠。

它专门加了一层 NaN 护栏

harness 只写 JSON,还有一个后果不那么显然:JSON 数值是 agent 直接生成的,而 Python 的 float("nan")float("inf") 也能被序列化进去,几何坐标、缩放系数这些字段一旦拿到非有限数,就会成为一份看起来正常、实则带毒的场景文件。

obs-studio 对这件事做了专门处理。utils/obs_utils.py:11 起有一个 _finite_number,会拒绝非有限数;测试侧还配了两个专门文件:我们采集时(2026-08-10 快照 39634a6),tests/test_validate_geometry_nan.py 是 189 行、tests/test_validate_range_nan.py 是 21 行。在我们读过的这 12 个 harness 里,只有 obs-studio 为一类输入单开了两个测试文件。

这条思路在同仓另有一个形态不同的对照:kdenlive 的 utils/melt_backend.py 17 到 21 行把理由直接写进了注释——由 AI agent 控制编解码参数、接受任意字符串会让一个被污染或被提示注入的 agent 把构造过的值传进 melt 子进程,因此它用 ALLOWED_VCODECS / ALLOWED_ACODECS 白名单。一个防的是传给子进程的字符串,一个防的是写进 JSON 的数值,方向不同,出发点都是”这些值是模型生成的”。

状态模型:与 blender 同款的那一套

obs-studio 的 core/session.py 是这样一套:Session 类 + 类常量 MAX_UNDO = 50 + 带 fcntl 文件锁的 JSON 落盘(core/session.py:36-39)。在我们读过的这 12 个 harness 里,blender、gimp、kdenlive 的 session 用的都是同一款;这 12 个之外的其它 harness 我们没有读,不作分布判断。

这里有一点 Windows 读者需要自己确认:这套实现里的文件锁走的是 fcntl,而 fcntl 在 Windows 上没有。我们采集时在 blender 与 freecad 的同款实现里读到了明确说明——freecad 的 core/session.py 第 20 到 23 行 docstring 原文是,在缺少 fcntl 的平台(Windows)上写入不加锁继续。obs-studio 的 core/session.py 与它们同款,具体到这份文件的措辞,你打开 36 到 39 行一带对照一下即可,不必听我转述。

另外,--dry-run 这个开关在 obs-studio 的 CLI 主文件里出现 4 处(同一档的还有 blender、gimp、kdenlive 各 4 处),而 QGIS、zotero、obsidian、comfyui 是 0 处。你可以用 grep -c 'dry.run\|dry_run' obs_studio_cli.py 自己数一遍。对一个只改 JSON 的 harness 来说,先干跑一遍看它准备写什么,是成本最低的一道确认。

TEST.md 上的数字与代码对不上

这个 harness 的 tests/TEST.md 第 5 到 9 行写的是:test_core 117 条、e2e 36 条、合计 153,16 个类。我们采集时(2026-08-10 快照 39634a6)用 grep -c 'def test_' 数出来是 test_core 116、e2e 37,合计同样是 153,但两个分项各差 1;按 ^class 数出的类数是 8 与 9,合计 17。合计数一致而分项与类数不一致,这是两处可以各自核对的事实,我们只陈述到这里。

需要说清计数口径:我们的 def test_ 统计没有排除测试内部的辅助函数,也没有把 @pytest.mark.parametrize 展开成实际用例数,所以这只能说明”两边的口径或时点不同”,不能据此断言哪一边是错的。CLI-Anything 的方法论文档里,TEST.md 的第一段本来就是在写代码之前填的计划稿(HARNESS.md 的 Phase 4 用词是 estimated test counts),这也是同仓多个 harness 的 TEST.md 都有分项差异的背景。

你可以自己跑一遍的核对动作

上面每个结论都落在文件和行号上,clone 下来就能复现。按顺序四步:

# 1. 确认它是否起子进程 / 发 HTTP(生产代码路径)
grep -rl subprocess obs-studio --include='*.py'
# 用 -n 而不是 -rl,把 from ... import ... 这种写法也带出来,再自己排除 tests/
grep -rn 'requests\|urllib' obs-studio --include='*.py'

# 2. 数命令面:8 个组、42 条组内命令 + 顶层 repl
grep -nE '@(cli|[a-z_]+)\.(group|command)\(' obs-studio/agent-harness/cli_anything/obs_studio/obs_studio_cli.py

# 3. 看内置枚举的合法取值(SOURCE_TYPES 从第 17 行起,是一整个字典字面量,只打一行看不到键名)
sed -n '17,78p' obs-studio/agent-harness/cli_anything/obs_studio/core/sources.py
sed -n '7,20p' obs-studio/agent-harness/cli_anything/obs_studio/core/output.py

# 4. 比 TEST.md 与实际函数数
grep -c 'def test_' obs-studio/agent-harness/cli_anything/obs_studio/tests/test_*.py

以上为按仓库中的文件路径与统计口径组合的示例,未经实测,以官方文档与 --help 的实际输出为准。

第 1 步是这篇的重点,也是你判断任何一个 harness”到底会不会动你机器上的东西”的通用第一动作:先看它的非测试代码里有没有 subprocess、有没有 HTTP 客户端,再看 utils/ 下那份 backend 是不是空的。对 obs-studio,这一步的答案是两个 0;对同仓另外那些 harness,答案不一样,得一个一个看。


本文依据 CLI-Anything 官方仓库(github.com/HKUDS/CLI-Anything)的 README、registry.jsondocs/ 与各 agent-harness/ 下的源码整理,核对日 2026-08-10,对应仓库快照 39634a6。 本文内容为仓库源码与文档口径,我们没有安装或运行过其中任何一个 harness, 也没有在本机驱动过任何一款宿主软件,因此不涉及实际操控效果的任何描述。 这类 harness 会在本机执行外部程序与脚本,是否使用请结合自身环境评估。 注册表与 harness 内容随上游更新而变动,请以仓库最新内容为准。 安全相关做法请结合自身环境评估,本文不构成安全方案建议。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。