cli-anything-kdenlive 实战:基于 JSON 项目模型与 MLT XML 生成的有状态 Kdenlive 命令行剪辑工具
【免费下载链接】CLI-Anything"CLI-Anything: Making ALL Software Agent-Native" -- CLI-Hub: https://clianything.cc/项目地址: https://gitcode.com/GitHub_Trending/cl/CLI-Anything
本篇文章围绕 CLI-Anything 仓库中cli-anything-kdenlive技能文档展开,讲解如何通过命令行完成 Kdenlive 工程全流程操作:从创建工程、导入素材、搭建时间线、叠加滤镜与转场,到最终导出可被 Kdenlive 与melt直接消费的 MLT XML。读者读完将掌握cli-anything-kdenlive的完整命令体系、JSON 工程文件结构与状态管理机制,并能像操作 GUI 一样用脚本化、可回滚、面向 Agent 的方式驱动视频剪辑。
工具定位:有状态的命令行剪辑引擎
skills/SKILL.md 开篇即定义了这个工具的本质:
A stateful command-line interface for video editing, following the same patterns as the Blender CLI harness. Uses a JSON project format with MLT XML generation for Kdenlive/melt.
它不是一个简单的一次性封装脚本,而是一套有状态(stateful)的剪辑会话系统:内部维护一份 JSON 工程模型(bin素材箱、tracks轨道、transitions转场、guides标记),并在每次修改前对工程打快照,从而提供最多 50 层的撤销/重做能力;最后通过export xml将 JSON 模型序列化为带 Kdenlive 元数据的MLT XML文档,实现"JSON 可读、XML 可播、Kdenlive 可编辑"三态互通。
在仓库中的实际目录结构为:
kdenlive/agent-harness/cli_anything/kdenlive/ ├── kdenlive_cli.py # Click 入口 + REPL 主循环 ├── __main__.py # python -m 模块入口 ├── core/ │ ├── project.py # 工程 create/open/save/info/profiles │ ├── bin.py # 素材箱管理 │ ├── timeline.py # 轨道与片段摆放 │ ├── filters.py # 滤镜注册表与参数校验 │ ├── transitions.py # 转场管理 │ ├── guides.py # 标记管理 │ ├── export.py # XML 生成与渲染预设 │ └── session.py # 带 undo/redo 的会话 ├── utils/ │ ├── mlt_xml.py # MLT XML 构建、时间码换算 │ └── repl_skin.py # REPL 交互皮肤(提示符/补全/帮助) ├── skills/SKILL.md # Agent 技能描述 └── tests/ ├── TEST.md ├── test_core.py # 118 个单元测试 └── test_full_e2e.py # 33 个端到端测试入口实现见 kdenlive_cli.py,各功能模块在core/下按领域拆分,utils/mlt_xml.py提供 MLT XML 构建器与时间码工具,结构清晰、可独立测试。
安装与前置条件
依据 SKILL.md 的安装说明,该 CLI 随cli-anything-kdenlive包一同安装:
pip install cli-anything-kdenlive前置条件:
- Python 3.10+
- 系统需要安装Kdenlive(若要在 Kdenlive 中打开生成的工程;仅做 JSON 工程编辑与校验则不需要 Kdenlive/melt)
需要特别说明的是:工程编辑本身不依赖 Kdenlive 运行时,这在包内 README.md 中有明确提示——"No Kdenlive or melt installation required for project editing. Kdenlive is only needed to open the generated .kdenlive XML files." 也就是说,素材的组织、时间线的排版、滤镜/转场/标记的配置全部可以在无 GUI 环境下完成;只有最后把导出的.kdenliveXML 交给 Kdenlive 或melt渲染时才需要相应软件。
在源码目录内直接开发运行的方式(来自 README 的 Quick Start 模式):
pip install click python3 -m cli_anything.kdenlive.kdenlive_cli project new --name "MyVideo" --profile hd1080p30 -o project.json基础命令一览
# 查看帮助 cli-anything-kdenlive --help # 进入交互式 REPL 模式 cli-anything-kdenlive # 新建工程 cli-anything-kdenlive project new -o project.json # 以 JSON 输出工程信息(供 Agent 程序消费) cli-anything-kdenlive --json project info -p project.jsonCLI 全局参数(见 kdenlive_cli.py 的 Click 定义):
| 全局选项 | 含义 |
|---|---|
--json | 所有输出切换为结构化 JSON |
--project <path> | 指定.kdenlive-cli.json工程文件(一次性命令自动加载) |
--dry-run | 只执行不落盘(配合--project使用) |
当--project缺省且未调用子命令时,CLI 自动进入 REPL;一次性命令执行完毕若工程有改动且未指定--dry-run,result_callback(auto_save_on_exit)会将其自动写回磁盘。
REPL 交互模式
SKILL.md 说明:不带子命令直接启动即进入交互式 REPL 会话:
cli-anything-kdenlive # 进入命令交互,支持 tab 补全与历史记录实现上,REPL 主循环位于 kdenlive_cli.py 的repl()命令,基于ReplSkin(见 utils/repl_skin.py)构造交互体验:启动打印版本 banner,输入行通过shlex.split拆分为参数后复用同一个 Click 命令树执行,因此 REPL 内可用的命令与一次性命令行完全一致。
REPL 内的关键交互能力:
- 输入
help查看所有命令组的速查帮助; - 输入
undo/redo进行历史导航(有状态会话的核心能力); - 输入
quit/exit/q退出; - 提示符会显示当前工程名与未保存修改状态(
*标记)。
启动时也可直接挂载已有工程:
cli-anything-kdenlive repl --project project.json命令组全解
SKILL.md 将全部功能划分为 8 个命令组。以下逐组展开,命令参数细节以源码为准。
Project(工程管理)
| 命令 | 说明 |
|---|---|
new | 创建新工程 |
open | 打开已有工程文件 |
save | 保存当前工程 |
info | 显示工程信息 |
profiles | 列出可用视频 Profile |
json | 打印原始工程 JSON |
project new支持参数:--name/-n(默认untitled)、--profile/-p(命名 profile)、--width/--height、--fps-num/--fps-den、--output/-o。核心校验逻辑位于 core/project.py:若指定了命名 profile 则整体覆盖分辨率/帧率/宽高比参数;宽度、高度、FPS 分子分母必须为正数,否则抛ValueError。
project info的统计输出来自get_project_info()(core/project.py),返回 profile 摘要(分辨率、fps、逐行/隔行、宽高比)以及各对象计数(bin_clips、tracks、clips_on_timeline、transitions、guides),方便 Agent 快速核对工程规模。
Bin(素材箱)
| 命令 | 说明 |
|---|---|
import | 导入素材到素材箱 |
remove | 从素材箱移除素材 |
list | 列出素材箱全部素材 |
get | 获取素材详细信息 |
# 导入视频与音频素材 cli-anything-kdenlive --project project.json bin import /path/to/video.mp4 --name "Interview" -d 120.5 cli-anything-kdenlive --project project.json bin import /path/to/music.mp3 --name "BGM" -d 180.0 --type audiobin import参数:source(必填路径)、--name/-n、--duration/-d(秒)、--type(video|audio|image|color|title,默认video)。素材被分配全局唯一的id(如clip0、clip1),时间线片段通过该 id 引用素材,与真实 NLE 的 bin/时间线引用关系一致。
Timeline(时间线)
| 命令 | 说明 |
|---|---|
add-track | 添加轨道 |
remove-track | 移除轨道 |
add-clip | 将素材放到轨道 |
remove-clip | 从轨道移除片段 |
trim | 调整片段入/出点 |
split | 在指定时间点分割片段 |
move | 移动片段到新位置 |
list | 列出全部轨道 |
cli-anything-kdenlive --project project.json timeline add-track --type video cli-anything-kdenlive --project project.json timeline add-track --type audio cli-anything-kdenlive --project project.json timeline add-clip 0 clip0 --position 0 --out 30.0参数语义(见 core/timeline.py 与入口命令):
add-track:--name、--type video|audio(默认 video)、--mute/--hide/--locked;未命名时自动生成V1/V2…或A1/A2…序号名(timeline.py);add-clip:track_id(整数)、clip_id(bin 内 id)、--position/-p(放置位置,秒)、--in(素材内入点)、--out(出点,缺省时按素材时长推算);向locked轨道添加会抛错;trim:--in/--out修改片段在素材内的使用区间;split:在split_at(相对片段的秒偏移)处一分为二;move:把片段移到new_position(秒)——实现层会保证同轨片段按位置排序。
Filter(滤镜/效果)
| 命令 | 说明 |
|---|---|
add | 给轨道上的片段添加滤镜 |
remove | 移除滤镜 |
set | 设置滤镜参数 |
list | 列出片段上的滤镜 |
available | 列出全部可用滤镜 |
# 给轨道0第0个片段加亮度滤镜并设置 level=1.3 cli-anything-kdenlive --project project.json filter add 0 0 brightness -p level=1.3filter add的位置参数是track_id与clip_index(片段在轨道内的序号,非 bin id);--param/-p可多次传入key=value,入口会按"含小数点→float,否则→int,失败→str"做类型推断(见 kdenlive_cli.py 的filter_add)。参数在底层还会经过_validate_filter_params的二次校验:未知参数名直接报错,数值类型会检查上下限范围(core/filters.py)。
Transition(转场)
| 命令 | 说明 |
|---|---|
add | 在轨道间添加转场 |
remove | 移除转场 |
set | 设置转场参数 |
list | 列出全部转场 |
cli-anything-kdenlive --project project.json transition add dissolve 0 1 -d 2.0transition add位置参数依次为:transition_type、track_a、track_b;选项--position/-p(默认 0.0 秒)、--duration/-d(默认 1.0 秒)、--param(key=value可重复)。由 core/transitions.py 定义的内置转场注册表:
| 类型 | MLT service | 可调参数 |
|---|---|---|
dissolve | luma | duration(0.01–60s)、softness(0–1) |
wipe | luma | duration、resource(擦除图案)、softness |
slide | affine | duration、direction(如left) |
composite | composite | fill(0/1)、aligned(0/1) |
affine | affine | distort(0/1) |
约束上,同一轨道的自转场会被拒绝,无效轨道索引也会报错。
Guide(标记)
| 命令 | 说明 |
|---|---|
add | 在指定位置(秒)添加标记 |
remove | 移除标记 |
list | 列出全部标记 |
cli-anything-kdenlive --project project.json guide add 30.0 --label "Scene 2"guide add支持--label/-l、--type(default|chapter|segment,默认default)与--comment/-c,可作为章节点或分镜标记。Guide 在导出 XML 时会写入序列的kdenlive:sequenceproperties.guides属性(JSON 数组,含pos/comment/type)。
Export(导出)
| 命令 | 说明 |
|---|---|
xml | 生成 Kdenlive/MLT XML |
presets | 列出可用渲染预设 |
cli-anything-kdenlive --project project.json export xml -o output.kdenlive注意:SKILL.md 的 Examples 一节保留了一条cli-anything-kdenlive --project myproject.json export render output.pdf --overwrite的示意命令;从当前源码的命令注册表看,实际落地实现的是export xml与export presets两个子命令(见 kdenlive_cli.py 的 export 组),因此自动化脚本请以export xml作为导出入口,并将输出文件交给 Kdenlive/melt处理渲染。
Session(会话)
| 命令 | 说明 |
|---|---|
status | 显示会话状态 |
undo | 撤销上一次操作 |
redo | 重做被撤销的操作 |
history | 显示撤销历史 |
状态管理:快照式撤销/重做与会话持久化
SKILL.md 明确本工具维护三种状态能力:
- Undo/Redo:最多50 层历史;
- Project persistence:工程以 JSON 保存/加载;
- Session tracking:跟踪修改状态。
源码层面由 core/session.py 的Session类实现,关键机制如下:
- 快照时机:每次会变更工程的命令(导入、建轨、放片段、修剪、分割、滤镜、转场、标记等)在执行前都会调用
sess.snapshot(description),将当前工程deepcopy压入_undo_stack(session.py); - 容量上限:
Session.MAX_UNDO = 50,超出时弹出最旧的快照(先进先出);执行新操作会清空 redo 栈; - 撤销/重做:
undo()把当前工程压入 redo 栈再从 undo 栈弹出恢复;redo()反向操作(session.py); - 修改跟踪:
status()返回modified、undo_count、redo_count、project_path等,Agent 可在长时间会话前查询是否有未保存修改; - 持久化:
save_session()通过_locked_save_json使用fcntl文件锁进行原子写入(不可用时优雅降级),避免多进程并发写坏工程文件(session.py)。
JSON 工程模型:一切状态的中枢
SKILL.md 反复强调"JSON project format",工程文件是整条链路的枢纽。一个典型工程的 JSON 结构如下(来自包内 README 的完整示例):
{ "version": "1.0", "name": "my_video", "profile": { "name": "hd1080p30", "width": 1920, "height": 1080, "fps_num": 30, "fps_den": 1, "progressive": true, "dar_num": 16, "dar_den": 9 }, "bin": [ {"id": "clip0", "name": "Interview", "source": "/path/to/video.mp4", "duration": 120.5, "type": "video"} ], "tracks": [ {"id": 0, "name": "V1", "type": "video", "mute": false, "hide": false, "locked": false, "clips": [ {"clip_id": "clip0", "in": 0.0, "out": 30.0, "position": 0.0, "filters": []} ]} ], "transitions": [], "guides": [], "metadata": {} }各字段语义与代码实现一一对应(core/project.py):
| 字段 | 含义 | 备注 |
|---|---|---|
version | 工程格式版本 | 固定1.0,open_project校验必需 |
name | 工程名 | project new --name |
profile | 工程 Profile | 含name/width/height/fps_num/fps_den/progressive/dar_num/dar_den |
bin[] | 素材箱 | 素材全局唯一,时间线通过clip_id引用 |
tracks[] | 时间线轨道 | 轨道自带mute/hide/locked/clips |
tracks[].clips[] | 轨道上的片段 | clip_id/in/out/position/filters |
transitions[] | 转场 | 引用两个轨道 id |
guides[] | 标记 | position/label/type |
metadata | 元数据 | created/modified/software |
open_project()只要求version与profile存在即可恢复工程;数据往返(保存→重新加载)在 E2E 测试中保证无损。
预置 Profile:从 SD 到 4K
project new的--profile选项来自 core/project.py 的PROFILES注册表。内置 Profile 汇总:
| Profile | 分辨率 | 帧率 | 扫描方式 | 宽高比 |
|---|---|---|---|---|
hd1080p30 | 1920×1080 | 30 fps | 逐行 | 16:9 |
hd1080p25 | 1920×1080 | 25 fps | 逐行 | 16:9 |
hd1080p24 | 1920×1080 | 24 fps | 逐行 | 16:9 |
hd1080p60 | 1920×1080 | 60 fps | 逐行 | 16:9 |
hd720p30 | 1280×720 | 30 fps | 逐行 | 16:9 |
hd720p25 | 1280×720 | 25 fps | 逐行 | 16:9 |
hd720p60 | 1280×720 | 60 fps | 逐行 | 16:9 |
4k30 | 3840×2160 | 30 fps | 逐行 | 16:9 |
4k60 | 3840×2160 | 60 fps | 逐行 | 16:9 |
sd_ntsc | 720×480 | 30000/1001 fps | 隔行 | 4:3 |
sd_pal | 720×576 | 25 fps | 隔行 | 4:3 |
注意 NTSC 的帧率用分数fps_num/fps_den = 30000/1001表达,避免小数误差;自定义工程也可绕过 profile,直接用--width/--height/--fps-num/--fps-den指定任意参数(此时 profile 名记为custom)。
可用滤镜注册表
内置滤镜清单来自 core/filters.py 的FILTER_REGISTRY(SKILL.md 未逐个列出,这里补全):
| 滤镜 | MLT service | 主要参数(范围) | 分类 |
|---|---|---|---|
brightness | brightness | level(0–5, 默认1.0) | color |
contrast | brightness | level(0–5, 默认1.0) | color |
saturation | avfilter.eq | saturation(0–3, 默认1.0) | color |
blur | boxblur | hblur/vblur(int 0–100, 默认2) | effect |
fade_in_video | brightness | duration(0.01–60s) | transition |
fade_out_video | brightness | duration(0.01–60s) | transition |
fade_in_audio | volume | duration(0.01–60s) | transition |
fade_out_audio | volume | duration(0.01–60s) | transition |
volume | volume | gain(0–10, 默认1.0) | audio |
crop | crop | left/right/top/bottom(int 0–9999) | effect |
rotate | affine | angle(-360–360) | effect |
speed | timewarp | speed(0.01–100, 默认1.0) | effect |
chroma_key | frei0r.select0r | color(默认#00ff00)、variance(0–1, 默认0.15) | keying |
几点值得注意的实现细节:
- 每个滤镜带
mlt_service,导出时直接写入 XML 的mlt_service属性;部分滤镜(如淡入淡出、speed)还映射了 Kdenlive 侧的kdenlive_name(fade_from_black、fadein、fadeout等),保证在 Kdenlive 界面中显示正确语义; filter available --category color可按分类过滤;- 参数规格在注册表内声明
type/default/min/max,_validate_filter_params负责补默认值并拒绝越界值——这意味着"传坏参数"会被前置拦截,而不是等 XML 生成后才失败。
MLT XML 生成原理:JSON → Kdenlive 文档的桥梁
export xml的实现核心位于 utils/mlt_xml.py 的build_mlt_xml()。它基于 Python 标准库xml.etree.ElementTree生成Kdenlive Gen 5(文档版本 1.1)兼容的 MLT XML,具备以下结构要点:
<mlt>根元素:LC_NUMERIC="C"(保证浮点序列化与 locale 无关)、version="7.0.0"、title取工程名、producer="main_bin";<profile>:写入分辨率、逐行/隔行、sample_aspect_num/den(由显示宽高比与分辨率推导)、frame_rate_num/den、colorspace="709";- 素材链(chain):每个素材生成一条
<chain>,按素材类型映射 MLT service——普通媒体走avformat-novalidate并按audio/image类型设置audio_index/video_index(见_set_producer_props与_avformat_indexes),color类走colorservice; - 轨道结构:音频轨在前、视频轨在后排序;每条轨道生成
chain + 双 playlist + 包裹 tractor的结构,轨道间留空以<blank>填充,方便后续 Kdenlive 内二次编辑; - 序列 tractor:使用 UUID 标识,写入
kdenlive:uuid、kdenlive:sequenceproperties.*(hasAudio/hasVideo/activeTrack/tracksCount/duration/maxduration/zoom/guides等)Kdenlive 私有属性,guides 以 JSON 数组序列化到kdenlive:sequenceproperties.guides; - 内部混合转场:为音频轨自动附加
mix、为视频轨自动附加qtblend内部转场(标记internal_added=237),再追加用户自定义转场(luma/affine/composite,换算 a_track/b_track 与 in/out 帧区间); - main_bin playlist:最后生成
main_bin播放列表,写入kdenlive:docproperties.*文档属性并把序列与全部素材入口挂入; - 工程 tractor:
tractor_project作为最后一个元素、标有kdenlive:projectTractor=1——这正是melt播放时所消费的顶层输出。
导出的 XML 属性对超长文件名/特殊字符做了转义,xml_escape会处理& < > " '(mlt_xml.py)。因此生成的产物可安全用于:直接在 Kdenlive 打开、交给melt命令行处理、或嵌入更上层的自动化渲染流水线。
时间码与帧换算工具
JSON 模型里时间均以秒(float)存储,而 MLT 世界以帧为单位。utils/mlt_xml.py提供换算函数作为桥梁:
seconds_to_timecode(seconds)→HH:MM:SS.mmm字符串,负数抛错;timecode_to_seconds(tc)→ 秒,接受纯数字字符串或HH:MM:SS.mmm(含小时位最多两位);seconds_to_frames(seconds, fps_num, fps_den)/frames_to_seconds(...)→ 与工程 profile 帧率互转。
REPL 与命令行的parse_time也复用了timecode_to_seconds,意味着时间参数既可以直接写秒(30.0),也可以写时间码(00:00:30.000)。
输出格式:双通道(人类可读 / Agent 可解析)
SKILL.md 规定所有命令支持双模式输出:
- 人类可读(默认):格式化文本/表格,多层 dict 与 list 会被缩进打印;
- 机器可读(
--json):输出结构化 JSON,供 Agent 直接解析。
# 人类输出 cli-anything-kdenlive project info -p project.json # Agent 使用的 JSON 输出 cli-anything-kdenlive --json project info -p project.json实现上,入口层维护_json_output全局开关,统一的output(data, message)在 JSON 模式下json.dumps(data, indent=2, default=str)输出完整对象;错误处理装饰器handle_error在 JSON 模式下把异常编码为{"error": ..., "type": "file_not_found" | "file_exists" | ...}的结构化错误(kdenlive_cli.py)。
面向 AI Agent 的 5 条操作规范
SKILL.md "For AI Agents" 一节给出了程序化调用时的硬性纪律:
- 始终使用
--json标志以获得可解析输出; - 检查返回码——0 表示成功,非零表示失败;
- 解析 stderr获取失败时的错误信息(人类模式下错误打到
err=True); - 所有文件操作使用绝对路径;
- 导出后验证产物存在(
export xml成功会回传{"path": ..., "size": ...})。
实战示例串讲
示例一:新建工程
cli-anything-kdenlive project new -o myproject.json # 或输出 JSON 供程序化使用 cli-anything-kdenlive --json project new -o myproject.json示例二:一条完整的"导素材→排轨→加效果→导出"链路
# 1) 建工程(1080p30) cli-anything-kdenlive --project project.json project new --name "MyVideo" --profile hd1080p30 -o project.json # 2) 素材进 bin cli-anything-kdenlive --project project.json bin import /path/to/video.mp4 --name "Interview" -d 120.5 cli-anything-kdenlive --project project.json bin import /path/to/music.mp3 --name "BGM" -d 180.0 --type audio # 3) 建轨道并摆放片段 cli-anything-kdenlive --project project.json timeline add-track --type video cli-anything-kdenlive --project project.json timeline add-track --type audio cli-anything-kdenlive --project project.json timeline add-clip 0 clip0 --position 0 --out 30.0 cli-anything-kdenlive --project project.json timeline add-clip 1 clip1 --position 0 --out 60.0 # 4) 加滤镜与转场 cli-anything-kdenlive --project project.json filter add 0 0 brightness -p level=1.3 cli-anything-kdenlive --project project.json transition add dissolve 0 1 -d 2.0 # 5) 加标记(章节点) cli-anything-kdenlive --project project.json guide add 30.0 --label "Scene 2" # 6) 导出 MLT XML 并保存工程 cli-anything-kdenlive --project project.json export xml -o output.kdenlive cli-anything-kdenlive --project project.json project save示例三:REPL 交互会话
cli-anything-kdenlive # 输入 help 查看命令 # 输入 project new --name demo -o demo.json 建工程 # 输入 undo / redo 做历史导航 # 输入 quit 退出测试覆盖与质量基线
工程附带了两套测试(详见 tests/TEST.md):
- tests/test_core.py:118 个纯内存单元测试,覆盖 Project(17)、Bin(12)、Timeline(18)、Filters(16)、Transitions(11)、Guides(8)、TimecodeUtils(13)、Session(12),无需安装 Kdenlive;
- tests/test_full_e2e.py:33 个 E2E 测试,验证 MLT XML 结构与格式(TestXMLGeneration 13 个)、JSON/XML 格式往返(TestFormatValidation 8 个)以及多机位、音视频分离、trim/split、滤镜链、转场、undo/redo 等真实剪辑工作流(TestWorkflowE2E 18 个)。
E2E 测试对 XML 的断言(根元素<mlt>、每个 bin 素材生成<producer>/<chain>、每条轨道生成<playlist>、滤镜含mlt_service、特殊字符正确转义、SD PAL profile 得到正确 XML 值等)从侧面印证了前述生成逻辑的契约,也是自行扩展渲染流水线时的参考蓝本。
更多资源
- 技能文档(本文依据):skills/SKILL.md
- 包内完整 README(Quick Start / JSON 格式 / MLT XML 说明):README.md
- 测试文档与测试源码:tests/TEST.md、tests/test_full_e2e.py
- 方法论参考:CLI-Anything 插件的 HARNESS.md,以及仓库内其他同类 harness(如 blender)的模式说明
总体而言,cli-anything-kdenlive把传统 GUI 剪辑的高频操作收敛为一套可编程、可撤销、可 JSON 化的命令原语,天然适合接入自动化渲染流水线、批量素材整理与 AI Agent 工具调用场景;若你的目标是让模型或脚本"在无人值守下完成一次可交付的视频粗剪",本工具提供了一条端到端可验证的路径。
【免费下载链接】CLI-Anything"CLI-Anything: Making ALL Software Agent-Native" -- CLI-Hub: https://clianything.cc/项目地址: https://gitcode.com/GitHub_Trending/cl/CLI-Anything
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考